clang 20.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 "MemberPointer.h"
17#include "PrimType.h"
18#include "Record.h"
19#include "clang/AST/ExprCXX.h"
21
22using namespace clang;
23using namespace clang::interp;
24
26 : Pointer(Pointee, Pointee->getDescriptor()->getMetadataSize(),
27 Pointee->getDescriptor()->getMetadataSize()) {}
28
29Pointer::Pointer(Block *Pointee, uint64_t BaseAndOffset)
30 : Pointer(Pointee, BaseAndOffset, BaseAndOffset) {}
31
33 : Offset(P.Offset), PointeeStorage(P.PointeeStorage),
34 StorageKind(P.StorageKind) {
35
36 if (isBlockPointer() && PointeeStorage.BS.Pointee)
37 PointeeStorage.BS.Pointee->addPointer(this);
38}
39
40Pointer::Pointer(Block *Pointee, unsigned Base, uint64_t Offset)
41 : Offset(Offset), StorageKind(Storage::Block) {
42 assert((Base == RootPtrMark || Base % alignof(void *) == 0) && "wrong base");
43
44 PointeeStorage.BS = {Pointee, Base};
45
46 if (Pointee)
47 Pointee->addPointer(this);
48}
49
51 : Offset(P.Offset), PointeeStorage(P.PointeeStorage),
52 StorageKind(P.StorageKind) {
53
54 if (StorageKind == Storage::Block && PointeeStorage.BS.Pointee)
55 PointeeStorage.BS.Pointee->replacePointer(&P, this);
56}
57
59 if (!isBlockPointer())
60 return;
61
62 if (Block *Pointee = PointeeStorage.BS.Pointee) {
63 Pointee->removePointer(this);
64 PointeeStorage.BS.Pointee = nullptr;
65 Pointee->cleanup();
66 }
67}
68
70 // If the current storage type is Block, we need to remove
71 // this pointer from the block.
72 if (isBlockPointer()) {
73 if (P.isBlockPointer() && this->block() == P.block()) {
74 Offset = P.Offset;
75 PointeeStorage.BS.Base = P.PointeeStorage.BS.Base;
76 return;
77 }
78
79 if (Block *Pointee = PointeeStorage.BS.Pointee) {
80 Pointee->removePointer(this);
81 PointeeStorage.BS.Pointee = nullptr;
82 Pointee->cleanup();
83 }
84 }
85
86 StorageKind = P.StorageKind;
87 Offset = P.Offset;
88
89 if (P.isBlockPointer()) {
90 PointeeStorage.BS = P.PointeeStorage.BS;
91 PointeeStorage.BS.Pointee = P.PointeeStorage.BS.Pointee;
92
93 if (PointeeStorage.BS.Pointee)
94 PointeeStorage.BS.Pointee->addPointer(this);
95 } else if (P.isIntegralPointer()) {
96 PointeeStorage.Int = P.PointeeStorage.Int;
97 } else if (P.isFunctionPointer()) {
98 PointeeStorage.Fn = P.PointeeStorage.Fn;
99 } else if (P.isTypeidPointer()) {
100 PointeeStorage.Typeid = P.PointeeStorage.Typeid;
101 } else {
102 assert(false && "Unhandled storage kind");
103 }
104}
105
107 // If the current storage type is Block, we need to remove
108 // this pointer from the block.
109 if (isBlockPointer()) {
110 if (P.isBlockPointer() && this->block() == P.block()) {
111 Offset = P.Offset;
112 PointeeStorage.BS.Base = P.PointeeStorage.BS.Base;
113 return;
114 }
115
116 if (Block *Pointee = PointeeStorage.BS.Pointee) {
117 assert(P.block() != this->block());
118 Pointee->removePointer(this);
119 PointeeStorage.BS.Pointee = nullptr;
120 Pointee->cleanup();
121 }
122 }
123
124 StorageKind = P.StorageKind;
125 Offset = P.Offset;
126
127 if (P.isBlockPointer()) {
128 PointeeStorage.BS = P.PointeeStorage.BS;
129 PointeeStorage.BS.Pointee = P.PointeeStorage.BS.Pointee;
130
131 if (PointeeStorage.BS.Pointee)
132 PointeeStorage.BS.Pointee->addPointer(this);
133 } else if (P.isIntegralPointer()) {
134 PointeeStorage.Int = P.PointeeStorage.Int;
135 } else if (P.isFunctionPointer()) {
136 PointeeStorage.Fn = P.PointeeStorage.Fn;
137 } else if (P.isTypeidPointer()) {
138 PointeeStorage.Typeid = P.PointeeStorage.Typeid;
139 } else {
140 assert(false && "Unhandled storage kind");
141 }
142}
143
146
147 if (isZero())
148 return APValue(static_cast<const Expr *>(nullptr), CharUnits::Zero(), Path,
149 /*IsOnePastEnd=*/false, /*IsNullPtr=*/true);
150 if (isIntegralPointer())
151 return APValue(static_cast<const Expr *>(nullptr),
153 Path,
154 /*IsOnePastEnd=*/false, /*IsNullPtr=*/false);
155 if (isFunctionPointer())
156 return asFunctionPointer().toAPValue(ASTCtx);
157
158 if (isTypeidPointer()) {
159 TypeInfoLValue TypeInfo(PointeeStorage.Typeid.TypePtr);
160 return APValue(
162 TypeInfo, QualType(PointeeStorage.Typeid.TypeInfoType, 0)),
164 }
165
166 // Build the lvalue base from the block.
167 const Descriptor *Desc = getDeclDesc();
169 if (const auto *VD = Desc->asValueDecl())
170 Base = VD;
171 else if (const auto *E = Desc->asExpr()) {
172 // Create a DynamicAlloc base of the right type.
173 if (const auto *NewExpr = dyn_cast<CXXNewExpr>(E)) {
174 QualType AllocatedType;
175 if (NewExpr->isArray()) {
176 assert(Desc->isArray());
177 APInt ArraySize(64, static_cast<uint64_t>(Desc->getNumElems()),
178 /*IsSigned=*/false);
179 AllocatedType =
180 ASTCtx.getConstantArrayType(NewExpr->getAllocatedType(), ArraySize,
181 nullptr, ArraySizeModifier::Normal, 0);
182 } else {
183 AllocatedType = NewExpr->getAllocatedType();
184 }
185 // FIXME: Suboptimal counting of dynamic allocations. Move this to Context
186 // or InterpState?
187 static int ReportedDynamicAllocs = 0;
188 DynamicAllocLValue DA(ReportedDynamicAllocs++);
189 Base = APValue::LValueBase::getDynamicAlloc(DA, AllocatedType);
190 } else {
191 Base = E;
192 }
193 } else
194 llvm_unreachable("Invalid allocation type");
195
196 if (isUnknownSizeArray())
197 return APValue(Base, CharUnits::Zero(), Path,
198 /*IsOnePastEnd=*/isOnePastEnd(), /*IsNullPtr=*/false);
199
200 CharUnits Offset = CharUnits::Zero();
201
202 auto getFieldOffset = [&](const FieldDecl *FD) -> CharUnits {
203 // This shouldn't happen, but if it does, don't crash inside
204 // getASTRecordLayout.
205 if (FD->getParent()->isInvalidDecl())
206 return CharUnits::Zero();
207 const ASTRecordLayout &Layout = ASTCtx.getASTRecordLayout(FD->getParent());
208 unsigned FieldIndex = FD->getFieldIndex();
209 return ASTCtx.toCharUnitsFromBits(Layout.getFieldOffset(FieldIndex));
210 };
211
212 // Build the path into the object.
213 Pointer Ptr = *this;
214 while (Ptr.isField() || Ptr.isArrayElement()) {
215
216 if (Ptr.isArrayRoot()) {
217 // An array root may still be an array element itself.
218 if (Ptr.isArrayElement()) {
219 Ptr = Ptr.expand();
220 unsigned Index = Ptr.getIndex();
222 QualType ElemType = Ptr.getFieldDesc()->getElemQualType();
223 Offset += (Index * ASTCtx.getTypeSizeInChars(ElemType));
224 Ptr = Ptr.getArray();
225 } else {
227 {Ptr.getFieldDesc()->asDecl(), /*IsVirtual=*/false}));
228
229 if (const auto *FD =
230 dyn_cast_if_present<FieldDecl>(Ptr.getFieldDesc()->asDecl()))
231 Offset += getFieldOffset(FD);
232
233 Ptr = Ptr.getBase();
234 }
235 } else if (Ptr.isArrayElement()) {
236 Ptr = Ptr.expand();
237 unsigned Index;
238 if (Ptr.isOnePastEnd())
239 Index = Ptr.getArray().getNumElems();
240 else
241 Index = Ptr.getIndex();
242
243 QualType ElemType = Ptr.getFieldDesc()->getElemQualType();
244 Offset += (Index * ASTCtx.getTypeSizeInChars(ElemType));
246 Ptr = Ptr.getArray();
247 } else {
248 bool IsVirtual = false;
249
250 // Create a path entry for the field.
251 const Descriptor *Desc = Ptr.getFieldDesc();
252 if (const auto *BaseOrMember = Desc->asDecl()) {
253 if (const auto *FD = dyn_cast<FieldDecl>(BaseOrMember)) {
254 Ptr = Ptr.getBase();
255 Offset += getFieldOffset(FD);
256 } else if (const auto *RD = dyn_cast<CXXRecordDecl>(BaseOrMember)) {
257 IsVirtual = Ptr.isVirtualBaseClass();
258 Ptr = Ptr.getBase();
259 const Record *BaseRecord = Ptr.getRecord();
260
261 const ASTRecordLayout &Layout = ASTCtx.getASTRecordLayout(
262 cast<CXXRecordDecl>(BaseRecord->getDecl()));
263 if (IsVirtual)
264 Offset += Layout.getVBaseClassOffset(RD);
265 else
266 Offset += Layout.getBaseClassOffset(RD);
267
268 } else {
269 Ptr = Ptr.getBase();
270 }
271 Path.push_back(APValue::LValuePathEntry({BaseOrMember, IsVirtual}));
272 continue;
273 }
274 llvm_unreachable("Invalid field type");
275 }
276 }
277
278 // We assemble the LValuePath starting from the innermost pointer to the
279 // outermost one. SO in a.b.c, the first element in Path will refer to
280 // the field 'c', while later code expects it to refer to 'a'.
281 // Just invert the order of the elements.
282 std::reverse(Path.begin(), Path.end());
283
284 return APValue(Base, Offset, Path, /*IsOnePastEnd=*/isOnePastEnd(),
285 /*IsNullPtr=*/false);
286}
287
288void Pointer::print(llvm::raw_ostream &OS) const {
289 switch (StorageKind) {
290 case Storage::Block: {
291 const Block *B = PointeeStorage.BS.Pointee;
292 OS << "(Block) " << B << " {";
293
294 if (isRoot())
295 OS << "rootptr(" << PointeeStorage.BS.Base << "), ";
296 else
297 OS << PointeeStorage.BS.Base << ", ";
298
299 if (isElementPastEnd())
300 OS << "pastend, ";
301 else
302 OS << Offset << ", ";
303
304 if (B)
305 OS << B->getSize();
306 else
307 OS << "nullptr";
308 OS << "}";
309 } break;
310 case Storage::Int:
311 OS << "(Int) {";
312 OS << PointeeStorage.Int.Value << " + " << Offset << ", "
313 << PointeeStorage.Int.Desc;
314 OS << "}";
315 break;
316 case Storage::Fn:
317 OS << "(Fn) { " << asFunctionPointer().getFunction() << " + " << Offset
318 << " }";
319 break;
320 case Storage::Typeid:
321 OS << "(Typeid)";
322 }
323}
324
325std::string Pointer::toDiagnosticString(const ASTContext &Ctx) const {
326 if (isZero())
327 return "nullptr";
328
329 if (isIntegralPointer())
330 return (Twine("&(") + Twine(asIntPointer().Value + Offset) + ")").str();
331
332 return toAPValue(Ctx).getAsString(Ctx, getType());
333}
334
336 if (!isBlockPointer())
337 return true;
338
339 if (isRoot() && PointeeStorage.BS.Base == sizeof(GlobalInlineDescriptor)) {
340 const GlobalInlineDescriptor &GD =
341 *reinterpret_cast<const GlobalInlineDescriptor *>(block()->rawData());
343 }
344
345 assert(PointeeStorage.BS.Pointee &&
346 "Cannot check if null pointer was initialized");
347 const Descriptor *Desc = getFieldDesc();
348 assert(Desc);
349 if (Desc->isPrimitiveArray()) {
350 if (isStatic() && PointeeStorage.BS.Base == 0)
351 return true;
352
353 InitMapPtr &IM = getInitMap();
354
355 if (!IM)
356 return false;
357
358 if (IM->first)
359 return true;
360
361 return IM->second->isElementInitialized(getIndex());
362 }
363
364 if (asBlockPointer().Base == 0)
365 return true;
366
367 // Field has its bit in an inline descriptor.
368 return getInlineDesc()->IsInitialized;
369}
370
372 if (!isBlockPointer())
373 return;
374
375 assert(PointeeStorage.BS.Pointee && "Cannot initialize null pointer");
376 const Descriptor *Desc = getFieldDesc();
377
378 if (isRoot() && PointeeStorage.BS.Base == sizeof(GlobalInlineDescriptor)) {
379 GlobalInlineDescriptor &GD = *reinterpret_cast<GlobalInlineDescriptor *>(