clang 23.0.0git
Origins.cpp
Go to the documentation of this file.
1//===- Origins.cpp - Origin Implementation -----------------------*- C++-*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
11#include "clang/AST/Attr.h"
12#include "clang/AST/Decl.h"
13#include "clang/AST/DeclCXX.h"
15#include "clang/AST/Expr.h"
16#include "clang/AST/ExprCXX.h"
18#include "clang/AST/TypeBase.h"
22#include "llvm/ADT/StringMap.h"
23
25namespace {
26/// A utility class to traverse the function body in the analysis
27/// context and collect the count of expressions with missing origins.
28class MissingOriginCollector
29 : public RecursiveASTVisitor<MissingOriginCollector> {
30public:
31 MissingOriginCollector(
32 const llvm::DenseMap<const clang::Expr *, OriginList *> &ExprToOriginList,
33 const OriginManager &OM, LifetimeSafetyStats &LSStats)
34 : ExprToOriginList(ExprToOriginList), OM(OM), LSStats(LSStats) {}
35 bool VisitExpr(Expr *E) {
36 if (!OM.hasOrigins(E))
37 return true;
38 // Check if we have an origin for this expression.
39 if (!ExprToOriginList.contains(E)) {
40 // No origin found: count this as missing origin.
41 LSStats.ExprTypeToMissingOriginCount[E->getType().getTypePtr()]++;
42 LSStats.ExprStmtClassToMissingOriginCount[std::string(
43 E->getStmtClassName())]++;
44 }
45 return true;
46 }
47
48private:
49 const llvm::DenseMap<const clang::Expr *, OriginList *> &ExprToOriginList;
50 const OriginManager &OM;
51 LifetimeSafetyStats &LSStats;
52};
53
54class LifetimeAnnotatedOriginTypeCollector
55 : public RecursiveASTVisitor<LifetimeAnnotatedOriginTypeCollector> {
56public:
57 bool VisitCallExpr(const CallExpr *CE) {
58 // Indirect calls (e.g., function pointers) are skipped because lifetime
59 // annotations currently apply to declarations, not types.
60 if (const auto *FD = CE->getDirectCallee())
61 collect(FD, FD->getReturnType());
62 return true;
63 }
64
65 bool VisitCXXConstructExpr(const CXXConstructExpr *CCE) {
66 collect(CCE->getConstructor(), CCE->getType());
67 return true;
68 }
69
70 bool shouldVisitLambdaBody() const { return false; }
71 bool shouldVisitTemplateInstantiations() const { return true; }
72
73 const llvm::SmallVector<QualType> &getCollectedTypes() const {
74 return CollectedTypes;
75 }
76
77private:
78 llvm::SmallVector<QualType> CollectedTypes;
79
80 void collect(const FunctionDecl *FD, QualType RetType) {
81 if (!FD)
82 return;
84
85 if (const auto *MD = dyn_cast<CXXMethodDecl>(FD);
86 MD && MD->isInstance() && !isa<CXXConstructorDecl>(MD) &&
88 CollectedTypes.push_back(RetType);
89 return;
90 }
91
92 for (const auto *Param : FD->parameters()) {
93 if (Param->hasAttr<LifetimeBoundAttr>()) {
94 CollectedTypes.push_back(RetType);
95 return;
96 }
97 }
98 }
99};
100
101} // namespace
102
105 return true;
106 if (LifetimeAnnotatedOriginTypes.contains(QT.getCanonicalType().getTypePtr()))
107 return true;
108 const auto *RD = QT->getAsCXXRecordDecl();
109 if (!RD)
110 return false;
111 // Standard library callable wrappers (e.g., std::function) can propagate the
112 // stored lambda's origins.
114 return true;
115 // TODO: Limit to lambdas for now. This will be extended to user-defined
116 // structs with pointer-like fields.
117 if (!RD->isLambda())
118 return false;
119 for (const auto *FD : RD->fields())
120 if (hasOrigins(FD->getType()))
121 return true;
122 return false;
123}
124
125/// Determines if an expression has origins that need to be tracked.
126///
127/// An expression has origins if:
128/// - It's a glvalue (has addressable storage), OR
129/// - Its type is pointer-like (pointer, reference, or gsl::Pointer), OR
130/// - Its type is registered for origin tracking (e.g., return type of a
131/// [[clang::lifetimebound]] function)
132///
133/// Examples:
134/// - `int x; x` : has origin (glvalue)
135/// - `int* p; p` : has 2 origins (1 for glvalue and 1 for pointer type)
136/// - `std::string_view{}` : has 1 origin (prvalue of pointer type)
137/// - `42` : no origin (prvalue of non-pointer type)
138/// - `x + y` : (where x, y are int) → no origin (prvalue of non-pointer type)
139bool OriginManager::hasOrigins(const Expr *E) const {
140 return E->isGLValue() || hasOrigins(E->getType());
141}
142
143/// Returns true if the declaration has its own storage that can be borrowed.
144///
145/// References generally have no storage - they are aliases to other storage.
146/// For example:
147/// int x; // has storage (can issue loans to x's storage)
148/// int& r = x; // no storage (r is an alias to x's storage)
149/// int* p; // has storage (the pointer variable p itself has storage)
150///
151/// TODO: Handle lifetime extension. References initialized by temporaries
152/// can have storage when the temporary's lifetime is extended:
153/// const int& r = 42; // temporary has storage, lifetime extended
154/// Foo&& f = Foo{}; // temporary has storage, lifetime extended
155/// Currently, this function returns false for all reference types.
157 return !D->getType()->isReferenceType();
158}
159
161 : AST(AC.getASTContext()) {
162 collectLifetimeAnnotatedOriginTypes(AC);
163 initializeThisOrigins(AC.getDecl());
164}
165
166void OriginManager::initializeThisOrigins(const Decl *D) {
167 const auto *MD = llvm::dyn_cast_or_null<CXXMethodDecl>(D);
168 if (!MD || !MD->isInstance())
169 return;
170 // Lambdas can capture 'this' from the surrounding context, but in that case
171 // 'this' does not refer to the lambda object itself.
172 if (const CXXRecordDecl *P = MD->getParent(); P && P->isLambda())
173 return;
174 ThisOrigins = buildListForType(MD->getThisType(), MD);
175}
176
177OriginList *OriginManager::createNode(const ValueDecl *D, QualType QT) {
178 OriginID NewID = getNextOriginID();
179 AllOrigins.emplace_back(NewID, D, QT.getTypePtrOrNull());
180 return new (ListAllocator.Allocate<OriginList>()) OriginList(NewID);
181}
182
183OriginList *OriginManager::createNode(const Expr *E, QualType QT) {
184 OriginID NewID = getNextOriginID();
185 AllOrigins.emplace_back(NewID, E, QT.getTypePtrOrNull());
186 return new (ListAllocator.Allocate<OriginList>()) OriginList(NewID);
187}
188
190 return new (ListAllocator.Allocate<OriginList>()) OriginList(OID);
191}
192
193template <typename T>
194OriginList *OriginManager::buildListForType(QualType QT, const T *Node) {
195 assert(hasOrigins(QT) && "buildListForType called for non-pointer type");
196 OriginList *Head = createNode(Node, QT);
197
198 if (QT->isPointerOrReferenceType()) {
199 QualType PointeeTy = QT->getPointeeType();
200 // We recurse if the pointee type is pointer-like, to build the next
201 // level in the origin tree. E.g., for T*& / View&.
202 if (hasOrigins(PointeeTy))
203 Head->setInnerOriginList(buildListForType(PointeeTy, Node));
204 }
205 return Head;
206}
207
209 if (!hasOrigins(D->getType()))
210 return nullptr;
211 auto It = DeclToList.find(D);
212 if (It != DeclToList.end())
213 return It->second;
214 return DeclToList[D] = buildListForType(D->getType(), D);
215}
216
218 if (auto *ParenIgnored = E->IgnoreParens(); ParenIgnored != E)
219 return getOrCreateList(ParenIgnored);
220 // We do not see CFG stmts for ExprWithCleanups. Simply peel them.
221 if (const ExprWithCleanups *EWC = dyn_cast<ExprWithCleanups>(E))
222 return getOrCreateList(EWC->getSubExpr());
223
224 if (!hasOrigins(E))
225 return nullptr;
226
227 auto It = ExprToList.find(E);
228 if (It != ExprToList.end())
229 return It->second;
230
231 QualType Type = E->getType();
232 // Special handling for 'this' expressions to share origins with the method's
233 // implicit object parameter.
234 if (isa<CXXThisExpr>(E) && ThisOrigins)
235 return *ThisOrigins;
236
237 // Special handling for expressions referring to a decl to share origins with
238 // the underlying decl.
239 const ValueDecl *ReferencedDecl = nullptr;
240 if (auto *DRE = dyn_cast<DeclRefExpr>(E))
241 ReferencedDecl = DRE->getDecl();
242 else if (auto *ME = dyn_cast<MemberExpr>(E))
243 if (auto *Field = dyn_cast<FieldDecl>(ME->getMemberDecl());
244 Field && isa<CXXThisExpr>(ME->getBase()->IgnoreParenImpCasts()))
245 ReferencedDecl = Field;
246 if (ReferencedDecl) {
247 OriginList *Head = nullptr;
248 // For non-reference declarations (e.g., `int* p`), the expression is an
249 // lvalue (addressable) that can be borrowed, so we create an outer origin
250 // for the lvalue itself, with the pointee being the declaration's list.
251 // This models taking the address: `&p` borrows the storage of `p`, not what
252 // `p` points to.
253 if (doesDeclHaveStorage(ReferencedDecl)) {
254 Head = createNode(E, QualType{});
255 // This ensures origin sharing: multiple expressions to the same
256 // declaration share the same underlying origins.
257 Head->setInnerOriginList(getOrCreateList(ReferencedDecl));
258 } else {
259 // For reference-typed declarations (e.g., `int& r = p`) which have no
260 // storage, the DeclRefExpr directly reuses the declaration's list since
261 // references don't add an extra level of indirection at the expression
262 // level.
263 Head = getOrCreateList(ReferencedDecl);
264 }
265 return ExprToList[E] = Head;
266 }
267
268 // If E is an lvalue , it refers to storage. We model this storage as the
269 // first level of origin list, as if it were a reference, because l-values are
270 // addressable.
271 if (E->isGLValue() && !Type->isReferenceType())
272 Type = AST.getLValueReferenceType(Type);
273 return ExprToList[E] = buildListForType(Type, E);
274}
275
276void OriginManager::dump(OriginID OID, llvm::raw_ostream &OS) const {
277 OS << OID << " (";
278 Origin O = getOrigin(OID);
279 if (const ValueDecl *VD = O.getDecl()) {
280 OS << "Decl: " << VD->getNameAsString();
281 } else if (const Expr *E = O.getExpr()) {
282 OS << "Expr: " << E->getStmtClassName();
283 if (auto *DRE = dyn_cast<DeclRefExpr>(E)) {
284 if (const ValueDecl *VD = DRE->getDecl())
285 OS << ", Decl: " << VD->getNameAsString();
286 }
287 } else {
288 OS << "Unknown";
289 }
290 if (O.Ty)
291 OS << ", Type : " << QualType(O.Ty, 0).getAsString();
292 OS << ")";
293}
294
296 assert(ID.Value < AllOrigins.size());
297 return AllOrigins[ID.Value];
298}
299
301 LifetimeSafetyStats &LSStats) {
302 MissingOriginCollector Collector(this->ExprToList, *this, LSStats);
303 Collector.TraverseStmt(const_cast<Stmt *>(&FunctionBody));
304}
305
306void OriginManager::collectLifetimeAnnotatedOriginTypes(
307 const AnalysisDeclContext &AC) {
308 LifetimeAnnotatedOriginTypeCollector Collector;
309 if (Stmt *Body = AC.getBody())
310 Collector.TraverseStmt(Body);
311 if (const auto *CD = dyn_cast<CXXConstructorDecl>(AC.getDecl()))
312 for (const auto *Init : CD->inits())
313 Collector.TraverseStmt(Init->getInit());
314 for (QualType QT : Collector.getCollectedTypes())
315 registerLifetimeAnnotatedOriginType(QT);
316}
317
318void OriginManager::registerLifetimeAnnotatedOriginType(QualType QT) {
319 if (!QT->getAsCXXRecordDecl() || hasOrigins(QT))
320 return;
321
322 LifetimeAnnotatedOriginTypes.insert(QT.getCanonicalType().getTypePtr());
323}
324
325} // namespace clang::lifetimes::internal
Defines the clang::ASTContext interface.
This file defines AnalysisDeclContext, a class that manages the analysis context data for context sen...
Defines the C++ Decl subclasses, other than those for templates (found in DeclTemplate....
Defines the C++ template declaration subclasses.
Defines the clang::Expr interface and subclasses for C++ expressions.
C Language Family Type Representation.
AnalysisDeclContext contains the context data for the function, method or block under analysis.
Represents a C++ struct/union/class.
Definition DeclCXX.h:258
DeclContext * getParent()
getParent - Returns the containing DeclContext.
Definition DeclBase.h:2122
Decl - This represents one declaration (or definition), e.g.
Definition DeclBase.h:86
Represents an expression – generally a full-expression – that introduces cleanups to be run at the en...
Definition ExprCXX.h:3661
This represents one expression.
Definition Expr.h:112
bool isGLValue() const
Definition Expr.h:287
Expr * IgnoreParens() LLVM_READONLY
Skip past any parentheses which might surround this expression until reaching a fixed point.
Definition Expr.cpp:3086
QualType getType() const
Definition Expr.h:144
A (possibly-)qualified type.
Definition TypeBase.h:937
const Type * getTypePtr() const
Retrieves a pointer to the underlying (unqualified) type.
Definition TypeBase.h:8431
QualType getCanonicalType() const
Definition TypeBase.h:8483
const Type * getTypePtrOrNull() const
Definition TypeBase.h:8435
static std::string getAsString(SplitQualType split, const PrintingPolicy &Policy)
Definition TypeBase.h:1338
Stmt - This represents one statement.
Definition Stmt.h:86
const char * getStmtClassName() const
Definition Stmt.cpp:86
The base class of the type hierarchy.
Definition TypeBase.h:1866
CXXRecordDecl * getAsCXXRecordDecl() const
Retrieves the CXXRecordDecl that this type refers to, either because the type is a RecordType or beca...
Definition Type.h:26
bool isReferenceType() const
Definition TypeBase.h:8692
QualType getPointeeType() const
If this is a pointer, ObjC object pointer, or block pointer, this returns the respective pointee.
Definition Type.cpp:754
bool isPointerOrReferenceType() const
Definition TypeBase.h:8672
Represent the declaration of a variable (in which case it is an lvalue) a function (in which case it ...
Definition Decl.h:712
QualType getType() const
Definition Decl.h:723
A list of origins representing levels of indirection for pointer-like types.
Definition Origins.h:95
void setInnerOriginList(OriginList *Inner)
Definition Origins.h:102
OriginList * getOrCreateList(const ValueDecl *D)
Gets or creates the OriginList for a given ValueDecl.
Definition Origins.cpp:208
OriginList * createSingleOriginList(OriginID OID)
Wraps an existing OriginID in a new single-element OriginList, so a fact can refer to a single level ...
Definition Origins.cpp:189
const Origin & getOrigin(OriginID ID) const
Definition Origins.cpp:295
void collectMissingOrigins(Stmt &FunctionBody, LifetimeSafetyStats &LSStats)
Collects statistics about expressions that lack associated origins.
Definition Origins.cpp:300
OriginManager(const AnalysisDeclContext &AC)
Definition Origins.cpp:160
void dump(OriginID OID, llvm::raw_ostream &OS) const
Definition Origins.cpp:276
utils::ID< struct OriginTag > OriginID
Definition Origins.h:28
bool doesDeclHaveStorage(const ValueDecl *D)
Returns true if the declaration has its own storage that can be borrowed.
Definition Origins.cpp:156
bool isGslPointerType(QualType QT)
bool isStdCallableWrapperType(const CXXRecordDecl *RD)
bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD)
const FunctionDecl * getDeclWithMergedLifetimeBoundAttrs(const FunctionDecl *FD)
bool isa(CodeGen::Address addr)
Definition Address.h:330
A structure to hold the statistics related to LifetimeAnalysis.
An Origin is a symbolic identifier that represents the set of possible loans a pointer-like object co...
Definition Origins.h:40
const clang::Expr * getExpr() const
Definition Origins.h:64
const clang::ValueDecl * getDecl() const
Definition Origins.h:61
const Type * Ty
The type at this indirection level.
Definition Origins.h:54