xref: /freebsd/contrib/llvm-project/clang/lib/AST/ByteCode/Pointer.h (revision 700637cbb5e582861067a11aaca4d053546871d2)
1 //===--- Pointer.h - 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 // Defines the classes responsible for pointer tracking.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #ifndef LLVM_CLANG_AST_INTERP_POINTER_H
14 #define LLVM_CLANG_AST_INTERP_POINTER_H
15 
16 #include "Descriptor.h"
17 #include "FunctionPointer.h"
18 #include "InterpBlock.h"
19 #include "clang/AST/ComparisonCategories.h"
20 #include "clang/AST/Decl.h"
21 #include "clang/AST/DeclCXX.h"
22 #include "clang/AST/Expr.h"
23 #include "llvm/Support/raw_ostream.h"
24 
25 namespace clang {
26 namespace interp {
27 class Block;
28 class DeadBlock;
29 class Pointer;
30 class Context;
31 template <unsigned A, bool B> class Integral;
32 enum PrimType : unsigned;
33 
34 class Pointer;
35 inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Pointer &P);
36 
37 struct BlockPointer {
38   /// The block the pointer is pointing to.
39   Block *Pointee;
40   /// Start of the current subfield.
41   unsigned Base;
42 };
43 
44 struct IntPointer {
45   const Descriptor *Desc;
46   uint64_t Value;
47 
48   IntPointer atOffset(const ASTContext &ASTCtx, unsigned Offset) const;
49   IntPointer baseCast(const ASTContext &ASTCtx, unsigned BaseOffset) const;
50 };
51 
52 struct TypeidPointer {
53   const Type *TypePtr;
54   const Type *TypeInfoType;
55 };
56 
57 enum class Storage { Block, Int, Fn, Typeid };
58 
59 /// A pointer to a memory block, live or dead.
60 ///
61 /// This object can be allocated into interpreter stack frames. If pointing to
62 /// a live block, it is a link in the chain of pointers pointing to the block.
63 ///
64 /// In the simplest form, a Pointer has a Block* (the pointee) and both Base
65 /// and Offset are 0, which means it will point to raw data.
66 ///
67 /// The Base field is used to access metadata about the data. For primitive
68 /// arrays, the Base is followed by an InitMap. In a variety of cases, the
69 /// Base is preceded by an InlineDescriptor, which is used to track the
70 /// initialization state, among other things.
71 ///
72 /// The Offset field is used to access the actual data. In other words, the
73 /// data the pointer decribes can be found at
74 /// Pointee->rawData() + Pointer.Offset.
75 ///
76 ///
77 /// Pointee                      Offset
78 /// │                              │
79 /// │                              │
80 /// ▼                              ▼
81 /// ┌───────┬────────────┬─────────┬────────────────────────────┐
82 /// │ Block │ InlineDesc │ InitMap │ Actual Data                │
83 /// └───────┴────────────┴─────────┴────────────────────────────┘
84 ///                      ▲
85 ///                      │
86 ///                      │
87 ///                     Base
88 class Pointer {
89 private:
90   static constexpr unsigned PastEndMark = ~0u;
91   static constexpr unsigned RootPtrMark = ~0u;
92 
93 public:
Pointer()94   Pointer() {
95     StorageKind = Storage::Int;
96     PointeeStorage.Int.Value = 0;
97     PointeeStorage.Int.Desc = nullptr;
98   }
Pointer(IntPointer && IntPtr)99   Pointer(IntPointer &&IntPtr) : StorageKind(Storage::Int) {
100     PointeeStorage.Int = std::move(IntPtr);
101   }
102   Pointer(Block *B);
103   Pointer(Block *B, uint64_t BaseAndOffset);
104   Pointer(const Pointer &P);
105   Pointer(Pointer &&P);
106   Pointer(uint64_t Address, const Descriptor *Desc, uint64_t Offset = 0)
Offset(Offset)107       : Offset(Offset), StorageKind(Storage::Int) {
108     PointeeStorage.Int.Value = Address;
109     PointeeStorage.Int.Desc = Desc;
110   }
111   Pointer(const Function *F, uint64_t Offset = 0)
Offset(Offset)112       : Offset(Offset), StorageKind(Storage::Fn) {
113     PointeeStorage.Fn = FunctionPointer(F);
114   }
115   Pointer(const Type *TypePtr, const Type *TypeInfoType, uint64_t Offset = 0)
Offset(Offset)116       : Offset(Offset), StorageKind(Storage::Typeid) {
117     PointeeStorage.Typeid.TypePtr = TypePtr;
118     PointeeStorage.Typeid.TypeInfoType = TypeInfoType;
119   }
120   Pointer(Block *Pointee, unsigned Base, uint64_t Offset);
121   ~Pointer();
122 
123   void operator=(const Pointer &P);
124   void operator=(Pointer &&P);
125 
126   /// Equality operators are just for tests.
127   bool operator==(const Pointer &P) const {
128     if (P.StorageKind != StorageKind)
129       return false;
130     if (isIntegralPointer())
131       return P.asIntPointer().Value == asIntPointer().Value &&
132              P.asIntPointer().Desc == asIntPointer().Desc && P.Offset == Offset;
133 
134     if (isFunctionPointer())
135       return P.asFunctionPointer().getFunction() ==
136                  asFunctionPointer().getFunction() &&
137              P.Offset == Offset;
138 
139     assert(isBlockPointer());
140     return P.asBlockPointer().Pointee == asBlockPointer().Pointee &&
141            P.asBlockPointer().Base == asBlockPointer().Base &&
142            P.Offset == Offset;
143   }
144 
145   bool operator!=(const Pointer &P) const { return !(P == *this); }
146 
147   /// Converts the pointer to an APValue.
148   APValue toAPValue(const ASTContext &ASTCtx) const;
149 
150   /// Converts the pointer to a string usable in diagnostics.
151   std::string toDiagnosticString(const ASTContext &Ctx) const;
152 
getIntegerRepresentation()153   uint64_t getIntegerRepresentation() const {
154     if (isIntegralPointer())
155       return asIntPointer().Value + (Offset * elemSize());
156     if (isFunctionPointer())
157       return asFunctionPointer().getIntegerRepresentation() + Offset;
158     return reinterpret_cast<uint64_t>(asBlockPointer().Pointee) + Offset;
159   }
160 
161   /// Converts the pointer to an APValue that is an rvalue.
162   std::optional<APValue> toRValue(const Context &Ctx,
163                                   QualType ResultType) const;
164 
165   /// Offsets a pointer inside an array.
atIndex(uint64_t Idx)166   [[nodiscard]] Pointer atIndex(uint64_t Idx) const {
167     if (isIntegralPointer())
168       return Pointer(asIntPointer().Value, asIntPointer().Desc, Idx);
169     if (isFunctionPointer())
170       return Pointer(asFunctionPointer().getFunction(), Idx);
171 
172     if (asBlockPointer().Base == RootPtrMark)
173       return Pointer(asBlockPointer().Pointee, RootPtrMark,
174                      getDeclDesc()->getSize());
175     uint64_t Off = Idx * elemSize();
176     if (getFieldDesc()->ElemDesc)
177       Off += sizeof(InlineDescriptor);
178     else
179       Off += sizeof(InitMapPtr);
180     return Pointer(asBlockPointer().Pointee, asBlockPointer().Base,
181                    asBlockPointer().Base + Off);
182   }
183 
184   /// Creates a pointer to a field.
atField(unsigned Off)185   [[nodiscard]] Pointer atField(unsigned Off) const {
186     assert(isBlockPointer());
187     unsigned Field = Offset + Off;
188     return Pointer(asBlockPointer().Pointee, Field, Field);
189   }
190 
191   /// Subtract the given offset from the current Base and Offset
192   /// of the pointer.
atFieldSub(unsigned Off)193   [[nodiscard]] Pointer atFieldSub(unsigned Off) const {
194     assert(Offset >= Off);
195     unsigned O = Offset - Off;
196     return Pointer(asBlockPointer().Pointee, O, O);
197   }
198 
199   /// Restricts the scope of an array element pointer.
narrow()200   [[nodiscard]] Pointer narrow() const {
201     if (!isBlockPointer())
202       return *this;
203     assert(isBlockPointer());
204     // Null pointers cannot be narrowed.
205     if (isZero() || isUnknownSizeArray())
206       return *this;
207 
208     unsigned Base = asBlockPointer().Base;
209     // Pointer to an array of base types - enter block.
210     if (Base == RootPtrMark)
211       return Pointer(asBlockPointer().Pointee, sizeof(InlineDescriptor),
212                      Offset == 0 ? Offset : PastEndMark);
213 
214     // Pointer is one past end - magic offset marks that.
215     if (isOnePastEnd())
216       return Pointer(asBlockPointer().Pointee, Base, PastEndMark);
217 
218     if (Offset != Base) {
219       // If we're pointing to a primitive array element, there's nothing to do.
220       if (inPrimitiveArray())
221         return *this;
222       // Pointer is to a composite array element - enter it.
223       if (Offset != Base)
224         return Pointer(asBlockPointer().Pointee, Offset, Offset);
225     }
226 
227     // Otherwise, we're pointing to a non-array element or
228     // are already narrowed to a composite array element. Nothing to do.
229     return *this;
230   }
231 
232   /// Expands a pointer to the containing array, undoing narrowing.
expand()233   [[nodiscard]] Pointer expand() const {
234     assert(isBlockPointer());
235     Block *Pointee = asBlockPointer().Pointee;
236 
237     if (isElementPastEnd()) {
238       // Revert to an outer one-past-end pointer.
239       unsigned Adjust;
240       if (inPrimitiveArray())
241         Adjust = sizeof(InitMapPtr);
242       else
243         Adjust = sizeof(InlineDescriptor);
244       return Pointer(Pointee, asBlockPointer().Base,
245                      asBlockPointer().Base + getSize() + Adjust);
246     }
247 
248     // Do not step out of array elements.
249     if (asBlockPointer().Base != Offset)
250       return *this;
251 
252     if (isRoot())
253       return Pointer(Pointee, asBlockPointer().Base, asBlockPointer().Base);
254 
255     // Step into the containing array, if inside one.
256     unsigned Next = asBlockPointer().Base - getInlineDesc()->Offset;
257     const Descriptor *Desc =
258         (Next == Pointee->getDescriptor()->getMetadataSize())
259             ? getDeclDesc()
260             : getDescriptor(Next)->Desc;
261     if (!Desc->IsArray)
262       return *this;
263     return Pointer(Pointee, Next, Offset);
264   }
265 
266   /// Checks if the pointer is null.
isZero()267   bool isZero() const {
268     if (isBlockPointer())
269       return asBlockPointer().Pointee == nullptr;
270     if (isFunctionPointer())
271       return asFunctionPointer().isZero();
272     if (isTypeidPointer())
273       return false;
274     assert(isIntegralPointer());
275     return asIntPointer().Value == 0 && Offset == 0;
276   }
277   /// Checks if the pointer is live.
isLive()278   bool isLive() const {
279     if (!isBlockPointer())
280       return true;
281     return asBlockPointer().Pointee && !asBlockPointer().Pointee->IsDead;
282   }
283   /// Checks if the item is a field in an object.
isField()284   bool isField() const {
285     if (!isBlockPointer())
286       return false;
287 
288     return !isRoot() && getFieldDesc()->asDecl();
289   }
290 
291   /// Accessor for information about the declaration site.
getDeclDesc()292   const Descriptor *getDeclDesc() const {
293     if (isIntegralPointer())
294       return asIntPointer().Desc;
295     if (isFunctionPointer() || isTypeidPointer())
296       return nullptr;
297 
298     assert(isBlockPointer());
299     assert(asBlockPointer().Pointee);
300     return asBlockPointer().Pointee->Desc;
301   }
getDeclLoc()302   SourceLocation getDeclLoc() const { return getDeclDesc()->getLocation(); }
303 
304   /// Returns the expression or declaration the pointer has been created for.
getSource()305   DeclTy getSource() const {
306     if (isBlockPointer())
307       return getDeclDesc()->getSource();
308     if (isFunctionPointer()) {
309       const Function *F = asFunctionPointer().getFunction();
310       return F ? F->getDecl() : DeclTy();
311     }
312     assert(isIntegralPointer());
313     return asIntPointer().Desc ? asIntPointer().Desc->getSource() : DeclTy();
314   }
315 
316   /// Returns a pointer to the object of which this pointer is a field.
getBase()317   [[nodiscard]] Pointer getBase() const {
318     if (asBlockPointer().Base == RootPtrMark) {
319       assert(Offset == PastEndMark && "cannot get base of a block");
320       return Pointer(asBlockPointer().Pointee, asBlockPointer().Base, 0);
321     }
322     unsigned NewBase = asBlockPointer().Base - getInlineDesc()->Offset;
323     return Pointer(asBlockPointer().Pointee, NewBase, NewBase);
324   }
325   /// Returns the parent array.
getArray()326   [[nodiscard]] Pointer getArray() const {
327     if (asBlockPointer().Base == RootPtrMark) {
328       assert(Offset != 0 && Offset != PastEndMark && "not an array element");
329       return Pointer(asBlockPointer().Pointee, asBlockPointer().Base, 0);
330     }
331     assert(Offset != asBlockPointer().Base && "not an array element");
332     return Pointer(asBlockPointer().Pointee, asBlockPointer().Base,
333                    asBlockPointer().Base);
334   }
335 
336   /// Accessors for information about the innermost field.
getFieldDesc()337   const Descriptor *getFieldDesc() const {
338     if (isIntegralPointer())
339       return asIntPointer().Desc;
340 
341     if (isRoot())
342       return getDeclDesc();
343     return getInlineDesc()->Desc;
344   }
345 
346   /// Returns the type of the innermost field.
getType()347   QualType getType() const {
348     if (isTypeidPointer())
349       return QualType(PointeeStorage.Typeid.TypeInfoType, 0);
350 
351     if (inPrimitiveArray() && Offset != asBlockPointer().Base) {
352       // Unfortunately, complex and vector types are not array types in clang,
353       // but they are for us.
354       if (const auto *AT = getFieldDesc()->getType()->getAsArrayTypeUnsafe())
355         return AT->getElementType();
356       if (const auto *CT = getFieldDesc()->getType()->getAs<ComplexType>())
357         return CT->getElementType();
358       if (const auto *CT = getFieldDesc()->getType()->getAs<VectorType>())
359         return CT->getElementType();
360     }
361     return getFieldDesc()->getType();
362   }
363 
getDeclPtr()364   [[nodiscard]] Pointer getDeclPtr() const {
365     return Pointer(asBlockPointer().Pointee);
366   }
367 
368   /// Returns the element size of the innermost field.
elemSize()369   size_t elemSize() const {
370     if (isIntegralPointer()) {
371       if (!asIntPointer().Desc)
372         return 1;
373       return asIntPointer().Desc->getElemSize();
374     }
375 
376     if (asBlockPointer().Base == RootPtrMark)
377       return getDeclDesc()->getSize();
378     return getFieldDesc()->getElemSize();
379   }
380   /// Returns the total size of the innermost field.
getSize()381   size_t getSize() const {
382     assert(isBlockPointer());
383     return getFieldDesc()->getSize();
384   }
385 
386   /// Returns the offset into an array.
getOffset()387   unsigned getOffset() const {
388     assert(Offset != PastEndMark && "invalid offset");
389     assert(isBlockPointer());
390     if (asBlockPointer().Base == RootPtrMark)
391       return Offset;
392 
393     unsigned Adjust = 0;
394     if (Offset != asBlockPointer().Base) {
395       if (getFieldDesc()->ElemDesc)
396         Adjust = sizeof(InlineDescriptor);
397       else
398         Adjust = sizeof(InitMapPtr);
399     }
400     return Offset - asBlockPointer().Base - Adjust;
401   }
402 
403   /// Whether this array refers to an array, but not
404   /// to the first element.
isArrayRoot()405   bool isArrayRoot() const {
406     return inArray() && Offset == asBlockPointer().Base;
407   }
408 
409   /// Checks if the innermost field is an array.
inArray()410   bool inArray() const {
411     if (isBlockPointer())
412       return getFieldDesc()->IsArray;
413     return false;
414   }
inUnion()415   bool inUnion() const {
416     if (isBlockPointer() && asBlockPointer().Base >= sizeof(InlineDescriptor))
417       return getInlineDesc()->InUnion;
418     return false;
419   };
420 
421   /// Checks if the structure is a primitive array.
inPrimitiveArray()422   bool inPrimitiveArray() const {
423     if (isBlockPointer())
424       return getFieldDesc()->isPrimitiveArray();
425     return false;
426   }
427   /// Checks if the structure is an array of unknown size.
isUnknownSizeArray()428   bool isUnknownSizeArray() const {
429     if (!isBlockPointer())
430       return false;
431     return getFieldDesc()->isUnknownSizeArray();
432   }
433   /// Checks if the pointer points to an array.
isArrayElement()434   bool isArrayElement() const {
435     if (!isBlockPointer())
436       return false;
437 
438     const BlockPointer &BP = asBlockPointer();
439     if (inArray() && BP.Base != Offset)
440       return true;
441 
442     // Might be a narrow()'ed element in a composite array.
443     // Check the inline descriptor.
444     if (BP.Base >= sizeof(InlineDescriptor) && getInlineDesc()->IsArrayElement)
445       return true;
446 
447     return false;
448   }
449   /// Pointer points directly to a block.
isRoot()450   bool isRoot() const {
451     if (isZero() || !isBlockPointer())
452       return true;
453     return (asBlockPointer().Base ==
454                 asBlockPointer().Pointee->getDescriptor()->getMetadataSize() ||
455             asBlockPointer().Base == 0);
456   }
457   /// If this pointer has an InlineDescriptor we can use to initialize.
canBeInitialized()458   bool canBeInitialized() const {
459     if (!isBlockPointer())
460       return false;
461 
462     return asBlockPointer().Pointee && asBlockPointer().Base > 0;
463   }
464 
asBlockPointer()465   [[nodiscard]] const BlockPointer &asBlockPointer() const {
466     assert(isBlockPointer());
467     return PointeeStorage.BS;
468   }
asIntPointer()469   [[nodiscard]] const IntPointer &asIntPointer() const {
470     assert(isIntegralPointer());
471     return PointeeStorage.Int;
472   }
asFunctionPointer()473   [[nodiscard]] const FunctionPointer &asFunctionPointer() const {
474     assert(isFunctionPointer());
475     return PointeeStorage.Fn;
476   }
asTypeidPointer()477   [[nodiscard]] const TypeidPointer &asTypeidPointer() const {
478     assert(isTypeidPointer());
479     return PointeeStorage.Typeid;
480   }
481 
isBlockPointer()482   bool isBlockPointer() const { return StorageKind == Storage::Block; }
isIntegralPointer()483   bool isIntegralPointer() const { return StorageKind == Storage::Int; }
isFunctionPointer()484   bool isFunctionPointer() const { return StorageKind == Storage::Fn; }
isTypeidPointer()485   bool isTypeidPointer() const { return StorageKind == Storage::Typeid; }
486 
487   /// Returns the record descriptor of a class.
getRecord()488   const Record *getRecord() const { return getFieldDesc()->ElemRecord; }
489   /// Returns the element record type, if this is a non-primive array.
getElemRecord()490   const Record *getElemRecord() const {
491     const Descriptor *ElemDesc = getFieldDesc()->ElemDesc;
492     return ElemDesc ? ElemDesc->ElemRecord : nullptr;
493   }
494   /// Returns the field information.
getField()495   const FieldDecl *getField() const {
496     if (const Descriptor *FD = getFieldDesc())
497       return FD->asFieldDecl();
498     return nullptr;
499   }
500 
501   /// Checks if the storage is extern.
isExtern()502   bool isExtern() const {
503     if (isBlockPointer())
504       return asBlockPointer().Pointee && asBlockPointer().Pointee->isExtern();
505     return false;
506   }
507   /// Checks if the storage is static.
isStatic()508   bool isStatic() const {
509     if (!isBlockPointer())
510       return true;
511     assert(asBlockPointer().Pointee);
512     return asBlockPointer().Pointee->isStatic();
513   }
514   /// Checks if the storage is temporary.
isTemporary()515   bool isTemporary() const {
516     if (isBlockPointer()) {
517       assert(asBlockPointer().Pointee);
518       return asBlockPointer().Pointee->isTemporary();
519     }
520     return false;
521   }
522   /// Checks if the storage has been dynamically allocated.
isDynamic()523   bool isDynamic() const {
524     if (isBlockPointer()) {
525       assert(asBlockPointer().Pointee);
526       return asBlockPointer().Pointee->isDynamic();
527     }
528     return false;
529   }
530   /// Checks if the storage is a static temporary.
isStaticTemporary()531   bool isStaticTemporary() const { return isStatic() && isTemporary(); }
532 
533   /// Checks if the field is mutable.
isMutable()534   bool isMutable() const {
535     if (!isBlockPointer())
536       return false;
537     return !isRoot() && getInlineDesc()->IsFieldMutable;
538   }
539 
isWeak()540   bool isWeak() const {
541     if (isFunctionPointer())
542       return asFunctionPointer().isWeak();
543     if (!isBlockPointer())
544       return false;
545 
546     assert(isBlockPointer());
547     return asBlockPointer().Pointee->isWeak();
548   }
549   /// Checks if an object was initialized.
550   bool isInitialized() const;
551   /// Checks if the object is active.
isActive()552   bool isActive() const {
553     if (!isBlockPointer())
554       return true;
555     return isRoot() || getInlineDesc()->IsActive;
556   }
557   /// Checks if a structure is a base class.
isBaseClass()558   bool isBaseClass() const { return isField() && getInlineDesc()->IsBase; }
isVirtualBaseClass()559   bool isVirtualBaseClass() const {
560     return isField() && getInlineDesc()->IsVirtualBase;
561   }
562   /// Checks if the pointer points to a dummy value.
isDummy()563   bool isDummy() const {
564     if (!isBlockPointer())
565       return false;
566 
567     if (!asBlockPointer().Pointee)
568       return false;
569 
570     return getDeclDesc()->isDummy();
571   }
572 
573   /// Checks if an object or a subfield is mutable.
isConst()574   bool isConst() const {
575     if (isIntegralPointer())
576       return true;
577     return isRoot() ? getDeclDesc()->IsConst : getInlineDesc()->IsConst;
578   }
579 
580   /// Checks if an object or a subfield is volatile.
isVolatile()581   bool isVolatile() const {
582     if (!isBlockPointer())
583       return false;
584     return isRoot() ? getDeclDesc()->IsVolatile : getInlineDesc()->IsVolatile;
585   }
586 
587   /// Returns the declaration ID.
getDeclID()588   std::optional<unsigned> getDeclID() const {
589     if (isBlockPointer()) {
590       assert(asBlockPointer().Pointee);
591       return asBlockPointer().Pointee->getDeclID();
592     }
593     return std::nullopt;
594   }
595 
596   /// Returns the byte offset from the start.
getByteOffset()597   uint64_t getByteOffset() const {
598     if (isIntegralPointer())
599       return asIntPointer().Value + Offset;
600     if (isTypeidPointer())
601       return reinterpret_cast<uintptr_t>(asTypeidPointer().TypePtr) + Offset;
602     if (isOnePastEnd())
603       return PastEndMark;
604     return Offset;
605   }
606 
607   /// Returns the number of elements.
getNumElems()608   unsigned getNumElems() const {
609     if (!isBlockPointer())
610       return ~0u;
611     return getSize() / elemSize();
612   }
613 
block()614   const Block *block() const { return asBlockPointer().Pointee; }
615 
616   /// If backed by actual data (i.e. a block pointer), return
617   /// an address to that data.
getRawAddress()618   const std::byte *getRawAddress() const {
619     assert(isBlockPointer());
620     return asBlockPointer().Pointee->rawData() + Offset;
621   }
622 
623   /// Returns the index into an array.
getIndex()624   int64_t getIndex() const {
625     if (!isBlockPointer())
626       return getIntegerRepresentation();
627 
628     if (isZero())
629       return 0;
630 
631     // narrow()ed element in a composite array.
632     if (asBlockPointer().Base > sizeof(InlineDescriptor) &&
633         asBlockPointer().Base == Offset)
634       return 0;
635 
636     if (auto ElemSize = elemSize())
637       return getOffset() / ElemSize;
638     return 0;
639   }
640 
641   /// Checks if the index is one past end.
isOnePastEnd()642   bool isOnePastEnd() const {
643     if (!isBlockPointer())
644       return false;
645 
646     if (!asBlockPointer().Pointee)
647       return false;
648 
649     if (isUnknownSizeArray())
650       return false;
651 
652     return isPastEnd() || (getSize() == getOffset() && !isZeroSizeArray());
653   }
654 
655   /// Checks if the pointer points past the end of the object.
isPastEnd()656   bool isPastEnd() const {
657     if (isIntegralPointer())
658       return false;
659 
660     return !isZero() && Offset > PointeeStorage.BS.Pointee->getSize();
661   }
662 
663   /// Checks if the pointer is an out-of-bounds element pointer.
isElementPastEnd()664   bool isElementPastEnd() const { return Offset == PastEndMark; }
665 
666   /// Checks if the pointer is pointing to a zero-size array.
isZeroSizeArray()667   bool isZeroSizeArray() const {
668     if (isFunctionPointer())
669       return false;
670     if (const auto *Desc = getFieldDesc())
671       return Desc->isZeroSizeArray();
672     return false;
673   }
674 
675   /// Dereferences the pointer, if it's live.
deref()676   template <typename T> T &deref() const {
677     assert(isLive() && "Invalid pointer");
678     assert(isBlockPointer());
679     assert(asBlockPointer().Pointee);
680     assert(isDereferencable());
681     assert(Offset + sizeof(T) <=
682            asBlockPointer().Pointee->getDescriptor()->getAllocSize());
683 
684     if (isArrayRoot())
685       return *reinterpret_cast<T *>(asBlockPointer().Pointee->rawData() +
686                                     asBlockPointer().Base + sizeof(InitMapPtr));
687 
688     return *reinterpret_cast<T *>(asBlockPointer().Pointee->rawData() + Offset);
689   }
690 
691   /// Whether this block can be read from at all. This is only true for
692   /// block pointers that point to a valid location inside that block.
isDereferencable()693   bool isDereferencable() const {
694     if (!isBlockPointer())
695       return false;
696     if (isPastEnd())
697       return false;
698 
699     return true;
700   }
701 
702   /// Initializes a field.
703   void initialize() const;
704   /// Activats a field.
705   void activate() const;
706   /// Deactivates an entire strurcutre.
707   void deactivate() const;
708 
getLifetime()709   Lifetime getLifetime() const {
710     if (!isBlockPointer())
711       return Lifetime::Started;
712     if (asBlockPointer().Base < sizeof(InlineDescriptor))
713       return Lifetime::Started;
714     return getInlineDesc()->LifeState;
715   }
716 
endLifetime()717   void endLifetime() const {
718     if (!isBlockPointer())
719       return;
720     if (asBlockPointer().Base < sizeof(InlineDescriptor))
721       return;
722     getInlineDesc()->LifeState = Lifetime::Ended;
723   }
724 
startLifetime()725   void startLifetime() const {
726     if (!isBlockPointer())
727       return;
728     if (asBlockPointer().Base < sizeof(InlineDescriptor))
729       return;
730     getInlineDesc()->LifeState = Lifetime::Started;
731   }
732 
733   /// Compare two pointers.
compare(const Pointer & Other)734   ComparisonCategoryResult compare(const Pointer &Other) const {
735     if (!hasSameBase(*this, Other))
736       return ComparisonCategoryResult::Unordered;
737 
738     if (Offset < Other.Offset)
739       return ComparisonCategoryResult::Less;
740     else if (Offset > Other.Offset)
741       return ComparisonCategoryResult::Greater;
742 
743     return ComparisonCategoryResult::Equal;
744   }
745 
746   /// Checks if two pointers are comparable.
747   static bool hasSameBase(const Pointer &A, const Pointer &B);
748   /// Checks if two pointers can be subtracted.
749   static bool hasSameArray(const Pointer &A, const Pointer &B);
750   /// Checks if both given pointers point to the same block.
751   static bool pointToSameBlock(const Pointer &A, const Pointer &B);
752 
753   static std::optional<std::pair<Pointer, Pointer>>
754   computeSplitPoint(const Pointer &A, const Pointer &B);
755 
756   /// Whether this points to a block that's been created for a "literal lvalue",
757   /// i.e. a non-MaterializeTemporaryExpr Expr.
758   bool pointsToLiteral() const;
759   bool pointsToStringLiteral() const;
760 
761   /// Prints the pointer.
762   void print(llvm::raw_ostream &OS) const;
763 
764   /// Compute an integer that can be used to compare this pointer to
765   /// another one. This is usually NOT the same as the pointer offset
766   /// regarding the AST record layout.
767   size_t computeOffsetForComparison() const;
768 
769 private:
770   friend class Block;
771   friend class DeadBlock;
772   friend class MemberPointer;
773   friend class InterpState;
774   friend struct InitMap;
775   friend class DynamicAllocator;
776 
777   /// Returns the embedded descriptor preceding a field.
getInlineDesc()778   InlineDescriptor *getInlineDesc() const {
779     assert(isBlockPointer());
780     assert(asBlockPointer().Base != sizeof(GlobalInlineDescriptor));
781     assert(asBlockPointer().Base <= asBlockPointer().Pointee->getSize());
782     assert(asBlockPointer().Base >= sizeof(InlineDescriptor));
783     return getDescriptor(asBlockPointer().Base);
784   }
785 
786   /// Returns a descriptor at a given offset.
getDescriptor(unsigned Offset)787   InlineDescriptor *getDescriptor(unsigned Offset) const {
788     assert(Offset != 0 && "Not a nested pointer");
789     assert(isBlockPointer());
790     assert(!isZero());
791     return reinterpret_cast<InlineDescriptor *>(
792                asBlockPointer().Pointee->rawData() + Offset) -
793            1;
794   }
795 
796   /// Returns a reference to the InitMapPtr which stores the initialization map.
getInitMap()797   InitMapPtr &getInitMap() const {
798     assert(isBlockPointer());
799     assert(!isZero());
800     return *reinterpret_cast<InitMapPtr *>(asBlockPointer().Pointee->rawData() +
801                                            asBlockPointer().Base);
802   }
803 
804   /// Offset into the storage.
805   uint64_t Offset = 0;
806 
807   /// Previous link in the pointer chain.
808   Pointer *Prev = nullptr;
809   /// Next link in the pointer chain.
810   Pointer *Next = nullptr;
811 
812   Storage StorageKind = Storage::Int;
813   union {
814     BlockPointer BS;
815     IntPointer Int;
816     FunctionPointer Fn;
817     TypeidPointer Typeid;
818   } PointeeStorage;
819 };
820 
821 inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Pointer &P) {
822   P.print(OS);
823   return OS;
824 }
825 
826 } // namespace interp
827 } // namespace clang
828 
829 #endif
830