https://github.com/aeft updated https://github.com/llvm/llvm-project/pull/184295

>From fe7ea93422db01f15d310941cc2e859f1748562f Mon Sep 17 00:00:00 2001
From: Zhijie Wang <[email protected]>
Date: Mon, 30 Mar 2026 09:54:06 -0700
Subject: [PATCH] [LifetimeSafety] Detect use of a reference type as a use of
 underlying origin

---
 .../Analysis/Analyses/LifetimeSafety/Facts.h  | 24 ++++++++++++++++---
 clang/lib/Analysis/LifetimeSafety/Facts.cpp   | 14 +++++------
 .../LifetimeSafety/FactsGenerator.cpp         | 23 ++++++++++++++++--
 .../Analysis/LifetimeSafety/LiveOrigins.cpp   |  6 ++---
 .../LifetimeSafety/LoanPropagation.cpp        |  5 ++--
 .../warn-lifetime-safety-invalidations.cpp    | 15 ++++++++----
 clang/test/Sema/warn-lifetime-safety.cpp      | 12 ++++++++++
 7 files changed, 75 insertions(+), 24 deletions(-)

diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
index 0f848abd913d3..a986f37e67c05 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
@@ -234,7 +234,7 @@ class GlobalEscapeFact : public OriginEscapesFact {
 
 class UseFact : public Fact {
   const Expr *UseExpr;
-  const OriginList *OList;
+  std::variant<const OriginList *, OriginID> Origins;
   // True if this use is a write operation (e.g., left-hand side of 
assignment).
   // Write operations are exempted from use-after-free checks.
   bool IsWritten = false;
@@ -243,13 +243,31 @@ class UseFact : public Fact {
   static bool classof(const Fact *F) { return F->getKind() == Kind::Use; }
 
   UseFact(const Expr *UseExpr, const OriginList *OList)
-      : Fact(Kind::Use), UseExpr(UseExpr), OList(OList) {}
+      : Fact(Kind::Use), UseExpr(UseExpr), Origins(OList) {}
+  UseFact(const Expr *UseExpr, OriginID OID)
+      : Fact(Kind::Use), UseExpr(UseExpr), Origins(OID) {}
 
-  const OriginList *getUsedOrigins() const { return OList; }
   const Expr *getUseExpr() const { return UseExpr; }
   void markAsWritten() { IsWritten = true; }
   bool isWritten() const { return IsWritten; }
 
+  void setOrigins(OriginID OID) { Origins = OID; }
+
+  const OriginList *getOriginList() const {
+    if (auto *OList = std::get_if<const OriginList *>(&Origins))
+      return *OList;
+    return nullptr;
+  }
+
+  template <typename Fn> void forEachOrigin(Fn &&F) const {
+    if (const OriginList *OList = getOriginList()) {
+      for (auto *Cur = OList; Cur; Cur = Cur->peelOuterOrigin())
+        F(Cur->getOuterOriginID());
+    } else {
+      F(std::get<OriginID>(Origins));
+    }
+  }
+
   void dump(llvm::raw_ostream &OS, const LoanManager &,
             const OriginManager &OM) const override;
 };
diff --git a/clang/lib/Analysis/LifetimeSafety/Facts.cpp 
b/clang/lib/Analysis/LifetimeSafety/Facts.cpp
index 1bc0521a72359..d1dd396bdbe10 100644
--- a/clang/lib/Analysis/LifetimeSafety/Facts.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Facts.cpp
@@ -80,14 +80,14 @@ void GlobalEscapeFact::dump(llvm::raw_ostream &OS, const 
LoanManager &,
 void UseFact::dump(llvm::raw_ostream &OS, const LoanManager &,
                    const OriginManager &OM) const {
   OS << "Use (";
-  size_t NumUsedOrigins = getUsedOrigins()->getLength();
-  size_t I = 0;
-  for (const OriginList *Cur = getUsedOrigins(); Cur;
-       Cur = Cur->peelOuterOrigin(), ++I) {
-    OM.dump(Cur->getOuterOriginID(), OS);
-    if (I < NumUsedOrigins - 1)
+  bool First = true;
+  forEachOrigin([&](OriginID OID) {
+    if (!First)
       OS << ", ";
-  }
+    else
+      First = false;
+    OM.dump(OID, OS);
+  });
   OS << ", " << (isWritten() ? "Write" : "Read") << ")\n";
 }
 
diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp 
b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
index 80a73a2bf687e..bf3589314d7f7 100644
--- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -366,8 +366,27 @@ void FactsGenerator::handleAssignment(const Expr *LHSExpr,
   // assigned.
   RHSList = getRValueOrigins(RHSExpr, RHSList);
 
-  if (const auto *DRE_LHS = dyn_cast<DeclRefExpr>(LHSExpr))
-    markUseAsWrite(DRE_LHS);
+  if (const auto *DRE_LHS = dyn_cast<DeclRefExpr>(LHSExpr)) {
+    QualType QT = DRE_LHS->getDecl()->getType();
+    if (QT->isReferenceType() && hasOrigins(QT->getPointeeType())) {
+      // Writing through a reference uses the binding but overwrites the
+      // pointee. Model this as a Read of the outer origin (keeping the binding
+      // live) and a Write of the inner origins (killing the pointee's
+      // liveness).
+      if (UseFact *UF = UseFacts.lookup(DRE_LHS)) {
+        const OriginList *FullList = UF->getOriginList();
+        assert(FullList);
+        UF->setOrigins(FullList->getOuterOriginID());
+        if (const OriginList *InnerList = FullList->peelOuterOrigin()) {
+          UseFact *WriteUF = FactMgr.createFact<UseFact>(DRE_LHS, InnerList);
+          WriteUF->markAsWritten();
+          CurrentBlockFacts.push_back(WriteUF);
+        }
+      }
+    } else if (!QT->isReferenceType()) {
+      markUseAsWrite(DRE_LHS);
+    }
+  }
   // Kill the old loans of the destination origin and flow the new loans
   // from the source origin.
   flow(LHSList->peelOuterOrigin(), RHSList, /*Kill=*/true);
diff --git a/clang/lib/Analysis/LifetimeSafety/LiveOrigins.cpp 
b/clang/lib/Analysis/LifetimeSafety/LiveOrigins.cpp
index bc7494360624e..de4d7fa833ddf 100644
--- a/clang/lib/Analysis/LifetimeSafety/LiveOrigins.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LiveOrigins.cpp
@@ -129,9 +129,7 @@ class AnalysisImpl
   /// the origin since it overwrites the value.
   Lattice transfer(Lattice In, const UseFact &UF) {
     Lattice Out = In;
-    for (const OriginList *Cur = UF.getUsedOrigins(); Cur;
-         Cur = Cur->peelOuterOrigin()) {
-      OriginID OID = Cur->getOuterOriginID();
+    UF.forEachOrigin([&](OriginID OID) {
       // Write kills liveness.
       if (UF.isWritten()) {
         Out = Lattice(Factory.remove(Out.LiveOrigins, OID));
@@ -141,7 +139,7 @@ class AnalysisImpl
         Out = Lattice(Factory.add(Out.LiveOrigins, OID,
                                   LivenessInfo(&UF, LivenessKind::Must)));
       }
-    }
+    });
     return Out;
   }
 
diff --git a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp 
b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
index e437fb7d41268..8960479edb17b 100644
--- a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
@@ -59,9 +59,8 @@ static llvm::BitVector computePersistentOrigins(const 
FactManager &FactMgr,
         break;
       }
       case Fact::Kind::Use:
-        for (const OriginList *Cur = F->getAs<UseFact>()->getUsedOrigins(); 
Cur;
-             Cur = Cur->peelOuterOrigin())
-          CheckOrigin(Cur->getOuterOriginID());
+        F->getAs<UseFact>()->forEachOrigin(
+            [&](OriginID OID) { CheckOrigin(OID); });
         break;
       case Fact::Kind::MovedOrigin:
       case Fact::Kind::OriginEscapes:
diff --git a/clang/test/Sema/warn-lifetime-safety-invalidations.cpp 
b/clang/test/Sema/warn-lifetime-safety-invalidations.cpp
index 486edd7a1a023..c7f920c03736a 100644
--- a/clang/test/Sema/warn-lifetime-safety-invalidations.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-invalidations.cpp
@@ -259,14 +259,19 @@ namespace ElementReferences {
 
 void ReferenceToVectorElement() {
   std::vector<int> v = {1, 2, 3};
-  int& ref = v[0];
-  v.push_back(4);
-  // FIXME: Detect this as a use of 'ref'.
-  // https://github.com/llvm/llvm-project/issues/180187
-  ref = 10;
+  int& ref = v[0]; // expected-warning {{object whose reference is captured is 
later invalidated}}
+  v.push_back(4);  // expected-note {{invalidated here}}
+  ref = 10;        // expected-note {{later used here}}
   (void)ref;
 }
 
+void PointerRefToVectorElement() {
+  std::vector<int*> v = {nullptr, nullptr};
+  int*& ref = v[0];     // expected-warning {{object whose reference is 
captured is later invalidated}}
+  v.push_back(nullptr); // expected-note {{invalidated here}}
+  ref = nullptr;        // expected-note {{later used here}}
+}
+
 void PointerToVectorElement() {
   std::vector<int> v = {1, 2, 3};
   int* ptr = &v[0];  // expected-warning {{object whose reference is captured 
is later invalidated}}
diff --git a/clang/test/Sema/warn-lifetime-safety.cpp 
b/clang/test/Sema/warn-lifetime-safety.cpp
index 76d43445f8636..866542303414a 100644
--- a/clang/test/Sema/warn-lifetime-safety.cpp
+++ b/clang/test/Sema/warn-lifetime-safety.cpp
@@ -752,6 +752,18 @@ void no_error_if_dangle_then_rescue_gsl() {
   v.use();     // This is safe.
 }
 
+void no_error_if_dangle_then_rescue_via_ref() {
+  MyObj safe;
+  MyObj* p;
+  MyObj*& ref = p;
+  {
+    MyObj temp;
+    ref = &temp;  // p temporarily points to temp via ref.
+  }
+  ref = &safe;    // p is "rescued" via ref before use.
+  (void)*ref;     // This is safe.
+}
+
 void no_error_loan_from_current_iteration(bool cond) {
   // See https://github.com/llvm/llvm-project/issues/156959.
   MyObj b;

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

Reply via email to