https://github.com/iitianpushkar updated 
https://github.com/llvm/llvm-project/pull/206337

>From 0798fec97660d9bfa086d157ded8bf070e48b7c5 Mon Sep 17 00:00:00 2001
From: iitianpushkar <[email protected]>
Date: Sun, 28 Jun 2026 12:04:08 +0530
Subject: [PATCH 1/6] added note for lifetimebound mark

---
 clang/include/clang/Basic/DiagnosticSemaKinds.td | 1 +
 1 file changed, 1 insertion(+)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 7e20630708312..a024e4cebfdcc 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -11069,6 +11069,7 @@ def note_lifetime_safety_escapes_to_global_here: 
Note<"escapes to this global st
 def note_lifetime_safety_escapes_to_static_storage_here: Note<"escapes to this 
static storage">;
 def note_lifetime_safety_lifetimebound_here: Note<"'lifetimebound' attribute 
appears here on the definition">;
 def note_lifetime_safety_aliases_storage : Note<"%0 aliases the storage of 
%1">;
+def note_lifetime_safety_aliases_storage_lifetimebound : Note<"%0 aliases the 
storage of %1" because %2 is marked [[clang::lifetimebound]]">; 
 
 def warn_lifetime_safety_intra_tu_param_suggestion
     : Warning<"parameter in intra-TU function should be marked "

>From b7dc4cdf3e6f8bdb92250f00e9c9940c5947a1d4 Mon Sep 17 00:00:00 2001
From: iitianpushkar <[email protected]>
Date: Sun, 28 Jun 2026 13:20:31 +0530
Subject: [PATCH 2/6] added lifetimeboundinfo struct

---
 .../Analysis/Analyses/LifetimeSafety/Facts.h    | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)

diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
index 5c671a93b149c..1e0ef8680f7db 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
@@ -131,24 +131,37 @@ class ExpireFact : public Fact {
 };
 
 class OriginFlowFact : public Fact {
+public:
+  struct LifetimeBoundInfo {
+    const Expr *Call = nullptr;
+    const ParmVarDecl *Param = nullptr;
+    bool isImplicitObject = false;
+  };
+
+private:
   OriginID OIDDest;
   OriginID OIDSrc;
   // True if the destination origin should be killed (i.e., its current loans
   // cleared) before the source origin's loans are flowed into it.
   bool KillDest;
+  std::optional<LifetimeBoundInfo> LifetimeBound;
 
 public:
   static bool classof(const Fact *F) {
     return F->getKind() == Kind::OriginFlow;
   }
 
-  OriginFlowFact(OriginID OIDDest, OriginID OIDSrc, bool KillDest)
+  OriginFlowFact(OriginID OIDDest, OriginID OIDSrc, bool KillDest,
+                 std::optional<LifetimeBoundInfo> LifetimeBound = std::nullopt)
       : Fact(Kind::OriginFlow), OIDDest(OIDDest), OIDSrc(OIDSrc),
-        KillDest(KillDest) {}
+        KillDest(KillDest), LifetimeBound(LifetimeBound) {}
 
   OriginID getDestOriginID() const { return OIDDest; }
   OriginID getSrcOriginID() const { return OIDSrc; }
   bool getKillDest() const { return KillDest; }
+  std::optional<LifetimeBoundInfo> getLifetimeBoundInfo() const {
+    return LifetimeBound;
+  }
 
   void dump(llvm::raw_ostream &OS, const LoanManager &,
             const OriginManager &OM) const override;

>From 84acd32e64085ef3dfd580658f222d97a9801240 Mon Sep 17 00:00:00 2001
From: iitianpushkar <[email protected]>
Date: Sun, 28 Jun 2026 18:18:09 +0530
Subject: [PATCH 3/6] added full flow to include lifetimebound reason in alias
 note

---
 .../Analyses/LifetimeSafety/LifetimeSafety.h  |  9 +++-
 .../Analyses/LifetimeSafety/LoanPropagation.h | 14 ++++++
 .../clang/Basic/DiagnosticSemaKinds.td        |  3 +-
 clang/lib/Analysis/LifetimeSafety/Checker.cpp | 26 ++++++----
 clang/lib/Analysis/LifetimeSafety/Facts.cpp   |  2 +
 .../LifetimeSafety/FactsGenerator.cpp         | 39 +++++++++++----
 .../LifetimeSafety/LoanPropagation.cpp        | 46 ++++++++++++++++--
 clang/lib/Sema/SemaLifetimeSafety.h           | 47 +++++++++++++++----
 8 files changed, 151 insertions(+), 35 deletions(-)

diff --git 
a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
index 80a23f18baebd..c177e9e222bdc 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
@@ -49,6 +49,12 @@ enum class WarningScope {
   IntraTU  // For warnings on functions local to a Translation Unit.
 };
 
+struct LifetimeSafetyAliasChainEntry {
+  const Expr *E = nullptr;
+  const ParmVarDecl *LifetimeBoundParam = nullptr;
+  bool LifetimeBoundImplicitObject = false;
+};
+
 /// Abstract interface for operations requiring Sema access.
 ///
 /// This class exists to break a circular dependency: the LifetimeSafety
@@ -67,7 +73,8 @@ class LifetimeSafetySemaHelper {
   virtual void reportUseAfterScope(const Expr *IssueExpr, const Expr *UseExpr,
                                    const Expr *MovedExpr,
                                    SourceLocation FreeLoc,
-                                   llvm::ArrayRef<const Expr *> ExprChain) {}
+                                   
llvm::ArrayRef<LifetimeSafetyAliasChainEntry>
+                                       AliasChain) {}
 
   virtual void reportUseAfterReturn(const Expr *IssueExpr,
                                     const Expr *ReturnExpr,
diff --git 
a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
index 724c6eee7d3c2..92d69f99c8794 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
@@ -29,6 +29,11 @@ namespace clang::lifetimes::internal {
 using LoanSet = llvm::ImmutableSet<LoanID>;
 using OriginLoanMap = llvm::ImmutableMap<OriginID, LoanSet>;
 
+struct OriginFlowChainStep {
+  OriginID OID;
+  const OriginFlowFact *FlowFact;
+};
+
 class LoanPropagationAnalysis {
 public:
   LoanPropagationAnalysis(const CFG &C, AnalysisDeclContext &AC, FactManager 
&F,
@@ -51,6 +56,15 @@ class LoanPropagationAnalysis {
   llvm::SmallVector<OriginID>
   buildOriginFlowChain(const UseFact *UF, const LoanID TargetLoan) const;
 
+  llvm::SmallVector<OriginFlowChainStep>
+  buildOriginFlowChainWithFacts(ProgramPoint StartPoint,
+                                const OriginID StartOID,
+                                const LoanID TargetLoan) const;
+
+  llvm::SmallVector<OriginFlowChainStep>
+  buildOriginFlowChainWithFacts(const UseFact *UF,
+                                const LoanID TargetLoan) const;
+
 private:
   class Impl;
   std::unique_ptr<Impl> PImpl;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index a024e4cebfdcc..63d7b54b5e735 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -11069,7 +11069,8 @@ def note_lifetime_safety_escapes_to_global_here: 
Note<"escapes to this global st
 def note_lifetime_safety_escapes_to_static_storage_here: Note<"escapes to this 
static storage">;
 def note_lifetime_safety_lifetimebound_here: Note<"'lifetimebound' attribute 
appears here on the definition">;
 def note_lifetime_safety_aliases_storage : Note<"%0 aliases the storage of 
%1">;
-def note_lifetime_safety_aliases_storage_lifetimebound : Note<"%0 aliases the 
storage of %1" because %2 is marked [[clang::lifetimebound]]">; 
+def note_lifetime_safety_aliases_storage_lifetimebound : Note<
+  "%0 aliases the storage of %1 because %2 is marked 
[[clang::lifetimebound]]">;
 
 def warn_lifetime_safety_intra_tu_param_suggestion
     : Warning<"parameter in intra-TU function should be marked "
diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp 
b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
index 746ebbfb15c39..335ca28861f4f 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -272,7 +272,8 @@ class LifetimeChecker {
           // Scope-based expiry (use-after-scope).
           SemaHelper->reportUseAfterScope(
               IssueExpr, UF->getUseExpr(), MovedExpr, ExpiryLoc,
-              getExprChain(LoanPropagation.buildOriginFlowChain(UF, LID)));
+              getAliasChain(
+                  LoanPropagation.buildOriginFlowChainWithFacts(UF, LID)));
 
       } else if (const auto *OEF =
                      CausingFact.dyn_cast<const OriginEscapesFact *>()) {
@@ -524,13 +525,22 @@ class LifetimeChecker {
   /// Given a chain of origins that shows how a loan propagates, this function
   /// extracts the corresponding expressions for each origin. Origins that 
refer
   /// to declarations (rather than expressions) are skipped.
-  llvm::SmallVector<const Expr *>
-  getExprChain(llvm::ArrayRef<OriginID> OriginFlowChain) {
-    llvm::SmallVector<const Expr *> rs;
-    for (const OriginID CurrOID : OriginFlowChain)
-      if (const Expr *CurrExpr =
-              FactMgr.getOriginMgr().getOrigin(CurrOID).getExpr())
-        rs.push_back(CurrExpr);
+  llvm::SmallVector<LifetimeSafetyAliasChainEntry>
+  getAliasChain(llvm::ArrayRef<OriginFlowChainStep> OriginFlowChain) {
+    llvm::SmallVector<LifetimeSafetyAliasChainEntry> rs;
+    for (const OriginFlowChainStep &Step : OriginFlowChain) {
+      const Expr *CurrExpr = 
FactMgr.getOriginMgr().getOrigin(Step.OID).getExpr();
+      if (!CurrExpr)
+        continue;
+
+      LifetimeSafetyAliasChainEntry Entry;
+      Entry.E = CurrExpr;
+      if (auto Info = Step.FlowFact->getLifetimeBoundInfo()) {
+        Entry.LifetimeBoundParam = Info->Param;
+        Entry.LifetimeBoundImplicitObject = Info->isImplicitObject;
+      }
+      rs.push_back(Entry);
+    }
     return rs;
   }
 };
diff --git a/clang/lib/Analysis/LifetimeSafety/Facts.cpp 
b/clang/lib/Analysis/LifetimeSafety/Facts.cpp
index 3d7fbcdacc830..6c1fb7ba4e214 100644
--- a/clang/lib/Analysis/LifetimeSafety/Facts.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Facts.cpp
@@ -46,6 +46,8 @@ void OriginFlowFact::dump(llvm::raw_ostream &OS, const 
LoanManager &,
   OS << "\tSrc:  ";
   OM.dump(getSrcOriginID(), OS);
   OS << (getKillDest() ? "" : ", Merge");
+  if (getLifetimeBoundInfo())
+    OS << ", LifetimeBound";
   OS << "\n";
 }
 
diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp 
b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
index 8358c69a5165a..dd6838630947e 100644
--- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -1085,28 +1085,46 @@ void FactsGenerator::handleFunctionCall(const Expr 
*Call,
     flow(CallList, getOriginsList(*Args[0]), /*Kill=*/true);
     return;
   }
-  auto IsArgLifetimeBound = [FD, &Args](unsigned I) -> bool {
+  auto GetLifetimeBoundInfo = [FD, Call](unsigned I)
+      -> std::optional<OriginFlowFact::LifetimeBoundInfo> {
     const ParmVarDecl *PVD = nullptr;
     if (const auto *Method = dyn_cast<CXXMethodDecl>(FD);
         Method && Method->isInstance() && !isa<CXXConstructorDecl>(FD)) {
       if (I == 0)
         // For the 'this' argument, the attribute is on the method itself.
-        return implicitObjectParamIsLifetimeBound(Method) ||
-               shouldTrackImplicitObjectArg(
-                   *Args[0], Method, /*RunningUnderLifetimeSafety=*/true);
+        return implicitObjectParamIsLifetimeBound(Method)
+                   ? std::optional<OriginFlowFact::LifetimeBoundInfo>(
+                         OriginFlowFact::LifetimeBoundInfo{
+                             Call, /*Param=*/nullptr,
+                             /*isImplicitObject=*/true})
+                   : std::nullopt;
       if ((I - 1) < Method->getNumParams())
         // For explicit arguments, find the corresponding parameter
         // declaration.
         PVD = Method->getParamDecl(I - 1);
+    } else if (I < FD->getNumParams()) {
+      // For free functions or static methods.
+      PVD = FD->getParamDecl(I);
+    }
+    if (PVD && PVD->hasAttr<clang::LifetimeBoundAttr>())
+      return OriginFlowFact::LifetimeBoundInfo{Call, PVD,
+                                               /*isImplicitObject=*/false};
+    return std::nullopt;
+  };
+  auto IsArgLifetimeBound = [FD, &Args, &GetLifetimeBoundInfo](unsigned I) {
+    if (GetLifetimeBoundInfo(I))
+      return true;
+    if (const auto *Method = dyn_cast<CXXMethodDecl>(FD);
+        Method && Method->isInstance() && !isa<CXXConstructorDecl>(FD)) {
+      if (I == 0)
+        return shouldTrackImplicitObjectArg(
+            *Args[0], Method, /*RunningUnderLifetimeSafety=*/true);
     } else if (I == 0 && shouldTrackFirstArgument(FD)) {
       return true;
     } else if (I == 1 && shouldTrackSecondArgument(FD)) {
       return true;
-    } else if (I < FD->getNumParams()) {
-      // For free functions or static methods.
-      PVD = FD->getParamDecl(I);
     }
-    return PVD ? PVD->hasAttr<clang::LifetimeBoundAttr>() : false;
+    return false;
   };
   auto shouldTrackPointerImplicitObjectArg = [FD, &Args](unsigned I) -> bool {
     const auto *Method = dyn_cast<CXXMethodDecl>(FD);
@@ -1148,7 +1166,7 @@ void FactsGenerator::handleFunctionCall(const Expr *Call,
         // inner origins.
         CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>(
             CallList->getOuterOriginID(), ArgList->getOuterOriginID(),
-            KillSrc));
+            KillSrc, GetLifetimeBoundInfo(I)));
         KillSrc = false;
       }
     } else if (shouldTrackPointerImplicitObjectArg(I)) {
@@ -1164,7 +1182,8 @@ void FactsGenerator::handleFunctionCall(const Expr *Call,
       // pointer/reference itself must not outlive the arguments. This
       // only constrains the top-level origin.
       CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>(
-          CallList->getOuterOriginID(), ArgList->getOuterOriginID(), KillSrc));
+          CallList->getOuterOriginID(), ArgList->getOuterOriginID(), KillSrc,
+          GetLifetimeBoundInfo(I)));
       KillSrc = false;
     }
   }
diff --git a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp 
b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
index a67b1b3c0f826..01ef3b67e7351 100644
--- a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
@@ -202,14 +202,15 @@ class AnalysisImpl
     return getLoans(getState(P), OID);
   }
 
-  llvm::SmallVector<OriginID>
-  buildOriginFlowChain(ProgramPoint StartPoint, const OriginID StartOID,
-                       const LoanID TargetLoan) const {
+  llvm::SmallVector<OriginFlowChainStep>
+  buildOriginFlowChainWithFacts(ProgramPoint StartPoint,
+                                const OriginID StartOID,
+                                const LoanID TargetLoan) const {
     assert(getLoans(StartOID, StartPoint).contains(TargetLoan) &&
            "TargetLoan must be present in the StartOID at the StartPoint");
 
     OriginID CurrOID = StartOID;
-    llvm::SmallVector<OriginID> OriginFlowChain;
+    llvm::SmallVector<OriginFlowChainStep> OriginFlowChain;
     llvm::ArrayRef<const Fact *> Facts = 
FactMgr.getBlockContaining(StartPoint);
     const auto *StartIt = llvm::find(Facts, StartPoint);
     assert(StartIt != Facts.end());
@@ -231,7 +232,7 @@ class AnalysisImpl
       const OriginID SrcOriginID = OFF->getSrcOriginID();
       if (!getLoans(SrcOriginID, OFF).contains(TargetLoan))
         continue;
-      OriginFlowChain.push_back(SrcOriginID);
+      OriginFlowChain.push_back({SrcOriginID, OFF});
       CurrOID = SrcOriginID;
     }
 
@@ -243,6 +244,28 @@ class AnalysisImpl
     return {};
   }
 
+  llvm::SmallVector<OriginID>
+  buildOriginFlowChain(ProgramPoint StartPoint, const OriginID StartOID,
+                       const LoanID TargetLoan) const {
+    llvm::SmallVector<OriginID> OriginFlowChain;
+    for (const OriginFlowChainStep &Step :
+         buildOriginFlowChainWithFacts(StartPoint, StartOID, TargetLoan))
+      OriginFlowChain.push_back(Step.OID);
+    return OriginFlowChain;
+  }
+
+  llvm::SmallVector<OriginFlowChainStep>
+  buildOriginFlowChainWithFacts(const UseFact *UF,
+                                const LoanID TargetLoan) const {
+    for (const OriginList *Cur = UF->getUsedOrigins(); Cur;
+         Cur = Cur->peelOuterOrigin())
+      if (getLoans(Cur->getOuterOriginID(), UF).contains(TargetLoan))
+        return buildOriginFlowChainWithFacts(UF, Cur->getOuterOriginID(),
+                                             TargetLoan);
+
+    return {};
+  }
+
   llvm::SmallVector<OriginID>
   buildOriginFlowChain(const UseFact *UF, const LoanID TargetLoan) const {
     for (const OriginList *Cur = UF->getUsedOrigins(); Cur;
@@ -315,4 +338,17 @@ LoanPropagationAnalysis::buildOriginFlowChain(const 
UseFact *UF,
                                               const LoanID TargetLoan) const {
   return PImpl->buildOriginFlowChain(UF, TargetLoan);
 }
+
+llvm::SmallVector<OriginFlowChainStep>
+LoanPropagationAnalysis::buildOriginFlowChainWithFacts(
+    ProgramPoint StartPoint, const OriginID StartOID,
+    const LoanID TargetLoan) const {
+  return PImpl->buildOriginFlowChainWithFacts(StartPoint, StartOID, 
TargetLoan);
+}
+
+llvm::SmallVector<OriginFlowChainStep>
+LoanPropagationAnalysis::buildOriginFlowChainWithFacts(
+    const UseFact *UF, const LoanID TargetLoan) const {
+  return PImpl->buildOriginFlowChainWithFacts(UF, TargetLoan);
+}
 } // namespace clang::lifetimes::internal
diff --git a/clang/lib/Sema/SemaLifetimeSafety.h 
b/clang/lib/Sema/SemaLifetimeSafety.h
index a3d546792fe41..3d8ca9e90abff 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -103,7 +103,8 @@ class LifetimeSafetySemaHelperImpl : public 
LifetimeSafetySemaHelper {
 
   void reportUseAfterScope(const Expr *IssueExpr, const Expr *UseExpr,
                            const Expr *MovedExpr, SourceLocation FreeLoc,
-                           llvm::ArrayRef<const Expr *> ExprChain) override {
+                           llvm::ArrayRef<LifetimeSafetyAliasChainEntry>
+                               AliasChain) override {
     unsigned DiagID = MovedExpr
                           ? diag::warn_lifetime_safety_use_after_scope_moved
                           : diag::warn_lifetime_safety_use_after_scope;
@@ -117,7 +118,7 @@ class LifetimeSafetySemaHelperImpl : public 
LifetimeSafetySemaHelper {
     S.Diag(FreeLoc, diag::note_lifetime_safety_destroyed_here)
         << DestroyedSubject;
 
-    reportAliasingChain(ExprChain);
+    reportAliasingChain(AliasChain);
 
     S.Diag(UseExpr->getExprLoc(), diag::note_lifetime_safety_used_here)
         << UseExpr->getSourceRange();
@@ -655,20 +656,46 @@ class LifetimeSafetySemaHelperImpl : public 
LifetimeSafetySemaHelper {
     return CurrExpr->getSourceRange() != LastExpr->getSourceRange();
   }
 
-  void reportAliasingChain(llvm::ArrayRef<const Expr *> OriginExprChain) {
+  void reportAliasingChain(
+      llvm::ArrayRef<LifetimeSafetyAliasChainEntry> OriginExprChain) {
     if (OriginExprChain.empty())
       return;
 
-    const Expr *LastExpr = OriginExprChain.back();
+    LifetimeSafetyAliasChainEntry Last = OriginExprChain.back();
+    const Expr *LastExpr = Last.E;
     std::string IssueStr = getDiagSubjectDescription(LastExpr);
 
-    for (const Expr *CurrExpr : reverse(OriginExprChain.drop_back())) {
-      if (!shouldShowInAliasChain(CurrExpr, LastExpr))
+    for (LifetimeSafetyAliasChainEntry Curr :
+         reverse(OriginExprChain.drop_back())) {
+      const Expr *CurrExpr = Curr.E;
+      if (!shouldShowInAliasChain(CurrExpr, LastExpr)) {
+        if (!Last.LifetimeBoundImplicitObject && !Last.LifetimeBoundParam &&
+            (Curr.LifetimeBoundImplicitObject || Curr.LifetimeBoundParam)) {
+          Last.LifetimeBoundImplicitObject = Curr.LifetimeBoundImplicitObject;
+          Last.LifetimeBoundParam = Curr.LifetimeBoundParam;
+        }
         continue;
-      S.Diag(CurrExpr->getBeginLoc(),
-             diag::note_lifetime_safety_aliases_storage)
-          << CurrExpr->getSourceRange() << getDiagSubjectDescription(CurrExpr)
-          << IssueStr;
+      }
+      if (Last.LifetimeBoundImplicitObject || Last.LifetimeBoundParam) {
+        std::string LifetimeBoundSubject = "the implicit object parameter";
+        if (Last.LifetimeBoundParam) {
+          LifetimeBoundSubject = "parameter ";
+          if (Last.LifetimeBoundParam->getIdentifier())
+            LifetimeBoundSubject +=
+                "'" + Last.LifetimeBoundParam->getNameAsString() + "'";
+          else
+            LifetimeBoundSubject += "'<unnamed>'";
+        }
+        S.Diag(CurrExpr->getBeginLoc(),
+               diag::note_lifetime_safety_aliases_storage_lifetimebound)
+            << CurrExpr->getSourceRange() << 
getDiagSubjectDescription(CurrExpr)
+            << IssueStr << LifetimeBoundSubject;
+      } else
+        S.Diag(CurrExpr->getBeginLoc(),
+               diag::note_lifetime_safety_aliases_storage)
+            << CurrExpr->getSourceRange() << 
getDiagSubjectDescription(CurrExpr)
+            << IssueStr;
+      Last = Curr;
       LastExpr = CurrExpr;
     }
   }

>From 9208df06da6819083e24a7652de1fb0da06ecae1 Mon Sep 17 00:00:00 2001
From: iitianpushkar <[email protected]>
Date: Sun, 28 Jun 2026 18:33:51 +0530
Subject: [PATCH 4/6] updated tests to include lifetimebound reason in note

---
 .../LifetimeSafety/annotation-suggestions.cpp |  12 +-
 clang/test/Sema/LifetimeSafety/safety-c.c     |   2 +-
 clang/test/Sema/LifetimeSafety/safety.cpp     | 109 +++++++++---------
 3 files changed, 62 insertions(+), 61 deletions(-)

diff --git a/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp 
b/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp
index 98c528516b8e7..f551f17d8f5e4 100644
--- a/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp
+++ b/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp
@@ -278,21 +278,21 @@ View return_view_field(const ViewProvider& v) {    // 
expected-warning {{paramet
 void test_get_on_temporary_pointer() {
   const ReturnsSelf* s_ref = &ReturnsSelf().get(); // expected-warning 
{{temporary object does not live long enough}}.
                                                    // expected-note@-1 
{{temporary object is destroyed here}}
-                                                   // expected-note@-2 
{{result of call to 'get' aliases the storage of temporary object}}
+                                                   // expected-note@-2 
{{result of call to 'get' aliases the storage of temporary object because the 
implicit object parameter is marked [[clang::lifetimebound]]}}
   (void)s_ref;                                     // expected-note {{later 
used here}}
 }
 
 void test_get_on_temporary_ref() {
   const ReturnsSelf& s_ref = ReturnsSelf().get();  // expected-warning 
{{temporary object does not live long enough}}.
                                                    // expected-note@-1 
{{temporary object is destroyed here}}
-                                                   // expected-note@-2 
{{result of call to 'get' aliases the storage of temporary object}}
+                                                   // expected-note@-2 
{{result of call to 'get' aliases the storage of temporary object because the 
implicit object parameter is marked [[clang::lifetimebound]]}}
   (void)s_ref;                                     // expected-note {{later 
used here}}
 }
 
 void test_getView_on_temporary() {
   View sv = ViewProvider{1}.getView();      // expected-warning {{temporary 
object does not live long enough}}.
                                             // expected-note@-1 {{temporary 
object is destroyed here}}
-                                            // expected-note@-2 {{result of 
call to 'getView' aliases the storage of temporary object}}
+                                            // expected-note@-2 {{result of 
call to 'getView' aliases the storage of temporary object because the implicit 
object parameter is marked [[clang::lifetimebound]]}}
   (void)sv;                                 // expected-note {{later used 
here}}
 }
 
@@ -603,7 +603,7 @@ void uaf_via_inferred_lifetimebound() {
   {
     int local;
     f = return_lambda_capturing_param(local); // expected-warning {{local 
variable 'local' does not live long enough}} \
-                                              // expected-note {{result of 
call to 'return_lambda_capturing_param' aliases the storage of local variable 
'local'}}
+                                              // expected-note {{result of 
call to 'return_lambda_capturing_param' aliases the storage of local variable 
'local' because parameter 'x' is marked [[clang::lifetimebound]]}}
   } // expected-note {{local variable 'local' is destroyed here}}
   (void)f; // expected-note {{later used here}}
 }
@@ -627,7 +627,7 @@ void test_inference() {
   {
     MyObj obj;
     ptr = create_target(obj); // expected-warning {{local variable 'obj' does 
not live long enough}} \
-                              // expected-note {{result of call to 
'create_target' aliases the storage of local variable 'obj'}}
+                              // expected-note {{result of call to 
'create_target' aliases the storage of local variable 'obj' because parameter 
'obj' is marked [[clang::lifetimebound]]}}
   } // expected-note {{local variable 'obj' is destroyed here}}
   (void)ptr; // expected-note {{later used here}}
 }
@@ -642,7 +642,7 @@ View* MakeView(const MyObj& in) { // expected-warning 
{{parameter in intra-TU fu
 void test_new_allocation() {
   View* v = MakeView(MyObj{}); // expected-warning {{temporary object does not 
live long enough}} \
                                // expected-note {{temporary object is 
destroyed here}} \
-                               // expected-note {{result of call to 'MakeView' 
aliases the storage of temporary object}}
+                               // expected-note {{result of call to 'MakeView' 
aliases the storage of temporary object because parameter 'in' is marked 
[[clang::lifetimebound]]}}
   (void)v;                     // expected-note {{later used here}}
 }
 
diff --git a/clang/test/Sema/LifetimeSafety/safety-c.c 
b/clang/test/Sema/LifetimeSafety/safety-c.c
index e9443899c9935..61fc6c8d7dbb3 100644
--- a/clang/test/Sema/LifetimeSafety/safety-c.c
+++ b/clang/test/Sema/LifetimeSafety/safety-c.c
@@ -63,7 +63,7 @@ void lifetimebound_call(void) {
   {
     int i;
     p = identity(&i); // expected-warning {{local variable 'i' does not live 
long enough}} \
-                      // expected-note {{result of call to 'identity' aliases 
the storage of local variable 'i'}}
+                      // expected-note {{result of call to 'identity' aliases 
the storage of local variable 'i' because parameter 'p' is marked 
[[clang::lifetimebound]]}}
   }                   // expected-note {{local variable 'i' is destroyed here}}
   (void)*p;           // expected-note {{later used here}}
 }
diff --git a/clang/test/Sema/LifetimeSafety/safety.cpp 
b/clang/test/Sema/LifetimeSafety/safety.cpp
index 2cbf651eb46b5..95037b634228a 100644
--- a/clang/test/Sema/LifetimeSafety/safety.cpp
+++ b/clang/test/Sema/LifetimeSafety/safety.cpp
@@ -697,7 +697,7 @@ void test_lifetimebound_multi_level() {
     int** pp = &p;  
     int*** ppp = &pp; // expected-warning {{local variable 'pp' does not live 
long enough}}
     result = return_inner_ptr_addr(ppp); // expected-note {{local variable 
'ppp' aliases the storage of local variable 'pp'}} \
-                                         // expected-note {{result of call to 
'return_inner_ptr_addr' aliases the storage of local variable 'pp'}}
+                                         // expected-note {{result of call to 
'return_inner_ptr_addr' aliases the storage of local variable 'pp' because 
parameter 'ppp' is marked [[clang::lifetimebound]]}}
   }                   // expected-note {{local variable 'pp' is destroyed 
here}}
   (void)**result;     // expected-note {{used here}}
 }
@@ -821,7 +821,7 @@ void lifetimebound_simple_function() {
   {
     MyObj obj;
     v = Identity(obj); // expected-warning {{local variable 'obj' does not 
live long enough}} \
-                       // expected-note {{result of call to 'Identity' aliases 
the storage of local variable 'obj'}}
+                       // expected-note {{result of call to 'Identity' aliases 
the storage of local variable 'obj' because parameter 'v' is marked 
[[clang::lifetimebound]]}}
   }                    // expected-note {{local variable 'obj' is destroyed 
here}}
   v.use();             // expected-note {{later used here}}
 }
@@ -830,8 +830,8 @@ void lifetimebound_multiple_args_definite() {
   View v;
   {
     MyObj obj1, obj2;
-    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'}}
+    v = Choose(true,  // expected-note {{result of call to 'Choose' aliases 
the storage of local variable 'obj1' because parameter 'a' is marked 
[[clang::lifetimebound]]}} \
+                      // expected-note {{result of call to 'Choose' aliases 
the storage of local variable 'obj2' because parameter 'b' is marked 
[[clang::lifetimebound]]}}
                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 'obj1' is 
destroyed here}} \
@@ -860,7 +860,7 @@ void lifetimebound_mixed_args() {
   {
     MyObj obj1, obj2;
     v = SelectFirst(obj1,        // expected-warning {{local variable 'obj1' 
does not live long enough}} \
-                                 // expected-note {{result of call to 
'SelectFirst' aliases the storage of local variable 'obj1'}}
+                                 // expected-note {{result of call to 
'SelectFirst' aliases the storage of local variable 'obj1' because parameter 
'a' is marked [[clang::lifetimebound]]}}
                     obj2);
   }                              // expected-note {{local variable 'obj1' is 
destroyed here}}
   v.use();                       // expected-note {{later used here}}
@@ -877,7 +877,7 @@ void lifetimebound_member_function() {
   {
     MyObj obj;
     v  = obj.getView(); // expected-warning {{local variable 'obj' does not 
live long enough}} \
-                        // expected-note {{result of call to 'getView' aliases 
the storage of local variable 'obj'}}
+                        // expected-note {{result of call to 'getView' aliases 
the storage of local variable 'obj' because the implicit object parameter is 
marked [[clang::lifetimebound]]}}
   }                     // expected-note {{local variable 'obj' is destroyed 
here}}
   v.use();              // expected-note {{later used here}}
 }
@@ -902,7 +902,7 @@ void lifetimebound_chained_calls() {
   {
     MyObj obj;
     v = Identity(Identity(Identity(obj))); // expected-warning {{local 
variable 'obj' does not live long enough}} \
-                                           // expected-note 3 {{result of call 
to 'Identity' aliases the storage of local variable 'obj'}}
+                                           // expected-note 3 {{result of call 
to 'Identity' aliases the storage of local variable 'obj' because parameter 'v' 
is marked [[clang::lifetimebound]]}}
   }                                        // expected-note {{local variable 
'obj' is destroyed here}}
   v.use();                                 // expected-note {{later used here}}
 }
@@ -912,7 +912,7 @@ void lifetimebound_with_pointers() {
   {
     MyObj obj;
     ptr = GetPointer(obj); // expected-warning {{local variable 'obj' does not 
live long enough}} \
-                           // expected-note {{result of call to 'GetPointer' 
aliases the storage of local variable 'obj'}}
+                           // expected-note {{result of call to 'GetPointer' 
aliases the storage of local variable 'obj' because parameter 'obj' is marked 
[[clang::lifetimebound]]}}
   }                        // expected-note {{local variable 'obj' is 
destroyed here}}
   (void)*ptr;              // expected-note {{later used here}}
 }
@@ -922,7 +922,7 @@ void chained_assignment_lifetimebound_call() {
   {
     MyObj s;
     p = Identity(obj = &s); // expected-warning {{does not live long enough}} \
-                            // expected-note {{result of call to 'Identity' 
aliases the storage of local variable 's'}}
+                            // expected-note {{result of call to 'Identity' 
aliases the storage of local variable 's' because parameter 'v' is marked 
[[clang::lifetimebound]]}}
   }                         // expected-note {{local variable 's' is destroyed 
here}}
   (void)*p;                 // expected-note {{later used here}}
 }
@@ -955,7 +955,7 @@ void lifetimebound_return_reference() {
     MyObj obj;
     View temp_v = obj;  // expected-warning {{local variable 'obj' does not 
live long enough}}
     const MyObj& ref = GetObject(temp_v); // expected-note {{local variable 
'temp_v' aliases the storage of local variable 'obj'}} \
-                                          // expected-note {{result of call to 
'GetObject' aliases the storage of local variable 'obj'}}
+                                          // expected-note {{result of call to 
'GetObject' aliases the storage of local variable 'obj' because parameter 'v' 
is marked [[clang::lifetimebound]]}}
     ptr = &ref;
   }                       // expected-note {{local variable 'obj' is destroyed 
here}}
   (void)*ptr;             // expected-note {{later used here}}
@@ -1010,7 +1010,7 @@ void lifetimebound_make_unique() {
   {
     MyObj obj;
     ptr = std::make_unique<LifetimeBoundCtor>(obj); // tu-warning {{local 
variable 'obj' does not live long enough}} \
-                                                    // tu-note {{result of 
call to 'make_unique<LifetimeBoundCtor, MyObj &>' aliases the storage of local 
variable 'obj'}}
+                                                    // tu-note {{result of 
call to 'make_unique<LifetimeBoundCtor, MyObj &>' aliases the storage of local 
variable 'obj' because parameter 'args' is marked [[clang::lifetimebound]]}}
   }                                                 // tu-note {{local 
variable 'obj' is destroyed here}}
   (void)ptr;                                        // tu-note {{later used 
here}}
 }
@@ -1028,7 +1028,7 @@ void non_lifetimebound_make_unique() {
 void lifetimebound_make_unique_temp() {
   std::unique_ptr<LifetimeBoundCtor> ptr = 
std::make_unique<LifetimeBoundCtor>(MyObj()); // tu-warning {{temporary object 
does not live long enough}} \
                                                                                
          // tu-note {{temporary object is destroyed here}} \
-                                                                               
          // tu-note {{result of call to 'make_unique<LifetimeBoundCtor, 
MyObj>' aliases the storage of temporary object}}
+                                                                               
          // tu-note {{result of call to 'make_unique<LifetimeBoundCtor, 
MyObj>' aliases the storage of temporary object because parameter 'args' is 
marked [[clang::lifetimebound]]}}
   (void)ptr; // tu-note {{later used here}}
 }
 
@@ -1066,7 +1066,7 @@ void lifetimebound_make_unique_multi_params() {
   {
     MyObj obj_short;
     ptr = std::make_unique<MultiLifetimeBoundCtor>(obj_short, obj_long); // 
tu-warning {{local variable 'obj_short' does not live long enough}} \
-                                                                         // 
tu-note {{result of call to 'make_unique<MultiLifetimeBoundCtor, MyObj &, MyObj 
&>' aliases the storage of local variable 'obj_short'}}
+                                                                         // 
tu-note {{result of call to 'make_unique<MultiLifetimeBoundCtor, MyObj &, MyObj 
&>' aliases the storage of local variable 'obj_short' because parameter 'args' 
is marked [[clang::lifetimebound]]}}
   } // tu-note {{local variable 'obj_short' is destroyed here}}
   (void)ptr; // tu-note {{later used here}}
 }
@@ -1077,7 +1077,7 @@ void lifetimebound_make_unique_multi_params2() {
   {
     MyObj obj_short;
     ptr = std::make_unique<MultiLifetimeBoundCtor>(obj_long, obj_short, 1); // 
tu-warning {{local variable 'obj_short' does not live long enough}} \
-                                                                            // 
tu-note {{result of call to 'make_unique<MultiLifetimeBoundCtor, MyObj &, MyObj 
&, int>' aliases the storage of local variable 'obj_short'}}
+                                                                            // 
tu-note {{result of call to 'make_unique<MultiLifetimeBoundCtor, MyObj &, MyObj 
&, int>' aliases the storage of local variable 'obj_short' because parameter 
'args' is marked [[clang::lifetimebound]]}}
   } // tu-note {{local variable 'obj_short' is destroyed here}}
   (void)ptr; // tu-note {{later used here}}
 }
@@ -1098,7 +1098,7 @@ void lifetimebound_make_unique_multi_params3_1() {
   {
     MyObj obj_short;
     ptr = std::make_unique<MultiLifetimeBoundCtor>(obj_short, obj_long, 1.0); 
// tu-warning {{local variable 'obj_short' does not live long enough}} \
-                                                                              
// tu-note {{result of call to 'make_unique<MultiLifetimeBoundCtor, MyObj &, 
MyObj &, double>' aliases the storage of local variable 'obj_short'}}
+                                                                              
// tu-note {{result of call to 'make_unique<MultiLifetimeBoundCtor, MyObj &, 
MyObj &, double>' aliases the storage of local variable 'obj_short' because 
parameter 'args' is marked [[clang::lifetimebound]]}}
   } // tu-note {{local variable 'obj_short' is destroyed here}}
   (void)ptr; // tu-note {{later used here}}
 }
@@ -1109,7 +1109,7 @@ void lifetimebound_make_unique_multi_params3_2() {
   {
     MyObj obj_short;
     ptr = std::make_unique<MultiLifetimeBoundCtor>(obj_long, obj_short, 1.0); 
// tu-warning {{local variable 'obj_short' does not live long enough}} \
-                                                                              
// tu-note {{result of call to 'make_unique<MultiLifetimeBoundCtor, MyObj &, 
MyObj &, double>' aliases the storage of local variable 'obj_short'}}
+                                                                              
// tu-note {{result of call to 'make_unique<MultiLifetimeBoundCtor, MyObj &, 
MyObj &, double>' aliases the storage of local variable 'obj_short' because 
parameter 'args' is marked [[clang::lifetimebound]]}}
   } // tu-note {{local variable 'obj_short' is destroyed here}}
   (void)ptr; // tu-note {{later used here}}
 }
@@ -1393,7 +1393,7 @@ void parentheses(bool cond) {
   {
     MyObj a;
     p = ((GetPointer((a))));  // expected-warning {{local variable 'a' does 
not live long enough}} \
-                              // expected-note {{result of call to 
'GetPointer' aliases the storage of local variable 'a'}}
+                              // expected-note {{result of call to 
'GetPointer' aliases the storage of local variable 'a' because parameter 'obj' 
is marked [[clang::lifetimebound]]}}
   }                           // expected-note {{local variable 'a' is 
destroyed here}}
   (void)*p;                   // expected-note {{later used here}}
 
@@ -1425,7 +1425,7 @@ void use_temporary_after_destruction() {
 void passing_temporary_to_lifetime_bound_function() {
   View a = construct_view(non_trivially_destructed_temporary()); // 
expected-warning {{temporary object does not live long enough}} \
                 expected-note {{temporary object is destroyed here}} \
-                expected-note {{result of call to 'construct_view' aliases the 
storage of temporary object}}
+                expected-note {{result of call to 'construct_view' aliases the 
storage of temporary object because parameter 'obj' is marked 
[[clang::lifetimebound]]}}
   use(a); // expected-note {{later used here}}
 }
 
@@ -1467,7 +1467,7 @@ int **bit_cast_multilevel() {
 namespace FullExprCleanupLoc {
 void var_initializer() {
   View v = non_trivially_destructed_temporary() // expected-warning 
{{temporary object does not live long enough}} \
-                                                // expected-note {{result of 
call to 'getView' aliases the storage of temporary object}}
+                                                // expected-note {{result of 
call to 'getView' aliases the storage of temporary object because the implicit 
object parameter is marked [[clang::lifetimebound]]}}
                .getView(); // expected-note {{temporary object is destroyed 
here}}
   v.use(); // expected-note {{later used here}}
 }
@@ -1475,7 +1475,7 @@ void var_initializer() {
 void expr_statement() {
   View v;
   v = non_trivially_destructed_temporary() // expected-warning {{temporary 
object does not live long enough}} \
-                                           // expected-note {{result of call 
to 'getView' aliases the storage of temporary object}}
+                                           // expected-note {{result of call 
to 'getView' aliases the storage of temporary object because the implicit 
object parameter is marked [[clang::lifetimebound]]}}
           .getView(); // expected-note {{temporary object is destroyed here}}
   v.use(); // expected-note {{later used here}}
 }
@@ -1519,7 +1519,7 @@ void foobar() {
   {
     StatusOr<MyObj> string_or = getStringOr();
     view = string_or. // expected-warning {{local variable 'string_or' does 
not live long enough}} \
-                      // expected-note {{result of call to 'value' aliases the 
storage of local variable 'string_or'}}
+                      // expected-note {{result of call to 'value' aliases the 
storage of local variable 'string_or' because the implicit object parameter is 
marked [[clang::lifetimebound]]}}
             value();
   }                     // expected-note {{local variable 'string_or' is 
destroyed here}}
   (void)view;           // expected-note {{later used here}}
@@ -1594,7 +1594,7 @@ void test_user_defined_deref_uaf() {
     MyObj obj;
     SmartPtr<MyObj> smart_ptr(&obj);
     p = &(*smart_ptr);  // expected-warning {{local variable 'smart_ptr' does 
not live long enough}} \
-                        // expected-note {{expression aliases the storage of 
local variable 'smart_ptr'}}
+                        // expected-note {{expression aliases the storage of 
local variable 'smart_ptr' because the implicit object parameter is marked 
[[clang::lifetimebound]]}}
   }                     // expected-note {{local variable 'smart_ptr' is 
destroyed here}}
   (void)*p;             // expected-note {{later used here}}
 }
@@ -1612,7 +1612,7 @@ void test_user_defined_deref_with_view() {
     MyObj obj;
     SmartPtr<MyObj> smart_ptr(&obj);
     v = *smart_ptr;  // expected-warning {{local variable 'smart_ptr' does not 
live long enough}} \
-                     // expected-note {{expression aliases the storage of 
local variable 'smart_ptr'}}
+                     // expected-note {{expression aliases the storage of 
local variable 'smart_ptr' because the implicit object parameter is marked 
[[clang::lifetimebound]]}}
   }                  // expected-note {{local variable 'smart_ptr' is 
destroyed here}}
   v.use();           // expected-note {{later used here}}
 }
@@ -1623,7 +1623,7 @@ void test_user_defined_deref_arrow() {
     MyObj obj;
     SmartPtr<MyObj> smart_ptr(&obj);
     p = smart_ptr.operator->();  // expected-warning {{local variable 
'smart_ptr' does not live long enough}} \
-                                 // expected-note {{expression aliases the 
storage of local variable 'smart_ptr'}}
+                                 // expected-note {{expression aliases the 
storage of local variable 'smart_ptr' because the implicit object parameter is 
marked [[clang::lifetimebound]]}}
   }                              // expected-note {{local variable 'smart_ptr' 
is destroyed here}}
   (void)*p;                      // expected-note {{later used here}}
 }
@@ -1634,7 +1634,7 @@ void test_user_defined_deref_chained() {
     MyObj obj;
     SmartPtr<SmartPtr<MyObj>> double_ptr;
     p = &(**double_ptr);  // expected-warning {{local variable 'double_ptr' 
does not live long enough}} \
-                          // expected-note 2 {{expression aliases the storage 
of local variable 'double_ptr'}}
+                          // expected-note 2 {{expression aliases the storage 
of local variable 'double_ptr' because the implicit object parameter is marked 
[[clang::lifetimebound]]}}
   }                       // expected-note {{local variable 'double_ptr' is 
destroyed here}}
   (void)*p;               // expected-note {{later used here}}
 }
@@ -1829,7 +1829,7 @@ void wrong_use_of_move_is_permissive() {
     MyObj a;
     p = std::move(a).getData(); // expected-warning {{local variable 'a' does 
not live long enough}} \
                                 // expected-note {{result of call to 
'move<MyObj &>' aliases the storage of local variable 'a'}} \
-                                // expected-note {{result of call to 'getData' 
aliases the storage of local variable 'a'}}
+                                // expected-note {{result of call to 'getData' 
aliases the storage of local variable 'a' because the implicit object parameter 
is marked [[clang::lifetimebound]]}}
   }         // expected-note {{local variable 'a' is destroyed here}}
   (void)p;  // expected-note {{later used here}}
 }
@@ -1864,10 +1864,10 @@ void bar() {
     {
         S s;
         x = s.x(); // expected-warning {{local variable 's' does not live long 
enough}} \
-                   // expected-note {{result of call to 'x' aliases the 
storage of local variable 's'}}
+                   // expected-note {{result of call to 'x' aliases the 
storage of local variable 's' because the implicit object parameter is marked 
[[clang::lifetimebound]]}}
         View y = S().x(); // expected-warning {{temporary object does not live 
long enough}} \
                              expected-note {{temporary object is destroyed 
here}} \
-                             expected-note {{result of call to 'x' aliases the 
storage of temporary object}}
+                             expected-note {{result of call to 'x' aliases the 
storage of temporary object because the implicit object parameter is marked 
[[clang::lifetimebound]]}}
         (void)y; // expected-note {{used here}}
     } // expected-note {{local variable 's' is destroyed here}}
     (void)x; // expected-note {{used here}}
@@ -1956,19 +1956,19 @@ const S& identity(const S& in [[clang::lifetimebound]]);
 
 void test_temporary() {
   const std::string& x = S().x(); // expected-warning {{temporary object does 
not live long enough}} expected-note {{temporary object is destroyed here}} \
-                                  // expected-note {{result of call to 'x' 
aliases the storage of temporary object}}
+                                  // expected-note {{result of call to 'x' 
aliases the storage of temporary object because the implicit object parameter 
is marked [[clang::lifetimebound]]}}
   (void)x; // expected-note {{later used here}}
 
   const std::string& y = identity(S().x()); // expected-warning {{temporary 
object does not live long enough}} expected-note {{temporary object is 
destroyed here}} \
-                                            // expected-note {{result of call 
to 'x' aliases the storage of temporary object}} \
-                                            // expected-note {{result of call 
to 'identity' aliases the storage of temporary object}}
+                                            // expected-note {{result of call 
to 'x' aliases the storage of temporary object because the implicit object 
parameter is marked [[clang::lifetimebound]]}} \
+                                            // expected-note {{result of call 
to 'identity' aliases the storage of temporary object because parameter 'in' is 
marked [[clang::lifetimebound]]}}
   (void)y; // expected-note {{later used here}}
 
   std::string_view z;
   {
     S s;
     const std::string& zz = s.x(); // expected-warning {{local variable 's' 
does not live long enough}} \
-                                   // expected-note {{result of call to 'x' 
aliases the storage of local variable 's'}}
+                                   // expected-note {{result of call to 'x' 
aliases the storage of local variable 's' because the implicit object parameter 
is marked [[clang::lifetimebound]]}}
     z = zz;                        // expected-note {{expression aliases the 
storage of local variable 's'}}
   } // expected-note {{local variable 's' is destroyed here}}
   (void)z; // expected-note {{later used here}}
@@ -1978,13 +1978,13 @@ void test_lifetime_extension_ok() {
   const S& x = S();
   (void)x;
   const S& y = identity(S()); // expected-warning {{temporary object does not 
live long enough}} expected-note {{temporary object is destroyed here}} \
-                              // expected-note {{result of call to 'identity' 
aliases the storage of temporary object}}
+                              // expected-note {{result of call to 'identity' 
aliases the storage of temporary object because parameter 'in' is marked 
[[clang::lifetimebound]]}}
   (void)y; // expected-note {{later used here}}
 }
 
 const std::string& test_return() {
   const std::string& x = S().x(); // expected-warning {{temporary object does 
not live long enough}} expected-note {{temporary object is destroyed here}} \
-                                  // expected-note {{result of call to 'x' 
aliases the storage of temporary object}}
+                                  // expected-note {{result of call to 'x' 
aliases the storage of temporary object because the implicit object parameter 
is marked [[clang::lifetimebound]]}}
   return x; // expected-note {{later used here}}
 }
 } // namespace reference_type_decl_ref_expr
@@ -2165,11 +2165,11 @@ const T* MemberFuncsTpl<T>::memberC(const T& x 
[[clang::lifetimebound]]) {
 void test() {
   MemberFuncsTpl<MyObj> mtf;
   const MyObj* pTMA = mtf.memberA(MyObj()); // expected-warning {{temporary 
object does not live long enough}} // expected-note {{temporary object is 
destroyed here}} \
-                                            // expected-note {{result of call 
to 'memberA' aliases the storage of temporary object}}
+                                            // expected-note {{result of call 
to 'memberA' aliases the storage of temporary object because parameter 'x' is 
marked [[clang::lifetimebound]]}}
   const MyObj* pTMB = mtf.memberB(MyObj()); // tu-warning {{temporary object 
does not live long enough}} // tu-note {{temporary object is destroyed here}} \
-                                            // tu-note {{result of call to 
'memberB' aliases the storage of temporary object}}
+                                            // tu-note {{result of call to 
'memberB' aliases the storage of temporary object because parameter 'x' is 
marked [[clang::lifetimebound]]}}
   const MyObj* pTMC = mtf.memberC(MyObj()); // expected-warning {{temporary 
object does not live long enough}} // expected-note {{temporary object is 
destroyed here}} \
-                                            // expected-note {{result of call 
to 'memberC' aliases the storage of temporary object}}
+                                            // expected-note {{result of call 
to 'memberC' aliases the storage of temporary object because parameter 'x' is 
marked [[clang::lifetimebound]]}}
   (void)pTMA; // expected-note {{later used here}}
   (void)pTMB; // tu-note {{later used here}}
   (void)pTMC; // expected-note {{later used here}}
@@ -2217,7 +2217,7 @@ void test_optional_arrow_lifetimebound() {
     std::optional<MyObj> opt;
     v = opt->getView();  // expected-warning {{local variable 'opt' does not 
live long enough}} \
                          // expected-note {{expression aliases the storage of 
local variable 'opt'}} \
-                         // expected-note {{result of call to 'getView' 
aliases the storage of local variable 'opt'}}
+                         // expected-note {{result of call to 'getView' 
aliases the storage of local variable 'opt' because the implicit object 
parameter is marked [[clang::lifetimebound]]}}
   }                      // expected-note {{local variable 'opt' is destroyed 
here}}
   v.use();               // expected-note {{later used here}}
 }
@@ -2580,7 +2580,8 @@ struct S {
 
 void indexing_with_static_operator() {
   S()(1, 2);
-  S& x = S()("1", // expected-note 2 {{expression aliases the storage of 
temporary object}}
+  S& x = S()("1", // expected-note {{expression aliases the storage of 
temporary object because parameter 'a' is marked [[clang::lifetimebound]]}} \
+                 // expected-note {{expression aliases the storage of 
temporary object because parameter 'b' is marked [[clang::lifetimebound]]}}
              2,   // expected-warning {{temporary object does not live long 
enough}}
              3);  // expected-warning {{temporary object does not live long 
enough}} expected-note 2 {{temporary object is destroyed here}}
 
@@ -2606,7 +2607,7 @@ S getS(const std::string &s [[clang::lifetimebound]]);
 void from_free_function() {
   S s = getS(std::string("temp")); // expected-warning {{temporary object does 
not live long enough}} \
                                    // expected-note {{temporary object is 
destroyed here}} \
-                                   // expected-note {{result of call to 'getS' 
aliases the storage of temporary object}}
+                                   // expected-note {{result of call to 'getS' 
aliases the storage of temporary object because parameter 's' is marked 
[[clang::lifetimebound]]}}
   use(s);                          // expected-note {{later used here}}
 }
 
@@ -2626,14 +2627,14 @@ void from_method() {
   Factory f;
   S s = f.make(std::string("temp")); // expected-warning {{temporary object 
does not live long enough}} \
                                      // expected-note {{temporary object is 
destroyed here}} \
-                                     // expected-note {{result of call to 
'make' aliases the storage of temporary object}}
+                                     // expected-note {{result of call to 
'make' aliases the storage of temporary object because parameter 's' is marked 
[[clang::lifetimebound]]}}
   use(s);                            // expected-note {{later used here}}
 }
 
 void from_static_method() {
   S s = Factory::create(std::string("temp")); // expected-warning {{temporary 
object does not live long enough}} \
                                               // expected-note {{temporary 
object is destroyed here}} \
-                                              // expected-note {{result of 
call to 'create' aliases the storage of temporary object}}
+                                              // expected-note {{result of 
call to 'create' aliases the storage of temporary object because parameter 's' 
is marked [[clang::lifetimebound]]}}
   use(s);                                     // expected-note {{later used 
here}}
 }
 
@@ -2642,7 +2643,7 @@ void from_lifetimebound_this_method() {
   {
     Factory f;
     value = f.makeThis(); // expected-warning {{local variable 'f' does not 
live long enough}} \
-                          // expected-note {{result of call to 'makeThis' 
aliases the storage of local variable 'f'}}
+                          // expected-note {{result of call to 'makeThis' 
aliases the storage of local variable 'f' because the implicit object parameter 
is marked [[clang::lifetimebound]]}}
   }                       // expected-note {{local variable 'f' is destroyed 
here}}
   use(value);             // expected-note {{later used here}}
 }
@@ -2652,7 +2653,7 @@ void across_scope() {
   {
     std::string str{"abc"};
     s = 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'}}
+                   // expected-note {{result of call to 'getS' aliases the 
storage of local variable 'str' because parameter 's' is marked 
[[clang::lifetimebound]]}}
   }                // expected-note {{local variable 'str' is destroyed here}}
   use(s);          // expected-note {{later used here}}
 }
@@ -2675,7 +2676,7 @@ void assignment_propagation() {
   {
     std::string str{"abc"};
     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'}}
+                   // expected-note {{result of call to 'getS' aliases the 
storage of local variable 'str' because parameter 's' is marked 
[[clang::lifetimebound]]}}
     b = a;         // expected-note {{local variable 'a' aliases the storage 
of local variable 'str'}}
   }                // expected-note {{local variable 'str' is destroyed here}}
   use(b);          // expected-note {{later used here}}
@@ -2686,7 +2687,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'}}
+                     // expected-note {{result of call to 'getS' aliases the 
storage of local variable 'str' because parameter 's' is marked 
[[clang::lifetimebound]]}}
     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'}}
   }                  // expected-note {{local variable 'str' is destroyed 
here}}
@@ -2703,7 +2704,7 @@ void no_annotation() {
 void mix_annotated_and_not() {
   S s1 = getS(std::string("temp")); // expected-warning {{temporary object 
does not live long enough}} \
                                     // expected-note {{temporary object is 
destroyed here}} \
-                                    // expected-note {{result of call to 
'getS' aliases the storage of temporary object}}
+                                    // expected-note {{result of call to 
'getS' aliases the storage of temporary object because parameter 's' is marked 
[[clang::lifetimebound]]}}
   S s2 = getSNoAnnotation(std::string("temp"));
   use(s1); // expected-note {{later used here}}
   use(s2);
@@ -2715,7 +2716,7 @@ S multiple_lifetimebound_params() {
   std::string str{"abc"};
   S s = getS2(str, std::string("temp")); // expected-warning {{stack memory 
associated with local variable 'str' is returned}} \
                                          // expected-warning {{temporary 
object does not live long enough}} \
-                                         // expected-note {{result of call to 
'getS2' aliases the storage of temporary object}} \
+                                         // expected-note {{result of call to 
'getS2' aliases the storage of temporary object because parameter 'b' is marked 
[[clang::lifetimebound]]}} \
                                          // expected-note {{temporary object 
is destroyed here}}
   return s;                              // expected-note {{returned here}} \
                                          // expected-note {{later used here}}
@@ -2736,7 +2737,7 @@ T make(const std::string &s [[clang::lifetimebound]]);
 void from_template_instantiation() {
   S s = make<S>(std::string("temp")); // expected-warning {{temporary object 
does not live long enough}} \
                                       // expected-note {{temporary object is 
destroyed here}} \
-                                      // expected-note {{result of call to 
'make<track_origins_for_lifetimebound_record_type::S>' aliases the storage of 
temporary object}}
+                                      // expected-note {{result of call to 
'make<track_origins_for_lifetimebound_record_type::S>' aliases the storage of 
temporary object because parameter 's' is marked [[clang::lifetimebound]]}}
   use(s);                             // expected-note {{later used here}}
 }
 
@@ -2800,7 +2801,7 @@ SAlias getSAlias(const std::string &s 
[[clang::lifetimebound]]);
 void from_typedef_return() {
   SAlias s = getSAlias(std::string("temp")); // expected-warning {{temporary 
object does not live long enough}} \
                                              // expected-note {{temporary 
object is destroyed here}} \
-                                             // expected-note {{result of call 
to 'getSAlias' aliases the storage of temporary object}}
+                                             // expected-note {{result of call 
to 'getSAlias' aliases the storage of temporary object because parameter 's' is 
marked [[clang::lifetimebound]]}}
   use(s);                                    // expected-note {{later used 
here}}
 }
 
@@ -2875,7 +2876,7 @@ std::unique_ptr<S> getUniqueS(const std::string &s 
[[clang::lifetimebound]]);
 void owner_return_unique_ptr_s() {
   auto ptr = getUniqueS(std::string("temp")); // expected-warning {{temporary 
object does not live long enough}} \
                                               // expected-note {{temporary 
object is destroyed here}} \
-                                              // expected-note {{result of 
call to 'getUniqueS' aliases the storage of temporary object}}
+                                              // expected-note {{result of 
call to 'getUniqueS' aliases the storage of temporary object because parameter 
's' is marked [[clang::lifetimebound]]}}
   (void)ptr;                                  // expected-note {{later used 
here}}
 }
 
@@ -2892,7 +2893,7 @@ void owner_outlives_lifetimebound_source() {
   {
     std::string local;
     ups = getUniqueS(local); // expected-warning {{local variable 'local' does 
not live long enough}} \
-                             // expected-note {{result of call to 'getUniqueS' 
aliases the storage of local variable 'local'}}
+                             // expected-note {{result of call to 'getUniqueS' 
aliases the storage of local variable 'local' because parameter 's' is marked 
[[clang::lifetimebound]]}}
   } // expected-note {{local variable 'local' is destroyed here}}
   (void)ups; // expected-note {{later used here}}
 }
@@ -2927,7 +2928,7 @@ void nested_local_pointer() {
   {
     Bar v;
     p = Pointer(v);     // expected-warning {{local variable 'v' does not live 
long enough}}
-    pp = Pointer(p);    // expected-note {{local variable 'p' aliases the 
storage of local variable 'v'}}
+    pp = Pointer(p);    // expected-note {{local variable 'p' aliases the 
storage of local variable 'v' because parameter 'bar' is marked 
[[clang::lifetimebound]]}}
     ppp = Pointer(pp);  // expected-note {{local variable 'pp' aliases the 
storage of local variable 'v'}}
   }                     // expected-note {{local variable 'v' is destroyed 
here}}
   use(***ppp);          // expected-note {{later used here}}
@@ -3685,7 +3686,7 @@ void uaf_via_lifetimebound() {
   {
     int local;
     f = capture_lifetimebound_param(local); // expected-warning {{local 
variable 'local' does not live long enough}} \
-                                            // expected-note {{result of call 
to 'capture_lifetimebound_param' aliases the storage of local variable 'local'}}
+                                            // expected-note {{result of call 
to 'capture_lifetimebound_param' aliases the storage of local variable 'local' 
because parameter 'x' is marked [[clang::lifetimebound]]}}
   } // expected-note {{local variable 'local' is destroyed here}}
   (void)f; // expected-note {{later used here}}
 }

>From e9cfca0bb9de2a15a78df1cf1048d2909fa290a3 Mon Sep 17 00:00:00 2001
From: iitianpushkar <[email protected]>
Date: Sun, 28 Jun 2026 20:11:35 +0530
Subject: [PATCH 5/6] formatted code

---
 .../Analysis/Analyses/LifetimeSafety/LifetimeSafety.h    | 9 ++++-----
 clang/lib/Analysis/LifetimeSafety/Checker.cpp            | 3 ++-
 clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp     | 9 +++++----
 clang/lib/Sema/SemaLifetimeSafety.h                      | 8 ++++----
 4 files changed, 15 insertions(+), 14 deletions(-)

diff --git 
a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
index c177e9e222bdc..2b6c494795739 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
@@ -70,11 +70,10 @@ class LifetimeSafetySemaHelper {
   LifetimeSafetySemaHelper() = default;
   virtual ~LifetimeSafetySemaHelper() = default;
 
-  virtual void reportUseAfterScope(const Expr *IssueExpr, const Expr *UseExpr,
-                                   const Expr *MovedExpr,
-                                   SourceLocation FreeLoc,
-                                   
llvm::ArrayRef<LifetimeSafetyAliasChainEntry>
-                                       AliasChain) {}
+  virtual void reportUseAfterScope(
+      const Expr *IssueExpr, const Expr *UseExpr, const Expr *MovedExpr,
+      SourceLocation FreeLoc,
+      llvm::ArrayRef<LifetimeSafetyAliasChainEntry> AliasChain) {}
 
   virtual void reportUseAfterReturn(const Expr *IssueExpr,
                                     const Expr *ReturnExpr,
diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp 
b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
index 335ca28861f4f..568fcdab62502 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -529,7 +529,8 @@ class LifetimeChecker {
   getAliasChain(llvm::ArrayRef<OriginFlowChainStep> OriginFlowChain) {
     llvm::SmallVector<LifetimeSafetyAliasChainEntry> rs;
     for (const OriginFlowChainStep &Step : OriginFlowChain) {
-      const Expr *CurrExpr = 
FactMgr.getOriginMgr().getOrigin(Step.OID).getExpr();
+      const Expr *CurrExpr =
+          FactMgr.getOriginMgr().getOrigin(Step.OID).getExpr();
       if (!CurrExpr)
         continue;
 
diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp 
b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
index dd6838630947e..b5b6c92ea7348 100644
--- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -1085,8 +1085,9 @@ void FactsGenerator::handleFunctionCall(const Expr *Call,
     flow(CallList, getOriginsList(*Args[0]), /*Kill=*/true);
     return;
   }
-  auto GetLifetimeBoundInfo = [FD, Call](unsigned I)
-      -> std::optional<OriginFlowFact::LifetimeBoundInfo> {
+  auto GetLifetimeBoundInfo =
+      [FD,
+       Call](unsigned I) -> std::optional<OriginFlowFact::LifetimeBoundInfo> {
     const ParmVarDecl *PVD = nullptr;
     if (const auto *Method = dyn_cast<CXXMethodDecl>(FD);
         Method && Method->isInstance() && !isa<CXXConstructorDecl>(FD)) {
@@ -1165,8 +1166,8 @@ void FactsGenerator::handleFunctionCall(const Expr *Call,
         // FIXME: Handle origin-shape mismatches gracefully so we can also flow
         // inner origins.
         CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>(
-            CallList->getOuterOriginID(), ArgList->getOuterOriginID(),
-            KillSrc, GetLifetimeBoundInfo(I)));
+            CallList->getOuterOriginID(), ArgList->getOuterOriginID(), KillSrc,
+            GetLifetimeBoundInfo(I)));
         KillSrc = false;
       }
     } else if (shouldTrackPointerImplicitObjectArg(I)) {
diff --git a/clang/lib/Sema/SemaLifetimeSafety.h 
b/clang/lib/Sema/SemaLifetimeSafety.h
index 3d8ca9e90abff..1828096debb4c 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -101,10 +101,10 @@ class LifetimeSafetySemaHelperImpl : public 
LifetimeSafetySemaHelper {
 public:
   LifetimeSafetySemaHelperImpl(Sema &S) : S(S) {}
 
-  void reportUseAfterScope(const Expr *IssueExpr, const Expr *UseExpr,
-                           const Expr *MovedExpr, SourceLocation FreeLoc,
-                           llvm::ArrayRef<LifetimeSafetyAliasChainEntry>
-                               AliasChain) override {
+  void reportUseAfterScope(
+      const Expr *IssueExpr, const Expr *UseExpr, const Expr *MovedExpr,
+      SourceLocation FreeLoc,
+      llvm::ArrayRef<LifetimeSafetyAliasChainEntry> AliasChain) override {
     unsigned DiagID = MovedExpr
                           ? diag::warn_lifetime_safety_use_after_scope_moved
                           : diag::warn_lifetime_safety_use_after_scope;

>From aa1a22e6a85f4c312bdf2c168499d221193529d7 Mon Sep 17 00:00:00 2001
From: iitianpushkar <[email protected]>
Date: Mon, 29 Jun 2026 17:08:18 +0530
Subject: [PATCH 6/6] improved design by taking lifetimeboundinfo out of
 originflowfact

---
 .../Analysis/Analyses/LifetimeSafety/Facts.h  | 34 +++++++++++--------
 .../Analyses/LifetimeSafety/LifetimeSafety.h  |  4 +--
 .../Analyses/LifetimeSafety/LoanPropagation.h |  9 ++---
 clang/lib/Analysis/LifetimeSafety/Checker.cpp | 11 +++---
 clang/lib/Analysis/LifetimeSafety/Facts.cpp   |  2 --
 .../LifetimeSafety/FactsGenerator.cpp         | 29 ++++++++--------
 .../LifetimeSafety/LoanPropagation.cpp        | 16 ++++-----
 clang/lib/Sema/SemaLifetimeSafety.h           | 15 ++++----
 8 files changed, 56 insertions(+), 64 deletions(-)

diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
index 1e0ef8680f7db..3aad46fb36282 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
@@ -20,6 +20,7 @@
 #include "clang/Analysis/Analyses/LifetimeSafety/Utils.h"
 #include "clang/Analysis/AnalysisDeclContext.h"
 #include "clang/Analysis/CFG.h"
+#include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/STLFunctionalExtras.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/Support/Debug.h"
@@ -30,6 +31,11 @@ namespace clang::lifetimes::internal {
 
 using FactID = utils::ID<struct FactTag>;
 
+struct LifetimeBoundInfo {
+  const ParmVarDecl *Param = nullptr;
+  bool IsImplicitObject = false;
+};
+
 /// An abstract base class for a single, atomic lifetime-relevant event.
 class Fact {
 
@@ -131,37 +137,24 @@ class ExpireFact : public Fact {
 };
 
 class OriginFlowFact : public Fact {
-public:
-  struct LifetimeBoundInfo {
-    const Expr *Call = nullptr;
-    const ParmVarDecl *Param = nullptr;
-    bool isImplicitObject = false;
-  };
-
-private:
   OriginID OIDDest;
   OriginID OIDSrc;
   // True if the destination origin should be killed (i.e., its current loans
   // cleared) before the source origin's loans are flowed into it.
   bool KillDest;
-  std::optional<LifetimeBoundInfo> LifetimeBound;
 
 public:
   static bool classof(const Fact *F) {
     return F->getKind() == Kind::OriginFlow;
   }
 
-  OriginFlowFact(OriginID OIDDest, OriginID OIDSrc, bool KillDest,
-                 std::optional<LifetimeBoundInfo> LifetimeBound = std::nullopt)
+  OriginFlowFact(OriginID OIDDest, OriginID OIDSrc, bool KillDest)
       : Fact(Kind::OriginFlow), OIDDest(OIDDest), OIDSrc(OIDSrc),
-        KillDest(KillDest), LifetimeBound(LifetimeBound) {}
+        KillDest(KillDest) {}
 
   OriginID getDestOriginID() const { return OIDDest; }
   OriginID getSrcOriginID() const { return OIDSrc; }
   bool getKillDest() const { return KillDest; }
-  std::optional<LifetimeBoundInfo> getLifetimeBoundInfo() const {
-    return LifetimeBound;
-  }
 
   void dump(llvm::raw_ostream &OS, const LoanManager &,
             const OriginManager &OM) const override;
@@ -398,6 +391,16 @@ class FactManager {
   const LoanManager &getLoanMgr() const { return LoanMgr; }
   OriginManager &getOriginMgr() { return OriginMgr; }
   const OriginManager &getOriginMgr() const { return OriginMgr; }
+  void setLifetimeBoundInfo(const OriginFlowFact *F, LifetimeBoundInfo Info) {
+    LifetimeBoundFlows.try_emplace(F, Info);
+  }
+  std::optional<LifetimeBoundInfo>
+  getLifetimeBoundInfo(const OriginFlowFact *F) const {
+    auto It = LifetimeBoundFlows.find(F);
+    if (It == LifetimeBoundFlows.end())
+      return std::nullopt;
+    return It->second;
+  }
 
 private:
   FactID NextFactID{0};
@@ -405,6 +408,7 @@ class FactManager {
   OriginManager OriginMgr;
   /// Facts for each CFG block, indexed by block ID.
   llvm::SmallVector<llvm::SmallVector<const Fact *>> BlockToFacts;
+  llvm::DenseMap<const OriginFlowFact *, LifetimeBoundInfo> LifetimeBoundFlows;
   llvm::BumpPtrAllocator FactAllocator;
 };
 } // namespace clang::lifetimes::internal
diff --git 
a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
index 2b6c494795739..cad40ead378fd 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
@@ -31,6 +31,7 @@
 #include "llvm/ADT/PointerUnion.h"
 #include <cstddef>
 #include <memory>
+#include <optional>
 
 namespace clang::lifetimes {
 
@@ -51,8 +52,7 @@ enum class WarningScope {
 
 struct LifetimeSafetyAliasChainEntry {
   const Expr *E = nullptr;
-  const ParmVarDecl *LifetimeBoundParam = nullptr;
-  bool LifetimeBoundImplicitObject = false;
+  std::optional<internal::LifetimeBoundInfo> LifetimeBound;
 };
 
 /// Abstract interface for operations requiring Sema access.
diff --git 
a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
index 92d69f99c8794..4f2b3cedd17ba 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
@@ -29,11 +29,6 @@ namespace clang::lifetimes::internal {
 using LoanSet = llvm::ImmutableSet<LoanID>;
 using OriginLoanMap = llvm::ImmutableMap<OriginID, LoanSet>;
 
-struct OriginFlowChainStep {
-  OriginID OID;
-  const OriginFlowFact *FlowFact;
-};
-
 class LoanPropagationAnalysis {
 public:
   LoanPropagationAnalysis(const CFG &C, AnalysisDeclContext &AC, FactManager 
&F,
@@ -56,12 +51,12 @@ class LoanPropagationAnalysis {
   llvm::SmallVector<OriginID>
   buildOriginFlowChain(const UseFact *UF, const LoanID TargetLoan) const;
 
-  llvm::SmallVector<OriginFlowChainStep>
+  llvm::SmallVector<const OriginFlowFact *>
   buildOriginFlowChainWithFacts(ProgramPoint StartPoint,
                                 const OriginID StartOID,
                                 const LoanID TargetLoan) const;
 
-  llvm::SmallVector<OriginFlowChainStep>
+  llvm::SmallVector<const OriginFlowFact *>
   buildOriginFlowChainWithFacts(const UseFact *UF,
                                 const LoanID TargetLoan) const;
 
diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp 
b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
index 568fcdab62502..014bb60fa51b5 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -526,20 +526,17 @@ class LifetimeChecker {
   /// extracts the corresponding expressions for each origin. Origins that 
refer
   /// to declarations (rather than expressions) are skipped.
   llvm::SmallVector<LifetimeSafetyAliasChainEntry>
-  getAliasChain(llvm::ArrayRef<OriginFlowChainStep> OriginFlowChain) {
+  getAliasChain(llvm::ArrayRef<const OriginFlowFact *> OriginFlowChain) {
     llvm::SmallVector<LifetimeSafetyAliasChainEntry> rs;
-    for (const OriginFlowChainStep &Step : OriginFlowChain) {
+    for (const OriginFlowFact *Flow : OriginFlowChain) {
       const Expr *CurrExpr =
-          FactMgr.getOriginMgr().getOrigin(Step.OID).getExpr();
+          FactMgr.getOriginMgr().getOrigin(Flow->getSrcOriginID()).getExpr();
       if (!CurrExpr)
         continue;
 
       LifetimeSafetyAliasChainEntry Entry;
       Entry.E = CurrExpr;
-      if (auto Info = Step.FlowFact->getLifetimeBoundInfo()) {
-        Entry.LifetimeBoundParam = Info->Param;
-        Entry.LifetimeBoundImplicitObject = Info->isImplicitObject;
-      }
+      Entry.LifetimeBound = FactMgr.getLifetimeBoundInfo(Flow);
       rs.push_back(Entry);
     }
     return rs;
diff --git a/clang/lib/Analysis/LifetimeSafety/Facts.cpp 
b/clang/lib/Analysis/LifetimeSafety/Facts.cpp
index 6c1fb7ba4e214..3d7fbcdacc830 100644
--- a/clang/lib/Analysis/LifetimeSafety/Facts.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Facts.cpp
@@ -46,8 +46,6 @@ void OriginFlowFact::dump(llvm::raw_ostream &OS, const 
LoanManager &,
   OS << "\tSrc:  ";
   OM.dump(getSrcOriginID(), OS);
   OS << (getKillDest() ? "" : ", Merge");
-  if (getLifetimeBoundInfo())
-    OS << ", LifetimeBound";
   OS << "\n";
 }
 
diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp 
b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
index b5b6c92ea7348..e5c071cb75110 100644
--- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -1086,18 +1086,16 @@ void FactsGenerator::handleFunctionCall(const Expr 
*Call,
     return;
   }
   auto GetLifetimeBoundInfo =
-      [FD,
-       Call](unsigned I) -> std::optional<OriginFlowFact::LifetimeBoundInfo> {
+      [FD](unsigned I) -> std::optional<LifetimeBoundInfo> {
     const ParmVarDecl *PVD = nullptr;
     if (const auto *Method = dyn_cast<CXXMethodDecl>(FD);
         Method && Method->isInstance() && !isa<CXXConstructorDecl>(FD)) {
       if (I == 0)
         // For the 'this' argument, the attribute is on the method itself.
         return implicitObjectParamIsLifetimeBound(Method)
-                   ? std::optional<OriginFlowFact::LifetimeBoundInfo>(
-                         OriginFlowFact::LifetimeBoundInfo{
-                             Call, /*Param=*/nullptr,
-                             /*isImplicitObject=*/true})
+                   ? std::optional<LifetimeBoundInfo>(
+                         LifetimeBoundInfo{/*Param=*/nullptr,
+                                           /*IsImplicitObject=*/true})
                    : std::nullopt;
       if ((I - 1) < Method->getNumParams())
         // For explicit arguments, find the corresponding parameter
@@ -1108,8 +1106,7 @@ void FactsGenerator::handleFunctionCall(const Expr *Call,
       PVD = FD->getParamDecl(I);
     }
     if (PVD && PVD->hasAttr<clang::LifetimeBoundAttr>())
-      return OriginFlowFact::LifetimeBoundInfo{Call, PVD,
-                                               /*isImplicitObject=*/false};
+      return LifetimeBoundInfo{PVD, /*IsImplicitObject=*/false};
     return std::nullopt;
   };
   auto IsArgLifetimeBound = [FD, &Args, &GetLifetimeBoundInfo](unsigned I) {
@@ -1165,9 +1162,11 @@ void FactsGenerator::handleFunctionCall(const Expr *Call,
         // destination origin lists may have different lengths.
         // FIXME: Handle origin-shape mismatches gracefully so we can also flow
         // inner origins.
-        CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>(
-            CallList->getOuterOriginID(), ArgList->getOuterOriginID(), KillSrc,
-            GetLifetimeBoundInfo(I)));
+        auto *F = FactMgr.createFact<OriginFlowFact>(
+            CallList->getOuterOriginID(), ArgList->getOuterOriginID(), 
KillSrc);
+        if (std::optional<LifetimeBoundInfo> Info = GetLifetimeBoundInfo(I))
+          FactMgr.setLifetimeBoundInfo(F, *Info);
+        CurrentBlockFacts.push_back(F);
         KillSrc = false;
       }
     } else if (shouldTrackPointerImplicitObjectArg(I)) {
@@ -1182,9 +1181,11 @@ void FactsGenerator::handleFunctionCall(const Expr *Call,
       // Lifetimebound on a non-GSL-ctor function means the returned
       // pointer/reference itself must not outlive the arguments. This
       // only constrains the top-level origin.
-      CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>(
-          CallList->getOuterOriginID(), ArgList->getOuterOriginID(), KillSrc,
-          GetLifetimeBoundInfo(I)));
+      auto *F = FactMgr.createFact<OriginFlowFact>(
+          CallList->getOuterOriginID(), ArgList->getOuterOriginID(), KillSrc);
+      if (std::optional<LifetimeBoundInfo> Info = GetLifetimeBoundInfo(I))
+        FactMgr.setLifetimeBoundInfo(F, *Info);
+      CurrentBlockFacts.push_back(F);
       KillSrc = false;
     }
   }
diff --git a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp 
b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
index 01ef3b67e7351..2179ec897cf74 100644
--- a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
@@ -202,7 +202,7 @@ class AnalysisImpl
     return getLoans(getState(P), OID);
   }
 
-  llvm::SmallVector<OriginFlowChainStep>
+  llvm::SmallVector<const OriginFlowFact *>
   buildOriginFlowChainWithFacts(ProgramPoint StartPoint,
                                 const OriginID StartOID,
                                 const LoanID TargetLoan) const {
@@ -210,7 +210,7 @@ class AnalysisImpl
            "TargetLoan must be present in the StartOID at the StartPoint");
 
     OriginID CurrOID = StartOID;
-    llvm::SmallVector<OriginFlowChainStep> OriginFlowChain;
+    llvm::SmallVector<const OriginFlowFact *> OriginFlowChain;
     llvm::ArrayRef<const Fact *> Facts = 
FactMgr.getBlockContaining(StartPoint);
     const auto *StartIt = llvm::find(Facts, StartPoint);
     assert(StartIt != Facts.end());
@@ -232,7 +232,7 @@ class AnalysisImpl
       const OriginID SrcOriginID = OFF->getSrcOriginID();
       if (!getLoans(SrcOriginID, OFF).contains(TargetLoan))
         continue;
-      OriginFlowChain.push_back({SrcOriginID, OFF});
+      OriginFlowChain.push_back(OFF);
       CurrOID = SrcOriginID;
     }
 
@@ -248,13 +248,13 @@ class AnalysisImpl
   buildOriginFlowChain(ProgramPoint StartPoint, const OriginID StartOID,
                        const LoanID TargetLoan) const {
     llvm::SmallVector<OriginID> OriginFlowChain;
-    for (const OriginFlowChainStep &Step :
+    for (const OriginFlowFact *Flow :
          buildOriginFlowChainWithFacts(StartPoint, StartOID, TargetLoan))
-      OriginFlowChain.push_back(Step.OID);
+      OriginFlowChain.push_back(Flow->getSrcOriginID());
     return OriginFlowChain;
   }
 
-  llvm::SmallVector<OriginFlowChainStep>
+  llvm::SmallVector<const OriginFlowFact *>
   buildOriginFlowChainWithFacts(const UseFact *UF,
                                 const LoanID TargetLoan) const {
     for (const OriginList *Cur = UF->getUsedOrigins(); Cur;
@@ -339,14 +339,14 @@ LoanPropagationAnalysis::buildOriginFlowChain(const 
UseFact *UF,
   return PImpl->buildOriginFlowChain(UF, TargetLoan);
 }
 
-llvm::SmallVector<OriginFlowChainStep>
+llvm::SmallVector<const OriginFlowFact *>
 LoanPropagationAnalysis::buildOriginFlowChainWithFacts(
     ProgramPoint StartPoint, const OriginID StartOID,
     const LoanID TargetLoan) const {
   return PImpl->buildOriginFlowChainWithFacts(StartPoint, StartOID, 
TargetLoan);
 }
 
-llvm::SmallVector<OriginFlowChainStep>
+llvm::SmallVector<const OriginFlowFact *>
 LoanPropagationAnalysis::buildOriginFlowChainWithFacts(
     const UseFact *UF, const LoanID TargetLoan) const {
   return PImpl->buildOriginFlowChainWithFacts(UF, TargetLoan);
diff --git a/clang/lib/Sema/SemaLifetimeSafety.h 
b/clang/lib/Sema/SemaLifetimeSafety.h
index 1828096debb4c..a2a6913dfe7c8 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -669,20 +669,17 @@ class LifetimeSafetySemaHelperImpl : public 
LifetimeSafetySemaHelper {
          reverse(OriginExprChain.drop_back())) {
       const Expr *CurrExpr = Curr.E;
       if (!shouldShowInAliasChain(CurrExpr, LastExpr)) {
-        if (!Last.LifetimeBoundImplicitObject && !Last.LifetimeBoundParam &&
-            (Curr.LifetimeBoundImplicitObject || Curr.LifetimeBoundParam)) {
-          Last.LifetimeBoundImplicitObject = Curr.LifetimeBoundImplicitObject;
-          Last.LifetimeBoundParam = Curr.LifetimeBoundParam;
-        }
+        if (!Last.LifetimeBound && Curr.LifetimeBound)
+          Last.LifetimeBound = Curr.LifetimeBound;
         continue;
       }
-      if (Last.LifetimeBoundImplicitObject || Last.LifetimeBoundParam) {
+      if (Last.LifetimeBound) {
         std::string LifetimeBoundSubject = "the implicit object parameter";
-        if (Last.LifetimeBoundParam) {
+        if (Last.LifetimeBound->Param) {
           LifetimeBoundSubject = "parameter ";
-          if (Last.LifetimeBoundParam->getIdentifier())
+          if (Last.LifetimeBound->Param->getIdentifier())
             LifetimeBoundSubject +=
-                "'" + Last.LifetimeBoundParam->getNameAsString() + "'";
+                "'" + Last.LifetimeBound->Param->getNameAsString() + "'";
           else
             LifetimeBoundSubject += "'<unnamed>'";
         }

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

Reply via email to