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