https://github.com/usx95 created 
https://github.com/llvm/llvm-project/pull/223635

None

>From b2a0e07b3df27e5de641e937fe3e97a263bdcc1f Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena <[email protected]>
Date: Tue, 15 Sep 2026 08:56:31 +0000
Subject: [PATCH] owner-constructor-inference

---
 .../LifetimeSafety/LifetimeAnnotations.h      |  5 +++
 clang/lib/Analysis/LifetimeSafety/Checker.cpp |  8 +++-
 .../LifetimeSafety/LifetimeAnnotations.cpp    |  5 +++
 clang/test/Sema/LifetimeSafety/safety.cpp     | 38 +++++++++++++++++++
 4 files changed, 54 insertions(+), 2 deletions(-)

diff --git 
a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h
index b7ee742445654..733cb318b35fd 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h
@@ -113,6 +113,11 @@ bool isGslPointerType(QualType QT);
 bool isGslOwnerType(QualType QT);
 bool isGslOwnerType(const CXXRecordDecl *RD);
 
+// Tells whether the given constructor belongs to an Owner type and the 
parameter
+// is of pointer type. This is useful to disable inference on owning pointers
+// being captured by owners.
+bool isOwnerPtrCtor(const CXXConstructorDecl *Ctor, const ParmVarDecl *PVD);
+
 // Returns true if the given method is std::unique_ptr::release().
 // This is treated as a move in lifetime analysis to avoid false-positives
 // when ownership is manually transferred.
diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp 
b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
index a358215a295d1..c49b9102ef869 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -153,8 +153,12 @@ class LifetimeChecker {
         if (auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF))
           AnnotationWarningsMap.try_emplace(PVD, ReturnEsc->getReturnExpr());
         else if (auto *FieldEsc = dyn_cast<FieldEscapeFact>(OEF);
-                 FieldEsc && isa<CXXConstructorDecl>(FD))
-          AnnotationWarningsMap.try_emplace(PVD, FieldEsc->getFieldDecl());
+                 FieldEsc && isa<CXXConstructorDecl>(FD)) {
+          // Disable inference for pointers being captured by an owner type,
+          // as owners typically consume these pointers rather than borrow 
them.
+          if (!isOwnerPtrCtor(dyn_cast<CXXConstructorDecl>(FD), PVD))
+            AnnotationWarningsMap.try_emplace(PVD, FieldEsc->getFieldDecl());
+        }
       }
       // TODO: Suggest lifetime_capture_by(this) for parameter escaping to a
       // field!
diff --git a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp 
b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
index 725e6dcd5ebdd..98b2a5a867752 100644
--- a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
@@ -387,6 +387,11 @@ bool isGslOwnerType(const CXXRecordDecl *RD) {
   return isRecordWithAttr<OwnerAttr>(RD);
 }
 
+bool isOwnerPtrCtor(const CXXConstructorDecl *Ctor, const ParmVarDecl *PVD) {
+  return Ctor && PVD->getType()->isPointerType() &&
+         isGslOwnerType(Ctor->getParent());
+}
+
 static StringRef getName(const CXXRecordDecl &RD) {
   if (const auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(&RD))
     return CTSD->getSpecializedTemplate()->getName();
diff --git a/clang/test/Sema/LifetimeSafety/safety.cpp 
b/clang/test/Sema/LifetimeSafety/safety.cpp
index 851f558c85dcd..c87acc9c58aeb 100644
--- a/clang/test/Sema/LifetimeSafety/safety.cpp
+++ b/clang/test/Sema/LifetimeSafety/safety.cpp
@@ -4283,3 +4283,41 @@ void test_cyclic_cfg(int n) {
   }         // expected-note {{local variable 'a' is destroyed here}}
   v.use();  // expected-note {{later used here}}
 }
+
+namespace TakeOwnershipTests {
+std::unique_ptr<int> takeOwnership(int* i) { return std::unique_ptr<int>(i); }
+
+void doubleFree() {
+    std::unique_ptr<int> up;
+    {
+        int a = 42;
+        // No use-after-scope warning here.
+        // This is a double-free due to multiple ownership which is currently 
not supported.
+        up = takeOwnership(&a);
+    }
+    (void)up.get();
+}
+
+void ok() {
+    std::unique_ptr<int> up;
+    {
+        int* a = new int(42);
+        up = takeOwnership(a); // Ok.
+    }
+    (void)up.get();
+}
+
+void take(std::unique_ptr<int> o);
+
+void foo() {
+    int* p;
+    std::unique_ptr<int> up;
+    {
+        std::unique_ptr<int> o = std::unique_ptr<int>(new int(42));
+        p = o.get();        // expected-warning {{local variable 'o' may not 
live long enough}} \
+                            // expected-note {{result of call to 'get' aliases 
the storage of local variable 'o' because the implicit object parameter is 
inferred as lifetimebound}}
+        up = std::move(o);  // expected-note {{potentially moved here}}
+    }                       // expected-note {{local variable 'o' is destroyed 
here}}
+    (void)*p;               // expected-note {{later used here}}
+}
+} // namespace TakeOwnershipTests

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to