https://github.com/suoyuan666 updated 
https://github.com/llvm/llvm-project/pull/204592

>From 66720433b6f0629878dc24f2bd3443b2ddaea33e Mon Sep 17 00:00:00 2001
From: Yuan Suo <[email protected]>
Date: Wed, 17 Jun 2026 18:04:06 +0800
Subject: [PATCH 01/15] [LifetimeSafety] Add multi-block support to
 buildOriginFlowChain

After introducing `buildOriginFlowChain` to use-after-scope diagnostics,
it should support multi-block analysis. This also allows it to be reused
by other diagnostics.

In some loops, UseFact may appear before OriginFlowFact:

```cpp
void for_loop_use_before_loop_body(MyObj safe) {
  MyObj* p = &safe;
  for (int i = 0; i < 1; ++i) {
    (void)*p;
    MyObj s;
    p = &s;
  }
  (void)*p;
}
```

So, I remove the `StartPoint` parameter to correctly find the `OriginID`.

Signed-off-by: Yuan Suo <[email protected]>
---
 .../Analysis/Analyses/LifetimeSafety/Facts.h  |  1 +
 .../Analyses/LifetimeSafety/LoanPropagation.h |  7 +-
 clang/lib/Analysis/LifetimeSafety/Checker.cpp |  6 +-
 clang/lib/Analysis/LifetimeSafety/Facts.cpp   | 15 ++-
 .../LifetimeSafety/LoanPropagation.cpp        | 99 ++++++++++++++-----
 clang/lib/Sema/SemaLifetimeSafety.h           |  4 +-
 clang/test/Sema/LifetimeSafety/safety-c.c     |  4 +-
 clang/test/Sema/LifetimeSafety/safety.cpp     | 24 +++--
 .../unittests/Analysis/LifetimeSafetyTest.cpp |  3 +-
 9 files changed, 119 insertions(+), 44 deletions(-)

diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
index 88b509e1b94df..e0ddfe95b00b2 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
@@ -374,6 +374,7 @@ class FactManager {
   /// Retrieves all the facts in the block containing Program Point P.
   /// \note This is intended for testing only.
   llvm::ArrayRef<const Fact *> getBlockContaining(ProgramPoint P) const;
+  std::optional<size_t> getBlockID(ProgramPoint P) const;
 
   unsigned getNumFacts() const { return NextFactID.Value; }
 
diff --git 
a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
index 724c6eee7d3c2..42d7df0838f35 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
@@ -16,6 +16,7 @@
 #define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_LOAN_PROPAGATION_H
 
 #include "clang/Analysis/Analyses/LifetimeSafety/Facts.h"
+#include "clang/Analysis/Analyses/PostOrderCFGView.h"
 #include "clang/Analysis/AnalysisDeclContext.h"
 #include "clang/Analysis/CFG.h"
 #include "llvm/ADT/ImmutableMap.h"
@@ -46,10 +47,12 @@ class LoanPropagationAnalysis {
   /// where the loan was originally issued.
   llvm::SmallVector<OriginID>
   buildOriginFlowChain(ProgramPoint StartPoint, const OriginID StartOID,
-                       const LoanID TargetLoan) const;
+                       const LoanID TargetLoan,
+                       const PostOrderCFGView *POV) const;
 
   llvm::SmallVector<OriginID>
-  buildOriginFlowChain(const UseFact *UF, const LoanID TargetLoan) const;
+  buildOriginFlowChain(const UseFact *UF, const LoanID TargetLoan,
+                       const PostOrderCFGView *POV) const;
 
 private:
   class Impl;
diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp 
b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
index c258a1dc3596c..17c0c1e4478ea 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -68,6 +68,7 @@ class LifetimeChecker {
   LifetimeSafetySemaHelper *SemaHelper;
   ASTContext &AST;
   const Decl *FD;
+  const PostOrderCFGView *POV;
 
   static SourceLocation
   GetFactLoc(llvm::PointerUnion<const UseFact *, const OriginEscapesFact *> F) 
{
@@ -90,7 +91,8 @@ class LifetimeChecker {
                   LifetimeSafetySemaHelper *SemaHelper)
       : LoanPropagation(LoanPropagation), MovedLoans(MovedLoans),
         LiveOrigins(LiveOrigins), FactMgr(FM), SemaHelper(SemaHelper),
-        AST(ADC.getASTContext()), FD(ADC.getDecl()) {
+        AST(ADC.getASTContext()), FD(ADC.getDecl()),
+        POV(ADC.getAnalysis<PostOrderCFGView>()) {
     for (const CFGBlock *B : *ADC.getAnalysis<PostOrderCFGView>())
       for (const Fact *F : FactMgr.getFacts(B))
         if (const auto *EF = F->getAs<ExpireFact>())
@@ -270,7 +272,7 @@ class LifetimeChecker {
           // Scope-based expiry (use-after-scope).
           SemaHelper->reportUseAfterScope(
               IssueExpr, UF->getUseExpr(), MovedExpr, ExpiryLoc,
-              getExprChain(LoanPropagation.buildOriginFlowChain(UF, LID)));
+              getExprChain(LoanPropagation.buildOriginFlowChain(UF, LID, 
POV)));
 
       } else if (const auto *OEF =
                      CausingFact.dyn_cast<const OriginEscapesFact *>()) {
diff --git a/clang/lib/Analysis/LifetimeSafety/Facts.cpp 
b/clang/lib/Analysis/LifetimeSafety/Facts.cpp
index 3d7fbcdacc830..823f1f686f904 100644
--- a/clang/lib/Analysis/LifetimeSafety/Facts.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Facts.cpp
@@ -145,12 +145,17 @@ void FactManager::dump(const CFG &Cfg, 
AnalysisDeclContext &AC) const {
 
 llvm::ArrayRef<const Fact *>
 FactManager::getBlockContaining(ProgramPoint P) const {
-  for (const auto &BlockToFactsVec : BlockToFacts) {
-    for (const Fact *F : BlockToFactsVec)
-      if (F == P)
-        return BlockToFactsVec;
-  }
+  std::optional<size_t> BlockIndex = getBlockID(P);
+  if (BlockIndex)
+    return BlockToFacts[BlockIndex.value()];
   return {};
 }
 
+std::optional<size_t> FactManager::getBlockID(ProgramPoint P) const {
+  for (size_t i = 0; i < BlockToFacts.size(); ++i)
+    for (const Fact *F : BlockToFacts[i])
+      if (F == P)
+        return i;
+  return std::nullopt;
+}
 } // namespace clang::lifetimes::internal
diff --git a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp 
b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
index a67b1b3c0f826..8e2d9a6577427 100644
--- a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
@@ -130,6 +130,11 @@ struct Lattice {
   }
 };
 
+struct BuildOriginFlowChainResult {
+  const llvm::SmallVector<OriginID> OriginFlowChain;
+  const bool Complete;
+};
+
 class AnalysisImpl
     : public DataflowAnalysis<AnalysisImpl, Lattice, Direction::Forward> {
 public:
@@ -204,22 +209,72 @@ class AnalysisImpl
 
   llvm::SmallVector<OriginID>
   buildOriginFlowChain(ProgramPoint StartPoint, const OriginID StartOID,
-                       const LoanID TargetLoan) const {
+                       const LoanID TargetLoan,
+                       const PostOrderCFGView *POV) const {
     assert(getLoans(StartOID, StartPoint).contains(TargetLoan) &&
            "TargetLoan must be present in the StartOID at the StartPoint");
 
+    std::optional<OriginID> FinalOID;
+    llvm::DenseMap<OriginID, OriginID> VistedOriginIDs;
+
+    const auto OriginFlowChainFilter = [&VistedOriginIDs](OriginID FinalOID) {
+      llvm::SmallVector<OriginID> OriginFlowChain;
+      while (true) {
+        OriginFlowChain.push_back(FinalOID);
+        const auto NextOriginID = VistedOriginIDs.find(FinalOID);
+        if (NextOriginID == VistedOriginIDs.end())
+          break;
+        FinalOID = NextOriginID->second;
+      }
+      return OriginFlowChain;
+    };
+
+    const auto InsertVistedOriginIDs =
+        [&VistedOriginIDs, &FinalOID](llvm::ArrayRef<OriginID> OriginFlowChain,
+                                      OriginID &StartOID) {
+          if (!VistedOriginIDs.empty())
+            VistedOriginIDs.insert({OriginFlowChain[0], StartOID});
+
+          for (size_t i = 0; i < OriginFlowChain.size() - 1; ++i)
+            VistedOriginIDs.insert(
+                {OriginFlowChain[i + 1], OriginFlowChain[i]});
+
+          StartOID = OriginFlowChain.back();
+          FinalOID = StartOID;
+        };
+
+    std::optional<size_t> BlockID = FactMgr.getBlockID(StartPoint);
+    assert(BlockID.has_value());
+    const auto StartIt = llvm::find_if(*POV, [&BlockID](const CFGBlock *Block) 
{
+      return Block->getBlockID() == BlockID;
+    });
+
+    OriginID CurrOID = StartOID;
+    for (const CFGBlock *B :
+         llvm::reverse(llvm::make_range(POV->begin(), StartIt + 1))) {
+      BuildOriginFlowChainResult BuildResult =
+          buildOriginFlowChain(B, CurrOID, TargetLoan);
+      if (!BuildResult.OriginFlowChain.empty())
+        InsertVistedOriginIDs(BuildResult.OriginFlowChain, CurrOID);
+      if (BuildResult.Complete)
+        return OriginFlowChainFilter(*FinalOID);
+    }
+
+    llvm_unreachable(
+        "buildOriginFlowChain should return at BuildResult.Complete");
+  }
+
+  BuildOriginFlowChainResult
+  buildOriginFlowChain(const CFGBlock *Block, const OriginID StartOID,
+                       const LoanID TargetLoan) const {
     OriginID CurrOID = StartOID;
     llvm::SmallVector<OriginID> OriginFlowChain;
-    llvm::ArrayRef<const Fact *> Facts = 
FactMgr.getBlockContaining(StartPoint);
-    const auto *StartIt = llvm::find(Facts, StartPoint);
-    assert(StartIt != Facts.end());
 
-    for (const Fact *F :
-         llvm::reverse(llvm::make_range(Facts.begin(), StartIt))) {
+    for (const Fact *F : llvm::reverse(FactMgr.getFacts(Block))) {
       if (const auto *IF = F->getAs<IssueFact>())
         if (IF->getLoanID() == TargetLoan) {
           assert(IF->getOriginID() == CurrOID);
-          return OriginFlowChain;
+          return {OriginFlowChain, true};
         }
 
       const auto *OFF = F->getAs<OriginFlowFact>();
@@ -235,20 +290,17 @@ class AnalysisImpl
       CurrOID = SrcOriginID;
     }
 
-    // FIXME: Ideally, this return is unreachable and should be an assert
-    // because we expect to always finish at an IssueFact. But since current
-    // traversal is limited to a single CFG block, multi-block OriginFlowChain
-    // construction might miss the IssueFact. We should add llvm_unreachable
-    // here once multi-block support is implemented.
-    return {};
+    return {OriginFlowChain, false};
   }
 
   llvm::SmallVector<OriginID>
-  buildOriginFlowChain(const UseFact *UF, const LoanID TargetLoan) const {
+  buildOriginFlowChain(const UseFact *UF, const LoanID TargetLoan,
+                       const PostOrderCFGView *POV) const {
     for (const OriginList *Cur = UF->getUsedOrigins(); Cur;
          Cur = Cur->peelOuterOrigin())
       if (getLoans(Cur->getOuterOriginID(), UF).contains(TargetLoan))
-        return buildOriginFlowChain(UF, Cur->getOuterOriginID(), TargetLoan);
+        return buildOriginFlowChain(UF, Cur->getOuterOriginID(), TargetLoan,
+                                    POV);
 
     return {};
   }
@@ -303,16 +355,15 @@ LoanSet LoanPropagationAnalysis::getLoans(OriginID OID, 
ProgramPoint P) const {
   return PImpl->getLoans(OID, P);
 }
 
-llvm::SmallVector<OriginID>
-LoanPropagationAnalysis::buildOriginFlowChain(ProgramPoint StartPoint,
-                                              const OriginID StartOID,
-                                              const LoanID TargetLoan) const {
-  return PImpl->buildOriginFlowChain(StartPoint, StartOID, TargetLoan);
+llvm::SmallVector<OriginID> LoanPropagationAnalysis::buildOriginFlowChain(
+    ProgramPoint StartPoint, const OriginID StartOID, const LoanID TargetLoan,
+    const PostOrderCFGView *POV) const {
+  return PImpl->buildOriginFlowChain(StartPoint, StartOID, TargetLoan, POV);
 }
 
-llvm::SmallVector<OriginID>
-LoanPropagationAnalysis::buildOriginFlowChain(const UseFact *UF,
-                                              const LoanID TargetLoan) const {
-  return PImpl->buildOriginFlowChain(UF, TargetLoan);
+llvm::SmallVector<OriginID> LoanPropagationAnalysis::buildOriginFlowChain(
+    const UseFact *UF, const LoanID TargetLoan,
+    const PostOrderCFGView *POV) const {
+  return PImpl->buildOriginFlowChain(UF, TargetLoan, POV);
 }
 } // namespace clang::lifetimes::internal
diff --git a/clang/lib/Sema/SemaLifetimeSafety.h 
b/clang/lib/Sema/SemaLifetimeSafety.h
index 1047aecf863fb..a1313816aae3a 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -566,10 +566,10 @@ class LifetimeSafetySemaHelperImpl : public 
LifetimeSafetySemaHelper {
     if (OriginExprChain.empty())
       return;
 
-    const Expr *LastExpr = OriginExprChain.back();
+    const Expr *LastExpr = OriginExprChain.front();
     std::string IssueStr = getDiagSubjectDescription(LastExpr);
 
-    for (const Expr *CurrExpr : reverse(OriginExprChain.drop_back())) {
+    for (const Expr *CurrExpr : OriginExprChain.drop_front()) {
       if (!shouldShowInAliasChain(CurrExpr, LastExpr))
         continue;
       S.Diag(CurrExpr->getBeginLoc(),
diff --git a/clang/test/Sema/LifetimeSafety/safety-c.c 
b/clang/test/Sema/LifetimeSafety/safety-c.c
index e9443899c9935..42adbbb3f0628 100644
--- a/clang/test/Sema/LifetimeSafety/safety-c.c
+++ b/clang/test/Sema/LifetimeSafety/safety-c.c
@@ -93,7 +93,9 @@ void conditional_operator_lifetimebound(int cond) {
   int *p;
   {
     int a, b;
-    p = identity(cond ? &a    // expected-warning {{local variable 'a' does 
not live long enough}}
+    p = identity(cond ? &a    // expected-warning {{local variable 'a' does 
not live long enough}} \
+                              // expected-note {{result of call to 'identity' 
aliases the storage of local variable 'a'}} \
+                              // expected-note {{result of call to 'identity' 
aliases the storage of local variable 'b'}}
                       : &b);  // expected-warning {{local variable 'b' does 
not live long enough}}
   }                           // expected-note {{local variable 'a' is 
destroyed here}} \
                               // expected-note {{local variable 'b' is 
destroyed here}}
diff --git a/clang/test/Sema/LifetimeSafety/safety.cpp 
b/clang/test/Sema/LifetimeSafety/safety.cpp
index abd3d9c61b784..0a6bea96af45d 100644
--- a/clang/test/Sema/LifetimeSafety/safety.cpp
+++ b/clang/test/Sema/LifetimeSafety/safety.cpp
@@ -448,7 +448,7 @@ void loan_from_previous_iteration(MyObj safe, bool 
condition) {
     p = &x;     // expected-warning {{does not live long enough}}
 
     if (condition)
-      q = p;
+      q = p;    // expected-note {{local variable 'p' aliases the storage of 
local variable 'x'}}
     (void)*p;
     (void)*q;   // expected-note {{later used here}}
   }             // expected-note {{local variable 'x' is destroyed here}}
@@ -846,7 +846,8 @@ void lifetimebound_multiple_args_potential(bool cond) {
     MyObj obj1;
     if (cond) {
       MyObj obj2;
-      v = Choose(true,
+      v = Choose(true,             // expected-note {{result of call to 
'Choose' aliases the storage of local variable 'obj1'}} \
+                                   // expected-note {{result of call to 
'Choose' aliases the storage of local variable 'obj2'}}
                  obj1,             // expected-warning {{local variable 'obj1' 
does not live long enough}}
                  obj2);            // expected-warning {{local variable 'obj2' 
does not live long enough}}
     }                              // expected-note {{local variable 'obj2' is 
destroyed here}}
@@ -940,7 +941,7 @@ void lifetimebound_partial_safety(bool cond) {
   
   if (cond) {
     MyObj temp_obj;
-    v = Choose(true, 
+    v = Choose(true,      // expected-note {{result of call to 'Choose' 
aliases the storage of local variable 'temp_obj'}}
                safe_obj,
                temp_obj); // expected-warning {{local variable 'temp_obj' does 
not live long enough}}
   }                       // expected-note {{local variable 'temp_obj' is 
destroyed here}}
@@ -1223,7 +1224,9 @@ void conditional_operator_lifetimebound(bool cond) {
   MyObj* p;
   {
     MyObj a, b;
-    p = Identity(cond ? &a    // expected-warning {{local variable 'a' does 
not live long enough}}
+    p = Identity(cond ? &a    // expected-warning {{local variable 'a' does 
not live long enough}} \
+                              // expected-note {{result of call to 'Identity' 
aliases the storage of local variable 'a'}} \
+                              // expected-note {{result of call to 'Identity' 
aliases the storage of local variable 'b'}}
                       : &b);  // expected-warning {{local variable 'b' does 
not live long enough}}
   }  // expected-note {{local variable 'b' is destroyed here}} expected-note 
{{local variable 'a' is destroyed here}}
   (void)*p;  // expected-note 2 {{later used here}}
@@ -1243,9 +1246,15 @@ void conditional_operator_lifetimebound_nested_deep(bool 
cond) {
   MyObj* p;
   {
     MyObj a, b, c, d;
-    p = Identity(cond ? Identity(cond ? &a     // expected-warning {{local 
variable 'a' does not live long enough}}
+    p = Identity(cond ? Identity(cond ? &a     // expected-warning {{local 
variable 'a' does not live long enough}} \
+                                               // expected-note 2 {{result of 
call to 'Identity' aliases the storage of local variable 'a'}} \
+                                               // expected-note 2 {{result of 
call to 'Identity' aliases the storage of local variable 'b'}} \
+                                               // expected-note {{result of 
call to 'Identity' aliases the storage of local variable 'c'}} \
+                                               // expected-note {{result of 
call to 'Identity' aliases the storage of local variable 'd'}}
                                       : &b)    // expected-warning {{local 
variable 'b' does not live long enough}}
-                      : Identity(cond ? &c     // expected-warning {{local 
variable 'c' does not live long enough}}
+                      : Identity(cond ? &c     // expected-warning {{local 
variable 'c' does not live long enough}} \
+                                               // expected-note {{result of 
call to 'Identity' aliases the storage of local variable 'c'}} \
+                                               // expected-note {{result of 
call to 'Identity' aliases the storage of local variable 'd'}}
                                       : &d));  // expected-warning {{local 
variable 'd' does not live long enough}}
   }  // expected-note {{local variable 'a' is destroyed here}} expected-note 
{{local variable 'd' is destroyed here}} expected-note {{local variable 'b' is 
destroyed here}} expected-note {{local variable 'c' is destroyed here}}
   (void)*p;  // expected-note 4 {{later used here}}
@@ -1539,7 +1548,8 @@ void range_based_for_use_after_scope() {
   View v;
   {
     MyObjStorage s;
-    for (const MyObj &o : s) { // expected-warning {{local variable 's' does 
not live long enough}}
+    for (const MyObj &o : s) { // expected-warning {{local variable 's' does 
not live long enough}} \
+                               // expected-note {{local variable '__range2' 
aliases the storage of local variable 's'}}
       v = o;
     }
   } // expected-note {{local variable 's' is destroyed here}}
diff --git a/clang/unittests/Analysis/LifetimeSafetyTest.cpp 
b/clang/unittests/Analysis/LifetimeSafetyTest.cpp
index 78b7449958140..8d5ed9c88dcff 100644
--- a/clang/unittests/Analysis/LifetimeSafetyTest.cpp
+++ b/clang/unittests/Analysis/LifetimeSafetyTest.cpp
@@ -215,7 +215,8 @@ class LifetimeTestHelper {
     for (LoanID LID : EndLoanIDs) {
       llvm::SmallVector<OriginID> OriginFlowChain =
           Runner.getAnalysis().getLoanPropagation().buildOriginFlowChain(
-              getProgramPoint(Annotation), *StartOriginID, LID);
+              getProgramPoint(Annotation), *StartOriginID, LID,
+              Runner.getAnalysisContext().getAnalysis<PostOrderCFGView>());
       if (!OriginFlowChain.empty())
         return OriginFlowChain;
     }

>From 2b0858c475a458ec73ff8593ce519f8e727eee98 Mon Sep 17 00:00:00 2001
From: Yuan Suo <[email protected]>
Date: Fri, 19 Jun 2026 15:25:42 +0800
Subject: [PATCH 02/15] Add return after llvm_unreachable and clean up code

Signed-off-by: Yuan Suo <[email protected]>
---
 .../LifetimeSafety/LoanPropagation.cpp        | 104 +++++++-----------
 clang/lib/Sema/SemaLifetimeSafety.h           |   4 +-
 2 files changed, 42 insertions(+), 66 deletions(-)

diff --git a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp 
b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
index 8e2d9a6577427..59081522b40f7 100644
--- a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
@@ -214,35 +214,7 @@ class AnalysisImpl
     assert(getLoans(StartOID, StartPoint).contains(TargetLoan) &&
            "TargetLoan must be present in the StartOID at the StartPoint");
 
-    std::optional<OriginID> FinalOID;
-    llvm::DenseMap<OriginID, OriginID> VistedOriginIDs;
-
-    const auto OriginFlowChainFilter = [&VistedOriginIDs](OriginID FinalOID) {
-      llvm::SmallVector<OriginID> OriginFlowChain;
-      while (true) {
-        OriginFlowChain.push_back(FinalOID);
-        const auto NextOriginID = VistedOriginIDs.find(FinalOID);
-        if (NextOriginID == VistedOriginIDs.end())
-          break;
-        FinalOID = NextOriginID->second;
-      }
-      return OriginFlowChain;
-    };
-
-    const auto InsertVistedOriginIDs =
-        [&VistedOriginIDs, &FinalOID](llvm::ArrayRef<OriginID> OriginFlowChain,
-                                      OriginID &StartOID) {
-          if (!VistedOriginIDs.empty())
-            VistedOriginIDs.insert({OriginFlowChain[0], StartOID});
-
-          for (size_t i = 0; i < OriginFlowChain.size() - 1; ++i)
-            VistedOriginIDs.insert(
-                {OriginFlowChain[i + 1], OriginFlowChain[i]});
-
-          StartOID = OriginFlowChain.back();
-          FinalOID = StartOID;
-        };
-
+    llvm::SmallVector<OriginID> OriginFlowChain;
     std::optional<size_t> BlockID = FactMgr.getBlockID(StartPoint);
     assert(BlockID.has_value());
     const auto StartIt = llvm::find_if(*POV, [&BlockID](const CFGBlock *Block) 
{
@@ -252,45 +224,20 @@ class AnalysisImpl
     OriginID CurrOID = StartOID;
     for (const CFGBlock *B :
          llvm::reverse(llvm::make_range(POV->begin(), StartIt + 1))) {
-      BuildOriginFlowChainResult BuildResult =
-          buildOriginFlowChain(B, CurrOID, TargetLoan);
-      if (!BuildResult.OriginFlowChain.empty())
-        InsertVistedOriginIDs(BuildResult.OriginFlowChain, CurrOID);
-      if (BuildResult.Complete)
-        return OriginFlowChainFilter(*FinalOID);
-    }
-
-    llvm_unreachable(
-        "buildOriginFlowChain should return at BuildResult.Complete");
-  }
-
-  BuildOriginFlowChainResult
-  buildOriginFlowChain(const CFGBlock *Block, const OriginID StartOID,
-                       const LoanID TargetLoan) const {
-    OriginID CurrOID = StartOID;
-    llvm::SmallVector<OriginID> OriginFlowChain;
+      auto [OFChain, Complete] = buildOriginFlowChain(B, CurrOID, TargetLoan);
 
-    for (const Fact *F : llvm::reverse(FactMgr.getFacts(Block))) {
-      if (const auto *IF = F->getAs<IssueFact>())
-        if (IF->getLoanID() == TargetLoan) {
-          assert(IF->getOriginID() == CurrOID);
-          return {OriginFlowChain, true};
-        }
-
-      const auto *OFF = F->getAs<OriginFlowFact>();
-      if (!OFF)
-        continue;
-      if (OFF->getDestOriginID() != CurrOID)
-        continue;
+      if (!OFChain.empty()) {
+        OriginFlowChain.append(OFChain.begin(), OFChain.end());
+        CurrOID = OFChain.back();
+      }
 
-      const OriginID SrcOriginID = OFF->getSrcOriginID();
-      if (!getLoans(SrcOriginID, OFF).contains(TargetLoan))
-        continue;
-      OriginFlowChain.push_back(SrcOriginID);
-      CurrOID = SrcOriginID;
+      if (Complete)
+        return OriginFlowChain;
     }
 
-    return {OriginFlowChain, false};
+    llvm_unreachable(
+        "buildOriginFlowChain should return at BuildResult.Complete");
+    return OriginFlowChain;
   }
 
   llvm::SmallVector<OriginID>
@@ -327,6 +274,35 @@ class AnalysisImpl
     return LoanSetFactory.getEmptySet();
   }
 
+  BuildOriginFlowChainResult
+  buildOriginFlowChain(const CFGBlock *Block, const OriginID StartOID,
+                       const LoanID TargetLoan) const {
+    OriginID CurrOID = StartOID;
+    llvm::SmallVector<OriginID> OriginFlowChain;
+
+    for (const Fact *F : llvm::reverse(FactMgr.getFacts(Block))) {
+      if (const auto *IF = F->getAs<IssueFact>())
+        if (IF->getLoanID() == TargetLoan) {
+          assert(IF->getOriginID() == CurrOID);
+          return {OriginFlowChain, true};
+        }
+
+      const auto *OFF = F->getAs<OriginFlowFact>();
+      if (!OFF)
+        continue;
+      if (OFF->getDestOriginID() != CurrOID)
+        continue;
+
+      const OriginID SrcOriginID = OFF->getSrcOriginID();
+      if (!getLoans(SrcOriginID, OFF).contains(TargetLoan))
+        continue;
+      OriginFlowChain.push_back(SrcOriginID);
+      CurrOID = SrcOriginID;
+    }
+
+    return {OriginFlowChain, false};
+  }
+
   OriginLoanMap::Factory &OriginLoanMapFactory;
   LoanSet::Factory &LoanSetFactory;
   /// Boolean vector indexed by origin ID. If true, the origin appears in
diff --git a/clang/lib/Sema/SemaLifetimeSafety.h 
b/clang/lib/Sema/SemaLifetimeSafety.h
index a1313816aae3a..4199e5dec1787 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -566,10 +566,10 @@ class LifetimeSafetySemaHelperImpl : public 
LifetimeSafetySemaHelper {
     if (OriginExprChain.empty())
       return;
 
-    const Expr *LastExpr = OriginExprChain.front();
+    const Expr *LastExpr = OriginExprChain.back();
     std::string IssueStr = getDiagSubjectDescription(LastExpr);
 
-    for (const Expr *CurrExpr : OriginExprChain.drop_front()) {
+    for (const Expr *CurrExpr : llvm::reverse(OriginExprChain.drop_back())) {
       if (!shouldShowInAliasChain(CurrExpr, LastExpr))
         continue;
       S.Diag(CurrExpr->getBeginLoc(),

>From bad3f3aa0ffcf6af363b164671c4c88441f06a82 Mon Sep 17 00:00:00 2001
From: Yuan Suo <[email protected]>
Date: Fri, 19 Jun 2026 15:49:18 +0800
Subject: [PATCH 03/15] Fix issues from previous commit

Signed-off-by: Yuan Suo <[email protected]>
---
 clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp | 9 ++-------
 clang/lib/Sema/SemaLifetimeSafety.h                   | 2 +-
 2 files changed, 3 insertions(+), 8 deletions(-)

diff --git a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp 
b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
index 59081522b40f7..d8b41f478b878 100644
--- a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
@@ -130,11 +130,6 @@ struct Lattice {
   }
 };
 
-struct BuildOriginFlowChainResult {
-  const llvm::SmallVector<OriginID> OriginFlowChain;
-  const bool Complete;
-};
-
 class AnalysisImpl
     : public DataflowAnalysis<AnalysisImpl, Lattice, Direction::Forward> {
 public:
@@ -237,7 +232,7 @@ class AnalysisImpl
 
     llvm_unreachable(
         "buildOriginFlowChain should return at BuildResult.Complete");
-    return OriginFlowChain;
+    return {};
   }
 
   llvm::SmallVector<OriginID>
@@ -274,7 +269,7 @@ class AnalysisImpl
     return LoanSetFactory.getEmptySet();
   }
 
-  BuildOriginFlowChainResult
+  std::pair<llvm::SmallVector<OriginID>, bool>
   buildOriginFlowChain(const CFGBlock *Block, const OriginID StartOID,
                        const LoanID TargetLoan) const {
     OriginID CurrOID = StartOID;
diff --git a/clang/lib/Sema/SemaLifetimeSafety.h 
b/clang/lib/Sema/SemaLifetimeSafety.h
index 4199e5dec1787..1047aecf863fb 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -569,7 +569,7 @@ class LifetimeSafetySemaHelperImpl : public 
LifetimeSafetySemaHelper {
     const Expr *LastExpr = OriginExprChain.back();
     std::string IssueStr = getDiagSubjectDescription(LastExpr);
 
-    for (const Expr *CurrExpr : llvm::reverse(OriginExprChain.drop_back())) {
+    for (const Expr *CurrExpr : reverse(OriginExprChain.drop_back())) {
       if (!shouldShowInAliasChain(CurrExpr, LastExpr))
         continue;
       S.Diag(CurrExpr->getBeginLoc(),

>From 8c84420389bfd0ad9ae33d3ee19160fc9f45f4f9 Mon Sep 17 00:00:00 2001
From: Yuan Suo <[email protected]>
Date: Fri, 19 Jun 2026 15:52:35 +0800
Subject: [PATCH 04/15] Reanme StartIt to EndBlockIt

Signed-off-by: Yuan Suo <[email protected]>
---
 clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp 
b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
index d8b41f478b878..3424bbc80a14f 100644
--- a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
@@ -212,13 +212,13 @@ class AnalysisImpl
     llvm::SmallVector<OriginID> OriginFlowChain;
     std::optional<size_t> BlockID = FactMgr.getBlockID(StartPoint);
     assert(BlockID.has_value());
-    const auto StartIt = llvm::find_if(*POV, [&BlockID](const CFGBlock *Block) 
{
+    const auto EndBlockIt = llvm::find_if(*POV, [&BlockID](const CFGBlock 
*Block) {
       return Block->getBlockID() == BlockID;
     });
 
     OriginID CurrOID = StartOID;
     for (const CFGBlock *B :
-         llvm::reverse(llvm::make_range(POV->begin(), StartIt + 1))) {
+         llvm::reverse(llvm::make_range(POV->begin(), EndBlockIt + 1))) {
       auto [OFChain, Complete] = buildOriginFlowChain(B, CurrOID, TargetLoan);
 
       if (!OFChain.empty()) {

>From 3487b3e8e338aec539bc8587f1e80d98b5085a64 Mon Sep 17 00:00:00 2001
From: Yuan Suo <[email protected]>
Date: Fri, 19 Jun 2026 15:55:02 +0800
Subject: [PATCH 05/15] Fix clang-format error

Signed-off-by: Yuan Suo <[email protected]>
---
 clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp 
b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
index 3424bbc80a14f..f6d620d6520ca 100644
--- a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
@@ -212,9 +212,10 @@ class AnalysisImpl
     llvm::SmallVector<OriginID> OriginFlowChain;
     std::optional<size_t> BlockID = FactMgr.getBlockID(StartPoint);
     assert(BlockID.has_value());
-    const auto EndBlockIt = llvm::find_if(*POV, [&BlockID](const CFGBlock 
*Block) {
-      return Block->getBlockID() == BlockID;
-    });
+    const auto EndBlockIt =
+        llvm::find_if(*POV, [&BlockID](const CFGBlock *Block) {
+          return Block->getBlockID() == BlockID;
+        });
 
     OriginID CurrOID = StartOID;
     for (const CFGBlock *B :

>From b42b7243152617a32bd6942f95441a3791c6544d Mon Sep 17 00:00:00 2001
From: Yuan Suo <[email protected]>
Date: Sat, 20 Jun 2026 15:29:12 +0800
Subject: [PATCH 06/15] Remove return after llvm_unreachable add
 llvm_unreachable in getBlockID

Also add a unit test for buildOriginFlowChain

Signed-off-by: Yuan Suo <[email protected]>
---
 .../Analysis/Analyses/LifetimeSafety/Facts.h  |  2 +-
 clang/lib/Analysis/LifetimeSafety/Facts.cpp   |  9 ++--
 .../LifetimeSafety/LoanPropagation.cpp        |  6 +--
 .../unittests/Analysis/LifetimeSafetyTest.cpp | 44 +++++++++++++++++++
 4 files changed, 50 insertions(+), 11 deletions(-)

diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
index e0ddfe95b00b2..e1380a6cf195c 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
@@ -374,7 +374,7 @@ class FactManager {
   /// Retrieves all the facts in the block containing Program Point P.
   /// \note This is intended for testing only.
   llvm::ArrayRef<const Fact *> getBlockContaining(ProgramPoint P) const;
-  std::optional<size_t> getBlockID(ProgramPoint P) const;
+  size_t getBlockID(ProgramPoint P) const;
 
   unsigned getNumFacts() const { return NextFactID.Value; }
 
diff --git a/clang/lib/Analysis/LifetimeSafety/Facts.cpp 
b/clang/lib/Analysis/LifetimeSafety/Facts.cpp
index 823f1f686f904..08e7c6cd62345 100644
--- a/clang/lib/Analysis/LifetimeSafety/Facts.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Facts.cpp
@@ -145,17 +145,14 @@ void FactManager::dump(const CFG &Cfg, 
AnalysisDeclContext &AC) const {
 
 llvm::ArrayRef<const Fact *>
 FactManager::getBlockContaining(ProgramPoint P) const {
-  std::optional<size_t> BlockIndex = getBlockID(P);
-  if (BlockIndex)
-    return BlockToFacts[BlockIndex.value()];
-  return {};
+  return BlockToFacts[getBlockID(P)];
 }
 
-std::optional<size_t> FactManager::getBlockID(ProgramPoint P) const {
+size_t FactManager::getBlockID(ProgramPoint P) const {
   for (size_t i = 0; i < BlockToFacts.size(); ++i)
     for (const Fact *F : BlockToFacts[i])
       if (F == P)
         return i;
-  return std::nullopt;
+  llvm_unreachable("Failed to find BlockID for given ProgramPoint");
 }
 } // namespace clang::lifetimes::internal
diff --git a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp 
b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
index f6d620d6520ca..385bf5f8a192f 100644
--- a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
@@ -210,8 +210,7 @@ class AnalysisImpl
            "TargetLoan must be present in the StartOID at the StartPoint");
 
     llvm::SmallVector<OriginID> OriginFlowChain;
-    std::optional<size_t> BlockID = FactMgr.getBlockID(StartPoint);
-    assert(BlockID.has_value());
+    size_t BlockID = FactMgr.getBlockID(StartPoint);
     const auto EndBlockIt =
         llvm::find_if(*POV, [&BlockID](const CFGBlock *Block) {
           return Block->getBlockID() == BlockID;
@@ -232,8 +231,7 @@ class AnalysisImpl
     }
 
     llvm_unreachable(
-        "buildOriginFlowChain should return at BuildResult.Complete");
-    return {};
+        "buildOriginFlowChain did not reach IssueFact for TargetLoan");
   }
 
   llvm::SmallVector<OriginID>
diff --git a/clang/unittests/Analysis/LifetimeSafetyTest.cpp 
b/clang/unittests/Analysis/LifetimeSafetyTest.cpp
index 8d5ed9c88dcff..153225dcdbf15 100644
--- a/clang/unittests/Analysis/LifetimeSafetyTest.cpp
+++ b/clang/unittests/Analysis/LifetimeSafetyTest.cpp
@@ -2078,5 +2078,49 @@ TEST_F(LifetimeAnalysisTest, 
BuildOriginFlowChainWithLifetimeBound) {
   EXPECT_THAT(ChainForTgtB, Contains(*Helper->getOriginForDecl("result")));
   EXPECT_THAT(ChainForTgtB, Not(Contains(*Helper->getOriginForDecl("a"))));
 }
+
+TEST_F(LifetimeAnalysisTest, BuildOriginFlowChainInMultiBlock) {
+  SetupTest(R"(
+    void target(bool c1, bool c2) {
+      int *s;
+      int *a, *b, *c;
+
+      {
+        int tgta, tgtb, tgtc;
+        a = &tgta;
+        b = &tgtb;
+        c = &tgtc;
+      }
+
+      if (c1) {
+        s = c2 ? a : b;
+      } else {
+        s = c;
+      }
+
+      POINT(after_nested_merge);
+      (void)*s;
+    }
+  )");
+
+  llvm::SmallVector<OriginID> ChainForTgtA =
+      Helper->buildOriginFlowChainInOneBlock("s", "tgta", 
"after_nested_merge");
+  llvm::SmallVector<OriginID> ChainForTgtB =
+      Helper->buildOriginFlowChainInOneBlock("s", "tgtb", 
"after_nested_merge");
+  llvm::SmallVector<OriginID> ChainForTgtC =
+      Helper->buildOriginFlowChainInOneBlock("s", "tgtc", 
"after_nested_merge");
+
+  EXPECT_THAT(ChainForTgtA, Contains(*Helper->getOriginForDecl("a")));
+  EXPECT_THAT(ChainForTgtA, Not(Contains(*Helper->getOriginForDecl("b"))));
+  EXPECT_THAT(ChainForTgtA, Not(Contains(*Helper->getOriginForDecl("c"))));
+
+  EXPECT_THAT(ChainForTgtB, Contains(*Helper->getOriginForDecl("b")));
+  EXPECT_THAT(ChainForTgtB, Not(Contains(*Helper->getOriginForDecl("a"))));
+  EXPECT_THAT(ChainForTgtB, Not(Contains(*Helper->getOriginForDecl("c"))));
+
+  EXPECT_THAT(ChainForTgtC, Contains(*Helper->getOriginForDecl("c")));
+  EXPECT_THAT(ChainForTgtC, Not(Contains(*Helper->getOriginForDecl("b"))));
+  EXPECT_THAT(ChainForTgtC, Not(Contains(*Helper->getOriginForDecl("a"))));
+}
 } // anonymous namespace
 } // namespace clang::lifetimes::internal

>From 62d8318e42ae7f36f57354711fa2eaecadf9acd4 Mon Sep 17 00:00:00 2001
From: Yuan Suo <[email protected]>
Date: Sat, 20 Jun 2026 16:17:23 +0800
Subject: [PATCH 07/15] Add Decl->isImplicit() in shouldShowInAliasChain

Signed-off-by: Yuan Suo <[email protected]>
---
 clang/lib/Sema/SemaLifetimeSafety.h       | 15 ++++++++++++++-
 clang/test/Sema/LifetimeSafety/safety.cpp |  2 +-
 2 files changed, 15 insertions(+), 2 deletions(-)

diff --git a/clang/lib/Sema/SemaLifetimeSafety.h 
b/clang/lib/Sema/SemaLifetimeSafety.h
index 1047aecf863fb..88c0d81979437 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -550,11 +550,24 @@ class LifetimeSafetySemaHelperImpl : public 
LifetimeSafetySemaHelper {
     return "expression";
   }
 
+  bool isInValidExpr(const Expr *E) {
+    if (const auto *DRE = dyn_cast<DeclRefExpr>(E)) {
+      return !DRE->getDecl()->isImplicit();
+    }
+
+    if (const auto *CE = dyn_cast<CallExpr>(E)) {
+      if (const auto *FE = CE->getDirectCallee())
+        return !FE->isImplicit();
+    }
+
+    return false;
+  }
+
   bool shouldShowInAliasChain(const Expr *CurrExpr, const Expr *LastExpr) {
     CurrExpr = CurrExpr->IgnoreImpCasts();
     LastExpr = LastExpr->IgnoreImpCasts();
 
-    if (!isa<CallExpr, DeclRefExpr>(CurrExpr))
+    if (!isInValidExpr(CurrExpr))
       return false;
     // Source ranges can be used to filter out many implicit expressions,
     // because operations between class objects often involve numerous implicit
diff --git a/clang/test/Sema/LifetimeSafety/safety.cpp 
b/clang/test/Sema/LifetimeSafety/safety.cpp
index 0a6bea96af45d..82d443b179bd3 100644
--- a/clang/test/Sema/LifetimeSafety/safety.cpp
+++ b/clang/test/Sema/LifetimeSafety/safety.cpp
@@ -1549,7 +1549,7 @@ void range_based_for_use_after_scope() {
   {
     MyObjStorage s;
     for (const MyObj &o : s) { // expected-warning {{local variable 's' does 
not live long enough}} \
-                               // expected-note {{local variable '__range2' 
aliases the storage of local variable 's'}}
+                               // expected-note {{result of call to 'begin' 
aliases the storage of local variable 's'}}
       v = o;
     }
   } // expected-note {{local variable 's' is destroyed here}}

>From ca7a6e9dae2e4cc3e968345cba177fa93eaf0676 Mon Sep 17 00:00:00 2001
From: Yuan Suo <[email protected]>
Date: Sat, 20 Jun 2026 16:55:16 +0800
Subject: [PATCH 08/15] Fix inconsistent semantics in isInValidExpr

Signed-off-by: Yuan Suo <[email protected]>
---
 clang/lib/Sema/SemaLifetimeSafety.h | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/clang/lib/Sema/SemaLifetimeSafety.h 
b/clang/lib/Sema/SemaLifetimeSafety.h
index 88c0d81979437..9f5c9592a2e6e 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -552,22 +552,22 @@ class LifetimeSafetySemaHelperImpl : public 
LifetimeSafetySemaHelper {
 
   bool isInValidExpr(const Expr *E) {
     if (const auto *DRE = dyn_cast<DeclRefExpr>(E)) {
-      return !DRE->getDecl()->isImplicit();
+      return DRE->getDecl()->isImplicit();
     }
 
     if (const auto *CE = dyn_cast<CallExpr>(E)) {
       if (const auto *FE = CE->getDirectCallee())
-        return !FE->isImplicit();
+        return FE->isImplicit();
     }
 
-    return false;
+    return true;
   }
 
   bool shouldShowInAliasChain(const Expr *CurrExpr, const Expr *LastExpr) {
     CurrExpr = CurrExpr->IgnoreImpCasts();
     LastExpr = LastExpr->IgnoreImpCasts();
 
-    if (!isInValidExpr(CurrExpr))
+    if (isInValidExpr(CurrExpr))
       return false;
     // Source ranges can be used to filter out many implicit expressions,
     // because operations between class objects often involve numerous implicit

>From b2331e72324e74e46b71ddb6104709cb2d7ba260 Mon Sep 17 00:00:00 2001
From: Yuan Suo <[email protected]>
Date: Sun, 21 Jun 2026 12:55:16 +0800
Subject: [PATCH 09/15] isInValidExpr -> isInvalidExpr

Signed-off-by: Yuan Suo <[email protected]>
---
 clang/lib/Sema/SemaLifetimeSafety.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/lib/Sema/SemaLifetimeSafety.h 
b/clang/lib/Sema/SemaLifetimeSafety.h
index 9f5c9592a2e6e..eea106911ae0a 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -550,7 +550,7 @@ class LifetimeSafetySemaHelperImpl : public 
LifetimeSafetySemaHelper {
     return "expression";
   }
 
-  bool isInValidExpr(const Expr *E) {
+  bool isInvalidExpr(const Expr *E) {
     if (const auto *DRE = dyn_cast<DeclRefExpr>(E)) {
       return DRE->getDecl()->isImplicit();
     }
@@ -567,7 +567,7 @@ class LifetimeSafetySemaHelperImpl : public 
LifetimeSafetySemaHelper {
     CurrExpr = CurrExpr->IgnoreImpCasts();
     LastExpr = LastExpr->IgnoreImpCasts();
 
-    if (isInValidExpr(CurrExpr))
+    if (isInvalidExpr(CurrExpr))
       return false;
     // Source ranges can be used to filter out many implicit expressions,
     // because operations between class objects often involve numerous implicit

>From b2dd596b44dbf69bd2a304797c67dcc124b0adce Mon Sep 17 00:00:00 2001
From: Yuan Suo <[email protected]>
Date: Sun, 21 Jun 2026 13:51:26 +0800
Subject: [PATCH 10/15] Using Block->preds() instead of PostOrderCFGView

Signed-off-by: Yuan Suo <[email protected]>
---
 .../Analyses/LifetimeSafety/LoanPropagation.h |  17 ++-
 clang/lib/Analysis/LifetimeSafety/Checker.cpp |   7 +-
 .../LifetimeSafety/LoanPropagation.cpp        |  98 +++++++++++-----
 clang/lib/Sema/SemaLifetimeSafety.h           |   4 +-
 .../unittests/Analysis/LifetimeSafetyTest.cpp | 107 +++++++++---------
 5 files changed, 134 insertions(+), 99 deletions(-)

diff --git 
a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
index 42d7df0838f35..97d529e91a9ee 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
@@ -16,7 +16,6 @@
 #define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_LOAN_PROPAGATION_H
 
 #include "clang/Analysis/Analyses/LifetimeSafety/Facts.h"
-#include "clang/Analysis/Analyses/PostOrderCFGView.h"
 #include "clang/Analysis/AnalysisDeclContext.h"
 #include "clang/Analysis/CFG.h"
 #include "llvm/ADT/ImmutableMap.h"
@@ -45,14 +44,14 @@ class LoanPropagationAnalysis {
   /// this function traces backwards through OriginFlowFacts to identify the
   /// sequence of origins through which the loan flowed, ending at the origin
   /// where the loan was originally issued.
-  llvm::SmallVector<OriginID>
-  buildOriginFlowChain(ProgramPoint StartPoint, const OriginID StartOID,
-                       const LoanID TargetLoan,
-                       const PostOrderCFGView *POV) const;
-
-  llvm::SmallVector<OriginID>
-  buildOriginFlowChain(const UseFact *UF, const LoanID TargetLoan,
-                       const PostOrderCFGView *POV) const;
+  llvm::SmallVector<OriginID> buildOriginFlowChain(ProgramPoint StartPoint,
+                                                   const OriginID StartOID,
+                                                   const LoanID TargetLoan,
+                                                   const CFG *Cfg) const;
+
+  llvm::SmallVector<OriginID> buildOriginFlowChain(const UseFact *UF,
+                                                   const LoanID TargetLoan,
+                                                   const CFG *Cfg) const;
 
 private:
   class Impl;
diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp 
b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
index 17c0c1e4478ea..0288e794998a6 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -67,8 +67,8 @@ class LifetimeChecker {
   FactManager &FactMgr;
   LifetimeSafetySemaHelper *SemaHelper;
   ASTContext &AST;
+  const CFG *Cfg;
   const Decl *FD;
-  const PostOrderCFGView *POV;
 
   static SourceLocation
   GetFactLoc(llvm::PointerUnion<const UseFact *, const OriginEscapesFact *> F) 
{
@@ -91,8 +91,7 @@ class LifetimeChecker {
                   LifetimeSafetySemaHelper *SemaHelper)
       : LoanPropagation(LoanPropagation), MovedLoans(MovedLoans),
         LiveOrigins(LiveOrigins), FactMgr(FM), SemaHelper(SemaHelper),
-        AST(ADC.getASTContext()), FD(ADC.getDecl()),
-        POV(ADC.getAnalysis<PostOrderCFGView>()) {
+        AST(ADC.getASTContext()), Cfg(ADC.getCFG()), FD(ADC.getDecl()) {
     for (const CFGBlock *B : *ADC.getAnalysis<PostOrderCFGView>())
       for (const Fact *F : FactMgr.getFacts(B))
         if (const auto *EF = F->getAs<ExpireFact>())
@@ -272,7 +271,7 @@ class LifetimeChecker {
           // Scope-based expiry (use-after-scope).
           SemaHelper->reportUseAfterScope(
               IssueExpr, UF->getUseExpr(), MovedExpr, ExpiryLoc,
-              getExprChain(LoanPropagation.buildOriginFlowChain(UF, LID, 
POV)));
+              getExprChain(LoanPropagation.buildOriginFlowChain(UF, LID, 
Cfg)));
 
       } else if (const auto *OEF =
                      CausingFact.dyn_cast<const OriginEscapesFact *>()) {
diff --git a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp 
b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
index 385bf5f8a192f..a77dbf3cf3b58 100644
--- a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
@@ -18,6 +18,7 @@
 #include "clang/Analysis/CFG.h"
 #include "clang/Basic/LLVM.h"
 #include "llvm/ADT/BitVector.h"
+#include "llvm/ADT/SmallSet.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/Support/TimeProfiler.h"
 #include "llvm/Support/raw_ostream.h"
@@ -202,46 +203,86 @@ class AnalysisImpl
     return getLoans(getState(P), OID);
   }
 
-  llvm::SmallVector<OriginID>
-  buildOriginFlowChain(ProgramPoint StartPoint, const OriginID StartOID,
-                       const LoanID TargetLoan,
-                       const PostOrderCFGView *POV) const {
+  llvm::SmallVector<OriginID> buildOriginFlowChain(ProgramPoint StartPoint,
+                                                   const OriginID StartOID,
+                                                   const LoanID TargetLoan,
+                                                   const CFG *Cfg) const {
     assert(getLoans(StartOID, StartPoint).contains(TargetLoan) &&
            "TargetLoan must be present in the StartOID at the StartPoint");
 
-    llvm::SmallVector<OriginID> OriginFlowChain;
-    size_t BlockID = FactMgr.getBlockID(StartPoint);
-    const auto EndBlockIt =
-        llvm::find_if(*POV, [&BlockID](const CFGBlock *Block) {
-          return Block->getBlockID() == BlockID;
-        });
+    std::optional<OriginID> FinalOID;
+    llvm::DenseMap<OriginID, OriginID> VistedOriginIDs;
+
+    const auto OriginFlowChainFilter = [&VistedOriginIDs](OriginID FinalOID) {
+      llvm::SmallVector<OriginID> OriginFlowChain;
+      while (true) {
+        OriginFlowChain.push_back(FinalOID);
+        const auto NextOriginID = VistedOriginIDs.find(FinalOID);
+        if (NextOriginID == VistedOriginIDs.end())
+          break;
+        FinalOID = NextOriginID->second;
+      }
+      return OriginFlowChain;
+    };
 
-    OriginID CurrOID = StartOID;
-    for (const CFGBlock *B :
-         llvm::reverse(llvm::make_range(POV->begin(), EndBlockIt + 1))) {
-      auto [OFChain, Complete] = buildOriginFlowChain(B, CurrOID, TargetLoan);
+    const auto InsertVistedOriginIDs =
+        [&VistedOriginIDs, &FinalOID](llvm::ArrayRef<OriginID> OriginFlowChain,
+                                      OriginID &StartOID) {
+          if (!VistedOriginIDs.empty())
+            VistedOriginIDs.insert({OriginFlowChain[0], StartOID});
 
-      if (!OFChain.empty()) {
-        OriginFlowChain.append(OFChain.begin(), OFChain.end());
-        CurrOID = OFChain.back();
+          for (size_t i = 0; i < OriginFlowChain.size() - 1; ++i)
+            VistedOriginIDs.insert(
+                {OriginFlowChain[i + 1], OriginFlowChain[i]});
+
+          StartOID = OriginFlowChain.back();
+          FinalOID = StartOID;
+        };
+
+    const CFGBlock *EndBlock = nullptr;
+    size_t BlockID = FactMgr.getBlockID(StartPoint);
+    for (const CFGBlock *Block : *Cfg)
+      if (Block->getBlockID() == BlockID) {
+        EndBlock = Block;
+        break;
       }
 
+    using SearchContext = std::pair<const CFGBlock *, OriginID>;
+    std::queue<SearchContext> PendingContext;
+    llvm::SmallSet<SearchContext, 32> VistedContext;
+    PendingContext.push({EndBlock, StartOID});
+
+    while (!PendingContext.empty()) {
+      auto [CurrBlock, CurrOID] = PendingContext.front();
+      PendingContext.pop();
+
+      const auto [BuildResult, Complete] =
+          buildOriginFlowChain(CurrBlock, CurrOID, TargetLoan);
+      if (!BuildResult.empty())
+        InsertVistedOriginIDs(BuildResult, CurrOID);
+
       if (Complete)
-        return OriginFlowChain;
+        return OriginFlowChainFilter(*FinalOID);
+
+      for (const CFGBlock *Block : CurrBlock->preds()) {
+        SearchContext Context = {Block, CurrOID};
+        if (Block && VistedContext.insert(Context).second)
+          PendingContext.push(Context);
+      }
     }
 
     llvm_unreachable(
         "buildOriginFlowChain did not reach IssueFact for TargetLoan");
   }
 
-  llvm::SmallVector<OriginID>
-  buildOriginFlowChain(const UseFact *UF, const LoanID TargetLoan,
-                       const PostOrderCFGView *POV) const {
+  llvm::SmallVector<OriginID> buildOriginFlowChain(const UseFact *UF,
+                                                   const LoanID TargetLoan,
+                                                   const CFG *Cfg) const {
     for (const OriginList *Cur = UF->getUsedOrigins(); Cur;
          Cur = Cur->peelOuterOrigin())
       if (getLoans(Cur->getOuterOriginID(), UF).contains(TargetLoan))
         return buildOriginFlowChain(UF, Cur->getOuterOriginID(), TargetLoan,
-                                    POV);
+                                    Cfg);
 
     return {};
   }
@@ -276,10 +317,8 @@ class AnalysisImpl
 
     for (const Fact *F : llvm::reverse(FactMgr.getFacts(Block))) {
       if (const auto *IF = F->getAs<IssueFact>())
-        if (IF->getLoanID() == TargetLoan) {
-          assert(IF->getOriginID() == CurrOID);
+        if (IF->getLoanID() == TargetLoan && IF->getOriginID() == CurrOID)
           return {OriginFlowChain, true};
-        }
 
       const auto *OFF = F->getAs<OriginFlowFact>();
       if (!OFF)
@@ -327,13 +366,12 @@ LoanSet LoanPropagationAnalysis::getLoans(OriginID OID, 
ProgramPoint P) const {
 
 llvm::SmallVector<OriginID> LoanPropagationAnalysis::buildOriginFlowChain(
     ProgramPoint StartPoint, const OriginID StartOID, const LoanID TargetLoan,
-    const PostOrderCFGView *POV) const {
-  return PImpl->buildOriginFlowChain(StartPoint, StartOID, TargetLoan, POV);
+    const CFG *Cfg) const {
+  return PImpl->buildOriginFlowChain(StartPoint, StartOID, TargetLoan, Cfg);
 }
 
 llvm::SmallVector<OriginID> LoanPropagationAnalysis::buildOriginFlowChain(
-    const UseFact *UF, const LoanID TargetLoan,
-    const PostOrderCFGView *POV) const {
-  return PImpl->buildOriginFlowChain(UF, TargetLoan, POV);
+    const UseFact *UF, const LoanID TargetLoan, const CFG *Cfg) const {
+  return PImpl->buildOriginFlowChain(UF, TargetLoan, Cfg);
 }
 } // namespace clang::lifetimes::internal
diff --git a/clang/lib/Sema/SemaLifetimeSafety.h 
b/clang/lib/Sema/SemaLifetimeSafety.h
index eea106911ae0a..6baaa320cfd6f 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -579,10 +579,10 @@ class LifetimeSafetySemaHelperImpl : public 
LifetimeSafetySemaHelper {
     if (OriginExprChain.empty())
       return;
 
-    const Expr *LastExpr = OriginExprChain.back();
+    const Expr *LastExpr = OriginExprChain.front();
     std::string IssueStr = getDiagSubjectDescription(LastExpr);
 
-    for (const Expr *CurrExpr : reverse(OriginExprChain.drop_back())) {
+    for (const Expr *CurrExpr : OriginExprChain.drop_front()) {
       if (!shouldShowInAliasChain(CurrExpr, LastExpr))
         continue;
       S.Diag(CurrExpr->getBeginLoc(),
diff --git a/clang/unittests/Analysis/LifetimeSafetyTest.cpp 
b/clang/unittests/Analysis/LifetimeSafetyTest.cpp
index 153225dcdbf15..d79161ae1db54 100644
--- a/clang/unittests/Analysis/LifetimeSafetyTest.cpp
+++ b/clang/unittests/Analysis/LifetimeSafetyTest.cpp
@@ -206,9 +206,8 @@ class LifetimeTestHelper {
   }
 
   llvm::SmallVector<OriginID>
-  buildOriginFlowChainInOneBlock(llvm::StringRef StartOriginVar,
-                                 llvm::StringRef EndLoanVar,
-                                 llvm::StringRef Annotation) {
+  buildOriginFlowChain(llvm::StringRef StartOriginVar,
+                       llvm::StringRef EndLoanVar, llvm::StringRef Annotation) 
{
     std::optional<OriginID> StartOriginID = getOriginForDecl(StartOriginVar);
     std::vector<LoanID> EndLoanIDs = getLoansForVar(EndLoanVar);
 
@@ -216,7 +215,7 @@ class LifetimeTestHelper {
       llvm::SmallVector<OriginID> OriginFlowChain =
           Runner.getAnalysis().getLoanPropagation().buildOriginFlowChain(
               getProgramPoint(Annotation), *StartOriginID, LID,
-              Runner.getAnalysisContext().getAnalysis<PostOrderCFGView>());
+              Runner.getAnalysisContext().getCFG());
       if (!OriginFlowChain.empty())
         return OriginFlowChain;
     }
@@ -1976,6 +1975,50 @@ TEST_F(LifetimeAnalysisTest, 
LambdaInitCaptureViewByValue) {
 //                    Tests for buildOriginFlowChain
 // ========================================================================= //
 
+TEST_F(LifetimeAnalysisTest, BuildOriginFlowChain) {
+  SetupTest(R"(
+    void target(bool c1, bool c2) {
+      int *s;
+      int *a, *b, *c;
+
+      {
+        int tgta, tgtb, tgtc;
+        a = &tgta;
+        b = &tgtb;
+        c = &tgtc;
+      }
+
+      if (c1) {
+        s = c2 ? a : b;
+      } else {
+        s = c;
+      }
+
+      POINT(after_nested_merge);
+      (void)*s;
+    }
+  )");
+
+  llvm::SmallVector<OriginID> ChainForTgtA =
+      Helper->buildOriginFlowChain("s", "tgta", "after_nested_merge");
+  llvm::SmallVector<OriginID> ChainForTgtB =
+      Helper->buildOriginFlowChain("s", "tgtb", "after_nested_merge");
+  llvm::SmallVector<OriginID> ChainForTgtC =
+      Helper->buildOriginFlowChain("s", "tgtc", "after_nested_merge");
+
+  EXPECT_THAT(ChainForTgtA, Contains(*Helper->getOriginForDecl("a")));
+  EXPECT_THAT(ChainForTgtA, Not(Contains(*Helper->getOriginForDecl("b"))));
+  EXPECT_THAT(ChainForTgtA, Not(Contains(*Helper->getOriginForDecl("c"))));
+
+  EXPECT_THAT(ChainForTgtB, Not(Contains(*Helper->getOriginForDecl("a"))));
+  EXPECT_THAT(ChainForTgtB, Contains(*Helper->getOriginForDecl("b")));
+  EXPECT_THAT(ChainForTgtB, Not(Contains(*Helper->getOriginForDecl("c"))));
+
+  EXPECT_THAT(ChainForTgtC, Not(Contains(*Helper->getOriginForDecl("a"))));
+  EXPECT_THAT(ChainForTgtC, Not(Contains(*Helper->getOriginForDecl("b"))));
+  EXPECT_THAT(ChainForTgtC, Contains(*Helper->getOriginForDecl("c")));
+}
+
 TEST_F(LifetimeAnalysisTest, BuildOriginFlowChainWithErrorTargetLoan) {
   SetupTest(R"(
     void target() {
@@ -1987,7 +2030,7 @@ TEST_F(LifetimeAnalysisTest, 
BuildOriginFlowChainWithErrorTargetLoan) {
   )");
 
 #if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST
-  EXPECT_DEATH(Helper->buildOriginFlowChainInOneBlock("s", "a", "after_use"),
+  EXPECT_DEATH(Helper->buildOriginFlowChain("s", "a", "after_use"),
                "TargetLoan must be present in the StartOID at the StartPoint");
 #endif
 }
@@ -2006,7 +2049,7 @@ TEST_F(LifetimeAnalysisTest, 
BuildOriginFlowChainWithSelfAssignment) {
   )");
 
   const llvm::SmallVector<OriginID> OriginFlowChain =
-      Helper->buildOriginFlowChainInOneBlock("s", "tgt", "after_use");
+      Helper->buildOriginFlowChain("s", "tgt", "after_use");
 
   EXPECT_THAT(OriginFlowChain, Contains(*Helper->getOriginForDecl("a")));
 }
@@ -2023,7 +2066,7 @@ TEST_F(LifetimeAnalysisTest, 
BuildOriginFlowChainWithMultiAssignInSameStmt) {
   )");
 
   const llvm::SmallVector<OriginID> OriginFlowChain =
-      Helper->buildOriginFlowChainInOneBlock("s", "tgt", "after_use");
+      Helper->buildOriginFlowChain("s", "tgt", "after_use");
 
   EXPECT_THAT(OriginFlowChain, Contains(*Helper->getOriginForDecl("a")));
   EXPECT_THAT(OriginFlowChain, Contains(*Helper->getOriginForDecl("b")));
@@ -2044,7 +2087,7 @@ TEST_F(LifetimeAnalysisTest, 
BuildOriginFlowChainWithOverwritingAssignments) {
   )");
 
   const llvm::SmallVector<OriginID> OriginFlowChain =
-      Helper->buildOriginFlowChainInOneBlock("s", "tgt1", "after_use");
+      Helper->buildOriginFlowChain("s", "tgt1", "after_use");
 
   EXPECT_THAT(OriginFlowChain, Contains(*Helper->getOriginForDecl("a")));
   EXPECT_THAT(OriginFlowChain, Contains(*Helper->getOriginForDecl("b")));
@@ -2066,9 +2109,9 @@ TEST_F(LifetimeAnalysisTest, 
BuildOriginFlowChainWithLifetimeBound) {
   )");
 
   llvm::SmallVector<OriginID> ChainForTgtA =
-      Helper->buildOriginFlowChainInOneBlock("s", "tgta", "after_use");
+      Helper->buildOriginFlowChain("s", "tgta", "after_use");
   llvm::SmallVector<OriginID> ChainForTgtB =
-      Helper->buildOriginFlowChainInOneBlock("s", "tgtb", "after_use");
+      Helper->buildOriginFlowChain("s", "tgtb", "after_use");
 
   EXPECT_THAT(ChainForTgtA, Contains(*Helper->getOriginForDecl("a")));
   EXPECT_THAT(ChainForTgtA, Contains(*Helper->getOriginForDecl("result")));
@@ -2078,49 +2121,5 @@ TEST_F(LifetimeAnalysisTest, 
BuildOriginFlowChainWithLifetimeBound) {
   EXPECT_THAT(ChainForTgtB, Contains(*Helper->getOriginForDecl("result")));
   EXPECT_THAT(ChainForTgtB, Not(Contains(*Helper->getOriginForDecl("a"))));
 }
-
-TEST_F(LifetimeAnalysisTest, BuildOriginFlowChainInMultiBlock) {
-  SetupTest(R"(
-    void target(bool c1, bool c2) {
-      int *s;
-      int *a, *b, *c;
-
-      {
-        int tgta, tgtb, tgtc;
-        a = &tgta;
-        b = &tgtb;
-        c = &tgtc;
-      }
-
-      if (c1) {
-        s = c2 ? a : b;
-      } else {
-        s = c;
-      }
-
-      POINT(after_nested_merge);
-      (void)*s;
-    }
-  )");
-
-  llvm::SmallVector<OriginID> ChainForTgtA =
-      Helper->buildOriginFlowChainInOneBlock("s", "tgta", 
"after_nested_merge");
-  llvm::SmallVector<OriginID> ChainForTgtB =
-      Helper->buildOriginFlowChainInOneBlock("s", "tgtb", 
"after_nested_merge");
-  llvm::SmallVector<OriginID> ChainForTgtC =
-      Helper->buildOriginFlowChainInOneBlock("s", "tgtc", 
"after_nested_merge");
-
-  EXPECT_THAT(ChainForTgtA, Contains(*Helper->getOriginForDecl("a")));
-  EXPECT_THAT(ChainForTgtA, Not(Contains(*Helper->getOriginForDecl("b"))));
-  EXPECT_THAT(ChainForTgtA, Not(Contains(*Helper->getOriginForDecl("c"))));
-
-  EXPECT_THAT(ChainForTgtB, Contains(*Helper->getOriginForDecl("b")));
-  EXPECT_THAT(ChainForTgtB, Not(Contains(*Helper->getOriginForDecl("a"))));
-  EXPECT_THAT(ChainForTgtB, Not(Contains(*Helper->getOriginForDecl("c"))));
-
-  EXPECT_THAT(ChainForTgtC, Contains(*Helper->getOriginForDecl("c")));
-  EXPECT_THAT(ChainForTgtC, Not(Contains(*Helper->getOriginForDecl("b"))));
-  EXPECT_THAT(ChainForTgtC, Not(Contains(*Helper->getOriginForDecl("a"))));
-}
 } // anonymous namespace
 } // namespace clang::lifetimes::internal

>From c9046561524beda497c09b35975e141e7f95eac4 Mon Sep 17 00:00:00 2001
From: Yuan Suo <[email protected]>
Date: Sun, 21 Jun 2026 15:02:59 +0800
Subject: [PATCH 11/15] Add a fallback in isInvalidExpr(CallExpr)

Signed-off-by: Yuan Suo <[email protected]>
---
 clang/lib/Sema/SemaLifetimeSafety.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/clang/lib/Sema/SemaLifetimeSafety.h 
b/clang/lib/Sema/SemaLifetimeSafety.h
index 6baaa320cfd6f..4130e72b8b88a 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -558,6 +558,7 @@ class LifetimeSafetySemaHelperImpl : public 
LifetimeSafetySemaHelper {
     if (const auto *CE = dyn_cast<CallExpr>(E)) {
       if (const auto *FE = CE->getDirectCallee())
         return FE->isImplicit();
+      return false;
     }
 
     return true;

>From 119a023286e909a70bc798b76afe4cb24faa413f Mon Sep 17 00:00:00 2001
From: Yuan Suo <[email protected]>
Date: Mon, 22 Jun 2026 15:58:15 +0800
Subject: [PATCH 12/15] Add debug output

==========================================
    Lifetime Analysis buildOriginFlow
==========================================
StartOriginID: 3, TargetLoanID: 6

CurrBlockID: 3, StartOriginID: 3
        Find OriginID: 29
        Find OriginID: 9
EndOriginID: 9
CurrBlockID: 6, StartOriginID: 3
EndOriginID: 3

Signed-off-by: Yuan Suo <[email protected]>
---
 .../LifetimeSafety/LoanPropagation.cpp        | 23 +++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp 
b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
index a77dbf3cf3b58..27f5ecf65ceb9 100644
--- a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
@@ -210,6 +210,18 @@ class AnalysisImpl
     assert(getLoans(StartOID, StartPoint).contains(TargetLoan) &&
            "TargetLoan must be present in the StartOID at the StartPoint");
 
+    DEBUG_WITH_TYPE("LifetimeBuildOriginFlow",
+                    llvm::dbgs()
+                        << "==========================================\n");
+    DEBUG_WITH_TYPE("LifetimeBuildOriginFlow",
+                    llvm::dbgs() << "    Lifetime Analysis buildOriginFlow\n");
+    DEBUG_WITH_TYPE("LifetimeBuildOriginFlow",
+                    llvm::dbgs()
+                        << "==========================================\n");
+    DEBUG_WITH_TYPE("LifetimeBuildOriginFlow",
+                    llvm::dbgs() << "StartOriginID: " << StartOID
+                                 << ", TargetLoanID: " << TargetLoan << 
"\n\n");
+
     std::optional<OriginID> FinalOID;
     llvm::DenseMap<OriginID, OriginID> VistedOriginIDs;
 
@@ -256,6 +268,10 @@ class AnalysisImpl
       auto [CurrBlock, CurrOID] = PendingContext.front();
       PendingContext.pop();
 
+      DEBUG_WITH_TYPE("LifetimeBuildOriginFlow",
+                      llvm::dbgs() << "CurrBlockID: " << 
CurrBlock->getBlockID()
+                                   << ", StartOriginID: " << CurrOID << "\n");
+
       const auto [BuildResult, Complete] =
           buildOriginFlowChain(CurrBlock, CurrOID, TargetLoan);
       if (!BuildResult.empty())
@@ -264,6 +280,9 @@ class AnalysisImpl
       if (Complete)
         return OriginFlowChainFilter(*FinalOID);
 
+      DEBUG_WITH_TYPE("LifetimeBuildOriginFlow",
+                      llvm::dbgs() << "EndOriginID: " << CurrOID << "\n");
+
       for (const CFGBlock *Block : CurrBlock->preds()) {
         SearchContext Context = {Block, CurrOID};
         if (Block && VistedContext.insert(Context).second)
@@ -329,6 +348,10 @@ class AnalysisImpl
       const OriginID SrcOriginID = OFF->getSrcOriginID();
       if (!getLoans(SrcOriginID, OFF).contains(TargetLoan))
         continue;
+
+      DEBUG_WITH_TYPE("LifetimeBuildOriginFlow",
+                      llvm::dbgs()
+                          << "\tFind OriginID: " << SrcOriginID << "\n");
       OriginFlowChain.push_back(SrcOriginID);
       CurrOID = SrcOriginID;
     }

>From 66398f953fbb06025db9ab01f6073c061cbc5b96 Mon Sep 17 00:00:00 2001
From: Yuan Suo <[email protected]>
Date: Tue, 23 Jun 2026 21:16:20 +0800
Subject: [PATCH 13/15] Simplify buildOriginFlowChain and add some test case

Signed-off-by: Yuan Suo <[email protected]>
---
 .../Analyses/LifetimeSafety/LoanPropagation.h |  2 +-
 .../LifetimeSafety/LoanPropagation.cpp        | 58 +++++--------------
 clang/lib/Sema/SemaLifetimeSafety.h           |  4 +-
 clang/test/Sema/LifetimeSafety/safety.cpp     | 42 ++++++++++++++
 4 files changed, 59 insertions(+), 47 deletions(-)

diff --git 
a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
index 97d529e91a9ee..2c3357125810c 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
@@ -40,7 +40,7 @@ class LoanPropagationAnalysis {
 
   /// Builds the chain of origins through which a loan has propagated.
   ///
-  /// Starting from StartPoint where StartOID currently holds TargetLoan,
+  /// Starting from the last fact of the block containg StartPoint,
   /// this function traces backwards through OriginFlowFacts to identify the
   /// sequence of origins through which the loan flowed, ending at the origin
   /// where the loan was originally issued.
diff --git a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp 
b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
index 27f5ecf65ceb9..2d9acbd822f16 100644
--- a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
@@ -222,35 +222,6 @@ class AnalysisImpl
                     llvm::dbgs() << "StartOriginID: " << StartOID
                                  << ", TargetLoanID: " << TargetLoan << 
"\n\n");
 
-    std::optional<OriginID> FinalOID;
-    llvm::DenseMap<OriginID, OriginID> VistedOriginIDs;
-
-    const auto OriginFlowChainFilter = [&VistedOriginIDs](OriginID FinalOID) {
-      llvm::SmallVector<OriginID> OriginFlowChain;
-      while (true) {
-        OriginFlowChain.push_back(FinalOID);
-        const auto NextOriginID = VistedOriginIDs.find(FinalOID);
-        if (NextOriginID == VistedOriginIDs.end())
-          break;
-        FinalOID = NextOriginID->second;
-      }
-      return OriginFlowChain;
-    };
-
-    const auto InsertVistedOriginIDs =
-        [&VistedOriginIDs, &FinalOID](llvm::ArrayRef<OriginID> OriginFlowChain,
-                                      OriginID &StartOID) {
-          if (!VistedOriginIDs.empty())
-            VistedOriginIDs.insert({OriginFlowChain[0], StartOID});
-
-          for (size_t i = 0; i < OriginFlowChain.size() - 1; ++i)
-            VistedOriginIDs.insert(
-                {OriginFlowChain[i + 1], OriginFlowChain[i]});
-
-          StartOID = OriginFlowChain.back();
-          FinalOID = StartOID;
-        };
-
     const CFGBlock *EndBlock = nullptr;
     size_t BlockID = FactMgr.getBlockID(StartPoint);
     for (const CFGBlock *Block : *Cfg)
@@ -259,14 +230,14 @@ class AnalysisImpl
         break;
       }
 
-    using SearchContext = std::pair<const CFGBlock *, OriginID>;
-    std::queue<SearchContext> PendingContext;
-    llvm::SmallSet<SearchContext, 32> VistedContext;
-    PendingContext.push({EndBlock, StartOID});
+    OriginID CurrOID = StartOID;
+    llvm::SmallVector<OriginID> OriginFlowChain;
+    std::queue<const CFGBlock *> PendingState;
+    PendingState.push(EndBlock);
 
-    while (!PendingContext.empty()) {
-      auto [CurrBlock, CurrOID] = PendingContext.front();
-      PendingContext.pop();
+    while (!PendingState.empty()) {
+      const CFGBlock *CurrBlock = PendingState.front();
+      PendingState.pop();
 
       DEBUG_WITH_TYPE("LifetimeBuildOriginFlow",
                       llvm::dbgs() << "CurrBlockID: " << 
CurrBlock->getBlockID()
@@ -274,20 +245,19 @@ class AnalysisImpl
 
       const auto [BuildResult, Complete] =
           buildOriginFlowChain(CurrBlock, CurrOID, TargetLoan);
-      if (!BuildResult.empty())
-        InsertVistedOriginIDs(BuildResult, CurrOID);
+      if (!BuildResult.empty()) {
+        OriginFlowChain.append(BuildResult);
+        CurrOID = BuildResult.back();
+      }
 
       if (Complete)
-        return OriginFlowChainFilter(*FinalOID);
+        return OriginFlowChain;
 
       DEBUG_WITH_TYPE("LifetimeBuildOriginFlow",
                       llvm::dbgs() << "EndOriginID: " << CurrOID << "\n");
 
-      for (const CFGBlock *Block : CurrBlock->preds()) {
-        SearchContext Context = {Block, CurrOID};
-        if (Block && VistedContext.insert(Context).second)
-          PendingContext.push(Context);
-      }
+      for (const CFGBlock *Block : CurrBlock->preds())
+        PendingState.push(Block);
     }
 
     llvm_unreachable(
diff --git a/clang/lib/Sema/SemaLifetimeSafety.h 
b/clang/lib/Sema/SemaLifetimeSafety.h
index 4130e72b8b88a..ff127baa5549d 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -580,10 +580,10 @@ class LifetimeSafetySemaHelperImpl : public 
LifetimeSafetySemaHelper {
     if (OriginExprChain.empty())
       return;
 
-    const Expr *LastExpr = OriginExprChain.front();
+    const Expr *LastExpr = OriginExprChain.back();
     std::string IssueStr = getDiagSubjectDescription(LastExpr);
 
-    for (const Expr *CurrExpr : OriginExprChain.drop_front()) {
+    for (const Expr *CurrExpr : reverse(OriginExprChain.drop_back())) {
       if (!shouldShowInAliasChain(CurrExpr, LastExpr))
         continue;
       S.Diag(CurrExpr->getBeginLoc(),
diff --git a/clang/test/Sema/LifetimeSafety/safety.cpp 
b/clang/test/Sema/LifetimeSafety/safety.cpp
index 82d443b179bd3..fa2779a9b8811 100644
--- a/clang/test/Sema/LifetimeSafety/safety.cpp
+++ b/clang/test/Sema/LifetimeSafety/safety.cpp
@@ -3905,3 +3905,45 @@ struct [[gsl::Pointer()]] PtrWithInt { int x; };
 PtrWithInt f() {
   return PtrWithInt{10};
 }
+
+//===----------------------------------------------------------------------===//
+// buildOriginFlowChain
+//===----------------------------------------------------------------------===//
+
+void used_variable_reassigned() {
+  View p, q, r;
+  {
+    MyObj a;
+    p = a; // expected-warning {{local variable 'a' does not live long enough}}
+    q = p; // expected-note {{local variable 'p' aliases the storage of local 
variable 'a'}}
+    r = q; // expected-note {{local variable 'q' aliases the storage of local 
variable 'a'}}
+  }        // expected-note {{destroyed here}}
+  r.use(); // expected-note {{later used here}}
+
+  MyObj b;
+  r = b;
+  r.use();
+}
+
+void multi_reassigned(bool condition) {
+  MyObj v1, v2, v3;
+  View p1, p2, p3, p4;
+  {
+    MyObj v4;
+
+    p1 = v1;
+    p2 = v2;
+    p3 = v3;
+    p4 = v4;    // expected-warning {{local variable 'v4' does not live long 
enough}}
+
+    while (condition) {
+      View temp = p1;
+      p1 = p2;  // expected-note {{local variable 'p2' aliases the storage of 
local variable 'v4'}}
+      p2 = p3;  // expected-note {{local variable 'p3' aliases the storage of 
local variable 'v4'}}
+      p3 = p4;  // expected-note {{local variable 'p4' aliases the storage of 
local variable 'v4'}}
+      p4 = temp;
+    }
+  }  // expected-note {{destroyed here}}
+
+  p1.use();  // expected-note {{later used here}}
+}

>From 1b3bda5fd643f2dd2d9d54998edd2b9c324ffd4e Mon Sep 17 00:00:00 2001
From: Yuan Suo <[email protected]>
Date: Tue, 23 Jun 2026 21:48:00 +0800
Subject: [PATCH 14/15] rebase

Signed-off-by: Yuan Suo <[email protected]>
---
 clang/test/Sema/LifetimeSafety/safety.cpp | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/clang/test/Sema/LifetimeSafety/safety.cpp 
b/clang/test/Sema/LifetimeSafety/safety.cpp
index fa2779a9b8811..d1f8ffa6c430e 100644
--- a/clang/test/Sema/LifetimeSafety/safety.cpp
+++ b/clang/test/Sema/LifetimeSafety/safety.cpp
@@ -236,7 +236,7 @@ void overrides_potential(bool cond) {
   {
     MyObj s;
     q = &s;       // expected-warning {{does not live long enough}}
-    p = q;
+    p = q;        // expected-note {{local variable 'q' aliases the storage of 
local variable 's'}}
   }               // expected-note {{local variable 's' is destroyed here}}
 
   if (cond) {
@@ -1236,8 +1236,11 @@ void conditional_operator_lifetimebound_nested(bool 
cond) {
   MyObj* p;
   {
     MyObj a, b;
-    p = Identity(cond ? Identity(&a)    // expected-warning {{local variable 
'a' does not live long enough}}
-                      : Identity(&b));  // expected-warning {{local variable 
'b' does not live long enough}}
+    p = Identity(cond ? Identity(&a)    // expected-warning {{local variable 
'a' does not live long enough}} \
+                                        // expected-note 2 {{result of call to 
'Identity' aliases the storage of local variable 'a'}} \
+                                        // expected-note {{result of call to 
'Identity' aliases the storage of local variable 'b'}}
+                      : Identity(&b));  // expected-warning {{local variable 
'b' does not live long enough}} \
+                                        // expected-note {{result of call to 
'Identity' aliases the storage of local variable 'b'}}
   }  // expected-note {{local variable 'b' is destroyed here}} expected-note 
{{local variable 'a' is destroyed here}}
   (void)*p;  // expected-note 2 {{later used here}}
 }
@@ -2637,8 +2640,7 @@ void chained_defaulted_assignment_propagation() {
     std::string str{"abc"};
     S a = getS(str); // expected-warning {{local variable 'str' does not live 
long enough}} \
                      // expected-note {{result of call to 'getS' aliases the 
storage of local variable 'str'}}
-    c = b = a;       // expected-note {{local variable 'a' aliases the storage 
of local variable 'str'}}\
-                     // expected-note {{expression aliases the storage of 
local variable 'str'}}
+    c = b = a;       // expected-note {{local variable 'a' aliases the storage 
of local variable 'str'}}
   }                  // expected-note {{local variable 'str' is destroyed 
here}}
   use(c);            // expected-note {{later used here}}
 }

>From e023dc361b76c9c29b2283f3931a1f9e7bbb4ebb Mon Sep 17 00:00:00 2001
From: Yuan Suo <[email protected]>
Date: Tue, 23 Jun 2026 22:15:43 +0800
Subject: [PATCH 15/15] Add a test case for function pointer

Signed-off-by: Yuan Suo <[email protected]>
---
 clang/test/Sema/LifetimeSafety/safety.cpp | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/clang/test/Sema/LifetimeSafety/safety.cpp 
b/clang/test/Sema/LifetimeSafety/safety.cpp
index d1f8ffa6c430e..0ba3ff527122c 100644
--- a/clang/test/Sema/LifetimeSafety/safety.cpp
+++ b/clang/test/Sema/LifetimeSafety/safety.cpp
@@ -855,6 +855,18 @@ void lifetimebound_multiple_args_potential(bool cond) {
   v.use();                         // expected-note 2 {{later used here}}
 }
 
+// FIXME: Detect this.
+void func_pointer() {
+  View p;
+  View (*func_ptr)(View v [[clang::lifetimebound]]);
+  {
+   MyObj s;
+   View a = Identity(s);
+   p = func_ptr(a);
+  }
+  p.use();
+}
+
 View SelectFirst(View a [[clang::lifetimebound]], View b);
 void lifetimebound_mixed_args() {
   View v;

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

Reply via email to