================
@@ -793,3 +793,108 @@ void test13() {
 }
 
 } // namespace GH100526
+
+namespace lifetime_capture_by {
+struct S {
+  const int *x;
+  void captureInt(const int&x [[clang::lifetime_capture_by(this)]]) { this->x 
= &x; }
+  void captureSV(std::string_view sv [[clang::lifetime_capture_by(this)]]);
+};
+///////////////////////////
+// Detect dangling cases.
+///////////////////////////
+void captureInt(const int&x [[clang::lifetime_capture_by(s)]], S&s);
+void captureRValInt(int&&x [[clang::lifetime_capture_by(s)]], S&s);
+void noCaptureInt(int x [[clang::lifetime_capture_by(s)]], S&s);
+std::string_view substr(const std::string& s [[clang::lifetimebound]]);
+std::string_view strcopy(const std::string& s);
+void captureSV(std::string_view x [[clang::lifetime_capture_by(s)]], S&s);
+void captureRValSV(std::string_view&& x [[clang::lifetime_capture_by(s)]], 
S&s);
+void noCaptureSV(std::string_view x, S&s);
+void captureS(const std::string& x [[clang::lifetime_capture_by(s)]], S&s);
+void captureRValS(std::string&& x [[clang::lifetime_capture_by(s)]], S&s);
+const std::string* getPointerLB(const std::string& s[[clang::lifetimebound]]);
+const std::string* getPointerNoLB(const std::string& s);
+void capturePointer(const std::string* x [[clang::lifetime_capture_by(s)]], 
S&s);
+struct ThisIsCaptured {
+  void capture(S& s) [[clang::lifetime_capture_by(s)]];
+  void bar(S& s) [[clang::lifetime_capture_by(abcd)]]; // expected-error 
{{'lifetime_capture_by' attribute argument 'abcd' is not a known function 
parameter}}
+  void baz(S& s) [[clang::lifetime_capture_by(this)]]; // expected-error 
{{'lifetime_capture_by' argument references itself}}
+};
+void use() {
+  std::string_view local_sv;
+  std::string local_s;
+  S s;
+  // Capture an 'int'.
+  int local;
+  captureInt(1, // expected-warning {{object captured by 's' will be destroyed 
at the end of the full-expression}}
+            s);
+  captureRValInt(1, s); // expected-warning {{object captured by 's'}}
+  captureInt(local, s);
+  noCaptureInt(1, s);
+  noCaptureInt(local, s);
+
+  // Capture lifetimebound pointer.
+  capturePointer(getPointerLB(std::string()), s); // expected-warning {{object 
captured by 's'}}
+  capturePointer(getPointerLB(*getPointerLB(std::string())), s); // 
expected-warning {{object captured by 's'}}
+  capturePointer(getPointerNoLB(std::string()), s);
+
+  // Capture using std::string_view.
+  captureSV(local_sv, s);
+  captureSV(std::string(), // expected-warning {{object captured by 's'}}
+            s);
+  captureSV(substr(
+      std::string() // expected-warning {{object captured by 's'}}
+      ), s);
+  captureSV(substr(local_s), s);
+  captureSV(strcopy(std::string()), s);
+  captureRValSV(std::move(local_sv), s);
+  captureRValSV(std::string(), s); // expected-warning {{object captured by 
's'}}
+  captureRValSV(std::string_view{"abcd"}, s);
+  captureRValSV(substr(local_s), s);
+  captureRValSV(substr(std::string()), s); // expected-warning {{object 
captured by 's'}}
+  captureRValSV(strcopy(std::string()), s);
+  noCaptureSV(local_sv, s);
+  noCaptureSV(std::string(), s);
+  noCaptureSV(substr(std::string()), s);
+
+  // Capture using std::string.
+  captureS(std::string(), s); // expected-warning {{object captured by 's'}}
+  captureS(local_s, s);
+  captureRValS(std::move(local_s), s);
+  captureRValS(std::string(), s); // expected-warning {{object captured by 
's'}}
+
+  // Member functions.
+  s.captureInt(1); // expected-warning {{object captured by 's'}}
+  s.captureSV(std::string()); // expected-warning {{object captured by 's'}}
+  s.captureSV(substr(std::string())); // expected-warning {{object captured by 
's'}}
+  s.captureSV(strcopy(std::string()));
+
+  // 'this' is captured.
+  ThisIsCaptured{}.capture(s); // expected-warning {{object captured by 's'}}
+  ThisIsCaptured TIS;
+  TIS.capture(s);
+}
+class [[gsl::Pointer()]] my_string_view : public std::string_view {};
+class my_string_view_not_pointer : public std::string_view {};
+std::optional<std::string_view> getOptionalSV();
+std::optional<std::string> getOptionalS();
+std::optional<my_string_view> getOptionalMySV();
+std::optional<my_string_view_not_pointer> getOptionalMySVNotP();
+my_string_view getMySV();
+my_string_view_not_pointer getMySVNotP();
+
+template<class T>
+struct MySet {
+void insert(T&& t [[clang::lifetime_capture_by(this)]]);
+void insert(const T& t [[clang::lifetime_capture_by(this)]]);
+};
+void user_defined_containers() {
+  MySet<int> set_of_int;
+  set_of_int.insert(1); // expected-warning {{object captured by 'set_of_int' 
will be destroyed}}
----------------
higher-performance wrote:

The fundamental problem here (and the reason you see an expectation mismatch) 
is that `Container` is misnamed. It's an unconstrained templated type, so we 
may as well call it `T` or `U` -- it could be anything. It seems the intention 
here was to have it be `Range` (a range may or may not actually contain the 
elements), and once we name it that, it becomes obvious that we can't 
`lifetimebound` it -- because that constructor could just be copying the range.

So (1) seems like the correct solution to me. It's certainly verbose, but if 
that's the concern then the fix would be to find some way to make 
`lifetimebound` conditional (maybe like `noexcept(noexcept(...))`?) rather than 
by changing the analysis.

https://github.com/llvm/llvm-project/pull/115921
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to