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