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/TypeBase.h"
16
18
21}
22
23/// Determines if an expression has origins that need to be tracked.
24///
25/// An expression has origins if:
26/// - It's a glvalue (has addressable storage), OR
27/// - Its type is pointer-like (pointer, reference, or gsl::Pointer)
28///
29/// Examples:
30/// - `int x; x` : has origin (glvalue)
31/// - `int* p; p` : has 2 origins (1 for glvalue and 1 for pointer type)
32/// - `std::string_view{}` : has 1 origin (prvalue of pointer type)
33/// - `42` : no origin (prvalue of non-pointer type)
34/// - `x + y` : (where x, y are int) → no origin (prvalue of non-pointer type)
35bool hasOrigins(const Expr *E) {
36 return E->isGLValue() || hasOrigins(E->getType());
37}
38
39/// Returns true if the declaration has its own storage that can be borrowed.
40///
41/// References generally have no storage - they are aliases to other storage.
42/// For example:
43/// int x; // has storage (can issue loans to x's storage)
44/// int& r = x; // no storage (r is an alias to x's storage)
45/// int* p; // has storage (the pointer variable p itself has storage)
46///
47/// TODO: Handle lifetime extension. References initialized by temporaries
48/// can have storage when the temporary's lifetime is extended:
49/// const int& r = 42; // temporary has storage, lifetime extended
50/// Foo&& f = Foo{}; // temporary has storage, lifetime extended
51/// Currently, this function returns false for all reference types.
53 return !D->getType()->isReferenceType();
54}
55
56OriginList *OriginManager::createNode(const ValueDecl *D, QualType QT) {
57 OriginID NewID = getNextOriginID();
58 AllOrigins.emplace_back(NewID, D, QT.getTypePtrOrNull());
59 return new (ListAllocator.Allocate<OriginList>()) OriginList(NewID);
60}
61
62OriginList *OriginManager::createNode(const Expr *E, QualType QT) {
63 OriginID NewID = getNextOriginID();
64 AllOrigins.emplace_back(NewID, E, QT.getTypePtrOrNull());
65 return new (ListAllocator.Allocate<OriginList>()) OriginList(NewID);
66}
67
68template <typename T>
69OriginList *OriginManager::buildListForType(QualType QT, const T *Node) {
70 assert(hasOrigins(QT) && "buildListForType called for non-pointer type");
71 OriginList *Head = createNode(Node, QT);
72
73 if (QT->isPointerOrReferenceType()) {
74 QualType PointeeTy = QT->getPointeeType();
75 // We recurse if the pointee type is pointer-like, to build the next
76 // level in the origin tree. E.g., for T*& / View&.
77 if (hasOrigins(PointeeTy))
78 Head->setInnerOriginList(buildListForType(PointeeTy, Node));
79 }
80 return Head;
81}
82
84 if (!hasOrigins(D->getType()))
85 return nullptr;
86 auto It = DeclToList.find(D);
87 if (It != DeclToList.end())
88 return It->second;
89 return DeclToList[D] = buildListForType(D->getType(), D);
90}
91
93 if (auto *ParenIgnored = E->IgnoreParens(); ParenIgnored != E)
94 return getOrCreateList(ParenIgnored);
95
96 if (!hasOrigins(E))
97 return nullptr;
98
99 auto It = ExprToList.find(E);
100 if (It != ExprToList.end())
101 return It->second;
102
103 QualType Type = E->getType();
104
105 // Special handling for DeclRefExpr to share origins with the underlying decl.
106 if (auto *DRE = dyn_cast<DeclRefExpr>(E)) {
107 OriginList *Head = nullptr;
108 // For non-reference declarations (e.g., `int* p`), the DeclRefExpr is an
109 // lvalue (addressable) that can be borrowed, so we create an outer origin
110 // for the lvalue itself, with the pointee being the declaration's list.
111 // This models taking the address: `&p` borrows the storage of `p`, not what
112 // `p` points to.
113 if (doesDeclHaveStorage(DRE->getDecl())) {
114 Head = createNode(DRE, QualType{});
115 // This ensures origin sharing: multiple DeclRefExprs to the same
116 // declaration share the same underlying origins.
117 Head->setInnerOriginList(getOrCreateList(DRE->getDecl()));
118 } else {
119 // For reference-typed declarations (e.g., `int& r = p`) which have no
120 // storage, the DeclRefExpr directly reuses the declaration's list since
121 // references don't add an extra level of indirection at the expression
122 // level.
123 Head = getOrCreateList(DRE->getDecl());
124 }
125 return ExprToList[E] = Head;
126 }
127
128 // If E is an lvalue , it refers to storage. We model this storage as the
129 // first level of origin list, as if it were a reference, because l-values are
130 // addressable.
131 if (E->isGLValue() && !Type->isReferenceType())
132 Type = AST.getLValueReferenceType(Type);
133 return ExprToList[E] = buildListForType(Type, E);
134}
135
136void OriginManager::dump(OriginID OID, llvm::raw_ostream &OS) const {
137 OS << OID << " (";
138 Origin O = getOrigin(OID);
139 if (const ValueDecl *VD = O.getDecl()) {
140 OS << "Decl: " << VD->getNameAsString();
141 } else if (const Expr *E = O.getExpr()) {
142 OS << "Expr: " << E->getStmtClassName();
143 if (auto *DRE = dyn_cast<DeclRefExpr>(E)) {
144 if (const ValueDecl *VD = DRE->getDecl())
145 OS << ", Decl: " << VD->getNameAsString();
146 }
147 } else {
148 OS << "Unknown";
149 }
150 if (O.Ty)
151 OS << ", Type : " << QualType(O.Ty, 0).getAsString();
152 OS << ")";
153}
154
156 assert(ID.Value < AllOrigins.size());
157 return AllOrigins[ID.Value];
158}
159
160} // 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
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:91
void setInnerOriginList(OriginList *Inner)
Definition Origins.h:98
OriginList * getOrCreateList(const ValueDecl *D)
Gets or creates the OriginList for a given ValueDecl.
Definition Origins.cpp:83
const Origin & getOrigin(OriginID ID) const
Definition Origins.cpp:155
void dump(OriginID OID, llvm::raw_ostream &OS) const
Definition Origins.cpp:136
utils::ID< struct OriginTag > OriginID
Definition Origins.h:24
bool doesDeclHaveStorage(const ValueDecl *D)
Returns true if the declaration has its own storage that can be borrowed.
Definition Origins.cpp:52
bool hasOrigins(QualType QT)
Definition Origins.cpp:19
bool isGslPointerType(QualType QT)
An Origin is a symbolic identifier that represents the set of possible loans a pointer-like object co...
Definition Origins.h:36
const clang::Expr * getExpr() const
Definition Origins.h:60
const clang::ValueDecl * getDecl() const
Definition Origins.h:57
const Type * Ty
The type at this indirection level.
Definition Origins.h:50