[clang] [LifetimeSafety] Use ImmutableList for buildOriginFlowChain DFS nodes (PR #208890)
Yuan Suo via cfe-commits
cfe-commits at lists.llvm.org
Sat Jul 11 01:44:00 PDT 2026
https://github.com/suoyuan666 created https://github.com/llvm/llvm-project/pull/208890
This commit mainly address the suggestion in https://github.com/llvm/llvm-project/pull/204592#discussion_r3523582077 to use `ImmutableList` for the DFS nodes in `buildOriginFlowChain`, and also updates the unreachable messages.
>From 9d667b1da7631efd70dcfa1723ff689d6b3a2948 Mon Sep 17 00:00:00 2001
From: Yuan Suo <suoyuan666 at s5n.xyz>
Date: Sat, 11 Jul 2026 16:22:16 +0800
Subject: [PATCH] [LifetimeSafety] Use ImmutableList for buildOriginFlowChain
DFS nodes
This commit mainly address the suggestion in https://github.com/llvm/llvm-project/pull/204592#discussion_r3523582077 to use `ImmutableList` for the DFS nodes in `buildOriginFlowChain`, and also updates the unreachable messages.
Signed-off-by: Yuan Suo <suoyuan666 at s5n.xyz>
---
.../Analyses/LifetimeSafety/LoanPropagation.h | 23 +++++-----
clang/lib/Analysis/LifetimeSafety/Checker.cpp | 2 +-
.../LifetimeSafety/LifetimeSafety.cpp | 5 ++-
.../LifetimeSafety/LoanPropagation.cpp | 42 +++++++++++--------
clang/lib/Sema/SemaLifetimeSafety.h | 4 +-
.../unittests/Analysis/LifetimeSafetyTest.cpp | 22 +++++-----
6 files changed, 56 insertions(+), 42 deletions(-)
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
index 838daa024c953..3d489965a5aa6 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
@@ -18,6 +18,7 @@
#include "clang/Analysis/Analyses/LifetimeSafety/Facts.h"
#include "clang/Analysis/AnalysisDeclContext.h"
#include "clang/Analysis/CFG.h"
+#include "llvm/ADT/ImmutableList.h"
#include "llvm/ADT/ImmutableMap.h"
#include "llvm/ADT/ImmutableSet.h"
@@ -31,9 +32,11 @@ using OriginLoanMap = llvm::ImmutableMap<OriginID, LoanSet>;
class LoanPropagationAnalysis {
public:
- LoanPropagationAnalysis(const CFG &C, AnalysisDeclContext &AC, FactManager &F,
- OriginLoanMap::Factory &OriginLoanMapFactory,
- LoanSet::Factory &LoanSetFactory);
+ LoanPropagationAnalysis(
+ const CFG &C, AnalysisDeclContext &AC, FactManager &F,
+ OriginLoanMap::Factory &OriginLoanMapFactory,
+ LoanSet::Factory &LoanSetFactory,
+ llvm::ImmutableList<OriginID>::Factory &OriginFlowChainFactory);
~LoanPropagationAnalysis();
LoanSet getLoans(OriginID OID, ProgramPoint P) const;
@@ -47,14 +50,14 @@ class LoanPropagationAnalysis {
/// 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,
- const OriginID StartOID,
- const LoanID TargetLoan,
- const CFG *Cfg) const;
+ llvm::ImmutableList<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;
+ llvm::ImmutableList<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 f72f7f80abbc0..3b7248b28b945 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -527,7 +527,7 @@ class LifetimeChecker {
/// extracts the corresponding expressions for each origin. Origins that refer
/// to declarations (rather than expressions) are skipped.
llvm::SmallVector<const Expr *>
- getExprChain(llvm::ArrayRef<OriginID> OriginFlowChain) {
+ getExprChain(llvm::ImmutableList<OriginID> OriginFlowChain) {
llvm::SmallVector<const Expr *> rs;
for (const OriginID CurrOID : OriginFlowChain)
if (const Expr *CurrExpr =
diff --git a/clang/lib/Analysis/LifetimeSafety/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety/LifetimeSafety.cpp
index 798812327d3ab..50fc8e033d81b 100644
--- a/clang/lib/Analysis/LifetimeSafety/LifetimeSafety.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LifetimeSafety.cpp
@@ -86,8 +86,11 @@ void LifetimeSafetyAnalysis::run() {
/// the analysis.
/// 3. Collapse ExpireFacts belonging to same source location into a single
/// Fact.
+
+ llvm::ImmutableList<OriginID>::Factory OriginFlowChainFactory;
LoanPropagation = std::make_unique<LoanPropagationAnalysis>(
- Cfg, AC, *FactMgr, Factory.OriginMapFactory, Factory.LoanSetFactory);
+ Cfg, AC, *FactMgr, Factory.OriginMapFactory, Factory.LoanSetFactory,
+ OriginFlowChainFactory);
LiveOrigins = std::make_unique<LiveOriginsAnalysis>(
Cfg, AC, *FactMgr, Factory.LivenessMapFactory);
diff --git a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
index 078892bd48c10..56e17c080db3d 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/ImmutableList.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/TimeProfiler.h"
@@ -136,9 +137,11 @@ class AnalysisImpl
public:
AnalysisImpl(const CFG &C, AnalysisDeclContext &AC, FactManager &F,
OriginLoanMap::Factory &OriginLoanMapFactory,
- LoanSet::Factory &LoanSetFactory)
+ LoanSet::Factory &LoanSetFactory,
+ llvm::ImmutableList<OriginID>::Factory &OriginFlowChainFactory)
: DataflowAnalysis(C, AC, F), OriginLoanMapFactory(OriginLoanMapFactory),
LoanSetFactory(LoanSetFactory),
+ OriginFlowChainFactory(OriginFlowChainFactory),
PersistentOrigins(computePersistentOrigins(F, C)) {}
using Base::transfer;
@@ -203,10 +206,10 @@ class AnalysisImpl
return getLoans(getState(P), OID);
}
- llvm::SmallVector<OriginID> buildOriginFlowChain(ProgramPoint StartPoint,
- const OriginID StartOID,
- const LoanID TargetLoan,
- const CFG *Cfg) const {
+ llvm::ImmutableList<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");
@@ -225,12 +228,13 @@ class AnalysisImpl
using SearchState = std::pair<const CFGBlock *, OriginID>;
struct DFSNode {
SearchState CurrState;
- llvm::SmallVector<OriginID> OriginFlowChain;
+ llvm::ImmutableList<OriginID> OriginFlowChain;
};
llvm::SmallVector<DFSNode> PendingStates;
llvm::SmallSet<SearchState, 16> VistedStates;
- PendingStates.push_back({{EndBlock, StartOID}, {}});
+ PendingStates.push_back(
+ {{EndBlock, StartOID}, OriginFlowChainFactory.getEmptyList()});
// DFS loop to trace loan backwards through CFG
while (!PendingStates.empty()) {
@@ -241,7 +245,9 @@ class AnalysisImpl
const auto [BuildResult, Complete] =
buildOriginFlowChain(CurrBlock, CurrOID, TargetLoan);
if (!BuildResult.empty()) {
- CurrNode.OriginFlowChain.append(BuildResult);
+ for (OriginID OID : BuildResult)
+ CurrNode.OriginFlowChain =
+ OriginFlowChainFactory.add(OID, CurrNode.OriginFlowChain);
CurrOID = BuildResult.back();
}
@@ -259,13 +265,13 @@ class AnalysisImpl
}
}
- llvm_unreachable(
- "buildOriginFlowChain did not reach IssueFact for TargetLoan");
+ llvm_unreachable("Could not reconstruct origin flow. Search finished "
+ "without reaching IssueFact");
}
- llvm::SmallVector<OriginID> buildOriginFlowChain(const UseFact *UF,
- const LoanID TargetLoan,
- const CFG *Cfg) const {
+ llvm::ImmutableList<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))
@@ -333,6 +339,7 @@ class AnalysisImpl
OriginLoanMap::Factory &OriginLoanMapFactory;
LoanSet::Factory &LoanSetFactory;
+ llvm::ImmutableList<OriginID>::Factory &OriginFlowChainFactory;
/// Boolean vector indexed by origin ID. If true, the origin appears in
/// multiple basic blocks and must participate in join operations. If false,
/// the origin is block-local and can be discarded at block boundaries.
@@ -347,9 +354,10 @@ class LoanPropagationAnalysis::Impl final : public AnalysisImpl {
LoanPropagationAnalysis::LoanPropagationAnalysis(
const CFG &C, AnalysisDeclContext &AC, FactManager &F,
OriginLoanMap::Factory &OriginLoanMapFactory,
- LoanSet::Factory &LoanSetFactory)
+ LoanSet::Factory &LoanSetFactory,
+ llvm::ImmutableList<OriginID>::Factory &OriginFlowChainFactory)
: PImpl(std::make_unique<Impl>(C, AC, F, OriginLoanMapFactory,
- LoanSetFactory)) {
+ LoanSetFactory, OriginFlowChainFactory)) {
PImpl->run();
}
@@ -359,13 +367,13 @@ LoanSet LoanPropagationAnalysis::getLoans(OriginID OID, ProgramPoint P) const {
return PImpl->getLoans(OID, P);
}
-llvm::SmallVector<OriginID> LoanPropagationAnalysis::buildOriginFlowChain(
+llvm::ImmutableList<OriginID> LoanPropagationAnalysis::buildOriginFlowChain(
ProgramPoint StartPoint, const OriginID StartOID, const LoanID TargetLoan,
const CFG *Cfg) const {
return PImpl->buildOriginFlowChain(StartPoint, StartOID, TargetLoan, Cfg);
}
-llvm::SmallVector<OriginID> LoanPropagationAnalysis::buildOriginFlowChain(
+llvm::ImmutableList<OriginID> LoanPropagationAnalysis::buildOriginFlowChain(
const UseFact *UF, const LoanID TargetLoan, const CFG *Cfg) const {
return PImpl->buildOriginFlowChain(UF, TargetLoan, Cfg);
}
diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h
index 48edf5f3f070f..c36402b393729 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -668,10 +668,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 57cf7068affae..cb69b440c482c 100644
--- a/clang/unittests/Analysis/LifetimeSafetyTest.cpp
+++ b/clang/unittests/Analysis/LifetimeSafetyTest.cpp
@@ -205,18 +205,18 @@ class LifetimeTestHelper {
return Runner.getAnalysis().getFactManager().getBlockContaining(P);
}
- llvm::SmallVector<OriginID>
+ llvm::ImmutableList<OriginID>
buildOriginFlowChain(llvm::StringRef StartOriginVar,
llvm::StringRef EndLoanVar, llvm::StringRef Annotation) {
std::optional<OriginID> StartOriginID = getOriginForDecl(StartOriginVar);
std::vector<LoanID> EndLoanIDs = getLoansForVar(EndLoanVar);
for (LoanID LID : EndLoanIDs) {
- llvm::SmallVector<OriginID> OriginFlowChain =
+ llvm::ImmutableList<OriginID> OriginFlowChain =
Runner.getAnalysis().getLoanPropagation().buildOriginFlowChain(
getProgramPoint(Annotation), *StartOriginID, LID,
Runner.getAnalysisContext().getCFG());
- if (!OriginFlowChain.empty())
+ if (!OriginFlowChain.isEmpty())
return OriginFlowChain;
}
@@ -2001,11 +2001,11 @@ TEST_F(LifetimeAnalysisTest, BuildOriginFlowChain) {
}
)");
- llvm::SmallVector<OriginID> ChainForTgtA =
+ llvm::ImmutableList<OriginID> ChainForTgtA =
Helper->buildOriginFlowChain("s", "tgta", "after_nested_merge");
- llvm::SmallVector<OriginID> ChainForTgtB =
+ llvm::ImmutableList<OriginID> ChainForTgtB =
Helper->buildOriginFlowChain("s", "tgtb", "after_nested_merge");
- llvm::SmallVector<OriginID> ChainForTgtC =
+ llvm::ImmutableList<OriginID> ChainForTgtC =
Helper->buildOriginFlowChain("s", "tgtc", "after_nested_merge");
EXPECT_THAT(ChainForTgtA, Contains(*Helper->getOriginForDecl("a")));
@@ -2050,7 +2050,7 @@ TEST_F(LifetimeAnalysisTest, BuildOriginFlowChainWithSelfAssignment) {
}
)");
- const llvm::SmallVector<OriginID> OriginFlowChain =
+ const llvm::ImmutableList<OriginID> OriginFlowChain =
Helper->buildOriginFlowChain("s", "tgt", "after_use");
EXPECT_THAT(OriginFlowChain, Contains(*Helper->getOriginForDecl("a")));
@@ -2067,7 +2067,7 @@ TEST_F(LifetimeAnalysisTest, BuildOriginFlowChainWithMultiAssignInSameStmt) {
}
)");
- const llvm::SmallVector<OriginID> OriginFlowChain =
+ const llvm::ImmutableList<OriginID> OriginFlowChain =
Helper->buildOriginFlowChain("s", "tgt", "after_use");
EXPECT_THAT(OriginFlowChain, Contains(*Helper->getOriginForDecl("a")));
@@ -2088,7 +2088,7 @@ TEST_F(LifetimeAnalysisTest, BuildOriginFlowChainWithOverwritingAssignments) {
}
)");
- const llvm::SmallVector<OriginID> OriginFlowChain =
+ const llvm::ImmutableList<OriginID> OriginFlowChain =
Helper->buildOriginFlowChain("s", "tgt1", "after_use");
EXPECT_THAT(OriginFlowChain, Contains(*Helper->getOriginForDecl("a")));
@@ -2110,9 +2110,9 @@ TEST_F(LifetimeAnalysisTest, BuildOriginFlowChainWithLifetimeBound) {
}
)");
- llvm::SmallVector<OriginID> ChainForTgtA =
+ llvm::ImmutableList<OriginID> ChainForTgtA =
Helper->buildOriginFlowChain("s", "tgta", "after_use");
- llvm::SmallVector<OriginID> ChainForTgtB =
+ llvm::ImmutableList<OriginID> ChainForTgtB =
Helper->buildOriginFlowChain("s", "tgtb", "after_use");
EXPECT_THAT(ChainForTgtA, Contains(*Helper->getOriginForDecl("a")));
More information about the cfe-commits
mailing list