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