xref: /llvm-project/clang/lib/AST/ByteCode/Pointer.cpp (revision 800b07396ff54b037fa9b73bb15586456656fb79)
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"
20 #include "clang/AST/RecordLayout.h"
21 
22 using namespace clang;
23 using namespace clang::interp;
24 
25 Pointer::Pointer(Block *Pointee)
26     : Pointer(Pointee, Pointee->getDescriptor()->getMetadataSize(),
27               Pointee->getDescriptor()->getMetadataSize()) {}
28 
29 Pointer::Pointer(Block *Pointee, uint64_t BaseAndOffset)
30     : Pointer(Pointee, BaseAndOffset, BaseAndOffset) {}
31 
32 Pointer::Pointer(const Pointer &P)
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 
40 Pointer::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 
50 Pointer::Pointer(Pointer &&P)
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 
58 Pointer::~Pointer() {
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 
69 void Pointer::operator=(const Pointer &P) {
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 {
100     assert(false && "Unhandled storage kind");
101   }
102 }
103 
104 void Pointer::operator=(Pointer &&P) {
105   // If the current storage type is Block, we need to remove
106   // this pointer from the block.
107   if (isBlockPointer()) {
108     if (P.isBlockPointer() && this->block() == P.block()) {
109       Offset = P.Offset;
110       PointeeStorage.BS.Base = P.PointeeStorage.BS.Base;
111       return;
112     }
113 
114     if (Block *Pointee = PointeeStorage.BS.Pointee) {
115       assert(P.block() != this->block());
116       Pointee->removePointer(this);
117       PointeeStorage.BS.Pointee = nullptr;
118       Pointee->cleanup();
119     }
120   }
121 
122   StorageKind = P.StorageKind;
123   Offset = P.Offset;
124 
125   if (P.isBlockPointer()) {
126     PointeeStorage.BS = P.PointeeStorage.BS;
127     PointeeStorage.BS.Pointee = P.PointeeStorage.BS.Pointee;
128 
129     if (PointeeStorage.BS.Pointee)
130       PointeeStorage.BS.Pointee->addPointer(this);
131   } else if (P.isIntegralPointer()) {
132     PointeeStorage.Int = P.PointeeStorage.Int;
133   } else if (P.isFunctionPointer()) {
134     PointeeStorage.Fn = P.PointeeStorage.Fn;
135   } else {
136     assert(false && "Unhandled storage kind");
137   }
138 }
139 
140 APValue Pointer::toAPValue(const ASTContext &ASTCtx) const {
141   llvm::SmallVector<APValue::LValuePathEntry, 5> Path;
142 
143   if (isZero())
144     return APValue(static_cast<const Expr *>(nullptr), CharUnits::Zero(), Path,
145                    /*IsOnePastEnd=*/false, /*IsNullPtr=*/true);
146   if (isIntegralPointer())
147     return APValue(static_cast<const Expr *>(nullptr),
148                    CharUnits::fromQuantity(asIntPointer().Value + this->Offset),
149                    Path,
150                    /*IsOnePastEnd=*/false, /*IsNullPtr=*/false);
151   if (isFunctionPointer())
152     return asFunctionPointer().toAPValue(ASTCtx);
153 
154   // Build the lvalue base from the block.
155   const Descriptor *Desc = getDeclDesc();
156   APValue::LValueBase Base;
157   if (const auto *VD = Desc->asValueDecl())
158     Base = VD;
159   else if (const auto *E = Desc->asExpr()) {
160     // Create a DynamicAlloc base of the right type.
161     if (const auto *NewExpr = dyn_cast<CXXNewExpr>(E)) {
162       QualType AllocatedType;
163       if (NewExpr->isArray()) {
164         assert(Desc->isArray());
165         APInt ArraySize(64, static_cast<uint64_t>(Desc->getNumElems()),
166                         /*IsSigned=*/false);
167         AllocatedType =
168             ASTCtx.getConstantArrayType(NewExpr->getAllocatedType(), ArraySize,
169                                         nullptr, ArraySizeModifier::Normal, 0);
170       } else {
171         AllocatedType = NewExpr->getAllocatedType();
172       }
173       // FIXME: Suboptimal counting of dynamic allocations. Move this to Context
174       // or InterpState?
175       static int ReportedDynamicAllocs = 0;
176       DynamicAllocLValue DA(ReportedDynamicAllocs++);
177       Base = APValue::LValueBase::getDynamicAlloc(DA, AllocatedType);
178     } else {
179       Base = E;
180     }
181   } else
182     llvm_unreachable("Invalid allocation type");
183 
184   if (isUnknownSizeArray())
185     return APValue(Base, CharUnits::Zero(), Path,
186                    /*IsOnePastEnd=*/isOnePastEnd(), /*IsNullPtr=*/false);
187 
188   CharUnits Offset = CharUnits::Zero();
189 
190   auto getFieldOffset = [&](const FieldDecl *FD) -> CharUnits {
191     // This shouldn't happen, but if it does, don't crash inside
192     // getASTRecordLayout.
193     if (FD->getParent()->isInvalidDecl())
194       return CharUnits::Zero();
195     const ASTRecordLayout &Layout = ASTCtx.getASTRecordLayout(FD->getParent());
196     unsigned FieldIndex = FD->getFieldIndex();
197     return ASTCtx.toCharUnitsFromBits(Layout.getFieldOffset(FieldIndex));
198   };
199 
200   // Build the path into the object.
201   Pointer Ptr = *this;
202   while (Ptr.isField() || Ptr.isArrayElement()) {
203     if (Ptr.isArrayRoot()) {
204       Path.push_back(APValue::LValuePathEntry(
205           {Ptr.getFieldDesc()->asDecl(), /*IsVirtual=*/false}));
206 
207       if (const auto *FD =
208               dyn_cast_if_present<FieldDecl>(Ptr.getFieldDesc()->asDecl()))
209         Offset += getFieldOffset(FD);
210 
211       Ptr = Ptr.getBase();
212     } else if (Ptr.isArrayElement()) {
213       Ptr = Ptr.expand();
214       unsigned Index;
215       if (Ptr.isOnePastEnd())
216         Index = Ptr.getArray().getNumElems();
217       else
218         Index = Ptr.getIndex();
219 
220       QualType ElemType = Ptr.getFieldDesc()->getElemQualType();
221       Offset += (Index * ASTCtx.getTypeSizeInChars(ElemType));
222 
223       Path.push_back(APValue::LValuePathEntry::ArrayIndex(Index));
224       Ptr = Ptr.getArray();
225     } else {
226       bool IsVirtual = false;
227 
228       // Create a path entry for the field.
229       const Descriptor *Desc = Ptr.getFieldDesc();
230       if (const auto *BaseOrMember = Desc->asDecl()) {
231         if (const auto *FD = dyn_cast<FieldDecl>(BaseOrMember)) {
232           Ptr = Ptr.getBase();
233           Offset += getFieldOffset(FD);
234         } else if (const auto *RD = dyn_cast<CXXRecordDecl>(BaseOrMember)) {
235           IsVirtual = Ptr.isVirtualBaseClass();
236           Ptr = Ptr.getBase();
237           const Record *BaseRecord = Ptr.getRecord();
238 
239           const ASTRecordLayout &Layout = ASTCtx.getASTRecordLayout(
240               cast<CXXRecordDecl>(BaseRecord->getDecl()));
241           if (IsVirtual)
242             Offset += Layout.getVBaseClassOffset(RD);
243           else
244             Offset += Layout.getBaseClassOffset(RD);
245 
246         } else {
247           Ptr = Ptr.getBase();
248         }
249         Path.push_back(APValue::LValuePathEntry({BaseOrMember, IsVirtual}));
250         continue;
251       }
252       llvm_unreachable("Invalid field type");
253     }
254   }
255 
256   // FIXME(perf): We compute the lvalue path above, but we can't supply it
257   // for dummy pointers (that causes crashes later in CheckConstantExpression).
258   if (isDummy())
259     Path.clear();
260 
261   // We assemble the LValuePath starting from the innermost pointer to the
262   // outermost one. SO in a.b.c, the first element in Path will refer to
263   // the field 'c', while later code expects it to refer to 'a'.
264   // Just invert the order of the elements.
265   std::reverse(Path.begin(), Path.end());
266 
267   return APValue(Base, Offset, Path, /*IsOnePastEnd=*/isOnePastEnd(),
268                  /*IsNullPtr=*/false);
269 }
270 
271 void Pointer::print(llvm::raw_ostream &OS) const {
272   switch (StorageKind) {
273   case Storage::Block: {
274     const Block *B = PointeeStorage.BS.Pointee;
275     OS << "(Block) " << B << " {";
276 
277     if (isRoot())
278       OS << "rootptr(" << PointeeStorage.BS.Base << "), ";
279     else
280       OS << PointeeStorage.BS.Base << ", ";
281 
282     if (isElementPastEnd())
283       OS << "pastend, ";
284     else
285       OS << Offset << ", ";
286 
287     if (B)
288       OS << B->getSize();
289     else
290       OS << "nullptr";
291     OS << "}";
292   } break;
293   case Storage::Int:
294     OS << "(Int) {";
295     OS << PointeeStorage.Int.Value << " + " << Offset << ", "
296        << PointeeStorage.Int.Desc;
297     OS << "}";
298     break;
299   case Storage::Fn:
300     OS << "(Fn) { " << asFunctionPointer().getFunction() << " + " << Offset
301        << " }";
302   }
303 }
304 
305 std::string Pointer::toDiagnosticString(const ASTContext &Ctx) const {
306   if (isZero())
307     return "nullptr";
308 
309   if (isIntegralPointer())
310     return (Twine("&(") + Twine(asIntPointer().Value + Offset) + ")").str();
311 
312   return toAPValue(Ctx).getAsString(Ctx, getType());
313 }
314 
315 bool Pointer::isInitialized() const {
316   if (!isBlockPointer())
317     return true;
318 
319   if (isRoot() && PointeeStorage.BS.Base == sizeof(GlobalInlineDescriptor)) {
320     const GlobalInlineDescriptor &GD =
321         *reinterpret_cast<const GlobalInlineDescriptor *>(block()->rawData());
322     return GD.InitState == GlobalInitState::Initialized;
323   }
324 
325   assert(PointeeStorage.BS.Pointee &&
326          "Cannot check if null pointer was initialized");
327   const Descriptor *Desc = getFieldDesc();
328   assert(Desc);
329   if (Desc->isPrimitiveArray()) {
330     if (isStatic() && PointeeStorage.BS.Base == 0)
331       return true;
332 
333     InitMapPtr &IM = getInitMap();
334 
335     if (!IM)
336       return false;
337 
338     if (IM->first)
339       return true;
340 
341     return IM->second->isElementInitialized(getIndex());
342   }
343 
344   if (asBlockPointer().Base == 0)
345     return true;
346 
347   // Field has its bit in an inline descriptor.
348   return getInlineDesc()->IsInitialized;
349 }
350 
351 void Pointer::initialize() const {
352   if (!isBlockPointer())
353     return;
354 
355   assert(PointeeStorage.BS.Pointee && "Cannot initialize null pointer");
356   const Descriptor *Desc = getFieldDesc();
357 
358   if (isRoot() && PointeeStorage.BS.Base == sizeof(GlobalInlineDescriptor)) {
359     GlobalInlineDescriptor &GD = *reinterpret_cast<GlobalInlineDescriptor *>(
360         asBlockPointer().Pointee->rawData());
361     GD.InitState = GlobalInitState::Initialized;
362     return;
363   }
364 
365   assert(Desc);
366   if (Desc->isPrimitiveArray()) {
367     // Primitive global arrays don't have an initmap.
368     if (isStatic() && PointeeStorage.BS.Base == 0)
369       return;
370 
371     // Nothing to do for these.
372     if (Desc->getNumElems() == 0)
373       return;
374 
375     InitMapPtr &IM = getInitMap();
376     if (!IM)
377       IM =
378           std::make_pair(false, std::make_shared<InitMap>(Desc->getNumElems()));
379 
380     assert(IM);
381 
382     // All initialized.
383     if (IM->first)
384       return;
385 
386     if (IM->second->initializeElement(getIndex())) {
387       IM->first = true;
388       IM->second.reset();
389     }
390     return;
391   }
392 
393   // Field has its bit in an inline descriptor.
394   assert(PointeeStorage.BS.Base != 0 &&
395          "Only composite fields can be initialised");
396   getInlineDesc()->IsInitialized = true;
397 }
398 
399 void Pointer::activate() const {
400   // Field has its bit in an inline descriptor.
401   assert(PointeeStorage.BS.Base != 0 &&
402          "Only composite fields can be activated");
403 
404   if (isRoot() && PointeeStorage.BS.Base == sizeof(GlobalInlineDescriptor))
405     return;
406   if (!getInlineDesc()->InUnion)
407     return;
408 
409   getInlineDesc()->IsActive = true;
410 
411   // Get the union, iterate over its fields and DEactivate all others.
412   Pointer UnionPtr = getBase();
413   while (!UnionPtr.getFieldDesc()->isUnion())
414     UnionPtr = UnionPtr.getBase();
415 
416   const Record *UnionRecord = UnionPtr.getRecord();
417   for (const Record::Field &F : UnionRecord->fields()) {
418     Pointer FieldPtr = UnionPtr.atField(F.Offset);
419     if (FieldPtr == *this) {
420     } else {
421       FieldPtr.getInlineDesc()->IsActive = false;
422       // FIXME: Recurse.
423     }
424   }
425 
426   Pointer B = getBase();
427   while (!B.isRoot() && B.inUnion()) {
428     // FIXME: Need to de-activate other fields of parent records.
429     B.getInlineDesc()->IsActive = true;
430     assert(B.isActive());
431     B = B.getBase();
432   }
433 }
434 
435 void Pointer::deactivate() const {
436   // TODO: this only appears in constructors, so nothing to deactivate.
437 }
438 
439 bool Pointer::hasSameBase(const Pointer &A, const Pointer &B) {
440   // Two null pointers always have the same base.
441   if (A.isZero() && B.isZero())
442     return true;
443 
444   if (A.isIntegralPointer() && B.isIntegralPointer())
445     return true;
446   if (A.isFunctionPointer() && B.isFunctionPointer())
447     return true;
448 
449   if (A.isIntegralPointer() || B.isIntegralPointer())
450     return A.getSource() == B.getSource();
451 
452   if (A.StorageKind != B.StorageKind)
453     return false;
454 
455   return A.asBlockPointer().Pointee == B.asBlockPointer().Pointee;
456 }
457 
458 bool Pointer::pointToSameBlock(const Pointer &A, const Pointer &B) {
459   if (!A.isBlockPointer() || !B.isBlockPointer())
460     return false;
461   return A.block() == B.block();
462 }
463 
464 bool Pointer::hasSameArray(const Pointer &A, const Pointer &B) {
465   return hasSameBase(A, B) &&
466          A.PointeeStorage.BS.Base == B.PointeeStorage.BS.Base &&
467          A.getFieldDesc()->IsArray;
468 }
469 
470 bool Pointer::pointsToLiteral() const {
471   if (isZero() || !isBlockPointer())
472     return false;
473 
474   const Expr *E = block()->getDescriptor()->asExpr();
475   if (block()->isDynamic())
476     return false;
477 
478   return E && !isa<MaterializeTemporaryExpr, StringLiteral>(E);
479 }
480 
481 std::optional<APValue> Pointer::toRValue(const Context &Ctx,
482                                          QualType ResultType) const {
483   const ASTContext &ASTCtx = Ctx.getASTContext();
484   assert(!ResultType.isNull());
485   // Method to recursively traverse composites.
486   std::function<bool(QualType, const Pointer &, APValue &)> Composite;
487   Composite = [&Composite, &Ctx, &ASTCtx](QualType Ty, const Pointer &Ptr,
488                                           APValue &R) {
489     if (const auto *AT = Ty->getAs<AtomicType>())
490       Ty = AT->getValueType();
491 
492     // Invalid pointers.
493     if (Ptr.isDummy() || !Ptr.isLive() || !Ptr.isBlockPointer() ||
494         Ptr.isPastEnd())
495       return false;
496 
497     // Primitive values.
498     if (std::optional<PrimType> T = Ctx.classify(Ty)) {
499       TYPE_SWITCH(*T, R = Ptr.deref<T>().toAPValue(ASTCtx));
500       return true;
501     }
502 
503     if (const auto *RT = Ty->getAs<RecordType>()) {
504       const auto *Record = Ptr.getRecord();
505       assert(Record && "Missing record descriptor");
506 
507       bool Ok = true;
508       if (RT->getDecl()->isUnion()) {
509         const FieldDecl *ActiveField = nullptr;
510         APValue Value;
511         for (const auto &F : Record->fields()) {
512           const Pointer &FP = Ptr.atField(F.Offset);
513           QualType FieldTy = F.Decl->getType();
514           if (FP.isActive()) {
515             if (std::optional<PrimType> T = Ctx.classify(FieldTy)) {
516               TYPE_SWITCH(*T, Value = FP.deref<T>().toAPValue(ASTCtx));
517             } else {
518               Ok &= Composite(FieldTy, FP, Value);
519             }
520             ActiveField = FP.getFieldDesc()->asFieldDecl();
521             break;
522           }
523         }
524         R = APValue(ActiveField, Value);
525       } else {
526         unsigned NF = Record->getNumFields();
527         unsigned NB = Record->getNumBases();
528         unsigned NV = Ptr.isBaseClass() ? 0 : Record->getNumVirtualBases();
529 
530         R = APValue(APValue::UninitStruct(), NB, NF);
531 
532         for (unsigned I = 0; I < NF; ++I) {
533           const Record::Field *FD = Record->getField(I);
534           QualType FieldTy = FD->Decl->getType();
535           const Pointer &FP = Ptr.atField(FD->Offset);
536           APValue &Value = R.getStructField(I);
537 
538           if (std::optional<PrimType> T = Ctx.classify(FieldTy)) {
539             TYPE_SWITCH(*T, Value = FP.deref<T>().toAPValue(ASTCtx));
540           } else {
541             Ok &= Composite(FieldTy, FP, Value);
542           }
543         }
544 
545         for (unsigned I = 0; I < NB; ++I) {
546           const Record::Base *BD = Record->getBase(I);
547           QualType BaseTy = Ctx.getASTContext().getRecordType(BD->Decl);
548           const Pointer &BP = Ptr.atField(BD->Offset);
549           Ok &= Composite(BaseTy, BP, R.getStructBase(I));
550         }
551 
552         for (unsigned I = 0; I < NV; ++I) {
553           const Record::Base *VD = Record->getVirtualBase(I);
554           QualType VirtBaseTy = Ctx.getASTContext().getRecordType(VD->Decl);
555           const Pointer &VP = Ptr.atField(VD->Offset);
556           Ok &= Composite(VirtBaseTy, VP, R.getStructBase(NB + I));
557         }
558       }
559       return Ok;
560     }
561 
562     if (Ty->isIncompleteArrayType()) {
563       R = APValue(APValue::UninitArray(), 0, 0);
564       return true;
565     }
566 
567     if (const auto *AT = Ty->getAsArrayTypeUnsafe()) {
568       const size_t NumElems = Ptr.getNumElems();
569       QualType ElemTy = AT->getElementType();
570       R = APValue(APValue::UninitArray{}, NumElems, NumElems);
571 
572       bool Ok = true;
573       for (unsigned I = 0; I < NumElems; ++I) {
574         APValue &Slot = R.getArrayInitializedElt(I);
575         const Pointer &EP = Ptr.atIndex(I);
576         if (std::optional<PrimType> T = Ctx.classify(ElemTy)) {
577           TYPE_SWITCH(*T, Slot = EP.deref<T>().toAPValue(ASTCtx));
578         } else {
579           Ok &= Composite(ElemTy, EP.narrow(), Slot);
580         }
581       }
582       return Ok;
583     }
584 
585     // Complex types.
586     if (const auto *CT = Ty->getAs<ComplexType>()) {
587       QualType ElemTy = CT->getElementType();
588 
589       if (ElemTy->isIntegerType()) {
590         std::optional<PrimType> ElemT = Ctx.classify(ElemTy);
591         assert(ElemT);
592         INT_TYPE_SWITCH(*ElemT, {
593           auto V1 = Ptr.atIndex(0).deref<T>();
594           auto V2 = Ptr.atIndex(1).deref<T>();
595           R = APValue(V1.toAPSInt(), V2.toAPSInt());
596           return true;
597         });
598       } else if (ElemTy->isFloatingType()) {
599         R = APValue(Ptr.atIndex(0).deref<Floating>().getAPFloat(),
600                     Ptr.atIndex(1).deref<Floating>().getAPFloat());
601         return true;
602       }
603       return false;
604     }
605 
606     // Vector types.
607     if (const auto *VT = Ty->getAs<VectorType>()) {
608       assert(Ptr.getFieldDesc()->isPrimitiveArray());
609       QualType ElemTy = VT->getElementType();
610       PrimType ElemT = *Ctx.classify(ElemTy);
611 
612       SmallVector<APValue> Values;
613       Values.reserve(VT->getNumElements());
614       for (unsigned I = 0; I != VT->getNumElements(); ++I) {
615         TYPE_SWITCH(ElemT, {
616           Values.push_back(Ptr.atIndex(I).deref<T>().toAPValue(ASTCtx));
617         });
618       }
619 
620       assert(Values.size() == VT->getNumElements());
621       R = APValue(Values.data(), Values.size());
622       return true;
623     }
624 
625     llvm_unreachable("invalid value to return");
626   };
627 
628   // Invalid to read from.
629   if (isDummy() || !isLive() || isPastEnd())
630     return std::nullopt;
631 
632   // We can return these as rvalues, but we can't deref() them.
633   if (isZero() || isIntegralPointer())
634     return toAPValue(ASTCtx);
635 
636   // Just load primitive types.
637   if (std::optional<PrimType> T = Ctx.classify(ResultType)) {
638     TYPE_SWITCH(*T, return this->deref<T>().toAPValue(ASTCtx));
639   }
640 
641   // Return the composite type.
642   APValue Result;
643   if (!Composite(getType(), *this, Result))
644     return std::nullopt;
645   return Result;
646 }
647 
648 IntPointer IntPointer::atOffset(const ASTContext &ASTCtx,
649                                 unsigned Offset) const {
650   if (!this->Desc)
651     return *this;
652   const Record *R = this->Desc->ElemRecord;
653   if (!R)
654     return *this;
655 
656   const Record::Field *F = nullptr;
657   for (auto &It : R->fields()) {
658     if (It.Offset == Offset) {
659       F = &It;
660       break;
661     }
662   }
663   if (!F)
664     return *this;
665 
666   const FieldDecl *FD = F->Decl;
667   const ASTRecordLayout &Layout = ASTCtx.getASTRecordLayout(FD->getParent());
668   unsigned FieldIndex = FD->getFieldIndex();
669   uint64_t FieldOffset =
670       ASTCtx.toCharUnitsFromBits(Layout.getFieldOffset(FieldIndex))
671           .getQuantity();
672   return IntPointer{F->Desc, this->Value + FieldOffset};
673 }
674 
675 IntPointer IntPointer::baseCast(const ASTContext &ASTCtx,
676                                 unsigned BaseOffset) const {
677   const Record *R = Desc->ElemRecord;
678   const Descriptor *BaseDesc = nullptr;
679 
680   // This iterates over bases and checks for the proper offset. That's
681   // potentially slow but this case really shouldn't happen a lot.
682   for (const Record::Base &B : R->bases()) {
683     if (B.Offset == BaseOffset) {
684       BaseDesc = B.Desc;
685       break;
686     }
687   }
688   assert(BaseDesc);
689 
690   // Adjust the offset value based on the information from the record layout.
691   const ASTRecordLayout &Layout = ASTCtx.getASTRecordLayout(R->getDecl());
692   CharUnits BaseLayoutOffset =
693       Layout.getBaseClassOffset(cast<CXXRecordDecl>(BaseDesc->asDecl()));
694 
695   return {BaseDesc, Value + BaseLayoutOffset.getQuantity()};
696 }
697