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/ADT/PointerUnion.h" 23a7dea167SDimitry Andric #include "llvm/Support/raw_ostream.h" 24a7dea167SDimitry Andric 25a7dea167SDimitry Andric namespace clang { 26a7dea167SDimitry Andric namespace interp { 27a7dea167SDimitry Andric class Block; 28a7dea167SDimitry Andric class DeadBlock; 29a7dea167SDimitry Andric class Pointer; 30a7dea167SDimitry Andric enum PrimType : unsigned; 31a7dea167SDimitry Andric 32a7dea167SDimitry Andric /// A pointer to a memory block, live or dead. 33a7dea167SDimitry Andric /// 34a7dea167SDimitry Andric /// This object can be allocated into interpreter stack frames. If pointing to 35a7dea167SDimitry Andric /// a live block, it is a link in the chain of pointers pointing to the block. 36*bdd1243dSDimitry Andric /// 37*bdd1243dSDimitry Andric /// In the simplest form, a Pointer has a Block* (the pointee) and both Base 38*bdd1243dSDimitry Andric /// and Offset are 0, which means it will point to raw data. 39*bdd1243dSDimitry Andric /// 40*bdd1243dSDimitry Andric /// The Base field is used to access metadata about the data. For primitive 41*bdd1243dSDimitry Andric /// arrays, the Base is followed by an InitMap. In a variety of cases, the 42*bdd1243dSDimitry Andric /// Base is preceded by an InlineDescriptor, which is used to track the 43*bdd1243dSDimitry Andric /// initialization state, among other things. 44*bdd1243dSDimitry Andric /// 45*bdd1243dSDimitry Andric /// The Offset field is used to access the actual data. In other words, the 46*bdd1243dSDimitry Andric /// data the pointer decribes can be found at 47*bdd1243dSDimitry Andric /// Pointee->rawData() + Pointer.Offset. 48*bdd1243dSDimitry Andric /// 49*bdd1243dSDimitry Andric /// 50*bdd1243dSDimitry Andric /// Pointee Offset 51*bdd1243dSDimitry Andric /// │ │ 52*bdd1243dSDimitry Andric /// │ │ 53*bdd1243dSDimitry Andric /// ▼ ▼ 54*bdd1243dSDimitry Andric /// ┌───────┬────────────┬─────────┬────────────────────────────┐ 55*bdd1243dSDimitry Andric /// │ Block │ InlineDesc │ InitMap │ Actual Data │ 56*bdd1243dSDimitry Andric /// └───────┴────────────┴─────────┴────────────────────────────┘ 57*bdd1243dSDimitry Andric /// ▲ 58*bdd1243dSDimitry Andric /// │ 59*bdd1243dSDimitry Andric /// │ 60*bdd1243dSDimitry Andric /// Base 61a7dea167SDimitry Andric class Pointer { 62a7dea167SDimitry Andric private: 63*bdd1243dSDimitry Andric static constexpr unsigned PastEndMark = ~0u; 64*bdd1243dSDimitry Andric static constexpr unsigned RootPtrMark = ~0u; 65a7dea167SDimitry Andric 66a7dea167SDimitry Andric public: 67a7dea167SDimitry Andric Pointer() {} 68a7dea167SDimitry Andric Pointer(Block *B); 69*bdd1243dSDimitry Andric Pointer(Block *B, unsigned BaseAndOffset); 70a7dea167SDimitry Andric Pointer(const Pointer &P); 71a7dea167SDimitry Andric Pointer(Pointer &&P); 72a7dea167SDimitry Andric ~Pointer(); 73a7dea167SDimitry Andric 74a7dea167SDimitry Andric void operator=(const Pointer &P); 75a7dea167SDimitry Andric void operator=(Pointer &&P); 76a7dea167SDimitry Andric 77a7dea167SDimitry Andric /// Converts the pointer to an APValue. 78a7dea167SDimitry Andric APValue toAPValue() const; 79a7dea167SDimitry Andric 80a7dea167SDimitry Andric /// Offsets a pointer inside an array. 81a7dea167SDimitry Andric Pointer atIndex(unsigned Idx) const { 82a7dea167SDimitry Andric if (Base == RootPtrMark) 83a7dea167SDimitry Andric return Pointer(Pointee, RootPtrMark, getDeclDesc()->getSize()); 84a7dea167SDimitry Andric unsigned Off = Idx * elemSize(); 85a7dea167SDimitry Andric if (getFieldDesc()->ElemDesc) 86a7dea167SDimitry Andric Off += sizeof(InlineDescriptor); 87a7dea167SDimitry Andric else 88a7dea167SDimitry Andric Off += sizeof(InitMap *); 89a7dea167SDimitry Andric return Pointer(Pointee, Base, Base + Off); 90a7dea167SDimitry Andric } 91a7dea167SDimitry Andric 92a7dea167SDimitry Andric /// Creates a pointer to a field. 93a7dea167SDimitry Andric Pointer atField(unsigned Off) const { 94a7dea167SDimitry Andric unsigned Field = Offset + Off; 95a7dea167SDimitry Andric return Pointer(Pointee, Field, Field); 96a7dea167SDimitry Andric } 97a7dea167SDimitry Andric 98a7dea167SDimitry Andric /// Restricts the scope of an array element pointer. 99a7dea167SDimitry Andric Pointer narrow() const { 100a7dea167SDimitry Andric // Null pointers cannot be narrowed. 101a7dea167SDimitry Andric if (isZero() || isUnknownSizeArray()) 102a7dea167SDimitry Andric return *this; 103a7dea167SDimitry Andric 104a7dea167SDimitry Andric // Pointer to an array of base types - enter block. 105a7dea167SDimitry Andric if (Base == RootPtrMark) 106a7dea167SDimitry Andric return Pointer(Pointee, 0, Offset == 0 ? Offset : PastEndMark); 107a7dea167SDimitry Andric 108a7dea167SDimitry Andric // Pointer is one past end - magic offset marks that. 109a7dea167SDimitry Andric if (isOnePastEnd()) 110a7dea167SDimitry Andric return Pointer(Pointee, Base, PastEndMark); 111a7dea167SDimitry Andric 112a7dea167SDimitry Andric // Primitive arrays are a bit special since they do not have inline 113a7dea167SDimitry Andric // descriptors. If Offset != Base, then the pointer already points to 114a7dea167SDimitry Andric // an element and there is nothing to do. Otherwise, the pointer is 115a7dea167SDimitry Andric // adjusted to the first element of the array. 116a7dea167SDimitry Andric if (inPrimitiveArray()) { 117a7dea167SDimitry Andric if (Offset != Base) 118a7dea167SDimitry Andric return *this; 119a7dea167SDimitry Andric return Pointer(Pointee, Base, Offset + sizeof(InitMap *)); 120a7dea167SDimitry Andric } 121a7dea167SDimitry Andric 122a7dea167SDimitry Andric // Pointer is to a field or array element - enter it. 123a7dea167SDimitry Andric if (Offset != Base) 124a7dea167SDimitry Andric return Pointer(Pointee, Offset, Offset); 125a7dea167SDimitry Andric 126a7dea167SDimitry Andric // Enter the first element of an array. 127a7dea167SDimitry Andric if (!getFieldDesc()->isArray()) 128a7dea167SDimitry Andric return *this; 129a7dea167SDimitry Andric 130a7dea167SDimitry Andric const unsigned NewBase = Base + sizeof(InlineDescriptor); 131a7dea167SDimitry Andric return Pointer(Pointee, NewBase, NewBase); 132a7dea167SDimitry Andric } 133a7dea167SDimitry Andric 134a7dea167SDimitry Andric /// Expands a pointer to the containing array, undoing narrowing. 135a7dea167SDimitry Andric Pointer expand() const { 136a7dea167SDimitry Andric if (isElementPastEnd()) { 137a7dea167SDimitry Andric // Revert to an outer one-past-end pointer. 138a7dea167SDimitry Andric unsigned Adjust; 139a7dea167SDimitry Andric if (inPrimitiveArray()) 140a7dea167SDimitry Andric Adjust = sizeof(InitMap *); 141a7dea167SDimitry Andric else 142a7dea167SDimitry Andric Adjust = sizeof(InlineDescriptor); 143a7dea167SDimitry Andric return Pointer(Pointee, Base, Base + getSize() + Adjust); 144a7dea167SDimitry Andric } 145a7dea167SDimitry Andric 146a7dea167SDimitry Andric // Do not step out of array elements. 147a7dea167SDimitry Andric if (Base != Offset) 148a7dea167SDimitry Andric return *this; 149a7dea167SDimitry Andric 150a7dea167SDimitry Andric // If at base, point to an array of base types. 151a7dea167SDimitry Andric if (Base == 0) 152a7dea167SDimitry Andric return Pointer(Pointee, RootPtrMark, 0); 153a7dea167SDimitry Andric 154a7dea167SDimitry Andric // Step into the containing array, if inside one. 155a7dea167SDimitry Andric unsigned Next = Base - getInlineDesc()->Offset; 156a7dea167SDimitry Andric Descriptor *Desc = Next == 0 ? getDeclDesc() : getDescriptor(Next)->Desc; 157a7dea167SDimitry Andric if (!Desc->IsArray) 158a7dea167SDimitry Andric return *this; 159a7dea167SDimitry Andric return Pointer(Pointee, Next, Offset); 160a7dea167SDimitry Andric } 161a7dea167SDimitry Andric 162a7dea167SDimitry Andric /// Checks if the pointer is null. 163a7dea167SDimitry Andric bool isZero() const { return Pointee == nullptr; } 164a7dea167SDimitry Andric /// Checks if the pointer is live. 165a7dea167SDimitry Andric bool isLive() const { return Pointee && !Pointee->IsDead; } 166a7dea167SDimitry Andric /// Checks if the item is a field in an object. 167a7dea167SDimitry Andric bool isField() const { return Base != 0 && Base != RootPtrMark; } 168a7dea167SDimitry Andric 169a7dea167SDimitry Andric /// Accessor for information about the declaration site. 170a7dea167SDimitry Andric Descriptor *getDeclDesc() const { return Pointee->Desc; } 171a7dea167SDimitry Andric SourceLocation getDeclLoc() const { return getDeclDesc()->getLocation(); } 172a7dea167SDimitry Andric 173a7dea167SDimitry Andric /// Returns a pointer to the object of which this pointer is a field. 174a7dea167SDimitry Andric Pointer getBase() const { 175a7dea167SDimitry Andric if (Base == RootPtrMark) { 176a7dea167SDimitry Andric assert(Offset == PastEndMark && "cannot get base of a block"); 177a7dea167SDimitry Andric return Pointer(Pointee, Base, 0); 178a7dea167SDimitry Andric } 179a7dea167SDimitry Andric assert(Offset == Base && "not an inner field"); 180a7dea167SDimitry Andric unsigned NewBase = Base - getInlineDesc()->Offset; 181a7dea167SDimitry Andric return Pointer(Pointee, NewBase, NewBase); 182a7dea167SDimitry Andric } 183a7dea167SDimitry Andric /// Returns the parent array. 184a7dea167SDimitry Andric Pointer getArray() const { 185a7dea167SDimitry Andric if (Base == RootPtrMark) { 186a7dea167SDimitry Andric assert(Offset != 0 && Offset != PastEndMark && "not an array element"); 187a7dea167SDimitry Andric return Pointer(Pointee, Base, 0); 188a7dea167SDimitry Andric } 189a7dea167SDimitry Andric assert(Offset != Base && "not an array element"); 190a7dea167SDimitry Andric return Pointer(Pointee, Base, Base); 191a7dea167SDimitry Andric } 192a7dea167SDimitry Andric 193a7dea167SDimitry Andric /// Accessors for information about the innermost field. 194a7dea167SDimitry Andric Descriptor *getFieldDesc() const { 195a7dea167SDimitry Andric if (Base == 0 || Base == RootPtrMark) 196a7dea167SDimitry Andric return getDeclDesc(); 197a7dea167SDimitry Andric return getInlineDesc()->Desc; 198a7dea167SDimitry Andric } 199a7dea167SDimitry Andric 200a7dea167SDimitry Andric /// Returns the type of the innermost field. 201a7dea167SDimitry Andric QualType getType() const { return getFieldDesc()->getType(); } 202a7dea167SDimitry Andric 203a7dea167SDimitry Andric /// Returns the element size of the innermost field. 204a7dea167SDimitry Andric size_t elemSize() const { 205a7dea167SDimitry Andric if (Base == RootPtrMark) 206a7dea167SDimitry Andric return getDeclDesc()->getSize(); 207a7dea167SDimitry Andric return getFieldDesc()->getElemSize(); 208a7dea167SDimitry Andric } 209a7dea167SDimitry Andric /// Returns the total size of the innermost field. 210a7dea167SDimitry Andric size_t getSize() const { return getFieldDesc()->getSize(); } 211a7dea167SDimitry Andric 212a7dea167SDimitry Andric /// Returns the offset into an array. 213a7dea167SDimitry Andric unsigned getOffset() const { 214a7dea167SDimitry Andric assert(Offset != PastEndMark && "invalid offset"); 215a7dea167SDimitry Andric if (Base == RootPtrMark) 216a7dea167SDimitry Andric return Offset; 217a7dea167SDimitry Andric 218a7dea167SDimitry Andric unsigned Adjust = 0; 219a7dea167SDimitry Andric if (Offset != Base) { 220a7dea167SDimitry Andric if (getFieldDesc()->ElemDesc) 221a7dea167SDimitry Andric Adjust = sizeof(InlineDescriptor); 222a7dea167SDimitry Andric else 223a7dea167SDimitry Andric Adjust = sizeof(InitMap *); 224a7dea167SDimitry Andric } 225a7dea167SDimitry Andric return Offset - Base - Adjust; 226a7dea167SDimitry Andric } 227a7dea167SDimitry Andric 228a7dea167SDimitry Andric /// Checks if the innermost field is an array. 229a7dea167SDimitry Andric bool inArray() const { return getFieldDesc()->IsArray; } 230a7dea167SDimitry Andric /// Checks if the structure is a primitive array. 231a7dea167SDimitry Andric bool inPrimitiveArray() const { return getFieldDesc()->isPrimitiveArray(); } 232a7dea167SDimitry Andric /// Checks if the structure is an array of unknown size. 233a7dea167SDimitry Andric bool isUnknownSizeArray() const { 234a7dea167SDimitry Andric return getFieldDesc()->isUnknownSizeArray(); 235a7dea167SDimitry Andric } 236a7dea167SDimitry Andric /// Checks if the pointer points to an array. 237a7dea167SDimitry Andric bool isArrayElement() const { return Base != Offset; } 238a7dea167SDimitry Andric /// Pointer points directly to a block. 239a7dea167SDimitry Andric bool isRoot() const { 240a7dea167SDimitry Andric return (Base == 0 || Base == RootPtrMark) && Offset == 0; 241a7dea167SDimitry Andric } 242a7dea167SDimitry Andric 243a7dea167SDimitry Andric /// Returns the record descriptor of a class. 244a7dea167SDimitry Andric Record *getRecord() const { return getFieldDesc()->ElemRecord; } 245*bdd1243dSDimitry Andric // Returns the element record type, if this is a non-primive array. 246*bdd1243dSDimitry Andric Record *getElemRecord() const { return getFieldDesc()->ElemDesc->ElemRecord; } 247a7dea167SDimitry Andric /// Returns the field information. 248a7dea167SDimitry Andric const FieldDecl *getField() const { return getFieldDesc()->asFieldDecl(); } 249a7dea167SDimitry Andric 250a7dea167SDimitry Andric /// Checks if the object is a union. 251a7dea167SDimitry Andric bool isUnion() const; 252a7dea167SDimitry Andric 253a7dea167SDimitry Andric /// Checks if the storage is extern. 254a7dea167SDimitry Andric bool isExtern() const { return Pointee->isExtern(); } 255a7dea167SDimitry Andric /// Checks if the storage is static. 256a7dea167SDimitry Andric bool isStatic() const { return Pointee->isStatic(); } 257a7dea167SDimitry Andric /// Checks if the storage is temporary. 258a7dea167SDimitry Andric bool isTemporary() const { return Pointee->isTemporary(); } 259a7dea167SDimitry Andric /// Checks if the storage is a static temporary. 260a7dea167SDimitry Andric bool isStaticTemporary() const { return isStatic() && isTemporary(); } 261a7dea167SDimitry Andric 262a7dea167SDimitry Andric /// Checks if the field is mutable. 263*bdd1243dSDimitry Andric bool isMutable() const { 264*bdd1243dSDimitry Andric return Base != 0 && getInlineDesc()->IsFieldMutable; 265*bdd1243dSDimitry Andric } 266a7dea167SDimitry Andric /// Checks if an object was initialized. 267a7dea167SDimitry Andric bool isInitialized() const; 268a7dea167SDimitry Andric /// Checks if the object is active. 269a7dea167SDimitry Andric bool isActive() const { return Base == 0 || getInlineDesc()->IsActive; } 270a7dea167SDimitry Andric /// Checks if a structure is a base class. 271a7dea167SDimitry Andric bool isBaseClass() const { return isField() && getInlineDesc()->IsBase; } 272a7dea167SDimitry Andric 273a7dea167SDimitry Andric /// Checks if an object or a subfield is mutable. 274a7dea167SDimitry Andric bool isConst() const { 275a7dea167SDimitry Andric return Base == 0 ? getDeclDesc()->IsConst : getInlineDesc()->IsConst; 276a7dea167SDimitry Andric } 277a7dea167SDimitry Andric 278a7dea167SDimitry Andric /// Returns the declaration ID. 279*bdd1243dSDimitry Andric std::optional<unsigned> getDeclID() const { return Pointee->getDeclID(); } 280a7dea167SDimitry Andric 281a7dea167SDimitry Andric /// Returns the byte offset from the start. 282a7dea167SDimitry Andric unsigned getByteOffset() const { 283a7dea167SDimitry Andric return Offset; 284a7dea167SDimitry Andric } 285a7dea167SDimitry Andric 286a7dea167SDimitry Andric /// Returns the number of elements. 287a7dea167SDimitry Andric unsigned getNumElems() const { return getSize() / elemSize(); } 288a7dea167SDimitry Andric 289a7dea167SDimitry Andric /// Returns the index into an array. 290a7dea167SDimitry Andric int64_t getIndex() const { 291a7dea167SDimitry Andric if (isElementPastEnd()) 292a7dea167SDimitry Andric return 1; 293a7dea167SDimitry Andric if (auto ElemSize = elemSize()) 294a7dea167SDimitry Andric return getOffset() / ElemSize; 295a7dea167SDimitry Andric return 0; 296a7dea167SDimitry Andric } 297a7dea167SDimitry Andric 298a7dea167SDimitry Andric /// Checks if the index is one past end. 299a7dea167SDimitry Andric bool isOnePastEnd() const { 300a7dea167SDimitry Andric return isElementPastEnd() || getSize() == getOffset(); 301a7dea167SDimitry Andric } 302a7dea167SDimitry Andric 303a7dea167SDimitry Andric /// Checks if the pointer is an out-of-bounds element pointer. 304a7dea167SDimitry Andric bool isElementPastEnd() const { return Offset == PastEndMark; } 305a7dea167SDimitry Andric 306a7dea167SDimitry Andric /// Dereferences the pointer, if it's live. 307a7dea167SDimitry Andric template <typename T> T &deref() const { 308a7dea167SDimitry Andric assert(isLive() && "Invalid pointer"); 309*bdd1243dSDimitry Andric return *reinterpret_cast<T *>(Pointee->rawData() + Offset); 310a7dea167SDimitry Andric } 311a7dea167SDimitry Andric 312a7dea167SDimitry Andric /// Dereferences a primitive element. 313a7dea167SDimitry Andric template <typename T> T &elem(unsigned I) const { 314*bdd1243dSDimitry Andric return reinterpret_cast<T *>(Pointee->rawData())[I]; 315a7dea167SDimitry Andric } 316a7dea167SDimitry Andric 317a7dea167SDimitry Andric /// Initializes a field. 318a7dea167SDimitry Andric void initialize() const; 319a7dea167SDimitry Andric /// Activats a field. 320a7dea167SDimitry Andric void activate() const; 321a7dea167SDimitry Andric /// Deactivates an entire strurcutre. 322a7dea167SDimitry Andric void deactivate() const; 323a7dea167SDimitry Andric 324a7dea167SDimitry Andric /// Checks if two pointers are comparable. 325a7dea167SDimitry Andric static bool hasSameBase(const Pointer &A, const Pointer &B); 326a7dea167SDimitry Andric /// Checks if two pointers can be subtracted. 327a7dea167SDimitry Andric static bool hasSameArray(const Pointer &A, const Pointer &B); 328a7dea167SDimitry Andric 329a7dea167SDimitry Andric /// Prints the pointer. 330a7dea167SDimitry Andric void print(llvm::raw_ostream &OS) const { 331*bdd1243dSDimitry Andric OS << Pointee << " {" << Base << ", " << Offset << ", "; 332a7dea167SDimitry Andric if (Pointee) 333a7dea167SDimitry Andric OS << Pointee->getSize(); 334a7dea167SDimitry Andric else 335a7dea167SDimitry Andric OS << "nullptr"; 336a7dea167SDimitry Andric OS << "}"; 337a7dea167SDimitry Andric } 338a7dea167SDimitry Andric 339a7dea167SDimitry Andric private: 340a7dea167SDimitry Andric friend class Block; 341a7dea167SDimitry Andric friend class DeadBlock; 342a7dea167SDimitry Andric 343a7dea167SDimitry Andric Pointer(Block *Pointee, unsigned Base, unsigned Offset); 344a7dea167SDimitry Andric 345a7dea167SDimitry Andric /// Returns the embedded descriptor preceding a field. 346a7dea167SDimitry Andric InlineDescriptor *getInlineDesc() const { return getDescriptor(Base); } 347a7dea167SDimitry Andric 348a7dea167SDimitry Andric /// Returns a descriptor at a given offset. 349a7dea167SDimitry Andric InlineDescriptor *getDescriptor(unsigned Offset) const { 350a7dea167SDimitry Andric assert(Offset != 0 && "Not a nested pointer"); 351*bdd1243dSDimitry Andric return reinterpret_cast<InlineDescriptor *>(Pointee->rawData() + Offset) - 352*bdd1243dSDimitry Andric 1; 353a7dea167SDimitry Andric } 354a7dea167SDimitry Andric 355a7dea167SDimitry Andric /// Returns a reference to the pointer which stores the initialization map. 356a7dea167SDimitry Andric InitMap *&getInitMap() const { 357*bdd1243dSDimitry Andric return *reinterpret_cast<InitMap **>(Pointee->rawData() + Base); 358a7dea167SDimitry Andric } 359a7dea167SDimitry Andric 360a7dea167SDimitry Andric /// The block the pointer is pointing to. 361a7dea167SDimitry Andric Block *Pointee = nullptr; 362a7dea167SDimitry Andric /// Start of the current subfield. 363a7dea167SDimitry Andric unsigned Base = 0; 364a7dea167SDimitry Andric /// Offset into the block. 365a7dea167SDimitry Andric unsigned Offset = 0; 366a7dea167SDimitry Andric 367a7dea167SDimitry Andric /// Previous link in the pointer chain. 368a7dea167SDimitry Andric Pointer *Prev = nullptr; 369a7dea167SDimitry Andric /// Next link in the pointer chain. 370a7dea167SDimitry Andric Pointer *Next = nullptr; 371a7dea167SDimitry Andric }; 372a7dea167SDimitry Andric 373a7dea167SDimitry Andric inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Pointer &P) { 374a7dea167SDimitry Andric P.print(OS); 375a7dea167SDimitry Andric return OS; 376a7dea167SDimitry Andric } 377a7dea167SDimitry Andric 378a7dea167SDimitry Andric } // namespace interp 379a7dea167SDimitry Andric } // namespace clang 380a7dea167SDimitry Andric 381a7dea167SDimitry Andric #endif 382