https://github.com/benedekaibas updated 
https://github.com/llvm/llvm-project/pull/213779

>From 52d7192d8ce997bac8f6e890e633b095986d1c64 Mon Sep 17 00:00:00 2001
From: benedekaibas <[email protected]>
Date: Tue, 4 Aug 2026 00:11:56 +0200
Subject: [PATCH 1/7] [analyzer] Discard non-live source frames from the
 current stack

---
 .../Checkers/LifetimeModeling.cpp             | 34 +++++++++++++++++--
 1 file changed, 31 insertions(+), 3 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp 
b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
index 4b6d76a09575a..3765f4907c9e7 100644
--- a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
@@ -52,9 +52,17 @@ static bool isDanglingStackSource(const MemRegion *Source,
         })) {
       return false;
     }
-
-    if (SF == CurrentSF || !SF->isParentOf(CurrentSF))
-      return true;
+    // Only a source whose frame is still live on the current stack can
+    // dangle. If that frame is not on the stack then the source outlives
+    // the returned value. The source is still alive when the returned value
+    // is used, so it does not dangle.
+    if (llvm::any_of(C.stackframes(), [&](const StackFrame &Frame) {
+          if (&Frame != SF)
+            return false;
+          return true;
+        }))
+      if (SF == CurrentSF || !SF->isParentOf(CurrentSF))
+        return true;
   }
   return false;
 }
@@ -117,7 +125,27 @@ void LifetimeModeling::checkPostCall(const CallEvent &Call,
         State = bindSource(State, RetVal, ArgValRegion);
     }
   }
+  /*
+  auto ViewObj = Call.getReturnValue().getAs<nonloc::LazyCompoundVal>();
+  llvm::errs() << ViewObj << "\n";
+  RetVal.dump();
+  if (!ViewObj)
+    return;
 
+  llvm::errs() << ViewObj;
+  const MemRegion *LCVRegion = ViewObj->getRegion();
+  if (!LCVRegion)
+    return;
+  llvm::errs() << LCVRegion << "\n";
+  for (const ParmVarDecl *PVD : FD->parameters()) {
+    if (PVD->hasAttr<LifetimeBoundAttr>()) {
+      unsigned Idx = PVD->getFunctionScopeIndex();
+      SVal Arg = Call.getArgSVal(Idx);
+      if (const MemRegion *ArgValRegion = Arg.getAsRegion())
+        State = bindSource(State, RetVal, ArgValRegion);
+    }
+  }
+  */
   const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
   if (IC && lifetimes::implicitObjectParamIsLifetimeBound(FD)) {
     if (const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion())

>From 29cb487f72b578fa1f093a775e0a2712b9db611a Mon Sep 17 00:00:00 2001
From: benedekaibas <[email protected]>
Date: Tue, 4 Aug 2026 13:38:31 +0200
Subject: [PATCH 2/7] Removed commented LCV test implementation.

---
 .../Checkers/LifetimeModeling.cpp             | 21 +------------------
 1 file changed, 1 insertion(+), 20 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp 
b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
index 3765f4907c9e7..33b36115a6cb0 100644
--- a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
@@ -92,6 +92,7 @@ static ProgramStateRef bindSource(ProgramStateRef State, SVal 
RetVal,
   LifetimeSourceSet Set = LSet ? *LSet : F.getEmptySet();
   Set = F.add(Set, Source);
   State = State->set<LifetimeBoundMap>(RetVal, Set);
+
   return State;
 }
 
@@ -125,27 +126,7 @@ void LifetimeModeling::checkPostCall(const CallEvent &Call,
         State = bindSource(State, RetVal, ArgValRegion);
     }
   }
-  /*
-  auto ViewObj = Call.getReturnValue().getAs<nonloc::LazyCompoundVal>();
-  llvm::errs() << ViewObj << "\n";
-  RetVal.dump();
-  if (!ViewObj)
-    return;
 
-  llvm::errs() << ViewObj;
-  const MemRegion *LCVRegion = ViewObj->getRegion();
-  if (!LCVRegion)
-    return;
-  llvm::errs() << LCVRegion << "\n";
-  for (const ParmVarDecl *PVD : FD->parameters()) {
-    if (PVD->hasAttr<LifetimeBoundAttr>()) {
-      unsigned Idx = PVD->getFunctionScopeIndex();
-      SVal Arg = Call.getArgSVal(Idx);
-      if (const MemRegion *ArgValRegion = Arg.getAsRegion())
-        State = bindSource(State, RetVal, ArgValRegion);
-    }
-  }
-  */
   const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
   if (IC && lifetimes::implicitObjectParamIsLifetimeBound(FD)) {
     if (const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion())

>From e82f2a099b072ee4e80c4a54ca1c68f6161cf0d1 Mon Sep 17 00:00:00 2001
From: benedekaibas <[email protected]>
Date: Tue, 4 Aug 2026 14:19:15 +0200
Subject: [PATCH 3/7] Add test case.

---
 .../StaticAnalyzer/Checkers/LifetimeModeling.cpp   |  6 ++++--
 clang/test/Analysis/lifetime-bound.cpp             | 14 ++++++++++++++
 2 files changed, 18 insertions(+), 2 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp 
b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
index 33b36115a6cb0..e9a4ed8559d51 100644
--- a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
@@ -56,13 +56,15 @@ static bool isDanglingStackSource(const MemRegion *Source,
     // dangle. If that frame is not on the stack then the source outlives
     // the returned value. The source is still alive when the returned value
     // is used, so it does not dangle.
+    /*
     if (llvm::any_of(C.stackframes(), [&](const StackFrame &Frame) {
           if (&Frame != SF)
             return false;
           return true;
         }))
-      if (SF == CurrentSF || !SF->isParentOf(CurrentSF))
-        return true;
+    */
+    if (SF == CurrentSF || !SF->isParentOf(CurrentSF))
+      return true;
   }
   return false;
 }
diff --git a/clang/test/Analysis/lifetime-bound.cpp 
b/clang/test/Analysis/lifetime-bound.cpp
index ef8ffeb87a8dd..8a1030add8ff7 100644
--- a/clang/test/Analysis/lifetime-bound.cpp
+++ b/clang/test/Analysis/lifetime-bound.cpp
@@ -378,3 +378,17 @@ CustomStringView dangling_sv() {
   char s[] = "dangling";
   return CustomStringView(s); // expected-warning {{address of stack memory 
associated with local variable 's' returned}} 
 }
+
+struct Chained {
+  Chained &self() [[clang::lifetimebound]] { return *this; }
+  Chained() {
+    self();
+    self(); // no-warning
+  }
+};
+
+void takes_by_value(Chained arg);
+
+void no_dangling_by_value_argument() {
+  takes_by_value(Chained()); // no-warning
+}

>From 3eff8e8af97477b9400549b13b2e7a4e6ada2fe3 Mon Sep 17 00:00:00 2001
From: Benedek Kaibas <[email protected]>
Date: Tue, 4 Aug 2026 15:05:12 +0200
Subject: [PATCH 4/7] Update
 clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp

Co-authored-by: isuckatcs <[email protected]>
---
 clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp 
b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
index e9a4ed8559d51..c008443a6ecae 100644
--- a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
@@ -58,9 +58,7 @@ static bool isDanglingStackSource(const MemRegion *Source,
     // is used, so it does not dangle.
     /*
     if (llvm::any_of(C.stackframes(), [&](const StackFrame &Frame) {
-          if (&Frame != SF)
-            return false;
-          return true;
+          return &Frame == SF;
         }))
     */
     if (SF == CurrentSF || !SF->isParentOf(CurrentSF))

>From a65c222d404f3e0247b590fad87dd01fee8a6cc5 Mon Sep 17 00:00:00 2001
From: benedekaibas <[email protected]>
Date: Tue, 4 Aug 2026 15:16:05 +0200
Subject: [PATCH 5/7] Applied nits.

---
 .../StaticAnalyzer/Checkers/LifetimeModeling.cpp    | 13 +++++--------
 1 file changed, 5 insertions(+), 8 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp 
b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
index c008443a6ecae..2f1eb9f965310 100644
--- a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
@@ -56,13 +56,11 @@ static bool isDanglingStackSource(const MemRegion *Source,
     // dangle. If that frame is not on the stack then the source outlives
     // the returned value. The source is still alive when the returned value
     // is used, so it does not dangle.
-    /*
-    if (llvm::any_of(C.stackframes(), [&](const StackFrame &Frame) {
-          return &Frame == SF;
-        }))
-    */
-    if (SF == CurrentSF || !SF->isParentOf(CurrentSF))
-      return true;
+    if (llvm::any_of(C.stackframes(),
+                     [&](const StackFrame &Frame) { return &Frame == SF; }))
+
+      if (SF == CurrentSF || !SF->isParentOf(CurrentSF))
+        return true;
   }
   return false;
 }
@@ -92,7 +90,6 @@ static ProgramStateRef bindSource(ProgramStateRef State, SVal 
RetVal,
   LifetimeSourceSet Set = LSet ? *LSet : F.getEmptySet();
   Set = F.add(Set, Source);
   State = State->set<LifetimeBoundMap>(RetVal, Set);
-
   return State;
 }
 

>From 30cce49530f3eb7b5731afcd19ddd9529aebed07 Mon Sep 17 00:00:00 2001
From: benedekaibas <[email protected]>
Date: Tue, 4 Aug 2026 20:11:25 +0200
Subject: [PATCH 6/7] Add explanation.

---
 clang/test/Analysis/lifetime-bound.cpp | 17 +++++++++++------
 1 file changed, 11 insertions(+), 6 deletions(-)

diff --git a/clang/test/Analysis/lifetime-bound.cpp 
b/clang/test/Analysis/lifetime-bound.cpp
index 8a1030add8ff7..30e779d567545 100644
--- a/clang/test/Analysis/lifetime-bound.cpp
+++ b/clang/test/Analysis/lifetime-bound.cpp
@@ -379,16 +379,21 @@ CustomStringView dangling_sv() {
   return CustomStringView(s); // expected-warning {{address of stack memory 
associated with local variable 's' returned}} 
 }
 
-struct Chained {
-  Chained &self() [[clang::lifetimebound]] { return *this; }
-  Chained() {
+// `self()` is annotated [[clang::lifetimebound]], so its return is bound to
+// *this. The BoundToSelf instance is built as a by-value argument temporary,
+// so its frame is not live on the stack when self() returns. 
+struct BoundToSelf {
+  BoundToSelf &self() [[clang::lifetimebound]] { return *this; } // no-warning
+  BoundToSelf() {
+    self();
     self();
-    self(); // no-warning
   }
 };
 
-void takes_by_value(Chained arg);
+void takes_by_value(BoundToSelf arg);
 
 void no_dangling_by_value_argument() {
-  takes_by_value(Chained()); // no-warning
+  // The BoundToSelf temporary's frame is not live on the stack when `self()` 
returns.
+  // The returned reference does not dangle.
+  takes_by_value(BoundToSelf());
 }

>From ae0ea6cd125c440162cd3089e63fbc640e54aafe Mon Sep 17 00:00:00 2001
From: benedekaibas <[email protected]>
Date: Wed, 5 Aug 2026 13:45:43 +0200
Subject: [PATCH 7/7] Use is_contained with make_pointer_range instead of
 any_of.

---
 clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp 
b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
index 2f1eb9f965310..3dc0df169e4c7 100644
--- a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
@@ -56,11 +56,10 @@ static bool isDanglingStackSource(const MemRegion *Source,
     // dangle. If that frame is not on the stack then the source outlives
     // the returned value. The source is still alive when the returned value
     // is used, so it does not dangle.
-    if (llvm::any_of(C.stackframes(),
-                     [&](const StackFrame &Frame) { return &Frame == SF; }))
+    return is_contained(make_pointer_range(C.stackframes()), SF);
 
-      if (SF == CurrentSF || !SF->isParentOf(CurrentSF))
-        return true;
+    if (SF == CurrentSF || !SF->isParentOf(CurrentSF))
+      return true;
   }
   return false;
 }

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

Reply via email to