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