Skip to content

Commit 67d76ba

Browse files
committed
Tree -> List
1 parent 3036de7 commit 67d76ba

File tree

14 files changed

+1115
-639
lines changed

14 files changed

+1115
-639
lines changed

clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -155,18 +155,19 @@ class OriginEscapesFact : public Fact {
155155

156156
class UseFact : public Fact {
157157
const Expr *UseExpr;
158-
OriginID OID;
158+
// The origins of the expression being used.
159+
llvm::SmallVector<OriginID, 1> OIDs;
159160
// True if this use is a write operation (e.g., left-hand side of assignment).
160161
// Write operations are exempted from use-after-free checks.
161162
bool IsWritten = false;
162163

163164
public:
164165
static bool classof(const Fact *F) { return F->getKind() == Kind::Use; }
165166

166-
UseFact(const Expr *UseExpr, OriginManager &OM)
167-
: Fact(Kind::Use), UseExpr(UseExpr), OID(OM.get(*UseExpr)) {}
167+
UseFact(const Expr *UseExpr, llvm::ArrayRef<OriginID> OIDs)
168+
: Fact(Kind::Use), UseExpr(UseExpr), OIDs(OIDs.begin(), OIDs.end()) {}
168169

169-
OriginID getUsedOrigin() const { return OID; }
170+
llvm::ArrayRef<OriginID> getUsedOrigins() const { return OIDs; }
170171
const Expr *getUseExpr() const { return UseExpr; }
171172
void markAsWritten() { IsWritten = true; }
172173
bool isWritten() const { return IsWritten; }
@@ -194,8 +195,8 @@ class TestPointFact : public Fact {
194195

195196
class FactManager {
196197
public:
197-
void init(const CFG &Cfg) {
198-
assert(BlockToFacts.empty() && "FactManager already initialized");
198+
FactManager(const AnalysisDeclContext &AC, const CFG &Cfg)
199+
: OriginMgr(AC.getASTContext()) {
199200
BlockToFacts.resize(Cfg.getNumBlockIDs());
200201
}
201202

clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ class FactsGenerator : public ConstStmtVisitor<FactsGenerator> {
5050
void VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *MTE);
5151

5252
private:
53+
OriginList *getOriginsList(const ValueDecl &D);
54+
OriginList *getOriginsList(const Expr &E);
55+
56+
void flow(OriginList *Dst, OriginList *Src, bool Kill);
57+
5358
void handleLifetimeEnds(const CFGLifetimeEnds &LifetimeEnds);
5459

5560
void handleGSLPointerConstruction(const CXXConstructExpr *CCE);
@@ -64,26 +69,18 @@ class FactsGenerator : public ConstStmtVisitor<FactsGenerator> {
6469

6570
template <typename Destination, typename Source>
6671
void flowOrigin(const Destination &D, const Source &S) {
67-
OriginID DestOID = FactMgr.getOriginMgr().getOrCreate(D);
68-
OriginID SrcOID = FactMgr.getOriginMgr().get(S);
69-
CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>(
70-
DestOID, SrcOID, /*KillDest=*/false));
72+
flow(getOriginsList(D), getOriginsList(S), /*Kill=*/false);
7173
}
7274

7375
template <typename Destination, typename Source>
7476
void killAndFlowOrigin(const Destination &D, const Source &S) {
75-
OriginID DestOID = FactMgr.getOriginMgr().getOrCreate(D);
76-
OriginID SrcOID = FactMgr.getOriginMgr().get(S);
77-
CurrentBlockFacts.push_back(
78-
FactMgr.createFact<OriginFlowFact>(DestOID, SrcOID, /*KillDest=*/true));
77+
flow(getOriginsList(D), getOriginsList(S), /*Kill=*/true);
7978
}
8079

8180
/// Checks if the expression is a `void("__lifetime_test_point_...")` cast.
8281
/// If so, creates a `TestPointFact` and returns true.
8382
bool handleTestPoint(const CXXFunctionalCastExpr *FCE);
8483

85-
void handleAssignment(const Expr *LHSExpr, const Expr *RHSExpr);
86-
8784
// A DeclRefExpr will be treated as a use of the referenced decl. It will be
8885
// checked for use-after-free unless it is later marked as being written to
8986
// (e.g. on the left-hand side of an assignment).

clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,13 +76,13 @@ class LifetimeSafetyAnalysis {
7676
return *LoanPropagation;
7777
}
7878
LiveOriginsAnalysis &getLiveOrigins() const { return *LiveOrigins; }
79-
FactManager &getFactManager() { return FactMgr; }
79+
FactManager &getFactManager() { return *FactMgr; }
8080

8181
private:
8282
AnalysisDeclContext &AC;
8383
LifetimeSafetyReporter *Reporter;
8484
LifetimeFactory Factory;
85-
FactManager FactMgr;
85+
std::unique_ptr<FactManager> FactMgr;
8686
std::unique_ptr<LiveOriginsAnalysis> LiveOrigins;
8787
std::unique_ptr<LoanPropagationAnalysis> LoanPropagation;
8888
};

clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h

Lines changed: 110 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
#include "clang/AST/Decl.h"
1818
#include "clang/AST/Expr.h"
19+
#include "clang/AST/TypeBase.h"
1920
#include "clang/Analysis/Analyses/LifetimeSafety/Utils.h"
2021

2122
namespace clang::lifetimes::internal {
@@ -28,21 +29,30 @@ inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, OriginID ID) {
2829

2930
/// An Origin is a symbolic identifier that represents the set of possible
3031
/// loans a pointer-like object could hold at any given time.
31-
/// TODO: Enhance the origin model to handle complex types, pointer
32-
/// indirection and reborrowing. The plan is to move from a single origin per
33-
/// variable/expression to a "list of origins" governed by the Type.
34-
/// For example, the type 'int**' would have two origins.
35-
/// See discussion:
36-
/// https://github.com/llvm/llvm-project/pull/142313/commits/0cd187b01e61b200d92ca0b640789c1586075142#r2137644238
32+
///
33+
/// Each Origin corresponds to a single level of indirection. For complex types
34+
/// with multiple levels of indirection (e.g., `int**`), multiple Origins are
35+
/// organized into an OriginList structure (see below).
3736
struct Origin {
3837
OriginID ID;
3938
/// A pointer to the AST node that this origin represents. This union
4039
/// distinguishes between origins from declarations (variables or parameters)
4140
/// and origins from expressions.
4241
llvm::PointerUnion<const clang::ValueDecl *, const clang::Expr *> Ptr;
4342

44-
Origin(OriginID ID, const clang::ValueDecl *D) : ID(ID), Ptr(D) {}
45-
Origin(OriginID ID, const clang::Expr *E) : ID(ID), Ptr(E) {}
43+
/// The type at this indirection level.
44+
///
45+
/// For `int** pp`:
46+
/// Root origin: QT = `int**` (what pp points to)
47+
/// Pointee origin: QT = `int*` (what *pp points to)
48+
///
49+
/// Null for synthetic lvalue origins (e.g., outer origin of DeclRefExpr).
50+
const Type *Ty;
51+
52+
Origin(OriginID ID, const clang::ValueDecl *D, const Type *QT)
53+
: ID(ID), Ptr(D), Ty(QT) {}
54+
Origin(OriginID ID, const clang::Expr *E, const Type *QT)
55+
: ID(ID), Ptr(E), Ty(QT) {}
4656

4757
const clang::ValueDecl *getDecl() const {
4858
return Ptr.dyn_cast<const clang::ValueDecl *>();
@@ -52,41 +62,118 @@ struct Origin {
5262
}
5363
};
5464

55-
/// Manages the creation, storage, and retrieval of origins for pointer-like
56-
/// variables and expressions.
57-
class OriginManager {
65+
/// A list of origins representing levels of indirection for pointer-like types.
66+
///
67+
/// Each node in the list contains an OriginID representing a level of
68+
/// indirection. The list structure captures the multi-level nature of
69+
/// pointer and reference types in the lifetime analysis.
70+
///
71+
/// Examples:
72+
/// - For `int& x`, the list has size 2:
73+
/// * Root: origin for the reference storage itself (the lvalue `x`)
74+
/// * Pointee: origin for what `x` refers to
75+
///
76+
/// - For `int* p`, the list has size 2:
77+
/// * Root: origin for the pointer variable `p`
78+
/// * Pointee: origin for what `p` points to
79+
///
80+
/// - For `View v` (where View is gsl::Pointer), the list has size 2:
81+
/// * Root: origin for the view object itself
82+
/// * Pointee: origin for what the view refers to
83+
///
84+
/// - For `int** pp`, the list has size 3:
85+
/// * Root: origin for `pp` itself
86+
/// * Pointee: origin for `*pp` (what `pp` points to)
87+
/// * Pointee->Pointee: origin for `**pp` (what `*pp` points to)
88+
///
89+
/// The list structure enables the analysis to track how loans flow through
90+
/// different levels of indirection when assignments and dereferences occur.
91+
class OriginList {
5892
public:
59-
OriginManager() = default;
93+
OriginList(OriginID OID) : OuterOID(OID) {}
94+
95+
OriginList *peelOuterOrigin() { return InnerList; }
96+
OriginID getOuterOriginID() const { return OuterOID; }
6097

61-
Origin &addOrigin(OriginID ID, const clang::ValueDecl &D);
62-
Origin &addOrigin(OriginID ID, const clang::Expr &E);
98+
void setInnerOriginList(OriginList *Inner) { InnerList = Inner; }
6399

64-
// TODO: Mark this method as const once we remove the call to getOrCreate.
65-
OriginID get(const Expr &E);
100+
size_t getLength() const {
101+
size_t Length = 1;
102+
const OriginList *T = this;
103+
while (T->InnerList) {
104+
T = T->InnerList;
105+
Length++;
106+
}
107+
return Length;
108+
}
66109

67-
OriginID get(const ValueDecl &D);
110+
private:
111+
OriginID OuterOID;
112+
OriginList *InnerList = nullptr;
113+
};
68114

69-
OriginID getOrCreate(const Expr &E);
115+
bool hasOrigins(QualType QT);
116+
bool hasOrigins(const Expr *E);
117+
bool doesDeclHaveStorage(const ValueDecl *D);
118+
119+
/// Manages the creation, storage, and retrieval of origins for pointer-like
120+
/// variables and expressions.
121+
class OriginManager {
122+
public:
123+
explicit OriginManager(ASTContext &AST) : AST(AST) {}
124+
125+
/// Gets or creates the OriginList for a given ValueDecl.
126+
///
127+
/// Creates a list structure mirroring the levels of indirection in the
128+
/// declaration's type (e.g., `int** p` creates list of size 2).
129+
///
130+
/// \returns The OriginList, or nullptr if the type is not pointer-like.
131+
OriginList *getOrCreateList(const ValueDecl *D);
132+
133+
/// Gets or creates the OriginList for a given Expr.
134+
///
135+
/// Creates a list based on the expression's type and value category:
136+
/// - Lvalues get an implicit reference level (modeling addressability)
137+
/// - Rvalues of non-pointer type return nullptr (no trackable origin)
138+
/// - DeclRefExpr may reuse the underlying declaration's list
139+
///
140+
/// \returns The OriginList, or nullptr for non-pointer rvalues.
141+
OriginList *getOrCreateList(const Expr *E, size_t Depth = 0);
70142

71143
const Origin &getOrigin(OriginID ID) const;
72144

73145
llvm::ArrayRef<Origin> getOrigins() const { return AllOrigins; }
74146

75-
OriginID getOrCreate(const ValueDecl &D);
76-
77147
unsigned getNumOrigins() const { return NextOriginID.Value; }
78148

79149
void dump(OriginID OID, llvm::raw_ostream &OS) const;
80150

81151
private:
82152
OriginID getNextOriginID() { return NextOriginID++; }
83153

154+
OriginList *createNode(const ValueDecl *D, QualType QT) {
155+
OriginID NewID = getNextOriginID();
156+
AllOrigins.emplace_back(NewID, D, QT.getTypePtrOrNull());
157+
return new (ListAllocator.Allocate<OriginList>()) OriginList(NewID);
158+
}
159+
160+
OriginList *createNode(const Expr *E, QualType QT) {
161+
OriginID NewID = getNextOriginID();
162+
AllOrigins.emplace_back(NewID, E, QT.getTypePtrOrNull());
163+
return new (ListAllocator.Allocate<OriginList>()) OriginList(NewID);
164+
}
165+
166+
template <typename T>
167+
OriginList *buildListForType(QualType QT, const T *Node);
168+
169+
ASTContext &AST;
84170
OriginID NextOriginID{0};
85-
/// TODO(opt): Profile and evaluate the usefullness of small buffer
171+
/// TODO(opt): Profile and evaluate the usefulness of small buffer
86172
/// optimisation.
87173
llvm::SmallVector<Origin> AllOrigins;
88-
llvm::DenseMap<const clang::ValueDecl *, OriginID> DeclToOriginID;
89-
llvm::DenseMap<const clang::Expr *, OriginID> ExprToOriginID;
174+
llvm::BumpPtrAllocator ListAllocator;
175+
llvm::DenseMap<const clang::ValueDecl *, OriginList *> DeclToList;
176+
llvm::DenseMap<const clang::Expr *, OriginList *> ExprToList;
90177
};
91178
} // namespace clang::lifetimes::internal
92179

clang/lib/Analysis/LifetimeSafety/Facts.cpp

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,14 @@ void ExpireFact::dump(llvm::raw_ostream &OS, const LoanManager &LM,
3535

3636
void OriginFlowFact::dump(llvm::raw_ostream &OS, const LoanManager &,
3737
const OriginManager &OM) const {
38-
OS << "OriginFlow (Dest: ";
38+
OS << "OriginFlow: \n";
39+
OS << "\tDest: ";
3940
OM.dump(getDestOriginID(), OS);
40-
OS << ", Src: ";
41+
OS << "\n";
42+
OS << "\tSrc: ";
4143
OM.dump(getSrcOriginID(), OS);
4244
OS << (getKillDest() ? "" : ", Merge");
43-
OS << ")\n";
45+
OS << "\n";
4446
}
4547

4648
void OriginEscapesFact::dump(llvm::raw_ostream &OS, const LoanManager &,
@@ -53,7 +55,12 @@ void OriginEscapesFact::dump(llvm::raw_ostream &OS, const LoanManager &,
5355
void UseFact::dump(llvm::raw_ostream &OS, const LoanManager &,
5456
const OriginManager &OM) const {
5557
OS << "Use (";
56-
OM.dump(getUsedOrigin(), OS);
58+
size_t NumUsedOrigins = getUsedOrigins().size();
59+
for (size_t I = 0; I < NumUsedOrigins; ++I) {
60+
OM.dump(getUsedOrigins()[I], OS);
61+
if (I < NumUsedOrigins - 1)
62+
OS << ", ";
63+
}
5764
OS << ", " << (isWritten() ? "Write" : "Read") << ")\n";
5865
}
5966

0 commit comments

Comments
 (0)