xref: /llvm-project/clang/lib/AST/ByteCode/Pointer.cpp (revision a07aba5d44204a7ca0d891a3da05af9960081e4c)
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   OS << PointeeStorage.BS.Pointee << " (";
269   if (isBlockPointer()) {
270     const Block *B = PointeeStorage.BS.Pointee;
271     OS << "Block) {";
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   } else {
288     OS << "Int) {";
289     OS << PointeeStorage.Int.Value << ", " << PointeeStorage.Int.Desc;
290   }
291   OS << "}";
292 }
293 
294 std::string Pointer::toDiagnosticString(const ASTContext &Ctx) const {
295   if (isZero())
296     return "nullptr";
297 
298   if (isIntegralPointer())
299     return (Twine("&(") + Twine(asIntPointer().Value + Offset) + ")").str();
300 
301   return toAPValue(Ctx).getAsString(Ctx, getType());
302 }
303 
304 bool Pointer::isInitialized() const {
305   if (!isBlockPointer())
306     return true;
307 
308   if (isRoot() && PointeeStorage.BS.Base == sizeof(GlobalInlineDescriptor)) {
309     const GlobalInlineDescriptor &GD =
310         *reinterpret_cast<const GlobalInlineDescriptor *>(block()->rawData());
311     return GD.InitState == GlobalInitState::Initialized;
312   }
313 
314   assert(PointeeStorage.BS.Pointee &&
315          "Cannot check if null pointer was initialized");
316   const Descriptor *Desc = getFieldDesc();
317   assert(Desc);
318   if (Desc->isPrimitiveArray()) {
319     if (isStatic() && PointeeStorage.BS.Base == 0)
320       return true;
321 
322     InitMapPtr &IM = getInitMap();
323 
324     if (!IM)
325       return false;
326 
327     if (IM->first)
328       return true;
329 
330     return IM->second->isElementInitialized(getIndex());
331   }
332 
333   if (asBlockPointer().Base == 0)
334     return true;
335 
336   // Field has its bit in an inline descriptor.
337   return getInlineDesc()->IsInitialized;
338 }
339 
340 void Pointer::initialize() const {
341   if (!isBlockPointer())
342     return;
343 
344   assert(PointeeStorage.BS.Pointee && "Cannot initialize null pointer");
345   const Descriptor *Desc = getFieldDesc();
346 
347   if (isRoot() && PointeeStorage.BS.Base == sizeof(GlobalInlineDescriptor)) {
348     GlobalInlineDescriptor &GD = *reinterpret_cast<GlobalInlineDescriptor *>(
349         asBlockPointer().Pointee->rawData());
350     GD.InitState = GlobalInitState::Initialized;
351     return;
352   }
353 
354   assert(Desc);
355   if (Desc->isPrimitiveArray()) {
356     // Primitive global arrays don't have an initmap.
357     if (isStatic() && PointeeStorage.BS.Base == 0)
358       return;
359 
360     // Nothing to do for these.
361     if (Desc->getNumElems() == 0)
362       return;
363 
364     InitMapPtr &IM = getInitMap();
365     if (!IM)
366       IM =
367           std::make_pair(false, std::make_shared<InitMap>(Desc->getNumElems()));
368 
369     assert(IM);
370 
371     // All initialized.
372     if (IM->first)
373       return;
374 
375     if (IM->second->initializeElement(getIndex())) {
376       IM->first = true;
377       IM->second.reset();
378     }
379     return;
380   }
381 
382   // Field has its bit in an inline descriptor.
383   assert(PointeeStorage.BS.Base != 0 &&
384          "Only composite fields can be initialised");
385   getInlineDesc()->IsInitialized = true;
386 }
387 
388 void Pointer::activate() const {
389   // Field has its bit in an inline descriptor.
390   assert(PointeeStorage.BS.Base != 0 &&
391          "Only composite fields can be activated");
392 
393   if (isRoot() && PointeeStorage.BS.Base == sizeof(GlobalInlineDescriptor))
394     return;
395   if (!getInlineDesc()->InUnion)
396     return;
397 
398   getInlineDesc()->IsActive = true;
399 
400   // Get the union, iterate over its fields and DEactivate all others.
401   Pointer UnionPtr = getBase();
402   while (!UnionPtr.getFieldDesc()->isUnion())
403     UnionPtr = UnionPtr.getBase();
404 
405   const Record *UnionRecord = UnionPtr.getRecord();
406   for (const Record::Field &F : UnionRecord->fields()) {
407     Pointer FieldPtr = UnionPtr.atField(F.Offset);
408     if (FieldPtr == *this) {
409     } else {
410       FieldPtr.getInlineDesc()->IsActive = false;
411       // FIXME: Recurse.
412     }
413   }
414 
415   Pointer B = getBase();
416   while (!B.isRoot() && B.inUnion()) {
417     // FIXME: Need to de-activate other fields of parent records.
418     B.getInlineDesc()->IsActive = true;
419     assert(B.isActive());
420     B = B.getBase();
421   }
422 }
423 
424 void Pointer::deactivate() const {
425   // TODO: this only appears in constructors, so nothing to deactivate.
426 }
427 
428 bool Pointer::hasSameBase(const Pointer &A, const Pointer &B) {
429   // Two null pointers always have the same base.
430   if (A.isZero() && B.isZero())
431     return true;
432 
433   if (A.isIntegralPointer() && B.isIntegralPointer())
434     return true;
435   if (A.isFunctionPointer() && B.isFunctionPointer())
436     return true;
437 
438   if (A.isIntegralPointer() || B.isIntegralPointer())
439     return A.getSource() == B.getSource();
440 
441   if (A.StorageKind != B.StorageKind)
442     return false;
443 
444   return A.asBlockPointer().Pointee == B.asBlockPointer().Pointee;
445 }
446 
447 bool Pointer::pointToSameBlock(const Pointer &A, const Pointer &B) {
448   if (!A.isBlockPointer() || !B.isBlockPointer())
449     return false;
450   return A.block() == B.block();
451 }
452 
453 bool Pointer::hasSameArray(const Pointer &A, const Pointer &B) {
454   return hasSameBase(A, B) &&
455          A.PointeeStorage.BS.Base == B.PointeeStorage.BS.Base &&
456          A.getFieldDesc()->IsArray;
457 }
458 
459 std::optional<APValue> Pointer::toRValue(const Context &Ctx,
460                                          QualType ResultType) const {
461   const ASTContext &ASTCtx = Ctx.getASTContext();
462   assert(!ResultType.isNull());
463   // Method to recursively traverse composites.
464   std::function<bool(QualType, const Pointer &, APValue &)> Composite;
465   Composite = [&Composite, &Ctx, &ASTCtx](QualType Ty, const Pointer &Ptr,
466                                           APValue &R) {
467     if (const auto *AT = Ty->getAs<AtomicType>())
468       Ty = AT->getValueType();
469 
470     // Invalid pointers.
471     if (Ptr.isDummy() || !Ptr.isLive() || !Ptr.isBlockPointer() ||
472         Ptr.isPastEnd())
473       return false;
474 
475     // Primitive values.
476     if (std::optional<PrimType> T = Ctx.classify(Ty)) {
477       TYPE_SWITCH(*T, R = Ptr.deref<T>().toAPValue(ASTCtx));
478       return true;
479     }
480 
481     if (const auto *RT = Ty->getAs<RecordType>()) {
482       const auto *Record = Ptr.getRecord();
483       assert(Record && "Missing record descriptor");
484 
485       bool Ok = true;
486       if (RT->getDecl()->isUnion()) {
487         const FieldDecl *ActiveField = nullptr;
488         APValue Value;
489         for (const auto &F : Record->fields()) {
490           const Pointer &FP = Ptr.atField(F.Offset);
491           QualType FieldTy = F.Decl->getType();
492           if (FP.isActive()) {
493             if (std::optional<PrimType> T = Ctx.classify(FieldTy)) {
494               TYPE_SWITCH(*T, Value = FP.deref<T>().toAPValue(ASTCtx));
495             } else {
496               Ok &= Composite(FieldTy, FP, Value);
497             }
498             ActiveField = FP.getFieldDesc()->asFieldDecl();
499             break;
500           }
501         }
502         R = APValue(ActiveField, Value);
503       } else {
504         unsigned NF = Record->getNumFields();
505         unsigned NB = Record->getNumBases();
506         unsigned NV = Ptr.isBaseClass() ? 0 : Record->getNumVirtualBases();
507 
508         R = APValue(APValue::UninitStruct(), NB, NF);
509 
510         for (unsigned I = 0; I < NF; ++I) {
511           const Record::Field *FD = Record->getField(I);
512           QualType FieldTy = FD->Decl->getType();
513           const Pointer &FP = Ptr.atField(FD->Offset);
514           APValue &Value = R.getStructField(I);
515 
516           if (std::optional<PrimType> T = Ctx.classify(FieldTy)) {
517             TYPE_SWITCH(*T, Value = FP.deref<T>().toAPValue(ASTCtx));
518           } else {
519             Ok &= Composite(FieldTy, FP, Value);
520           }
521         }
522 
523         for (unsigned I = 0; I < NB; ++I) {
524           const Record::Base *BD = Record->getBase(I);
525           QualType BaseTy = Ctx.getASTContext().getRecordType(BD->Decl);
526           const Pointer &BP = Ptr.atField(BD->Offset);
527           Ok &= Composite(BaseTy, BP, R.getStructBase(I));
528         }
529 
530         for (unsigned I = 0; I < NV; ++I) {
531           const Record::Base *VD = Record->getVirtualBase(I);
532           QualType VirtBaseTy = Ctx.getASTContext().getRecordType(VD->Decl);
533           const Pointer &VP = Ptr.atField(VD->Offset);
534           Ok &= Composite(VirtBaseTy, VP, R.getStructBase(NB + I));
535         }
536       }
537       return Ok;
538     }
539 
540     if (Ty->isIncompleteArrayType()) {
541       R = APValue(APValue::UninitArray(), 0, 0);
542       return true;
543     }
544 
545     if (const auto *AT = Ty->getAsArrayTypeUnsafe()) {
546       const size_t NumElems = Ptr.getNumElems();
547       QualType ElemTy = AT->getElementType();
548       R = APValue(APValue::UninitArray{}, NumElems, NumElems);
549 
550       bool Ok = true;
551       for (unsigned I = 0; I < NumElems; ++I) {
552         APValue &Slot = R.getArrayInitializedElt(I);
553         const Pointer &EP = Ptr.atIndex(I);
554         if (std::optional<PrimType> T = Ctx.classify(ElemTy)) {
555           TYPE_SWITCH(*T, Slot = EP.deref<T>().toAPValue(ASTCtx));
556         } else {
557           Ok &= Composite(ElemTy, EP.narrow(), Slot);
558         }
559       }
560       return Ok;
561     }
562 
563     // Complex types.
564     if (const auto *CT = Ty->getAs<ComplexType>()) {
565       QualType ElemTy = CT->getElementType();
566 
567       if (ElemTy->isIntegerType()) {
568         std::optional<PrimType> ElemT = Ctx.classify(ElemTy);
569         assert(ElemT);
570         INT_TYPE_SWITCH(*ElemT, {
571           auto V1 = Ptr.atIndex(0).deref<T>();
572           auto V2 = Ptr.atIndex(1).deref<T>();
573           R = APValue(V1.toAPSInt(), V2.toAPSInt());
574           return true;
575         });
576       } else if (ElemTy->isFloatingType()) {
577         R = APValue(Ptr.atIndex(0).deref<Floating>().getAPFloat(),
578                     Ptr.atIndex(1).deref<Floating>().getAPFloat());
579         return true;
580       }
581       return false;
582     }
583 
584     // Vector types.
585     if (const auto *VT = Ty->getAs<VectorType>()) {
586       assert(Ptr.getFieldDesc()->isPrimitiveArray());
587       QualType ElemTy = VT->getElementType();
588       PrimType ElemT = *Ctx.classify(ElemTy);
589 
590       SmallVector<APValue> Values;
591       Values.reserve(VT->getNumElements());
592       for (unsigned I = 0; I != VT->getNumElements(); ++I) {
593         TYPE_SWITCH(ElemT, {
594           Values.push_back(Ptr.atIndex(I).deref<T>().toAPValue(ASTCtx));
595         });
596       }
597 
598       assert(Values.size() == VT->getNumElements());
599       R = APValue(Values.data(), Values.size());
600       return true;
601     }
602 
603     llvm_unreachable("invalid value to return");
604   };
605 
606   // Invalid to read from.
607   if (isDummy() || !isLive() || isPastEnd())
608     return std::nullopt;
609 
610   // We can return these as rvalues, but we can't deref() them.
611   if (isZero() || isIntegralPointer())
612     return toAPValue(ASTCtx);
613 
614   // Just load primitive types.
615   if (std::optional<PrimType> T = Ctx.classify(ResultType)) {
616     TYPE_SWITCH(*T, return this->deref<T>().toAPValue(ASTCtx));
617   }
618 
619   // Return the composite type.
620   APValue Result;
621   if (!Composite(getType(), *this, Result))
622     return std::nullopt;
623   return Result;
624 }
625 
626 IntPointer IntPointer::atOffset(const ASTContext &ASTCtx,
627                                 unsigned Offset) const {
628   if (!this->Desc)
629     return *this;
630   const Record *R = this->Desc->ElemRecord;
631   if (!R)
632     return *this;
633 
634   const Record::Field *F = nullptr;
635   for (auto &It : R->fields()) {
636     if (It.Offset == Offset) {
637       F = &It;
638       break;
639     }
640   }
641   if (!F)
642     return *this;
643 
644   const FieldDecl *FD = F->Decl;
645   const ASTRecordLayout &Layout = ASTCtx.getASTRecordLayout(FD->getParent());
646   unsigned FieldIndex = FD->getFieldIndex();
647   uint64_t FieldOffset =
648       ASTCtx.toCharUnitsFromBits(Layout.getFieldOffset(FieldIndex))
649           .getQuantity();
650   return IntPointer{this->Desc, FieldOffset};
651 }
652