clang 23.0.0git
Pointer.cpp
Go to the documentation of this file.
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 "Char.h"
12#include "Context.h"
13#include "Floating.h"
14#include "Function.h"
15#include "InitMap.h"
16#include "Integral.h"
17#include "InterpBlock.h"
18#include "MemberPointer.h"
19#include "PrimType.h"
20#include "Record.h"
21#include "clang/AST/Expr.h"
22#include "clang/AST/ExprCXX.h"
24
25using namespace clang;
26using namespace clang::interp;
27
29 : Pointer(Pointee, Pointee->getDescriptor()->getMetadataSize(),
30 Pointee->getDescriptor()->getMetadataSize()) {}
31
32Pointer::Pointer(Block *Pointee, uint64_t BaseAndOffset)
33 : Pointer(Pointee, BaseAndOffset, BaseAndOffset) {}
34
35Pointer::Pointer(Block *Pointee, unsigned Base, uint64_t Offset)
36 : Offset(Offset), StorageKind(Storage::Block) {
37 assert(Pointee);
38 assert((Base == RootPtrMark || Base % alignof(void *) == 0) && "wrong base");
39 assert(Base >= Pointee->getDescriptor()->getMetadataSize());
40
41 BS = {Pointee, Base, nullptr, nullptr};
42 Pointee->addPointer(this);
43}
44
46 : Offset(P.Offset), StorageKind(P.StorageKind) {
47 switch (StorageKind) {
48 case Storage::Int:
49 Int = P.Int;
50 break;
51 case Storage::Block:
52 BS = P.BS;
53 if (BS.Pointee)
54 BS.Pointee->addPointer(this);
55 break;
56 case Storage::Fn:
57 Fn = P.Fn;
58 break;
59 case Storage::Typeid:
60 Typeid = P.Typeid;
61 break;
62 }
63}
64
65Pointer::Pointer(Pointer &&P) : Offset(P.Offset), StorageKind(P.StorageKind) {
66 switch (StorageKind) {
67 case Storage::Int:
68 Int = P.Int;
69 break;
70 case Storage::Block:
71 BS = P.BS;
72 if (BS.Pointee)
73 BS.Pointee->replacePointer(&P, this);
74 break;
75 case Storage::Fn:
76 Fn = P.Fn;
77 break;
78 case Storage::Typeid:
79 Typeid = P.Typeid;
80 break;
81 }
82}
83
85 if (!isBlockPointer())
86 return;
87
88 if (Block *Pointee = BS.Pointee) {
89 Pointee->removePointer(this);
90 BS.Pointee = nullptr;
91 Pointee->cleanup();
92 }
93}
94
96 // If the current storage type is Block, we need to remove
97 // this pointer from the block.
98 if (isBlockPointer()) {
99 if (P.isBlockPointer() && this->block() == P.block()) {
100 Offset = P.Offset;
101 BS.Base = P.BS.Base;
102 return *this;
103 }
104
105 if (Block *Pointee = BS.Pointee) {
106 Pointee->removePointer(this);
107 BS.Pointee = nullptr;
108 Pointee->cleanup();
109 }
110 }
111
112 StorageKind = P.StorageKind;
113 Offset = P.Offset;
114
115 switch (StorageKind) {
116 case Storage::Int:
117 Int = P.Int;
118 break;
119 case Storage::Block:
120 BS = P.BS;
121
122 if (BS.Pointee)
123 BS.Pointee->addPointer(this);
124 break;
125 case Storage::Fn:
126 Fn = P.Fn;
127 break;
128 case Storage::Typeid:
129 Typeid = P.Typeid;
130 }
131 return *this;
132}
133
135 // If the current storage type is Block, we need to remove
136 // this pointer from the block.
137 if (isBlockPointer()) {
138 if (P.isBlockPointer() && this->block() == P.block()) {
139 Offset = P.Offset;
140 BS.Base = P.BS.Base;
141 return *this;
142 }
143
144 if (Block *Pointee = BS.Pointee) {
145 Pointee->removePointer(this);
146 BS.Pointee = nullptr;
147 Pointee->cleanup();
148 }
149 }
150
151 StorageKind = P.StorageKind;
152 Offset = P.Offset;
153
154 switch (StorageKind) {
155 case Storage::Int:
156 Int = P.Int;
157 break;
158 case Storage::Block:
159 BS = P.BS;
160
161 if (BS.Pointee)
162 BS.Pointee->addPointer(this);
163 break;
164 case Storage::Fn:
165 Fn = P.Fn;
166 break;
167 case Storage::Typeid:
168 Typeid = P.Typeid;
169 }
170 return *this;
171}
172
175
176 if (isZero())
177 return APValue(static_cast<const Expr *>(nullptr), CharUnits::Zero(), Path,
178 /*IsOnePastEnd=*/false, /*IsNullPtr=*/true);
179 if (isIntegralPointer())
180 return APValue(static_cast<const Expr *>(nullptr),
182 Path,
183 /*IsOnePastEnd=*/false, /*IsNullPtr=*/false);
184 if (isFunctionPointer()) {
186 if (const FunctionDecl *FD = FP.Func->getDecl())
187 return APValue(FD, CharUnits::fromQuantity(Offset), {},
188 /*OnePastTheEnd=*/false, /*IsNull=*/false);
189 return APValue(FP.Func->getExpr(), CharUnits::fromQuantity(Offset), {},
190 /*OnePastTheEnd=*/false, /*IsNull=*/false);
191 }
192
193 if (isTypeidPointer()) {
196 TypeInfo, QualType(Typeid.TypeInfoType, 0)),
197 CharUnits::Zero(), {},
198 /*OnePastTheEnd=*/false, /*IsNull=*/false);
199 }
200
201 // Build the lvalue base from the block.
202 const Descriptor *Desc = getDeclDesc();
204 if (const auto *VD = Desc->asValueDecl())
205 Base = VD;
206 else if (const auto *E = Desc->asExpr()) {
207 if (block()->isDynamic()) {
208 QualType AllocatedType = getDeclPtr().getFieldDesc()->getDataType(ASTCtx);
209 DynamicAllocLValue DA(*block()->DynAllocId);
210 Base = APValue::LValueBase::getDynamicAlloc(DA, AllocatedType);
211 } else {
212 Base = E;
213 }
214 } else
215 llvm_unreachable("Invalid allocation type");
216
217 if (isUnknownSizeArray())
218 return APValue(Base, CharUnits::Zero(), Path,
219 /*IsOnePastEnd=*/isOnePastEnd(), /*IsNullPtr=*/false);
220
221 CharUnits Offset = CharUnits::Zero();
222
223 auto getFieldOffset = [&](const FieldDecl *FD) -> CharUnits {
224 // This shouldn't happen, but if it does, don't crash inside
225 // getASTRecordLayout.
226 if (FD->getParent()->isInvalidDecl())
227 return CharUnits::Zero();
228 const ASTRecordLayout &Layout = ASTCtx.getASTRecordLayout(FD->getParent());
229 unsigned FieldIndex = FD->getFieldIndex();
230 return ASTCtx.toCharUnitsFromBits(Layout.getFieldOffset(FieldIndex));
231 };
232
233 // Build the path into the object.
234 bool OnePastEnd = isOnePastEnd() && !isZeroSizeArray();
235 Pointer Ptr = *this;
236 while (Ptr.isField() || Ptr.isArrayElement()) {
237
238 if (Ptr.isArrayRoot()) {
239 // An array root may still be an array element itself.
240 if (Ptr.isArrayElement()) {
241 Ptr = Ptr.expand();
242 const Descriptor *Desc = Ptr.getFieldDesc();
243 unsigned Index = Ptr.getIndex();
244 QualType ElemType = Desc->getElemQualType();
245 Offset += (Index * ASTCtx.getTypeSizeInChars(ElemType));
246 if (Ptr.getArray().getFieldDesc()->IsArray)
247 Path.push_back(APValue::LValuePathEntry::ArrayIndex(Index));
248 Ptr = Ptr.getArray();
249 } else {
250 const Descriptor *Desc = Ptr.getFieldDesc();
251 const auto *Dcl = Desc->asDecl();
252 Path.push_back(APValue::LValuePathEntry({Dcl, /*IsVirtual=*/false}));
253
254 if (const auto *FD = dyn_cast_if_present<FieldDecl>(Dcl))
255 Offset += getFieldOffset(FD);
256
257 Ptr = Ptr.getBase();
258 }
259 } else if (Ptr.isArrayElement()) {
260 Ptr = Ptr.expand();
261 const Descriptor *Desc = Ptr.getFieldDesc();
262 unsigned Index;
263 if (Ptr.isOnePastEnd()) {
264 Index = Ptr.getArray().getNumElems();
265 OnePastEnd = false;
266 } else
267 Index = Ptr.getIndex();
268
269 QualType ElemType = Desc->getElemQualType();
270 if (const auto *RD = ElemType->getAsRecordDecl();
271 RD && !RD->getDefinition()) {
272 // Ignore this for the offset.
273 } else {
274 Offset += (Index * ASTCtx.getTypeSizeInChars(ElemType));
275 }
276 if (Ptr.getArray().getFieldDesc()->IsArray)
277 Path.push_back(APValue::LValuePathEntry::ArrayIndex(Index));
278 Ptr = Ptr.getArray();
279 } else {
280 const Descriptor *Desc = Ptr.getFieldDesc();
281
282 // Create a path entry for the field.
283 if (const auto *BaseOrMember = Desc->asDecl()) {
284 bool IsVirtual = false;
285 if (const auto *FD = dyn_cast<FieldDecl>(BaseOrMember)) {
286 Ptr = Ptr.getBase();
287 Offset += getFieldOffset(FD);
288 } else if (const auto *RD = dyn_cast<CXXRecordDecl>(BaseOrMember)) {
289 IsVirtual = Ptr.isVirtualBaseClass();
290 Ptr = Ptr.getBase();
291 const Record *BaseRecord = Ptr.getRecord();
292
293 const ASTRecordLayout &Layout = ASTCtx.getASTRecordLayout(
294 cast<CXXRecordDecl>(BaseRecord->getDecl()));
295 if (IsVirtual)
296 Offset += Layout.getVBaseClassOffset(RD);
297 else
298 Offset += Layout.getBaseClassOffset(RD);
299
300 } else {
301 Ptr = Ptr.getBase();
302 }
303 Path.push_back(APValue::LValuePathEntry({BaseOrMember, IsVirtual}));
304 continue;
305 }
306 llvm_unreachable("Invalid field type");
307 }
308 }
309
310 // We assemble the LValuePath starting from the innermost pointer to the
311 // outermost one. SO in a.b.c, the first element in Path will refer to
312 // the field 'c', while later code expects it to refer to 'a'.
313 // Just invert the order of the elements.
314 std::reverse(Path.begin(), Path.end());
315
316 auto Result = APValue(Base, Offset, Path, OnePastEnd);
317 Result.setConstexprUnknown(isConstexprUnknown());
318 return Result;
319}
320
321void Pointer::print(llvm::raw_ostream &OS) const {
322 switch (StorageKind) {
323 case Storage::Block: {
324 const Block *B = BS.Pointee;
325 OS << "(Block) " << B << " {";
326
327 if (isRoot())
328 OS << "rootptr(" << BS.Base << "), ";
329 else
330 OS << BS.Base << ", ";
331
332 if (isElementPastEnd())
333 OS << "pastend, ";
334 else
335 OS << Offset << ", ";
336
337 if (B)
338 OS << B->getSize();
339 else
340 OS << "nullptr";
341 OS << "}";
342 } break;
343 case Storage::Int:
344 OS << "(Int) {";
345 OS << Int.Value << " + " << Offset << ", " << Int.Desc;
346 OS << "}";
347 break;
348 case Storage::Fn:
349 OS << "(Fn) { " << Fn.Func << " + " << Offset << " }";
350 break;
351 case Storage::Typeid:
352 OS << "(Typeid) { " << (const void *)asTypeidPointer().TypePtr << ", "
353 << (const void *)asTypeidPointer().TypeInfoType << " + " << Offset
354 << "}";
355 }
356}
357
358/// Compute an offset that can be used to compare the pointer to another one
359/// with the same base. To get accurate results, we basically _have to_ compute
360/// the lvalue offset using the ASTRecordLayout.
361///
362/// This function will fail if we're trying to get the type size of a forward
363/// declaration.
364///
365// FIXME: We're still mixing values from the record layout with our internal
366// offsets, which will inevitably lead to cryptic errors.
367std::optional<size_t>
369 switch (StorageKind) {
370 case Storage::Int:
371 return Int.Value + Offset;
372 case Storage::Block:
373 // See below.
374 break;
375 case Storage::Fn:
377 case Storage::Typeid:
378 return reinterpret_cast<uintptr_t>(asTypeidPointer().TypePtr) + Offset;
379 }
380
381 auto getTypeSize = [&](QualType T) -> std::optional<size_t> {
382 if (const RecordType *RT = T->getAs<RecordType>()) {
383 // We cannot get the type size of a forward declaration.
384 if (!RT->getDecl()->getDefinition())
385 return std::nullopt;
386 }
387 return ASTCtx.getTypeSizeInChars(T).getQuantity();
388 };
389
390 size_t Result = 0;
391 Pointer P = *this;
392 while (true) {
393 if (P.isVirtualBaseClass()) {
394 Result += getInlineDesc()->Offset;
395 P = P.getBase();
396 continue;
397 }
398
399 if (P.isBaseClass()) {
400 if (P.getRecord()->getNumVirtualBases() > 0)
401 Result += P.getInlineDesc()->Offset;
402 P = P.getBase();
403 continue;
404 }
405 if (P.isArrayElement()) {
406 P = P.expand();
407 Result += (P.getIndex() * P.elemSize());
408 P = P.getArray();
409 continue;
410 }
411
412 if (P.isRoot()) {
413 if (P.isOnePastEnd()) {
414 if (auto Size = getTypeSize(P.getDeclDesc()->getType()))
415 Result += *Size;
416 else
417 return std::nullopt;
418 }
419 break;
420 }
421
422 assert(P.getField());
423 const Record *R = P.getBase().getRecord();
424 assert(R);
425
426 const ASTRecordLayout &Layout = ASTCtx.getASTRecordLayout(R->getDecl());
427 Result += ASTCtx
430 .getQuantity();
431
432 if (P.isOnePastEnd()) {
433 if (auto Size = getTypeSize(P.getField()->getType()))
434 Result += *Size;
435 else
436 return std::nullopt;
437 }
438
439 P = P.getBase();
440 if (P.isRoot())
441 break;
442 }
443 return Result;
444}
445
446std::string Pointer::toDiagnosticString(const ASTContext &Ctx) const {
447 if (isZero())
448 return "nullptr";
449
450 if (isIntegralPointer())
451 return (Twine("&(") + Twine(asIntPointer().Value + Offset) + ")").str();
452
453 QualType Ty = getType();
454 if (Ty->isLValueReferenceType())
455 Ty = Ty->getPointeeType();
456 return toAPValue(Ctx).getAsString(Ctx, Ty);
457}
458
460 if (!isBlockPointer())
461 return true;
462
463 if (isRoot() && BS.Base == sizeof(GlobalInlineDescriptor) &&
464 Offset == BS.Base) {
465 const auto &GD = block()->getBlockDesc<GlobalInlineDescriptor>();
467 }
468
469 assert(BS.Pointee && "Cannot check if null pointer was initialized");
470 const Descriptor *Desc = getFieldDesc();
471 assert(Desc);
472 if (Desc->isPrimitiveArray())
474
475 if (asBlockPointer().Base == 0)
476 return true;
477 // Field has its bit in an inline descriptor.
478 return getInlineDesc()->IsInitialized;
479}
480
481bool Pointer::isElementInitialized(unsigned Index) const {
482 if (!isBlockPointer())
483 return true;
484
485 const Descriptor *Desc = getFieldDesc();
486 assert(Desc);
487
488 if (isStatic() && BS.Base == 0)
489 return true;
490
491 if (isRoot() && BS.Base == sizeof(GlobalInlineDescriptor) &&
492 Offset == BS.Base) {
493 const auto &GD = block()->getBlockDesc<GlobalInlineDescriptor>();
495 }
496
497 if (Desc->isPrimitiveArray()) {
498 InitMapPtr IM = getInitMap();
499
500 if (IM.allInitialized())
501 return true;
502
503 if (!IM.hasInitMap())
504 return false;
505 return IM->isElementInitialized(Index);
506 }
507 return isInitialized();
508}
509
510bool Pointer::isElementAlive(unsigned Index) const {
511 assert(getFieldDesc()->isPrimitiveArray());
512
513 InitMapPtr &IM = getInitMap();
514 if (!IM.hasInitMap())
515 return true;
516
517 if (IM.allInitialized())
518 return true;
519
520 return IM->isElementAlive(Index);
521}
522
524
526
528 if (!isBlockPointer())
529 return;
530 if (BS.Base < sizeof(InlineDescriptor))
531 return;
532
533 if (inArray() && !isArrayRoot()) {
534 assert(L == Lifetime::Started || L == Lifetime::Ended);
535 const Descriptor *Desc = getFieldDesc();
536 InitMapPtr &IM = getInitMap();
537 if (!IM.hasInitMap())
538 IM.setInitMap(new InitMap(Desc->getNumElems(), IM.allInitialized()));
539
540 if (L == Lifetime::Ended)
542 else if (L == Lifetime::Started)
544 assert(isArrayRoot() || (this->getLifetime() == L));
545 return;
546 }
547
548 getInlineDesc()->LifeState = L;
549}
550
552 if (!isBlockPointer())
553 return;
554
555 assert(BS.Pointee && "Cannot initialize null pointer");
556
557 if (isRoot() && BS.Base == sizeof(GlobalInlineDescriptor) &&
558 Offset == BS.Base) {
559 auto &GD = BS.Pointee->getBlockDesc<GlobalInlineDescriptor>();
561 return;
562 }
563
564 const Descriptor *Desc = getFieldDesc();
565 assert(Desc);
566 if (Desc->isPrimitiveArray()) {
567 if (Desc->getNumElems() != 0)
569 return;
570 }
571
572 // Field has its bit in an inline descriptor.
573 assert(BS.Base != 0 && "Only composite fields can be initialised");
574 getInlineDesc()->IsInitialized = true;
575 getInlineDesc()->LifeState = Lifetime::Started;
576}
577
578void Pointer::initializeElement(unsigned Index) const {
579 // Primitive global arrays don't have an initmap.
580 if (isStatic() && BS.Base == 0)
581 return;
582
583 assert(Index < getFieldDesc()->getNumElems());
584
585 InitMapPtr &IM = getInitMap();
586 if (IM.allInitialized())
587 return;
588
589 if (!IM.hasInitMap()) {
590 const Descriptor *Desc = getFieldDesc();
591 IM.setInitMap(new InitMap(Desc->getNumElems()));
592 }
593 assert(IM.hasInitMap());
594
595 if (IM->initializeElement(Index))
597}
598
600 assert(getFieldDesc()->isPrimitiveArray());
601 assert(isArrayRoot());
602
603 getInitMap().noteAllInitialized();
604}
605
607 assert(getFieldDesc()->isPrimitiveArray());
608 assert(isArrayRoot());
609
610 if (isStatic() && BS.Base == 0)
611 return true;
612
613 if (isRoot() && BS.Base == sizeof(GlobalInlineDescriptor) &&
614 Offset == BS.Base) {
615 const auto &GD = block()->getBlockDesc<GlobalInlineDescriptor>();
617 }
618
619 InitMapPtr IM = getInitMap();
620 return IM.allInitialized();
621}
622
624 assert(getFieldDesc()->isPrimitiveArray());
625 assert(isArrayRoot());
626
627 if (isStatic() && BS.Base == 0)
628 return true;
629
630 if (isRoot() && BS.Base == sizeof(GlobalInlineDescriptor) &&
631 Offset == BS.Base) {
632 const auto &GD = block()->getBlockDesc<GlobalInlineDescriptor>();
634 }
635
636 InitMapPtr &IM = getInitMap();
637 return IM.allInitialized() || (IM.hasInitMap() && IM->allElementsAlive());
638}
639
640void Pointer::activate() const {
641 // Field has its bit in an inline descriptor.
642 assert(BS.Base != 0 && "Only composite fields can be activated");
643
644 if (isRoot() && BS.Base == sizeof(GlobalInlineDescriptor))
645 return;
646 if (!getInlineDesc()->InUnion)
647 return;
648
650 activate = [&activate](Pointer &P) -> void {
651 P.getInlineDesc()->IsActive = true;
652 P.startLifetime();
653 if (const Record *R = P.getRecord(); R && !R->isUnion()) {
654 for (const Record::Field &F : R->fields()) {
655 Pointer FieldPtr = P.atField(F.Offset);
656 if (!FieldPtr.getInlineDesc()->IsActive)
657 activate(FieldPtr);
658 }
659 // FIXME: Bases?
660 }
661 };
662
664 deactivate = [&deactivate](Pointer &P) -> void {
665 P.getInlineDesc()->IsActive = false;
666
667 if (const Record *R = P.getRecord()) {
668 for (const Record::Field &F : R->fields()) {
669 Pointer FieldPtr = P.atField(F.Offset);
670 if (FieldPtr.getInlineDesc()->IsActive)
671 deactivate(FieldPtr);
672 }
673 // FIXME: Bases?
674 }
675 };
676
677 Pointer B = *this;
678 // Primitive array elements can't be activated individually, so
679 // look at the array root instead.
681 B = B.getArray();
682
683 while (!B.isRoot() && B.inUnion()) {
684 activate(B);
685
686 // When walking up the pointer chain, deactivate
687 // all union child pointers that aren't on our path.
688 Pointer Cur = B;
689 B = B.getBase();
690 if (const Record *BR = B.getRecord(); BR && BR->isUnion()) {
691 for (const Record::Field &F : BR->fields()) {
692 Pointer FieldPtr = B.atField(F.Offset);
693 if (FieldPtr != Cur)
694 deactivate(FieldPtr);
695 }
696 }
697 }
698}
699
701 // TODO: this only appears in constructors, so nothing to deactivate.
702}
703
704bool Pointer::hasSameBase(const Pointer &A, const Pointer &B) {
705 // Two null pointers always have the same base.
706 if (A.isZero() && B.isZero())
707 return true;
708
710 return true;
712 return true;
713 if (A.isTypeidPointer() && B.isTypeidPointer())
714 return true;
715
716 if (A.StorageKind != B.StorageKind)
717 return false;
718
720}
721
722bool Pointer::pointToSameBlock(const Pointer &A, const Pointer &B) {
723 if (!A.isBlockPointer() || !B.isBlockPointer())
724 return false;
725 return A.block() == B.block();
726}
727
728bool Pointer::hasSameArray(const Pointer &A, const Pointer &B) {
729 return hasSameBase(A, B) && A.BS.Base == B.BS.Base &&
731}
732
734 if (isZero() || !isBlockPointer())
735 return false;
736
737 if (block()->isDynamic())
738 return false;
739
740 const Expr *E = block()->getDescriptor()->asExpr();
742}
743
745 if (isZero() || !isBlockPointer())
746 return false;
747
748 if (block()->isDynamic())
749 return false;
750
751 const Expr *E = block()->getDescriptor()->asExpr();
752 return isa_and_nonnull<StringLiteral>(E);
753}
754
756 if (isZero() || !isBlockPointer())
757 return false;
758
759 if (const Expr *E = BS.Pointee->getDescriptor()->asExpr())
760 return isa<AddrLabelExpr>(E);
761 return false;
762}
763
764std::optional<std::pair<Pointer, Pointer>>
766 if (!A.isBlockPointer() || !B.isBlockPointer())
767 return std::nullopt;
768
770 return std::nullopt;
771 if (A.isRoot() && B.isRoot())
772 return std::nullopt;
773
774 if (A == B)
775 return std::make_pair(A, B);
776
777 auto getBase = [](const Pointer &P) -> Pointer {
778 if (P.isArrayElement())
779 return P.expand().getArray();
780 return P.getBase();
781 };
782
783 Pointer IterA = A;
784 Pointer IterB = B;
785 Pointer CurA = IterA;
786 Pointer CurB = IterB;
787 for (;;) {
788 if (IterA.asBlockPointer().Base > IterB.asBlockPointer().Base) {
789 CurA = IterA;
790 IterA = getBase(IterA);
791 } else {
792 CurB = IterB;
793 IterB = getBase(IterB);
794 }
795
796 if (IterA == IterB)
797 return std::make_pair(CurA, CurB);
798
799 if (IterA.isRoot() && IterB.isRoot())
800 return std::nullopt;
801 }
802
803 llvm_unreachable("The loop above should've returned.");
804}
805
806std::optional<APValue> Pointer::toRValue(const Context &Ctx,
807 QualType ResultType) const {
808 const ASTContext &ASTCtx = Ctx.getASTContext();
809 assert(!ResultType.isNull());
810 // Method to recursively traverse composites.
811 std::function<bool(QualType, const Pointer &, APValue &)> Composite;
812 Composite = [&Composite, &Ctx, &ASTCtx](QualType Ty, const Pointer &Ptr,
813 APValue &R) {
814 if (const auto *AT = Ty->getAs<AtomicType>())
815 Ty = AT->getValueType();
816
817 // Invalid pointers.
818 if (Ptr.isDummy() || !Ptr.isLive() || !Ptr.isBlockPointer() ||
819 Ptr.isPastEnd())
820 return false;
821
822 // Primitives should never end up here.
823 assert(!Ctx.canClassify(Ty));
824
825 if (const auto *RT = Ty->getAsCanonical<RecordType>()) {
826 const auto *Record = Ptr.getRecord();
827 assert(Record && "Missing record descriptor");
828
829 bool Ok = true;
830 if (RT->getDecl()->isUnion()) {
831 const FieldDecl *ActiveField = nullptr;
833 for (const auto &F : Record->fields()) {
834 const Pointer &FP = Ptr.atField(F.Offset);
835 if (FP.isActive()) {
836 const Descriptor *Desc = F.Desc;
837 if (Desc->isPrimitive()) {
838 TYPE_SWITCH(Desc->getPrimType(),
839 Value = FP.deref<T>().toAPValue(ASTCtx));
840 } else {
841 QualType FieldTy = F.Decl->getType();
842 Ok &= Composite(FieldTy, FP, Value);
843 }
844 ActiveField = FP.getFieldDesc()->asFieldDecl();
845 break;
846 }
847 }
848 R = APValue(ActiveField, Value);
849 } else {
850 unsigned NF = Record->getNumFields();
851 unsigned NB = Record->getNumBases();
852 unsigned NV = Ptr.isBaseClass() ? 0 : Record->getNumVirtualBases();
853
854 R = APValue(APValue::UninitStruct(), NB, NF);
855
856 for (unsigned I = 0; I != NF; ++I) {
857 const Record::Field *FD = Record->getField(I);
858 const Descriptor *Desc = FD->Desc;
859 const Pointer &FP = Ptr.atField(FD->Offset);
860 APValue &Value = R.getStructField(I);
861 if (Desc->isPrimitive()) {
862 TYPE_SWITCH(Desc->getPrimType(),
863 Value = FP.deref<T>().toAPValue(ASTCtx));
864 } else {
865 QualType FieldTy = FD->Decl->getType();
866 Ok &= Composite(FieldTy, FP, Value);
867 }
868 }
869
870 for (unsigned I = 0; I != NB; ++I) {
871 const Record::Base *BD = Record->getBase(I);
872 QualType BaseTy = Ctx.getASTContext().getCanonicalTagType(BD->Decl);
873 const Pointer &BP = Ptr.atField(BD->Offset);
874 Ok &= Composite(BaseTy, BP, R.getStructBase(I));
875 }
876
877 for (unsigned I = 0; I != NV; ++I) {
878 const Record::Base *VD = Record->getVirtualBase(I);
879 QualType VirtBaseTy =
880 Ctx.getASTContext().getCanonicalTagType(VD->Decl);
881 const Pointer &VP = Ptr.atField(VD->Offset);
882 Ok &= Composite(VirtBaseTy, VP, R.getStructBase(NB + I));
883 }
884 }
885 return Ok;
886 }
887
888 if (Ty->isIncompleteArrayType()) {
889 R = APValue(APValue::UninitArray(), 0, 0);
890 return true;
891 }
892
893 if (const auto *AT = Ty->getAsArrayTypeUnsafe()) {
894 const size_t NumElems = Ptr.getNumElems();
895 QualType ElemTy = AT->getElementType();
896 R = APValue(APValue::UninitArray{}, NumElems, NumElems);
897
898 bool Ok = true;
899 OptPrimType ElemT = Ctx.classify(ElemTy);
900 for (unsigned I = 0; I != NumElems; ++I) {
901 APValue &Slot = R.getArrayInitializedElt(I);
902 if (ElemT) {
903 TYPE_SWITCH(*ElemT, Slot = Ptr.elem<T>(I).toAPValue(ASTCtx));
904 } else {
905 Ok &= Composite(ElemTy, Ptr.atIndex(I).narrow(), Slot);
906 }
907 }
908 return Ok;
909 }
910
911 // Complex types.
912 if (Ty->isAnyComplexType()) {
913 const Descriptor *Desc = Ptr.getFieldDesc();
914 // Can happen via C casts.
915 if (!Desc->isPrimitiveArray())
916 return false;
917
918 PrimType ElemT = Desc->getPrimType();
919 if (isIntegerOrBoolType(ElemT)) {
920 PrimType ElemT = Desc->getPrimType();
921 INT_TYPE_SWITCH(ElemT, {
922 auto V1 = Ptr.elem<T>(0);
923 auto V2 = Ptr.elem<T>(1);
924 R = APValue(V1.toAPSInt(), V2.toAPSInt());
925 return true;
926 });
927 } else if (ElemT == PT_Float) {
928 R = APValue(Ptr.elem<Floating>(0).getAPFloat(),
929 Ptr.elem<Floating>(1).getAPFloat());
930 return true;
931 }
932 return false;
933 }
934
935 // Vector types.
936 if (const auto *VT = Ty->getAs<VectorType>()) {
937 const Descriptor *Desc = Ptr.getFieldDesc();
938 assert(Ptr.getFieldDesc()->isPrimitiveArray());
939 PrimType ElemT = Desc->getPrimType();
940
942 Values.reserve(VT->getNumElements());
943 for (unsigned I = 0; I != VT->getNumElements(); ++I) {
944 TYPE_SWITCH(ElemT,
945 { Values.push_back(Ptr.elem<T>(I).toAPValue(ASTCtx)); });
946 }
947
948 assert(Values.size() == VT->getNumElements());
949 R = APValue(Values.data(), Values.size());
950 return true;
951 }
952
953 // Constant Matrix types.
954 if (const auto *MT = Ty->getAs<ConstantMatrixType>()) {
955 assert(Ptr.getFieldDesc()->isPrimitiveArray());
956 const Descriptor *Desc = Ptr.getFieldDesc();
957 PrimType ElemT = Desc->getPrimType();
958 unsigned NumElems = MT->getNumElementsFlattened();
959
961 Values.reserve(NumElems);
962 for (unsigned I = 0; I != NumElems; ++I) {
963 TYPE_SWITCH(ElemT,
964 { Values.push_back(Ptr.elem<T>(I).toAPValue(ASTCtx)); });
965 }
966
967 R = APValue(Values.data(), MT->getNumRows(), MT->getNumColumns());
968 return true;
969 }
970
971 llvm_unreachable("invalid value to return");
972 };
973
974 // Can't return functions as rvalues.
975 if (ResultType->isFunctionType())
976 return std::nullopt;
977
978 // Invalid to read from.
979 if (isDummy() || !isLive() || isPastEnd() ||
981 return std::nullopt;
982
983 // We can return these as rvalues, but we can't deref() them.
984 if (isZero() || isIntegralPointer())
985 return toAPValue(ASTCtx);
986
987 // Just load primitive types.
988 if (OptPrimType T = Ctx.classify(ResultType)) {
989 if (!canDeref(*T))
990 return std::nullopt;
991 TYPE_SWITCH(*T, return this->deref<T>().toAPValue(ASTCtx));
992 }
993
994 // Return the composite type.
996 if (!Composite(ResultType, *this, Result))
997 return std::nullopt;
998 return Result;
999}
1000
1001std::optional<IntPointer> IntPointer::atOffset(const ASTContext &ASTCtx,
1002 unsigned Offset) const {
1003 if (!this->Desc)
1004 return *this;
1005 const Record *R = this->Desc->ElemRecord;
1006 if (!R)
1007 return *this;
1008
1009 const Record::Field *F = nullptr;
1010 for (auto &It : R->fields()) {
1011 if (It.Offset == Offset) {
1012 F = &It;
1013 break;
1014 }
1015 }
1016 if (!F)
1017 return *this;
1018
1019 const FieldDecl *FD = F->Decl;
1020 if (FD->getParent()->isInvalidDecl())
1021 return std::nullopt;
1022
1023 const ASTRecordLayout &Layout = ASTCtx.getASTRecordLayout(FD->getParent());
1024 unsigned FieldIndex = FD->getFieldIndex();
1025 uint64_t FieldOffset =
1026 ASTCtx.toCharUnitsFromBits(Layout.getFieldOffset(FieldIndex))
1027 .getQuantity();
1028 return IntPointer{F->Desc, this->Value + FieldOffset};
1029}
1030
1032 unsigned BaseOffset) const {
1033 if (!Desc) {
1034 assert(Value == 0);
1035 return *this;
1036 }
1037 const Record *R = Desc->ElemRecord;
1038 const Descriptor *BaseDesc = nullptr;
1039
1040 // This iterates over bases and checks for the proper offset. That's
1041 // potentially slow but this case really shouldn't happen a lot.
1042 for (const Record::Base &B : R->bases()) {
1043 if (B.Offset == BaseOffset) {
1044 BaseDesc = B.Desc;
1045 break;
1046 }
1047 }
1048 assert(BaseDesc);
1049
1050 // Adjust the offset value based on the information from the record layout.
1051 const ASTRecordLayout &Layout = ASTCtx.getASTRecordLayout(R->getDecl());
1052 CharUnits BaseLayoutOffset =
1053 Layout.getBaseClassOffset(cast<CXXRecordDecl>(BaseDesc->asDecl()));
1054
1055 return {BaseDesc, Value + BaseLayoutOffset.getQuantity()};
1056}
Defines the clang::Expr interface and subclasses for C++ expressions.
#define INT_TYPE_SWITCH(Expr, B)
Definition PrimType.h:244
#define TYPE_SWITCH(Expr, B)
Definition PrimType.h:223
static uint64_t getFieldOffset(const ASTContext &C, const FieldDecl *FD)
static LValueBase getTypeInfo(TypeInfoLValue LV, QualType TypeInfo)
Definition APValue.cpp:55
static LValueBase getDynamicAlloc(DynamicAllocLValue LV, QualType Type)
Definition APValue.cpp:47
A non-discriminated union of a base, field, or array index.
Definition APValue.h:208
static LValuePathEntry ArrayIndex(uint64_t Index)
Definition APValue.h:216
APValue - This class implements a discriminated union of [uninitialized] [APSInt] [APFloat],...
Definition APValue.h:122
std::string getAsString(const ASTContext &Ctx, QualType Ty) const
Definition APValue.cpp:988
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition ASTContext.h:229
const ASTRecordLayout & getASTRecordLayout(const RecordDecl *D) const
Get or compute information about the layout of the specified record (struct/union/class) D,...
CharUnits getTypeSizeInChars(QualType T) const
Return the size of the specified (complete) type T, in characters.
CharUnits toCharUnitsFromBits(int64_t BitSize) const
Convert a size in bits to a size in characters.
CanQualType getCanonicalTagType(const TagDecl *TD) const
ASTRecordLayout - This class contains layout information for one RecordDecl, which is a struct/union/...
uint64_t getFieldOffset(unsigned FieldNo) const
getFieldOffset - Get the offset of the given field index, in bits.
CharUnits getBaseClassOffset(const CXXRecordDecl *Base) const
getBaseClassOffset - Get the offset, in chars, for the given base class.
CharUnits getVBaseClassOffset(const CXXRecordDecl *VBase) const
getVBaseClassOffset - Get the offset, in chars, for the given base class.
CharUnits - This is an opaque type for sizes expressed in character units.
Definition CharUnits.h:38
QuantityType getQuantity() const
getQuantity - Get the raw integer representation of this quantity.
Definition CharUnits.h:185
static CharUnits fromQuantity(QuantityType Quantity)
fromQuantity - Construct a CharUnits quantity from a raw integer type.
Definition CharUnits.h:63
static CharUnits Zero()
Zero - Construct a CharUnits quantity of zero.
Definition CharUnits.h:53
Represents a concrete matrix type with constant number of rows and columns.
Definition TypeBase.h:4449
Decl()=delete
bool isInvalidDecl() const
Definition DeclBase.h:596
Symbolic representation of a dynamic allocation.
Definition APValue.h:65
This represents one expression.
Definition Expr.h:112
Represents a member of a struct/union/class.
Definition Decl.h:3182
unsigned getFieldIndex() const
Returns the index of this field within its record, as appropriate for passing to ASTRecordLayout::get...
Definition Decl.h:3267
const RecordDecl * getParent() const
Returns the parent of this field declaration, which is the struct in which this field is defined.
Definition Decl.h:3418
Represents a function declaration or definition.
Definition Decl.h:2018
A (possibly-)qualified type.
Definition TypeBase.h:937
bool isNull() const
Return true if this QualType doesn't point to a type yet.
Definition TypeBase.h:1004
RecordDecl * getDefinition() const
Returns the RecordDecl that actually defines this struct/union/class.
Definition Decl.h:4531
Symbolic representation of typeid(T) for some type T.
Definition APValue.h:44
RecordDecl * getAsRecordDecl() const
Retrieves the RecordDecl this type refers to.
Definition Type.h:41
QualType getPointeeType() const
If this is a pointer, ObjC object pointer, or block pointer, this returns the respective pointee.
Definition Type.cpp:789
bool isLValueReferenceType() const
Definition TypeBase.h:8710
bool isFunctionType() const
Definition TypeBase.h:8678
QualType getType() const
Definition Decl.h:723
Represents a GCC generic vector type.
Definition TypeBase.h:4237
unsigned getSize() const
Returns the size of the block.
Definition InterpBlock.h:87
const Descriptor * getDescriptor() const
Returns the block's descriptor.
Definition InterpBlock.h:73
Holds all information required to evaluate constexpr code in a module.
Definition Context.h:47
ASTContext & getASTContext() const
Returns the AST context.
Definition Context.h:101
OptPrimType classify(QualType T) const
Classifies a type.
Definition Context.cpp:424
bool canClassify(QualType T) const
Definition Context.h:123
If a Floating is constructed from Memory, it DOES NOT OWN THAT MEMORY.
Definition Floating.h:35
APFloat getAPFloat() const
Definition Floating.h:64
const BlockExpr * getExpr() const
Definition Function.h:137
const FunctionDecl * getDecl() const
Returns the original FunctionDecl.
Definition Function.h:134
static bool hasSameBase(const Pointer &A, const Pointer &B)
Checks if two pointers are comparable.
Definition Pointer.cpp:704
void deactivate() const
Deactivates an entire strurcutre.
Definition Pointer.cpp:700
bool isInitialized() const
Checks if an object was initialized.
Definition Pointer.cpp:459
bool pointsToLabel() const
Whether this points to a block created for an AddrLabelExpr.
Definition Pointer.cpp:755
bool isStatic() const
Checks if the storage is static.
Definition Pointer.h:505
bool isDynamic() const
Checks if the storage has been dynamically allocated.
Definition Pointer.h:520
bool inUnion() const
Definition Pointer.h:413
bool isZeroSizeArray() const
Checks if the pointer is pointing to a zero-size array.
Definition Pointer.h:669
bool isElementInitialized(unsigned Index) const
Like isInitialized(), but for primitive arrays.
Definition Pointer.cpp:481
FunctionPointer Fn
Definition Pointer.h:890
bool isDummy() const
Checks if the pointer points to a dummy value.
Definition Pointer.h:562
void print(llvm::raw_ostream &OS) const
Prints the pointer.
Definition Pointer.cpp:321
int64_t getIndex() const
Returns the index into an array.
Definition Pointer.h:627
bool isActive() const
Checks if the object is active.
Definition Pointer.h:551
void startLifetime() const
Start the lifetime of this pointer.
Definition Pointer.cpp:523
bool canDeref(PrimType T) const
Checks whether the pointer can be dereferenced to the given PrimType.
Definition Pointer.h:678
Pointer atField(unsigned Off) const
Creates a pointer to a field.
Definition Pointer.h:179
T & deref() const
Dereferences the pointer, if it's live.
Definition Pointer.h:687
unsigned getNumElems() const
Returns the number of elements.
Definition Pointer.h:611
Pointer getArray() const
Returns the parent array.
Definition Pointer.h:326
bool isUnknownSizeArray() const
Checks if the structure is an array of unknown size.
Definition Pointer.h:426
void activate() const
Activats a field.
Definition Pointer.cpp:640
static std::optional< std::pair< Pointer, Pointer > > computeSplitPoint(const Pointer &A, const Pointer &B)
Definition Pointer.cpp:765
const TypeidPointer & asTypeidPointer() const
Definition Pointer.h:474
bool isIntegralPointer() const
Definition Pointer.h:480
QualType getType() const
Returns the type of the innermost field.
Definition Pointer.h:346
bool isArrayElement() const
Checks if the pointer points to an array.
Definition Pointer.h:432
void initializeAllElements() const
Initialize all elements of a primitive array at once.
Definition Pointer.cpp:599
bool pointsToStringLiteral() const
Definition Pointer.cpp:744
std::optional< size_t > computeOffsetForComparison(const ASTContext &ASTCtx) const
Compute an integer that can be used to compare this pointer to another one.
Definition Pointer.cpp:368
bool isArrayRoot() const
Whether this array refers to an array, but not to the first element.
Definition Pointer.h:405
bool isLive() const
Checks if the pointer is live.
Definition Pointer.h:278
bool inArray() const
Checks if the innermost field is an array.
Definition Pointer.h:408
bool isElementAlive(unsigned Index) const
Definition Pointer.cpp:510
bool pointsToLiteral() const
Whether this points to a block that's been created for a "literal lvalue", i.e.
Definition Pointer.cpp:733
bool allElementsAlive() const
Definition Pointer.cpp:623
Pointer getBase() const
Returns a pointer to the object of which this pointer is a field.
Definition Pointer.h:317
void setLifeState(Lifetime L) const
Definition Pointer.cpp:527
bool isTypeidPointer() const
Definition Pointer.h:482
std::string toDiagnosticString(const ASTContext &Ctx) const
Converts the pointer to a string usable in diagnostics.
Definition Pointer.cpp:446
bool isZero() const
Checks if the pointer is null.
Definition Pointer.h:264
Pointer & operator=(const Pointer &P)
Definition Pointer.cpp:95
bool isConstexprUnknown() const
Definition Pointer.h:719
const IntPointer & asIntPointer() const
Definition Pointer.h:466
bool isRoot() const
Pointer points directly to a block.
Definition Pointer.h:448
const Descriptor * getDeclDesc() const
Accessor for information about the declaration site.
Definition Pointer.h:292
static bool pointToSameBlock(const Pointer &A, const Pointer &B)
Checks if both given pointers point to the same block.
Definition Pointer.cpp:722
APValue toAPValue(const ASTContext &ASTCtx) const
Converts the pointer to an APValue.
Definition Pointer.cpp:173
bool isOnePastEnd() const
Checks if the index is one past end.
Definition Pointer.h:644
static bool hasSameArray(const Pointer &A, const Pointer &B)
Checks if two pointers can be subtracted.
Definition Pointer.cpp:728
uint64_t getIntegerRepresentation() const
Definition Pointer.h:149
bool isPastEnd() const
Checks if the pointer points past the end of the object.
Definition Pointer.h:658
const FieldDecl * getField() const
Returns the field information.
Definition Pointer.h:492
Pointer expand() const
Expands a pointer to the containing array, undoing narrowing.
Definition Pointer.h:229
friend class Block
Definition Pointer.h:850
bool isElementPastEnd() const
Checks if the pointer is an out-of-bounds element pointer.
Definition Pointer.h:666
bool isBlockPointer() const
Definition Pointer.h:479
BlockPointer BS
Definition Pointer.h:889
TypeidPointer Typeid
Definition Pointer.h:891
std::optional< APValue > toRValue(const Context &Ctx, QualType ResultType) const
Converts the pointer to an APValue that is an rvalue.
Definition Pointer.cpp:806
const FunctionPointer & asFunctionPointer() const
Definition Pointer.h:470
bool allElementsInitialized() const
Definition Pointer.cpp:606
const Block * block() const
Definition Pointer.h:617
bool isFunctionPointer() const
Definition Pointer.h:481
void endLifetime() const
Ends the lifetime of the pointer.
Definition Pointer.cpp:525
Pointer getDeclPtr() const
Definition Pointer.h:366
const Descriptor * getFieldDesc() const
Accessors for information about the innermost field.
Definition Pointer.h:336
bool isVirtualBaseClass() const
Definition Pointer.h:558
bool isBaseClass() const
Checks if a structure is a base class.
Definition Pointer.h:557
size_t elemSize() const
Returns the element size of the innermost field.
Definition Pointer.h:369
Lifetime getLifetime() const
Definition Pointer.h:761
const BlockPointer & asBlockPointer() const
Definition Pointer.h:462
void initialize() const
Initializes a field.
Definition Pointer.cpp:551
bool isField() const
Checks if the item is a field in an object.
Definition Pointer.h:284
void initializeElement(unsigned Index) const
Initialized the given element of a primitive array.
Definition Pointer.cpp:578
const Record * getRecord() const
Returns the record descriptor of a class.
Definition Pointer.h:485
Structure/Class descriptor.
Definition Record.h:25
const RecordDecl * getDecl() const
Returns the underlying declaration.
Definition Record.h:65
bool isUnion() const
Checks if the record is a union.
Definition Record.h:69
const Field * getField(unsigned I) const
Definition Record.h:95
unsigned getNumBases() const
Definition Record.h:106
const Base * getBase(unsigned I) const
Definition Record.h:107
const Base * getVirtualBase(unsigned I) const
Definition Record.h:122
unsigned getNumFields() const
Definition Record.h:94
unsigned getNumVirtualBases() const
Definition Record.h:121
llvm::iterator_range< const_field_iter > fields() const
Definition Record.h:90
constexpr bool isIntegerOrBoolType(PrimType T)
Definition PrimType.h:52
PrimType
Enumeration of the primitive types of the VM.
Definition PrimType.h:34
The JSON file list parser is used to communicate input to InstallAPI.
bool isa(CodeGen::Address addr)
Definition Address.h:330
@ Result
The result type of a method or function.
Definition TypeBase.h:905
U cast(CodeGen::Address addr)
Definition Address.h:327
int const char * function
Definition c++config.h:31
__UINTPTR_TYPE__ uintptr_t
An unsigned integer type with the property that any valid pointer to void can be converted to this ty...
unsigned Base
Start of the current subfield.
Definition Pointer.h:40
Block * Pointee
The block the pointer is pointing to.
Definition Pointer.h:38
Describes a memory block created by an allocation site.
Definition Descriptor.h:123
unsigned getNumElems() const
Returns the number of elements stored in the block.
Definition Descriptor.h:260
bool isPrimitive() const
Checks if the descriptor is of a primitive.
Definition Descriptor.h:274
QualType getElemQualType() const
const ValueDecl * asValueDecl() const
Definition Descriptor.h:216
QualType getType() const
const Decl * asDecl() const
Definition Descriptor.h:212
unsigned getMetadataSize() const
Returns the size of the metadata.
Definition Descriptor.h:257
QualType getDataType(const ASTContext &Ctx) const
const bool IsArray
Flag indicating if the block is an array.
Definition Descriptor.h:169
bool isPrimitiveArray() const
Checks if the descriptor is of an array of primitives.
Definition Descriptor.h:265
const FieldDecl * asFieldDecl() const
Definition Descriptor.h:224
PrimType getPrimType() const
Definition Descriptor.h:242
const Expr * asExpr() const
Definition Descriptor.h:213
const Function * Func
Definition Pointer.h:57
Descriptor used for global variables.
Definition Descriptor.h:50
A pointer-sized struct we use to allocate into data storage.
Definition InitMap.h:79
bool hasInitMap() const
Definition InitMap.h:88
bool allInitialized() const
Are all elements in the array already initialized?
Definition InitMap.h:92
void setInitMap(const InitMap *IM)
Definition InitMap.h:94
Bitfield tracking the initialisation status of elements of primitive arrays.
Definition InitMap.h:22
void startElementLifetime(unsigned I)
Definition InitMap.cpp:32
void endElementLifetime(unsigned I)
Definition InitMap.cpp:45
bool allElementsAlive() const
Definition InitMap.h:58
bool isElementInitialized(unsigned I) const
Checks if an element was initialized.
Definition InitMap.cpp:23
bool initializeElement(unsigned I)
Initializes an element. Returns true when object if fully initialized.
Definition InitMap.cpp:13
bool isElementAlive(unsigned I) const
Definition InitMap.h:52
Inline descriptor embedded in structures and arrays.
Definition Descriptor.h:68
unsigned IsActive
Flag indicating if the field is the active member of a union.
Definition Descriptor.h:90
unsigned Offset
Offset inside the structure/array.
Definition Descriptor.h:70
std::optional< IntPointer > atOffset(const ASTContext &ASTCtx, unsigned Offset) const
Definition Pointer.cpp:1001
IntPointer baseCast(const ASTContext &ASTCtx, unsigned BaseOffset) const
Definition Pointer.cpp:1031
const Descriptor * Desc
Definition Pointer.h:48
const Type * TypeInfoType
Definition Pointer.h:62