[clang] [LifetimeSafety] Add multi-block support to buildOriginFlowChain (PR #204592)
Yuan Suo via cfe-commits
cfe-commits at lists.llvm.org
Fri Jun 26 05:38:58 PDT 2026
https://github.com/suoyuan666 updated https://github.com/llvm/llvm-project/pull/204592
>From c5cea83c1b9295c1883d6889c7cdf31e0e4954f8 Mon Sep 17 00:00:00 2001
From: Yuan Suo <suoyuan666 at s5n.xyz>
Date: Wed, 17 Jun 2026 18:04:06 +0800
Subject: [PATCH 01/21] [LifetimeSafety] Add multi-block support to
buildOriginFlowChain
After introducing `buildOriginFlowChain` to use-after-scope diagnostics,
it should support multi-block analysis. This also allows it to be reused
by other diagnostics.
In some loops, UseFact may appear before OriginFlowFact:
```cpp
void for_loop_use_before_loop_body(MyObj safe) {
MyObj* p = &safe;
for (int i = 0; i < 1; ++i) {
(void)*p;
MyObj s;
p = &s;
}
(void)*p;
}
```
So, I remove the `StartPoint` parameter to correctly find the `OriginID`.
Signed-off-by: Yuan Suo <suoyuan666 at s5n.xyz>
---
.../Analysis/Analyses/LifetimeSafety/Facts.h | 1 +
.../Analyses/LifetimeSafety/LoanPropagation.h | 7 +-
clang/lib/Analysis/LifetimeSafety/Checker.cpp | 6 +-
clang/lib/Analysis/LifetimeSafety/Facts.cpp | 15 ++-
.../LifetimeSafety/LoanPropagation.cpp | 99 ++++++++++++++-----
clang/lib/Sema/SemaLifetimeSafety.h | 4 +-
clang/test/Sema/LifetimeSafety/safety-c.c | 4 +-
clang/test/Sema/LifetimeSafety/safety.cpp | 24 +++--
.../unittests/Analysis/LifetimeSafetyTest.cpp | 3 +-
9 files changed, 119 insertions(+), 44 deletions(-)
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
index 88b509e1b94df..e0ddfe95b00b2 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
@@ -374,6 +374,7 @@ class FactManager {
/// Retrieves all the facts in the block containing Program Point P.
/// \note This is intended for testing only.
llvm::ArrayRef<const Fact *> getBlockContaining(ProgramPoint P) const;
+ std::optional<size_t> getBlockID(ProgramPoint P) const;
unsigned getNumFacts() const { return NextFactID.Value; }
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
index 724c6eee7d3c2..42d7df0838f35 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
@@ -16,6 +16,7 @@
#define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_LOAN_PROPAGATION_H
#include "clang/Analysis/Analyses/LifetimeSafety/Facts.h"
+#include "clang/Analysis/Analyses/PostOrderCFGView.h"
#include "clang/Analysis/AnalysisDeclContext.h"
#include "clang/Analysis/CFG.h"
#include "llvm/ADT/ImmutableMap.h"
@@ -46,10 +47,12 @@ class LoanPropagationAnalysis {
/// where the loan was originally issued.
llvm::SmallVector<OriginID>
buildOriginFlowChain(ProgramPoint StartPoint, const OriginID StartOID,
- const LoanID TargetLoan) const;
+ const LoanID TargetLoan,
+ const PostOrderCFGView *POV) const;
llvm::SmallVector<OriginID>
- buildOriginFlowChain(const UseFact *UF, const LoanID TargetLoan) const;
+ buildOriginFlowChain(const UseFact *UF, const LoanID TargetLoan,
+ const PostOrderCFGView *POV) const;
private:
class Impl;
diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
index 746ebbfb15c39..66a52ba1303c3 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -69,6 +69,7 @@ class LifetimeChecker {
ASTContext &AST;
const Decl *FD;
const LifetimeSafetyOpts &LSOpts;
+ const PostOrderCFGView *POV;
static SourceLocation
GetFactLoc(llvm::PointerUnion<const UseFact *, const OriginEscapesFact *> F) {
@@ -92,7 +93,8 @@ class LifetimeChecker {
const LifetimeSafetyOpts &LSOpts)
: LoanPropagation(LoanPropagation), MovedLoans(MovedLoans),
LiveOrigins(LiveOrigins), FactMgr(FM), SemaHelper(SemaHelper),
- AST(ADC.getASTContext()), FD(ADC.getDecl()), LSOpts(LSOpts) {
+ AST(ADC.getASTContext()), FD(ADC.getDecl()), LSOpts(LSOpts),
+ POV(ADC.getAnalysis<PostOrderCFGView>()) {
for (const CFGBlock *B : *ADC.getAnalysis<PostOrderCFGView>())
for (const Fact *F : FactMgr.getFacts(B))
if (const auto *EF = F->getAs<ExpireFact>())
@@ -272,7 +274,7 @@ class LifetimeChecker {
// Scope-based expiry (use-after-scope).
SemaHelper->reportUseAfterScope(
IssueExpr, UF->getUseExpr(), MovedExpr, ExpiryLoc,
- getExprChain(LoanPropagation.buildOriginFlowChain(UF, LID)));
+ getExprChain(LoanPropagation.buildOriginFlowChain(UF, LID, POV)));
} else if (const auto *OEF =
CausingFact.dyn_cast<const OriginEscapesFact *>()) {
diff --git a/clang/lib/Analysis/LifetimeSafety/Facts.cpp b/clang/lib/Analysis/LifetimeSafety/Facts.cpp
index 3d7fbcdacc830..823f1f686f904 100644
--- a/clang/lib/Analysis/LifetimeSafety/Facts.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Facts.cpp
@@ -145,12 +145,17 @@ void FactManager::dump(const CFG &Cfg, AnalysisDeclContext &AC) const {
llvm::ArrayRef<const Fact *>
FactManager::getBlockContaining(ProgramPoint P) const {
- for (const auto &BlockToFactsVec : BlockToFacts) {
- for (const Fact *F : BlockToFactsVec)
- if (F == P)
- return BlockToFactsVec;
- }
+ std::optional<size_t> BlockIndex = getBlockID(P);
+ if (BlockIndex)
+ return BlockToFacts[BlockIndex.value()];
return {};
}
+std::optional<size_t> FactManager::getBlockID(ProgramPoint P) const {
+ for (size_t i = 0; i < BlockToFacts.size(); ++i)
+ for (const Fact *F : BlockToFacts[i])
+ if (F == P)
+ return i;
+ return std::nullopt;
+}
} // namespace clang::lifetimes::internal
diff --git a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
index a67b1b3c0f826..8e2d9a6577427 100644
--- a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
@@ -130,6 +130,11 @@ struct Lattice {
}
};
+struct BuildOriginFlowChainResult {
+ const llvm::SmallVector<OriginID> OriginFlowChain;
+ const bool Complete;
+};
+
class AnalysisImpl
: public DataflowAnalysis<AnalysisImpl, Lattice, Direction::Forward> {
public:
@@ -204,22 +209,72 @@ class AnalysisImpl
llvm::SmallVector<OriginID>
buildOriginFlowChain(ProgramPoint StartPoint, const OriginID StartOID,
- const LoanID TargetLoan) const {
+ const LoanID TargetLoan,
+ const PostOrderCFGView *POV) const {
assert(getLoans(StartOID, StartPoint).contains(TargetLoan) &&
"TargetLoan must be present in the StartOID at the StartPoint");
+ std::optional<OriginID> FinalOID;
+ llvm::DenseMap<OriginID, OriginID> VistedOriginIDs;
+
+ const auto OriginFlowChainFilter = [&VistedOriginIDs](OriginID FinalOID) {
+ llvm::SmallVector<OriginID> OriginFlowChain;
+ while (true) {
+ OriginFlowChain.push_back(FinalOID);
+ const auto NextOriginID = VistedOriginIDs.find(FinalOID);
+ if (NextOriginID == VistedOriginIDs.end())
+ break;
+ FinalOID = NextOriginID->second;
+ }
+ return OriginFlowChain;
+ };
+
+ const auto InsertVistedOriginIDs =
+ [&VistedOriginIDs, &FinalOID](llvm::ArrayRef<OriginID> OriginFlowChain,
+ OriginID &StartOID) {
+ if (!VistedOriginIDs.empty())
+ VistedOriginIDs.insert({OriginFlowChain[0], StartOID});
+
+ for (size_t i = 0; i < OriginFlowChain.size() - 1; ++i)
+ VistedOriginIDs.insert(
+ {OriginFlowChain[i + 1], OriginFlowChain[i]});
+
+ StartOID = OriginFlowChain.back();
+ FinalOID = StartOID;
+ };
+
+ std::optional<size_t> BlockID = FactMgr.getBlockID(StartPoint);
+ assert(BlockID.has_value());
+ const auto StartIt = llvm::find_if(*POV, [&BlockID](const CFGBlock *Block) {
+ return Block->getBlockID() == BlockID;
+ });
+
+ OriginID CurrOID = StartOID;
+ for (const CFGBlock *B :
+ llvm::reverse(llvm::make_range(POV->begin(), StartIt + 1))) {
+ BuildOriginFlowChainResult BuildResult =
+ buildOriginFlowChain(B, CurrOID, TargetLoan);
+ if (!BuildResult.OriginFlowChain.empty())
+ InsertVistedOriginIDs(BuildResult.OriginFlowChain, CurrOID);
+ if (BuildResult.Complete)
+ return OriginFlowChainFilter(*FinalOID);
+ }
+
+ llvm_unreachable(
+ "buildOriginFlowChain should return at BuildResult.Complete");
+ }
+
+ BuildOriginFlowChainResult
+ buildOriginFlowChain(const CFGBlock *Block, const OriginID StartOID,
+ const LoanID TargetLoan) const {
OriginID CurrOID = StartOID;
llvm::SmallVector<OriginID> OriginFlowChain;
- llvm::ArrayRef<const Fact *> Facts = FactMgr.getBlockContaining(StartPoint);
- const auto *StartIt = llvm::find(Facts, StartPoint);
- assert(StartIt != Facts.end());
- for (const Fact *F :
- llvm::reverse(llvm::make_range(Facts.begin(), StartIt))) {
+ for (const Fact *F : llvm::reverse(FactMgr.getFacts(Block))) {
if (const auto *IF = F->getAs<IssueFact>())
if (IF->getLoanID() == TargetLoan) {
assert(IF->getOriginID() == CurrOID);
- return OriginFlowChain;
+ return {OriginFlowChain, true};
}
const auto *OFF = F->getAs<OriginFlowFact>();
@@ -235,20 +290,17 @@ class AnalysisImpl
CurrOID = SrcOriginID;
}
- // FIXME: Ideally, this return is unreachable and should be an assert
- // because we expect to always finish at an IssueFact. But since current
- // traversal is limited to a single CFG block, multi-block OriginFlowChain
- // construction might miss the IssueFact. We should add llvm_unreachable
- // here once multi-block support is implemented.
- return {};
+ return {OriginFlowChain, false};
}
llvm::SmallVector<OriginID>
- buildOriginFlowChain(const UseFact *UF, const LoanID TargetLoan) const {
+ buildOriginFlowChain(const UseFact *UF, const LoanID TargetLoan,
+ const PostOrderCFGView *POV) const {
for (const OriginList *Cur = UF->getUsedOrigins(); Cur;
Cur = Cur->peelOuterOrigin())
if (getLoans(Cur->getOuterOriginID(), UF).contains(TargetLoan))
- return buildOriginFlowChain(UF, Cur->getOuterOriginID(), TargetLoan);
+ return buildOriginFlowChain(UF, Cur->getOuterOriginID(), TargetLoan,
+ POV);
return {};
}
@@ -303,16 +355,15 @@ LoanSet LoanPropagationAnalysis::getLoans(OriginID OID, ProgramPoint P) const {
return PImpl->getLoans(OID, P);
}
-llvm::SmallVector<OriginID>
-LoanPropagationAnalysis::buildOriginFlowChain(ProgramPoint StartPoint,
- const OriginID StartOID,
- const LoanID TargetLoan) const {
- return PImpl->buildOriginFlowChain(StartPoint, StartOID, TargetLoan);
+llvm::SmallVector<OriginID> LoanPropagationAnalysis::buildOriginFlowChain(
+ ProgramPoint StartPoint, const OriginID StartOID, const LoanID TargetLoan,
+ const PostOrderCFGView *POV) const {
+ return PImpl->buildOriginFlowChain(StartPoint, StartOID, TargetLoan, POV);
}
-llvm::SmallVector<OriginID>
-LoanPropagationAnalysis::buildOriginFlowChain(const UseFact *UF,
- const LoanID TargetLoan) const {
- return PImpl->buildOriginFlowChain(UF, TargetLoan);
+llvm::SmallVector<OriginID> LoanPropagationAnalysis::buildOriginFlowChain(
+ const UseFact *UF, const LoanID TargetLoan,
+ const PostOrderCFGView *POV) const {
+ return PImpl->buildOriginFlowChain(UF, TargetLoan, POV);
}
} // namespace clang::lifetimes::internal
diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h
index a3d546792fe41..026bfa90719bc 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -659,10 +659,10 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
if (OriginExprChain.empty())
return;
- const Expr *LastExpr = OriginExprChain.back();
+ const Expr *LastExpr = OriginExprChain.front();
std::string IssueStr = getDiagSubjectDescription(LastExpr);
- for (const Expr *CurrExpr : reverse(OriginExprChain.drop_back())) {
+ for (const Expr *CurrExpr : OriginExprChain.drop_front()) {
if (!shouldShowInAliasChain(CurrExpr, LastExpr))
continue;
S.Diag(CurrExpr->getBeginLoc(),
diff --git a/clang/test/Sema/LifetimeSafety/safety-c.c b/clang/test/Sema/LifetimeSafety/safety-c.c
index e9443899c9935..42adbbb3f0628 100644
--- a/clang/test/Sema/LifetimeSafety/safety-c.c
+++ b/clang/test/Sema/LifetimeSafety/safety-c.c
@@ -93,7 +93,9 @@ void conditional_operator_lifetimebound(int cond) {
int *p;
{
int a, b;
- p = identity(cond ? &a // expected-warning {{local variable 'a' does not live long enough}}
+ p = identity(cond ? &a // expected-warning {{local variable 'a' does not live long enough}} \
+ // expected-note {{result of call to 'identity' aliases the storage of local variable 'a'}} \
+ // expected-note {{result of call to 'identity' aliases the storage of local variable 'b'}}
: &b); // expected-warning {{local variable 'b' does not live long enough}}
} // expected-note {{local variable 'a' is destroyed here}} \
// expected-note {{local variable 'b' is destroyed here}}
diff --git a/clang/test/Sema/LifetimeSafety/safety.cpp b/clang/test/Sema/LifetimeSafety/safety.cpp
index 2c2d62b25b6c8..e15e0c44de255 100644
--- a/clang/test/Sema/LifetimeSafety/safety.cpp
+++ b/clang/test/Sema/LifetimeSafety/safety.cpp
@@ -448,7 +448,7 @@ void loan_from_previous_iteration(MyObj safe, bool condition) {
p = &x; // expected-warning {{does not live long enough}}
if (condition)
- q = p;
+ q = p; // expected-note {{local variable 'p' aliases the storage of local variable 'x'}}
(void)*p;
(void)*q; // expected-note {{later used here}}
} // expected-note {{local variable 'x' is destroyed here}}
@@ -846,7 +846,8 @@ void lifetimebound_multiple_args_potential(bool cond) {
MyObj obj1;
if (cond) {
MyObj obj2;
- v = Choose(true,
+ v = Choose(true, // expected-note {{result of call to 'Choose' aliases the storage of local variable 'obj1'}} \
+ // expected-note {{result of call to 'Choose' aliases the storage of local variable 'obj2'}}
obj1, // expected-warning {{local variable 'obj1' does not live long enough}}
obj2); // expected-warning {{local variable 'obj2' does not live long enough}}
} // expected-note {{local variable 'obj2' is destroyed here}}
@@ -940,7 +941,7 @@ void lifetimebound_partial_safety(bool cond) {
if (cond) {
MyObj temp_obj;
- v = Choose(true,
+ v = Choose(true, // expected-note {{result of call to 'Choose' aliases the storage of local variable 'temp_obj'}}
safe_obj,
temp_obj); // expected-warning {{local variable 'temp_obj' does not live long enough}}
} // expected-note {{local variable 'temp_obj' is destroyed here}}
@@ -1223,7 +1224,9 @@ void conditional_operator_lifetimebound(bool cond) {
MyObj* p;
{
MyObj a, b;
- p = Identity(cond ? &a // expected-warning {{local variable 'a' does not live long enough}}
+ p = Identity(cond ? &a // expected-warning {{local variable 'a' does not live long enough}} \
+ // expected-note {{result of call to 'Identity' aliases the storage of local variable 'a'}} \
+ // expected-note {{result of call to 'Identity' aliases the storage of local variable 'b'}}
: &b); // expected-warning {{local variable 'b' does not live long enough}}
} // expected-note {{local variable 'b' is destroyed here}} expected-note {{local variable 'a' is destroyed here}}
(void)*p; // expected-note 2 {{later used here}}
@@ -1243,9 +1246,15 @@ void conditional_operator_lifetimebound_nested_deep(bool cond) {
MyObj* p;
{
MyObj a, b, c, d;
- p = Identity(cond ? Identity(cond ? &a // expected-warning {{local variable 'a' does not live long enough}}
+ p = Identity(cond ? Identity(cond ? &a // expected-warning {{local variable 'a' does not live long enough}} \
+ // expected-note 2 {{result of call to 'Identity' aliases the storage of local variable 'a'}} \
+ // expected-note 2 {{result of call to 'Identity' aliases the storage of local variable 'b'}} \
+ // expected-note {{result of call to 'Identity' aliases the storage of local variable 'c'}} \
+ // expected-note {{result of call to 'Identity' aliases the storage of local variable 'd'}}
: &b) // expected-warning {{local variable 'b' does not live long enough}}
- : Identity(cond ? &c // expected-warning {{local variable 'c' does not live long enough}}
+ : Identity(cond ? &c // expected-warning {{local variable 'c' does not live long enough}} \
+ // expected-note {{result of call to 'Identity' aliases the storage of local variable 'c'}} \
+ // expected-note {{result of call to 'Identity' aliases the storage of local variable 'd'}}
: &d)); // expected-warning {{local variable 'd' does not live long enough}}
} // expected-note {{local variable 'a' is destroyed here}} expected-note {{local variable 'd' is destroyed here}} expected-note {{local variable 'b' is destroyed here}} expected-note {{local variable 'c' is destroyed here}}
(void)*p; // expected-note 4 {{later used here}}
@@ -1539,7 +1548,8 @@ void range_based_for_use_after_scope() {
View v;
{
MyObjStorage s;
- for (const MyObj &o : s) { // expected-warning {{local variable 's' does not live long enough}}
+ for (const MyObj &o : s) { // expected-warning {{local variable 's' does not live long enough}} \
+ // expected-note {{local variable '__range2' aliases the storage of local variable 's'}}
v = o;
}
} // expected-note {{local variable 's' is destroyed here}}
diff --git a/clang/unittests/Analysis/LifetimeSafetyTest.cpp b/clang/unittests/Analysis/LifetimeSafetyTest.cpp
index 78b7449958140..8d5ed9c88dcff 100644
--- a/clang/unittests/Analysis/LifetimeSafetyTest.cpp
+++ b/clang/unittests/Analysis/LifetimeSafetyTest.cpp
@@ -215,7 +215,8 @@ class LifetimeTestHelper {
for (LoanID LID : EndLoanIDs) {
llvm::SmallVector<OriginID> OriginFlowChain =
Runner.getAnalysis().getLoanPropagation().buildOriginFlowChain(
- getProgramPoint(Annotation), *StartOriginID, LID);
+ getProgramPoint(Annotation), *StartOriginID, LID,
+ Runner.getAnalysisContext().getAnalysis<PostOrderCFGView>());
if (!OriginFlowChain.empty())
return OriginFlowChain;
}
>From 46d7462ffc1938808456239a2ed3f3518c3c46db Mon Sep 17 00:00:00 2001
From: Yuan Suo <suoyuan666 at s5n.xyz>
Date: Fri, 19 Jun 2026 15:25:42 +0800
Subject: [PATCH 02/21] Add return after llvm_unreachable and clean up code
Signed-off-by: Yuan Suo <suoyuan666 at s5n.xyz>
---
.../LifetimeSafety/LoanPropagation.cpp | 104 +++++++-----------
clang/lib/Sema/SemaLifetimeSafety.h | 4 +-
2 files changed, 42 insertions(+), 66 deletions(-)
diff --git a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
index 8e2d9a6577427..59081522b40f7 100644
--- a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
@@ -214,35 +214,7 @@ class AnalysisImpl
assert(getLoans(StartOID, StartPoint).contains(TargetLoan) &&
"TargetLoan must be present in the StartOID at the StartPoint");
- std::optional<OriginID> FinalOID;
- llvm::DenseMap<OriginID, OriginID> VistedOriginIDs;
-
- const auto OriginFlowChainFilter = [&VistedOriginIDs](OriginID FinalOID) {
- llvm::SmallVector<OriginID> OriginFlowChain;
- while (true) {
- OriginFlowChain.push_back(FinalOID);
- const auto NextOriginID = VistedOriginIDs.find(FinalOID);
- if (NextOriginID == VistedOriginIDs.end())
- break;
- FinalOID = NextOriginID->second;
- }
- return OriginFlowChain;
- };
-
- const auto InsertVistedOriginIDs =
- [&VistedOriginIDs, &FinalOID](llvm::ArrayRef<OriginID> OriginFlowChain,
- OriginID &StartOID) {
- if (!VistedOriginIDs.empty())
- VistedOriginIDs.insert({OriginFlowChain[0], StartOID});
-
- for (size_t i = 0; i < OriginFlowChain.size() - 1; ++i)
- VistedOriginIDs.insert(
- {OriginFlowChain[i + 1], OriginFlowChain[i]});
-
- StartOID = OriginFlowChain.back();
- FinalOID = StartOID;
- };
-
+ llvm::SmallVector<OriginID> OriginFlowChain;
std::optional<size_t> BlockID = FactMgr.getBlockID(StartPoint);
assert(BlockID.has_value());
const auto StartIt = llvm::find_if(*POV, [&BlockID](const CFGBlock *Block) {
@@ -252,45 +224,20 @@ class AnalysisImpl
OriginID CurrOID = StartOID;
for (const CFGBlock *B :
llvm::reverse(llvm::make_range(POV->begin(), StartIt + 1))) {
- BuildOriginFlowChainResult BuildResult =
- buildOriginFlowChain(B, CurrOID, TargetLoan);
- if (!BuildResult.OriginFlowChain.empty())
- InsertVistedOriginIDs(BuildResult.OriginFlowChain, CurrOID);
- if (BuildResult.Complete)
- return OriginFlowChainFilter(*FinalOID);
- }
-
- llvm_unreachable(
- "buildOriginFlowChain should return at BuildResult.Complete");
- }
-
- BuildOriginFlowChainResult
- buildOriginFlowChain(const CFGBlock *Block, const OriginID StartOID,
- const LoanID TargetLoan) const {
- OriginID CurrOID = StartOID;
- llvm::SmallVector<OriginID> OriginFlowChain;
+ auto [OFChain, Complete] = buildOriginFlowChain(B, CurrOID, TargetLoan);
- for (const Fact *F : llvm::reverse(FactMgr.getFacts(Block))) {
- if (const auto *IF = F->getAs<IssueFact>())
- if (IF->getLoanID() == TargetLoan) {
- assert(IF->getOriginID() == CurrOID);
- return {OriginFlowChain, true};
- }
-
- const auto *OFF = F->getAs<OriginFlowFact>();
- if (!OFF)
- continue;
- if (OFF->getDestOriginID() != CurrOID)
- continue;
+ if (!OFChain.empty()) {
+ OriginFlowChain.append(OFChain.begin(), OFChain.end());
+ CurrOID = OFChain.back();
+ }
- const OriginID SrcOriginID = OFF->getSrcOriginID();
- if (!getLoans(SrcOriginID, OFF).contains(TargetLoan))
- continue;
- OriginFlowChain.push_back(SrcOriginID);
- CurrOID = SrcOriginID;
+ if (Complete)
+ return OriginFlowChain;
}
- return {OriginFlowChain, false};
+ llvm_unreachable(
+ "buildOriginFlowChain should return at BuildResult.Complete");
+ return OriginFlowChain;
}
llvm::SmallVector<OriginID>
@@ -327,6 +274,35 @@ class AnalysisImpl
return LoanSetFactory.getEmptySet();
}
+ BuildOriginFlowChainResult
+ buildOriginFlowChain(const CFGBlock *Block, const OriginID StartOID,
+ const LoanID TargetLoan) const {
+ OriginID CurrOID = StartOID;
+ llvm::SmallVector<OriginID> OriginFlowChain;
+
+ for (const Fact *F : llvm::reverse(FactMgr.getFacts(Block))) {
+ if (const auto *IF = F->getAs<IssueFact>())
+ if (IF->getLoanID() == TargetLoan) {
+ assert(IF->getOriginID() == CurrOID);
+ return {OriginFlowChain, true};
+ }
+
+ const auto *OFF = F->getAs<OriginFlowFact>();
+ if (!OFF)
+ continue;
+ if (OFF->getDestOriginID() != CurrOID)
+ continue;
+
+ const OriginID SrcOriginID = OFF->getSrcOriginID();
+ if (!getLoans(SrcOriginID, OFF).contains(TargetLoan))
+ continue;
+ OriginFlowChain.push_back(SrcOriginID);
+ CurrOID = SrcOriginID;
+ }
+
+ return {OriginFlowChain, false};
+ }
+
OriginLoanMap::Factory &OriginLoanMapFactory;
LoanSet::Factory &LoanSetFactory;
/// Boolean vector indexed by origin ID. If true, the origin appears in
diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h
index 026bfa90719bc..ed3582e7ee2c1 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -659,10 +659,10 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
if (OriginExprChain.empty())
return;
- const Expr *LastExpr = OriginExprChain.front();
+ const Expr *LastExpr = OriginExprChain.back();
std::string IssueStr = getDiagSubjectDescription(LastExpr);
- for (const Expr *CurrExpr : OriginExprChain.drop_front()) {
+ for (const Expr *CurrExpr : llvm::reverse(OriginExprChain.drop_back())) {
if (!shouldShowInAliasChain(CurrExpr, LastExpr))
continue;
S.Diag(CurrExpr->getBeginLoc(),
>From ca36078d411ddc4d304b9fb3d5064efdbb52fa05 Mon Sep 17 00:00:00 2001
From: Yuan Suo <suoyuan666 at s5n.xyz>
Date: Fri, 19 Jun 2026 15:49:18 +0800
Subject: [PATCH 03/21] Fix issues from previous commit
Signed-off-by: Yuan Suo <suoyuan666 at s5n.xyz>
---
clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp | 9 ++-------
clang/lib/Sema/SemaLifetimeSafety.h | 2 +-
2 files changed, 3 insertions(+), 8 deletions(-)
diff --git a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
index 59081522b40f7..d8b41f478b878 100644
--- a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
@@ -130,11 +130,6 @@ struct Lattice {
}
};
-struct BuildOriginFlowChainResult {
- const llvm::SmallVector<OriginID> OriginFlowChain;
- const bool Complete;
-};
-
class AnalysisImpl
: public DataflowAnalysis<AnalysisImpl, Lattice, Direction::Forward> {
public:
@@ -237,7 +232,7 @@ class AnalysisImpl
llvm_unreachable(
"buildOriginFlowChain should return at BuildResult.Complete");
- return OriginFlowChain;
+ return {};
}
llvm::SmallVector<OriginID>
@@ -274,7 +269,7 @@ class AnalysisImpl
return LoanSetFactory.getEmptySet();
}
- BuildOriginFlowChainResult
+ std::pair<llvm::SmallVector<OriginID>, bool>
buildOriginFlowChain(const CFGBlock *Block, const OriginID StartOID,
const LoanID TargetLoan) const {
OriginID CurrOID = StartOID;
diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h
index ed3582e7ee2c1..a3d546792fe41 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -662,7 +662,7 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
const Expr *LastExpr = OriginExprChain.back();
std::string IssueStr = getDiagSubjectDescription(LastExpr);
- for (const Expr *CurrExpr : llvm::reverse(OriginExprChain.drop_back())) {
+ for (const Expr *CurrExpr : reverse(OriginExprChain.drop_back())) {
if (!shouldShowInAliasChain(CurrExpr, LastExpr))
continue;
S.Diag(CurrExpr->getBeginLoc(),
>From 1fc61c74f9349dffacdd0a8a8a60e0dd2bc853bb Mon Sep 17 00:00:00 2001
From: Yuan Suo <suoyuan666 at s5n.xyz>
Date: Fri, 19 Jun 2026 15:52:35 +0800
Subject: [PATCH 04/21] Reanme StartIt to EndBlockIt
Signed-off-by: Yuan Suo <suoyuan666 at s5n.xyz>
---
clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
index d8b41f478b878..3424bbc80a14f 100644
--- a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
@@ -212,13 +212,13 @@ class AnalysisImpl
llvm::SmallVector<OriginID> OriginFlowChain;
std::optional<size_t> BlockID = FactMgr.getBlockID(StartPoint);
assert(BlockID.has_value());
- const auto StartIt = llvm::find_if(*POV, [&BlockID](const CFGBlock *Block) {
+ const auto EndBlockIt = llvm::find_if(*POV, [&BlockID](const CFGBlock *Block) {
return Block->getBlockID() == BlockID;
});
OriginID CurrOID = StartOID;
for (const CFGBlock *B :
- llvm::reverse(llvm::make_range(POV->begin(), StartIt + 1))) {
+ llvm::reverse(llvm::make_range(POV->begin(), EndBlockIt + 1))) {
auto [OFChain, Complete] = buildOriginFlowChain(B, CurrOID, TargetLoan);
if (!OFChain.empty()) {
>From 8ac7195db4d26145940aa986163b4e5e9034a8dc Mon Sep 17 00:00:00 2001
From: Yuan Suo <suoyuan666 at s5n.xyz>
Date: Fri, 19 Jun 2026 15:55:02 +0800
Subject: [PATCH 05/21] Fix clang-format error
Signed-off-by: Yuan Suo <suoyuan666 at s5n.xyz>
---
clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
index 3424bbc80a14f..f6d620d6520ca 100644
--- a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
@@ -212,9 +212,10 @@ class AnalysisImpl
llvm::SmallVector<OriginID> OriginFlowChain;
std::optional<size_t> BlockID = FactMgr.getBlockID(StartPoint);
assert(BlockID.has_value());
- const auto EndBlockIt = llvm::find_if(*POV, [&BlockID](const CFGBlock *Block) {
- return Block->getBlockID() == BlockID;
- });
+ const auto EndBlockIt =
+ llvm::find_if(*POV, [&BlockID](const CFGBlock *Block) {
+ return Block->getBlockID() == BlockID;
+ });
OriginID CurrOID = StartOID;
for (const CFGBlock *B :
>From 1b15f8e4e99e7bb4304d7c45b44427fb295157d9 Mon Sep 17 00:00:00 2001
From: Yuan Suo <suoyuan666 at s5n.xyz>
Date: Sat, 20 Jun 2026 15:29:12 +0800
Subject: [PATCH 06/21] Remove return after llvm_unreachable add
llvm_unreachable in getBlockID
Also add a unit test for buildOriginFlowChain
Signed-off-by: Yuan Suo <suoyuan666 at s5n.xyz>
---
.../Analysis/Analyses/LifetimeSafety/Facts.h | 2 +-
clang/lib/Analysis/LifetimeSafety/Facts.cpp | 9 ++--
.../LifetimeSafety/LoanPropagation.cpp | 6 +--
.../unittests/Analysis/LifetimeSafetyTest.cpp | 44 +++++++++++++++++++
4 files changed, 50 insertions(+), 11 deletions(-)
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
index e0ddfe95b00b2..e1380a6cf195c 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
@@ -374,7 +374,7 @@ class FactManager {
/// Retrieves all the facts in the block containing Program Point P.
/// \note This is intended for testing only.
llvm::ArrayRef<const Fact *> getBlockContaining(ProgramPoint P) const;
- std::optional<size_t> getBlockID(ProgramPoint P) const;
+ size_t getBlockID(ProgramPoint P) const;
unsigned getNumFacts() const { return NextFactID.Value; }
diff --git a/clang/lib/Analysis/LifetimeSafety/Facts.cpp b/clang/lib/Analysis/LifetimeSafety/Facts.cpp
index 823f1f686f904..08e7c6cd62345 100644
--- a/clang/lib/Analysis/LifetimeSafety/Facts.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Facts.cpp
@@ -145,17 +145,14 @@ void FactManager::dump(const CFG &Cfg, AnalysisDeclContext &AC) const {
llvm::ArrayRef<const Fact *>
FactManager::getBlockContaining(ProgramPoint P) const {
- std::optional<size_t> BlockIndex = getBlockID(P);
- if (BlockIndex)
- return BlockToFacts[BlockIndex.value()];
- return {};
+ return BlockToFacts[getBlockID(P)];
}
-std::optional<size_t> FactManager::getBlockID(ProgramPoint P) const {
+size_t FactManager::getBlockID(ProgramPoint P) const {
for (size_t i = 0; i < BlockToFacts.size(); ++i)
for (const Fact *F : BlockToFacts[i])
if (F == P)
return i;
- return std::nullopt;
+ llvm_unreachable("Failed to find BlockID for given ProgramPoint");
}
} // namespace clang::lifetimes::internal
diff --git a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
index f6d620d6520ca..385bf5f8a192f 100644
--- a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
@@ -210,8 +210,7 @@ class AnalysisImpl
"TargetLoan must be present in the StartOID at the StartPoint");
llvm::SmallVector<OriginID> OriginFlowChain;
- std::optional<size_t> BlockID = FactMgr.getBlockID(StartPoint);
- assert(BlockID.has_value());
+ size_t BlockID = FactMgr.getBlockID(StartPoint);
const auto EndBlockIt =
llvm::find_if(*POV, [&BlockID](const CFGBlock *Block) {
return Block->getBlockID() == BlockID;
@@ -232,8 +231,7 @@ class AnalysisImpl
}
llvm_unreachable(
- "buildOriginFlowChain should return at BuildResult.Complete");
- return {};
+ "buildOriginFlowChain did not reach IssueFact for TargetLoan");
}
llvm::SmallVector<OriginID>
diff --git a/clang/unittests/Analysis/LifetimeSafetyTest.cpp b/clang/unittests/Analysis/LifetimeSafetyTest.cpp
index 8d5ed9c88dcff..153225dcdbf15 100644
--- a/clang/unittests/Analysis/LifetimeSafetyTest.cpp
+++ b/clang/unittests/Analysis/LifetimeSafetyTest.cpp
@@ -2078,5 +2078,49 @@ TEST_F(LifetimeAnalysisTest, BuildOriginFlowChainWithLifetimeBound) {
EXPECT_THAT(ChainForTgtB, Contains(*Helper->getOriginForDecl("result")));
EXPECT_THAT(ChainForTgtB, Not(Contains(*Helper->getOriginForDecl("a"))));
}
+
+TEST_F(LifetimeAnalysisTest, BuildOriginFlowChainInMultiBlock) {
+ SetupTest(R"(
+ void target(bool c1, bool c2) {
+ int *s;
+ int *a, *b, *c;
+
+ {
+ int tgta, tgtb, tgtc;
+ a = &tgta;
+ b = &tgtb;
+ c = &tgtc;
+ }
+
+ if (c1) {
+ s = c2 ? a : b;
+ } else {
+ s = c;
+ }
+
+ POINT(after_nested_merge);
+ (void)*s;
+ }
+ )");
+
+ llvm::SmallVector<OriginID> ChainForTgtA =
+ Helper->buildOriginFlowChainInOneBlock("s", "tgta", "after_nested_merge");
+ llvm::SmallVector<OriginID> ChainForTgtB =
+ Helper->buildOriginFlowChainInOneBlock("s", "tgtb", "after_nested_merge");
+ llvm::SmallVector<OriginID> ChainForTgtC =
+ Helper->buildOriginFlowChainInOneBlock("s", "tgtc", "after_nested_merge");
+
+ EXPECT_THAT(ChainForTgtA, Contains(*Helper->getOriginForDecl("a")));
+ EXPECT_THAT(ChainForTgtA, Not(Contains(*Helper->getOriginForDecl("b"))));
+ EXPECT_THAT(ChainForTgtA, Not(Contains(*Helper->getOriginForDecl("c"))));
+
+ EXPECT_THAT(ChainForTgtB, Contains(*Helper->getOriginForDecl("b")));
+ EXPECT_THAT(ChainForTgtB, Not(Contains(*Helper->getOriginForDecl("a"))));
+ EXPECT_THAT(ChainForTgtB, Not(Contains(*Helper->getOriginForDecl("c"))));
+
+ EXPECT_THAT(ChainForTgtC, Contains(*Helper->getOriginForDecl("c")));
+ EXPECT_THAT(ChainForTgtC, Not(Contains(*Helper->getOriginForDecl("b"))));
+ EXPECT_THAT(ChainForTgtC, Not(Contains(*Helper->getOriginForDecl("a"))));
+}
} // anonymous namespace
} // namespace clang::lifetimes::internal
>From 3b294d1d7eca15d5bad4239fe4088f0e99bfb180 Mon Sep 17 00:00:00 2001
From: Yuan Suo <suoyuan666 at s5n.xyz>
Date: Sat, 20 Jun 2026 16:17:23 +0800
Subject: [PATCH 07/21] Add Decl->isImplicit() in shouldShowInAliasChain
Signed-off-by: Yuan Suo <suoyuan666 at s5n.xyz>
---
clang/lib/Sema/SemaLifetimeSafety.h | 15 ++++++++++++++-
clang/test/Sema/LifetimeSafety/safety.cpp | 2 +-
2 files changed, 15 insertions(+), 2 deletions(-)
diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h
index a3d546792fe41..222b517154e6c 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -643,11 +643,24 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
return "expression";
}
+ bool isInValidExpr(const Expr *E) {
+ if (const auto *DRE = dyn_cast<DeclRefExpr>(E)) {
+ return !DRE->getDecl()->isImplicit();
+ }
+
+ if (const auto *CE = dyn_cast<CallExpr>(E)) {
+ if (const auto *FE = CE->getDirectCallee())
+ return !FE->isImplicit();
+ }
+
+ return false;
+ }
+
bool shouldShowInAliasChain(const Expr *CurrExpr, const Expr *LastExpr) {
CurrExpr = CurrExpr->IgnoreImpCasts();
LastExpr = LastExpr->IgnoreImpCasts();
- if (!isa<CallExpr, DeclRefExpr>(CurrExpr))
+ if (!isInValidExpr(CurrExpr))
return false;
// Source ranges can be used to filter out many implicit expressions,
// because operations between class objects often involve numerous implicit
diff --git a/clang/test/Sema/LifetimeSafety/safety.cpp b/clang/test/Sema/LifetimeSafety/safety.cpp
index e15e0c44de255..47e65993c9a76 100644
--- a/clang/test/Sema/LifetimeSafety/safety.cpp
+++ b/clang/test/Sema/LifetimeSafety/safety.cpp
@@ -1549,7 +1549,7 @@ void range_based_for_use_after_scope() {
{
MyObjStorage s;
for (const MyObj &o : s) { // expected-warning {{local variable 's' does not live long enough}} \
- // expected-note {{local variable '__range2' aliases the storage of local variable 's'}}
+ // expected-note {{result of call to 'begin' aliases the storage of local variable 's'}}
v = o;
}
} // expected-note {{local variable 's' is destroyed here}}
>From 312bee8208735975c50f878b6976959224493810 Mon Sep 17 00:00:00 2001
From: Yuan Suo <suoyuan666 at s5n.xyz>
Date: Sat, 20 Jun 2026 16:55:16 +0800
Subject: [PATCH 08/21] Fix inconsistent semantics in isInValidExpr
Signed-off-by: Yuan Suo <suoyuan666 at s5n.xyz>
---
clang/lib/Sema/SemaLifetimeSafety.h | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h
index 222b517154e6c..c866e1f284f54 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -645,22 +645,22 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
bool isInValidExpr(const Expr *E) {
if (const auto *DRE = dyn_cast<DeclRefExpr>(E)) {
- return !DRE->getDecl()->isImplicit();
+ return DRE->getDecl()->isImplicit();
}
if (const auto *CE = dyn_cast<CallExpr>(E)) {
if (const auto *FE = CE->getDirectCallee())
- return !FE->isImplicit();
+ return FE->isImplicit();
}
- return false;
+ return true;
}
bool shouldShowInAliasChain(const Expr *CurrExpr, const Expr *LastExpr) {
CurrExpr = CurrExpr->IgnoreImpCasts();
LastExpr = LastExpr->IgnoreImpCasts();
- if (!isInValidExpr(CurrExpr))
+ if (isInValidExpr(CurrExpr))
return false;
// Source ranges can be used to filter out many implicit expressions,
// because operations between class objects often involve numerous implicit
>From a8993ca95226b37b60bb51acbb7ad57520bde279 Mon Sep 17 00:00:00 2001
From: Yuan Suo <suoyuan666 at s5n.xyz>
Date: Sun, 21 Jun 2026 12:55:16 +0800
Subject: [PATCH 09/21] isInValidExpr -> isInvalidExpr
Signed-off-by: Yuan Suo <suoyuan666 at s5n.xyz>
---
clang/lib/Sema/SemaLifetimeSafety.h | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h
index c866e1f284f54..f8b9f48d4f485 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -643,7 +643,7 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
return "expression";
}
- bool isInValidExpr(const Expr *E) {
+ bool isInvalidExpr(const Expr *E) {
if (const auto *DRE = dyn_cast<DeclRefExpr>(E)) {
return DRE->getDecl()->isImplicit();
}
@@ -660,7 +660,7 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
CurrExpr = CurrExpr->IgnoreImpCasts();
LastExpr = LastExpr->IgnoreImpCasts();
- if (isInValidExpr(CurrExpr))
+ if (isInvalidExpr(CurrExpr))
return false;
// Source ranges can be used to filter out many implicit expressions,
// because operations between class objects often involve numerous implicit
>From 8a39452679fd72aa0c01e579f039f9724493d5b1 Mon Sep 17 00:00:00 2001
From: Yuan Suo <suoyuan666 at s5n.xyz>
Date: Sun, 21 Jun 2026 13:51:26 +0800
Subject: [PATCH 10/21] Using Block->preds() instead of PostOrderCFGView
Signed-off-by: Yuan Suo <suoyuan666 at s5n.xyz>
---
.../Analyses/LifetimeSafety/LoanPropagation.h | 17 ++-
clang/lib/Analysis/LifetimeSafety/Checker.cpp | 8 +-
.../LifetimeSafety/LoanPropagation.cpp | 98 +++++++++++-----
clang/lib/Sema/SemaLifetimeSafety.h | 4 +-
.../unittests/Analysis/LifetimeSafetyTest.cpp | 107 +++++++++---------
5 files changed, 135 insertions(+), 99 deletions(-)
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
index 42d7df0838f35..97d529e91a9ee 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
@@ -16,7 +16,6 @@
#define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_LOAN_PROPAGATION_H
#include "clang/Analysis/Analyses/LifetimeSafety/Facts.h"
-#include "clang/Analysis/Analyses/PostOrderCFGView.h"
#include "clang/Analysis/AnalysisDeclContext.h"
#include "clang/Analysis/CFG.h"
#include "llvm/ADT/ImmutableMap.h"
@@ -45,14 +44,14 @@ class LoanPropagationAnalysis {
/// this function traces backwards through OriginFlowFacts to identify the
/// sequence of origins through which the loan flowed, ending at the origin
/// where the loan was originally issued.
- llvm::SmallVector<OriginID>
- buildOriginFlowChain(ProgramPoint StartPoint, const OriginID StartOID,
- const LoanID TargetLoan,
- const PostOrderCFGView *POV) const;
-
- llvm::SmallVector<OriginID>
- buildOriginFlowChain(const UseFact *UF, const LoanID TargetLoan,
- const PostOrderCFGView *POV) const;
+ llvm::SmallVector<OriginID> buildOriginFlowChain(ProgramPoint StartPoint,
+ const OriginID StartOID,
+ const LoanID TargetLoan,
+ const CFG *Cfg) const;
+
+ llvm::SmallVector<OriginID> buildOriginFlowChain(const UseFact *UF,
+ const LoanID TargetLoan,
+ const CFG *Cfg) const;
private:
class Impl;
diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
index 66a52ba1303c3..f72f7f80abbc0 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -67,9 +67,9 @@ class LifetimeChecker {
FactManager &FactMgr;
LifetimeSafetySemaHelper *SemaHelper;
ASTContext &AST;
+ const CFG *Cfg;
const Decl *FD;
const LifetimeSafetyOpts &LSOpts;
- const PostOrderCFGView *POV;
static SourceLocation
GetFactLoc(llvm::PointerUnion<const UseFact *, const OriginEscapesFact *> F) {
@@ -93,8 +93,8 @@ class LifetimeChecker {
const LifetimeSafetyOpts &LSOpts)
: LoanPropagation(LoanPropagation), MovedLoans(MovedLoans),
LiveOrigins(LiveOrigins), FactMgr(FM), SemaHelper(SemaHelper),
- AST(ADC.getASTContext()), FD(ADC.getDecl()), LSOpts(LSOpts),
- POV(ADC.getAnalysis<PostOrderCFGView>()) {
+ AST(ADC.getASTContext()), Cfg(ADC.getCFG()), FD(ADC.getDecl()),
+ LSOpts(LSOpts) {
for (const CFGBlock *B : *ADC.getAnalysis<PostOrderCFGView>())
for (const Fact *F : FactMgr.getFacts(B))
if (const auto *EF = F->getAs<ExpireFact>())
@@ -274,7 +274,7 @@ class LifetimeChecker {
// Scope-based expiry (use-after-scope).
SemaHelper->reportUseAfterScope(
IssueExpr, UF->getUseExpr(), MovedExpr, ExpiryLoc,
- getExprChain(LoanPropagation.buildOriginFlowChain(UF, LID, POV)));
+ getExprChain(LoanPropagation.buildOriginFlowChain(UF, LID, Cfg)));
} else if (const auto *OEF =
CausingFact.dyn_cast<const OriginEscapesFact *>()) {
diff --git a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
index 385bf5f8a192f..a77dbf3cf3b58 100644
--- a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
@@ -18,6 +18,7 @@
#include "clang/Analysis/CFG.h"
#include "clang/Basic/LLVM.h"
#include "llvm/ADT/BitVector.h"
+#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/TimeProfiler.h"
#include "llvm/Support/raw_ostream.h"
@@ -202,46 +203,86 @@ class AnalysisImpl
return getLoans(getState(P), OID);
}
- llvm::SmallVector<OriginID>
- buildOriginFlowChain(ProgramPoint StartPoint, const OriginID StartOID,
- const LoanID TargetLoan,
- const PostOrderCFGView *POV) const {
+ llvm::SmallVector<OriginID> buildOriginFlowChain(ProgramPoint StartPoint,
+ const OriginID StartOID,
+ const LoanID TargetLoan,
+ const CFG *Cfg) const {
assert(getLoans(StartOID, StartPoint).contains(TargetLoan) &&
"TargetLoan must be present in the StartOID at the StartPoint");
- llvm::SmallVector<OriginID> OriginFlowChain;
- size_t BlockID = FactMgr.getBlockID(StartPoint);
- const auto EndBlockIt =
- llvm::find_if(*POV, [&BlockID](const CFGBlock *Block) {
- return Block->getBlockID() == BlockID;
- });
+ std::optional<OriginID> FinalOID;
+ llvm::DenseMap<OriginID, OriginID> VistedOriginIDs;
+
+ const auto OriginFlowChainFilter = [&VistedOriginIDs](OriginID FinalOID) {
+ llvm::SmallVector<OriginID> OriginFlowChain;
+ while (true) {
+ OriginFlowChain.push_back(FinalOID);
+ const auto NextOriginID = VistedOriginIDs.find(FinalOID);
+ if (NextOriginID == VistedOriginIDs.end())
+ break;
+ FinalOID = NextOriginID->second;
+ }
+ return OriginFlowChain;
+ };
- OriginID CurrOID = StartOID;
- for (const CFGBlock *B :
- llvm::reverse(llvm::make_range(POV->begin(), EndBlockIt + 1))) {
- auto [OFChain, Complete] = buildOriginFlowChain(B, CurrOID, TargetLoan);
+ const auto InsertVistedOriginIDs =
+ [&VistedOriginIDs, &FinalOID](llvm::ArrayRef<OriginID> OriginFlowChain,
+ OriginID &StartOID) {
+ if (!VistedOriginIDs.empty())
+ VistedOriginIDs.insert({OriginFlowChain[0], StartOID});
- if (!OFChain.empty()) {
- OriginFlowChain.append(OFChain.begin(), OFChain.end());
- CurrOID = OFChain.back();
+ for (size_t i = 0; i < OriginFlowChain.size() - 1; ++i)
+ VistedOriginIDs.insert(
+ {OriginFlowChain[i + 1], OriginFlowChain[i]});
+
+ StartOID = OriginFlowChain.back();
+ FinalOID = StartOID;
+ };
+
+ const CFGBlock *EndBlock = nullptr;
+ size_t BlockID = FactMgr.getBlockID(StartPoint);
+ for (const CFGBlock *Block : *Cfg)
+ if (Block->getBlockID() == BlockID) {
+ EndBlock = Block;
+ break;
}
+ using SearchContext = std::pair<const CFGBlock *, OriginID>;
+ std::queue<SearchContext> PendingContext;
+ llvm::SmallSet<SearchContext, 32> VistedContext;
+ PendingContext.push({EndBlock, StartOID});
+
+ while (!PendingContext.empty()) {
+ auto [CurrBlock, CurrOID] = PendingContext.front();
+ PendingContext.pop();
+
+ const auto [BuildResult, Complete] =
+ buildOriginFlowChain(CurrBlock, CurrOID, TargetLoan);
+ if (!BuildResult.empty())
+ InsertVistedOriginIDs(BuildResult, CurrOID);
+
if (Complete)
- return OriginFlowChain;
+ return OriginFlowChainFilter(*FinalOID);
+
+ for (const CFGBlock *Block : CurrBlock->preds()) {
+ SearchContext Context = {Block, CurrOID};
+ if (Block && VistedContext.insert(Context).second)
+ PendingContext.push(Context);
+ }
}
llvm_unreachable(
"buildOriginFlowChain did not reach IssueFact for TargetLoan");
}
- llvm::SmallVector<OriginID>
- buildOriginFlowChain(const UseFact *UF, const LoanID TargetLoan,
- const PostOrderCFGView *POV) const {
+ llvm::SmallVector<OriginID> buildOriginFlowChain(const UseFact *UF,
+ const LoanID TargetLoan,
+ const CFG *Cfg) const {
for (const OriginList *Cur = UF->getUsedOrigins(); Cur;
Cur = Cur->peelOuterOrigin())
if (getLoans(Cur->getOuterOriginID(), UF).contains(TargetLoan))
return buildOriginFlowChain(UF, Cur->getOuterOriginID(), TargetLoan,
- POV);
+ Cfg);
return {};
}
@@ -276,10 +317,8 @@ class AnalysisImpl
for (const Fact *F : llvm::reverse(FactMgr.getFacts(Block))) {
if (const auto *IF = F->getAs<IssueFact>())
- if (IF->getLoanID() == TargetLoan) {
- assert(IF->getOriginID() == CurrOID);
+ if (IF->getLoanID() == TargetLoan && IF->getOriginID() == CurrOID)
return {OriginFlowChain, true};
- }
const auto *OFF = F->getAs<OriginFlowFact>();
if (!OFF)
@@ -327,13 +366,12 @@ LoanSet LoanPropagationAnalysis::getLoans(OriginID OID, ProgramPoint P) const {
llvm::SmallVector<OriginID> LoanPropagationAnalysis::buildOriginFlowChain(
ProgramPoint StartPoint, const OriginID StartOID, const LoanID TargetLoan,
- const PostOrderCFGView *POV) const {
- return PImpl->buildOriginFlowChain(StartPoint, StartOID, TargetLoan, POV);
+ const CFG *Cfg) const {
+ return PImpl->buildOriginFlowChain(StartPoint, StartOID, TargetLoan, Cfg);
}
llvm::SmallVector<OriginID> LoanPropagationAnalysis::buildOriginFlowChain(
- const UseFact *UF, const LoanID TargetLoan,
- const PostOrderCFGView *POV) const {
- return PImpl->buildOriginFlowChain(UF, TargetLoan, POV);
+ const UseFact *UF, const LoanID TargetLoan, const CFG *Cfg) const {
+ return PImpl->buildOriginFlowChain(UF, TargetLoan, Cfg);
}
} // namespace clang::lifetimes::internal
diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h
index f8b9f48d4f485..778d2e89b5e1f 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -672,10 +672,10 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
if (OriginExprChain.empty())
return;
- const Expr *LastExpr = OriginExprChain.back();
+ const Expr *LastExpr = OriginExprChain.front();
std::string IssueStr = getDiagSubjectDescription(LastExpr);
- for (const Expr *CurrExpr : reverse(OriginExprChain.drop_back())) {
+ for (const Expr *CurrExpr : OriginExprChain.drop_front()) {
if (!shouldShowInAliasChain(CurrExpr, LastExpr))
continue;
S.Diag(CurrExpr->getBeginLoc(),
diff --git a/clang/unittests/Analysis/LifetimeSafetyTest.cpp b/clang/unittests/Analysis/LifetimeSafetyTest.cpp
index 153225dcdbf15..d79161ae1db54 100644
--- a/clang/unittests/Analysis/LifetimeSafetyTest.cpp
+++ b/clang/unittests/Analysis/LifetimeSafetyTest.cpp
@@ -206,9 +206,8 @@ class LifetimeTestHelper {
}
llvm::SmallVector<OriginID>
- buildOriginFlowChainInOneBlock(llvm::StringRef StartOriginVar,
- llvm::StringRef EndLoanVar,
- llvm::StringRef Annotation) {
+ buildOriginFlowChain(llvm::StringRef StartOriginVar,
+ llvm::StringRef EndLoanVar, llvm::StringRef Annotation) {
std::optional<OriginID> StartOriginID = getOriginForDecl(StartOriginVar);
std::vector<LoanID> EndLoanIDs = getLoansForVar(EndLoanVar);
@@ -216,7 +215,7 @@ class LifetimeTestHelper {
llvm::SmallVector<OriginID> OriginFlowChain =
Runner.getAnalysis().getLoanPropagation().buildOriginFlowChain(
getProgramPoint(Annotation), *StartOriginID, LID,
- Runner.getAnalysisContext().getAnalysis<PostOrderCFGView>());
+ Runner.getAnalysisContext().getCFG());
if (!OriginFlowChain.empty())
return OriginFlowChain;
}
@@ -1976,6 +1975,50 @@ TEST_F(LifetimeAnalysisTest, LambdaInitCaptureViewByValue) {
// Tests for buildOriginFlowChain
// ========================================================================= //
+TEST_F(LifetimeAnalysisTest, BuildOriginFlowChain) {
+ SetupTest(R"(
+ void target(bool c1, bool c2) {
+ int *s;
+ int *a, *b, *c;
+
+ {
+ int tgta, tgtb, tgtc;
+ a = &tgta;
+ b = &tgtb;
+ c = &tgtc;
+ }
+
+ if (c1) {
+ s = c2 ? a : b;
+ } else {
+ s = c;
+ }
+
+ POINT(after_nested_merge);
+ (void)*s;
+ }
+ )");
+
+ llvm::SmallVector<OriginID> ChainForTgtA =
+ Helper->buildOriginFlowChain("s", "tgta", "after_nested_merge");
+ llvm::SmallVector<OriginID> ChainForTgtB =
+ Helper->buildOriginFlowChain("s", "tgtb", "after_nested_merge");
+ llvm::SmallVector<OriginID> ChainForTgtC =
+ Helper->buildOriginFlowChain("s", "tgtc", "after_nested_merge");
+
+ EXPECT_THAT(ChainForTgtA, Contains(*Helper->getOriginForDecl("a")));
+ EXPECT_THAT(ChainForTgtA, Not(Contains(*Helper->getOriginForDecl("b"))));
+ EXPECT_THAT(ChainForTgtA, Not(Contains(*Helper->getOriginForDecl("c"))));
+
+ EXPECT_THAT(ChainForTgtB, Not(Contains(*Helper->getOriginForDecl("a"))));
+ EXPECT_THAT(ChainForTgtB, Contains(*Helper->getOriginForDecl("b")));
+ EXPECT_THAT(ChainForTgtB, Not(Contains(*Helper->getOriginForDecl("c"))));
+
+ EXPECT_THAT(ChainForTgtC, Not(Contains(*Helper->getOriginForDecl("a"))));
+ EXPECT_THAT(ChainForTgtC, Not(Contains(*Helper->getOriginForDecl("b"))));
+ EXPECT_THAT(ChainForTgtC, Contains(*Helper->getOriginForDecl("c")));
+}
+
TEST_F(LifetimeAnalysisTest, BuildOriginFlowChainWithErrorTargetLoan) {
SetupTest(R"(
void target() {
@@ -1987,7 +2030,7 @@ TEST_F(LifetimeAnalysisTest, BuildOriginFlowChainWithErrorTargetLoan) {
)");
#if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST
- EXPECT_DEATH(Helper->buildOriginFlowChainInOneBlock("s", "a", "after_use"),
+ EXPECT_DEATH(Helper->buildOriginFlowChain("s", "a", "after_use"),
"TargetLoan must be present in the StartOID at the StartPoint");
#endif
}
@@ -2006,7 +2049,7 @@ TEST_F(LifetimeAnalysisTest, BuildOriginFlowChainWithSelfAssignment) {
)");
const llvm::SmallVector<OriginID> OriginFlowChain =
- Helper->buildOriginFlowChainInOneBlock("s", "tgt", "after_use");
+ Helper->buildOriginFlowChain("s", "tgt", "after_use");
EXPECT_THAT(OriginFlowChain, Contains(*Helper->getOriginForDecl("a")));
}
@@ -2023,7 +2066,7 @@ TEST_F(LifetimeAnalysisTest, BuildOriginFlowChainWithMultiAssignInSameStmt) {
)");
const llvm::SmallVector<OriginID> OriginFlowChain =
- Helper->buildOriginFlowChainInOneBlock("s", "tgt", "after_use");
+ Helper->buildOriginFlowChain("s", "tgt", "after_use");
EXPECT_THAT(OriginFlowChain, Contains(*Helper->getOriginForDecl("a")));
EXPECT_THAT(OriginFlowChain, Contains(*Helper->getOriginForDecl("b")));
@@ -2044,7 +2087,7 @@ TEST_F(LifetimeAnalysisTest, BuildOriginFlowChainWithOverwritingAssignments) {
)");
const llvm::SmallVector<OriginID> OriginFlowChain =
- Helper->buildOriginFlowChainInOneBlock("s", "tgt1", "after_use");
+ Helper->buildOriginFlowChain("s", "tgt1", "after_use");
EXPECT_THAT(OriginFlowChain, Contains(*Helper->getOriginForDecl("a")));
EXPECT_THAT(OriginFlowChain, Contains(*Helper->getOriginForDecl("b")));
@@ -2066,9 +2109,9 @@ TEST_F(LifetimeAnalysisTest, BuildOriginFlowChainWithLifetimeBound) {
)");
llvm::SmallVector<OriginID> ChainForTgtA =
- Helper->buildOriginFlowChainInOneBlock("s", "tgta", "after_use");
+ Helper->buildOriginFlowChain("s", "tgta", "after_use");
llvm::SmallVector<OriginID> ChainForTgtB =
- Helper->buildOriginFlowChainInOneBlock("s", "tgtb", "after_use");
+ Helper->buildOriginFlowChain("s", "tgtb", "after_use");
EXPECT_THAT(ChainForTgtA, Contains(*Helper->getOriginForDecl("a")));
EXPECT_THAT(ChainForTgtA, Contains(*Helper->getOriginForDecl("result")));
@@ -2078,49 +2121,5 @@ TEST_F(LifetimeAnalysisTest, BuildOriginFlowChainWithLifetimeBound) {
EXPECT_THAT(ChainForTgtB, Contains(*Helper->getOriginForDecl("result")));
EXPECT_THAT(ChainForTgtB, Not(Contains(*Helper->getOriginForDecl("a"))));
}
-
-TEST_F(LifetimeAnalysisTest, BuildOriginFlowChainInMultiBlock) {
- SetupTest(R"(
- void target(bool c1, bool c2) {
- int *s;
- int *a, *b, *c;
-
- {
- int tgta, tgtb, tgtc;
- a = &tgta;
- b = &tgtb;
- c = &tgtc;
- }
-
- if (c1) {
- s = c2 ? a : b;
- } else {
- s = c;
- }
-
- POINT(after_nested_merge);
- (void)*s;
- }
- )");
-
- llvm::SmallVector<OriginID> ChainForTgtA =
- Helper->buildOriginFlowChainInOneBlock("s", "tgta", "after_nested_merge");
- llvm::SmallVector<OriginID> ChainForTgtB =
- Helper->buildOriginFlowChainInOneBlock("s", "tgtb", "after_nested_merge");
- llvm::SmallVector<OriginID> ChainForTgtC =
- Helper->buildOriginFlowChainInOneBlock("s", "tgtc", "after_nested_merge");
-
- EXPECT_THAT(ChainForTgtA, Contains(*Helper->getOriginForDecl("a")));
- EXPECT_THAT(ChainForTgtA, Not(Contains(*Helper->getOriginForDecl("b"))));
- EXPECT_THAT(ChainForTgtA, Not(Contains(*Helper->getOriginForDecl("c"))));
-
- EXPECT_THAT(ChainForTgtB, Contains(*Helper->getOriginForDecl("b")));
- EXPECT_THAT(ChainForTgtB, Not(Contains(*Helper->getOriginForDecl("a"))));
- EXPECT_THAT(ChainForTgtB, Not(Contains(*Helper->getOriginForDecl("c"))));
-
- EXPECT_THAT(ChainForTgtC, Contains(*Helper->getOriginForDecl("c")));
- EXPECT_THAT(ChainForTgtC, Not(Contains(*Helper->getOriginForDecl("b"))));
- EXPECT_THAT(ChainForTgtC, Not(Contains(*Helper->getOriginForDecl("a"))));
-}
} // anonymous namespace
} // namespace clang::lifetimes::internal
>From f9d407d7674135896e11029264b5312013af0c7c Mon Sep 17 00:00:00 2001
From: Yuan Suo <suoyuan666 at s5n.xyz>
Date: Sun, 21 Jun 2026 15:02:59 +0800
Subject: [PATCH 11/21] Add a fallback in isInvalidExpr(CallExpr)
Signed-off-by: Yuan Suo <suoyuan666 at s5n.xyz>
---
clang/lib/Sema/SemaLifetimeSafety.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h
index 778d2e89b5e1f..c0db4bd5d5f75 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -651,6 +651,7 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
if (const auto *CE = dyn_cast<CallExpr>(E)) {
if (const auto *FE = CE->getDirectCallee())
return FE->isImplicit();
+ return false;
}
return true;
>From d630f1f450c6b24359d20d5020696d61f26c1fbb Mon Sep 17 00:00:00 2001
From: Yuan Suo <suoyuan666 at s5n.xyz>
Date: Mon, 22 Jun 2026 15:58:15 +0800
Subject: [PATCH 12/21] Add debug output
==========================================
Lifetime Analysis buildOriginFlow
==========================================
StartOriginID: 3, TargetLoanID: 6
CurrBlockID: 3, StartOriginID: 3
Find OriginID: 29
Find OriginID: 9
EndOriginID: 9
CurrBlockID: 6, StartOriginID: 3
EndOriginID: 3
Signed-off-by: Yuan Suo <suoyuan666 at s5n.xyz>
---
.../LifetimeSafety/LoanPropagation.cpp | 23 +++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
index a77dbf3cf3b58..27f5ecf65ceb9 100644
--- a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
@@ -210,6 +210,18 @@ class AnalysisImpl
assert(getLoans(StartOID, StartPoint).contains(TargetLoan) &&
"TargetLoan must be present in the StartOID at the StartPoint");
+ DEBUG_WITH_TYPE("LifetimeBuildOriginFlow",
+ llvm::dbgs()
+ << "==========================================\n");
+ DEBUG_WITH_TYPE("LifetimeBuildOriginFlow",
+ llvm::dbgs() << " Lifetime Analysis buildOriginFlow\n");
+ DEBUG_WITH_TYPE("LifetimeBuildOriginFlow",
+ llvm::dbgs()
+ << "==========================================\n");
+ DEBUG_WITH_TYPE("LifetimeBuildOriginFlow",
+ llvm::dbgs() << "StartOriginID: " << StartOID
+ << ", TargetLoanID: " << TargetLoan << "\n\n");
+
std::optional<OriginID> FinalOID;
llvm::DenseMap<OriginID, OriginID> VistedOriginIDs;
@@ -256,6 +268,10 @@ class AnalysisImpl
auto [CurrBlock, CurrOID] = PendingContext.front();
PendingContext.pop();
+ DEBUG_WITH_TYPE("LifetimeBuildOriginFlow",
+ llvm::dbgs() << "CurrBlockID: " << CurrBlock->getBlockID()
+ << ", StartOriginID: " << CurrOID << "\n");
+
const auto [BuildResult, Complete] =
buildOriginFlowChain(CurrBlock, CurrOID, TargetLoan);
if (!BuildResult.empty())
@@ -264,6 +280,9 @@ class AnalysisImpl
if (Complete)
return OriginFlowChainFilter(*FinalOID);
+ DEBUG_WITH_TYPE("LifetimeBuildOriginFlow",
+ llvm::dbgs() << "EndOriginID: " << CurrOID << "\n");
+
for (const CFGBlock *Block : CurrBlock->preds()) {
SearchContext Context = {Block, CurrOID};
if (Block && VistedContext.insert(Context).second)
@@ -329,6 +348,10 @@ class AnalysisImpl
const OriginID SrcOriginID = OFF->getSrcOriginID();
if (!getLoans(SrcOriginID, OFF).contains(TargetLoan))
continue;
+
+ DEBUG_WITH_TYPE("LifetimeBuildOriginFlow",
+ llvm::dbgs()
+ << "\tFind OriginID: " << SrcOriginID << "\n");
OriginFlowChain.push_back(SrcOriginID);
CurrOID = SrcOriginID;
}
>From 3e5e730546c5ddfde9ad26db50228ae465c68333 Mon Sep 17 00:00:00 2001
From: Yuan Suo <suoyuan666 at s5n.xyz>
Date: Tue, 23 Jun 2026 21:16:20 +0800
Subject: [PATCH 13/21] Simplify buildOriginFlowChain and add some test case
Signed-off-by: Yuan Suo <suoyuan666 at s5n.xyz>
---
.../Analyses/LifetimeSafety/LoanPropagation.h | 2 +-
.../LifetimeSafety/LoanPropagation.cpp | 58 +++++--------------
clang/lib/Sema/SemaLifetimeSafety.h | 4 +-
clang/test/Sema/LifetimeSafety/safety.cpp | 41 +++++++++++++
4 files changed, 58 insertions(+), 47 deletions(-)
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
index 97d529e91a9ee..2c3357125810c 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
@@ -40,7 +40,7 @@ class LoanPropagationAnalysis {
/// Builds the chain of origins through which a loan has propagated.
///
- /// Starting from StartPoint where StartOID currently holds TargetLoan,
+ /// Starting from the last fact of the block containg StartPoint,
/// this function traces backwards through OriginFlowFacts to identify the
/// sequence of origins through which the loan flowed, ending at the origin
/// where the loan was originally issued.
diff --git a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
index 27f5ecf65ceb9..2d9acbd822f16 100644
--- a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
@@ -222,35 +222,6 @@ class AnalysisImpl
llvm::dbgs() << "StartOriginID: " << StartOID
<< ", TargetLoanID: " << TargetLoan << "\n\n");
- std::optional<OriginID> FinalOID;
- llvm::DenseMap<OriginID, OriginID> VistedOriginIDs;
-
- const auto OriginFlowChainFilter = [&VistedOriginIDs](OriginID FinalOID) {
- llvm::SmallVector<OriginID> OriginFlowChain;
- while (true) {
- OriginFlowChain.push_back(FinalOID);
- const auto NextOriginID = VistedOriginIDs.find(FinalOID);
- if (NextOriginID == VistedOriginIDs.end())
- break;
- FinalOID = NextOriginID->second;
- }
- return OriginFlowChain;
- };
-
- const auto InsertVistedOriginIDs =
- [&VistedOriginIDs, &FinalOID](llvm::ArrayRef<OriginID> OriginFlowChain,
- OriginID &StartOID) {
- if (!VistedOriginIDs.empty())
- VistedOriginIDs.insert({OriginFlowChain[0], StartOID});
-
- for (size_t i = 0; i < OriginFlowChain.size() - 1; ++i)
- VistedOriginIDs.insert(
- {OriginFlowChain[i + 1], OriginFlowChain[i]});
-
- StartOID = OriginFlowChain.back();
- FinalOID = StartOID;
- };
-
const CFGBlock *EndBlock = nullptr;
size_t BlockID = FactMgr.getBlockID(StartPoint);
for (const CFGBlock *Block : *Cfg)
@@ -259,14 +230,14 @@ class AnalysisImpl
break;
}
- using SearchContext = std::pair<const CFGBlock *, OriginID>;
- std::queue<SearchContext> PendingContext;
- llvm::SmallSet<SearchContext, 32> VistedContext;
- PendingContext.push({EndBlock, StartOID});
+ OriginID CurrOID = StartOID;
+ llvm::SmallVector<OriginID> OriginFlowChain;
+ std::queue<const CFGBlock *> PendingState;
+ PendingState.push(EndBlock);
- while (!PendingContext.empty()) {
- auto [CurrBlock, CurrOID] = PendingContext.front();
- PendingContext.pop();
+ while (!PendingState.empty()) {
+ const CFGBlock *CurrBlock = PendingState.front();
+ PendingState.pop();
DEBUG_WITH_TYPE("LifetimeBuildOriginFlow",
llvm::dbgs() << "CurrBlockID: " << CurrBlock->getBlockID()
@@ -274,20 +245,19 @@ class AnalysisImpl
const auto [BuildResult, Complete] =
buildOriginFlowChain(CurrBlock, CurrOID, TargetLoan);
- if (!BuildResult.empty())
- InsertVistedOriginIDs(BuildResult, CurrOID);
+ if (!BuildResult.empty()) {
+ OriginFlowChain.append(BuildResult);
+ CurrOID = BuildResult.back();
+ }
if (Complete)
- return OriginFlowChainFilter(*FinalOID);
+ return OriginFlowChain;
DEBUG_WITH_TYPE("LifetimeBuildOriginFlow",
llvm::dbgs() << "EndOriginID: " << CurrOID << "\n");
- for (const CFGBlock *Block : CurrBlock->preds()) {
- SearchContext Context = {Block, CurrOID};
- if (Block && VistedContext.insert(Context).second)
- PendingContext.push(Context);
- }
+ for (const CFGBlock *Block : CurrBlock->preds())
+ PendingState.push(Block);
}
llvm_unreachable(
diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h
index c0db4bd5d5f75..1212c74bd0976 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -673,10 +673,10 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
if (OriginExprChain.empty())
return;
- const Expr *LastExpr = OriginExprChain.front();
+ const Expr *LastExpr = OriginExprChain.back();
std::string IssueStr = getDiagSubjectDescription(LastExpr);
- for (const Expr *CurrExpr : OriginExprChain.drop_front()) {
+ for (const Expr *CurrExpr : reverse(OriginExprChain.drop_back())) {
if (!shouldShowInAliasChain(CurrExpr, LastExpr))
continue;
S.Diag(CurrExpr->getBeginLoc(),
diff --git a/clang/test/Sema/LifetimeSafety/safety.cpp b/clang/test/Sema/LifetimeSafety/safety.cpp
index 47e65993c9a76..7e08defcbe96d 100644
--- a/clang/test/Sema/LifetimeSafety/safety.cpp
+++ b/clang/test/Sema/LifetimeSafety/safety.cpp
@@ -3970,4 +3970,45 @@ PtrWithInt f() {
// Since it is off, we expect NO warnings or notes here.
View suggestion_disabled_test(View a) {
return a;
+
+//===----------------------------------------------------------------------===//
+// buildOriginFlowChain
+//===----------------------------------------------------------------------===//
+
+void used_variable_reassigned() {
+ View p, q, r;
+ {
+ MyObj a;
+ p = a; // expected-warning {{local variable 'a' does not live long enough}}
+ q = p; // expected-note {{local variable 'p' aliases the storage of local variable 'a'}}
+ r = q; // expected-note {{local variable 'q' aliases the storage of local variable 'a'}}
+ } // expected-note {{destroyed here}}
+ r.use(); // expected-note {{later used here}}
+
+ MyObj b;
+ r = b;
+ r.use();
+}
+
+void multi_reassigned(bool condition) {
+ MyObj v1, v2, v3;
+ View p1, p2, p3, p4;
+ {
+ MyObj v4;
+
+ p1 = v1;
+ p2 = v2;
+ p3 = v3;
+ p4 = v4; // expected-warning {{local variable 'v4' does not live long enough}}
+
+ while (condition) {
+ View temp = p1;
+ p1 = p2; // expected-note {{local variable 'p2' aliases the storage of local variable 'v4'}}
+ p2 = p3; // expected-note {{local variable 'p3' aliases the storage of local variable 'v4'}}
+ p3 = p4; // expected-note {{local variable 'p4' aliases the storage of local variable 'v4'}}
+ p4 = temp;
+ }
+ } // expected-note {{destroyed here}}
+
+ p1.use(); // expected-note {{later used here}}
}
>From 255ca7f00a34fe3ad9d4c8897f908f96593baca5 Mon Sep 17 00:00:00 2001
From: Yuan Suo <suoyuan666 at s5n.xyz>
Date: Tue, 23 Jun 2026 21:48:00 +0800
Subject: [PATCH 14/21] rebase
Signed-off-by: Yuan Suo <suoyuan666 at s5n.xyz>
---
clang/test/Sema/LifetimeSafety/safety.cpp | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/clang/test/Sema/LifetimeSafety/safety.cpp b/clang/test/Sema/LifetimeSafety/safety.cpp
index 7e08defcbe96d..7d8249ced4d92 100644
--- a/clang/test/Sema/LifetimeSafety/safety.cpp
+++ b/clang/test/Sema/LifetimeSafety/safety.cpp
@@ -236,7 +236,7 @@ void overrides_potential(bool cond) {
{
MyObj s;
q = &s; // expected-warning {{does not live long enough}}
- p = q;
+ p = q; // expected-note {{local variable 'q' aliases the storage of local variable 's'}}
} // expected-note {{local variable 's' is destroyed here}}
if (cond) {
@@ -1236,8 +1236,11 @@ void conditional_operator_lifetimebound_nested(bool cond) {
MyObj* p;
{
MyObj a, b;
- p = Identity(cond ? Identity(&a) // expected-warning {{local variable 'a' does not live long enough}}
- : Identity(&b)); // expected-warning {{local variable 'b' does not live long enough}}
+ p = Identity(cond ? Identity(&a) // expected-warning {{local variable 'a' does not live long enough}} \
+ // expected-note 2 {{result of call to 'Identity' aliases the storage of local variable 'a'}} \
+ // expected-note {{result of call to 'Identity' aliases the storage of local variable 'b'}}
+ : Identity(&b)); // expected-warning {{local variable 'b' does not live long enough}} \
+ // expected-note {{result of call to 'Identity' aliases the storage of local variable 'b'}}
} // expected-note {{local variable 'b' is destroyed here}} expected-note {{local variable 'a' is destroyed here}}
(void)*p; // expected-note 2 {{later used here}}
}
@@ -2697,8 +2700,7 @@ void chained_defaulted_assignment_propagation() {
std::string str{"abc"};
S a = getS(str); // expected-warning {{local variable 'str' does not live long enough}} \
// expected-note {{result of call to 'getS' aliases the storage of local variable 'str'}}
- c = b = a; // expected-note {{local variable 'a' aliases the storage of local variable 'str'}}\
- // expected-note {{expression aliases the storage of local variable 'str'}}
+ c = b = a; // expected-note {{local variable 'a' aliases the storage of local variable 'str'}}
} // expected-note {{local variable 'str' is destroyed here}}
use(c); // expected-note {{later used here}}
}
>From 57b4eeb43d17cb0c8b1ca46aec3f6255d9a2099a Mon Sep 17 00:00:00 2001
From: Yuan Suo <suoyuan666 at s5n.xyz>
Date: Tue, 23 Jun 2026 22:15:43 +0800
Subject: [PATCH 15/21] Add a test case for function pointer
Signed-off-by: Yuan Suo <suoyuan666 at s5n.xyz>
---
clang/test/Sema/LifetimeSafety/safety.cpp | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/clang/test/Sema/LifetimeSafety/safety.cpp b/clang/test/Sema/LifetimeSafety/safety.cpp
index 7d8249ced4d92..da9e2ac13f8ea 100644
--- a/clang/test/Sema/LifetimeSafety/safety.cpp
+++ b/clang/test/Sema/LifetimeSafety/safety.cpp
@@ -855,6 +855,18 @@ void lifetimebound_multiple_args_potential(bool cond) {
v.use(); // expected-note 2 {{later used here}}
}
+// FIXME: Detect this.
+void func_pointer() {
+ View p;
+ View (*func_ptr)(View v [[clang::lifetimebound]]);
+ {
+ MyObj s;
+ View a = Identity(s);
+ p = func_ptr(a);
+ }
+ p.use();
+}
+
View SelectFirst(View a [[clang::lifetimebound]], View b);
void lifetimebound_mixed_args() {
View v;
>From 9966260eaa3f270c38dd55d1fbd284d0a22fb1bb Mon Sep 17 00:00:00 2001
From: Yuan Suo <suoyuan666 at s5n.xyz>
Date: Tue, 23 Jun 2026 22:22:16 +0800
Subject: [PATCH 16/21] Remove unused include
Signed-off-by: Yuan Suo <suoyuan666 at s5n.xyz>
---
clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp | 1 -
1 file changed, 1 deletion(-)
diff --git a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
index 2d9acbd822f16..212c98720165f 100644
--- a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
@@ -18,7 +18,6 @@
#include "clang/Analysis/CFG.h"
#include "clang/Basic/LLVM.h"
#include "llvm/ADT/BitVector.h"
-#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/TimeProfiler.h"
#include "llvm/Support/raw_ostream.h"
>From 86d80ca4990d7378ad3d5b4ece45da1aaf9d7858 Mon Sep 17 00:00:00 2001
From: Yuan Suo <suoyuan666 at s5n.xyz>
Date: Thu, 25 Jun 2026 15:42:56 +0800
Subject: [PATCH 17/21] Imporve unit test case
Signed-off-by: Yuan Suo <suoyuan666 at s5n.xyz>
---
clang/unittests/Analysis/LifetimeSafetyTest.cpp | 2 ++
1 file changed, 2 insertions(+)
diff --git a/clang/unittests/Analysis/LifetimeSafetyTest.cpp b/clang/unittests/Analysis/LifetimeSafetyTest.cpp
index d79161ae1db54..57cf7068affae 100644
--- a/clang/unittests/Analysis/LifetimeSafetyTest.cpp
+++ b/clang/unittests/Analysis/LifetimeSafetyTest.cpp
@@ -1996,6 +1996,8 @@ TEST_F(LifetimeAnalysisTest, BuildOriginFlowChain) {
POINT(after_nested_merge);
(void)*s;
+ int reset;
+ s = &reset;
}
)");
>From b7d12e45b87a3c5f14ab51a3f1dbc76836ac0273 Mon Sep 17 00:00:00 2001
From: Yuan Suo <suoyuan666 at s5n.xyz>
Date: Thu, 25 Jun 2026 15:43:57 +0800
Subject: [PATCH 18/21] PendingState -> PendingBlocks
Signed-off-by: Yuan Suo <suoyuan666 at s5n.xyz>
---
.../lib/Analysis/LifetimeSafety/LoanPropagation.cpp | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
index 212c98720165f..8bde1f9c84ef8 100644
--- a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
@@ -231,12 +231,12 @@ class AnalysisImpl
OriginID CurrOID = StartOID;
llvm::SmallVector<OriginID> OriginFlowChain;
- std::queue<const CFGBlock *> PendingState;
- PendingState.push(EndBlock);
+ std::queue<const CFGBlock *> PendingBlocks;
+ PendingBlocks.push(EndBlock);
- while (!PendingState.empty()) {
- const CFGBlock *CurrBlock = PendingState.front();
- PendingState.pop();
+ while (!PendingBlocks.empty()) {
+ const CFGBlock *CurrBlock = PendingBlocks.front();
+ PendingBlocks.pop();
DEBUG_WITH_TYPE("LifetimeBuildOriginFlow",
llvm::dbgs() << "CurrBlockID: " << CurrBlock->getBlockID()
@@ -256,7 +256,7 @@ class AnalysisImpl
llvm::dbgs() << "EndOriginID: " << CurrOID << "\n");
for (const CFGBlock *Block : CurrBlock->preds())
- PendingState.push(Block);
+ PendingBlocks.push(Block);
}
llvm_unreachable(
>From 0e3b015b5d238a24d474aabd62f3848408439193 Mon Sep 17 00:00:00 2001
From: Yuan Suo <suoyuan666 at s5n.xyz>
Date: Thu, 25 Jun 2026 15:50:31 +0800
Subject: [PATCH 19/21] Merge first debug statement
Signed-off-by: Yuan Suo <suoyuan666 at s5n.xyz>
---
.../Analysis/LifetimeSafety/LoanPropagation.cpp | 14 +++++---------
1 file changed, 5 insertions(+), 9 deletions(-)
diff --git a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
index 8bde1f9c84ef8..c75a1a0216183 100644
--- a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
@@ -211,15 +211,11 @@ class AnalysisImpl
DEBUG_WITH_TYPE("LifetimeBuildOriginFlow",
llvm::dbgs()
- << "==========================================\n");
- DEBUG_WITH_TYPE("LifetimeBuildOriginFlow",
- llvm::dbgs() << " Lifetime Analysis buildOriginFlow\n");
- DEBUG_WITH_TYPE("LifetimeBuildOriginFlow",
- llvm::dbgs()
- << "==========================================\n");
- DEBUG_WITH_TYPE("LifetimeBuildOriginFlow",
- llvm::dbgs() << "StartOriginID: " << StartOID
- << ", TargetLoanID: " << TargetLoan << "\n\n");
+ << "==========================================\n"
+ << " Lifetime Analysis buildOriginFlow\n"
+ << "==========================================\n"
+ << "StartOriginID: " << StartOID
+ << ", TargetLoanID: " << TargetLoan << "\n\n");
const CFGBlock *EndBlock = nullptr;
size_t BlockID = FactMgr.getBlockID(StartPoint);
>From 42ae7f5b8be7ceb869b5fc69c3538d970f85ad86 Mon Sep 17 00:00:00 2001
From: Yuan Suo <suoyuan666 at s5n.xyz>
Date: Fri, 26 Jun 2026 20:23:04 +0800
Subject: [PATCH 20/21] Add function comments and rebase
Signed-off-by: Yuan Suo <suoyuan666 at s5n.xyz>
---
.../Analysis/Analyses/LifetimeSafety/LoanPropagation.h | 7 +++++--
clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp | 8 ++++++++
clang/test/Sema/LifetimeSafety/safety.cpp | 1 +
3 files changed, 14 insertions(+), 2 deletions(-)
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
index 2c3357125810c..8bc7755f815c7 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
@@ -40,8 +40,11 @@ class LoanPropagationAnalysis {
/// Builds the chain of origins through which a loan has propagated.
///
- /// Starting from the last fact of the block containg StartPoint,
- /// this function traces backwards through OriginFlowFacts to identify the
+ /// Starting from the last fact of the block containing StartPoint, this
+ /// function performs a BFS over CFG blocks to explore all reachable blocks.
+ /// Within each block, facts are processed in reverse order.
+ ///
+ /// The traversal follows OriginFlowFacts backwards to reconstruct the
/// sequence of origins through which the loan flowed, ending at the origin
/// where the loan was originally issued.
llvm::SmallVector<OriginID> buildOriginFlowChain(ProgramPoint StartPoint,
diff --git a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
index c75a1a0216183..4914b31dd66f8 100644
--- a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
@@ -293,6 +293,14 @@ class AnalysisImpl
return LoanSetFactory.getEmptySet();
}
+ /// Builds the chain of origins through which a loan has propagated.
+ ///
+ /// This procedure operates strictly within a single Block. Starting from the
+ /// last fact of the Block, it traces backwards through OriginFlowFacts to
+ /// identify the sequence of origins through which the loan flowed.
+ ///
+ /// Returns (chain, true) if the target loan origin is found during the
+ /// traversal, otherwise returns (chain, false).
std::pair<llvm::SmallVector<OriginID>, bool>
buildOriginFlowChain(const CFGBlock *Block, const OriginID StartOID,
const LoanID TargetLoan) const {
diff --git a/clang/test/Sema/LifetimeSafety/safety.cpp b/clang/test/Sema/LifetimeSafety/safety.cpp
index da9e2ac13f8ea..02433bad137c0 100644
--- a/clang/test/Sema/LifetimeSafety/safety.cpp
+++ b/clang/test/Sema/LifetimeSafety/safety.cpp
@@ -3984,6 +3984,7 @@ PtrWithInt f() {
// Since it is off, we expect NO warnings or notes here.
View suggestion_disabled_test(View a) {
return a;
+}
//===----------------------------------------------------------------------===//
// buildOriginFlowChain
>From 911c316d9308534f128d8781fe13f2aa7e961ec6 Mon Sep 17 00:00:00 2001
From: Yuan Suo <suoyuan666 at s5n.xyz>
Date: Fri, 26 Jun 2026 20:34:49 +0800
Subject: [PATCH 21/21] Use <Block, OriginID> as search state and add visited
state tracking
Signed-off-by: Yuan Suo <suoyuan666 at s5n.xyz>
---
.../LifetimeSafety/LoanPropagation.cpp | 22 ++++++++++++-------
1 file changed, 14 insertions(+), 8 deletions(-)
diff --git a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
index 4914b31dd66f8..8c73a985fc975 100644
--- a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
@@ -18,6 +18,7 @@
#include "clang/Analysis/CFG.h"
#include "clang/Basic/LLVM.h"
#include "llvm/ADT/BitVector.h"
+#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/TimeProfiler.h"
#include "llvm/Support/raw_ostream.h"
@@ -225,14 +226,16 @@ class AnalysisImpl
break;
}
- OriginID CurrOID = StartOID;
llvm::SmallVector<OriginID> OriginFlowChain;
- std::queue<const CFGBlock *> PendingBlocks;
- PendingBlocks.push(EndBlock);
- while (!PendingBlocks.empty()) {
- const CFGBlock *CurrBlock = PendingBlocks.front();
- PendingBlocks.pop();
+ using SearchState = std::pair<const CFGBlock *, OriginID>;
+ std::queue<SearchState> PendingStates;
+ llvm::SmallSet<SearchState, 16> VistedStates;
+ PendingStates.push({EndBlock, StartOID});
+
+ while (!PendingStates.empty()) {
+ auto [CurrBlock, CurrOID] = PendingStates.front();
+ PendingStates.pop();
DEBUG_WITH_TYPE("LifetimeBuildOriginFlow",
llvm::dbgs() << "CurrBlockID: " << CurrBlock->getBlockID()
@@ -251,8 +254,11 @@ class AnalysisImpl
DEBUG_WITH_TYPE("LifetimeBuildOriginFlow",
llvm::dbgs() << "EndOriginID: " << CurrOID << "\n");
- for (const CFGBlock *Block : CurrBlock->preds())
- PendingBlocks.push(Block);
+ for (const CFGBlock *Block : CurrBlock->preds()) {
+ SearchState NextState = {Block, CurrOID};
+ if (VistedStates.insert(NextState).second)
+ PendingStates.push(NextState);
+ }
}
llvm_unreachable(
More information about the cfe-commits
mailing list