475 lines · cpp
1// RUN: %clang_cc1 --std=c++20 -fsyntax-only -verify -Wdangling-capture %s2 3#include "Inputs/lifetime-analysis.h"4 5// ****************************************************************************6// Capture an integer7// ****************************************************************************8namespace capture_int {9struct X {} x;10void captureInt(const int &i [[clang::lifetime_capture_by(x)]], X &x);11void captureRValInt(int &&i [[clang::lifetime_capture_by(x)]], X &x);12void noCaptureInt(int i [[clang::lifetime_capture_by(x)]], X &x);13 14void use() {15 int local;16 captureInt(1, // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}17 x);18 captureRValInt(1, x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}19 captureInt(local, x);20 noCaptureInt(1, x);21 noCaptureInt(local, x);22}23} // namespace capture_int24 25// ****************************************************************************26// Capture std::string (gsl owner types)27// ****************************************************************************28namespace capture_string {29struct X {} x;30void captureString(const std::string &s [[clang::lifetime_capture_by(x)]], X &x);31void captureRValString(std::string &&s [[clang::lifetime_capture_by(x)]], X &x);32 33void use() {34 std::string local_string;35 captureString(std::string(), x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}36 captureString(local_string, x);37 captureRValString(std::move(local_string), x);38 captureRValString(std::string(), x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}39}40} // namespace capture_string41 42// ****************************************************************************43// Capture std::string_view (gsl pointer types)44// ****************************************************************************45namespace capture_string_view {46struct X {} x;47void captureStringView(std::string_view s [[clang::lifetime_capture_by(x)]], X &x);48void captureRValStringView(std::string_view &&sv [[clang::lifetime_capture_by(x)]], X &x);49void noCaptureStringView(std::string_view sv, X &x);50 51std::string_view getLifetimeBoundView(const std::string& s [[clang::lifetimebound]]);52std::string_view getNotLifetimeBoundView(const std::string& s);53const std::string& getLifetimeBoundString(const std::string &s [[clang::lifetimebound]]);54const std::string& getLifetimeBoundString(std::string_view sv [[clang::lifetimebound]]);55 56void use() {57 std::string_view local_string_view;58 std::string local_string;59 captureStringView(local_string_view, x);60 captureStringView(std::string(), // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}61 x);62 63 captureStringView(getLifetimeBoundView(local_string), x);64 captureStringView(getNotLifetimeBoundView(std::string()), x);65 captureRValStringView(std::move(local_string_view), x);66 captureRValStringView(std::string(), x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}67 captureRValStringView(std::string_view{"abcd"}, x);68 69 noCaptureStringView(local_string_view, x);70 noCaptureStringView(std::string(), x);71 72 // With lifetimebound functions.73 captureStringView(getLifetimeBoundView(74 std::string() // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}75 ), x);76 captureRValStringView(getLifetimeBoundView(local_string), x);77 captureRValStringView(getLifetimeBoundView(std::string()), x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}78 captureRValStringView(getNotLifetimeBoundView(std::string()), x);79 noCaptureStringView(getLifetimeBoundView(std::string()), x);80 captureStringView(getLifetimeBoundString(std::string()), x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}81 captureStringView(getLifetimeBoundString(getLifetimeBoundView(std::string())), x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}82 captureStringView(getLifetimeBoundString(getLifetimeBoundString(83 std::string() // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}84 )), x);85}86} // namespace capture_string_view87 88// ****************************************************************************89// Capture pointer (eg: std::string*)90// ****************************************************************************91const std::string* getLifetimeBoundPointer(const std::string &s [[clang::lifetimebound]]);92const std::string* getNotLifetimeBoundPointer(const std::string &s);93 94namespace capture_pointer {95struct X {} x;96void capturePointer(const std::string* sp [[clang::lifetime_capture_by(x)]], X &x);97void use() {98 capturePointer(getLifetimeBoundPointer(std::string()), x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}99 capturePointer(getLifetimeBoundPointer(*getLifetimeBoundPointer(100 std::string() // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}101 )), x);102 capturePointer(getNotLifetimeBoundPointer(std::string()), x);103 104}105} // namespace capture_pointer106 107// ****************************************************************************108// Arrays and initializer lists.109// ****************************************************************************110namespace init_lists {111struct X {} x;112void captureVector(const std::vector<int> &a [[clang::lifetime_capture_by(x)]], X &x);113void captureArray(int array [[clang::lifetime_capture_by(x)]] [2], X &x);114void captureInitList(std::initializer_list<int> abc [[clang::lifetime_capture_by(x)]], X &x);115 116 117std::initializer_list<int> getLifetimeBoundInitList(std::initializer_list<int> abc [[clang::lifetimebound]]);118 119void use() {120 captureVector({1, 2, 3}, x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}121 captureVector(std::vector<int>{}, x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}122 std::vector<int> local_vector;123 captureVector(local_vector, x);124 int local_array[2]; 125 captureArray(local_array, x);126 captureInitList({1, 2}, x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}127 captureInitList(getLifetimeBoundInitList({1, 2}), x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}128}129} // namespace init_lists130 131// ****************************************************************************132// Implicit object param 'this' is captured133// ****************************************************************************134namespace this_is_captured {135struct X {} x;136struct S {137 void capture(X &x) [[clang::lifetime_capture_by(x)]];138};139void use() {140 S{}.capture(x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}141 S s;142 s.capture(x);143}144} // namespace this_is_captured145 146namespace temporary_capturing_object {147struct S {148 void add(const int& x [[clang::lifetime_capture_by(this)]]);149};150 151void test() {152 // We still give an warning even the capturing object is a temoprary.153 // It is possible that the capturing object uses the captured object in its154 // destructor.155 S().add(1); // expected-warning {{object whose reference is captured}}156 S{}.add(1); // expected-warning {{object whose reference is captured}}157}158} // namespace ignore_temporary_class_object159 160// ****************************************************************************161// Capture by Global and Unknown.162// ****************************************************************************163namespace capture_by_global_unknown {164void captureByGlobal(std::string_view s [[clang::lifetime_capture_by(global)]]);165void captureByUnknown(std::string_view s [[clang::lifetime_capture_by(unknown)]]);166 167std::string_view getLifetimeBoundView(const std::string& s [[clang::lifetimebound]]);168 169void use() { 170 std::string_view local_string_view;171 std::string local_string;172 // capture by global.173 captureByGlobal(std::string()); // expected-warning {{object whose reference is captured will be destroyed at the end of the full-expression}}174 captureByGlobal(getLifetimeBoundView(std::string())); // expected-warning {{object whose reference is captured will be destroyed at the end of the full-expression}}175 captureByGlobal(local_string);176 captureByGlobal(local_string_view);177 178 // capture by unknown.179 captureByUnknown(std::string()); // expected-warning {{object whose reference is captured will be destroyed at the end of the full-expression}}180 captureByUnknown(getLifetimeBoundView(std::string())); // expected-warning {{object whose reference is captured will be destroyed at the end of the full-expression}}181 captureByUnknown(local_string);182 captureByUnknown(local_string_view);183}184} // namespace capture_by_global_unknown185 186// ****************************************************************************187// Member functions: Capture by 'this'188// ****************************************************************************189namespace capture_by_this {190struct S {191 void captureInt(const int& x [[clang::lifetime_capture_by(this)]]);192 void captureView(std::string_view sv [[clang::lifetime_capture_by(this)]]);193};194std::string_view getLifetimeBoundView(const std::string& s [[clang::lifetimebound]]);195std::string_view getNotLifetimeBoundView(const std::string& s);196const std::string& getLifetimeBoundString(const std::string &s [[clang::lifetimebound]]);197 198void use() {199 S s;200 s.captureInt(1); // expected-warning {{object whose reference is captured by 's' will be destroyed at the end of the full-expression}}201 s.captureView(std::string()); // expected-warning {{object whose reference is captured by 's' will be destroyed at the end of the full-expression}}202 s.captureView(getLifetimeBoundView(std::string())); // expected-warning {{object whose reference is captured by 's' will be destroyed at the end of the full-expression}}203 s.captureView(getLifetimeBoundString(std::string())); // expected-warning {{object whose reference is captured by 's' will be destroyed at the end of the full-expression}}204 s.captureView(getNotLifetimeBoundView(std::string()));205} 206} // namespace capture_by_this207 208// ****************************************************************************209// Struct with field as a reference210// ****************************************************************************211namespace reference_field {212struct X {} x;213struct Foo {214 const int& b;215};216void captureField(Foo param [[clang::lifetime_capture_by(x)]], X &x);217void use() {218 captureField(Foo{219 1 // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}220 }, x);221 int local;222 captureField(Foo{local}, x);223}224} // namespace reference_field225 226// ****************************************************************************227// Capture default argument.228// ****************************************************************************229namespace default_arg {230struct X {} x;231void captureDefaultArg(X &x, std::string_view s [[clang::lifetime_capture_by(x)]] = std::string());232 233std::string_view getLifetimeBoundView(const std::string& s [[clang::lifetimebound]]);234 235void useCaptureDefaultArg() {236 X x;237 captureDefaultArg(x); // FIXME: Diagnose temporary default arg.238 captureDefaultArg(x, std::string("temp")); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}239 captureDefaultArg(x, getLifetimeBoundView(std::string())); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}240 std::string local;241 captureDefaultArg(x, local);242}243} // namespace default_arg 244 245// ****************************************************************************246// Container: *No* distinction between pointer-like and other element type247// ****************************************************************************248namespace containers_no_distinction {249template<class T>250struct MySet {251 void insert(T&& t [[clang::lifetime_capture_by(this)]]);252 void insert(const T& t [[clang::lifetime_capture_by(this)]]);253};254void user_defined_containers() {255 MySet<int> set_of_int;256 set_of_int.insert(1); // expected-warning {{object whose reference is captured by 'set_of_int' will be destroyed at the end of the full-expression}}257 MySet<std::string_view> set_of_sv;258 set_of_sv.insert(std::string()); // expected-warning {{object whose reference is captured by 'set_of_sv' will be destroyed at the end of the full-expression}}259 set_of_sv.insert(std::string_view());260}261} // namespace containers_no_distinction262 263// ****************************************************************************264// Container: Different for pointer-like and other element type.265// ****************************************************************************266namespace conatiners_with_different {267template<typename T> struct IsPointerLikeTypeImpl : std::false_type {};268template<> struct IsPointerLikeTypeImpl<std::string_view> : std::true_type {};269template<typename T> concept IsPointerLikeType = std::is_pointer<T>::value || IsPointerLikeTypeImpl<T>::value;270 271template<class T> struct MyVector {272 void push_back(T&& t [[clang::lifetime_capture_by(this)]]) requires IsPointerLikeType<T>;273 void push_back(const T& t [[clang::lifetime_capture_by(this)]]) requires IsPointerLikeType<T>;274 275 void push_back(T&& t) requires (!IsPointerLikeType<T>);276 void push_back(const T& t) requires (!IsPointerLikeType<T>);277};278 279std::string_view getLifetimeBoundView(const std::string& s [[clang::lifetimebound]]);280 281void use_container() {282 std::string local;283 284 MyVector<std::string> vector_of_string;285 vector_of_string.push_back(std::string()); // Ok.286 287 MyVector<std::string_view> vector_of_view;288 vector_of_view.push_back(std::string()); // expected-warning {{object whose reference is captured by 'vector_of_view' will be destroyed at the end of the full-expression}}289 vector_of_view.push_back(getLifetimeBoundView(std::string())); // expected-warning {{object whose reference is captured by 'vector_of_view' will be destroyed at the end of the full-expression}}290 291 MyVector<const std::string*> vector_of_pointer;292 vector_of_pointer.push_back(getLifetimeBoundPointer(std::string())); // expected-warning {{object whose reference is captured by 'vector_of_pointer' will be destroyed at the end of the full-expression}}293 vector_of_pointer.push_back(getLifetimeBoundPointer(*getLifetimeBoundPointer(std::string()))); // expected-warning {{object whose reference is captured by 'vector_of_pointer' will be destroyed at the end of the full-expression}}294 vector_of_pointer.push_back(getLifetimeBoundPointer(local));295 vector_of_pointer.push_back(getNotLifetimeBoundPointer(std::string()));296}297 298// ****************************************************************************299// Container: For user defined view types300// ****************************************************************************301struct [[gsl::Pointer()]] MyStringView : public std::string_view {302 MyStringView();303 MyStringView(std::string_view&&);304 MyStringView(const MyStringView&);305 MyStringView(const std::string&);306};307template<> struct IsPointerLikeTypeImpl<MyStringView> : std::true_type {};308 309std::optional<std::string_view> getOptionalSV();310std::optional<std::string> getOptionalS();311std::optional<MyStringView> getOptionalMySV();312MyStringView getMySV();313 314class MyStringViewNotPointer : public std::string_view {};315std::optional<MyStringViewNotPointer> getOptionalMySVNotP();316MyStringViewNotPointer getMySVNotP();317 318std::string_view getLifetimeBoundView(const std::string& s [[clang::lifetimebound]]);319std::string_view getNotLifetimeBoundView(const std::string& s);320const std::string& getLifetimeBoundString(const std::string &s [[clang::lifetimebound]]);321const std::string& getLifetimeBoundString(std::string_view sv [[clang::lifetimebound]]);322 323void use_my_view() {324 std::string local;325 MyVector<MyStringView> vector_of_my_view;326 vector_of_my_view.push_back(getMySV());327 vector_of_my_view.push_back(MyStringView{});328 vector_of_my_view.push_back(std::string_view{});329 vector_of_my_view.push_back(std::string{}); // expected-warning {{object whose reference is captured by 'vector_of_my_view' will be destroyed at the end of the full-expression}}330 vector_of_my_view.push_back(getLifetimeBoundView(std::string{})); // expected-warning {{object whose reference is captured by 'vector_of_my_view' will be destroyed at the end of the full-expression}}331 vector_of_my_view.push_back(getLifetimeBoundString(getLifetimeBoundView(std::string{}))); // expected-warning {{object whose reference is captured by 'vector_of_my_view' will be destroyed at the end of the full-expression}}332 vector_of_my_view.push_back(getNotLifetimeBoundView(getLifetimeBoundString(getLifetimeBoundView(std::string{}))));333 334 // Use with container of other view types.335 MyVector<std::string_view> vector_of_view;336 vector_of_view.push_back(getMySV());337 vector_of_view.push_back(getMySVNotP());338}339 340// ****************************************************************************341// Container: Use with std::optional<view> (owner<pointer> types)342// ****************************************************************************343void use_with_optional_view() {344 MyVector<std::string_view> vector_of_view;345 346 std::optional<std::string_view> optional_of_view;347 vector_of_view.push_back(optional_of_view.value());348 vector_of_view.push_back(getOptionalS().value()); // expected-warning {{object whose reference is captured by 'vector_of_view' will be destroyed at the end of the full-expression}}349 350 vector_of_view.push_back(getOptionalSV().value());351 vector_of_view.push_back(getOptionalMySV().value());352 vector_of_view.push_back(getOptionalMySVNotP().value());353}354} // namespace conatiners_with_different355 356// ****************************************************************************357// Capture 'temporary' views358// ****************************************************************************359namespace temporary_views {360void capture1(std::string_view s [[clang::lifetime_capture_by(x)]], std::vector<std::string_view>& x);361 362// Intended to capture the "string_view" itself363void capture2(const std::string_view& s [[clang::lifetime_capture_by(x)]], std::vector<std::string_view*>& x);364// Intended to capture the pointee of the "string_view"365void capture3(const std::string_view& s [[clang::lifetime_capture_by(x)]], std::vector<std::string_view>& x);366 367void use() {368 std::vector<std::string_view> x1;369 capture1(std::string(), x1); // expected-warning {{object whose reference is captured by 'x1' will be destroyed at the end of the full-expression}}370 capture1(std::string_view(), x1);371 372 std::vector<std::string_view*> x2;373 // Clang considers 'const std::string_view&' to refer to the owner374 // 'std::string' and not 'std::string_view'. Therefore no diagnostic here.375 capture2(std::string_view(), x2);376 capture2(std::string(), x2); // expected-warning {{object whose reference is captured by 'x2' will be destroyed at the end of the full-expression}}377 378 std::vector<std::string_view> x3;379 capture3(std::string_view(), x3);380 capture3(std::string(), x3); // expected-warning {{object whose reference is captured by 'x3' will be destroyed at the end of the full-expression}}381}382} // namespace temporary_views383 384// ****************************************************************************385// Inferring annotation for STL containers386// ****************************************************************************387namespace inferred_capture_by {388const std::string* getLifetimeBoundPointer(const std::string &s [[clang::lifetimebound]]);389const std::string* getNotLifetimeBoundPointer(const std::string &s);390 391std::string_view getLifetimeBoundView(const std::string& s [[clang::lifetimebound]]);392std::string_view getNotLifetimeBoundView(const std::string& s);393void use() {394 std::string local;395 std::vector<std::string_view> views;396 views.push_back(std::string()); // expected-warning {{object whose reference is captured by 'views' will be destroyed at the end of the full-expression}}397 views.insert(views.begin(), 398 std::string()); // expected-warning {{object whose reference is captured by 'views' will be destroyed at the end of the full-expression}}399 views.push_back(getLifetimeBoundView(std::string())); // expected-warning {{object whose reference is captured by 'views' will be destroyed at the end of the full-expression}}400 views.push_back(getNotLifetimeBoundView(std::string()));401 views.push_back(local);402 views.insert(views.end(), local);403 404 std::vector<std::string> strings;405 strings.push_back(std::string());406 strings.insert(strings.begin(), std::string());407 408 std::vector<const std::string*> pointers;409 pointers.push_back(getLifetimeBoundPointer(std::string()));410 pointers.push_back(&local);411}412 413namespace with_span {414// Templated view types.415template<typename T>416struct [[gsl::Pointer]] Span {417 Span(const std::vector<T> &V);418};419 420void use() {421 std::vector<Span<int>> spans;422 spans.push_back(std::vector<int>{1, 2, 3}); // expected-warning {{object whose reference is captured by 'spans' will be destroyed at the end of the full-expression}}423 std::vector<int> local;424 spans.push_back(local);425}426} // namespace with_span427} // namespace inferred_capture_by428 429namespace on_constructor {430struct T {431 T(const int& t [[clang::lifetime_capture_by(this)]]);432};433struct T2 {434 T2(const int& t [[clang::lifetime_capture_by(x)]], int& x);435};436struct T3 {437 T3(const T& t [[clang::lifetime_capture_by(this)]]);438};439 440int foo(const T& t);441int bar(const T& t[[clang::lifetimebound]]);442 443void test() {444 auto x = foo(T(1)); // OK. no diagnosic445 T(1); // OK. no diagnostic446 T t(1); // expected-warning {{temporary whose address is used}}447 auto y = bar(T(1)); // expected-warning {{temporary whose address is used}}448 T3 t3(T(1)); // expected-warning {{temporary whose address is used}}449 450 int a;451 T2(1, a); // expected-warning {{object whose reference is captured by}}452}453} // namespace on_constructor454 455namespace GH121391 {456 457struct Foo {};458 459template <typename T>460struct Container {461 const T& tt() [[clang::lifetimebound]];462};463template<typename T>464struct StatusOr {465 T* get() [[clang::lifetimebound]];466};467StatusOr<Container<const Foo*>> getContainer();468 469void test() {470 std::vector<const Foo*> vv;471 vv.push_back(getContainer().get()->tt()); // OK472}473 474} // namespace GH121391475