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 "Function.h"
18#include "InitMap.h"
19#include "InterpBlock.h"
20#include "clang/AST/ComparisonCategories.h"
21#include "clang/AST/Decl.h"
22#include "clang/AST/DeclCXX.h"
23#include "clang/AST/Expr.h"
24#include "llvm/Support/raw_ostream.h"
25
26namespace clang {
27namespace interp {
28class Block;
29class DeadBlock;
30class Pointer;
31class Context;
32
33class Pointer;
34inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Pointer &P);
35
36struct PtrView {
37 static constexpr unsigned PastEndMark = ~0u;
38
39 Block *Pointee;
40 unsigned Base;
41 uint64_t Offset;
42
43 bool isZero() const { return !Pointee; }
44 bool isLive() const { return Pointee && !Pointee->isDead(); }
45 bool isActive() const { return isRoot() || getInlineDesc()->IsActive; }
46 bool isArrayRoot() const { return inArray() && Offset == Base; }
47 bool isElementPastEnd() const { return Offset == PastEndMark; }
48 bool isZeroSizeArray() const { return getFieldDesc()->isZeroSizeArray(); }
49 bool isMutable() const {
50 return !isRoot() && getInlineDesc()->IsFieldMutable;
51 }
52 bool isExtern() const { return Pointee && Pointee->isExtern(); }
53 bool isVolatile() const {
54 return isRoot() ? getDeclDesc()->IsVolatile : getInlineDesc()->IsVolatile;
55 }
56 bool inUnion() const { return getInlineDesc()->InUnion; };
57 bool inArray() const { return getFieldDesc()->IsArray; }
58 bool inPrimitiveArray() const { return getFieldDesc()->isPrimitiveArray(); }
59 bool canBeInitialized() const { return Pointee && Base > 0; }
60 const Block *block() const { return Pointee; }
61
62 unsigned getEvalID() { return Pointee->getEvalID(); }
63
64 bool isRoot() const { return Base == Pointee->getMetadataSize(); }
65
66 bool isConst() const {
67 return isRoot() ? getDeclDesc()->IsConst : getInlineDesc()->IsConst;
68 }
69
70 InlineDescriptor *getInlineDesc() const {
71 assert(Base != sizeof(GlobalInlineDescriptor));
72 assert(Base <= Pointee->getSize());
73 assert(Base >= sizeof(InlineDescriptor));
74 return getDescriptor(Offset: Base);
75 }
76
77 InlineDescriptor *getDescriptor(unsigned Offset) const {
78 assert(Offset != 0 && "Not a nested pointer");
79 return reinterpret_cast<InlineDescriptor *>(Pointee->rawData() + Offset) -
80 1;
81 }
82
83 const Descriptor *getFieldDesc() const {
84 if (isRoot())
85 return Pointee->getDescriptor();
86 return getInlineDesc()->Desc;
87 }
88
89 const Descriptor *getDeclDesc() const { return Pointee->getDescriptor(); }
90
91 size_t elemSize() const { return getFieldDesc()->getElemSize(); }
92
93 [[nodiscard]] PtrView narrow() const {
94 // Null pointers cannot be narrowed.
95 if (isZero() || isUnknownSizeArray())
96 return *this;
97
98 if (inArray()) {
99 // Pointer is one past end - magic offset marks that.
100 if (isOnePastEnd())
101 return PtrView{.Pointee: Pointee, .Base: Base, .Offset: PastEndMark};
102
103 if (Offset != Base) {
104 // If we're pointing to a primitive array element, there's nothing to
105 // do.
106 if (inPrimitiveArray())
107 return *this;
108 // Pointer is to a composite array element - enter it.
109 return PtrView{.Pointee: Pointee, .Base: static_cast<unsigned>(Offset), .Offset: Offset};
110 }
111 }
112 // Otherwise, we're pointing to a non-array element or
113 // are already narrowed to a composite array element. Nothing to do.
114 return *this;
115 }
116
117 [[nodiscard]] PtrView expand() const {
118 if (isElementPastEnd()) {
119 // Revert to an outer one-past-end pointer.
120 unsigned Adjust;
121 if (inPrimitiveArray())
122 Adjust = sizeof(InitMapPtr);
123 else
124 Adjust = sizeof(InlineDescriptor);
125 return PtrView{.Pointee: Pointee, .Base: Base, .Offset: Base + getSize() + Adjust};
126 }
127
128 // Do not step out of array elements.
129 if (Base != Offset)
130 return *this;
131
132 if (isRoot())
133 return PtrView{.Pointee: Pointee, .Base: Base, .Offset: Base};
134
135 // Step into the containing array, if inside one.
136 unsigned Next = Base - getInlineDesc()->Offset;
137 const Descriptor *Desc = (Next == Pointee->getMetadataSize())
138 ? getDeclDesc()
139 : getDescriptor(Offset: Next)->Desc;
140 if (!Desc->IsArray)
141 return *this;
142 return PtrView{.Pointee: Pointee, .Base: Next, .Offset: Offset};
143 }
144
145 [[nodiscard]] PtrView stripBaseCasts() const {
146 PtrView V = *this;
147 while (V.isBaseClass())
148 V = V.getBase();
149 return V;
150 }
151
152 [[nodiscard]] PtrView getArray() const {
153 assert(Offset != Base && "not an array element");
154 return PtrView{.Pointee: Pointee, .Base: Base, .Offset: Base};
155 }
156
157 const Record *getRecord() const { return getFieldDesc()->ElemRecord; }
158 const Record *getElemRecord() const {
159 const Descriptor *ElemDesc = getFieldDesc()->ElemDesc;
160 return ElemDesc ? ElemDesc->ElemRecord : nullptr;
161 }
162 const FieldDecl *getField() const { return getFieldDesc()->asFieldDecl(); }
163
164 bool isField() const {
165 return !isZero() && !isRoot() && getFieldDesc()->asDecl();
166 }
167
168 bool isBaseClass() const { return isField() && getInlineDesc()->IsBase; }
169 bool isVirtualBaseClass() const {
170 return isField() && getInlineDesc()->IsVirtualBase;
171 }
172 bool isUnknownSizeArray() const {
173 return getFieldDesc()->isUnknownSizeArray();
174 }
175
176 bool isPastEnd() const { return Offset > Pointee->getSize(); }
177
178 unsigned getOffset() const {
179 assert(Offset != PastEndMark);
180
181 unsigned Adjust = 0;
182 if (Offset != Base) {
183 if (getFieldDesc()->ElemDesc)
184 Adjust = sizeof(InlineDescriptor);
185 else
186 Adjust = sizeof(InitMapPtr);
187 }
188 return Offset - Base - Adjust;
189 }
190 size_t getSize() const { return getFieldDesc()->getSize(); }
191
192 bool isOnePastEnd() const {
193 if (!Pointee)
194 return false;
195
196 const Descriptor *Desc = getFieldDesc();
197 if (Desc->isUnknownSizeArray())
198 return false;
199
200 if (isPastEnd())
201 return true;
202
203 if (Offset != Base) {
204 unsigned Adjust =
205 Desc->ElemDesc ? sizeof(InlineDescriptor) : sizeof(InitMapPtr);
206 unsigned Off = Offset - Base - Adjust;
207 return Desc->getSize() == Off;
208 }
209
210 return Desc->getSize() == 0;
211 }
212
213 PtrView atIndex(unsigned Idx) const {
214 unsigned Off = Idx * elemSize();
215 if (getFieldDesc()->ElemDesc)
216 Off += sizeof(InlineDescriptor);
217 else
218 Off += sizeof(InitMapPtr);
219 return PtrView{.Pointee: Pointee, .Base: Base, .Offset: Base + Off};
220 }
221
222 int64_t getIndex() const {
223 if (isZero())
224 return 0;
225 // narrow()ed element in a composite array.
226 if (Base > sizeof(InlineDescriptor) && Base == Offset)
227 return 0;
228
229 if (auto ElemSize = elemSize())
230 return getOffset() / ElemSize;
231 return 0;
232 }
233
234 unsigned getNumElems() const { return getSize() / elemSize(); }
235
236 bool isArrayElement() const {
237 if (inArray() && Base != Offset)
238 return true;
239
240 // Might be a narrow()'ed element in a composite array.
241 // Check the inline descriptor.
242 if (Base >= sizeof(InlineDescriptor) && getInlineDesc()->IsArrayElement)
243 return true;
244
245 return false;
246 }
247
248 template <typename T> T &deref() const {
249 assert(isLive() && "Invalid pointer");
250 assert(Pointee);
251
252 if (isArrayRoot())
253 return *reinterpret_cast<T *>(Pointee->rawData() + Base +
254 sizeof(InitMapPtr));
255
256 return *reinterpret_cast<T *>(Pointee->rawData() + Offset);
257 }
258
259 template <typename T> T &elem(unsigned I) const {
260 assert(isLive() && "Invalid pointer");
261 assert(Pointee);
262 assert(getFieldDesc()->isPrimitiveArray());
263 assert(I < getFieldDesc()->getNumElems());
264
265 unsigned ElemByteOffset = I * getFieldDesc()->getElemSize();
266 unsigned ReadOffset = Base + sizeof(InitMapPtr) + ElemByteOffset;
267 assert(ReadOffset + sizeof(T) <= Pointee->getSize());
268
269 return *reinterpret_cast<T *>(Pointee->rawData() + ReadOffset);
270 }
271
272 [[nodiscard]] PtrView getBase() const {
273 unsigned NewBase = Base - getInlineDesc()->Offset;
274 return PtrView{.Pointee: Pointee, .Base: NewBase, .Offset: NewBase};
275 }
276
277 [[nodiscard]] PtrView atField(unsigned Offset) const {
278 unsigned F = this->Offset + Offset;
279 return PtrView{.Pointee: Pointee, .Base: F, .Offset: F};
280 }
281
282 QualType getType() const {
283 if (isRoot() && Base == Offset) {
284 // If this pointer points to the root of a declaration, try to consult
285 // the ValueDecl directly, since that has a type with more information,
286 // e.g. the correct ElaboratedTypeKeyword.
287 if (const ValueDecl *VD = getDeclDesc()->asValueDecl())
288 return VD->getType();
289 return getDeclDesc()->getType();
290 }
291 if (inPrimitiveArray() && Offset != Base) {
292 // Unfortunately, complex and vector types are not array types in clang,
293 // but they are for us.
294 if (const auto *AT = getFieldDesc()->getType()->getAsArrayTypeUnsafe())
295 return AT->getElementType();
296 if (const auto *CT = getFieldDesc()->getType()->getAs<ComplexType>())
297 return CT->getElementType();
298 if (const auto *CT = getFieldDesc()->getType()->getAs<VectorType>())
299 return CT->getElementType();
300 }
301
302 return getFieldDesc()->getType();
303 }
304
305 bool isInitialized() const {
306
307 if (!Pointee->isInitialized())
308 return false;
309
310 if (isRoot() && Base == sizeof(GlobalInlineDescriptor) && Offset == Base) {
311 const auto &GD = Pointee->getBlockDesc<GlobalInlineDescriptor>();
312 return GD.InitState == GlobalInitState::Initialized;
313 }
314
315 assert(Pointee && "Cannot check if null pointer was initialized");
316 const Descriptor *Desc = getFieldDesc();
317 assert(Desc);
318 if (Desc->isPrimitiveArray())
319 return isElementInitialized(Index: getIndex());
320
321 if (Base == 0)
322 return true;
323 // Field has its bit in an inline descriptor.
324 return getInlineDesc()->IsInitialized;
325 }
326
327 void initializeElement(unsigned Index) const;
328 bool allElementsInitialized() const;
329 bool isElementInitialized(unsigned Index) const;
330 InitMapPtr &getInitMap() const {
331 return *reinterpret_cast<InitMapPtr *>(Pointee->rawData() + Base);
332 }
333 void initialize() const;
334 void activate() const;
335
336 void setLifeState(Lifetime L) const;
337 Lifetime getLifetime() const;
338 void startLifetime() const { setLifeState(Lifetime::Started); }
339 void endLifetime() const { setLifeState(Lifetime::Ended); }
340
341 bool operator==(const PtrView &Other) const {
342 return Other.Pointee == Pointee && Base == Other.Base &&
343 Offset == Other.Offset;
344 }
345
346 bool operator!=(const PtrView &Other) const { return !(Other == *this); }
347};
348
349struct BlockPointer {
350 /// The block the pointer is pointing to.
351 Block *Pointee;
352 /// Start of the current subfield.
353 unsigned Base;
354 /// Previous link in the pointer chain.
355 Pointer *Prev;
356 /// Next link in the pointer chain.
357 Pointer *Next;
358};
359
360struct IntPointer {
361 llvm::PointerIntPair<const Type *, 1, bool> TypeAndIsNull;
362 uint64_t Value;
363
364 std::optional<IntPointer> atOffset(const Context &Ctx, unsigned Offset) const;
365 IntPointer baseCast(const Context &Ctx, unsigned BaseOffset) const;
366
367 const Type *getType() const { return TypeAndIsNull.getPointer(); }
368 bool isNull() const { return TypeAndIsNull.getInt(); }
369
370 QualType getPointeeType() const {
371 if (!getType())
372 return QualType();
373
374 QualType QT(getType(), 0);
375 if (QT->isPointerOrReferenceType())
376 QT = QT->getPointeeType();
377 else if (QT->isArrayType())
378 QT = QT->getAsArrayTypeUnsafe()->getElementType();
379
380 return QT.IgnoreParens();
381 }
382};
383
384struct FunctionPointer {
385 const Function *Func;
386};
387
388struct TypeidPointer {
389 const Type *TypePtr;
390 const Type *TypeInfoType;
391};
392
393struct StringPointer {
394 const Expr *Base = nullptr;
395 unsigned ID = 0;
396 bool Decayed = false;
397
398 StringPointer decay() const { return StringPointer{.Base: Base, .ID: ID, .Decayed: true}; }
399 const StringLiteral *getLiteral() const {
400 if (const auto *PE = dyn_cast<PredefinedExpr>(Val: Base))
401 return PE->getFunctionName();
402 return cast<StringLiteral>(Val: Base);
403 }
404};
405
406struct PointerPathEntry {
407 enum { Base, Field, Array, NegativeArray } Kind;
408 union {
409 uint64_t Index;
410 const FieldDecl *FD;
411 llvm::PointerIntPair<const CXXRecordDecl *, 1, bool> RD = {};
412 };
413
414 static PointerPathEntry base(const CXXRecordDecl *RD, bool Virtual = false) {
415 PointerPathEntry E;
416 E.Kind = Base;
417 E.RD = {RD, Virtual};
418 return E;
419 }
420
421 static PointerPathEntry array(int64_t Index) {
422 PointerPathEntry E;
423 E.Kind = Array;
424 E.Index = Index;
425 return E;
426 }
427
428 static PointerPathEntry negativeArray(int64_t Index) {
429 PointerPathEntry E;
430 E.Kind = NegativeArray;
431 E.Index = Index;
432 return E;
433 }
434
435 static PointerPathEntry field(const FieldDecl *FD) {
436 PointerPathEntry E;
437 E.Kind = Field;
438 E.FD = FD;
439 return E;
440 }
441};
442
443struct OpaquePointer {
444 DeclOrExpr Base;
445 // FieldType and IsOnePastEnd/IsConstexprUnknown bits.
446 llvm::PointerIntPair<const Type *, 2, unsigned> FieldType = {};
447 const PointerPathEntry *Path = nullptr;
448 unsigned PathLength = 0;
449
450 ArrayRef<PointerPathEntry> path() const { return ArrayRef(Path, PathLength); }
451 bool hasDeclBase() const { return Base.isDecl(); }
452 const ValueDecl *getBaseDecl() const { return Base.asValueDecl(); }
453 const VarDecl *getBaseVarDecl() const { return Base.asVarDecl(); }
454 const Expr *getBaseExpr() const { return Base.asExpr(); }
455 bool hasValidBase() const;
456
457 OpaquePointer
458 withFieldType(const Type *FieldTy,
459 std::optional<bool> PastEnd = std::nullopt) const {
460 unsigned NewBitFieldValue = FieldType.getInt();
461 if (PastEnd)
462 NewBitFieldValue =
463 (isConstexprUnknown() ? 2u : 0u) + static_cast<unsigned>(*PastEnd);
464 return OpaquePointer{.Base: Base, .FieldType: {FieldTy, NewBitFieldValue}, .Path: Path, .PathLength: PathLength};
465 }
466
467 OpaquePointer withPath(const PointerPathEntry *Path, unsigned PathLength,
468 const Type *FieldTy,
469 std::optional<bool> PastEnd = std::nullopt) const {
470 unsigned NewBitFieldValue = FieldType.getInt();
471 if (PastEnd)
472 NewBitFieldValue =
473 (isConstexprUnknown() ? 2u : 0u) + static_cast<unsigned>(*PastEnd);
474 return OpaquePointer{.Base: Base, .FieldType: {FieldTy, NewBitFieldValue}, .Path: Path, .PathLength: PathLength};
475 }
476
477 OpaquePointer withPastEnd(bool PastEnd) const {
478 return OpaquePointer{.Base: Base,
479 .FieldType: {FieldType.getPointer(),
480 FieldType.getInt() | static_cast<unsigned>(PastEnd)},
481 .Path: Path,
482 .PathLength: PathLength};
483 }
484
485 QualType getObjectType() const {
486 QualType T = Base.getType();
487 if (T->isPointerOrReferenceType())
488 return T->getPointeeType();
489 return T;
490 }
491
492 QualType getFieldType() const {
493 if (FieldType.getPointer()->isPointerOrReferenceType() && Base.isDecl())
494 return FieldType.getPointer()->getPointeeType();
495 return QualType(FieldType.getPointer(), 0);
496 }
497
498 bool isArrayElement() const {
499 return PathLength != 0 &&
500 Path[PathLength - 1].Kind == PointerPathEntry::Array;
501 }
502
503 std::optional<size_t> computeLayoutOffset(const ASTContext &ASTCtx) const;
504 /// If this is pointing to an array element, return the array.
505 QualType getSurroundingArray() const;
506
507 bool isOnePastEnd() const { return FieldType.getInt() & 1u; }
508 bool isOnePastEndOrElementPastEnd() const;
509 bool isConstexprUnknown() const { return FieldType.getInt() & 2u; }
510 bool isUnknownSizeArray() const;
511 bool isRoot() const;
512};
513
514enum class Storage { Int, Block, Fn, Typeid, String, Opaque };
515
516/// A pointer to a memory block, live or dead.
517///
518/// This object can be allocated into interpreter stack frames. If pointing to
519/// a live block, it is a link in the chain of pointers pointing to the block.
520///
521/// In the simplest form, a Pointer has a Block* (the pointee) and both Base
522/// and Offset are 0, which means it will point to raw data.
523///
524/// The Base field is used to access metadata about the data. For primitive
525/// arrays, the Base is followed by an InitMap. In a variety of cases, the
526/// Base is preceded by an InlineDescriptor, which is used to track the
527/// initialization state, among other things.
528///
529/// The Offset field is used to access the actual data. In other words, the
530/// data the pointer decribes can be found at
531/// Pointee->rawData() + Pointer.Offset.
532///
533/// \verbatim
534/// Pointee Offset
535/// │ │
536/// │ │
537/// ▼ ▼
538/// ┌───────┬────────────┬─────────┬────────────────────────────┐
539/// │ Block │ InlineDesc │ InitMap │ Actual Data │
540/// └───────┴────────────┴─────────┴────────────────────────────┘
541/// ▲
542/// │
543/// │
544/// Base
545/// \endverbatim
546class Pointer {
547public:
548 Pointer() : StorageKind(Storage::Int), Int{.TypeAndIsNull: {nullptr, true}, .Value: 0} {}
549 Pointer(IntPointer &&IntPtr)
550 : StorageKind(Storage::Int), Int(std::move(IntPtr)) {}
551 Pointer(Block *B);
552 Pointer(Block *B, uint64_t BaseAndOffset);
553 Pointer(const Pointer &P);
554 Pointer(Pointer &&P);
555 Pointer(uint64_t Address, const Type *Ty, uint64_t Offset = 0,
556 std::optional<bool> IsNull = std::nullopt)
557 : Offset(Offset), StorageKind(Storage::Int),
558 Int{.TypeAndIsNull: {Ty, IsNull.value_or(u: Address == 0)}, .Value: Address} {}
559 Pointer(const Function *F, uint64_t Offset = 0)
560 : Offset(Offset), StorageKind(Storage::Fn), Fn{.Func: F} {}
561 Pointer(const Type *TypePtr, const Type *TypeInfoType, uint64_t Offset = 0)
562 : Offset(Offset), StorageKind(Storage::Typeid) {
563 Typeid.TypePtr = TypePtr;
564 Typeid.TypeInfoType = TypeInfoType;
565 }
566 Pointer(const Expr *Base, unsigned Id)
567 : Offset(0), StorageKind(Storage::String), Str{.Base: Base, .ID: Id} {}
568 Pointer(StringPointer Str, uint64_t Offset = 0)
569 : Offset(Offset), StorageKind(Storage::String), Str(Str) {}
570
571 Pointer(DeclOrExpr DOE, bool ConstexprUnknown = false)
572 : Offset(0), StorageKind(Storage::Opaque) {
573 Opaque.Base = DOE;
574 Opaque.FieldType = {DOE.getType().getTypePtr(), ConstexprUnknown ? 2u : 0u};
575 Opaque.Path = nullptr;
576 Opaque.PathLength = 0;
577 }
578 Pointer(OpaquePointer OP, uint64_t Offset = 0)
579 : Offset(Offset), StorageKind(Storage::Opaque), Opaque(OP) {}
580
581 Pointer(Block *Pointee, unsigned Base, uint64_t Offset);
582 explicit Pointer(PtrView V) : Pointer(V.Pointee, V.Base, V.Offset) {}
583 ~Pointer();
584
585 Pointer &operator=(const Pointer &P);
586 Pointer &operator=(Pointer &&P);
587
588 bool operator==(const Pointer &P) const;
589 bool operator!=(const Pointer &P) const { return !(P == *this); }
590
591 /// Converts the pointer to an APValue.
592 APValue toAPValue(const ASTContext &ASTCtx) const;
593
594 /// Converts the pointer to a string usable in diagnostics.
595 std::string toDiagnosticString(const ASTContext &Ctx) const;
596
597 uint64_t getIntegerRepresentation() const {
598 if (isIntegralPointer())
599 return Int.Value + (Offset * elemSize());
600 if (isFunctionPointer())
601 return reinterpret_cast<uint64_t>(Fn.Func) + Offset;
602 return reinterpret_cast<uint64_t>(BS.Pointee) + Offset;
603 }
604
605 PtrView view() const {
606 assert(isBlockPointer());
607 return PtrView{.Pointee: BS.Pointee, .Base: BS.Base, .Offset: Offset};
608 }
609
610 /// Converts the pointer to an APValue that is an rvalue.
611 std::optional<APValue> toRValue(const Context &Ctx,
612 QualType ResultType) const;
613
614 /// Offsets a pointer inside an array.
615 [[nodiscard]] Pointer atIndex(uint64_t Idx) const {
616 switch (StorageKind) {
617 case Storage::Int:
618 return Pointer(Int.Value, Int.getType(), Idx);
619 case Storage::Block:
620 return Pointer(view().atIndex(Idx));
621 case Storage::Fn:
622 return Pointer(Fn.Func, Idx);
623 case Storage::String:
624 return Pointer(Str, Idx);
625 default:
626 llvm_unreachable("Unexpected pointer type in atIndex()");
627 }
628 }
629
630 /// Creates a pointer to a field.
631 [[nodiscard]] Pointer atField(unsigned Off) const {
632 return Pointer(view().atField(Offset: Off));
633 }
634
635 /// Subtract the given offset from the current Base and Offset
636 /// of the pointer.
637 [[nodiscard]] Pointer atFieldSub(unsigned Off) const {
638 assert(Offset >= Off);
639 unsigned O = Offset - Off;
640 return Pointer(BS.Pointee, O, O);
641 }
642
643 /// Restricts the scope of an array element pointer.
644 [[nodiscard]] Pointer narrow() const {
645 if (!isBlockPointer())
646 return *this;
647 return Pointer(view().narrow());
648 }
649
650 /// Expands a pointer to the containing array, undoing narrowing.
651 [[nodiscard]] Pointer expand() const {
652 if (!isBlockPointer())
653 return *this;
654 return Pointer(view().expand());
655 }
656
657 /// Checks if the pointer is null.
658 bool isZero() const {
659 switch (StorageKind) {
660 case Storage::Int:
661 return Int.isNull();
662 case Storage::Block:
663 return BS.Pointee == nullptr;
664 case Storage::Fn:
665 return !Fn.Func;
666 case Storage::Typeid:
667 case Storage::String:
668 case Storage::Opaque:
669 return false;
670 }
671 llvm_unreachable("Unknown clang::interp::Storage enum");
672 }
673 /// Checks if the pointer is live.
674 bool isLive() const {
675 if (!isBlockPointer())
676 return true;
677 return view().isLive();
678 }
679 /// Checks if the item is a field in an object.
680 bool isField() const {
681 if (!isBlockPointer())
682 return false;
683
684 return view().isField();
685 }
686
687 /// Accessor for information about the declaration site.
688 const Descriptor *getDeclDesc() const {
689 if (!isBlockPointer())
690 return nullptr;
691
692 assert(isBlockPointer());
693 assert(BS.Pointee);
694 return BS.Pointee->Desc;
695 }
696 SourceLocation getDeclLoc() const { return getDeclDesc()->getLocation(); }
697
698 /// Returns the expression or declaration the pointer has been created for.
699 DeclOrExpr getSource() const {
700 if (isBlockPointer())
701 return getDeclDesc()->getSource();
702 if (isFunctionPointer()) {
703 const Function *F = Fn.Func;
704 return F ? F->getDecl() : DeclOrExpr();
705 }
706 llvm_unreachable("Unsupported pointer type in getSource()");
707 return DeclOrExpr();
708 }
709
710 /// Returns a pointer to the object of which this pointer is a field.
711 [[nodiscard]] Pointer getBase() const { return Pointer(view().getBase()); }
712 /// Returns the parent array.
713 [[nodiscard]] Pointer getArray() const { return Pointer(view().getArray()); }
714
715 /// Accessors for information about the innermost field.
716 const Descriptor *getFieldDesc() const {
717 if (!isBlockPointer())
718 return nullptr;
719
720 if (isRoot())
721 return getDeclDesc();
722 return getInlineDesc()->Desc;
723 }
724
725 /// Returns the type of the innermost field.
726 QualType getType() const {
727 switch (StorageKind) {
728 case Storage::Int:
729 return Int.getPointeeType();
730 case Storage::Block:
731 return view().getType();
732 case Storage::Fn:
733 return Fn.Func->getDecl()->getType();
734 case Storage::Typeid:
735 return QualType(Typeid.TypeInfoType, 0);
736 case Storage::String:
737 if (Str.Decayed)
738 return Str.getLiteral()
739 ->getType()
740 ->getAsArrayTypeUnsafe()
741 ->getElementType();
742 return Str.getLiteral()->getType();
743 case Storage::Opaque:
744 return Opaque.getFieldType();
745 }
746 llvm_unreachable("Unhandled StorageKind");
747 }
748
749 const VarDecl *getRootVarDecl() const;
750 const ValueDecl *getRootValueDecl() const;
751 const Expr *getRootExpr() const;
752
753 [[nodiscard]] Pointer getDeclPtr() const { return Pointer(BS.Pointee); }
754
755 /// Returns the element size of the innermost field.
756 size_t elemSize() const {
757 if (isIntegralPointer()) {
758 // FIXME: Remove this and handle int ptrs specially?
759 return 1;
760 }
761 if (isStringPointer())
762 return Str.getLiteral()->getCharByteWidth();
763
764 return view().elemSize();
765 }
766 /// Returns the total size of the innermost field.
767 size_t getSize() const {
768 assert(isBlockPointer());
769 return getFieldDesc()->getSize();
770 }
771
772 /// Returns the offset into an array.
773 unsigned getOffset() const {
774 assert(Offset != PtrView::PastEndMark && "invalid offset");
775 return view().getOffset();
776 }
777
778 /// Whether this array refers to an array, but not
779 /// to the first element.
780 bool isArrayRoot() const { return view().isArrayRoot(); }
781
782 /// Checks if the innermost field is an array.
783 bool inArray() const {
784 if (isBlockPointer())
785 return view().inArray();
786 if (isStringPointer())
787 return true;
788 return false;
789 }
790 bool inUnion() const {
791 if (isBlockPointer() && BS.Base >= sizeof(InlineDescriptor))
792 return view().inUnion();
793 return false;
794 };
795
796 /// Checks if the structure is a primitive array.
797 bool inPrimitiveArray() const {
798 if (isBlockPointer())
799 return view().inPrimitiveArray();
800 return false;
801 }
802 /// Checks if the structure is an array of unknown size.
803 bool isUnknownSizeArray() const {
804 if (isBlockPointer())
805 return getFieldDesc()->isUnknownSizeArray();
806 if (isOpaquePointer())
807 return Opaque.isUnknownSizeArray();
808 return false;
809 }
810 /// Checks if the pointer points to an array.
811 bool isArrayElement() const {
812 if (!isBlockPointer())
813 return false;
814
815 return view().isArrayElement();
816 }
817 /// Pointer points directly to a block.
818 bool isRoot() const {
819 if (isZero())
820 return true;
821 if (isBlockPointer())
822 return view().isRoot();
823 if (isOpaquePointer())
824 return Opaque.isRoot();
825 return true;
826 }
827 /// If this pointer has an InlineDescriptor we can use to initialize.
828 bool canBeInitialized() const {
829 if (!isBlockPointer())
830 return false;
831
832 return BS.Pointee && BS.Base > 0;
833 }
834
835 [[nodiscard]] const BlockPointer &asBlockPointer() const {
836 assert(isBlockPointer());
837 return BS;
838 }
839 [[nodiscard]] const IntPointer &asIntPointer() const {
840 assert(isIntegralPointer());
841 return Int;
842 }
843 [[nodiscard]] const FunctionPointer &asFunctionPointer() const {
844 assert(isFunctionPointer());
845 return Fn;
846 }
847 [[nodiscard]] const TypeidPointer &asTypeidPointer() const {
848 assert(isTypeidPointer());
849 return Typeid;
850 }
851 [[nodiscard]] const StringPointer &asStringPointer() const {
852 assert(isStringPointer());
853 return Str;
854 }
855 [[nodiscard]] const OpaquePointer &asOpaquePointer() const {
856 assert(isOpaquePointer());
857 return Opaque;
858 }
859
860 bool isBlockPointer() const { return StorageKind == Storage::Block; }
861 bool isIntegralPointer() const { return StorageKind == Storage::Int; }
862 bool isFunctionPointer() const { return StorageKind == Storage::Fn; }
863 bool isTypeidPointer() const { return StorageKind == Storage::Typeid; }
864 bool isStringPointer() const { return StorageKind == Storage::String; }
865 bool isOpaquePointer() const { return StorageKind == Storage::Opaque; }
866
867 /// Returns the record descriptor of a class.
868 const Record *getRecord() const {
869 if (!isBlockPointer())
870 return nullptr;
871 return view().getRecord();
872 }
873 /// Returns the element record type, if this is a non-primive array.
874 const Record *getElemRecord() const { return view().getElemRecord(); }
875 /// Returns the field information.
876 const FieldDecl *getField() const {
877 if (const Descriptor *FD = getFieldDesc())
878 return FD->asFieldDecl();
879 return nullptr;
880 }
881
882 /// Checks if the storage is extern.
883 bool isExtern() const {
884 if (isBlockPointer())
885 return view().isExtern();
886 return false;
887 }
888 /// Checks if the storage is static.
889 bool isStatic() const {
890 if (!isBlockPointer())
891 return true;
892 assert(BS.Pointee);
893 return BS.Pointee->isStatic();
894 }
895 /// Checks if the storage is temporary.
896 bool isTemporary() const {
897 if (isBlockPointer()) {
898 assert(BS.Pointee);
899 return BS.Pointee->isTemporary();
900 }
901 return false;
902 }
903 /// Checks if the storage has been dynamically allocated.
904 bool isDynamic() const {
905 if (isBlockPointer()) {
906 assert(BS.Pointee);
907 return BS.Pointee->isDynamic();
908 }
909 return false;
910 }
911 /// Checks if the storage is a static temporary.
912 bool isStaticTemporary() const { return isStatic() && isTemporary(); }
913
914 /// Checks if the field is mutable.
915 bool isMutable() const {
916 if (!isBlockPointer())
917 return false;
918 return view().isMutable();
919 }
920
921 bool isWeak() const {
922 if (isFunctionPointer()) {
923 if (!Fn.Func || !Fn.Func->getDecl())
924 return false;
925
926 return Fn.Func->getDecl()->isWeak();
927 }
928
929 if (isOpaquePointer()) {
930 if (const VarDecl *BaseDecl = Opaque.getBaseVarDecl())
931 return BaseDecl->isWeak();
932 return false;
933 }
934 if (!isBlockPointer())
935 return false;
936
937 assert(isBlockPointer());
938 return BS.Pointee->isWeak();
939 }
940 /// Checks if the object is active.
941 bool isActive() const {
942 if (!isBlockPointer())
943 return true;
944 return view().isActive();
945 }
946 /// Checks if a structure is a base class.
947 bool isBaseClass() const { return view().isBaseClass(); }
948 bool isVirtualBaseClass() const { return view().isVirtualBaseClass(); }
949
950 /// Checks if the pointer points to a dummy value.
951 bool isDummy() const { return isOpaquePointer(); }
952
953 /// Checks if an object or a subfield is mutable.
954 bool isConst() const {
955 if (isIntegralPointer())
956 return true;
957 if (isStringPointer())
958 return true;
959 if (!isBlockPointer())
960 return false;
961 return view().isConst();
962 }
963 bool isConstInMutable() const {
964 if (!isBlockPointer())
965 return false;
966 return isRoot() ? false : getInlineDesc()->IsConstInMutable;
967 }
968
969 /// Checks if an object or a subfield is volatile.
970 bool isVolatile() const {
971 if (!isBlockPointer())
972 return false;
973 return view().isVolatile();
974 }
975
976 /// Returns the declaration ID.
977 UnsignedOrNone getDeclID() const {
978 if (isBlockPointer()) {
979 assert(BS.Pointee);
980 return BS.Pointee->getDeclID();
981 }
982 return std::nullopt;
983 }
984
985 /// Returns the byte offset from the start.
986 uint64_t getByteOffset() const {
987 if (isBlockPointer())
988 return isOnePastEnd() ? PtrView::PastEndMark : Offset;
989 return Offset;
990 }
991
992 /// Returns the number of elements.
993 unsigned getNumElems() const {
994 if (isStringPointer())
995 return Str.getLiteral()->getLength() + 1;
996 if (isOpaquePointer()) {
997 const ArrayType *AT =
998 Opaque.getSurroundingArray()->getAsArrayTypeUnsafe();
999 if (const auto *CAT = dyn_cast_if_present<ConstantArrayType>(Val: AT))
1000 return CAT->getZExtSize();
1001 }
1002 if (!isBlockPointer())
1003 return ~0u;
1004 return view().getNumElems();
1005 }
1006
1007 const Block *block() const {
1008 assert(isBlockPointer());
1009 return BS.Pointee;
1010 }
1011
1012 /// If backed by actual data (i.e. a block or string pointer), return
1013 /// an address to that data.
1014 const std::byte *getRawAddress() const {
1015 if (isStringPointer()) {
1016 const StringLiteral *Lit = Str.getLiteral();
1017 return reinterpret_cast<const std::byte *>(
1018 Lit->getBytes().data() + (Offset * Lit->getCharByteWidth()));
1019 }
1020 assert(isBlockPointer());
1021 return BS.Pointee->rawData() + Offset;
1022 }
1023
1024 /// Returns the index into an array.
1025 int64_t getIndex() const {
1026 if (isStringPointer())
1027 return Offset;
1028 if (isOpaquePointer()) {
1029 if (Opaque.isArrayElement())
1030 return Opaque.Path[Opaque.PathLength - 1].Index;
1031 return 0;
1032 }
1033 if (!isBlockPointer())
1034 return getIntegerRepresentation();
1035
1036 return view().getIndex();
1037 }
1038
1039 /// Checks if the index is one past end.
1040 bool isOnePastEnd() const {
1041 if (isStringPointer())
1042 return Offset == (Str.getLiteral()->getLength() + 1);
1043 if (isOpaquePointer())
1044 return Opaque.isOnePastEndOrElementPastEnd();
1045
1046 if (!isBlockPointer())
1047 return false;
1048
1049 if (!BS.Pointee)
1050 return false;
1051
1052 return view().isOnePastEnd();
1053 }
1054
1055 /// Checks if the pointer points past the end of the object.
1056 bool isPastEnd() const {
1057 if (isIntegralPointer())
1058 return false;
1059 if (isStringPointer())
1060 return Offset >= (Str.getLiteral()->getLength() + 1);
1061
1062 return !isZero() && Offset > BS.Pointee->getSize();
1063 }
1064
1065 /// Checks if the pointer is an out-of-bounds element pointer.
1066 bool isElementPastEnd() const { return Offset == PtrView::PastEndMark; }
1067
1068 /// Checks if the pointer is pointing to a zero-size array.
1069 bool isZeroSizeArray() const {
1070 if (isFunctionPointer())
1071 return false;
1072 if (isOpaquePointer())
1073 return false; // FIXME: Can actually happen I think?
1074 if (const auto *Desc = getFieldDesc())
1075 return Desc->isZeroSizeArray();
1076 return false;
1077 }
1078
1079 /// Checks whether the pointer can be dereferenced to the given PrimType.
1080 bool canDeref(PrimType T) const {
1081 if (isStringPointer()) {
1082 switch (Str.getLiteral()->getCharByteWidth()) {
1083 case 1:
1084 return T == PT_Sint8 || T == PT_Uint8;
1085 case 2:
1086 return T == PT_Sint16 || T == PT_Uint16;
1087 case 4:
1088 return T == PT_Sint32 || T == PT_Uint32;
1089 }
1090
1091 return false;
1092 }
1093
1094 assert(isBlockPointer());
1095 if (const Descriptor *FieldDesc = getFieldDesc()) {
1096 return (FieldDesc->isPrimitive() || FieldDesc->isPrimitiveArray()) &&
1097 FieldDesc->getPrimType() == T;
1098 }
1099 return false;
1100 }
1101
1102 /// Dereferences the pointer, if it's live.
1103 template <typename T> T &deref() const {
1104 assert(isLive() && "Invalid pointer");
1105 assert(isBlockPointer());
1106 assert(BS.Pointee);
1107 assert(isDereferencable());
1108 assert(Offset + sizeof(T) <= BS.Pointee->getSize());
1109 return view().deref<T>();
1110 }
1111
1112 template <typename T> T load() const {
1113 assert(isLive() && "Invalid pointer");
1114 if (isBlockPointer()) {
1115 assert(BS.Pointee);
1116 assert(isDereferencable());
1117 assert(Offset + sizeof(T) <= BS.Pointee->getSize());
1118 return view().deref<T>();
1119 }
1120
1121 if (isStringPointer()) {
1122 const StringLiteral *Lit = Str.getLiteral();
1123
1124 if constexpr (isFixedSizeIntegralType<T>()) {
1125 // The literal does not include the nul byte.
1126 if (Offset >= Lit->getLength())
1127 return T::from('\0');
1128 return T::from(Lit->getCodeUnit(I: Offset));
1129 } else if constexpr (std::is_integral_v<T>) {
1130 if (Offset >= Lit->getLength())
1131 return '\0';
1132 return Lit->getCodeUnit(I: Offset);
1133 }
1134 }
1135
1136 llvm_unreachable("Unexpected pointer type in load()");
1137 }
1138
1139 /// Dereferences the element at index \p I.
1140 /// This is equivalent to atIndex(I).deref<T>().
1141 template <typename T> T &elem(unsigned I) const {
1142 assert(isLive() && "Invalid pointer");
1143 assert(isBlockPointer());
1144 assert(BS.Pointee);
1145 assert(isDereferencable());
1146 assert(getFieldDesc()->isPrimitiveArray());
1147 assert(I < getFieldDesc()->getNumElems());
1148
1149 return view().elem<T>(I);
1150 }
1151
1152 template <typename T> T loadElem(unsigned I) const {
1153 assert(isLive() && "Invalid pointer");
1154 if (isBlockPointer()) {
1155 assert(BS.Pointee);
1156 assert(isDereferencable());
1157 assert(getFieldDesc()->isPrimitiveArray());
1158 assert(I < getFieldDesc()->getNumElems());
1159
1160 return view().elem<T>(I);
1161 }
1162
1163 assert(isStringPointer());
1164 const StringLiteral *Lit = Str.getLiteral();
1165 unsigned Index = Offset + I;
1166 if constexpr (isFixedSizeIntegralType<T>()) {
1167 // The literal does not include the nul byte.
1168 if (Index >= Lit->getLength())
1169 return T::from('\0');
1170 return T::from(Lit->getCodeUnit(I: Index));
1171 } else if constexpr (std::is_integral_v<T>) {
1172 if (Index >= Lit->getLength())
1173 return '\0';
1174 return Lit->getCodeUnit(I: Index);
1175 }
1176 llvm_unreachable("Unexpected pointer type in loadElem()");
1177 }
1178
1179 bool isConstexprUnknown() const {
1180 if (isOpaquePointer())
1181 return Opaque.isConstexprUnknown();
1182 if (isBlockPointer())
1183 return getDeclDesc()->IsConstexprUnknown;
1184 return false;
1185 }
1186
1187 /// Whether this block can be read from at all. This is only true for
1188 /// block pointers that point to a valid location inside that block.
1189 bool isDereferencable() const {
1190 if (!isBlockPointer())
1191 return false;
1192 if (isDummy())
1193 return false;
1194 if (isConstexprUnknown())
1195 return false;
1196 if (isPastEnd())
1197 return false;
1198
1199 return true;
1200 }
1201
1202 bool isReadablePointerType() const {
1203 return StorageKind == Storage::Block || StorageKind == Storage::String;
1204 }
1205
1206 /// Initializes a field.
1207 void initialize() const {
1208 if (!isBlockPointer())
1209 return;
1210 view().initialize();
1211 }
1212 /// Initialized the given element of a primitive array.
1213 void initializeElement(unsigned Index) const {
1214 view().initializeElement(Index);
1215 }
1216 /// Initialize all elements of a primitive array at once. This can be
1217 /// used in situations where we *know* we have initialized *all* elements
1218 /// of a primtive array.
1219 void initializeAllElements() const;
1220 /// Checks if an object was initialized.
1221 bool isInitialized() const {
1222 if (!isBlockPointer())
1223 return true;
1224
1225 return view().isInitialized();
1226 }
1227
1228 /// Like isInitialized(), but for primitive arrays.
1229 bool isElementInitialized(unsigned Index) const {
1230 if (!isBlockPointer())
1231 return true;
1232
1233 return view().isElementInitialized(Index);
1234 }
1235 bool allElementsInitialized() const {
1236 assert(getFieldDesc()->isPrimitiveArray());
1237 assert(isArrayRoot());
1238 return view().allElementsInitialized();
1239 }
1240 bool allElementsAlive() const;
1241 bool isElementAlive(unsigned Index) const;
1242
1243 /// Activates a field.
1244 void activate() const { view().activate(); }
1245 /// Deactivates an entire strurcutre.
1246 void deactivate() const {
1247 // TODO: this only appears in constructors, so nothing to deactivate.
1248 }
1249
1250 Lifetime getLifetime() const {
1251 if (!isBlockPointer())
1252 return Lifetime::Started;
1253 return view().getLifetime();
1254 }
1255
1256 /// Start the lifetime of this pointer. This works for pointer with an
1257 /// InlineDescriptor as well as primitive array elements. Pointers are usually
1258 /// alive by default, unless the underlying object has been allocated with
1259 /// std::allocator. This function is used by std::construct_at.
1260 void startLifetime() const { setLifeState(Lifetime::Started); }
1261 /// Ends the lifetime of the pointer. This works for pointer with an
1262 /// InlineDescriptor as well as primitive array elements. This function is
1263 /// used by std::destroy_at.
1264 void endLifetime() const { setLifeState(Lifetime::Ended); }
1265
1266 void setLifeState(Lifetime L) const {
1267 if (!isBlockPointer())
1268 return;
1269 view().setLifeState(L);
1270 };
1271
1272 /// Strip base casts from this Pointer.
1273 /// The result is either a root pointer or something
1274 /// that isn't a base class anymore.
1275 [[nodiscard]] Pointer stripBaseCasts() const {
1276 return Pointer(view().stripBaseCasts());
1277 }
1278
1279 /// Compare two pointers.
1280 ComparisonCategoryResult compare(const Pointer &Other) const {
1281 if (!hasSameBase(A: *this, B: Other))
1282 return ComparisonCategoryResult::Unordered;
1283
1284 if (Offset < Other.Offset)
1285 return ComparisonCategoryResult::Less;
1286 if (Offset > Other.Offset)
1287 return ComparisonCategoryResult::Greater;
1288
1289 return ComparisonCategoryResult::Equal;
1290 }
1291
1292 /// Checks if two pointers are comparable.
1293 static bool hasSameBase(const Pointer &A, const Pointer &B);
1294 /// Checks if two pointers can be subtracted.
1295 static bool elemsOfSameArray(const Pointer &A, const Pointer &B);
1296 /// Checks if both given pointers point to the same block.
1297 static bool pointToSameBlock(const Pointer &A, const Pointer &B);
1298
1299 static std::optional<std::pair<PtrView, PtrView>>
1300 computeSplitPoint(const Pointer &A, const Pointer &B);
1301
1302 /// Whether this points to a block that's been created for a "literal lvalue",
1303 /// i.e. a non-MaterializeTemporaryExpr Expr.
1304 bool pointsToLiteral() const;
1305 /// Whether this points to a block created for an AddrLabelExpr.
1306 bool pointsToLabel() const;
1307 /// Returns the AddrLabelExpr the Pointer points to, if any.
1308 const AddrLabelExpr *getPointedToLabel() const {
1309 return dyn_cast_if_present<AddrLabelExpr>(Val: getRootExpr());
1310 }
1311
1312 /// Prints the pointer.
1313 void print(llvm::raw_ostream &OS) const;
1314
1315 /// Compute an integer that can be used to compare this pointer to
1316 /// another one. This is usually NOT the same as the pointer offset
1317 /// regarding the AST record layout.
1318 std::optional<size_t>
1319 computeOffsetForComparison(const ASTContext &ASTCtx) const;
1320 /// Compute the pointer offset as given by the ASTRecordLayout.
1321 /// Returns the result in bytes.
1322 std::optional<size_t> computeLayoutOffset(const ASTContext &ASTCtx) const;
1323
1324private:
1325 friend class Block;
1326 friend class DeadBlock;
1327 friend class MemberPointer;
1328 friend class InterpState;
1329 friend class DynamicAllocator;
1330 friend class Program;
1331
1332 /// Returns the embedded descriptor preceding a field.
1333 InlineDescriptor *getInlineDesc() const {
1334 assert(isBlockPointer());
1335 assert(BS.Base != sizeof(GlobalInlineDescriptor));
1336 assert(BS.Base <= BS.Pointee->getSize());
1337 assert(BS.Base >= sizeof(InlineDescriptor));
1338 return getDescriptor(Offset: BS.Base);
1339 }
1340
1341 /// Returns a descriptor at a given offset.
1342 InlineDescriptor *getDescriptor(unsigned Offset) const {
1343 assert(Offset != 0 && "Not a nested pointer");
1344 assert(isBlockPointer());
1345 assert(!isZero());
1346 return view().getDescriptor(Offset);
1347 }
1348
1349 /// Returns a reference to the InitMapPtr which stores the initialization map.
1350 InitMapPtr &getInitMap() const {
1351 assert(isBlockPointer());
1352 assert(!isZero());
1353 return view().getInitMap();
1354 }
1355
1356 /// Offset into the storage.
1357 uint64_t Offset = 0;
1358
1359 Storage StorageKind = Storage::Int;
1360 union {
1361 IntPointer Int;
1362 BlockPointer BS;
1363 FunctionPointer Fn;
1364 TypeidPointer Typeid;
1365 StringPointer Str;
1366 OpaquePointer Opaque;
1367 };
1368};
1369
1370inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Pointer &P) {
1371 P.print(OS);
1372 OS << ' ';
1373 if (P.isZero())
1374 return OS;
1375
1376 if (const Descriptor *D = P.getFieldDesc())
1377 D->dump(OS);
1378 if (P.isArrayElement()) {
1379 if (P.isOnePastEnd())
1380 OS << " one-past-the-end";
1381 else {
1382 OS << ' ';
1383 std::string Indices;
1384 llvm::raw_string_ostream SS(Indices);
1385 Pointer K = P;
1386 while (K.isArrayElement()) {
1387 SS << ']' << K.expand().getIndex() << '[';
1388 K = K.expand().getArray();
1389 }
1390 std::reverse(first: Indices.begin(), last: Indices.end());
1391 OS << Indices;
1392 }
1393 } else if (P.isBlockPointer() && P.isArrayRoot())
1394 OS << " arrayroot";
1395
1396 if (P.isDummy())
1397 OS << " dummy";
1398 if (!P.isLive())
1399 OS << " dead";
1400 if (P.isBlockPointer() && P.isBaseClass())
1401 OS << " base-class";
1402 return OS;
1403}
1404
1405} // namespace interp
1406} // namespace clang
1407
1408#endif
1409