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