clang 19.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
23 : Pointer(Pointee, Pointee->getDescriptor()->getMetadataSize(),
24 Pointee->getDescriptor()->getMetadataSize()) {}
25
26Pointer::Pointer(Block *Pointee, unsigned BaseAndOffset)
27 : Pointer(Pointee, BaseAndOffset, BaseAndOffset) {}
28
29Pointer::Pointer(const Pointer &P) : Pointer(P.Pointee, P.Base, P.Offset) {}
30
32 : Pointee(P.Pointee), Base(P.Base), Offset(P.Offset) {
33 if (Pointee)
34 Pointee->replacePointer(&P, this);
35}
36
37Pointer::Pointer(Block *Pointee, unsigned Base, unsigned Offset)
38 : Pointee(Pointee), Base(Base), Offset(Offset) {
39 assert((Base == RootPtrMark || Base % alignof(void *) == 0) && "wrong base");
40 if (Pointee)
41 Pointee->addPointer(this);
42}
43
45 if (Pointee) {
46 Pointee->removePointer(this);
47 Pointee->cleanup();
48 }
49}
50
52 Block *Old = Pointee;
53
54 if (Pointee)
55 Pointee->removePointer(this);
56
57 Offset = P.Offset;
58 Base = P.Base;
59
60 Pointee = P.Pointee;
61 if (Pointee)
62 Pointee->addPointer(this);
63
64 if (Old)
65 Old->cleanup();
66}
67
69 Block *Old = Pointee;
70
71 if (Pointee)
72 Pointee->removePointer(this);
73
74 Offset = P.Offset;
75 Base = P.Base;
76
77 Pointee = P.Pointee;
78 if (Pointee)
79 Pointee->replacePointer(&P, this);
80
81 if (Old)
82 Old->cleanup();
83}
84
87
88 if (isZero())
89 return APValue(static_cast<const Expr *>(nullptr), CharUnits::Zero(), Path,
90 /*IsOnePastEnd=*/false, /*IsNullPtr=*/true);
91
92 // Build the lvalue base from the block.
93 const Descriptor *Desc = getDeclDesc();
95 if (const auto *VD = Desc->asValueDecl())
96 Base = VD;
97 else if (const auto *E = Desc->asExpr())
98 Base = E;
99 else
100 llvm_unreachable("Invalid allocation type");
101
102 if (isDummy() || isUnknownSizeArray() || Desc->asExpr())
103 return APValue(Base, CharUnits::Zero(), Path,
104 /*IsOnePastEnd=*/false, /*IsNullPtr=*/false);
105
106 // TODO: compute the offset into the object.
107 CharUnits Offset = CharUnits::Zero();
108 bool IsOnePastEnd = isOnePastEnd();
109
110 // Build the path into the object.
111 Pointer Ptr = *this;
112 while (Ptr.isField() || Ptr.isArrayElement()) {
113 if (Ptr.isArrayElement()) {
114 Path.push_back(APValue::LValuePathEntry::ArrayIndex(Ptr.getIndex()));
115 Ptr = Ptr.getArray();
116 } else {
117 // TODO: figure out if base is virtual
118 bool IsVirtual = false;
119
120 // Create a path entry for the field.
121 const Descriptor *Desc = Ptr.getFieldDesc();
122 if (const auto *BaseOrMember = Desc->asDecl()) {
123 Path.push_back(APValue::LValuePathEntry({BaseOrMember, IsVirtual}));
124 Ptr = Ptr.getBase();
125 continue;
126 }
127 llvm_unreachable("Invalid field type");
128 }
129 }
130
131 // We assemble the LValuePath starting from the innermost pointer to the
132 // outermost one. SO in a.b.c, the first element in Path will refer to
133 // the field 'c', while later code expects it to refer to 'a'.
134 // Just invert the order of the elements.
135 std::reverse(Path.begin(), Path.end());
136
137 return APValue(Base, Offset, Path, IsOnePastEnd, /*IsNullPtr=*/false);
138}
139
140std::string Pointer::toDiagnosticString(const ASTContext &Ctx) const {
141 if (!Pointee)
142 return "nullptr";
143
144 return toAPValue().getAsString(Ctx, getType());
145}
146
148 assert(Pointee && "Cannot check if null pointer was initialized");
149 const Descriptor *Desc = getFieldDesc();
150 assert(Desc);
151 if (Desc->isPrimitiveArray()) {
152 if (isStatic() && Base == 0)
153 return true;
154
155 InitMapPtr &IM = getInitMap();
156
157 if (!IM)
158 return false;
159
160 if (IM->first)
161 return true;
162
163 return IM->second->isElementInitialized(getIndex());
164 }
165
166 // Field has its bit in an inline descriptor.
167 return Base == 0 || getInlineDesc()->IsInitialized;
168}
169
171 assert(Pointee && "Cannot initialize null pointer");
172 const Descriptor *Desc = getFieldDesc();
173
174 assert(Desc);
175 if (Desc->isPrimitiveArray()) {
176 // Primitive global arrays don't have an initmap.
177 if (isStatic() && Base == 0)
178 return;
179
180 InitMapPtr &IM = getInitMap();
181 if (!IM)
182 IM =
183 std::make_pair(false, std::make_shared<InitMap>(Desc->getNumElems()));
184
185 assert(IM);
186
187 // All initialized.
188 if (IM->first)
189 return;
190
191 if (IM->second->initializeElement(getIndex())) {
192 IM->first = true;
193 IM->second.reset();
194 }
195 return;
196 }
197
198 // Field has its bit in an inline descriptor.
199 assert(Base != 0 && "Only composite fields can be initialised");
200 getInlineDesc()->IsInitialized = true;
201}
202
203void Pointer::activate() const {
204 // Field has its bit in an inline descriptor.
205 assert(Base != 0 && "Only composite fields can be initialised");
206 getInlineDesc()->IsActive = true;
207}
208
210 // TODO: this only appears in constructors, so nothing to deactivate.
211}
212
213bool Pointer::hasSameBase(const Pointer &A, const Pointer &B) {
214 return A.Pointee == B.Pointee;
215}
216
217bool Pointer::hasSameArray(const Pointer &A, const Pointer &B) {
218 return hasSameBase(A, B) && A.Base == B.Base && A.getFieldDesc()->IsArray;
219}
220
221std::optional<APValue> Pointer::toRValue(const Context &Ctx) const {
222 // Method to recursively traverse composites.
223 std::function<bool(QualType, const Pointer &, APValue &)> Composite;
224 Composite = [&Composite, &Ctx](QualType Ty, const Pointer &Ptr, APValue &R) {
225 if (const auto *AT = Ty->getAs<AtomicType>())
226 Ty = AT->getValueType();
227
228 // Invalid pointers.
229 if (Ptr.isDummy() || !Ptr.isLive() ||
230 (!Ptr.isUnknownSizeArray() && Ptr.isOnePastEnd()))
231 return false;
232
233 // Primitive values.
234 if (std::optional<PrimType> T = Ctx.classify(Ty)) {
235 TYPE_SWITCH(*T, R = Ptr.deref<T>().toAPValue());
236 return true;
237 }
238
239 if (const auto *RT = Ty->getAs<RecordType>()) {
240 const auto *Record = Ptr.getRecord();
241 assert(Record && "Missing record descriptor");
242
243 bool Ok = true;
244 if (RT->getDecl()->isUnion()) {
245 const FieldDecl *ActiveField = nullptr;
247 for (const auto &F : Record->fields()) {
248 const Pointer &FP = Ptr.atField(F.Offset);
249 QualType FieldTy = F.Decl->getType();
250 if (FP.isActive()) {
251 if (std::optional<PrimType> T = Ctx.classify(FieldTy)) {
252 TYPE_SWITCH(*T, Value = FP.deref<T>().toAPValue());
253 } else {
254 Ok &= Composite(FieldTy, FP, Value);
255 }
256 break;
257 }
258 }
259 R = APValue(ActiveField, Value);
260 } else {
261 unsigned NF = Record->getNumFields();
262 unsigned NB = Record->getNumBases();
263 unsigned NV = Ptr.isBaseClass() ? 0 : Record->getNumVirtualBases();
264
265 R = APValue(APValue::UninitStruct(), NB, NF);
266
267 for (unsigned I = 0; I < NF; ++I) {
268 const Record::Field *FD = Record->getField(I);
269 QualType FieldTy = FD->Decl->getType();
270 const Pointer &FP = Ptr.atField(FD->Offset);
271 APValue &Value = R.getStructField(I);
272
273 if (std::optional<PrimType> T = Ctx.classify(FieldTy)) {
274 TYPE_SWITCH(*T, Value = FP.deref<T>().toAPValue());
275 } else {
276 Ok &= Composite(FieldTy, FP, Value);
277 }
278 }
279
280 for (unsigned I = 0; I < NB; ++I) {
281 const Record::Base *BD = Record->getBase(I);
282 QualType BaseTy = Ctx.getASTContext().getRecordType(BD->Decl);
283 const Pointer &BP = Ptr.atField(BD->Offset);
284 Ok &= Composite(BaseTy, BP, R.getStructBase(I));
285 }
286
287 for (unsigned I = 0; I < NV; ++I) {
288 const Record::Base *VD = Record->getVirtualBase(I);
289 QualType VirtBaseTy = Ctx.getASTContext().getRecordType(VD->Decl);
290 const Pointer &VP = Ptr.atField(VD->Offset);
291 Ok &= Composite(VirtBaseTy, VP, R.getStructBase(NB + I));
292 }
293 }
294 return Ok;
295 }
296
297 if (Ty->isIncompleteArrayType()) {
298 R = APValue(APValue::UninitArray(), 0, 0);
299 return true;
300 }
301
302 if (const auto *AT = Ty->getAsArrayTypeUnsafe()) {
303 const size_t NumElems = Ptr.getNumElems();
304 QualType ElemTy = AT->getElementType();
305 R = APValue(APValue::UninitArray{}, NumElems, NumElems);
306
307 bool Ok = true;
308 for (unsigned I = 0; I < NumElems; ++I) {
309 APValue &Slot = R.getArrayInitializedElt(I);
310 const Pointer &EP = Ptr.atIndex(I);
311 if (std::optional<PrimType> T = Ctx.classify(ElemTy)) {
312 TYPE_SWITCH(*T, Slot = EP.deref<T>().toAPValue());
313 } else {
314 Ok &= Composite(ElemTy, EP.narrow(), Slot);
315 }
316 }
317 return Ok;
318 }
319
320 // Complex types.
321 if (const auto *CT = Ty->getAs<ComplexType>()) {
322 QualType ElemTy = CT->getElementType();
323 std::optional<PrimType> ElemT = Ctx.classify(ElemTy);
324 assert(ElemT);
325
326 if (ElemTy->isIntegerType()) {
327 INT_TYPE_SWITCH(*ElemT, {
328 auto V1 = Ptr.atIndex(0).deref<T>();
329 auto V2 = Ptr.atIndex(1).deref<T>();
330 R = APValue(V1.toAPSInt(), V2.toAPSInt());
331 return true;
332 });
333 } else if (ElemTy->isFloatingType()) {
334 R = APValue(Ptr.atIndex(0).deref<Floating>().getAPFloat(),
335 Ptr.atIndex(1).deref<Floating>().getAPFloat());
336 return true;
337 }
338 return false;
339 }
340
341 llvm_unreachable("invalid value to return");
342 };
343
344 if (isZero())
345 return APValue(static_cast<Expr *>(nullptr), CharUnits::Zero(), {}, false,
346 true);
347
348 if (isDummy() || !isLive())
349 return std::nullopt;
350
351 // Return the composite type.
353 if (!Composite(getType(), *this, Result))
354 return std::nullopt;
355 return Result;
356}
StringRef P
#define INT_TYPE_SWITCH(Expr, B)
Definition: PrimType.h:133
#define TYPE_SWITCH(Expr, B)
Definition: PrimType.h:113
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
APValue & getArrayInitializedElt(unsigned I)
Definition: APValue.h:510
std::string getAsString(const ASTContext &Ctx, QualType Ty) const
Definition: APValue.cpp:942
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:182
QualType getRecordType(const RecordDecl *Decl) const
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
Complex values, per C99 6.2.5p11.
Definition: Type.h:2844
This represents one expression.
Definition: Expr.h:110
Represents a member of a struct/union/class.
Definition: Decl.h:3025
A (possibly-)qualified type.
Definition: Type.h:737
A helper class that allows the use of isa/cast/dyncast to detect TagType objects of structs/unions/cl...
Definition: Type.h:5091
bool isIntegerType() const
isIntegerType() does not include complex integers (a GCC extension).
Definition: Type.h:7474
bool isFloatingType() const
Definition: Type.cpp:2186
QualType getType() const
Definition: Decl.h:717
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
ASTContext & getASTContext() const
Returns the AST context.
Definition: Context.h:61
std::optional< PrimType > classify(QualType T) const
Classifies a type.
Definition: Context.cpp:120
const APFloat & getAPFloat() const
Definition: Floating.h:40
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:213
Pointer narrow() const
Restricts the scope of an array element pointer.
Definition: Pointer.h:130
void deactivate() const
Deactivates an entire strurcutre.
Definition: Pointer.cpp:209
bool isInitialized() const
Checks if an object was initialized.
Definition: Pointer.cpp:147
bool isStatic() const
Checks if the storage is static.
Definition: Pointer.h:315
bool isDummy() const
Checks if the pointer points to a dummy value.
Definition: Pointer.h:342
int64_t getIndex() const
Returns the index into an array.
Definition: Pointer.h:372
Pointer atIndex(unsigned Idx) const
Offsets a pointer inside an array.
Definition: Pointer.h:104
bool isActive() const
Checks if the object is active.
Definition: Pointer.h:335
Pointer atField(unsigned Off) const
Creates a pointer to a field.
Definition: Pointer.h:116
T & deref() const
Dereferences the pointer, if it's live.
Definition: Pointer.h:396
Pointer getArray() const
Returns the parent array.
Definition: Pointer.h:222
bool isUnknownSizeArray() const
Checks if the structure is an array of unknown size.
Definition: Pointer.h:287
void activate() const
Activats a field.
Definition: Pointer.cpp:203
void operator=(const Pointer &P)
Definition: Pointer.cpp:51
QualType getType() const
Returns the type of the innermost field.
Definition: Pointer.h:239
bool isArrayElement() const
Checks if the pointer points to an array.
Definition: Pointer.h:291
bool isLive() const
Checks if the pointer is live.
Definition: Pointer.h:198
Pointer getBase() const
Returns a pointer to the object of which this pointer is a field.
Definition: Pointer.h:213
std::string toDiagnosticString(const ASTContext &Ctx) const
Converts the pointer to a string usable in diagnostics.
Definition: Pointer.cpp:140
bool isZero() const
Checks if the pointer is null.
Definition: Pointer.h:196
const Descriptor * getDeclDesc() const
Accessor for information about the declaration site.
Definition: Pointer.h:206
bool isOnePastEnd() const
Checks if the index is one past end.
Definition: Pointer.h:386
static bool hasSameArray(const Pointer &A, const Pointer &B)
Checks if two pointers can be subtracted.
Definition: Pointer.cpp:217
std::optional< APValue > toRValue(const Context &Ctx) const
Converts the pointer to an APValue that is an rvalue.
Definition: Pointer.cpp:221
APValue toAPValue() const
Converts the pointer to an APValue.
Definition: Pointer.cpp:85
const Descriptor * getFieldDesc() const
Accessors for information about the innermost field.
Definition: Pointer.h:232
void initialize() const
Initializes a field.
Definition: Pointer.cpp:170
bool isField() const
Checks if the item is a field in an object.
Definition: Pointer.h:200
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
const Base * getVirtualBase(const RecordDecl *RD) const
Returns a virtual base descriptor.
Definition: Record.cpp:50
unsigned getNumFields() const
Definition: Record.h:81
unsigned getNumVirtualBases() const
Definition: Record.h:100
llvm::iterator_range< const_field_iter > fields() const
Definition: Record.h:77
const Base * getBase(const RecordDecl *FD) const
Returns a base descriptor.
Definition: Record.cpp:36
std::optional< std::pair< bool, std::shared_ptr< InitMap > > > InitMapPtr
Definition: Descriptor.h:28
The JSON file list parser is used to communicate input to InstallAPI.
@ Result
The result type of a method or function.
#define bool
Definition: stdbool.h:20
Describes a memory block created by an allocation site.
Definition: Descriptor.h:88
unsigned getNumElems() const
Returns the number of elements stored in the block.
Definition: Descriptor.h:194
const ValueDecl * asValueDecl() const
Definition: Descriptor.h:164
const Decl * asDecl() const
Definition: Descriptor.h:161
const bool IsArray
Flag indicating if the block is an array.
Definition: Descriptor.h:122
bool isPrimitiveArray() const
Checks if the descriptor is of an array of primitives.
Definition: Descriptor.h:199
const Expr * asExpr() const
Definition: Descriptor.h:162
unsigned IsActive
Flag indicating if the field is the active member of a union.
Definition: Descriptor.h:75
unsigned IsInitialized
For primitive fields, it indicates if the field was initialized.
Definition: Descriptor.h:69
Describes a base class.
Definition: Record.h:36
const RecordDecl * Decl
Definition: Record.h:37
Describes a record field.
Definition: Record.h:28
const FieldDecl * Decl
Definition: Record.h:29