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/DeclCXX.h"
14#include "clang/AST/Expr.h"
15#include "clang/AST/ExprCXX.h"
17#include "clang/AST/TypeBase.h"
20#include "llvm/ADT/StringMap.h"
21
23namespace {
24/// A utility class to traverse the function body in the analysis
25/// context and collect the count of expressions with missing origins.
26class MissingOriginCollector
27 : public RecursiveASTVisitor<MissingOriginCollector> {
28public:
29 MissingOriginCollector(
30 const llvm::DenseMap<const clang::Expr *, OriginList *> &ExprToOriginList,
31 LifetimeSafetyStats &LSStats)
32 : ExprToOriginList(ExprToOriginList), LSStats(LSStats) {}
33 bool VisitExpr(Expr *E) {
34 if (!hasOrigins(E))
35 return true;
36 // Check if we have an origin for this expression.
37 if (!ExprToOriginList.contains(E)) {
38 // No origin found: count this as missing origin.
39 LSStats.ExprTypeToMissingOriginCount[E->getType().getTypePtr()]++;
40 LSStats.ExprStmtClassToMissingOriginCount[std::string(
41 E->getStmtClassName())]++;
42 }
43 return true;
44 }
45
46private:
47 const llvm::DenseMap<const clang::Expr *, OriginList *> &ExprToOriginList;
48 LifetimeSafetyStats &LSStats;
49};
50} // namespace
51
54}
55
56/// Determines if an expression has origins that need to be tracked.
57///
58/// An expression has origins if:
59/// - It's a glvalue (has addressable storage), OR
60/// - Its type is pointer-like (pointer, reference, or gsl::Pointer)
61///
62/// Examples:
63/// - `int x; x` : has origin (glvalue)
64/// - `int* p; p` : has 2 origins (1 for glvalue and 1 for pointer type)
65/// - `std::string_view{}` : has 1 origin (prvalue of pointer type)
66/// - `42` : no origin (prvalue of non-pointer type)
67/// - `x + y` : (where x, y are int) → no origin (prvalue of non-pointer type)
68bool hasOrigins(const Expr *E) {
69 return E->isGLValue() || hasOrigins(E->getType());
70}
71
72/// Returns true if the declaration has its own storage that can be borrowed.
73///
74/// References generally have no storage - they are aliases to other storage.
75/// For example:
76/// int x; // has storage (can issue loans to x's storage)
77/// int& r = x; // no storage (r is an alias to x's storage)
78/// int* p; // has storage (the pointer variable p itself has storage)
79///
80/// TODO: Handle lifetime extension. References initialized by temporaries
81/// can have storage when the temporary's lifetime is extended:
82/// const int& r = 42; // temporary has storage, lifetime extended
83/// Foo&& f = Foo{}; // temporary has storage, lifetime extended
84/// Currently, this function returns false for all reference types.
86 return !D->getType()->isReferenceType();
87}
88
89OriginList *OriginManager::createNode(const ValueDecl *D, QualType QT) {
90 OriginID NewID = getNextOriginID();
91 AllOrigins.emplace_back(NewID, D, QT.getTypePtrOrNull());
92 return new (ListAllocator.Allocate<OriginList>()) OriginList(NewID);
93}
94
95OriginList *OriginManager::createNode(const Expr *E, QualType QT) {
96 OriginID NewID = getNextOriginID();
97 AllOrigins.emplace_back(NewID, E, QT.getTypePtrOrNull());
98 return new (ListAllocator.Allocate<OriginList>()) OriginList(NewID);
99}
100
101template <typename T>
102OriginList *OriginManager::buildListForType(QualType QT, const T *Node) {
103 assert(hasOrigins(QT) && "buildListForType called for non-pointer type");
104 OriginList *Head = createNode(Node, QT);
105
106 if (QT->isPointerOrReferenceType()) {
107 QualType PointeeTy = QT->getPointeeType();
108 // We recurse if the pointee type is pointer-like, to build the next
109 // level in the origin tree. E.g., for T*& / View&.
110 if (hasOrigins(PointeeTy))
111 Head->setInnerOriginList(buildListForType(PointeeTy, Node));
112 }
113 return Head;
114}
115
117 if (!hasOrigins(D->getType()))
118 return nullptr;
119 auto It = DeclToList.find(D);
120 if (It != DeclToList.end())
121 return It->second;
122 return DeclToList[D] = buildListForType(D->getType(), D);
123}
124
126 if (auto *ParenIgnored = E->IgnoreParens(); ParenIgnored != E)
127 return getOrCreateList(ParenIgnored);
128 // We do not see CFG stmts for ExprWithCleanups. Simply peel them.
129 if (const ExprWithCleanups *EWC = dyn_cast<ExprWithCleanups>(E))
130 return getOrCreateList(EWC->getSubExpr());
131
132 if (!hasOrigins(E))
133 return nullptr;
134
135 auto It = ExprToList.find(E);
136 if (It != ExprToList.end())
137 return It->second;
138
139 QualType Type = E->getType();
140
141 // Special handling for DeclRefExpr to share origins with the underlying decl.
142 if (auto *DRE = dyn_cast<DeclRefExpr>(E)) {
143 OriginList *Head = nullptr;
144 // For non-reference declarations (e.g., `int* p`), the DeclRefExpr is an
145 // lvalue (addressable) that can be borrowed, so we create an outer origin
146 // for the lvalue itself, with the pointee being the declaration's list.
147 // This models taking the address: `&p` borrows the storage of `p`, not what
148 // `p` points to.
149 if (doesDeclHaveStorage(DRE->getDecl())) {
150 Head = createNode(DRE, QualType{});
151 // This ensures origin sharing: multiple DeclRefExprs to the same
152 // declaration share the same underlying origins.
153 Head->setInnerOriginList(getOrCreateList(DRE->getDecl()));
154 } else {
155 // For reference-typed declarations (e.g., `int& r = p`) which have no
156 // storage, the DeclRefExpr directly reuses the declaration's list since
157 // references don't add an extra level of indirection at the expression
158 // level.
159 Head = getOrCreateList(DRE->getDecl());
160 }
161 return ExprToList[E] = Head;
162 }
163
164 // If E is an lvalue , it refers to storage. We model this storage as the
165 // first level of origin list, as if it were a reference, because l-values are
166 // addressable.
167 if (E->isGLValue() && !Type->isReferenceType())
168 Type = AST.getLValueReferenceType(Type);
169 return ExprToList[E] = buildListForType(Type, E);
170}
171
172void OriginManager::dump(OriginID OID, llvm::raw_ostream &OS) const {
173 OS << OID << " (";
174 Origin O = getOrigin(OID);
175 if (const ValueDecl *VD = O.getDecl()) {
176 OS << "Decl: " << VD->getNameAsString();
177 } else if (const Expr *E = O.getExpr()) {
178 OS << "Expr: " << E->getStmtClassName();
179 if (auto *DRE = dyn_cast<DeclRefExpr>(E)) {
180 if (const ValueDecl *VD = DRE->getDecl())
181 OS << ", Decl: " << VD->getNameAsString();
182 }
183 } else {
184 OS << "Unknown";
185 }
186 if (O.Ty)
187 OS << ", Type : " << QualType(O.Ty, 0).getAsString();
188 OS << ")";
189}
190
192 assert(ID.Value < AllOrigins.size());
193 return AllOrigins[ID.Value];
194}
195
197 LifetimeSafetyStats &LSStats) {
198 MissingOriginCollector Collector(this->ExprToList, LSStats);
199 Collector.TraverseStmt(const_cast<Stmt *>(&FunctionBody));
200}
201
202} // namespace clang::lifetimes::internal
Defines the clang::ASTContext interface.
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.
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:3085
QualType getType() const
Definition Expr.h:144
A (possibly-)qualified type.
Definition TypeBase.h:937
const Type * getTypePtrOrNull() const
Definition TypeBase.h:8296
static std::string getAsString(SplitQualType split, const PrintingPolicy &Policy)
Definition TypeBase.h:1332
Stmt - This represents one statement.
Definition Stmt.h:86
const char * getStmtClassName() const
Definition Stmt.cpp:87
The base class of the type hierarchy.
Definition TypeBase.h:1833
bool isReferenceType() const
Definition TypeBase.h:8553
bool isPointerOrReferenceType() const
Definition TypeBase.h:8533
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:93
void setInnerOriginList(OriginList *Inner)
Definition Origins.h:100
OriginList * getOrCreateList(const ValueDecl *D)
Gets or creates the OriginList for a given ValueDecl.
Definition Origins.cpp:116
const Origin & getOrigin(OriginID ID) const
Definition Origins.cpp:191
void collectMissingOrigins(Stmt &FunctionBody, LifetimeSafetyStats &LSStats)
Collects statistics about expressions that lack associated origins.
Definition Origins.cpp:196
void dump(OriginID OID, llvm::raw_ostream &OS) const
Definition Origins.cpp:172
utils::ID< struct OriginTag > OriginID
Definition Origins.h:26
bool doesDeclHaveStorage(const ValueDecl *D)
Returns true if the declaration has its own storage that can be borrowed.
Definition Origins.cpp:85
bool hasOrigins(QualType QT)
Definition Origins.cpp:52
bool isGslPointerType(QualType QT)
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:38
const clang::Expr * getExpr() const
Definition Origins.h:62
const clang::ValueDecl * getDecl() const
Definition Origins.h:59
const Type * Ty
The type at this indirection level.
Definition Origins.h:52