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 isDummy() const { return Pointee && Pointee->isDummy(); }
46 bool isActive() const { return isRoot() || getInlineDesc()->IsActive; }
47 bool isArrayRoot() const { return inArray() && Offset == Base; }
48 bool isElementPastEnd() const { return Offset == PastEndMark; }
49 bool isZeroSizeArray() const { return getFieldDesc()->isZeroSizeArray(); }
50 bool isMutable() const {
51 return !isRoot() && getInlineDesc()->IsFieldMutable;
52 }
53 bool inUnion() const { return getInlineDesc()->InUnion; };
54 bool inArray() const { return getFieldDesc()->IsArray; }
55 bool inPrimitiveArray() const { return getFieldDesc()->isPrimitiveArray(); }
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
395enum class Storage { Int, Block, Fn, Typeid, String };
396
397/// A pointer to a memory block, live or dead.
398///
399/// This object can be allocated into interpreter stack frames. If pointing to
400/// a live block, it is a link in the chain of pointers pointing to the block.
401///
402/// In the simplest form, a Pointer has a Block* (the pointee) and both Base
403/// and Offset are 0, which means it will point to raw data.
404///
405/// The Base field is used to access metadata about the data. For primitive
406/// arrays, the Base is followed by an InitMap. In a variety of cases, the
407/// Base is preceded by an InlineDescriptor, which is used to track the
408/// initialization state, among other things.
409///
410/// The Offset field is used to access the actual data. In other words, the
411/// data the pointer decribes can be found at
412/// Pointee->rawData() + Pointer.Offset.
413///
414/// \verbatim
415/// Pointee Offset
416/// │ │
417/// │ │
418/// ▼ ▼
419/// ┌───────┬────────────┬─────────┬────────────────────────────┐
420/// │ Block │ InlineDesc │ InitMap │ Actual Data │
421/// └───────┴────────────┴─────────┴────────────────────────────┘
422///
423///
424///
425/// Base
426/// \endverbatim
427class Pointer {
428public:
429 Pointer() : StorageKind(Storage::Int), Int{.Ty: nullptr, .Value: 0} {}
430 Pointer(IntPointer &&IntPtr)
431 : StorageKind(Storage::Int), Int(std::move(IntPtr)) {}
432 Pointer(Block *B);
433 Pointer(Block *B, uint64_t BaseAndOffset);
434 Pointer(const Pointer &P);
435 Pointer(Pointer &&P);
436 Pointer(uint64_t Address, const Type *Ty, uint64_t Offset = 0)
437 : Offset(Offset), StorageKind(Storage::Int), Int{.Ty: Ty, .Value: Address} {}
438 Pointer(const Function *F, uint64_t Offset = 0)
439 : Offset(Offset), StorageKind(Storage::Fn), Fn{.Func: F} {}
440 Pointer(const Type *TypePtr, const Type *TypeInfoType, uint64_t Offset = 0)
441 : Offset(Offset), StorageKind(Storage::Typeid) {
442 Typeid.TypePtr = TypePtr;
443 Typeid.TypeInfoType = TypeInfoType;
444 }
445 Pointer(const Expr *Base, unsigned Id)
446 : Offset(0), StorageKind(Storage::String), Str{.Base: Base, .ID: Id} {}
447 Pointer(StringPointer Str, uint64_t Offset = 0)
448 : Offset(Offset), StorageKind(Storage::String), Str(Str) {}
449
450 Pointer(Block *Pointee, unsigned Base, uint64_t Offset);
451 explicit Pointer(PtrView V) : Pointer(V.Pointee, V.Base, V.Offset) {}
452 ~Pointer();
453
454 Pointer &operator=(const Pointer &P);
455 Pointer &operator=(Pointer &&P);
456
457 /// Equality operators are just for tests.
458 bool operator==(const Pointer &P) const {
459 if (P.StorageKind != StorageKind)
460 return false;
461 if (isIntegralPointer())
462 return P.Int.Value == Int.Value && P.Int.Ty == Int.Ty &&
463 P.Offset == Offset;
464
465 if (isFunctionPointer())
466 return P.Fn.Func == Fn.Func && P.Offset == Offset;
467 if (isStringPointer())
468 return Str.Base == P.Str.Base && Offset == P.Offset;
469
470 return P.view() == view();
471 }
472
473 bool operator!=(const Pointer &P) const { return !(P == *this); }
474
475 /// Converts the pointer to an APValue.
476 APValue toAPValue(const ASTContext &ASTCtx) const;
477
478 /// Converts the pointer to a string usable in diagnostics.
479 std::string toDiagnosticString(const ASTContext &Ctx) const;
480
481 uint64_t getIntegerRepresentation() const {
482 if (isIntegralPointer())
483 return Int.Value + (Offset * elemSize());
484 if (isFunctionPointer())
485 return reinterpret_cast<uint64_t>(Fn.Func) + Offset;
486 return reinterpret_cast<uint64_t>(BS.Pointee) + Offset;
487 }
488
489 PtrView view() const {
490 assert(isBlockPointer());
491 return PtrView{.Pointee: BS.Pointee, .Base: BS.Base, .Offset: Offset};
492 }
493
494 /// Converts the pointer to an APValue that is an rvalue.
495 std::optional<APValue> toRValue(const Context &Ctx,
496 QualType ResultType) const;
497
498 /// Offsets a pointer inside an array.
499 [[nodiscard]] Pointer atIndex(uint64_t Idx) const {
500 switch (StorageKind) {
501 case Storage::Int:
502 return Pointer(Int.Value, Int.Ty, Idx);
503 case Storage::Block:
504 return Pointer(view().atIndex(Idx));
505 case Storage::Fn:
506 return Pointer(Fn.Func, Idx);
507 case Storage::String:
508 return Pointer(Str, Idx);
509 default:
510 llvm_unreachable("Unexpected pointer type in atIndex()");
511 }
512 }
513
514 /// Creates a pointer to a field.
515 [[nodiscard]] Pointer atField(unsigned Off) const {
516 return Pointer(view().atField(Offset: Off));
517 }
518
519 /// Subtract the given offset from the current Base and Offset
520 /// of the pointer.
521 [[nodiscard]] Pointer atFieldSub(unsigned Off) const {
522 assert(Offset >= Off);
523 unsigned O = Offset - Off;
524 return Pointer(BS.Pointee, O, O);
525 }
526
527 /// Restricts the scope of an array element pointer.
528 [[nodiscard]] Pointer narrow() const {
529 if (!isBlockPointer())
530 return *this;
531 return Pointer(view().narrow());
532 }
533
534 /// Expands a pointer to the containing array, undoing narrowing.
535 [[nodiscard]] Pointer expand() const {
536 if (!isBlockPointer())
537 return *this;
538 return Pointer(view().expand());
539 }
540
541 /// Checks if the pointer is null.
542 bool isZero() const {
543 switch (StorageKind) {
544 case Storage::Int:
545 return Int.Value == 0 && Offset == 0;
546 case Storage::Block:
547 return BS.Pointee == nullptr;
548 case Storage::Fn:
549 return !Fn.Func;
550 case Storage::Typeid:
551 case Storage::String:
552 return false;
553 }
554 llvm_unreachable("Unknown clang::interp::Storage enum");
555 }
556 /// Checks if the pointer is live.
557 bool isLive() const {
558 if (!isBlockPointer())
559 return true;
560 return view().isLive();
561 }
562 /// Checks if the item is a field in an object.
563 bool isField() const {
564 if (!isBlockPointer())
565 return false;
566
567 return view().isField();
568 }
569
570 /// Accessor for information about the declaration site.
571 const Descriptor *getDeclDesc() const {
572 if (!isBlockPointer())
573 return nullptr;
574
575 assert(isBlockPointer());
576 assert(BS.Pointee);
577 return BS.Pointee->Desc;
578 }
579 SourceLocation getDeclLoc() const { return getDeclDesc()->getLocation(); }
580
581 /// Returns the expression or declaration the pointer has been created for.
582 DeclOrExpr getSource() const {
583 if (isBlockPointer())
584 return getDeclDesc()->getSource();
585 if (isFunctionPointer()) {
586 const Function *F = Fn.Func;
587 return F ? F->getDecl() : DeclOrExpr();
588 }
589 llvm_unreachable("Unsupported pointer type in getSource()");
590 return DeclOrExpr();
591 }
592
593 /// Returns a pointer to the object of which this pointer is a field.
594 [[nodiscard]] Pointer getBase() const { return Pointer(view().getBase()); }
595 /// Returns the parent array.
596 [[nodiscard]] Pointer getArray() const { return Pointer(view().getArray()); }
597
598 /// Accessors for information about the innermost field.
599 const Descriptor *getFieldDesc() const {
600 if (isIntegralPointer())
601 return nullptr;
602
603 if (isRoot())
604 return getDeclDesc();
605 return getInlineDesc()->Desc;
606 }
607
608 /// Returns the type of the innermost field.
609 QualType getType() const {
610 switch (StorageKind) {
611 case Storage::Int:
612 return Int.getPointeeType();
613 case Storage::Block:
614 return view().getType();
615 case Storage::Fn:
616 return Fn.Func->getDecl()->getType();
617 case Storage::Typeid:
618 return QualType(Typeid.TypeInfoType, 0);
619 case Storage::String:
620 if (Str.Decayed)
621 return Str.getLiteral()
622 ->getType()
623 ->getAsArrayTypeUnsafe()
624 ->getElementType();
625 return Str.getLiteral()->getType();
626 }
627 llvm_unreachable("Unhandled StorageKind");
628 }
629
630 const VarDecl *getRootVarDecl() const;
631 const Expr *getRootExpr() const;
632
633 [[nodiscard]] Pointer getDeclPtr() const { return Pointer(BS.Pointee); }
634
635 /// Returns the element size of the innermost field.
636 size_t elemSize() const {
637 if (isIntegralPointer()) {
638 // FIXME: Remove this and handle int ptrs specially?
639 return 1;
640 }
641 if (isStringPointer())
642 return Str.getLiteral()->getCharByteWidth();
643
644 return view().elemSize();
645 }
646 /// Returns the total size of the innermost field.
647 size_t getSize() const {
648 assert(isBlockPointer());
649 return getFieldDesc()->getSize();
650 }
651
652 /// Returns the offset into an array.
653 unsigned getOffset() const {
654 assert(Offset != PtrView::PastEndMark && "invalid offset");
655 return view().getOffset();
656 }
657
658 /// Whether this array refers to an array, but not
659 /// to the first element.
660 bool isArrayRoot() const { return view().isArrayRoot(); }
661
662 /// Checks if the innermost field is an array.
663 bool inArray() const {
664 if (isBlockPointer())
665 return view().inArray();
666 if (isStringPointer())
667 return true;
668 return false;
669 }
670 bool inUnion() const {
671 if (isBlockPointer() && BS.Base >= sizeof(InlineDescriptor))
672 return view().inUnion();
673 return false;
674 };
675
676 /// Checks if the structure is a primitive array.
677 bool inPrimitiveArray() const {
678 if (isBlockPointer())
679 return view().inPrimitiveArray();
680 return false;
681 }
682 /// Checks if the structure is an array of unknown size.
683 bool isUnknownSizeArray() const {
684 if (!isBlockPointer())
685 return false;
686 return getFieldDesc()->isUnknownSizeArray();
687 }
688 /// Checks if the pointer points to an array.
689 bool isArrayElement() const {
690 if (!isBlockPointer())
691 return false;
692
693 return view().isArrayElement();
694 }
695 /// Pointer points directly to a block.
696 bool isRoot() const {
697 if (isZero() || !isBlockPointer())
698 return true;
699 return view().isRoot();
700 }
701 /// If this pointer has an InlineDescriptor we can use to initialize.
702 bool canBeInitialized() const {
703 if (!isBlockPointer())
704 return false;
705
706 return BS.Pointee && BS.Base > 0;
707 }
708
709 [[nodiscard]] const BlockPointer &asBlockPointer() const {
710 assert(isBlockPointer());
711 return BS;
712 }
713 [[nodiscard]] const IntPointer &asIntPointer() const {
714 assert(isIntegralPointer());
715 return Int;
716 }
717 [[nodiscard]] const FunctionPointer &asFunctionPointer() const {
718 assert(isFunctionPointer());
719 return Fn;
720 }
721 [[nodiscard]] const TypeidPointer &asTypeidPointer() const {
722 assert(isTypeidPointer());
723 return Typeid;
724 }
725 [[nodiscard]] const StringPointer &asStringPointer() const {
726 assert(isStringPointer());
727 return Str;
728 }
729
730 bool isBlockPointer() const { return StorageKind == Storage::Block; }
731 bool isIntegralPointer() const { return StorageKind == Storage::Int; }
732 bool isFunctionPointer() const { return StorageKind == Storage::Fn; }
733 bool isTypeidPointer() const { return StorageKind == Storage::Typeid; }
734 bool isStringPointer() const { return StorageKind == Storage::String; }
735
736 /// Returns the record descriptor of a class.
737 const Record *getRecord() const {
738 if (!isBlockPointer())
739 return nullptr;
740 return view().getRecord();
741 }
742 /// Returns the element record type, if this is a non-primive array.
743 const Record *getElemRecord() const { return view().getElemRecord(); }
744 /// Returns the field information.
745 const FieldDecl *getField() const {
746 if (const Descriptor *FD = getFieldDesc())
747 return FD->asFieldDecl();
748 return nullptr;
749 }
750
751 /// Checks if the storage is extern.
752 bool isExtern() const {
753 if (isBlockPointer())
754 return BS.Pointee && BS.Pointee->isExtern();
755 return false;
756 }
757 /// Checks if the storage is static.
758 bool isStatic() const {
759 if (!isBlockPointer())
760 return true;
761 assert(BS.Pointee);
762 return BS.Pointee->isStatic();
763 }
764 /// Checks if the storage is temporary.
765 bool isTemporary() const {
766 if (isBlockPointer()) {
767 assert(BS.Pointee);
768 return BS.Pointee->isTemporary();
769 }
770 return false;
771 }
772 /// Checks if the storage has been dynamically allocated.
773 bool isDynamic() const {
774 if (isBlockPointer()) {
775 assert(BS.Pointee);
776 return BS.Pointee->isDynamic();
777 }
778 return false;
779 }
780 /// Checks if the storage is a static temporary.
781 bool isStaticTemporary() const { return isStatic() && isTemporary(); }
782
783 /// Checks if the field is mutable.
784 bool isMutable() const {
785 if (!isBlockPointer())
786 return false;
787 return view().isMutable();
788 }
789
790 bool isWeak() const {
791 if (isFunctionPointer()) {
792 if (!Fn.Func || !Fn.Func->getDecl())
793 return false;
794
795 return Fn.Func->getDecl()->isWeak();
796 }
797 if (!isBlockPointer())
798 return false;
799
800 assert(isBlockPointer());
801 return BS.Pointee->isWeak();
802 }
803 /// Checks if the object is active.
804 bool isActive() const {
805 if (!isBlockPointer())
806 return true;
807 return view().isActive();
808 }
809 /// Checks if a structure is a base class.
810 bool isBaseClass() const { return view().isBaseClass(); }
811 bool isVirtualBaseClass() const { return view().isVirtualBaseClass(); }
812
813 /// Checks if the pointer points to a dummy value.
814 bool isDummy() const {
815 if (!isBlockPointer())
816 return false;
817 return view().isDummy();
818 }
819
820 /// Checks if an object or a subfield is mutable.
821 bool isConst() const {
822 if (isIntegralPointer())
823 return true;
824 if (isStringPointer())
825 return true;
826 return view().isConst();
827 }
828 bool isConstInMutable() const {
829 if (!isBlockPointer())
830 return false;
831 return isRoot() ? false : getInlineDesc()->IsConstInMutable;
832 }
833
834 /// Checks if an object or a subfield is volatile.
835 bool isVolatile() const {
836 if (!isBlockPointer())
837 return false;
838 return isRoot() ? getDeclDesc()->IsVolatile : getInlineDesc()->IsVolatile;
839 }
840
841 /// Returns the declaration ID.
842 UnsignedOrNone getDeclID() const {
843 if (isBlockPointer()) {
844 assert(BS.Pointee);
845 return BS.Pointee->getDeclID();
846 }
847 return std::nullopt;
848 }
849
850 /// Returns the byte offset from the start.
851 uint64_t getByteOffset() const {
852 if (isIntegralPointer())
853 return Int.Value + Offset;
854 if (isTypeidPointer())
855 return reinterpret_cast<uintptr_t>(Typeid.TypePtr) + Offset;
856 if (isOnePastEnd())
857 return PtrView::PastEndMark;
858 return Offset;
859 }
860
861 uint64_t getRawOffset() const { return Offset; }
862
863 /// Returns the number of elements.
864 unsigned getNumElems() const {
865 if (isStringPointer())
866 return Str.getLiteral()->getLength() + 1;
867 if (!isBlockPointer())
868 return ~0u;
869 return view().getNumElems();
870 }
871
872 const Block *block() const { return BS.Pointee; }
873
874 /// If backed by actual data (i.e. a block or string pointer), return
875 /// an address to that data.
876 const std::byte *getRawAddress() const {
877 if (isStringPointer()) {
878 const StringLiteral *Lit = Str.getLiteral();
879 return reinterpret_cast<const std::byte *>(
880 Lit->getBytes().data() + (Offset * Lit->getCharByteWidth()));
881 }
882 assert(isBlockPointer());
883 return BS.Pointee->rawData() + Offset;
884 }
885
886 /// Returns the index into an array.
887 int64_t getIndex() const {
888 if (isStringPointer())
889 return Offset;
890 if (!isBlockPointer())
891 return getIntegerRepresentation();
892
893 return view().getIndex();
894 }
895
896 /// Checks if the index is one past end.
897 bool isOnePastEnd() const {
898 if (isStringPointer())
899 return Offset == (Str.getLiteral()->getLength() + 1);
900 if (!isBlockPointer())
901 return false;
902
903 if (!BS.Pointee)
904 return false;
905
906 return view().isOnePastEnd();
907 }
908
909 /// Checks if the pointer points past the end of the object.
910 bool isPastEnd() const {
911 if (isIntegralPointer())
912 return false;
913 if (isStringPointer())
914 return Offset >= (Str.getLiteral()->getLength() + 1);
915
916 return !isZero() && Offset > BS.Pointee->getSize();
917 }
918
919 /// Checks if the pointer is an out-of-bounds element pointer.
920 bool isElementPastEnd() const { return Offset == PtrView::PastEndMark; }
921
922 /// Checks if the pointer is pointing to a zero-size array.
923 bool isZeroSizeArray() const {
924 if (isFunctionPointer())
925 return false;
926 if (const auto *Desc = getFieldDesc())
927 return Desc->isZeroSizeArray();
928 return false;
929 }
930
931 /// Checks whether the pointer can be dereferenced to the given PrimType.
932 bool canDeref(PrimType T) const {
933 if (isStringPointer()) {
934 switch (Str.getLiteral()->getCharByteWidth()) {
935 case 1:
936 return T == PT_Sint8 || T == PT_Uint8;
937 case 2:
938 return T == PT_Sint16 || T == PT_Uint16;
939 case 4:
940 return T == PT_Sint32 || T == PT_Uint32;
941 }
942
943 return false;
944 }
945
946 assert(isBlockPointer());
947 if (const Descriptor *FieldDesc = getFieldDesc()) {
948 return (FieldDesc->isPrimitive() || FieldDesc->isPrimitiveArray()) &&
949 FieldDesc->getPrimType() == T;
950 }
951 return false;
952 }
953
954 /// Dereferences the pointer, if it's live.
955 template <typename T> T &deref() const {
956 assert(isLive() && "Invalid pointer");
957 assert(isBlockPointer());
958 assert(BS.Pointee);
959 assert(isDereferencable());
960 assert(Offset + sizeof(T) <= BS.Pointee->getSize());
961 return view().deref<T>();
962 }
963
964 template <typename T> T load() const {
965 assert(isLive() && "Invalid pointer");
966 if (isBlockPointer()) {
967 assert(BS.Pointee);
968 assert(isDereferencable());
969 assert(Offset + sizeof(T) <= BS.Pointee->getSize());
970 return view().deref<T>();
971 }
972
973 if (isStringPointer()) {
974 const StringLiteral *Lit = Str.getLiteral();
975
976 if constexpr (isFixedSizeIntegralType<T>()) {
977 // The literal does not include the nul byte.
978 if (Offset >= Lit->getLength())
979 return T::from('\0');
980 return T::from(Lit->getCodeUnit(I: Offset));
981 } else if constexpr (std::is_integral_v<T>) {
982 if (Offset >= Lit->getLength())
983 return '\0';
984 return Lit->getCodeUnit(I: Offset);
985 }
986 }
987
988 llvm_unreachable("Unexpected pointer type in load()");
989 }
990
991 /// Dereferences the element at index \p I.
992 /// This is equivalent to atIndex(I).deref<T>().
993 template <typename T> T &elem(unsigned I) const {
994 assert(isLive() && "Invalid pointer");
995 assert(isBlockPointer());
996 assert(BS.Pointee);
997 assert(isDereferencable());
998 assert(getFieldDesc()->isPrimitiveArray());
999 assert(I < getFieldDesc()->getNumElems());
1000
1001 return view().elem<T>(I);
1002 }
1003
1004 template <typename T> T loadElem(unsigned I) const {
1005 assert(isLive() && "Invalid pointer");
1006 if (isBlockPointer()) {
1007 assert(BS.Pointee);
1008 assert(isDereferencable());
1009 assert(getFieldDesc()->isPrimitiveArray());
1010 assert(I < getFieldDesc()->getNumElems());
1011
1012 return view().elem<T>(I);
1013 }
1014
1015 assert(isStringPointer());
1016 const StringLiteral *Lit = Str.getLiteral();
1017 unsigned Index = Offset + I;
1018 if constexpr (isFixedSizeIntegralType<T>()) {
1019 // The literal does not include the nul byte.
1020 if (Index >= Lit->getLength())
1021 return T::from('\0');
1022 return T::from(Lit->getCodeUnit(I: Index));
1023 } else if constexpr (std::is_integral_v<T>) {
1024 if (Index >= Lit->getLength())
1025 return '\0';
1026 return Lit->getCodeUnit(I: Index);
1027 }
1028 llvm_unreachable("Unexpected pointer type in loadElem()");
1029 }
1030
1031 bool isConstexprUnknown() const {
1032 if (!isBlockPointer())
1033 return false;
1034 return getDeclDesc()->IsConstexprUnknown;
1035 }
1036
1037 /// Whether this block can be read from at all. This is only true for
1038 /// block pointers that point to a valid location inside that block.
1039 bool isDereferencable() const {
1040 if (!isBlockPointer())
1041 return false;
1042 if (isDummy())
1043 return false;
1044 if (isConstexprUnknown())
1045 return false;
1046 if (isPastEnd())
1047 return false;
1048
1049 return true;
1050 }
1051
1052 bool isReadablePointerType() const {
1053 return StorageKind == Storage::Block || StorageKind == Storage::String;
1054 }
1055
1056 /// Initializes a field.
1057 void initialize() const {
1058 if (!isBlockPointer())
1059 return;
1060 view().initialize();
1061 }
1062 /// Initialized the given element of a primitive array.
1063 void initializeElement(unsigned Index) const {
1064 view().initializeElement(Index);
1065 }
1066 /// Initialize all elements of a primitive array at once. This can be
1067 /// used in situations where we *know* we have initialized *all* elements
1068 /// of a primtive array.
1069 void initializeAllElements() const;
1070 /// Checks if an object was initialized.
1071 bool isInitialized() const;
1072 /// Like isInitialized(), but for primitive arrays.
1073 bool isElementInitialized(unsigned Index) const {
1074 if (!isBlockPointer())
1075 return true;
1076
1077 return view().isElementInitialized(Index);
1078 }
1079 bool allElementsInitialized() const {
1080 assert(getFieldDesc()->isPrimitiveArray());
1081 assert(isArrayRoot());
1082 return view().allElementsInitialized();
1083 }
1084 bool allElementsAlive() const;
1085 bool isElementAlive(unsigned Index) const;
1086
1087 /// Activates a field.
1088 void activate() const { view().activate(); }
1089 /// Deactivates an entire strurcutre.
1090 void deactivate() const {
1091 // TODO: this only appears in constructors, so nothing to deactivate.
1092 }
1093
1094 Lifetime getLifetime() const {
1095 if (!isBlockPointer())
1096 return Lifetime::Started;
1097 return view().getLifetime();
1098 }
1099
1100 /// Start the lifetime of this pointer. This works for pointer with an
1101 /// InlineDescriptor as well as primitive array elements. Pointers are usually
1102 /// alive by default, unless the underlying object has been allocated with
1103 /// std::allocator. This function is used by std::construct_at.
1104 void startLifetime() const { setLifeState(Lifetime::Started); }
1105 /// Ends the lifetime of the pointer. This works for pointer with an
1106 /// InlineDescriptor as well as primitive array elements. This function is
1107 /// used by std::destroy_at.
1108 void endLifetime() const { setLifeState(Lifetime::Ended); }
1109
1110 void setLifeState(Lifetime L) const {
1111 if (!isBlockPointer())
1112 return;
1113 view().setLifeState(L);
1114 };
1115
1116 /// Strip base casts from this Pointer.
1117 /// The result is either a root pointer or something
1118 /// that isn't a base class anymore.
1119 [[nodiscard]] Pointer stripBaseCasts() const {
1120 return Pointer(view().stripBaseCasts());
1121 }
1122
1123 /// Compare two pointers.
1124 ComparisonCategoryResult compare(const Pointer &Other) const {
1125 if (!hasSameBase(A: *this, B: Other))
1126 return ComparisonCategoryResult::Unordered;
1127
1128 if (Offset < Other.Offset)
1129 return ComparisonCategoryResult::Less;
1130 if (Offset > Other.Offset)
1131 return ComparisonCategoryResult::Greater;
1132
1133 return ComparisonCategoryResult::Equal;
1134 }
1135
1136 /// Checks if two pointers are comparable.
1137 static bool hasSameBase(const Pointer &A, const Pointer &B);
1138 /// Checks if two pointers can be subtracted.
1139 static bool elemsOfSameArray(const Pointer &A, const Pointer &B);
1140 /// Checks if both given pointers point to the same block.
1141 static bool pointToSameBlock(const Pointer &A, const Pointer &B);
1142
1143 static std::optional<std::pair<PtrView, PtrView>>
1144 computeSplitPoint(const Pointer &A, const Pointer &B);
1145
1146 /// Whether this points to a block that's been created for a "literal lvalue",
1147 /// i.e. a non-MaterializeTemporaryExpr Expr.
1148 bool pointsToLiteral() const;
1149 /// Whether this points to a block created for an AddrLabelExpr.
1150 bool pointsToLabel() const;
1151 /// Returns the AddrLabelExpr the Pointer points to, if any.
1152 const AddrLabelExpr *getPointedToLabel() const {
1153 if (const Descriptor *Desc = getDeclDesc())
1154 return dyn_cast_if_present<AddrLabelExpr>(Val: Desc->asExpr());
1155 return nullptr;
1156 }
1157
1158 /// Prints the pointer.
1159 void print(llvm::raw_ostream &OS) const;
1160
1161 /// Compute an integer that can be used to compare this pointer to
1162 /// another one. This is usually NOT the same as the pointer offset
1163 /// regarding the AST record layout.
1164 std::optional<size_t>
1165 computeOffsetForComparison(const ASTContext &ASTCtx) const;
1166 /// Compute the pointer offset as given by the ASTRecordLayout.
1167 /// Returns the result in bytes.
1168 std::optional<size_t> computeLayoutOffset(const ASTContext &ASTCtx) const;
1169
1170private:
1171 friend class Block;
1172 friend class DeadBlock;
1173 friend class MemberPointer;
1174 friend class InterpState;
1175 friend class DynamicAllocator;
1176 friend class Program;
1177
1178 /// Returns the embedded descriptor preceding a field.
1179 InlineDescriptor *getInlineDesc() const {
1180 assert(isBlockPointer());
1181 assert(BS.Base != sizeof(GlobalInlineDescriptor));
1182 assert(BS.Base <= BS.Pointee->getSize());
1183 assert(BS.Base >= sizeof(InlineDescriptor));
1184 return getDescriptor(Offset: BS.Base);
1185 }
1186
1187 /// Returns a descriptor at a given offset.
1188 InlineDescriptor *getDescriptor(unsigned Offset) const {
1189 assert(Offset != 0 && "Not a nested pointer");
1190 assert(isBlockPointer());
1191 assert(!isZero());
1192 return view().getDescriptor(Offset);
1193 }
1194
1195 /// Returns a reference to the InitMapPtr which stores the initialization map.
1196 InitMapPtr &getInitMap() const {
1197 assert(isBlockPointer());
1198 assert(!isZero());
1199 return view().getInitMap();
1200 }
1201
1202 /// Offset into the storage.
1203 uint64_t Offset = 0;
1204
1205 Storage StorageKind = Storage::Int;
1206 union {
1207 IntPointer Int;
1208 BlockPointer BS;
1209 FunctionPointer Fn;
1210 TypeidPointer Typeid;
1211 StringPointer Str;
1212 };
1213};
1214
1215inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Pointer &P) {
1216 P.print(OS);
1217 OS << ' ';
1218 if (P.isZero())
1219 return OS;
1220
1221 if (const Descriptor *D = P.getFieldDesc())
1222 D->dump(OS);
1223 if (P.isArrayElement()) {
1224 if (P.isOnePastEnd())
1225 OS << " one-past-the-end";
1226 else {
1227 OS << ' ';
1228 std::string Indices;
1229 llvm::raw_string_ostream SS(Indices);
1230 Pointer K = P;
1231 while (K.isArrayElement()) {
1232 SS << ']' << K.expand().getIndex() << '[';
1233 K = K.expand().getArray();
1234 }
1235 std::reverse(first: Indices.begin(), last: Indices.end());
1236 OS << Indices;
1237 }
1238 } else if (P.isBlockPointer() && P.isArrayRoot())
1239 OS << " arrayroot";
1240
1241 if (P.isBlockPointer() && P.block() && P.block()->isDummy())
1242 OS << " dummy";
1243 if (!P.isLive())
1244 OS << " dead";
1245 if (P.isBlockPointer() && P.isBaseClass())
1246 OS << " base-class";
1247 return OS;
1248}
1249
1250} // namespace interp
1251} // namespace clang
1252
1253#endif
1254