[llvm-branch-commits] [clang] [LifetimeSafety] Implement support for lifetimebound attribute (PR #158489)

Gábor Horváth via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Tue Sep 23 09:12:29 PDT 2025


================
@@ -371,3 +371,152 @@ void no_error_if_dangle_then_rescue_gsl() {
   v = safe;    // 'v' is "rescued" before use by reassigning to a valid object.
   v.use();     // This is safe.
 }
+
+
+//===----------------------------------------------------------------------===//
+// Lifetimebound Attribute Tests
+//===----------------------------------------------------------------------===//
+
+View Identity(View v [[clang::lifetimebound]]);
+View Choose(bool cond, View a [[clang::lifetimebound]], View b [[clang::lifetimebound]]);
+MyObj* GetPointer(const MyObj& obj [[clang::lifetimebound]]);
+
+struct [[gsl::Pointer()]] LifetimeBoundView {
+  LifetimeBoundView();
+  LifetimeBoundView(const MyObj& obj [[clang::lifetimebound]]);
+  LifetimeBoundView pass() [[clang::lifetimebound]] { return *this; }
+  operator View() const [[clang::lifetimebound]];
+};
+
+void lifetimebound_simple_function() {
+  View v;
+  {
+    MyObj obj;
+    v = Identity(obj); // expected-warning {{object whose reference is captured does not live long enough}}
+  }                    // expected-note {{destroyed here}}
+  v.use();             // expected-note {{later used here}}
+}
+
+void lifetimebound_multiple_args_definite() {
+  View v;
+  {
+    MyObj obj1, obj2;
+    v = Choose(true,
+               obj1,  // expected-warning {{object whose reference is captured does not live long enough}}
+               obj2); // expected-warning {{object whose reference is captured does not live long enough}}
+  }                              // expected-note 2 {{destroyed here}}
+  v.use();                       // expected-note 2 {{later used here}}
+}
+
+void lifetimebound_multiple_args_potential(bool cond) {
+  MyObj safe;
+  View v = safe;
+  {
+    MyObj obj1;
+    if (cond) {
+      MyObj obj2;
+      v = Choose(true, 
+                 obj1,             // expected-warning {{object whose reference is captured may not live long enough}}
+                 obj2);            // expected-warning {{object whose reference is captured may not live long enough}}
+    }                              // expected-note {{destroyed here}}
+  }                                // expected-note {{destroyed here}}
+  v.use();                         // expected-note 2 {{later used here}}
+}
+
+View SelectFirst(View a [[clang::lifetimebound]], View b);
+void lifetimebound_mixed_args() {
+  View v;
+  {
+    MyObj obj1, obj2;
+    v = SelectFirst(obj1,        // expected-warning {{object whose reference is captured does not live long enough}}
+                    obj2);
+  }                              // expected-note {{destroyed here}}
+  v.use();                       // expected-note {{later used here}}
+}
+
+void lifetimebound_member_function() {
+  LifetimeBoundView lbv, lbv2;
+  {
+    MyObj obj;
+    lbv = obj;        // expected-warning {{object whose reference is captured does not live long enough}}
+    lbv2 = lbv.pass();
+  }                   // expected-note {{destroyed here}}
+  View v = lbv2;      // expected-note {{later used here}}
+  v.use();
+}
+
+void lifetimebound_conversion_operator() {
+  View v;
+  {
+    MyObj obj;
+    LifetimeBoundView lbv = obj; // expected-warning {{object whose reference is captured does not live long enough}}
+    v = lbv;                     // Conversion operator is lifetimebound
+  }                              // expected-note {{destroyed here}}
+  v.use();                       // expected-note {{later used here}}
+}
+
+void lifetimebound_chained_calls() {
+  View v;
+  {
+    MyObj obj;
+    v = Identity(Identity(Identity(obj))); // expected-warning {{object whose reference is captured does not live long enough}}
+  }                                        // expected-note {{destroyed here}}
+  v.use();                                 // expected-note {{later used here}}
+}
+
+void lifetimebound_with_pointers() {
+  MyObj* ptr;
+  {
+    MyObj obj;
+    ptr = GetPointer(obj); // expected-warning {{object whose reference is captured does not live long enough}}
+  }                        // expected-note {{destroyed here}}
+  (void)*ptr;              // expected-note {{later used here}}
+}
+
+void lifetimebound_no_error_safe_usage() {
+  MyObj obj;
+  View v1 = Identity(obj);      // No warning - obj lives long enough
+  View v2 = Choose(true, v1, Identity(obj)); // No warning - all args are safe
+  v2.use();                     // Safe usage
+}
+
+void lifetimebound_partial_safety(bool cond) {
+  MyObj safe_obj;
+  View v = safe_obj;
+  
+  if (cond) {
+    MyObj temp_obj;
+    v = Choose(true, 
+               safe_obj,
+               temp_obj); // expected-warning {{object whose reference is captured may not live long enough}}
+  }                       // expected-note {{destroyed here}}
+  v.use();                // expected-note {{later used here}}
+}
+
+// FIXME: Creating reference from lifetimebound call doesn't propagate loans.
+const MyObj& GetObject(View v [[clang::lifetimebound]]);
+void lifetimebound_return_reference() {
+  View v;
+  const MyObj* ptr;
+  {
+    MyObj obj;
+    View temp_v = obj;
+    const MyObj& ref = GetObject(temp_v);
+    ptr = &ref;
+  }
+  (void)*ptr;
+}
+
+// FIXME: No warning for non gsl::Pointer types. Origin tracking is only supported for pointer types.
+struct LifetimeBoundCtor {
+  LifetimeBoundCtor();
+  LifetimeBoundCtor(const MyObj& obj [[clang::lifetimebound]]);
+};
+void lifetimebound_ctor() {
+  LifetimeBoundCtor v;
+  {
+    MyObj obj;
+    v = obj;
+  }
+  (void)v;
+}
----------------
Xazax-hun wrote:

Nit: missing new line. 

https://github.com/llvm/llvm-project/pull/158489


More information about the llvm-branch-commits mailing list