https://github.com/aeft updated https://github.com/llvm/llvm-project/pull/184295
>From c92d6cf5aecd8ddfa243b40326ce52b9ac0e747e Mon Sep 17 00:00:00 2001 From: Zhijie Wang <[email protected]> Date: Sun, 29 Mar 2026 00:02:05 -0700 Subject: [PATCH 1/2] [LifetimeSafety] Detect use of a reference type as a use of underlying origin --- .../Analysis/Analyses/LifetimeSafety/Facts.h | 3 +++ .../Analyses/LifetimeSafety/FactsGenerator.h | 2 +- .../Analysis/LifetimeSafety/FactsGenerator.cpp | 16 ++++++++++++---- .../Analysis/LifetimeSafety/LiveOrigins.cpp | 18 +++++++++++++++--- .../warn-lifetime-safety-invalidations.cpp | 15 ++++++++++----- clang/test/Sema/warn-lifetime-safety.cpp | 12 ++++++++++++ 6 files changed, 53 insertions(+), 13 deletions(-) diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h index 0f848abd913d3..7d95d9a01b1db 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h @@ -238,6 +238,7 @@ class UseFact : public Fact { // 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; + bool IsReferenceWrite = false; public: static bool classof(const Fact *F) { return F->getKind() == Kind::Use; } @@ -248,7 +249,9 @@ class UseFact : public Fact { const OriginList *getUsedOrigins() const { return OList; } const Expr *getUseExpr() const { return UseExpr; } void markAsWritten() { IsWritten = true; } + void markAsReferenceWrite() { IsReferenceWrite = true; } bool isWritten() const { return IsWritten; } + bool isReferenceWrite() const { return IsReferenceWrite; } void dump(llvm::raw_ostream &OS, const LoanManager &, const OriginManager &OM) const override; diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h index dfcbdc7d73007..60b79168ca9b5 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h @@ -111,7 +111,7 @@ class FactsGenerator : public ConstStmtVisitor<FactsGenerator> { // (e.g. on the left-hand side of an assignment in the case of a DeclRefExpr). void handleUse(const Expr *E); - void markUseAsWrite(const DeclRefExpr *DRE); + void markUseAsWrite(const DeclRefExpr *DRE, bool IsReferenceWrite); bool escapesViaReturn(OriginID OID) const; diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp index 80a73a2bf687e..16f435c3a4301 100644 --- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp +++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp @@ -366,8 +366,12 @@ 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(); + bool IsRef = QT->isReferenceType(); + if (!IsRef || hasOrigins(QT->getPointeeType())) + markUseAsWrite(DRE_LHS, IsRef); + } // Kill the old loans of the destination origin and flow the new loans // from the source origin. flow(LHSList->peelOuterOrigin(), RHSList, /*Kill=*/true); @@ -753,9 +757,13 @@ void FactsGenerator::handleUse(const Expr *E) { } } -void FactsGenerator::markUseAsWrite(const DeclRefExpr *DRE) { - if (UseFacts.contains(DRE)) +void FactsGenerator::markUseAsWrite(const DeclRefExpr *DRE, + bool IsReferenceWrite) { + if (UseFacts.contains(DRE)) { UseFacts[DRE]->markAsWritten(); + if (IsReferenceWrite) + UseFacts[DRE]->markAsReferenceWrite(); + } } // Creates an IssueFact for a new placeholder loan for each pointer or reference diff --git a/clang/lib/Analysis/LifetimeSafety/LiveOrigins.cpp b/clang/lib/Analysis/LifetimeSafety/LiveOrigins.cpp index bc7494360624e..e6bbcb616e193 100644 --- a/clang/lib/Analysis/LifetimeSafety/LiveOrigins.cpp +++ b/clang/lib/Analysis/LifetimeSafety/LiveOrigins.cpp @@ -126,11 +126,23 @@ class AnalysisImpl /// A read operation makes the origin live with definite confidence, as it /// dominates this program point. A write operation kills the liveness of - /// the origin since it overwrites the value. + /// the origin since it overwrites the value. For reference writes, the outer + /// origin (reference binding) is kept live and only inner origins are killed. Lattice transfer(Lattice In, const UseFact &UF) { Lattice Out = In; - for (const OriginList *Cur = UF.getUsedOrigins(); Cur; - Cur = Cur->peelOuterOrigin()) { + const OriginList *Cur = UF.getUsedOrigins(); + + // Writing through a reference (e.g., `int*& ref; ref = &y;`) is a use of + // the reference itself. Keep the outer origin (reference binding) live and + // kill only inner origins. + if (UF.isReferenceWrite()) { + assert(Cur); + Out = Lattice(Factory.add(Out.LiveOrigins, Cur->getOuterOriginID(), + LivenessInfo(&UF, LivenessKind::Must))); + Cur = Cur->peelOuterOrigin(); + } + + for (; Cur; Cur = Cur->peelOuterOrigin()) { OriginID OID = Cur->getOuterOriginID(); // Write kills liveness. if (UF.isWritten()) { 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; >From 6306425bc58fcb2f38043d48c2022fc88f241706 Mon Sep 17 00:00:00 2001 From: Zhijie Wang <[email protected]> Date: Sun, 29 Mar 2026 09:17:52 -0700 Subject: [PATCH 2/2] avoid multiple lookup --- clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp index 16f435c3a4301..8337d5376666c 100644 --- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp +++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp @@ -759,10 +759,10 @@ void FactsGenerator::handleUse(const Expr *E) { void FactsGenerator::markUseAsWrite(const DeclRefExpr *DRE, bool IsReferenceWrite) { - if (UseFacts.contains(DRE)) { - UseFacts[DRE]->markAsWritten(); + if (UseFact *UF = UseFacts.lookup(DRE)) { + UF->markAsWritten(); if (IsReferenceWrite) - UseFacts[DRE]->markAsReferenceWrite(); + UF->markAsReferenceWrite(); } } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
