https://github.com/usx95 updated 
https://github.com/llvm/llvm-project/pull/159991

>From 73f7069f9100441ccb3027b0890284b5d7fcf80e Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena <u...@google.com>
Date: Sun, 21 Sep 2025 16:30:28 +0000
Subject: [PATCH] liveness-based-lifetime-policy

---
 .../clang/Analysis/Analyses/LifetimeSafety.h  |   9 +-
 clang/lib/Analysis/LifetimeSafety.cpp         | 392 +++++++-------
 clang/test/Sema/warn-lifetime-safety.cpp      | 141 +++--
 .../unittests/Analysis/LifetimeSafetyTest.cpp | 501 +++++++++---------
 4 files changed, 583 insertions(+), 460 deletions(-)

diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety.h
index 512cb76cd6349..2cc3fb3d69eb4 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety.h
@@ -55,6 +55,7 @@ class Fact;
 class FactManager;
 class LoanPropagationAnalysis;
 class ExpiredLoansAnalysis;
+class LiveOriginAnalysis;
 struct LifetimeFactory;
 
 /// A generic, type-safe wrapper for an ID, distinguished by its `Tag` type.
@@ -89,6 +90,7 @@ inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, 
OriginID ID) {
 // TODO(opt): Consider using a bitset to represent the set of loans.
 using LoanSet = llvm::ImmutableSet<LoanID>;
 using OriginSet = llvm::ImmutableSet<OriginID>;
+using OriginLoanMap = llvm::ImmutableMap<OriginID, LoanSet>;
 
 /// A `ProgramPoint` identifies a location in the CFG by pointing to a specific
 /// `Fact`. identified by a lifetime-related event (`Fact`).
@@ -110,8 +112,9 @@ class LifetimeSafetyAnalysis {
   /// Returns the set of loans an origin holds at a specific program point.
   LoanSet getLoansAtPoint(OriginID OID, ProgramPoint PP) const;
 
-  /// Returns the set of loans that have expired at a specific program point.
-  std::vector<LoanID> getExpiredLoansAtPoint(ProgramPoint PP) const;
+  /// TODO:Document.
+  std::vector<std::pair<OriginID, Confidence>>
+  getLiveOriginsAtPoint(ProgramPoint PP) const;
 
   /// Finds the OriginID for a given declaration.
   /// Returns a null optional if not found.
@@ -138,7 +141,7 @@ class LifetimeSafetyAnalysis {
   std::unique_ptr<LifetimeFactory> Factory;
   std::unique_ptr<FactManager> FactMgr;
   std::unique_ptr<LoanPropagationAnalysis> LoanPropagation;
-  std::unique_ptr<ExpiredLoansAnalysis> ExpiredLoans;
+  std::unique_ptr<LiveOriginAnalysis> LiveOrigins;
 };
 } // namespace internal
 } // namespace clang::lifetimes
diff --git a/clang/lib/Analysis/LifetimeSafety.cpp 
b/clang/lib/Analysis/LifetimeSafety.cpp
index 14fb945382e65..01076a3cee7f2 100644
--- a/clang/lib/Analysis/LifetimeSafety.cpp
+++ b/clang/lib/Analysis/LifetimeSafety.cpp
@@ -22,6 +22,7 @@
 #include "llvm/ADT/SmallBitVector.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/Support/Debug.h"
+#include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/TimeProfiler.h"
 #include <cstdint>
 #include <memory>
@@ -873,19 +874,26 @@ class DataflowAnalysis {
     llvm::SmallBitVector Visited(Cfg.getNumBlockIDs() + 1);
 
     while (const CFGBlock *B = W.dequeue()) {
-      Lattice StateIn = getInState(B);
+      Lattice StateIn = *getInState(B);
       Lattice StateOut = transferBlock(B, StateIn);
       OutStates[B] = StateOut;
-      Visited.set(B->getBlockID());
       for (const CFGBlock *AdjacentB : isForward() ? B->succs() : B->preds()) {
         if (!AdjacentB)
           continue;
-        Lattice OldInState = getInState(AdjacentB);
-        Lattice NewInState = D.join(OldInState, StateOut);
+        Lattice OldInState;
+        bool SawFirstTime = false;
+        Lattice NewInState;
+        if (const Lattice *In = getInState(AdjacentB)) {
+          OldInState = *In;
+          NewInState = D.join(OldInState, StateOut);
+        } else {
+          OldInState = D.getInitialState();
+          SawFirstTime = true;
+          NewInState = StateOut;
+        }
         // Enqueue the adjacent block if its in-state has changed or if we have
         // never visited it.
-        if (!Visited.test(AdjacentB->getBlockID()) ||
-            NewInState != OldInState) {
+        if (SawFirstTime || NewInState != OldInState) {
           InStates[AdjacentB] = NewInState;
           W.enqueueBlock(AdjacentB);
         }
@@ -896,7 +904,12 @@ class DataflowAnalysis {
 protected:
   Lattice getState(ProgramPoint P) const { return PerPointStates.lookup(P); }
 
-  Lattice getInState(const CFGBlock *B) const { return InStates.lookup(B); }
+  const Lattice *getInState(const CFGBlock *B) const {
+    auto It = InStates.find(B);
+    if (It != InStates.end())
+      return &It->second;
+    return nullptr;
+  }
 
   Lattice getOutState(const CFGBlock *B) const { return OutStates.lookup(B); }
 
@@ -992,22 +1005,26 @@ static bool isSubsetOf(const llvm::ImmutableSet<T> &A,
 // instead of the current AVL-tree-based ImmutableMap.
 template <typename K, typename V, typename Joiner>
 static llvm::ImmutableMap<K, V>
-join(llvm::ImmutableMap<K, V> A, llvm::ImmutableMap<K, V> B,
+join(const llvm::ImmutableMap<K, V> &A, const llvm::ImmutableMap<K, V> &B,
      typename llvm::ImmutableMap<K, V>::Factory &F, Joiner JoinValues) {
   if (A.getHeight() < B.getHeight())
-    std::swap(A, B);
+    return join(B, A, F, JoinValues);
 
   // For each element in B, join it with the corresponding element in A
   // (or with an empty value if it doesn't exist in A).
+  llvm::ImmutableMap<K, V> Res = A;
   for (const auto &Entry : B) {
     const K &Key = Entry.first;
     const V &ValB = Entry.second;
-    if (const V *ValA = A.lookup(Key))
-      A = F.add(A, Key, JoinValues(*ValA, ValB));
-    else
-      A = F.add(A, Key, ValB);
+    Res = F.add(Res, Key, JoinValues(A.lookup(Key), &ValB));
   }
-  return A;
+  for (const auto &Entry : A) {
+    const K &Key = Entry.first;
+    const V &ValA = Entry.second;
+    if (!B.contains(Key))
+      Res = F.add(Res, Key, JoinValues(&ValA, nullptr));
+  }
+  return Res;
 }
 } // namespace utils
 
@@ -1015,19 +1032,6 @@ join(llvm::ImmutableMap<K, V> A, llvm::ImmutableMap<K, 
V> B,
 //                          Loan Propagation Analysis
 // ========================================================================= //
 
-using OriginLoanMap = llvm::ImmutableMap<OriginID, LoanSet>;
-using ExpiredLoanMap = llvm::ImmutableMap<LoanID, const ExpireFact *>;
-
-/// An object to hold the factories for immutable collections, ensuring
-/// that all created states share the same underlying memory management.
-struct LifetimeFactory {
-  llvm::BumpPtrAllocator Allocator;
-  OriginLoanMap::Factory OriginMapFactory{Allocator, /*canonicalize=*/false};
-  LoanSet::Factory LoanSetFactory{Allocator, /*canonicalize=*/false};
-  ExpiredLoanMap::Factory ExpiredLoanMapFactory{Allocator,
-                                                /*canonicalize=*/false};
-};
-
 /// Represents the dataflow lattice for loan propagation.
 ///
 /// This lattice tracks which loans each origin may hold at a given program
@@ -1071,10 +1075,10 @@ class LoanPropagationAnalysis
 
 public:
   LoanPropagationAnalysis(const CFG &C, AnalysisDeclContext &AC, FactManager 
&F,
-                          LifetimeFactory &LFactory)
-      : DataflowAnalysis(C, AC, F),
-        OriginLoanMapFactory(LFactory.OriginMapFactory),
-        LoanSetFactory(LFactory.LoanSetFactory) {}
+                          OriginLoanMap::Factory &OriginLoanMapFactory,
+                          LoanSet::Factory &LoanSetFactory)
+      : DataflowAnalysis(C, AC, F), OriginLoanMapFactory(OriginLoanMapFactory),
+        LoanSetFactory(LoanSetFactory) {}
 
   using Base::transfer;
 
@@ -1087,8 +1091,13 @@ class LoanPropagationAnalysis
   Lattice join(Lattice A, Lattice B) {
     OriginLoanMap JoinedOrigins =
         utils::join(A.Origins, B.Origins, OriginLoanMapFactory,
-                    [&](LoanSet S1, LoanSet S2) {
-                      return utils::join(S1, S2, LoanSetFactory);
+                    [&](const LoanSet *S1, const LoanSet *S2) {
+                      assert((S1 || S2) && "unexpectedly merging 2 empty 
sets");
+                      if (!S1)
+                        return *S2;
+                      if (!S2)
+                        return *S1;
+                      return utils::join(*S1, *S2, LoanSetFactory);
                     });
     return Lattice(JoinedOrigins);
   }
@@ -1131,96 +1140,151 @@ class LoanPropagationAnalysis
 };
 
 // ========================================================================= //
-//                         Expired Loans Analysis
+//                         Live Origins Analysis
 // ========================================================================= //
 
-/// The dataflow lattice for tracking the set of expired loans.
-struct ExpiredLattice {
-  /// Map from an expired `LoanID` to the `ExpireFact` that made it expire.
-  ExpiredLoanMap Expired;
+/// Information about why an origin is live at a program point.
+struct LivenessInfo {
+  // TODO: Doc.
+  const UseFact *CausingUseFact;
+  // TODO: Doc.
+  Confidence ConfidenceLevel;
 
-  ExpiredLattice() : Expired(nullptr) {};
-  explicit ExpiredLattice(ExpiredLoanMap M) : Expired(M) {}
+  LivenessInfo() : CausingUseFact(nullptr), ConfidenceLevel(Confidence::None) 
{}
+  LivenessInfo(const UseFact *UF, Confidence C)
+      : CausingUseFact(UF), ConfidenceLevel(C) {}
+
+  bool operator==(const LivenessInfo &Other) const {
+    return CausingUseFact == Other.CausingUseFact &&
+           ConfidenceLevel == Other.ConfidenceLevel;
+  }
+  bool operator!=(const LivenessInfo &Other) const { return !(*this == Other); 
}
 
-  bool operator==(const ExpiredLattice &Other) const {
-    return Expired == Other.Expired;
+  void Profile(llvm::FoldingSetNodeID &IDBuilder) const {
+    IDBuilder.AddPointer(CausingUseFact);
+    IDBuilder.Add(ConfidenceLevel);
   }
-  bool operator!=(const ExpiredLattice &Other) const {
+};
+
+using LivenessMap = llvm::ImmutableMap<OriginID, LivenessInfo>;
+
+/// The dataflow lattice for origin liveness analysis.
+/// It tracks which origins are live, why they're live (which UseFact),
+/// and the confidence level of that liveness.
+struct LivenessLattice {
+  LivenessMap LiveOrigins;
+
+  LivenessLattice() : LiveOrigins(nullptr) {};
+
+  explicit LivenessLattice(LivenessMap L) : LiveOrigins(L) {}
+
+  bool operator==(const LivenessLattice &Other) const {
+    return LiveOrigins == Other.LiveOrigins;
+  }
+
+  bool operator!=(const LivenessLattice &Other) const {
     return !(*this == Other);
   }
 
   void dump(llvm::raw_ostream &OS) const {
-    OS << "ExpiredLattice State:\n";
-    if (Expired.isEmpty())
+    OS << "LivenessLattice State:\n";
+    if (LiveOrigins.isEmpty())
       OS << "  <empty>\n";
-    for (const auto &[ID, _] : Expired)
-      OS << "  Loan " << ID << " is expired\n";
+    for (const auto &Entry : LiveOrigins) {
+      OriginID OID = Entry.first;
+      const LivenessInfo &Info = Entry.second;
+      OS << "  Origin " << OID << " is ";
+      switch (Info.ConfidenceLevel) {
+      case Confidence::Definite:
+        OS << "definitely";
+        break;
+      case Confidence::Maybe:
+        OS << "maybe";
+        break;
+      case Confidence::None:
+        llvm_unreachable("liveness condidence should not be none.");
+      }
+      OS << " live at this point\n";
+    }
   }
 };
 
-/// The analysis that tracks which loans have expired.
-class ExpiredLoansAnalysis
-    : public DataflowAnalysis<ExpiredLoansAnalysis, ExpiredLattice,
-                              Direction::Forward> {
-
-  ExpiredLoanMap::Factory &Factory;
+/// The analysis that tracks which origins are live, with granular information
+/// about the causing use fact and confidence level. This is a backward
+/// analysis.
+class LiveOriginAnalysis
+    : public DataflowAnalysis<LiveOriginAnalysis, LivenessLattice,
+                              Direction::Backward> {
+  FactManager &FactMgr;
+  LivenessMap::Factory &Factory;
 
 public:
-  ExpiredLoansAnalysis(const CFG &C, AnalysisDeclContext &AC, FactManager &F,
-                       LifetimeFactory &Factory)
-      : DataflowAnalysis(C, AC, F), Factory(Factory.ExpiredLoanMapFactory) {}
+  LiveOriginAnalysis(const CFG &C, AnalysisDeclContext &AC, FactManager &F,
+                     LivenessMap::Factory &SF)
+      : DataflowAnalysis(C, AC, F), FactMgr(F), Factory(SF) {}
+  using DataflowAnalysis<LiveOriginAnalysis, Lattice,
+                         Direction::Backward>::transfer;
 
-  using Base::transfer;
-
-  StringRef getAnalysisName() const { return "ExpiredLoans"; }
+  StringRef getAnalysisName() const { return "LiveOrigins"; }
 
   Lattice getInitialState() { return Lattice(Factory.getEmptyMap()); }
 
-  /// Merges two lattices by taking the union of the two expired loans.
-  Lattice join(Lattice L1, Lattice L2) {
-    return Lattice(
-        utils::join(L1.Expired, L2.Expired, Factory,
-                    // Take the last expiry fact to make this hermetic.
-                    [](const ExpireFact *F1, const ExpireFact *F2) {
-                      return F1->getExpiryLoc() > F2->getExpiryLoc() ? F1 : F2;
-                    }));
-  }
-
-  Lattice transfer(Lattice In, const ExpireFact &F) {
-    return Lattice(Factory.add(In.Expired, F.getLoanID(), &F));
-  }
-
-  // Removes the loan from the set of expired loans.
-  //
-  // When a loan is re-issued (e.g., in a loop), it is no longer considered
-  // expired. A loan can be in the expired set at the point of issue due to
-  // the dataflow state from a previous loop iteration being propagated along
-  // a backedge in the CFG.
-  //
-  // Note: This has a subtle false-negative though where a loan from previous
-  // iteration is not overwritten by a reissue. This needs careful tracking
-  // of loans "across iterations" which can be considered for future
-  // enhancements.
-  //
-  //    void foo(int safe) {
-  //      int* p = &safe;
-  //      int* q = &safe;
-  //      while (condition()) {
-  //        int x = 1;
-  //        p = &x;    // A loan to 'x' is issued to 'p' in every iteration.
-  //        if (condition()) {
-  //          q = p;
-  //        }
-  //        (void)*p; // OK  — 'p' points to 'x' from new iteration.
-  //        (void)*q; // UaF - 'q' still points to 'x' from previous iteration
-  //                  // which is now destroyed.
-  //      }
-  // }
-  Lattice transfer(Lattice In, const IssueFact &F) {
-    return Lattice(Factory.remove(In.Expired, F.getLoanID()));
+  /// Merges two lattices by combining liveness information.
+  /// When the same origin has different confidence levels, we take the lower
+  /// one.
+  Lattice join(Lattice L1, Lattice L2) const {
+    LivenessMap Merged = L1.LiveOrigins;
+    auto CombineConfidence = [](Confidence C1, Confidence C2) -> Confidence {
+      if (C1 == Confidence::Definite && C2 == Confidence::Definite)
+        return Confidence::Definite;
+      return Confidence::Maybe;
+    };
+    auto CombineUseFact = [](const LivenessInfo &A,
+                             const LivenessInfo &B) -> const UseFact * {
+      return A.ConfidenceLevel >= B.ConfidenceLevel ? A.CausingUseFact
+                                                    : B.CausingUseFact;
+    };
+    return Lattice(utils::join(
+        L1.LiveOrigins, L2.LiveOrigins, Factory,
+        [&](const LivenessInfo *L1, const LivenessInfo *L2) -> LivenessInfo {
+          assert((L1 || L2) && "unexpectedly merging 2 empty sets");
+          if (!L1)
+            return LivenessInfo(L2->CausingUseFact, Confidence::Maybe);
+          if (!L2)
+            return LivenessInfo(L1->CausingUseFact, Confidence::Maybe);
+          return LivenessInfo(
+              CombineUseFact(*L1, *L2),
+              CombineConfidence(L1->ConfidenceLevel, L2->ConfidenceLevel));
+        }));
   }
 
-  ExpiredLoanMap getExpiredLoans(ProgramPoint P) { return getState(P).Expired; 
}
+  /// TODO:Document.
+  Lattice transfer(Lattice In, const UseFact &UF) {
+    OriginID OID = UF.getUsedOrigin(FactMgr.getOriginMgr());
+    // Write kills liveness.
+    if (UF.isWritten())
+      return Lattice(Factory.remove(In.LiveOrigins, OID));
+    // Read makes origin live with definite confidence (dominates this point).
+    LivenessInfo Info(&UF, Confidence::Definite);
+    return Lattice(Factory.add(In.LiveOrigins, OID, Info));
+  }
+
+  /// Issuing a new loan to an origin kills its liveness.
+  Lattice transfer(Lattice In, const IssueFact &IF) {
+    return Lattice(Factory.remove(In.LiveOrigins, IF.getOriginID()));
+  }
+
+  Lattice transfer(Lattice In, const OriginFlowFact &OF) {
+    if (!OF.getKillDest())
+      return In;
+    return Lattice(Factory.remove(In.LiveOrigins, OF.getDestOriginID()));
+  }
+
+  Lattice transfer(Lattice In, const KillOriginFact &KF) {
+    return Lattice(Factory.remove(In.LiveOrigins, KF.getOriginID()));
+  }
+
+  LivenessMap getLiveOrigins(ProgramPoint P) { return getState(P).LiveOrigins; 
}
 };
 
 // ========================================================================= //
@@ -1238,84 +1302,49 @@ class LifetimeChecker {
 private:
   llvm::DenseMap<LoanID, PendingWarning> FinalWarningsMap;
   LoanPropagationAnalysis &LoanPropagation;
-  ExpiredLoansAnalysis &ExpiredLoans;
+  LiveOriginAnalysis &LiveOrigins;
   FactManager &FactMgr;
   AnalysisDeclContext &ADC;
   LifetimeSafetyReporter *Reporter;
 
 public:
-  LifetimeChecker(LoanPropagationAnalysis &LPA, ExpiredLoansAnalysis &ELA,
+  LifetimeChecker(LoanPropagationAnalysis &LPA, LiveOriginAnalysis &LOA,
                   FactManager &FM, AnalysisDeclContext &ADC,
                   LifetimeSafetyReporter *Reporter)
-      : LoanPropagation(LPA), ExpiredLoans(ELA), FactMgr(FM), ADC(ADC),
+      : LoanPropagation(LPA), LiveOrigins(LOA), FactMgr(FM), ADC(ADC),
         Reporter(Reporter) {}
 
   void run() {
     llvm::TimeTraceScope TimeProfile("LifetimeChecker");
     for (const CFGBlock *B : *ADC.getAnalysis<PostOrderCFGView>())
       for (const Fact *F : FactMgr.getFacts(B))
-        if (const auto *UF = F->getAs<UseFact>())
-          checkUse(UF);
+        if (const auto *EF = F->getAs<ExpireFact>())
+          checkExpiry(EF);
     issuePendingWarnings();
   }
 
-  /// Checks for use-after-free errors for a given use of an Origin.
-  ///
-  /// This method is called for each 'UseFact' identified in the control flow
-  /// graph. It determines if the loans held by the used origin have expired
-  /// at the point of use.
-  void checkUse(const UseFact *UF) {
-    if (UF->isWritten())
-      return;
-    OriginID O = UF->getUsedOrigin(FactMgr.getOriginMgr());
-
-    // Get the set of loans that the origin might hold at this program point.
-    LoanSet HeldLoans = LoanPropagation.getLoans(O, UF);
-
-    // Get the set of all loans that have expired at this program point.
-    ExpiredLoanMap AllExpiredLoans = ExpiredLoans.getExpiredLoans(UF);
-
-    // If the pointer holds no loans or no loans have expired, there's nothing
-    // to check.
-    if (HeldLoans.isEmpty() || AllExpiredLoans.isEmpty())
-      return;
-
-    // Identify loans that which have expired but are held by the pointer. 
Using
-    // them is a use-after-free.
-    llvm::SmallVector<LoanID> DefaultedLoans;
-    // A definite UaF error occurs if all loans the origin might hold have
-    // expired.
-    bool IsDefiniteError = true;
-    for (LoanID L : HeldLoans) {
-      if (AllExpiredLoans.contains(L))
-        DefaultedLoans.push_back(L);
-      else
-        // If at least one loan is not expired, this use is not a definite UaF.
-        IsDefiniteError = false;
-    }
-    // If there are no defaulted loans, the use is safe.
-    if (DefaultedLoans.empty())
-      return;
-
-    // Determine the confidence level of the error (definite or maybe).
-    Confidence CurrentConfidence =
-        IsDefiniteError ? Confidence::Definite : Confidence::Maybe;
-
-    // For each expired loan, create a pending warning.
-    for (LoanID DefaultedLoan : DefaultedLoans) {
-      // If we already have a warning for this loan with a higher or equal
-      // confidence, skip this one.
-      if (FinalWarningsMap.count(DefaultedLoan) &&
-          CurrentConfidence <= FinalWarningsMap[DefaultedLoan].ConfidenceLevel)
+  void checkExpiry(const ExpireFact *EF) {
+    LoanID ExpiredLoan = EF->getLoanID();
+    LivenessMap Origins = LiveOrigins.getLiveOrigins(EF);
+    Confidence CurConfidence = Confidence::None;
+    const UseFact *BadUse = nullptr;
+    for (auto &[OID, Info] : Origins) {
+      LoanSet HeldLoans = LoanPropagation.getLoans(OID, EF);
+      if (!HeldLoans.contains(ExpiredLoan))
         continue;
-
-      auto *EF = AllExpiredLoans.lookup(DefaultedLoan);
-      assert(EF && "Could not find ExpireFact for an expired loan.");
-
-      FinalWarningsMap[DefaultedLoan] = {/*ExpiryLoc=*/(*EF)->getExpiryLoc(),
-                                         /*UseExpr=*/UF->getUseExpr(),
-                                         
/*ConfidenceLevel=*/CurrentConfidence};
+      // Loan is defaulted.
+      if (CurConfidence < Info.ConfidenceLevel) {
+        CurConfidence = Info.ConfidenceLevel;
+        BadUse = Info.CausingUseFact;
+      }
     }
+    Confidence LastConf = FinalWarningsMap.lookup(ExpiredLoan).ConfidenceLevel;
+    if (LastConf >= CurConfidence)
+      return;
+    // We have a use-after-free.
+    FinalWarningsMap[ExpiredLoan] = {/*ExpiryLoc=*/EF->getExpiryLoc(),
+                                     /*UseExpr=*/BadUse->getUseExpr(),
+                                     /*ConfidenceLevel=*/CurConfidence};
   }
 
   void issuePendingWarnings() {
@@ -1334,6 +1363,15 @@ class LifetimeChecker {
 //                  LifetimeSafetyAnalysis Class Implementation
 // ========================================================================= //
 
+/// An object to hold the factories for immutable collections, ensuring
+/// that all created states share the same underlying memory management.
+struct LifetimeFactory {
+  llvm::BumpPtrAllocator Allocator;
+  OriginLoanMap::Factory OriginMapFactory{Allocator, /*canonicalize=*/false};
+  LoanSet::Factory LoanSetFactory{Allocator, /*canonicalize=*/false};
+  LivenessMap::Factory LivenessMapFactory{Allocator, /*canonicalize=*/false};
+};
+
 // We need this here for unique_ptr with forward declared class.
 LifetimeSafetyAnalysis::~LifetimeSafetyAnalysis() = default;
 
@@ -1364,15 +1402,15 @@ void LifetimeSafetyAnalysis::run() {
   ///    the analysis.
   /// 3. Collapse ExpireFacts belonging to same source location into a single
   ///    Fact.
-  LoanPropagation =
-      std::make_unique<LoanPropagationAnalysis>(Cfg, AC, *FactMgr, *Factory);
+  LoanPropagation = std::make_unique<LoanPropagationAnalysis>(
+      Cfg, AC, *FactMgr, Factory->OriginMapFactory, Factory->LoanSetFactory);
   LoanPropagation->run();
 
-  ExpiredLoans =
-      std::make_unique<ExpiredLoansAnalysis>(Cfg, AC, *FactMgr, *Factory);
-  ExpiredLoans->run();
+  LiveOrigins = std::make_unique<LiveOriginAnalysis>(
+      Cfg, AC, *FactMgr, Factory->LivenessMapFactory);
+  LiveOrigins->run();
 
-  LifetimeChecker Checker(*LoanPropagation, *ExpiredLoans, *FactMgr, AC,
+  LifetimeChecker Checker(*LoanPropagation, *LiveOrigins, *FactMgr, AC,
                           Reporter);
   Checker.run();
 }
@@ -1383,15 +1421,6 @@ LoanSet LifetimeSafetyAnalysis::getLoansAtPoint(OriginID 
OID,
   return LoanPropagation->getLoans(OID, PP);
 }
 
-std::vector<LoanID>
-LifetimeSafetyAnalysis::getExpiredLoansAtPoint(ProgramPoint PP) const {
-  assert(ExpiredLoans && "ExpiredLoansAnalysis has not been run.");
-  std::vector<LoanID> Result;
-  for (const auto &pair : ExpiredLoans->getExpiredLoans(PP))
-    Result.push_back(pair.first);
-  return Result;
-}
-
 std::optional<OriginID>
 LifetimeSafetyAnalysis::getOriginIDForDecl(const ValueDecl *D) const {
   assert(FactMgr && "FactManager not initialized");
@@ -1411,6 +1440,15 @@ LifetimeSafetyAnalysis::getLoanIDForVar(const VarDecl 
*VD) const {
   return Result;
 }
 
+std::vector<std::pair<OriginID, Confidence>>
+LifetimeSafetyAnalysis::getLiveOriginsAtPoint(ProgramPoint PP) const {
+  assert(LiveOrigins && "LiveOriginAnalysis has not been run.");
+  std::vector<std::pair<OriginID, Confidence>> Result;
+  for (auto &[OID, Info] : LiveOrigins->getLiveOrigins(PP))
+    Result.push_back({OID, Info.ConfidenceLevel});
+  return Result;
+}
+
 llvm::StringMap<ProgramPoint> LifetimeSafetyAnalysis::getTestPoints() const {
   assert(FactMgr && "FactManager not initialized");
   llvm::StringMap<ProgramPoint> AnnotationToPointMap;
diff --git a/clang/test/Sema/warn-lifetime-safety.cpp 
b/clang/test/Sema/warn-lifetime-safety.cpp
index f6bec6e66c365..d15fd29b7f7f1 100644
--- a/clang/test/Sema/warn-lifetime-safety.cpp
+++ b/clang/test/Sema/warn-lifetime-safety.cpp
@@ -126,11 +126,15 @@ void definite_single_pointer_multiple_loans_gsl(bool 
cond) {
   v.use();      // expected-note 2 {{later used here}}
 }
 
-
-//===----------------------------------------------------------------------===//
-// Potential (Maybe) Use-After-Free (-W...strict)
-// These are cases where the pointer *may* become dangling, depending on the 
path taken.
-//===----------------------------------------------------------------------===//
+void definite_if_branch(bool cond) {
+  MyObj safe;
+  MyObj* p = &safe;
+  if (cond) {
+    MyObj temp;
+    p = &temp;  // expected-warning {{object whose reference is captured does 
not live long enough}}
+  }             // expected-note {{destroyed here}}
+  (void)*p;     // expected-note {{later used here}}
+}
 
 void potential_if_branch(bool cond) {
   MyObj safe;
@@ -139,15 +143,18 @@ void potential_if_branch(bool cond) {
     MyObj temp;
     p = &temp;  // expected-warning {{object whose reference is captured may 
not live long enough}}
   }             // expected-note {{destroyed here}}
-  (void)*p;     // expected-note {{later used here}}
+  if (!cond)
+    (void)*p;   // expected-note {{later used here}}
+  else
+    p = &safe;
 }
 
-void potential_if_branch_gsl(bool cond) {
+void definite_if_branch_gsl(bool cond) {
   MyObj safe;
   View v = safe;
   if (cond) {
     MyObj temp;
-    v = temp;   // expected-warning {{object whose reference is captured may 
not live long enough}}
+    v = temp;   // expected-warning {{object whose reference is captured does 
not live long enough}}
   }             // expected-note {{destroyed here}}
   v.use();      // expected-note {{later used here}}
 }
@@ -159,13 +166,14 @@ void definite_potential_together(bool cond) {
 
   {
     MyObj s;
-    p_definite = &s;  // expected-warning {{does not live long enough}}
-    if (cond) {
-      p_maybe = &s;   // expected-warning {{may not live long enough}}
-    }                 
-  }                   // expected-note 2 {{destroyed here}}
-  (void)*p_definite;  // expected-note {{later used here}}
-  (void)*p_maybe;     // expected-note {{later used here}}
+    if (cond)
+      p_definite = &s;  // expected-warning {{does not live long enough}}
+    if (cond)
+      p_maybe = &s;     // expected-warning {{may not live long enough}}       
  
+  }                     // expected-note 2 {{destroyed here}}
+  (void)*p_definite;    // expected-note {{later used here}}
+  if (!cond)
+    (void)*p_maybe;     // expected-note {{later used here}}
 }
 
 void definite_overrides_potential(bool cond) {
@@ -189,10 +197,19 @@ void definite_overrides_potential(bool cond) {
   (void)*q;
 }
 
-
-//===----------------------------------------------------------------------===//
-// Control Flow Tests
-//===----------------------------------------------------------------------===//
+void potential_due_to_conditional_killing(bool cond) {
+  MyObj safe;
+  MyObj* q;
+  {
+    MyObj s;
+    q = &s;       // expected-warning {{may not live long enough}}
+  }               // expected-note {{destroyed here}}
+  if (cond) {
+    // 'q' is conditionally "rescued". 'p' is not.
+    q = &safe;
+  }
+  (void)*q;       // expected-note {{later used here}}
+}
 
 void potential_for_loop_use_after_loop_body(MyObj safe) {
   MyObj* p = &safe;
@@ -216,33 +233,33 @@ void potential_for_loop_gsl() {
 void potential_for_loop_use_before_loop_body(MyObj safe) {
   MyObj* p = &safe;
   for (int i = 0; i < 1; ++i) {
-    (void)*p;   // expected-note {{later used here}}
+    (void)*p;
     MyObj s;
-    p = &s;     // expected-warning {{may not live long enough}}
+    p = &s;     // expected-warning {{does not live long enough}}
   }             // expected-note {{destroyed here}}
-  (void)*p;
+  (void)*p;     // expected-note {{later used here}}
 }
 
-void potential_loop_with_break(bool cond) {
+void definite_loop_with_break(bool cond) {
   MyObj safe;
   MyObj* p = &safe;
   for (int i = 0; i < 10; ++i) {
     if (cond) {
       MyObj temp;
-      p = &temp; // expected-warning {{may not live long enough}}
+      p = &temp; // expected-warning {{does not live long enough}}
       break;     // expected-note {{destroyed here}}
     }           
   } 
   (void)*p;     // expected-note {{later used here}}
 }
 
-void potential_loop_with_break_gsl(bool cond) {
+void definite_loop_with_break_gsl(bool cond) {
   MyObj safe;
   View v = safe;
   for (int i = 0; i < 10; ++i) {
     if (cond) {
       MyObj temp;
-      v = temp;   // expected-warning {{object whose reference is captured may 
not live long enough}}
+      v = temp;   // expected-warning {{object whose reference is captured 
does not live long enough}}
       break;      // expected-note {{destroyed here}}
     }
   }
@@ -250,37 +267,52 @@ void potential_loop_with_break_gsl(bool cond) {
 }
 
 void potential_multiple_expiry_of_same_loan(bool cond) {
-  // Choose the last expiry location for the loan.
+  // Choose the last expiry location for the loan (e.g., through scope-ends 
and break statements).
   MyObj safe;
   MyObj* p = &safe;
   for (int i = 0; i < 10; ++i) {
     MyObj unsafe;
     if (cond) {
-      p = &unsafe; // expected-warning {{may not live long enough}}
-      break;
+      p = &unsafe; // expected-warning {{does not live long enough}}
+      break;       // expected-note {{destroyed here}} 
     }
-  }               // expected-note {{destroyed here}} 
+  }
   (void)*p;       // expected-note {{later used here}}
 
   p = &safe;
   for (int i = 0; i < 10; ++i) {
     MyObj unsafe;
     if (cond) {
-      p = &unsafe;    // expected-warning {{may not live long enough}}
+      p = &unsafe;    // expected-warning {{does not live long enough}}
       if (cond)
-        break;
+        break;        // expected-note {{destroyed here}}
     }
-  }                   // expected-note {{destroyed here}}
+  }
   (void)*p;           // expected-note {{later used here}}
 
   p = &safe;
   for (int i = 0; i < 10; ++i) {
     if (cond) {
       MyObj unsafe2;
-      p = &unsafe2;   // expected-warning {{may not live long enough}}
+      p = &unsafe2;   // expected-warning {{does not live long enough}}
       break;          // expected-note {{destroyed here}}
     }
   }
+
+  // TODO: This can be argued to be a "maybe" warning. This is because
+  // we only check for confidence of liveness and not the confidence of
+  // the loan contained in an origin. To deal with this, we can introduce
+  // a confidence in loan propagation analysis as well like liveness.
+  (void)*p;           // expected-note {{later used here}}
+
+  p = &safe;
+  for (int i = 0; i < 10; ++i) {
+    MyObj unsafe;
+    if (cond)
+      p = &unsafe;    // expected-warning {{does not live long enough}}
+    if (cond)
+      break;          // expected-note {{destroyed here}}
+  }
   (void)*p;           // expected-note {{later used here}}
 }
 
@@ -298,13 +330,14 @@ void potential_switch(int mode) {
     break;
   }
   }
-  (void)*p;     // expected-note {{later used here}}
+  if (mode == 2)
+    (void)*p;     // expected-note {{later used here}}
 }
 
 void definite_switch(int mode) {
   MyObj safe;
   MyObj* p = &safe;
-  // All cases are UaF --> Definite error.
+  // A use domintates all the loan expires --> all definite error.
   switch (mode) {
   case 1: {
     MyObj temp1;
@@ -347,6 +380,21 @@ void definite_switch_gsl(int mode) {
   v.use();      // expected-note 3 {{later used here}}
 }
 
+void loan_from_previous_iteration(MyObj safe, bool condition) {
+  MyObj* p = &safe;
+  MyObj* q = &safe;
+
+  while (condition) {
+    MyObj x;
+    p = &x;     // expected-warning {{may not live long enough}}
+
+    if (condition)
+      q = p;
+    (void)*p;
+    (void)*q;   // expected-note {{later used here}}
+  }             // expected-note {{destroyed here}}
+}
+
 
//===----------------------------------------------------------------------===//
 // No-Error Cases
 
//===----------------------------------------------------------------------===//
@@ -372,6 +420,19 @@ void no_error_if_dangle_then_rescue_gsl() {
   v.use();     // This is safe.
 }
 
+void no_error_loan_from_current_iteration(bool cond) {
+  // See https://github.com/llvm/llvm-project/issues/156959.
+  MyObj b;
+  while (cond) {
+    MyObj a;
+    View p = b;
+    if (cond) {
+      p = a;
+    }
+    (void)p;
+  }
+}
+
 
 
//===----------------------------------------------------------------------===//
 // Lifetimebound Attribute Tests
@@ -415,9 +476,9 @@ void lifetimebound_multiple_args_potential(bool cond) {
     MyObj obj1;
     if (cond) {
       MyObj obj2;
-      v = Choose(true, 
-                 obj1,             // expected-warning {{object whose 
reference is captured may not live long enough}}
-                 obj2);            // expected-warning {{object whose 
reference is captured may not live long enough}}
+      v = Choose(true,
+                 obj1,             // expected-warning {{object whose 
reference is captured does not live long enough}}
+                 obj2);            // expected-warning {{object whose 
reference is captured does not live long enough}}
     }                              // expected-note {{destroyed here}}
   }                                // expected-note {{destroyed here}}
   v.use();                         // expected-note 2 {{later used here}}
@@ -488,7 +549,7 @@ void lifetimebound_partial_safety(bool cond) {
     MyObj temp_obj;
     v = Choose(true, 
                safe_obj,
-               temp_obj); // expected-warning {{object whose reference is 
captured may not live long enough}}
+               temp_obj); // expected-warning {{object whose reference is 
captured does not live long enough}}
   }                       // expected-note {{destroyed here}}
   v.use();                // expected-note {{later used here}}
 }
diff --git a/clang/unittests/Analysis/LifetimeSafetyTest.cpp 
b/clang/unittests/Analysis/LifetimeSafetyTest.cpp
index 3821015f07fb1..41783f7c99352 100644
--- a/clang/unittests/Analysis/LifetimeSafetyTest.cpp
+++ b/clang/unittests/Analysis/LifetimeSafetyTest.cpp
@@ -126,12 +126,12 @@ class LifetimeTestHelper {
     return Analysis.getLoansAtPoint(OID, PP);
   }
 
-  std::optional<std::vector<LoanID>>
-  getExpiredLoansAtPoint(llvm::StringRef Annotation) {
+  std::optional<std::vector<std::pair<OriginID, Confidence>>>
+  getLiveOriginsAtPoint(llvm::StringRef Annotation) {
     ProgramPoint PP = Runner.getProgramPoint(Annotation);
     if (!PP)
       return std::nullopt;
-    return Analysis.getExpiredLoansAtPoint(PP);
+    return Analysis.getLiveOriginsAtPoint(PP);
   }
 
 private:
@@ -180,6 +180,15 @@ class OriginInfo {
   LifetimeTestHelper &Helper;
 };
 
+// A helper class to represent a set of origins, identified by variable names.
+class OriginsInfo {
+public:
+  OriginsInfo(const std::vector<std::string> &Vars, LifetimeTestHelper &H)
+      : OriginVars(Vars), Helper(H) {}
+  std::vector<std::string> OriginVars;
+  LifetimeTestHelper &Helper;
+};
+
 /// Matcher to verify the set of loans held by an origin at a specific
 /// program point.
 ///
@@ -221,14 +230,15 @@ MATCHER_P2(HasLoansToImpl, LoanVars, Annotation, "") {
   std::sort(ExpectedLoans.begin(), ExpectedLoans.end());
   std::sort(ActualLoans.begin(), ActualLoans.end());
   if (ExpectedLoans != ActualLoans) {
-    *result_listener << "Expected: ";
+    *result_listener << "Expected: {";
     for (const auto &LoanID : ExpectedLoans) {
       *result_listener << LoanID.Value << ", ";
     }
-    *result_listener << "Actual: ";
+    *result_listener << "} Actual: {";
     for (const auto &LoanID : ActualLoans) {
       *result_listener << LoanID.Value << ", ";
     }
+    *result_listener << "}";
     return false;
   }
 
@@ -236,32 +246,71 @@ MATCHER_P2(HasLoansToImpl, LoanVars, Annotation, "") {
                             ActualLoans, result_listener);
 }
 
-/// Matcher to verify that the complete set of expired loans at a program point
-/// matches the expected loan set.
-MATCHER_P(AreExpiredAt, Annotation, "") {
-  const LoanSetInfo &Info = arg;
-  auto &Helper = Info.Helper;
+enum class ConfidenceFilter { Maybe, Definite, All };
 
-  auto ActualExpiredSetOpt = Helper.getExpiredLoansAtPoint(Annotation);
-  if (!ActualExpiredSetOpt) {
-    *result_listener << "could not get a valid expired loan set at point '"
+/// Matcher to verify the complete set of live origins at a program point.
+MATCHER_P2(AreLiveAtImpl, Annotation, ConfFilter, "") {
+  const OriginsInfo &Info = arg;
+  auto &Helper = Info.Helper;
+  auto ActualLiveSetOpt = Helper.getLiveOriginsAtPoint(Annotation);
+  if (!ActualLiveSetOpt) {
+    *result_listener << "could not get a valid live origin set at point '"
                      << Annotation << "'";
     return false;
   }
-  std::vector<LoanID> ActualExpiredLoans = *ActualExpiredSetOpt;
-  std::vector<LoanID> ExpectedExpiredLoans;
-  for (const auto &VarName : Info.LoanVars) {
-    auto LoanIDs = Helper.getLoansForVar(VarName);
-    if (LoanIDs.empty()) {
-      *result_listener << "could not find a loan for variable '" << VarName
+  std::vector<OriginID> ActualLiveOrigins;
+  for (const auto [OID, ActualConfidence] : ActualLiveSetOpt.value()) {
+    if (ConfFilter == ConfidenceFilter::All)
+      ActualLiveOrigins.push_back(OID);
+    if (ActualConfidence == Confidence::Maybe &&
+        ConfFilter == ConfidenceFilter::Maybe)
+      ActualLiveOrigins.push_back(OID);
+    if (ActualConfidence == Confidence::Definite &&
+        ConfFilter == ConfidenceFilter::Definite)
+      ActualLiveOrigins.push_back(OID);
+  }
+
+  std::vector<OriginID> ExpectedLiveOrigins;
+  for (const auto &VarName : Info.OriginVars) {
+    auto OriginIDOpt = Helper.getOriginForDecl(VarName);
+    if (!OriginIDOpt) {
+      *result_listener << "could not find an origin for variable '" << VarName
                        << "'";
       return false;
     }
-    ExpectedExpiredLoans.insert(ExpectedExpiredLoans.end(), LoanIDs.begin(),
-                                LoanIDs.end());
+    ExpectedLiveOrigins.push_back(*OriginIDOpt);
   }
-  return ExplainMatchResult(UnorderedElementsAreArray(ExpectedExpiredLoans),
-                            ActualExpiredLoans, result_listener);
+  std::sort(ExpectedLiveOrigins.begin(), ExpectedLiveOrigins.end());
+  std::sort(ActualLiveOrigins.begin(), ActualLiveOrigins.end());
+  if (ExpectedLiveOrigins != ActualLiveOrigins) {
+    *result_listener << "Expected: {";
+    for (const auto &OriginID : ExpectedLiveOrigins) {
+      *result_listener << OriginID.Value << ", ";
+    }
+    *result_listener << "} Actual: {";
+    for (const auto &OriginID : ActualLiveOrigins) {
+      *result_listener << OriginID.Value << ", ";
+    }
+    *result_listener << "}";
+    return false;
+  }
+  return true;
+}
+
+MATCHER_P(AreDefinitelyLiveAt, Annotation, "") {
+  return ExplainMatchResult(
+      AreLiveAtImpl(Annotation, ConfidenceFilter::Definite), arg,
+      result_listener);
+}
+
+MATCHER_P(AreMaybeLiveAt, Annotation, "") {
+  return ExplainMatchResult(AreLiveAtImpl(Annotation, ConfidenceFilter::Maybe),
+                            arg, result_listener);
+}
+
+MATCHER_P(AreLiveAt, Annotation, "") {
+  return ExplainMatchResult(AreLiveAtImpl(Annotation, ConfidenceFilter::All),
+                            arg, result_listener);
 }
 
 // Base test fixture to manage the runner and helper.
@@ -276,6 +325,13 @@ class LifetimeAnalysisTest : public ::testing::Test {
     return OriginInfo(OriginVar, *Helper);
   }
 
+  /// Factory function that hides the std::vector creation.
+  OriginsInfo Origins(std::initializer_list<std::string> OriginVars) {
+    return OriginsInfo({OriginVars}, *Helper);
+  }
+
+  OriginsInfo NoOrigins() { return Origins({}); }
+
   /// Factory function that hides the std::vector creation.
   LoanSetInfo LoansTo(std::initializer_list<std::string> LoanVars) {
     return LoanSetInfo({LoanVars}, *Helper);
@@ -428,29 +484,6 @@ TEST_F(LifetimeAnalysisTest, AssignInSwitch) {
   EXPECT_THAT(Origin("p"), HasLoansTo({"s1", "s2", "s3"}, "after_switch"));
 }
 
-TEST_F(LifetimeAnalysisTest, LoanInLoop) {
-  SetupTest(R"(
-    void target(bool condition) {
-      MyObj* p = nullptr;
-      while (condition) {
-        POINT(start_loop);
-        MyObj inner;
-        p = &inner;
-        POINT(end_loop);
-      }
-      POINT(after_loop);
-    }
-  )");
-  EXPECT_THAT(Origin("p"), HasLoansTo({"inner"}, "start_loop"));
-  EXPECT_THAT(LoansTo({"inner"}), AreExpiredAt("start_loop"));
-
-  EXPECT_THAT(Origin("p"), HasLoansTo({"inner"}, "end_loop"));
-  EXPECT_THAT(NoLoans(), AreExpiredAt("end_loop"));
-
-  EXPECT_THAT(Origin("p"), HasLoansTo({"inner"}, "after_loop"));
-  EXPECT_THAT(LoansTo({"inner"}), AreExpiredAt("after_loop"));
-}
-
 TEST_F(LifetimeAnalysisTest, LoopWithBreak) {
   SetupTest(R"(
     void target(int count) {
@@ -528,20 +561,16 @@ TEST_F(LifetimeAnalysisTest, 
PointersAndExpirationInACycle) {
   )");
   EXPECT_THAT(Origin("p1"), HasLoansTo({"v1"}, "before_while"));
   EXPECT_THAT(Origin("p2"), HasLoansTo({"v2"}, "before_while"));
-  EXPECT_THAT(NoLoans(), AreExpiredAt("before_while"));
 
   EXPECT_THAT(Origin("p1"),
               HasLoansTo({"v1", "v2", "temp"}, "in_loop_before_temp"));
   EXPECT_THAT(Origin("p2"), HasLoansTo({"v2", "temp"}, "in_loop_before_temp"));
-  EXPECT_THAT(LoansTo({"temp"}), AreExpiredAt("in_loop_before_temp"));
 
   EXPECT_THAT(Origin("p1"), HasLoansTo({"temp"}, "in_loop_after_temp"));
   EXPECT_THAT(Origin("p2"), HasLoansTo({"v2", "temp"}, "in_loop_after_temp"));
-  EXPECT_THAT(NoLoans(), AreExpiredAt("in_loop_after_temp"));
 
   EXPECT_THAT(Origin("p1"), HasLoansTo({"v1", "v2", "temp"}, "after_loop"));
   EXPECT_THAT(Origin("p2"), HasLoansTo({"v2", "temp"}, "after_loop"));
-  EXPECT_THAT(LoansTo({"temp"}), AreExpiredAt("after_loop"));
 }
 
 TEST_F(LifetimeAnalysisTest, InfiniteLoopPrunesEdges) {
@@ -585,178 +614,6 @@ TEST_F(LifetimeAnalysisTest, NestedScopes) {
   EXPECT_THAT(Origin("p"), HasLoansTo({"inner"}, "after_inner_scope"));
 }
 
-TEST_F(LifetimeAnalysisTest, SimpleExpiry) {
-  SetupTest(R"(
-    void target() {
-      MyObj* p = nullptr;
-      {
-        MyObj s;
-        p = &s;
-        POINT(before_expiry);
-      } // s goes out of scope here
-      POINT(after_expiry);
-    }
-  )");
-  EXPECT_THAT(NoLoans(), AreExpiredAt("before_expiry"));
-  EXPECT_THAT(LoansTo({"s"}), AreExpiredAt("after_expiry"));
-}
-
-TEST_F(LifetimeAnalysisTest, NestedExpiry) {
-  SetupTest(R"(
-    void target() {
-      MyObj s1;
-      MyObj* p = &s1;
-      POINT(before_inner);
-      {
-        MyObj s2;
-        p = &s2;
-        POINT(in_inner);
-      } // s2 expires
-      POINT(after_inner);
-    }
-  )");
-  EXPECT_THAT(NoLoans(), AreExpiredAt("before_inner"));
-  EXPECT_THAT(NoLoans(), AreExpiredAt("in_inner"));
-  EXPECT_THAT(LoansTo({"s2"}), AreExpiredAt("after_inner"));
-}
-
-TEST_F(LifetimeAnalysisTest, ConditionalExpiry) {
-  SetupTest(R"(
-    void target(bool cond) {
-      MyObj s1;
-      MyObj* p = &s1;
-      POINT(before_if);
-      if (cond) {
-        MyObj s2;
-        p = &s2;
-        POINT(then_block);
-      } // s2 expires here
-      POINT(after_if);
-    }
-  )");
-  EXPECT_THAT(NoLoans(), AreExpiredAt("before_if"));
-  EXPECT_THAT(NoLoans(), AreExpiredAt("then_block"));
-  EXPECT_THAT(LoansTo({"s2"}), AreExpiredAt("after_if"));
-}
-
-TEST_F(LifetimeAnalysisTest, LoopExpiry) {
-  SetupTest(R"(
-    void target() {
-      MyObj *p = nullptr;
-      for (int i = 0; i < 2; ++i) {
-        POINT(start_loop);
-        MyObj s;
-        p = &s;
-        POINT(end_loop);
-      } // s expires here on each iteration
-      POINT(after_loop);
-    }
-  )");
-  EXPECT_THAT(LoansTo({"s"}), AreExpiredAt("start_loop"));
-  EXPECT_THAT(NoLoans(), AreExpiredAt("end_loop"));
-  EXPECT_THAT(LoansTo({"s"}), AreExpiredAt("after_loop"));
-}
-
-TEST_F(LifetimeAnalysisTest, MultipleExpiredLoans) {
-  SetupTest(R"(
-    void target() {
-      MyObj *p1, *p2, *p3;
-      {
-        MyObj s1;
-        p1 = &s1;
-        POINT(p1);
-      } // s1 expires
-      POINT(p2);
-      {
-        MyObj s2;
-        p2 = &s2;
-        MyObj s3;
-        p3 = &s3;
-        POINT(p3);
-      } // s2, s3 expire
-      POINT(p4);
-    }
-  )");
-  EXPECT_THAT(NoLoans(), AreExpiredAt("p1"));
-  EXPECT_THAT(LoansTo({"s1"}), AreExpiredAt("p2"));
-  EXPECT_THAT(LoansTo({"s1"}), AreExpiredAt("p3"));
-  EXPECT_THAT(LoansTo({"s1", "s2", "s3"}), AreExpiredAt("p4"));
-}
-
-TEST_F(LifetimeAnalysisTest, GotoJumpsOutOfScope) {
-  SetupTest(R"(
-    void target(bool cond) {
-      MyObj *p = nullptr;
-      {
-        MyObj s;
-        p = &s;
-        POINT(before_goto);
-        if (cond) {
-          goto end;
-        }
-      } // `s` expires here on the path that doesn't jump
-      POINT(after_scope);
-    end:
-      POINT(after_goto);
-    }
-  )");
-  EXPECT_THAT(NoLoans(), AreExpiredAt("before_goto"));
-  EXPECT_THAT(LoansTo({"s"}), AreExpiredAt("after_scope"));
-  EXPECT_THAT(LoansTo({"s"}), AreExpiredAt("after_goto"));
-}
-
-TEST_F(LifetimeAnalysisTest, ContinueInLoop) {
-  SetupTest(R"(
-    void target(int count) {
-      MyObj *p = nullptr;
-      MyObj outer;
-      p = &outer;
-      POINT(before_loop);
-
-      for (int i = 0; i < count; ++i) {
-        if (i % 2 == 0) {
-          MyObj s_even;
-          p = &s_even;
-          POINT(in_even_iter);
-          continue;
-        }
-        MyObj s_odd;
-        p = &s_odd;
-        POINT(in_odd_iter);
-      }
-      POINT(after_loop);
-    }
-  )");
-  EXPECT_THAT(NoLoans(), AreExpiredAt("before_loop"));
-  EXPECT_THAT(LoansTo({"s_odd"}), AreExpiredAt("in_even_iter"));
-  EXPECT_THAT(LoansTo({"s_even"}), AreExpiredAt("in_odd_iter"));
-  EXPECT_THAT(LoansTo({"s_even", "s_odd"}), AreExpiredAt("after_loop"));
-}
-
-TEST_F(LifetimeAnalysisTest, ReassignedPointerThenOriginalExpires) {
-  SetupTest(R"(
-    void target() {
-      MyObj* p = nullptr;
-      {
-        MyObj s1;
-        p = &s1;
-        POINT(p_has_s1);
-        {
-          MyObj s2;
-          p = &s2;
-          POINT(p_has_s2);
-        }
-        POINT(p_after_s2_expires);
-      } // s1 expires here.
-      POINT(p_after_s1_expires);
-    }
-  )");
-  EXPECT_THAT(NoLoans(), AreExpiredAt("p_has_s1"));
-  EXPECT_THAT(NoLoans(), AreExpiredAt("p_has_s2"));
-  EXPECT_THAT(LoansTo({"s2"}), AreExpiredAt("p_after_s2_expires"));
-  EXPECT_THAT(LoansTo({"s1", "s2"}), AreExpiredAt("p_after_s1_expires"));
-}
-
 TEST_F(LifetimeAnalysisTest, NoDuplicateLoansForImplicitCastToConst) {
   SetupTest(R"(
     void target() {
@@ -880,23 +737,6 @@ TEST_F(LifetimeAnalysisTest, GslPointerPropagation) {
   EXPECT_THAT(Origin("z"), HasLoansTo({"a"}, "p3"));
 }
 
-TEST_F(LifetimeAnalysisTest, GslPointerLoanExpiration) {
-  SetupTest(R"(
-    void target() {
-      View x;
-      {
-        MyObj a;
-        x = a;
-        POINT(before_expiry);
-      } // `a` is destroyed here.
-      POINT(after_expiry);
-    }
-  )");
-
-  EXPECT_THAT(NoLoans(), AreExpiredAt("before_expiry"));
-  EXPECT_THAT(LoansTo({"a"}), AreExpiredAt("after_expiry"));
-}
-
 TEST_F(LifetimeAnalysisTest, GslPointerReassignment) {
   SetupTest(R"(
     void target() {
@@ -916,7 +756,6 @@ TEST_F(LifetimeAnalysisTest, GslPointerReassignment) {
   EXPECT_THAT(Origin("v"), HasLoansTo({"safe"}, "p1"));
   EXPECT_THAT(Origin("v"), HasLoansTo({"unsafe"}, "p2"));
   EXPECT_THAT(Origin("v"), HasLoansTo({"unsafe"}, "p3"));
-  EXPECT_THAT(LoansTo({"unsafe"}), AreExpiredAt("p3"));
 }
 
 TEST_F(LifetimeAnalysisTest, GslPointerConversionOperator) {
@@ -1174,5 +1013,187 @@ TEST_F(LifetimeAnalysisTest, 
LifetimeboundConversionOperator) {
   )");
   EXPECT_THAT(Origin("v"), HasLoansTo({"owner"}, "p1"));
 }
+
+TEST_F(LifetimeAnalysisTest, LivenessDeadPointer) {
+  SetupTest(R"(
+    void target() {
+      POINT(p2);
+      MyObj s;
+      MyObj* p = &s;
+      POINT(p1);
+    }
+  )");
+  EXPECT_THAT(NoOrigins(), AreLiveAt("p1"));
+  EXPECT_THAT(NoOrigins(), AreLiveAt("p2"));
+}
+
+TEST_F(LifetimeAnalysisTest, LivenessSimpleReturn) {
+  SetupTest(R"(
+    MyObj* target() {
+      MyObj s;
+      MyObj* p = &s;
+      POINT(p1);
+      return p;
+    }
+  )");
+  EXPECT_THAT(Origins({"p"}), AreDefinitelyLiveAt("p1"));
+}
+
+TEST_F(LifetimeAnalysisTest, LivenessKilledByReassignment) {
+  SetupTest(R"(
+    MyObj* target() {
+      MyObj s1, s2;
+      MyObj* p = &s1;
+      POINT(p1);
+      p = &s2;
+      POINT(p2);
+      return p;
+    }
+  )");
+  EXPECT_THAT(Origins({"p"}), AreDefinitelyLiveAt("p2"));
+  EXPECT_THAT(NoOrigins(), AreLiveAt("p1"));
+}
+
+TEST_F(LifetimeAnalysisTest, LivenessAcrossBranches) {
+  SetupTest(R"(
+    MyObj* target(bool c) {
+      MyObj x, y;
+      MyObj* p = nullptr;
+      POINT(p1);
+      if (c) {
+        p = &x;
+        POINT(p2);
+      } else {
+        p = &y;
+        POINT(p3);
+      }
+      return p;
+    }
+  )");
+  EXPECT_THAT(Origins({"p"}), AreDefinitelyLiveAt("p2"));
+  EXPECT_THAT(Origins({"p"}), AreDefinitelyLiveAt("p3"));
+  // Before the `if`, the value of `p` (`nullptr`) is always overwritten 
before.
+  EXPECT_THAT(NoOrigins(), AreLiveAt("p1"));
+}
+
+TEST_F(LifetimeAnalysisTest, LivenessInLoop) {
+  SetupTest(R"(
+    MyObj* target(bool c) {
+      MyObj s1, s2;
+      MyObj* p = &s1;
+      MyObj* q = &s2;
+      POINT(p1);
+      while(c) {
+        POINT(p2);
+
+        p = q;
+        POINT(p3);
+      }
+      POINT(p4);
+      return p;
+    }
+  )");
+
+  EXPECT_THAT(Origins({"p"}), AreDefinitelyLiveAt("p4"));
+  EXPECT_THAT(NoOrigins(), AreMaybeLiveAt("p4"));
+
+  EXPECT_THAT(Origins({"p", "q"}), AreMaybeLiveAt("p3"));
+
+  EXPECT_THAT(Origins({"q"}), AreDefinitelyLiveAt("p2"));
+  EXPECT_THAT(NoOrigins(), AreMaybeLiveAt("p2"));
+
+  EXPECT_THAT(Origins({"p", "q"}), AreMaybeLiveAt("p1"));
+}
+
+TEST_F(LifetimeAnalysisTest, LivenessInLoopAndIf) {
+  // See https://github.com/llvm/llvm-project/issues/156959.
+  SetupTest(R"(
+    void target(bool cond) {
+      MyObj b;
+      while (cond) {
+        POINT(p1);
+
+        MyObj a;
+        View p = b;
+
+        POINT(p2);
+
+        if (cond) {
+          POINT(p3);
+          p = a;
+        }
+        POINT(p4);
+        (void)p;
+        POINT(p5);
+      }
+    }
+  )");
+  EXPECT_THAT(NoOrigins(), AreLiveAt("p5"));
+  EXPECT_THAT(Origins({"p"}), AreDefinitelyLiveAt("p4"));
+  EXPECT_THAT(NoOrigins(), AreLiveAt("p3"));
+  EXPECT_THAT(Origins({"p"}), AreMaybeLiveAt("p2"));
+  EXPECT_THAT(NoOrigins(), AreLiveAt("p1"));
+}
+
+TEST_F(LifetimeAnalysisTest, LivenessInLoopAndIf2) {
+  SetupTest(R"(
+    void target(MyObj safe, bool condition) {
+      MyObj* p = &safe;
+      MyObj* q = &safe;
+      POINT(p6);
+
+      while (condition) {
+        POINT(p5);
+        MyObj x;
+        p = &x;
+
+        POINT(p4);
+
+        if (condition) {
+          q = p;
+          POINT(p3);
+        }
+        
+        POINT(p2);
+        (void)*p;
+        (void)*q;
+        POINT(p1);
+      }
+    }
+  )");
+  EXPECT_THAT(Origins({"q"}), AreMaybeLiveAt("p1"));
+  EXPECT_THAT(NoOrigins(), AreDefinitelyLiveAt("p1"));
+
+  EXPECT_THAT(Origins({"p", "q"}), AreDefinitelyLiveAt("p2"));
+
+  EXPECT_THAT(Origins({"p", "q"}), AreDefinitelyLiveAt("p3"));
+
+  EXPECT_THAT(Origins({"p"}), AreDefinitelyLiveAt("p4"));
+  EXPECT_THAT(Origins({"q"}), AreMaybeLiveAt("p4"));
+
+  EXPECT_THAT(Origins({"q"}), AreMaybeLiveAt("p5"));
+  EXPECT_THAT(NoOrigins(), AreDefinitelyLiveAt("p5"));
+
+  EXPECT_THAT(Origins({"q"}), AreMaybeLiveAt("p6"));
+  EXPECT_THAT(NoOrigins(), AreDefinitelyLiveAt("p6"));
+}
+
+TEST_F(LifetimeAnalysisTest, LivenessOutsideLoop) {
+  SetupTest(R"(
+    void target(MyObj safe) {
+      MyObj* p = &safe;
+      for (int i = 0; i < 1; ++i) {
+        MyObj s;
+        p = &s;
+        POINT(p2);
+      }
+      POINT(p1);
+      (void)*p;
+    }
+  )");
+  EXPECT_THAT(Origins({"p"}), AreDefinitelyLiveAt("p1"));
+  EXPECT_THAT(Origins({"p"}), AreMaybeLiveAt("p2"));
+}
+
 } // anonymous namespace
 } // namespace clang::lifetimes::internal

_______________________________________________
llvm-branch-commits mailing list
llvm-branch-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits

Reply via email to