clang 24.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 collectCaptureBy(FD);
63 }
64 return true;
65 }
66
67 bool VisitCXXConstructExpr(const CXXConstructExpr *CCE) {
68 collect(CCE->getConstructor(), CCE->getType());
69 collectCaptureBy(CCE->getConstructor());
70 return true;
71 }
72
73 bool shouldVisitLambdaBody() const { return false; }
74 bool shouldVisitTemplateInstantiations() const { return true; }
75
76 const llvm::SmallVector<QualType> &getCollectedTypes() const {
77 return CollectedTypes;
78 }
79
80private:
81 llvm::SmallVector<QualType> CollectedTypes;
82
83 void collect(const FunctionDecl *FD, QualType RetType) {
84 if (!FD)
85 return;
87
88 if (const auto *MD = dyn_cast<CXXMethodDecl>(FD);
89 MD && MD->isInstance() && !isa<CXXConstructorDecl>(MD) &&
91 CollectedTypes.push_back(RetType);
92 return;
93 }
94
95 for (const auto *Param : FD->parameters()) {
96 if (Param->hasAttr<LifetimeBoundAttr>()) {
97 CollectedTypes.push_back(RetType);
98 return;
99 }
100 }
101 }
102
103 void collectCaptureBy(const FunctionDecl *FD) {
104 if (!FD)
105 return;
107 const auto *MD = dyn_cast<CXXMethodDecl>(FD);
108 bool IsInstance = MD && MD->isInstance();
109 int Offset = (MD && MD->isImplicitObjectMemberFunction()) ? 1 : 0;
110 auto ProcessAttr = [&](const LifetimeCaptureByAttr *Attr) {
111 for (int Idx : Attr->params()) {
112 if (Idx == LifetimeCaptureByAttr::Global ||
113 Idx == LifetimeCaptureByAttr::Unknown ||
114 Idx == LifetimeCaptureByAttr::Invalid)
115 continue;
116 if (Idx == LifetimeCaptureByAttr::This) {
117 if (IsInstance)
118 CollectedTypes.push_back(MD->getFunctionObjectParameterType());
119 } else if (int LogicalIdx = Idx - Offset;
120 LogicalIdx >= 0 &&
121 (unsigned)LogicalIdx < FD->getNumParams()) {
122 CollectedTypes.push_back(
123 FD->getParamDecl(LogicalIdx)->getType().getNonReferenceType());
124 }
125 }
126 };
127 if (const auto *MethodAttr =
128 IsInstance ? getCaptureByAttrFromFunctionType(FD) : nullptr)
129 ProcessAttr(MethodAttr);
130 for (const auto *Param : FD->parameters()) {
131 if (auto *Attr = Param->getAttr<LifetimeCaptureByAttr>())
132 ProcessAttr(Attr);
133 }
134 }
135};
136
137} // namespace
138
139bool OriginManager::hasOrigins(QualType QT, bool IntrinsicOnly) const {
141 return true;
142 if (!IntrinsicOnly &&
143 LifetimeAnnotatedOriginTypes.contains(QT.getCanonicalType().getTypePtr()))
144 return true;
145 // An `_Atomic(T)` wraps T transparently for lifetime purposes (the atomic
146 // holds the same value); see through it.
147 if (const auto *AT = QT->getAs<AtomicType>())
148 return hasOrigins(AT->getValueType(), IntrinsicOnly);
149 const auto *RD = QT->getAsCXXRecordDecl();
150 if (!RD)
151 return false;
152 // Standard library callable wrappers (e.g., std::function) can propagate the
153 // stored lambda's origins.
155 return true;
156 // TODO: Limit to lambdas for now. This will be extended to user-defined
157 // structs with pointer-like fields.
158 if (!RD->isLambda())
159 return false;
160 for (const auto *FD : RD->fields())
161 if (hasOrigins(FD->getType(), IntrinsicOnly))
162 return true;
163 return false;
164}
165
166/// Determines if an expression has origins that need to be tracked.
167///
168/// An expression has origins if:
169/// - It's a glvalue (has addressable storage), OR
170/// - Its type is pointer-like (pointer, reference, or gsl::Pointer), OR
171/// - Its type is registered for origin tracking (e.g., return type of a
172/// [[clang::lifetimebound]] function)
173///
174/// Examples:
175/// - `int x; x` : has origin (glvalue)
176/// - `int* p; p` : has 2 origins (1 for glvalue and 1 for pointer type)
177/// - `std::string_view{}` : has 1 origin (prvalue of pointer type)
178/// - `42` : no origin (prvalue of non-pointer type)
179/// - `x + y` : (where x, y are int) → no origin (prvalue of non-pointer type)
180bool OriginManager::hasOrigins(const Expr *E) const {
181 return E->isGLValue() || hasOrigins(E->getType());
182}
183
184/// Returns true if the declaration has its own storage that can be borrowed.
185///
186/// References generally have no storage - they are aliases to other storage.
187/// For example:
188/// int x; // has storage (can issue loans to x's storage)
189/// int& r = x; // no storage (r is an alias to x's storage)
190/// int* p; // has storage (the pointer variable p itself has storage)
191///
192/// TODO: Handle lifetime extension. References initialized by temporaries
193/// can have storage when the temporary's lifetime is extended:
194/// const int& r = 42; // temporary has storage, lifetime extended
195/// Foo&& f = Foo{}; // temporary has storage, lifetime extended
196/// Currently, this function returns false for all reference types.
198 return !D->getType()->isReferenceType();
199}
200
202 : AST(AC.getASTContext()) {
203 collectLifetimeAnnotatedOriginTypes(AC);
204 initializeThisOrigins(AC.getDecl());
205}
206
207void OriginManager::initializeThisOrigins(const Decl *D) {
208 const auto *MD = llvm::dyn_cast_or_null<CXXMethodDecl>(D);
209 if (!MD || !MD->isInstance())
210 return;
211 // Lambdas can capture 'this' from the surrounding context, but in that case
212 // 'this' does not refer to the lambda object itself.
213 if (const CXXRecordDecl *P = MD->getParent(); P && P->isLambda())
214 return;
215 ThisOrigins = buildListForType(MD->getThisType(), MD);
216}
217
218OriginList *OriginManager::createNode(const ValueDecl *D, QualType QT,
219 bool NamesDeclStorage) {
220 OriginID NewID = getNextOriginID();
221 AllOrigins.emplace_back(NewID, D, QT.getTypePtrOrNull(), NamesDeclStorage);
222 return new (ListAllocator.Allocate<OriginList>()) OriginList(NewID);
223}
224
225OriginList *OriginManager::createNode(const Expr *E, QualType QT,
226 bool NamesDeclStorage) {
227 OriginID NewID = getNextOriginID();
228 AllOrigins.emplace_back(NewID, E, QT.getTypePtrOrNull(), NamesDeclStorage);
229 return new (ListAllocator.Allocate<OriginList>()) OriginList(NewID);
230}
231
233 return new (ListAllocator.Allocate<OriginList>()) OriginList(OID);
234}
235
236template <typename T>
237OriginList *OriginManager::buildListForType(QualType QT, const T *Node,
238 bool NamesDeclStorage) {
239 assert(hasOrigins(QT) && "buildListForType called for non-pointer type");
240 // `_Atomic(T)` is transparent for lifetime purposes: build the node for T.
241 if (const auto *AT = QT->getAs<AtomicType>())
242 return buildListForType(AT->getValueType(), Node, NamesDeclStorage);
243 OriginList *Head = createNode(Node, QT, NamesDeclStorage);
244
245 if (QT->isPointerOrReferenceType()) {
246 QualType PointeeTy = QT->getPointeeType();
247 // We recurse if the pointee type is pointer-like, to build the next
248 // level in the origin tree. E.g., for T*& / View&.
249 if (hasOrigins(PointeeTy))
250 Head->setInnerOriginList(buildListForType(PointeeTy, Node));
251 }
252 return Head;
253}
254
256 if (!hasOrigins(D->getType()))
257 return nullptr;
258 auto It = DeclToList.find(D);
259 if (It != DeclToList.end())
260 return It->second;
261 return DeclToList[D] = buildListForType(D->getType(), D);
262}
263
264/// Returns true if the field named by \p ME denotes a subobject of `*this`,
265/// reached as `this->f` or through anonymous-struct/union subobjects.
266static bool isSubobjectOfThis(const MemberExpr *ME) {
267 const Expr *Base = ME->getBase()->IgnoreParenImpCasts();
269 return true;
270
271 // An arrow access dereferences a pointer, so the field belongs to another
272 // object, not a subobject of `*this`.
273 if (ME->isArrow())
274 return false;
275
276 // Traverse only *anonymous* subobjects: their members are uniquely named
277 // within the class, so the FieldDecl-keyed origin can't conflate two
278 // subobjects. A named subobject (`this->a.f`) could repeat the same inner
279 // type, so it is rejected.
280 if (const auto *BaseAsME = dyn_cast<MemberExpr>(Base)) {
281 const auto *BaseField = dyn_cast<FieldDecl>(BaseAsME->getMemberDecl());
282 if (BaseField && BaseField->isAnonymousStructOrUnion())
283 return isSubobjectOfThis(BaseAsME);
284 }
285 return false;
286}
287
289 if (auto *ParenIgnored = E->IgnoreParens(); ParenIgnored != E)
290 return getOrCreateList(ParenIgnored);
291 // We do not see CFG stmts for ExprWithCleanups. Simply peel them.
292 if (const ExprWithCleanups *EWC = dyn_cast<ExprWithCleanups>(E))
293 return getOrCreateList(EWC->getSubExpr());
294
295 // An OpaqueValueExpr is a placeholder for an already-evaluated subexpression
296 // (e.g. the common operand of `a ?: b`) and is not itself a CFG statement, so
297 // reuse its source's origins rather than flowing into a fresh node.
298 if (const auto *OVE = dyn_cast<OpaqueValueExpr>(E))
299 if (const Expr *Src = OVE->getSourceExpr())
300 return getOrCreateList(Src);
301
302 if (!hasOrigins(E))
303 return nullptr;
304
305 auto It = ExprToList.find(E);
306 if (It != ExprToList.end())
307 return It->second;
308
309 QualType Type = E->getType();
310 // Special handling for 'this' expressions to share origins with the method's
311 // implicit object parameter.
312 if (isa<CXXThisExpr>(E) && ThisOrigins)
313 return *ThisOrigins;
314
315 // Special handling for expressions referring to a decl to share origins with
316 // the underlying decl.
317 const ValueDecl *ReferencedDecl = nullptr;
318 if (auto *DRE = dyn_cast<DeclRefExpr>(E))
319 ReferencedDecl = DRE->getDecl();
320 else if (auto *ME = dyn_cast<MemberExpr>(E)) {
321 if (auto *Field = dyn_cast<FieldDecl>(ME->getMemberDecl());
322 Field && isSubobjectOfThis(ME))
323 ReferencedDecl = Field;
324 // A static data member names the same variable regardless of its base.
325 else if (auto *VD = dyn_cast<VarDecl>(ME->getMemberDecl()))
326 ReferencedDecl = VD;
327 }
328 if (ReferencedDecl) {
329 OriginList *Head = nullptr;
330 // For non-reference declarations (e.g., `int* p`), the expression is an
331 // lvalue (addressable) that can be borrowed, so we create an outer origin
332 // for the lvalue itself, with the pointee being the declaration's list.
333 // This models taking the address: `&p` borrows the storage of `p`, not what
334 // `p` points to.
335 if (doesDeclHaveStorage(ReferencedDecl)) {
336 // `this->f` reaches its field through `this` instead of naming it.
337 Head = createNode(E, QualType{},
338 /*NamesDeclStorage=*/isa<DeclRefExpr>(E) ||
339 isa<VarDecl>(ReferencedDecl));
340 // This ensures origin sharing: multiple expressions to the same
341 // declaration share the same underlying origins.
342 Head->setInnerOriginList(getOrCreateList(ReferencedDecl));
343 } else {
344 // For reference-typed declarations (e.g., `int& r = p`) which have no
345 // storage, the DeclRefExpr directly reuses the declaration's list since
346 // references don't add an extra level of indirection at the expression
347 // level.
348 Head = getOrCreateList(ReferencedDecl);
349 }
350 return ExprToList[E] = Head;
351 }
352
353 // If E is an lvalue , it refers to storage. We model this storage as the
354 // first level of origin list, as if it were a reference, because l-values are
355 // addressable.
356 if (E->isGLValue() && !Type->isReferenceType())
357 Type = AST.getLValueReferenceType(Type);
358 // A qualification conversion of a glvalue names what its operand names. It is
359 // not transparent: for class types it is the node alias notes report.
360 bool NamesDeclStorage = false;
361 if (const auto *CE = dyn_cast<CastExpr>(E);
362 CE && CE->getCastKind() == CK_NoOp && E->isGLValue())
363 if (const OriginList *Sub = getOrCreateList(CE->getSubExpr()))
364 NamesDeclStorage = getOrigin(Sub->getOuterOriginID()).NamesDeclStorage;
365 return ExprToList[E] = buildListForType(Type, E, NamesDeclStorage);
366}
367
368void OriginManager::dump(OriginID OID, llvm::raw_ostream &OS) const {
369 OS << OID << " (";
370 Origin O = getOrigin(OID);
371 if (const ValueDecl *VD = O.getDecl()) {
372 OS << "Decl: " << VD->getNameAsString();
373 } else if (const Expr *E = O.getExpr()) {
374 OS << "Expr: " << E->getStmtClassName();
375 if (auto *DRE = dyn_cast<DeclRefExpr>(E)) {
376 if (const ValueDecl *VD = DRE->getDecl())
377 OS << ", Decl: " << VD->getNameAsString();
378 }
379 } else {
380 OS << "Unknown";
381 }
382 if (O.Ty)
383 OS << ", Type : " << QualType(O.Ty, 0).getAsString();
384 OS << ")";
385}
386
388 assert(ID.Value < AllOrigins.size());
389 return AllOrigins[ID.Value];
390}
391
393 LifetimeSafetyStats &LSStats) {
394 MissingOriginCollector Collector(this->ExprToList, *this, LSStats);
395 Collector.TraverseStmt(const_cast<Stmt *>(&FunctionBody));
396}
397
398void OriginManager::collectLifetimeAnnotatedOriginTypes(
399 const AnalysisDeclContext &AC) {
400 LifetimeAnnotatedOriginTypeCollector Collector;
401 if (Stmt *Body = AC.getBody())
402 Collector.TraverseStmt(Body);
403 if (const auto *CD = dyn_cast<CXXConstructorDecl>(AC.getDecl()))
404 for (const auto *Init : CD->inits())
405 Collector.TraverseStmt(Init->getInit());
406 for (QualType QT : Collector.getCollectedTypes())
407 registerLifetimeAnnotatedOriginType(QT);
408}
409
410void OriginManager::registerLifetimeAnnotatedOriginType(QualType QT) {
411 if (!QT->getAsCXXRecordDecl() || hasOrigins(QT))
412 return;
413
414 LifetimeAnnotatedOriginTypes.insert(QT.getCanonicalType().getTypePtr());
415}
416
417} // 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:2108
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:3715
This represents one expression.
Definition Expr.h:114
bool isGLValue() const
Definition Expr.h:289
Expr * IgnoreParenImpCasts() LLVM_READONLY
Skip past any parentheses and implicit casts which might surround this expression until reaching a fi...
Definition Expr.cpp:3129
Expr * IgnoreParens() LLVM_READONLY
Skip past any parentheses which might surround this expression until reaching a fixed point.
Definition Expr.cpp:3125
QualType getType() const
Definition Expr.h:146
MemberExpr - [C99 6.5.2.3] Structure and Union Members.
Definition Expr.h:3433
Expr * getBase() const
Definition Expr.h:3510
bool isArrow() const
Definition Expr.h:3617
A (possibly-)qualified type.
Definition TypeBase.h:938
const Type * getTypePtr() const
Retrieves a pointer to the underlying (unqualified) type.
Definition TypeBase.h:8578
QualType getCanonicalType() const
Definition TypeBase.h:8630
const Type * getTypePtrOrNull() const
Definition TypeBase.h:8582
static std::string getAsString(SplitQualType split, const PrintingPolicy &Policy)
Definition TypeBase.h:1348
Stmt - This represents one statement.
Definition Stmt.h:85
const char * getStmtClassName() const
Definition Stmt.cpp:86
The base class of the type hierarchy.
Definition TypeBase.h:1879
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:8839
QualType getPointeeType() const
If this is a pointer, ObjC object pointer, or block pointer, this returns the respective pointee.
Definition Type.cpp:885
bool isPointerOrReferenceType() const
Definition TypeBase.h:8819
const T * getAs() const
Member-template getAs<specific type>'.
Definition TypeBase.h:9431
Represent the declaration of a variable (in which case it is an lvalue) a function (in which case it ...
Definition Decl.h:714
QualType getType() const
Definition Decl.h:725
A list of origins representing levels of indirection for pointer-like types.
Definition Origins.h:105
void setInnerOriginList(OriginList *Inner)
Definition Origins.h:112
OriginList * getOrCreateList(const ValueDecl *D)
Gets or creates the OriginList for a given ValueDecl.
Definition Origins.cpp:255
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:232
const Origin & getOrigin(OriginID ID) const
Definition Origins.cpp:387
void collectMissingOrigins(Stmt &FunctionBody, LifetimeSafetyStats &LSStats)
Collects statistics about expressions that lack associated origins.
Definition Origins.cpp:392
OriginManager(const AnalysisDeclContext &AC)
Definition Origins.cpp:201
void dump(OriginID OID, llvm::raw_ostream &OS) const
Definition Origins.cpp:368
bool hasOrigins(QualType QT, bool IntrinsicOnly=false) const
Determines whether a type can carry lifetime origins.
Definition Origins.cpp:139
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:197
static bool isSubobjectOfThis(const MemberExpr *ME)
Returns true if the field named by ME denotes a subobject of *this, reached as this->f or through ano...
Definition Origins.cpp:266
bool isGslPointerType(QualType QT)
bool isStdCallableWrapperType(const CXXRecordDecl *RD)
bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD)
const FunctionDecl * getDeclWithMergedLifetimeBoundAttrs(const FunctionDecl *FD)
const LifetimeCaptureByAttr * getCaptureByAttrFromFunctionType(const FunctionDecl *FD)
bool isa(CodeGen::Address addr)
Definition Address.h:330
const FunctionProtoType * T
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
bool NamesDeclStorage
True if this origin only holds a loan to a declaration named in scope, so it can never hold an expire...
Definition Origins.h:62
const clang::Expr * getExpr() const
Definition Origins.h:74
const clang::ValueDecl * getDecl() const
Definition Origins.h:71
const Type * Ty
The type at this indirection level.
Definition Origins.h:54