clang 18.0.0git
Pointer.cpp
Go to the documentation of this file.
1//===--- Pointer.cpp - Types for the constexpr VM ---------------*- 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
9#include "Pointer.h"
10#include "Boolean.h"
11#include "Context.h"
12#include "Floating.h"
13#include "Function.h"
14#include "Integral.h"
15#include "InterpBlock.h"
16#include "PrimType.h"
17#include "Record.h"
18
19using namespace clang;
20using namespace clang::interp;
21
22Pointer::Pointer(Block *Pointee) : Pointer(Pointee, 0, 0) {}
23
24Pointer::Pointer(Block *Pointee, unsigned BaseAndOffset)
25 : Pointer(Pointee, BaseAndOffset, BaseAndOffset) {}
26
27Pointer::Pointer(const Pointer &P) : Pointer(P.Pointee, P.Base, P.Offset) {}
28
30 : Pointee(P.Pointee), Base(P.Base), Offset(P.Offset) {
31 if (Pointee)
32 Pointee->replacePointer(&P, this);
33}
34
35Pointer::Pointer(Block *Pointee, unsigned Base, unsigned Offset)
36 : Pointee(Pointee), Base(Base), Offset(Offset) {
37 assert((Base == RootPtrMark || Base % alignof(void *) == 0) && "wrong base");
38 if (Pointee)
39 Pointee->addPointer(this);
40}
41
43 if (Pointee) {
44 Pointee->removePointer(this);
45 Pointee->cleanup();
46 }
47}
48
50 Block *Old = Pointee;
51
52 if (Pointee)
53 Pointee->removePointer(this);
54
55 Offset = P.Offset;
56 Base = P.Base;
57
58 Pointee = P.Pointee;
59 if (Pointee)
60 Pointee->addPointer(this);
61
62 if (Old)
63 Old->cleanup();
64}
65
67 Block *Old = Pointee;
68
69 if (Pointee)
70 Pointee->removePointer(this);
71
72 Offset = P.Offset;
73 Base = P.Base;
74
75 Pointee = P.Pointee;
76 if (Pointee)
77 Pointee->replacePointer(&P, this);
78
79 if (Old)
80 Old->cleanup();
81}
82
86 CharUnits Offset;
87 bool IsNullPtr;
88 bool IsOnePastEnd;
89
90 if (isZero()) {
91 Base = static_cast<const Expr *>(nullptr);
92 IsNullPtr = true;
93 IsOnePastEnd = false;
94 Offset = CharUnits::Zero();
95 } else {
96 // Build the lvalue base from the block.
97 Descriptor *Desc = getDeclDesc();
98 if (auto *VD = Desc->asValueDecl())
99 Base = VD;
100 else if (auto *E = Desc->asExpr())
101 Base = E;
102 else
103 llvm_unreachable("Invalid allocation type");
104
105 // Not a null pointer.
106 IsNullPtr = false;
107
108 if (isUnknownSizeArray()) {
109 IsOnePastEnd = false;
110 Offset = CharUnits::Zero();
111 } else if (Desc->asExpr()) {
112 // Pointer pointing to a an expression.
113 IsOnePastEnd = false;
114 Offset = CharUnits::Zero();
115 } else {
116 // TODO: compute the offset into the object.
117 Offset = CharUnits::Zero();
118
119 // Build the path into the object.
120 Pointer Ptr = *this;
121 while (Ptr.isField() || Ptr.isArrayElement()) {
122 if (Ptr.isArrayElement()) {
123 Path.push_back(APValue::LValuePathEntry::ArrayIndex(Ptr.getIndex()));
124 Ptr = Ptr.getArray();
125 } else {
126 // TODO: figure out if base is virtual
127 bool IsVirtual = false;
128
129 // Create a path entry for the field.
130 const Descriptor *Desc = Ptr.getFieldDesc();
131 if (const auto *BaseOrMember = Desc->asDecl()) {
132 Path.push_back(APValue::LValuePathEntry({BaseOrMember, IsVirtual}));
133 Ptr = Ptr.getBase();
134 continue;
135 }
136 llvm_unreachable("Invalid field type");
137 }
138 }
139
140 IsOnePastEnd = isOnePastEnd();
141 }
142 }
143
144 // We assemble the LValuePath starting from the innermost pointer to the
145 // outermost one. SO in a.b.c, the first element in Path will refer to
146 // the field 'c', while later code expects it to refer to 'a'.
147 // Just invert the order of the elements.
148 std::reverse(Path.begin(), Path.end());
149
150 return APValue(Base, Offset, Path, IsOnePastEnd, IsNullPtr);
151}
152
153std::string Pointer::toDiagnosticString(const ASTContext &Ctx) const {
154 if (!Pointee)
155 return "nullptr";
156
157 return toAPValue().getAsString(Ctx, getType());
158}
159
161 assert(Pointee && "Cannot check if null pointer was initialized");
162 const Descriptor *Desc = getFieldDesc();
163 assert(Desc);
164 if (Desc->isPrimitiveArray()) {
165 if (isStatic() && Base == 0)
166 return true;
167 // Primitive array field are stored in a bitset.
168 InitMap *Map = getInitMap();
169 if (!Map)
170 return false;
171 if (Map == (InitMap *)-1)
172 return true;
173 return Map->isInitialized(getIndex());
174 }
175
176 // Field has its bit in an inline descriptor.
177 return Base == 0 || getInlineDesc()->IsInitialized;
178}
179
181 assert(Pointee && "Cannot initialize null pointer");
182 const Descriptor *Desc = getFieldDesc();
183
184 assert(Desc);
185 if (Desc->isPrimitiveArray()) {
186 // Primitive global arrays don't have an initmap.
187 if (isStatic() && Base == 0)
188 return;
189
190 // Primitive array initializer.
191 InitMap *&Map = getInitMap();
192 if (Map == (InitMap *)-1)
193 return;
194 if (Map == nullptr)
195 Map = InitMap::allocate(Desc->getNumElems());
196 if (Map->initialize(getIndex())) {
197 free(Map);
198 Map = (InitMap *)-1;
199 }
200 return;
201 }
202
203 // Field has its bit in an inline descriptor.
204 assert(Base != 0 && "Only composite fields can be initialised");
205 getInlineDesc()->IsInitialized = true;
206}
207
208void Pointer::activate() const {
209 // Field has its bit in an inline descriptor.
210 assert(Base != 0 && "Only composite fields can be initialised");
211 getInlineDesc()->IsActive = true;
212}
213
215 // TODO: this only appears in constructors, so nothing to deactivate.
216}
217
218bool Pointer::hasSameBase(const Pointer &A, const Pointer &B) {
219 return A.Pointee == B.Pointee;
220}
221
222bool Pointer::hasSameArray(const Pointer &A, const Pointer &B) {
223 return hasSameBase(A, B) && A.Base == B.Base && A.getFieldDesc()->IsArray;
224}
225
227 // Primitives.
228 if (getFieldDesc()->isPrimitive()) {
229 PrimType PT = *Ctx.classify(getType());
230 TYPE_SWITCH(PT, return deref<T>().toAPValue());
231 llvm_unreachable("Unhandled PrimType?");
232 }
233
235 // Records.
236 if (getFieldDesc()->isRecord()) {
237 const Record *R = getRecord();
238 Result =
240
241 for (unsigned I = 0; I != R->getNumFields(); ++I) {
242 const Pointer &FieldPtr = this->atField(R->getField(I)->Offset);
243 Result.getStructField(I) = FieldPtr.toRValue(Ctx);
244 }
245
246 for (unsigned I = 0; I != R->getNumBases(); ++I) {
247 const Pointer &BasePtr = this->atField(R->getBase(I)->Offset);
248 Result.getStructBase(I) = BasePtr.toRValue(Ctx);
249 }
250 }
251
252 // TODO: Arrays
253
254 return Result;
255}
StringRef P
#define TYPE_SWITCH(Expr, B)
Definition: PrimType.h:109
A non-discriminated union of a base, field, or array index.
Definition: APValue.h:208
static LValuePathEntry ArrayIndex(uint64_t Index)
Definition: APValue.h:216
APValue - This class implements a discriminated union of [uninitialized] [APSInt] [APFloat],...
Definition: APValue.h:122
std::string getAsString(const ASTContext &Ctx, QualType Ty) const
Definition: APValue.cpp:938
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:182
CharUnits - This is an opaque type for sizes expressed in character units.
Definition: CharUnits.h:38
static CharUnits Zero()
Zero - Construct a CharUnits quantity of zero.
Definition: CharUnits.h:53
This represents one expression.
Definition: Expr.h:110
A memory block, either on the stack or in the heap.
Definition: InterpBlock.h:49
void addPointer(Pointer *P)
Pointer chain management.
Definition: InterpBlock.cpp:19
void replacePointer(Pointer *Old, Pointer *New)
Definition: InterpBlock.cpp:61
void removePointer(Pointer *P)
Definition: InterpBlock.cpp:36
void cleanup()
Deletes a dead block at the end of its lifetime.
Definition: InterpBlock.cpp:56
Holds all information required to evaluate constexpr code in a module.
Definition: Context.h:40
std::optional< PrimType > classify(QualType T) const
Classifies an expression.
Definition: Context.cpp:91
A pointer to a memory block, live or dead.
Definition: Pointer.h:65
static bool hasSameBase(const Pointer &A, const Pointer &B)
Checks if two pointers are comparable.
Definition: Pointer.cpp:218
void deactivate() const
Deactivates an entire strurcutre.
Definition: Pointer.cpp:214
bool isInitialized() const
Checks if an object was initialized.
Definition: Pointer.cpp:160
bool isStatic() const
Checks if the storage is static.
Definition: Pointer.h:290
int64_t getIndex() const
Returns the index into an array.
Definition: Pointer.h:326
Pointer atField(unsigned Off) const
Creates a pointer to a field.
Definition: Pointer.h:107
Pointer getArray() const
Returns the parent array.
Definition: Pointer.h:206
bool isUnknownSizeArray() const
Checks if the structure is an array of unknown size.
Definition: Pointer.h:265
void activate() const
Activats a field.
Definition: Pointer.cpp:208
void operator=(const Pointer &P)
Definition: Pointer.cpp:49
QualType getType() const
Returns the type of the innermost field.
Definition: Pointer.h:223
bool isArrayElement() const
Checks if the pointer points to an array.
Definition: Pointer.h:269
Descriptor * getFieldDesc() const
Accessors for information about the innermost field.
Definition: Pointer.h:216
Pointer getBase() const
Returns a pointer to the object of which this pointer is a field.
Definition: Pointer.h:196
APValue toRValue(const Context &Ctx) const
Converts the pointer to an APValue that is an rvalue.
Definition: Pointer.cpp:226
std::string toDiagnosticString(const ASTContext &Ctx) const
Converts the pointer to a string usable in diagnostics.
Definition: Pointer.cpp:153
bool isZero() const
Checks if the pointer is null.
Definition: Pointer.h:185
Descriptor * getDeclDesc() const
Accessor for information about the declaration site.
Definition: Pointer.h:192
bool isOnePastEnd() const
Checks if the index is one past end.
Definition: Pointer.h:335
static bool hasSameArray(const Pointer &A, const Pointer &B)
Checks if two pointers can be subtracted.
Definition: Pointer.cpp:222
APValue toAPValue() const
Converts the pointer to an APValue.
Definition: Pointer.cpp:83
void initialize() const
Initializes a field.
Definition: Pointer.cpp:180
bool isField() const
Checks if the item is a field in an object.
Definition: Pointer.h:189
const Record * getRecord() const
Returns the record descriptor of a class.
Definition: Pointer.h:276
Structure/Class descriptor.
Definition: Record.h:25
unsigned getNumBases() const
Definition: Record.h:89
const Field * getField(const FieldDecl *FD) const
Returns a field.
Definition: Record.cpp:30
unsigned getNumFields() const
Definition: Record.h:80
const Base * getBase(const RecordDecl *FD) const
Returns a base descriptor.
Definition: Record.cpp:36
PrimType
Enumeration of the primitive types of the VM.
Definition: PrimType.h:32
@ Result
The result type of a method or function.
Describes a memory block created by an allocation site.
Definition: Descriptor.h:77
unsigned getNumElems() const
Returns the number of elements stored in the block.
Definition: Descriptor.h:173
const ValueDecl * asValueDecl() const
Definition: Descriptor.h:147
const Decl * asDecl() const
Definition: Descriptor.h:144
const bool IsArray
Flag indicating if the block is an array.
Definition: Descriptor.h:111
bool isPrimitiveArray() const
Checks if the descriptor is of an array of primitives.
Definition: Descriptor.h:178
const Expr * asExpr() const
Definition: Descriptor.h:145
Bitfield tracking the initialisation status of elements of primitive arrays.
Definition: Descriptor.h:199
static InitMap * allocate(unsigned N)
Allocates a map holding N elements.
Definition: Descriptor.cpp:340
bool isInitialized(unsigned I) const
Checks if an element was initialized.
Definition: Descriptor.cpp:335
bool initialize(unsigned I)
Initializes an element. Returns true when object if fully initialized.
Definition: Descriptor.cpp:325
unsigned IsActive
Flag indicating if the field is the active member of a union.
Definition: Descriptor.h:69
unsigned IsInitialized
For primitive fields, it indicates if the field was initialized.
Definition: Descriptor.h:65