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 "Char.h"
12#include "Context.h"
13#include "Floating.h"
14#include "Function.h"
15#include "InitMap.h"
16#include "Integral.h"
17#include "InterpBlock.h"
18#include "MemberPointer.h"
19#include "PrimType.h"
20#include "Record.h"
21#include "clang/AST/Expr.h"
22#include "clang/AST/ExprCXX.h"
23#include "clang/AST/RecordLayout.h"
24
25using namespace clang;
26using namespace clang::interp;
27
28// Helper to check if a Type can be passed to
29// ASTContext::getTypeSize().
30static bool validType(QualType T) {
31 if (const RecordDecl *RD = T->getAsRecordDecl())
32 return ASTContext::hasLayout(D: RD);
33 return !T->isDependentType() && !T->isUndeducedAutoType() &&
34 !T->isSpecificBuiltinType(K: BuiltinType::UnknownAny) &&
35 !T->isIncompleteType();
36}
37
38Pointer::Pointer(Block *Pointee)
39 : Pointer(Pointee, Pointee->getMetadataSize(), Pointee->getMetadataSize()) {
40}
41
42Pointer::Pointer(Block *Pointee, uint64_t BaseAndOffset)
43 : Pointer(Pointee, BaseAndOffset, BaseAndOffset) {}
44
45Pointer::Pointer(Block *Pointee, unsigned Base, uint64_t Offset)
46 : Offset(Offset), StorageKind(Storage::Block) {
47 assert(Pointee);
48 assert(Base % alignof(void *) == 0 && "wrong base");
49 assert(Base >= Pointee->getMetadataSize());
50
51 BS = {.Pointee: Pointee, .Base: Base, .Prev: nullptr, .Next: nullptr};
52 Pointee->addPointer(P: this);
53}
54
55Pointer::Pointer(const Pointer &P)
56 : Offset(P.Offset), StorageKind(P.StorageKind) {
57 switch (StorageKind) {
58 case Storage::Int:
59 Int = P.Int;
60 break;
61 case Storage::Block:
62 BS = P.BS;
63 if (BS.Pointee)
64 BS.Pointee->addPointer(P: this);
65 break;
66 case Storage::Fn:
67 Fn = P.Fn;
68 break;
69 case Storage::Typeid:
70 Typeid = P.Typeid;
71 break;
72 case Storage::String:
73 Str = P.Str;
74 break;
75 case Storage::Opaque:
76 Opaque = P.Opaque;
77 break;
78 }
79}
80
81Pointer::Pointer(Pointer &&P) : Offset(P.Offset), StorageKind(P.StorageKind) {
82 switch (StorageKind) {
83 case Storage::Int:
84 Int = P.Int;
85 break;
86 case Storage::Block:
87 BS = P.BS;
88 if (BS.Pointee)
89 BS.Pointee->replacePointer(Old: &P, New: this);
90 break;
91 case Storage::Fn:
92 Fn = P.Fn;
93 break;
94 case Storage::Typeid:
95 Typeid = P.Typeid;
96 break;
97 case Storage::String:
98 Str = P.Str;
99 break;
100 case Storage::Opaque:
101 Opaque = P.Opaque;
102 break;
103 }
104}
105
106Pointer::~Pointer() {
107 if (!isBlockPointer())
108 return;
109
110 if (Block *Pointee = BS.Pointee) {
111 Pointee->removePointer(P: this);
112 BS.Pointee = nullptr;
113 Pointee->cleanup();
114 }
115}
116
117Pointer &Pointer::operator=(const Pointer &P) {
118 // If the current storage type is Block, we need to remove
119 // this pointer from the block.
120 if (isBlockPointer()) {
121 if (P.isBlockPointer() && this->block() == P.block()) {
122 Offset = P.Offset;
123 BS.Base = P.BS.Base;
124 return *this;
125 }
126
127 if (Block *Pointee = BS.Pointee) {
128 Pointee->removePointer(P: this);
129 BS.Pointee = nullptr;
130 Pointee->cleanup();
131 }
132 }
133
134 StorageKind = P.StorageKind;
135 Offset = P.Offset;
136
137 switch (StorageKind) {
138 case Storage::Int:
139 Int = P.Int;
140 break;
141 case Storage::Block:
142 BS = P.BS;
143
144 if (BS.Pointee)
145 BS.Pointee->addPointer(P: this);
146 break;
147 case Storage::Fn:
148 Fn = P.Fn;
149 break;
150 case Storage::Typeid:
151 Typeid = P.Typeid;
152 break;
153 case Storage::String:
154 Str = P.Str;
155 break;
156 case Storage::Opaque:
157 Opaque = P.Opaque;
158 break;
159 }
160 return *this;
161}
162
163Pointer &Pointer::operator=(Pointer &&P) {
164 // If the current storage type is Block, we need to remove
165 // this pointer from the block.
166 if (isBlockPointer()) {
167 if (P.isBlockPointer() && this->block() == P.block()) {
168 Offset = P.Offset;
169 BS.Base = P.BS.Base;
170 return *this;
171 }
172
173 if (Block *Pointee = BS.Pointee) {
174 Pointee->removePointer(P: this);
175 BS.Pointee = nullptr;
176 Pointee->cleanup();
177 }
178 }
179
180 StorageKind = P.StorageKind;
181 Offset = P.Offset;
182
183 switch (StorageKind) {
184 case Storage::Int:
185 Int = P.Int;
186 break;
187 case Storage::Block:
188 BS = P.BS;
189
190 if (BS.Pointee)
191 BS.Pointee->addPointer(P: this);
192 break;
193 case Storage::Fn:
194 Fn = P.Fn;
195 break;
196 case Storage::Typeid:
197 Typeid = P.Typeid;
198 break;
199 case Storage::String:
200 Str = P.Str;
201 break;
202 case Storage::Opaque:
203 Opaque = P.Opaque;
204 break;
205 }
206 return *this;
207}
208
209bool Pointer::operator==(const Pointer &P) const {
210 if (StorageKind != P.StorageKind)
211 return false;
212
213 switch (StorageKind) {
214 case Storage::Int:
215 return P.Int.Value == Int.Value && P.Int.Ty == Int.Ty && P.Offset == Offset;
216 case Storage::Block:
217 return P.view() == view();
218 case Storage::Fn:
219 return P.Fn.Func == Fn.Func && P.Offset == Offset;
220 case Storage::Typeid:
221 llvm_unreachable("typeid in operator==?");
222 case Storage::String:
223 return Str.Base == P.Str.Base && Offset == P.Offset;
224 case Storage::Opaque:
225 if (P.Opaque.Base != Opaque.Base ||
226 P.Opaque.PathLength != Opaque.PathLength || P.Offset != Offset)
227 return false;
228
229 for (unsigned I = 0; I != Opaque.PathLength; ++I) {
230 if (Opaque.Path[I].Kind != P.Opaque.Path[I].Kind)
231 return false;
232 switch (Opaque.Path[I].Kind) {
233 case PointerPathEntry::Base:
234 if (Opaque.Path[I].RD != P.Opaque.Path[I].RD)
235 return false;
236 break;
237 case PointerPathEntry::Array:
238 case PointerPathEntry::NegativeArray:
239 if (Opaque.Path[I].Index != P.Opaque.Path[I].Index)
240 return false;
241 break;
242 case PointerPathEntry::Field:
243 if (Opaque.Path[I].FD != P.Opaque.Path[I].FD)
244 return false;
245 break;
246 }
247 }
248 }
249 return true;
250}
251
252APValue Pointer::toAPValue(const ASTContext &ASTCtx) const {
253 llvm::SmallVector<APValue::LValuePathEntry, 5> Path;
254
255 if (isZero())
256 return APValue(APValue::LValueBase(), CharUnits::Zero(), Path,
257 /*IsOnePastEnd=*/false, /*IsNullPtr=*/true);
258
259 switch (StorageKind) {
260 case Storage::Int:
261 return APValue(static_cast<const Expr *>(nullptr),
262 CharUnits::fromQuantity(Quantity: asIntPointer().Value + this->Offset),
263 Path,
264 /*IsOnePastEnd=*/false, /*IsNullPtr=*/false);
265 case Storage::Block:
266 // See below.
267 break;
268 case Storage::Fn: {
269 const FunctionPointer &FP = asFunctionPointer();
270 if (const FunctionDecl *FD = FP.Func->getDecl())
271 return APValue(FD, CharUnits::fromQuantity(Quantity: Offset), {},
272 /*OnePastTheEnd=*/false, /*IsNull=*/false);
273 return APValue(FP.Func->getExpr(), CharUnits::fromQuantity(Quantity: Offset), {},
274 /*OnePastTheEnd=*/false, /*IsNull=*/false);
275 } break;
276 case Storage::Typeid: {
277 TypeInfoLValue TypeInfo(Typeid.TypePtr);
278 return APValue(APValue::LValueBase::getTypeInfo(
279 LV: TypeInfo, TypeInfo: QualType(Typeid.TypeInfoType, 0)),
280 CharUnits::Zero(), {},
281 /*OnePastTheEnd=*/false, /*IsNull=*/false);
282 } break;
283 case Storage::String:
284 if (Offset != 0 || Str.Decayed)
285 Path.push_back(Elt: APValue::LValuePathEntry::ArrayIndex(Index: Offset));
286
287 return APValue(APValue::LValueBase(Str.Base),
288 CharUnits::fromQuantity(Quantity: Offset * elemSize()), Path,
289 /*OnePastTheEnd=*/false, /*IsNull=*/false);
290 case Storage::Opaque: {
291 if (!Opaque.Base.getType()->isPointerType()) {
292 for (const PointerPathEntry &Entry : Opaque.path()) {
293 switch (Entry.Kind) {
294 case PointerPathEntry::Field:
295 Path.push_back(Elt: APValue::LValuePathEntry({Entry.FD, false}));
296 break;
297 case PointerPathEntry::Base:
298 Path.push_back(Elt: APValue::LValuePathEntry(
299 {Entry.RD.getPointer(), Entry.RD.getInt()}));
300 break;
301 case PointerPathEntry::Array:
302 Path.push_back(Elt: APValue::LValuePathEntry::ArrayIndex(Index: Entry.Index));
303 break;
304 case PointerPathEntry::NegativeArray:
305 Path.push_back(Elt: APValue::LValuePathEntry::ArrayIndex(Index: -Entry.Index));
306 break;
307 }
308 }
309 }
310 size_t LayoutOffset = Opaque.computeLayoutOffset(ASTCtx).value_or(u: 0);
311 size_t ElemSize = 0;
312 if (validType(T: Opaque.getFieldType()))
313 ElemSize = ASTCtx.getTypeSizeInChars(T: Opaque.getFieldType()).getQuantity();
314 auto Offset =
315 CharUnits::fromQuantity(Quantity: LayoutOffset + (this->Offset * ElemSize));
316
317 APValue::LValueBase Base;
318 if (const Expr *E = Opaque.Base.asExpr())
319 Base = E;
320 else
321 Base = Opaque.Base.asValueDecl();
322 APValue Result =
323 APValue(Base, Offset, Path, Opaque.isOnePastEnd(), /*IsNullPtr=*/false);
324 Result.setConstexprUnknown(Opaque.isConstexprUnknown());
325 return Result;
326 }
327 }
328
329 assert(isBlockPointer());
330 // Build the lvalue base from the block.
331 const Descriptor *Desc = getDeclDesc();
332 APValue::LValueBase Base;
333 if (const auto *VD = Desc->asValueDecl())
334 Base = VD;
335 else if (const auto *E = Desc->asExpr()) {
336 if (block()->isDynamic()) {
337 QualType AllocatedType = getDeclPtr().getFieldDesc()->getDataType(Ctx: ASTCtx);
338 DynamicAllocLValue DA(*block()->DynAllocId);
339 Base = APValue::LValueBase::getDynamicAlloc(LV: DA, Type: AllocatedType);
340 } else {
341 Base = E;
342 }
343 } else
344 llvm_unreachable("Invalid allocation type");
345
346 CharUnits Offset = CharUnits::Zero();
347
348 auto getFieldOffset = [&](const FieldDecl *FD) -> std::optional<CharUnits> {
349 if (!ASTContext::hasLayout(D: FD->getParent()))
350 return std::nullopt;
351 // This shouldn't happen, but if it does, don't crash inside
352 // getASTRecordLayout.
353 const ASTRecordLayout &Layout = ASTCtx.getASTRecordLayout(D: FD->getParent());
354 unsigned FieldIndex = FD->getFieldIndex();
355 return ASTCtx.toCharUnitsFromBits(BitSize: Layout.getFieldOffset(FieldNo: FieldIndex));
356 };
357
358 // Build the path into the object.
359 bool OnePastEnd = isOnePastEnd() && !isZeroSizeArray();
360
361 PtrView Ptr = view();
362 while (Ptr.isField() || Ptr.isArrayElement()) {
363
364 if (Ptr.isArrayRoot()) {
365 // An array root may still be an array element itself.
366 if (Ptr.isArrayElement()) {
367 Ptr = Ptr.expand();
368 const Descriptor *Desc = Ptr.getFieldDesc();
369 unsigned Index = Ptr.getIndex();
370 QualType ElemType = Desc->getElemQualType();
371 Offset += (Index * ASTCtx.getTypeSizeInChars(T: ElemType));
372 if (Ptr.getArray().getFieldDesc()->IsArray)
373 Path.push_back(Elt: APValue::LValuePathEntry::ArrayIndex(Index));
374 Ptr = Ptr.getArray();
375 } else {
376 const Descriptor *Desc = Ptr.getFieldDesc();
377 const auto *Dcl = Desc->asDecl();
378 Path.push_back(Elt: APValue::LValuePathEntry({Dcl, /*IsVirtual=*/false}));
379
380 if (const auto *FD = dyn_cast_if_present<FieldDecl>(Val: Dcl)) {
381 if (std::optional<CharUnits> FieldOffset = getFieldOffset(FD))
382 Offset += *FieldOffset;
383 else
384 return APValue();
385 }
386
387 Ptr = Ptr.getBase();
388 }
389 } else if (Ptr.isArrayElement()) {
390 Ptr = Ptr.expand();
391 const Descriptor *Desc = Ptr.getFieldDesc();
392 unsigned Index;
393 if (Ptr.isOnePastEnd()) {
394 Index = Ptr.getArray().getNumElems();
395 OnePastEnd = false;
396 } else
397 Index = Ptr.getIndex();
398
399 QualType ElemType = Desc->getElemQualType();
400 if (const auto *RD = ElemType->getAsRecordDecl();
401 RD && !RD->getDefinition()) {
402 // Ignore this for the offset.
403 } else {
404 Offset += (Index * ASTCtx.getTypeSizeInChars(T: ElemType));
405 }
406 if (Ptr.getArray().getFieldDesc()->IsArray)
407 Path.push_back(Elt: APValue::LValuePathEntry::ArrayIndex(Index));
408 Ptr = Ptr.getArray();
409 } else {
410 const Descriptor *Desc = Ptr.getFieldDesc();
411
412 // Create a path entry for the field.
413 if (const auto *BaseOrMember = Desc->asDecl()) {
414 bool IsVirtual = false;
415 if (const auto *FD = dyn_cast<FieldDecl>(Val: BaseOrMember)) {
416 Ptr = Ptr.getBase();
417 if (std::optional<CharUnits> FieldOffset = getFieldOffset(FD))
418 Offset += *FieldOffset;
419 else
420 return APValue();
421 } else if (const auto *RD = dyn_cast<CXXRecordDecl>(Val: BaseOrMember)) {
422 IsVirtual = Ptr.isVirtualBaseClass();
423 Ptr = Ptr.getBase();
424 const Record *BaseRecord = Ptr.getRecord();
425
426 if (!ASTContext::hasLayout(D: BaseRecord->getDecl()))
427 return APValue();
428
429 const ASTRecordLayout &Layout = ASTCtx.getASTRecordLayout(
430 D: cast<CXXRecordDecl>(Val: BaseRecord->getDecl()));
431 if (IsVirtual)
432 Offset += Layout.getVBaseClassOffset(VBase: RD);
433 else
434 Offset += Layout.getBaseClassOffset(Base: RD);
435
436 } else {
437 Ptr = Ptr.getBase();
438 }
439 Path.push_back(Elt: APValue::LValuePathEntry({BaseOrMember, IsVirtual}));
440 continue;
441 }
442 llvm_unreachable("Invalid field type");
443 }
444 }
445
446 // We assemble the LValuePath starting from the innermost pointer to the
447 // outermost one. SO in a.b.c, the first element in Path will refer to
448 // the field 'c', while later code expects it to refer to 'a'.
449 // Just invert the order of the elements.
450 std::reverse(first: Path.begin(), last: Path.end());
451
452 auto Result = APValue(Base, Offset, Path, OnePastEnd);
453 Result.setConstexprUnknown(isConstexprUnknown());
454 return Result;
455}
456
457void Pointer::print(llvm::raw_ostream &OS) const {
458 switch (StorageKind) {
459 case Storage::Block: {
460 const Block *B = BS.Pointee;
461 OS << "(Block) " << B << " {";
462
463 if (isRoot())
464 OS << "rootptr(" << BS.Base << "), ";
465 else
466 OS << BS.Base << ", ";
467
468 if (isElementPastEnd())
469 OS << "pastend, ";
470 else
471 OS << Offset << ", ";
472
473 if (B)
474 OS << B->getSize();
475 else
476 OS << "nullptr";
477 OS << "}";
478 } break;
479 case Storage::Int:
480 OS << "(Int) {" << Int.Value << " + " << Offset << ", " << Int.Ty << "}";
481 break;
482 case Storage::Fn:
483 OS << "(Fn) { " << Fn.Func << " + " << Offset << " }";
484 break;
485 case Storage::Typeid:
486 OS << "(Typeid) { " << (const void *)asTypeidPointer().TypePtr << ", "
487 << (const void *)asTypeidPointer().TypeInfoType << " + " << Offset
488 << "}";
489 break;
490 case Storage::String:
491 OS << "(String) { " << (const void *)Str.getLiteral() << ' ';
492 Str.getLiteral()->outputString(OS);
493 OS << ". ID: " << Str.ID << " + " << Offset << "}";
494 break;
495 case Storage::Opaque:
496 OS << "(Opaque) { Base: " << Opaque.Base << ", "
497 << Opaque.FieldType.getPointer() << " Length: " << Opaque.PathLength
498 << ". PastEnd: " << Opaque.isOnePastEnd();
499 OS << "} + " << Offset;
500 break;
501 }
502}
503
504/// Compute an offset that can be used to compare the pointer to another one
505/// with the same base. To get accurate results, we basically _have to_ compute
506/// the lvalue offset using the ASTRecordLayout.
507///
508/// This function will fail if we're trying to get the type size of a forward
509/// declaration.
510///
511// FIXME: We're still mixing values from the record layout with our internal
512// offsets, which will inevitably lead to cryptic errors.
513std::optional<size_t>
514Pointer::computeOffsetForComparison(const ASTContext &ASTCtx) const {
515 switch (StorageKind) {
516 case Storage::Int:
517 return Int.Value + Offset;
518 case Storage::Block:
519 // See below.
520 break;
521 case Storage::Fn:
522 return getIntegerRepresentation();
523 case Storage::Typeid:
524 return reinterpret_cast<uintptr_t>(asTypeidPointer().TypePtr) + Offset;
525 case Storage::String:
526 return reinterpret_cast<uintptr_t>(Str.getLiteral()) + Offset;
527 case Storage::Opaque:
528 return computeLayoutOffset(ASTCtx);
529 }
530
531 auto getTypeSize = [&](QualType T) -> std::optional<size_t> {
532 if (!validType(T))
533 return std::nullopt;
534 return ASTCtx.getTypeSizeInChars(T).getQuantity();
535 };
536
537 size_t Result = 0;
538 PtrView P = view();
539 while (true) {
540 if (P.isVirtualBaseClass()) {
541 Result += getInlineDesc()->Offset;
542 P = P.getBase();
543 continue;
544 }
545
546 if (P.isBaseClass()) {
547 Result += P.getInlineDesc()->Offset - sizeof(InlineDescriptor);
548 P = P.getBase();
549 continue;
550 }
551 if (P.isArrayElement()) {
552 P = P.expand();
553 Result += (P.getIndex() * P.elemSize());
554 P = P.getArray();
555 continue;
556 }
557
558 if (P.isRoot()) {
559 if (P.isOnePastEnd()) {
560 if (auto Size = getTypeSize(P.getDeclDesc()->getType()))
561 Result += *Size;
562 else
563 return std::nullopt;
564 }
565 break;
566 }
567
568 assert(P.getField());
569 const Record *R = P.getBase().getRecord();
570 assert(R);
571
572 if (!ASTContext::hasLayout(D: R->getDecl()))
573 return std::nullopt;
574 const ASTRecordLayout &Layout = ASTCtx.getASTRecordLayout(D: R->getDecl());
575 Result += ASTCtx
576 .toCharUnitsFromBits(
577 BitSize: Layout.getFieldOffset(FieldNo: P.getField()->getFieldIndex()))
578 .getQuantity();
579
580 if (P.isOnePastEnd()) {
581 if (auto Size = getTypeSize(P.getField()->getType()))
582 Result += *Size;
583 else
584 return std::nullopt;
585 }
586
587 P = P.getBase();
588 if (P.isRoot())
589 break;
590 }
591 return Result;
592}
593
594std::optional<size_t>
595Pointer::computeLayoutOffset(const ASTContext &ASTCtx) const {
596 switch (StorageKind) {
597 case Storage::Int:
598 return Int.Value + Offset;
599 case Storage::Block:
600 // See below.
601 break;
602 case Storage::Fn:
603 return getIntegerRepresentation();
604 case Storage::Typeid:
605 return reinterpret_cast<uintptr_t>(asTypeidPointer().TypePtr) + Offset;
606 case Storage::String:
607 return Offset * Str.getLiteral()->getCharByteWidth();
608 case Storage::Opaque:
609 if (auto O = Opaque.computeLayoutOffset(ASTCtx)) {
610 size_t TypeSize = 0;
611 if (QualType FT = Opaque.getFieldType(); validType(T: FT))
612 TypeSize = ASTCtx.getTypeSizeInChars(T: FT).getQuantity();
613 return *O + (Offset * TypeSize);
614 }
615 return std::nullopt;
616 }
617
618 auto getTypeSize = [&](QualType T) -> std::optional<size_t> {
619 if (!validType(T))
620 return std::nullopt;
621 return ASTCtx.getTypeSizeInChars(T).getQuantity();
622 };
623
624 auto getRecordDecl = [&](PtrView P) -> const CXXRecordDecl * {
625 if (const Record *R = P.getRecord())
626 return cast<CXXRecordDecl>(Val: R->getDecl());
627 return cast<CXXRecordDecl>(Val: P.getFieldDesc()->asDecl());
628 };
629
630 auto getRecordSize = [&](const RecordDecl *RD) -> unsigned {
631 CanQualType RecordTy = ASTCtx.getCanonicalTagType(TD: RD);
632 return ASTCtx.getTypeSizeInChars(T: RecordTy).getQuantity();
633 };
634
635 size_t Result = 0;
636 PtrView P = view();
637 while (true) {
638 if (P.isBaseClass()) {
639 const CXXRecordDecl *BaseRD = getRecordDecl(P.getBase());
640 if (!ASTContext::hasLayout(D: BaseRD))
641 return std::nullopt;
642 const ASTRecordLayout &Layout = ASTCtx.getASTRecordLayout(D: BaseRD);
643 const CXXRecordDecl *RD = getRecordDecl(P);
644 if (P.isVirtualBaseClass())
645 Result += Layout.getVBaseClassOffset(VBase: RD).getQuantity();
646 else
647 Result += Layout.getBaseClassOffset(Base: RD).getQuantity();
648
649 if (P.isOnePastEnd())
650 Result += getRecordSize(RD);
651
652 P = P.getBase();
653 continue;
654 }
655
656 if (P.isArrayElement()) {
657 P = P.expand();
658 assert(P.getFieldDesc()->isArray());
659 if (std::optional<size_t> ElemSize =
660 getTypeSize(P.getFieldDesc()->getElemQualType()))
661 Result += *ElemSize * P.getIndex();
662 else
663 return std::nullopt;
664
665 P = P.getArray();
666 continue;
667 }
668
669 if (P.isRoot()) {
670 if (P.isPastEnd() || P.isOnePastEnd()) {
671 if (std::optional<size_t> Size =
672 getTypeSize(P.getDeclDesc()->getType()))
673 Result += *Size * P.getIndex();
674 else
675 return std::nullopt;
676 }
677 break;
678 }
679
680 assert(P.getField());
681 const FieldDecl *F = P.getField();
682 if (!ASTContext::hasLayout(D: F->getParent()))
683 return std::nullopt;
684 const ASTRecordLayout &Layout = ASTCtx.getASTRecordLayout(D: F->getParent());
685 Result +=
686 ASTCtx.toCharUnitsFromBits(BitSize: Layout.getFieldOffset(FieldNo: F->getFieldIndex()))
687 .getQuantity();
688
689 if (P.isPastEnd() || P.isOnePastEnd()) {
690 if (std::optional<size_t> Size = getTypeSize(F->getType()))
691 Result += *Size * P.getIndex();
692 else
693 return std::nullopt;
694 }
695
696 P = P.getBase();
697 if (P.isRoot())
698 break;
699 }
700 return Result;
701}
702
703std::string Pointer::toDiagnosticString(const ASTContext &Ctx) const {
704 if (isZero())
705 return "nullptr";
706
707 if (isIntegralPointer())
708 return (Twine("&(") + Twine(asIntPointer().Value + Offset) + ")").str();
709
710 QualType Ty = getType();
711 if (Ty->isLValueReferenceType())
712 Ty = Ty->getPointeeType();
713 return toAPValue(ASTCtx: Ctx).getAsString(Ctx, Ty);
714}
715
716bool Pointer::isInitialized() const {
717 if (!isBlockPointer())
718 return true;
719
720 if (isRoot() && BS.Base == sizeof(GlobalInlineDescriptor) &&
721 Offset == BS.Base) {
722 const auto &GD = block()->getBlockDesc<GlobalInlineDescriptor>();
723 return GD.InitState == GlobalInitState::Initialized;
724 }
725
726 assert(BS.Pointee && "Cannot check if null pointer was initialized");
727 const Descriptor *Desc = getFieldDesc();
728 assert(Desc);
729 if (Desc->isPrimitiveArray())
730 return isElementInitialized(Index: getIndex());
731
732 if (asBlockPointer().Base == 0)
733 return true;
734 // Field has its bit in an inline descriptor.
735 return getInlineDesc()->IsInitialized;
736}
737
738bool PtrView::isElementInitialized(unsigned Index) const {
739 const Descriptor *Desc = getFieldDesc();
740 assert(Desc);
741
742 if (Pointee->isStatic() && Base == 0)
743 return true;
744
745 if (isRoot() && Base == sizeof(GlobalInlineDescriptor) && Offset == Base) {
746 const auto &GD = Pointee->getBlockDesc<GlobalInlineDescriptor>();
747 return GD.InitState == GlobalInitState::Initialized;
748 }
749
750 if (Desc->isPrimitiveArray()) {
751 InitMapPtr IM = getInitMap();
752
753 if (IM.allInitialized())
754 return true;
755
756 if (!IM.hasInitMap())
757 return false;
758 return IM->isElementInitialized(I: Index);
759 }
760 return isInitialized();
761}
762
763bool Pointer::isElementAlive(unsigned Index) const {
764 assert(getFieldDesc()->isPrimitiveArray());
765
766 InitMapPtr &IM = getInitMap();
767 if (!IM.hasInitMap())
768 return true;
769
770 if (IM.allInitialized())
771 return true;
772
773 return IM->isElementAlive(I: Index);
774}
775
776Lifetime PtrView::getLifetime() const {
777 if (Base < sizeof(InlineDescriptor))
778 return Lifetime::Started;
779
780 if (inArray() && !isArrayRoot()) {
781 InitMapPtr &IM = getInitMap();
782
783 if (!IM.hasInitMap()) {
784 if (IM.allInitialized())
785 return Lifetime::Started;
786 return getArray().getLifetime();
787 }
788
789 return IM->isElementAlive(I: getIndex()) ? Lifetime::Started : Lifetime::Ended;
790 }
791
792 return getInlineDesc()->LifeState;
793}
794
795void PtrView::setLifeState(Lifetime L) const {
796 if (Base < sizeof(InlineDescriptor))
797 return;
798
799 if (inArray() && !isArrayRoot()) {
800 assert(L == Lifetime::Started || L == Lifetime::Ended);
801 const Descriptor *Desc = getFieldDesc();
802 InitMapPtr &IM = getInitMap();
803 if (!IM.hasInitMap())
804 IM.setInitMap(new InitMap(Desc->getNumElems(), IM.allInitialized()));
805
806 if (L == Lifetime::Ended)
807 IM->endElementLifetime(I: getIndex());
808 else if (L == Lifetime::Started)
809 IM->startElementLifetime(I: getIndex());
810 assert(isArrayRoot() || (this->getLifetime() == L));
811 return;
812 }
813
814 getInlineDesc()->LifeState = L;
815}
816
817void PtrView::initialize() const {
818 if (isRoot() && Base == sizeof(GlobalInlineDescriptor) && Offset == Base) {
819 auto &GD = Pointee->getBlockDesc<GlobalInlineDescriptor>();
820 GD.InitState = GlobalInitState::Initialized;
821 return;
822 }
823
824 const Descriptor *Desc = getFieldDesc();
825 assert(Desc);
826 if (Desc->isPrimitiveArray()) {
827 if (Desc->getNumElems() != 0)
828 initializeElement(Index: getIndex());
829 return;
830 }
831
832 // Field has its bit in an inline descriptor.
833 assert(Base != 0 && "Only composite fields can be initialised");
834 getInlineDesc()->IsInitialized = true;
835 getInlineDesc()->LifeState = Lifetime::Started;
836}
837
838void PtrView::initializeElement(unsigned Index) const {
839 // Primitive global arrays don't have an initmap.
840 if (Pointee->isStatic() && Base == 0)
841 return;
842
843 assert(Index < getFieldDesc()->getNumElems());
844
845 InitMapPtr &IM = getInitMap();
846 if (IM.allInitialized())
847 return;
848
849 if (!IM.hasInitMap()) {
850 const Descriptor *Desc = getFieldDesc();
851 IM.setInitMap(new InitMap(Desc->getNumElems()));
852 }
853 assert(IM.hasInitMap());
854
855 if (IM->initializeElement(I: Index))
856 IM.noteAllInitialized();
857}
858
859void Pointer::initializeAllElements() const {
860 assert(getFieldDesc()->isPrimitiveArray());
861 assert(isArrayRoot());
862
863 getInitMap().noteAllInitialized();
864}
865
866bool PtrView::allElementsInitialized() const {
867 assert(getFieldDesc()->isPrimitiveArray());
868 assert(isArrayRoot());
869
870 if (Pointee->isStatic() && Base == 0)
871 return true;
872
873 if (isRoot() && Base == sizeof(GlobalInlineDescriptor) && Offset == Base) {
874 const auto &GD = Pointee->getBlockDesc<GlobalInlineDescriptor>();
875 return GD.InitState == GlobalInitState::Initialized;
876 }
877
878 InitMapPtr IM = getInitMap();
879 return IM.allInitialized();
880}
881
882bool Pointer::allElementsAlive() const {
883 assert(getFieldDesc()->isPrimitiveArray());
884 assert(isArrayRoot());
885
886 if (isStatic() && BS.Base == 0)
887 return true;
888
889 if (isRoot() && BS.Base == sizeof(GlobalInlineDescriptor) &&
890 Offset == BS.Base) {
891 const auto &GD = block()->getBlockDesc<GlobalInlineDescriptor>();
892 return GD.InitState == GlobalInitState::Initialized;
893 }
894
895 InitMapPtr &IM = getInitMap();
896 return IM.allInitialized() || (IM.hasInitMap() && IM->allElementsAlive());
897}
898
899void PtrView::activate() const {
900 // Field has its bit in an inline descriptor.
901 assert(Base != 0 && "Only composite fields can be activated");
902
903 if (isRoot() && Base == sizeof(GlobalInlineDescriptor))
904 return;
905 if (!getInlineDesc()->InUnion)
906 return;
907
908 std::function<void(PtrView P)> activate;
909 activate = [&activate](PtrView P) -> void {
910 P.getInlineDesc()->IsActive = true;
911 P.startLifetime();
912 if (const Record *R = P.getRecord(); R && !R->isUnion()) {
913 for (const Record::Field &F : R->fields()) {
914 PtrView FieldPtr = P.atField(Offset: F.Offset);
915 if (!FieldPtr.getInlineDesc()->IsActive)
916 activate(FieldPtr);
917 }
918 // FIXME: Bases?
919 }
920 };
921
922 std::function<void(PtrView &)> deactivate;
923 deactivate = [&deactivate](PtrView &P) -> void {
924 P.getInlineDesc()->IsActive = false;
925
926 if (const Record *R = P.getRecord()) {
927 for (const Record::Field &F : R->fields()) {
928 PtrView FieldPtr = P.atField(Offset: F.Offset);
929 if (FieldPtr.getInlineDesc()->IsActive)
930 deactivate(FieldPtr);
931 }
932 // FIXME: Bases?
933 }
934 };
935
936 PtrView B = *this;
937 // Primitive array elements can't be activated individually, so
938 // look at the array root instead.
939 if (B.getFieldDesc()->isPrimitiveArray() && B.isArrayElement())
940 B = B.getArray();
941
942 while (!B.isRoot() && B.inUnion()) {
943 activate(B);
944
945 // When walking up the pointer chain, deactivate
946 // all union child pointers that aren't on our path.
947 PtrView Cur = B;
948 B = B.getBase();
949 if (const Record *BR = B.getRecord(); BR && BR->isUnion()) {
950 for (const Record::Field &F : BR->fields()) {
951 PtrView FieldPtr = B.atField(Offset: F.Offset);
952 if (FieldPtr != Cur)
953 deactivate(FieldPtr);
954 }
955 }
956 }
957}
958
959bool Pointer::hasSameBase(const Pointer &A, const Pointer &B) {
960 // Two null pointers always have the same base.
961 if (A.isZero() && B.isZero())
962 return true;
963
964 // We allow comparisons between opaque pointers and block pointers, provided
965 // they have the same declaration as base.
966 if (A.StorageKind != B.StorageKind) {
967 if (A.isOpaquePointer() && A.Opaque.Base.isVarDecl() &&
968 B.isBlockPointer()) {
969 if (const VarDecl *BDecl = B.block()->getDescriptor()->asVarDecl())
970 return BDecl == A.Opaque.Base.asVarDecl()->getMostRecentDecl();
971 return false;
972 }
973 if (B.isOpaquePointer() && B.Opaque.Base.isVarDecl() &&
974 A.isBlockPointer()) {
975 if (const VarDecl *ADecl = A.block()->getDescriptor()->asVarDecl())
976 return ADecl == B.Opaque.Base.asVarDecl()->getMostRecentDecl();
977 return false;
978 }
979 return false;
980 }
981
982 switch (A.StorageKind) {
983 case Storage::Int:
984 return true;
985 case Storage::Block:
986 return A.BS.Pointee == B.BS.Pointee;
987 case Storage::Fn:
988 return true;
989 case Storage::Typeid:
990 return A.asTypeidPointer().TypePtr == B.asTypeidPointer().TypePtr;
991 case Storage::String:
992 return A.Str.ID == B.Str.ID && A.Str.getLiteral() == B.Str.getLiteral();
993 case Storage::Opaque:
994 if (A.Opaque.Base.isExpr())
995 return B.Opaque.Base.isExpr() && A.Opaque.Base == B.Opaque.Base;
996 if (A.Opaque.Base.isVarDecl())
997 return B.Opaque.Base.isVarDecl() &&
998 A.Opaque.Base.asVarDecl()->getMostRecentDecl() ==
999 B.Opaque.Base.asVarDecl()->getMostRecentDecl();
1000 return false;
1001 }
1002 llvm_unreachable("should have been handled by the fully covered switch");
1003}
1004
1005bool Pointer::pointToSameBlock(const Pointer &A, const Pointer &B) {
1006 if (!A.isBlockPointer() || !B.isBlockPointer())
1007 return false;
1008 return A.block() == B.block();
1009}
1010
1011bool Pointer::elemsOfSameArray(const Pointer &A, const Pointer &B) {
1012 assert(hasSameBase(A, B));
1013 assert(A.isBlockPointer());
1014 assert(B.isBlockPointer());
1015
1016 if (A.BS.Base == B.BS.Base)
1017 return true;
1018
1019 if (A.isBaseClass() || B.isBaseClass())
1020 return false;
1021
1022 if (A.getField() || B.getField())
1023 return false;
1024
1025 auto closestArray = [](const Pointer &P) -> PtrView {
1026 if (P.isArrayRoot())
1027 return P.view();
1028
1029 PtrView V = P.view();
1030 if (V.isArrayElement() || V.isOnePastEnd())
1031 V = V.expand().getArray();
1032
1033 if (P.isRoot())
1034 return P.view();
1035
1036 while (!V.isRoot() && !V.getFieldDesc()->IsArray) {
1037 if (V.isArrayElement()) {
1038 V = V.expand().getArray();
1039 break;
1040 }
1041 V = V.getBase();
1042 }
1043 return V;
1044 };
1045
1046 if (closestArray(A) != closestArray(B))
1047 return false;
1048
1049 return true;
1050}
1051
1052// FIXME: This should return true for string pointers.
1053bool Pointer::pointsToLiteral() const {
1054 if (isZero())
1055 return false;
1056
1057 if (isDynamic())
1058 return false;
1059
1060 const Expr *E = getRootExpr();
1061 return E && !isa<MaterializeTemporaryExpr, StringLiteral>(Val: E);
1062}
1063
1064bool Pointer::pointsToLabel() const {
1065 if (isZero())
1066 return false;
1067
1068 if (isOpaquePointer())
1069 return isa_and_nonnull<AddrLabelExpr>(Val: Opaque.Base.asExpr());
1070 return false;
1071}
1072
1073std::optional<std::pair<PtrView, PtrView>>
1074Pointer::computeSplitPoint(const Pointer &A, const Pointer &B) {
1075 if (!A.isBlockPointer() || !B.isBlockPointer())
1076 return std::nullopt;
1077
1078 if (A.asBlockPointer().Pointee != B.asBlockPointer().Pointee)
1079 return std::nullopt;
1080 if (A.isRoot() && B.isRoot())
1081 return std::nullopt;
1082
1083 if (A == B)
1084 return std::make_pair(x: A.view(), y: B.view());
1085
1086 auto getBase = [](PtrView P) -> PtrView {
1087 if (P.isArrayElement())
1088 return P.expand().getArray();
1089 return P.getBase();
1090 };
1091
1092 PtrView IterA = A.view();
1093 PtrView IterB = B.view();
1094 PtrView CurA = IterA;
1095 PtrView CurB = IterB;
1096 for (;;) {
1097 if (IterA.Base > IterB.Base) {
1098 CurA = IterA;
1099 IterA = getBase(IterA);
1100 } else {
1101 CurB = IterB;
1102 IterB = getBase(IterB);
1103 }
1104
1105 if (IterA == IterB) {
1106 // If the Iter is an array, CurA and CurB are both elements of the same
1107 // array. That is fine, so return nullopt.
1108 if (IterA.getFieldDesc()->isArray())
1109 return std::nullopt;
1110 return std::make_pair(x&: CurA, y&: CurB);
1111 }
1112
1113 if (IterA.isRoot() && IterB.isRoot())
1114 return std::nullopt;
1115 }
1116
1117 llvm_unreachable("The loop above should've returned.");
1118}
1119
1120/// Convert a pointer to a composite value to an rvalue.
1121static bool toRValue(const Context &Ctx, QualType Ty, PtrView Ptr, APValue &R) {
1122 const ASTContext &ASTCtx = Ctx.getASTContext();
1123 if (const auto *AT = Ty->getAs<AtomicType>())
1124 Ty = AT->getValueType();
1125
1126 // Invalid pointers.
1127 if (!Ptr.isLive() || Ptr.isPastEnd())
1128 return false;
1129
1130 // Primitives should never end up here.
1131 assert(!Ctx.canClassify(Ty));
1132 const Descriptor *FieldDesc = Ptr.getFieldDesc();
1133 assert(FieldDesc);
1134
1135 if (const auto *RT = Ty->getAsCanonical<RecordType>()) {
1136 if (!FieldDesc->isRecord())
1137 return false;
1138 const auto *Record = Ptr.getRecord();
1139 assert(Record && "Missing record descriptor");
1140
1141 bool Ok = true;
1142 if (RT->getDecl()->isUnion()) {
1143 const FieldDecl *ActiveField = nullptr;
1144 APValue Value;
1145 for (const auto &F : Record->fields()) {
1146 PtrView FP = Ptr.atField(Offset: F.Offset);
1147 if (FP.isActive()) {
1148 const Descriptor *Desc = F.Desc;
1149 if (Desc->isPrimitive()) {
1150 TYPE_SWITCH(Desc->getPrimType(),
1151 Value = FP.deref<T>().toAPValue(ASTCtx));
1152 } else {
1153 QualType FieldTy = F.Decl->getType();
1154 Ok &= toRValue(Ctx, Ty: FieldTy, Ptr: FP, R&: Value);
1155 }
1156 ActiveField = FP.getFieldDesc()->asFieldDecl();
1157 break;
1158 }
1159 }
1160 R = APValue(ActiveField, Value);
1161 } else {
1162 unsigned NF = Record->getNumFields();
1163 unsigned NB = Record->getNumBases();
1164 unsigned NV = Ptr.isBaseClass() ? 0 : Record->getNumVirtualBases();
1165
1166 R = APValue(APValue::UninitStruct(), NB, NF, NV);
1167
1168 for (unsigned I = 0; I != NF; ++I) {
1169 const Record::Field *FD = Record->getField(I);
1170 const Descriptor *Desc = FD->Desc;
1171 PtrView FP = Ptr.atField(Offset: FD->Offset);
1172 APValue &Value = R.getStructField(i: I);
1173 if (Desc->isPrimitive()) {
1174 TYPE_SWITCH(Desc->getPrimType(),
1175 Value = FP.deref<T>().toAPValue(ASTCtx));
1176 } else {
1177 QualType FieldTy = FD->Decl->getType();
1178 Ok &= toRValue(Ctx, Ty: FieldTy, Ptr: FP, R&: Value);
1179 }
1180 }
1181
1182 for (unsigned I = 0; I != NB; ++I) {
1183 const Record::Base *BD = Record->getBase(I);
1184 QualType BaseTy = Ctx.getASTContext().getCanonicalTagType(TD: BD->Decl);
1185 PtrView BP = Ptr.atField(Offset: BD->Offset);
1186 Ok &= toRValue(Ctx, Ty: BaseTy, Ptr: BP, R&: R.getStructBase(i: I));
1187 }
1188
1189 for (unsigned I = 0; I != NV; ++I) {
1190 const Record::Base *VD = Record->getVirtualBase(I);
1191 assert(VD);
1192 QualType VirtBaseTy = Ctx.getASTContext().getCanonicalTagType(TD: VD->Decl);
1193 PtrView VP = Ptr.atField(Offset: VD->Offset);
1194 Ok &= toRValue(Ctx, Ty: VirtBaseTy, Ptr: VP, R&: R.getStructVirtualBase(i: I));
1195 }
1196 }
1197 return Ok;
1198 }
1199
1200 if (Ty->isIncompleteArrayType()) {
1201 R = APValue(APValue::UninitArray(), 0, 0);
1202 return true;
1203 }
1204
1205 if (const auto *AT = Ty->getAsArrayTypeUnsafe()) {
1206 if (!FieldDesc->isArray())
1207 return false;
1208 const size_t NumElems = Ptr.getNumElems();
1209 QualType ElemTy = AT->getElementType();
1210 R = APValue(APValue::UninitArray{}, NumElems, NumElems);
1211
1212 bool Ok = true;
1213 OptPrimType ElemT = Ctx.classify(T: ElemTy);
1214 for (unsigned I = 0; I != NumElems; ++I) {
1215 APValue &Slot = R.getArrayInitializedElt(I);
1216 if (ElemT) {
1217 TYPE_SWITCH(*ElemT, Slot = Ptr.elem<T>(I).toAPValue(ASTCtx));
1218 } else {
1219 Ok &= toRValue(Ctx, Ty: ElemTy, Ptr: Ptr.atIndex(Idx: I).narrow(), R&: Slot);
1220 }
1221 }
1222 return Ok;
1223 }
1224
1225 // Complex types.
1226 if (Ty->isAnyComplexType()) {
1227 // Can happen via C casts.
1228 if (!FieldDesc->getType()->isAnyComplexType())
1229 return false;
1230
1231 PrimType ElemT = FieldDesc->getPrimType();
1232 if (isIntegerOrBoolType(T: ElemT)) {
1233 INT_TYPE_SWITCH(ElemT, {
1234 auto V1 = Ptr.elem<T>(0);
1235 auto V2 = Ptr.elem<T>(1);
1236 R = APValue(V1.toAPSInt(), V2.toAPSInt());
1237 return true;
1238 });
1239 } else if (ElemT == PT_Float) {
1240 R = APValue(Ptr.elem<Floating>(I: 0).getAPFloat(),
1241 Ptr.elem<Floating>(I: 1).getAPFloat());
1242 return true;
1243 }
1244 return false;
1245 }
1246
1247 // Vector types.
1248 if (const auto *VT = Ty->getAs<VectorType>()) {
1249 if (!FieldDesc->isPrimitiveArray())
1250 return false;
1251
1252 PrimType ElemT = FieldDesc->getPrimType();
1253 SmallVector<APValue> Values;
1254 Values.reserve(N: VT->getNumElements());
1255 for (unsigned I = 0; I != VT->getNumElements(); ++I) {
1256 TYPE_SWITCH(ElemT,
1257 { Values.push_back(Ptr.elem<T>(I).toAPValue(ASTCtx)); });
1258 }
1259
1260 assert(Values.size() == VT->getNumElements());
1261 R = APValue(Values.data(), Values.size());
1262 return true;
1263 }
1264
1265 // Constant Matrix types.
1266 if (const auto *MT = Ty->getAs<ConstantMatrixType>()) {
1267 if (!FieldDesc->isPrimitiveArray())
1268 return false;
1269 PrimType ElemT = FieldDesc->getPrimType();
1270 unsigned NumElems = MT->getNumElementsFlattened();
1271
1272 SmallVector<APValue> Values;
1273 Values.reserve(N: NumElems);
1274 for (unsigned I = 0; I != NumElems; ++I) {
1275 TYPE_SWITCH(ElemT,
1276 { Values.push_back(Ptr.elem<T>(I).toAPValue(ASTCtx)); });
1277 }
1278
1279 R = APValue(Values.data(), MT->getNumRows(), MT->getNumColumns());
1280 return true;
1281 }
1282
1283 llvm_unreachable("invalid value to return");
1284}
1285
1286std::optional<APValue> Pointer::toRValue(const Context &Ctx,
1287 QualType ResultType) const {
1288 const ASTContext &ASTCtx = Ctx.getASTContext();
1289 assert(!ResultType.isNull());
1290
1291 // Can't return functions as rvalues.
1292 if (ResultType->isFunctionType())
1293 return std::nullopt;
1294
1295 // Invalid to read from.
1296 if (isDummy() || !isLive() || isPastEnd() ||
1297 (isOnePastEnd() && !isZeroSizeArray()))
1298 return std::nullopt;
1299
1300 // We can return these as rvalues, but we can't deref() them.
1301 if (isZero() || isIntegralPointer())
1302 return toAPValue(ASTCtx);
1303
1304 // Just load primitive types.
1305 if (OptPrimType T = Ctx.classify(T: ResultType)) {
1306 if (!canDeref(T: *T))
1307 return std::nullopt;
1308 TYPE_SWITCH(*T, return this->load<T>().toAPValue(ASTCtx));
1309 }
1310
1311 if (!isBlockPointer())
1312 return std::nullopt;
1313
1314 // Return the composite type.
1315 APValue Result;
1316 if (!::toRValue(Ctx, Ty: ResultType, Ptr: view(), R&: Result))
1317 return std::nullopt;
1318 return Result;
1319}
1320
1321const VarDecl *Pointer::getRootVarDecl() const {
1322 if (isBlockPointer())
1323 return getDeclDesc()->asVarDecl();
1324 if (isOpaquePointer())
1325 return Opaque.getBaseDecl();
1326 return nullptr;
1327}
1328
1329const Expr *Pointer::getRootExpr() const {
1330 if (isBlockPointer())
1331 return getDeclDesc()->asExpr();
1332 if (isStringPointer())
1333 return Str.getLiteral();
1334 if (isOpaquePointer())
1335 return Opaque.getBaseExpr();
1336 return nullptr;
1337}
1338
1339std::optional<IntPointer> IntPointer::atOffset(const interp::Context &Ctx,
1340 unsigned Offset) const {
1341 QualType CurType = getPointeeType();
1342 if (CurType.isNull() || !CurType->isRecordType())
1343 return std::nullopt;
1344
1345 const Record *R = Ctx.getRecord(D: CurType->getAsRecordDecl());
1346 if (!R)
1347 return *this;
1348
1349 const Record::Field *F = R->findField(Offset);
1350 if (!F)
1351 return *this;
1352
1353 const FieldDecl *FD = F->Decl;
1354 if (FD->getParent()->isInvalidDecl())
1355 return std::nullopt;
1356
1357 const ASTContext &ASTCtx = Ctx.getASTContext();
1358 const ASTRecordLayout &Layout = ASTCtx.getASTRecordLayout(D: FD->getParent());
1359 unsigned FieldIndex = FD->getFieldIndex();
1360 uint64_t FieldOffset =
1361 ASTCtx.toCharUnitsFromBits(BitSize: Layout.getFieldOffset(FieldNo: FieldIndex))
1362 .getQuantity();
1363
1364 return IntPointer{.Ty: FD->getType().getTypePtr(), .Value: this->Value + FieldOffset};
1365}
1366
1367IntPointer IntPointer::baseCast(const interp::Context &Ctx,
1368 unsigned BaseOffset) const {
1369 if (!Ty)
1370 return *this;
1371
1372 QualType CurType = getPointeeType();
1373 if (CurType.isNull() || !CurType->isRecordType())
1374 return *this;
1375
1376 const Record *R = Ctx.getRecord(D: CurType->getAsRecordDecl());
1377
1378 // This iterates over bases and checks for the proper offset. That's
1379 // potentially slow but this case really shouldn't happen a lot.
1380 const Record::Base *B = R->findBase(Offset: BaseOffset);
1381 if (!B)
1382 return *this;
1383
1384 const Descriptor *BaseDesc = B->Desc;
1385 // Adjust the offset value based on the information from the record layout.
1386 const ASTContext &ASTCtx = Ctx.getASTContext();
1387 const ASTRecordLayout &Layout = ASTCtx.getASTRecordLayout(D: R->getDecl());
1388 CharUnits BaseLayoutOffset =
1389 Layout.getBaseClassOffset(Base: cast<CXXRecordDecl>(Val: BaseDesc->asDecl()));
1390
1391 const RecordDecl *RD = BaseDesc->ElemRecord->getDecl();
1392 QualType T = RD->getASTContext().getTagType(Keyword: ElaboratedTypeKeyword::None,
1393 Qualifier: std::nullopt, TD: RD, OwnsTag: false);
1394 return {.Ty: T.getTypePtr(), .Value: Value + BaseLayoutOffset.getQuantity()};
1395}
1396
1397std::optional<size_t>
1398OpaquePointer::computeLayoutOffset(const ASTContext &ASTCtx) const {
1399 size_t Offset = 0;
1400 QualType CurType = getObjectType();
1401 for (const PointerPathEntry &Entry : path()) {
1402 switch (Entry.Kind) {
1403 case PointerPathEntry::Base: {
1404 const RecordDecl *RD = CurType->getAsRecordDecl();
1405 if (!ASTContext::hasLayout(D: RD))
1406 return std::nullopt;
1407
1408 const ASTRecordLayout &Layout = ASTCtx.getASTRecordLayout(D: RD);
1409 if (Entry.RD.getInt())
1410 Offset +=
1411 Layout.getVBaseClassOffset(VBase: Entry.RD.getPointer()).getQuantity();
1412 else
1413 Offset +=
1414 Layout.getBaseClassOffset(Base: Entry.RD.getPointer()).getQuantity();
1415
1416 CurType = ASTCtx.getCanonicalTagType(TD: Entry.RD.getPointer());
1417 } break;
1418
1419 case PointerPathEntry::Field: {
1420 const FieldDecl *FD = Entry.FD;
1421 const RecordDecl *RD = FD->getParent();
1422 if (!ASTContext::hasLayout(D: RD))
1423 return std::nullopt;
1424
1425 const ASTRecordLayout &Layout = ASTCtx.getASTRecordLayout(D: RD);
1426 Offset +=
1427 ASTCtx.toCharUnitsFromBits(BitSize: Layout.getFieldOffset(FieldNo: FD->getFieldIndex()))
1428 .getQuantity();
1429
1430 CurType = FD->getType();
1431 } break;
1432 case PointerPathEntry::Array:
1433 case PointerPathEntry::NegativeArray: {
1434 bool Add = (Entry.Kind == PointerPathEntry::Array);
1435 uint64_t Index = Entry.Index;
1436 if (!CurType->isArrayType()) {
1437 if (Add)
1438 Offset += Index * ASTCtx.getTypeSizeInChars(T: CurType).getQuantity();
1439 else
1440 Offset -= Index * ASTCtx.getTypeSizeInChars(T: CurType).getQuantity();
1441 continue;
1442 }
1443 const ArrayType *AT = CurType->getAsArrayTypeUnsafe();
1444 assert(AT);
1445 QualType ElemTy = AT->getElementType();
1446 if (!validType(T: ElemTy) || isa<VariableArrayType>(Val: AT))
1447 return std::nullopt;
1448 if (Add)
1449 Offset += Index * ASTCtx.getTypeSizeInChars(T: ElemTy).getQuantity();
1450 else
1451 Offset -= Index * ASTCtx.getTypeSizeInChars(T: ElemTy).getQuantity();
1452 CurType = AT->getElementType();
1453 } break;
1454 }
1455 }
1456
1457 return Offset;
1458}
1459
1460QualType OpaquePointer::getSurroundingArray() const {
1461 if (PathLength == 0)
1462 return getObjectType();
1463 if (Path[PathLength - 1].Kind != PointerPathEntry::Array)
1464 return getFieldType();
1465
1466 assert(Path[PathLength - 1].Kind == PointerPathEntry::Array);
1467 assert(isArrayElement());
1468
1469 QualType CurType = getObjectType();
1470 for (const PointerPathEntry &Entry : path().drop_back(N: 1)) {
1471 switch (Entry.Kind) {
1472 case PointerPathEntry::Base:
1473 CurType = Entry.RD.getPointer()->getASTContext().getCanonicalTagType(
1474 TD: Entry.RD.getPointer());
1475 break;
1476 case PointerPathEntry::Field:
1477 CurType = Entry.FD->getType();
1478 break;
1479 case PointerPathEntry::Array:
1480 case PointerPathEntry::NegativeArray:
1481 if (!CurType->isArrayType())
1482 break;
1483 CurType = CurType->getAsArrayTypeUnsafe()->getElementType();
1484 }
1485 }
1486 return CurType;
1487}
1488
1489/// Check if the pointer has offset 0.
1490// As an optimization, don't actually compute the offset.
1491bool OpaquePointer::isRoot() const {
1492 QualType CurType = getObjectType();
1493 for (const PointerPathEntry &Entry : path()) {
1494 switch (Entry.Kind) {
1495 case PointerPathEntry::Base:
1496 if (Entry.RD.getInt())
1497 return false;
1498 CurType = Entry.RD.getPointer()->getASTContext().getCanonicalTagType(
1499 TD: Entry.RD.getPointer());
1500 break;
1501 case PointerPathEntry::Field:
1502 if (!Entry.FD->getParent()->isUnion() && Entry.FD->getFieldIndex() != 0)
1503 return false;
1504 CurType = Entry.FD->getType();
1505 break;
1506 case PointerPathEntry::Array:
1507 if (Entry.Index != 0)
1508 return false;
1509 if (!CurType->isArrayType())
1510 continue;
1511 CurType = CurType->getAsArrayTypeUnsafe()->getElementType();
1512 break;
1513 case PointerPathEntry::NegativeArray:
1514 return false;
1515 }
1516 }
1517 return true;
1518}
1519
1520bool OpaquePointer::isUnknownSizeArray() const {
1521 QualType FieldType = getFieldType();
1522
1523 if (isArrayElement())
1524 FieldType = getSurroundingArray();
1525
1526 bool Result = false;
1527 // If the field type is an IncompleteArrayType, we still need to check the
1528 // base to see if this array is a flexible array member _and_ has actually
1529 // been initialized by data we know the size of.
1530 if (isa<IncompleteArrayType>(Val: FieldType)) {
1531 const VarDecl *Base = this->Base.asVarDecl();
1532 if (!Base || !Base->getType()->isRecordType() || !Base->hasInit())
1533 Result = true;
1534 else
1535 Result = !Base->hasFlexibleArrayInit(Ctx: Base->getASTContext());
1536 } else if (isa<VariableArrayType>(Val: FieldType))
1537 Result = true;
1538
1539 return Result;
1540}
1541
1542/// This is used in Pointer::isOnePastEnd(). We cannot read from such pointers.
1543/// We can of course never read from opaque pointers anyway but we diagnose
1544/// one-past-the-end pointers differently.
1545///
1546/// In contrast, OpaquePointer::isOnePastEnd() only uses the past-end bit. That
1547/// is used for the APValue conversion.
1548bool OpaquePointer::isOnePastEndOrElementPastEnd() const {
1549 if (isOnePastEnd())
1550 return true;
1551
1552 if (PathLength == 0)
1553 return false;
1554
1555 if (Path[PathLength - 1].Kind != PointerPathEntry::Array)
1556 return false;
1557
1558 QualType ArrTy = getSurroundingArray();
1559 if (!ArrTy->isArrayType())
1560 return false;
1561 // FIXME: Flexible array members?
1562 if (const auto *CAT =
1563 dyn_cast<ConstantArrayType>(Val: ArrTy->getAsArrayTypeUnsafe())) {
1564 if (Path[PathLength - 1].Index >= CAT->getZExtSize())
1565 return true;
1566 }
1567
1568 return false;
1569}
1570