xref: /llvm-project/clang/lib/AST/ByteCode/Pointer.cpp (revision dac182990dabe8d15cfb8079aba68df2ded015aa)
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 std::optional<APValue> Pointer::toRValue(const Context &Ctx,
467                                          QualType ResultType) const {
468   const ASTContext &ASTCtx = Ctx.getASTContext();
469   assert(!ResultType.isNull());
470   // Method to recursively traverse composites.
471   std::function<bool(QualType, const Pointer &, APValue &)> Composite;
472   Composite = [&Composite, &Ctx, &ASTCtx](QualType Ty, const Pointer &Ptr,
473                                           APValue &R) {
474     if (const auto *AT = Ty->getAs<AtomicType>())
475       Ty = AT->getValueType();
476 
477     // Invalid pointers.
478     if (Ptr.isDummy() || !Ptr.isLive() || !Ptr.isBlockPointer() ||
479         Ptr.isPastEnd())
480       return false;
481 
482     // Primitive values.
483     if (std::optional<PrimType> T = Ctx.classify(Ty)) {
484       TYPE_SWITCH(*T, R = Ptr.deref<T>().toAPValue(ASTCtx));
485       return true;
486     }
487 
488     if (const auto *RT = Ty->getAs<RecordType>()) {
489       const auto *Record = Ptr.getRecord();
490       assert(Record && "Missing record descriptor");
491 
492       bool Ok = true;
493       if (RT->getDecl()->isUnion()) {
494         const FieldDecl *ActiveField = nullptr;
495         APValue Value;
496         for (const auto &F : Record->fields()) {
497           const Pointer &FP = Ptr.atField(F.Offset);
498           QualType FieldTy = F.Decl->getType();
499           if (FP.isActive()) {
500             if (std::optional<PrimType> T = Ctx.classify(FieldTy)) {
501               TYPE_SWITCH(*T, Value = FP.deref<T>().toAPValue(ASTCtx));
502             } else {
503               Ok &= Composite(FieldTy, FP, Value);
504             }
505             ActiveField = FP.getFieldDesc()->asFieldDecl();
506             break;
507           }
508         }
509         R = APValue(ActiveField, Value);
510       } else {
511         unsigned NF = Record->getNumFields();
512         unsigned NB = Record->getNumBases();
513         unsigned NV = Ptr.isBaseClass() ? 0 : Record->getNumVirtualBases();
514 
515         R = APValue(APValue::UninitStruct(), NB, NF);
516 
517         for (unsigned I = 0; I < NF; ++I) {
518           const Record::Field *FD = Record->getField(I);
519           QualType FieldTy = FD->Decl->getType();
520           const Pointer &FP = Ptr.atField(FD->Offset);
521           APValue &Value = R.getStructField(I);
522 
523           if (std::optional<PrimType> T = Ctx.classify(FieldTy)) {
524             TYPE_SWITCH(*T, Value = FP.deref<T>().toAPValue(ASTCtx));
525           } else {
526             Ok &= Composite(FieldTy, FP, Value);
527           }
528         }
529 
530         for (unsigned I = 0; I < NB; ++I) {
531           const Record::Base *BD = Record->getBase(I);
532           QualType BaseTy = Ctx.getASTContext().getRecordType(BD->Decl);
533           const Pointer &BP = Ptr.atField(BD->Offset);
534           Ok &= Composite(BaseTy, BP, R.getStructBase(I));
535         }
536 
537         for (unsigned I = 0; I < NV; ++I) {
538           const Record::Base *VD = Record->getVirtualBase(I);
539           QualType VirtBaseTy = Ctx.getASTContext().getRecordType(VD->Decl);
540           const Pointer &VP = Ptr.atField(VD->Offset);
541           Ok &= Composite(VirtBaseTy, VP, R.getStructBase(NB + I));
542         }
543       }
544       return Ok;
545     }
546 
547     if (Ty->isIncompleteArrayType()) {
548       R = APValue(APValue::UninitArray(), 0, 0);
549       return true;
550     }
551 
552     if (const auto *AT = Ty->getAsArrayTypeUnsafe()) {
553       const size_t NumElems = Ptr.getNumElems();
554       QualType ElemTy = AT->getElementType();
555       R = APValue(APValue::UninitArray{}, NumElems, NumElems);
556 
557       bool Ok = true;
558       for (unsigned I = 0; I < NumElems; ++I) {
559         APValue &Slot = R.getArrayInitializedElt(I);
560         const Pointer &EP = Ptr.atIndex(I);
561         if (std::optional<PrimType> T = Ctx.classify(ElemTy)) {
562           TYPE_SWITCH(*T, Slot = EP.deref<T>().toAPValue(ASTCtx));
563         } else {
564           Ok &= Composite(ElemTy, EP.narrow(), Slot);
565         }
566       }
567       return Ok;
568     }
569 
570     // Complex types.
571     if (const auto *CT = Ty->getAs<ComplexType>()) {
572       QualType ElemTy = CT->getElementType();
573 
574       if (ElemTy->isIntegerType()) {
575         std::optional<PrimType> ElemT = Ctx.classify(ElemTy);
576         assert(ElemT);
577         INT_TYPE_SWITCH(*ElemT, {
578           auto V1 = Ptr.atIndex(0).deref<T>();
579           auto V2 = Ptr.atIndex(1).deref<T>();
580           R = APValue(V1.toAPSInt(), V2.toAPSInt());
581           return true;
582         });
583       } else if (ElemTy->isFloatingType()) {
584         R = APValue(Ptr.atIndex(0).deref<Floating>().getAPFloat(),
585                     Ptr.atIndex(1).deref<Floating>().getAPFloat());
586         return true;
587       }
588       return false;
589     }
590 
591     // Vector types.
592     if (const auto *VT = Ty->getAs<VectorType>()) {
593       assert(Ptr.getFieldDesc()->isPrimitiveArray());
594       QualType ElemTy = VT->getElementType();
595       PrimType ElemT = *Ctx.classify(ElemTy);
596 
597       SmallVector<APValue> Values;
598       Values.reserve(VT->getNumElements());
599       for (unsigned I = 0; I != VT->getNumElements(); ++I) {
600         TYPE_SWITCH(ElemT, {
601           Values.push_back(Ptr.atIndex(I).deref<T>().toAPValue(ASTCtx));
602         });
603       }
604 
605       assert(Values.size() == VT->getNumElements());
606       R = APValue(Values.data(), Values.size());
607       return true;
608     }
609 
610     llvm_unreachable("invalid value to return");
611   };
612 
613   // Invalid to read from.
614   if (isDummy() || !isLive() || isPastEnd())
615     return std::nullopt;
616 
617   // We can return these as rvalues, but we can't deref() them.
618   if (isZero() || isIntegralPointer())
619     return toAPValue(ASTCtx);
620 
621   // Just load primitive types.
622   if (std::optional<PrimType> T = Ctx.classify(ResultType)) {
623     TYPE_SWITCH(*T, return this->deref<T>().toAPValue(ASTCtx));
624   }
625 
626   // Return the composite type.
627   APValue Result;
628   if (!Composite(getType(), *this, Result))
629     return std::nullopt;
630   return Result;
631 }
632 
633 IntPointer IntPointer::atOffset(const ASTContext &ASTCtx,
634                                 unsigned Offset) const {
635   if (!this->Desc)
636     return *this;
637   const Record *R = this->Desc->ElemRecord;
638   if (!R)
639     return *this;
640 
641   const Record::Field *F = nullptr;
642   for (auto &It : R->fields()) {
643     if (It.Offset == Offset) {
644       F = &It;
645       break;
646     }
647   }
648   if (!F)
649     return *this;
650 
651   const FieldDecl *FD = F->Decl;
652   const ASTRecordLayout &Layout = ASTCtx.getASTRecordLayout(FD->getParent());
653   unsigned FieldIndex = FD->getFieldIndex();
654   uint64_t FieldOffset =
655       ASTCtx.toCharUnitsFromBits(Layout.getFieldOffset(FieldIndex))
656           .getQuantity();
657   return IntPointer{this->Desc, this->Value + FieldOffset};
658 }
659