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
2122namespace 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).
3736struct 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 {
5892public:
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
81151private:
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
0 commit comments