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