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 BlockPointer {
37 /// The block the pointer is pointing to.
38 Block *Pointee;
39 /// Start of the current subfield.
40 unsigned Base;
41 /// Previous link in the pointer chain.
42 Pointer *Prev;
43 /// Next link in the pointer chain.
44 Pointer *Next;
45};
46
47struct IntPointer {
48 const Descriptor *Desc;
49 uint64_t Value;
50
51 std::optional<IntPointer> atOffset(const ASTContext &ASTCtx,
52 unsigned Offset) const;
53 IntPointer baseCast(const ASTContext &ASTCtx, unsigned BaseOffset) const;
54};
55
56struct FunctionPointer {
57 const Function *Func;
58};
59
60struct TypeidPointer {
61 const Type *TypePtr;
62 const Type *TypeInfoType;
63};
64
65enum class Storage { Int, Block, Fn, Typeid };
66
67/// A pointer to a memory block, live or dead.
68///
69/// This object can be allocated into interpreter stack frames. If pointing to
70/// a live block, it is a link in the chain of pointers pointing to the block.
71///
72/// In the simplest form, a Pointer has a Block* (the pointee) and both Base
73/// and Offset are 0, which means it will point to raw data.
74///
75/// The Base field is used to access metadata about the data. For primitive
76/// arrays, the Base is followed by an InitMap. In a variety of cases, the
77/// Base is preceded by an InlineDescriptor, which is used to track the
78/// initialization state, among other things.
79///
80/// The Offset field is used to access the actual data. In other words, the
81/// data the pointer decribes can be found at
82/// Pointee->rawData() + Pointer.Offset.
83///
84/// \verbatim
85/// Pointee Offset
86/// │ │
87/// │ │
88/// ▼ ▼
89/// ┌───────┬────────────┬─────────┬────────────────────────────┐
90/// │ Block │ InlineDesc │ InitMap │ Actual Data │
91/// └───────┴────────────┴─────────┴────────────────────────────┘
92///
93///
94///
95/// Base
96/// \endverbatim
97class Pointer {
98private:
99 static constexpr unsigned PastEndMark = ~0u;
100 static constexpr unsigned RootPtrMark = ~0u;
101
102public:
103 Pointer() : StorageKind(Storage::Int), Int{.Desc: nullptr, .Value: 0} {}
104 Pointer(IntPointer &&IntPtr)
105 : StorageKind(Storage::Int), Int(std::move(IntPtr)) {}
106 Pointer(Block *B);
107 Pointer(Block *B, uint64_t BaseAndOffset);
108 Pointer(const Pointer &P);
109 Pointer(Pointer &&P);
110 Pointer(uint64_t Address, const Descriptor *Desc, uint64_t Offset = 0)
111 : Offset(Offset), StorageKind(Storage::Int), Int{.Desc: Desc, .Value: Address} {}
112 Pointer(const Function *F, uint64_t Offset = 0)
113 : Offset(Offset), StorageKind(Storage::Fn), Fn{.Func: F} {}
114 Pointer(const Type *TypePtr, const Type *TypeInfoType, uint64_t Offset = 0)
115 : Offset(Offset), StorageKind(Storage::Typeid) {
116 Typeid.TypePtr = TypePtr;
117 Typeid.TypeInfoType = TypeInfoType;
118 }
119 Pointer(Block *Pointee, unsigned Base, uint64_t Offset);
120 ~Pointer();
121
122 Pointer &operator=(const Pointer &P);
123 Pointer &operator=(Pointer &&P);
124
125 /// Equality operators are just for tests.
126 bool operator==(const Pointer &P) const {
127 if (P.StorageKind != StorageKind)
128 return false;
129 if (isIntegralPointer())
130 return P.Int.Value == Int.Value && P.Int.Desc == Int.Desc &&
131 P.Offset == Offset;
132
133 if (isFunctionPointer())
134 return P.Fn.Func == Fn.Func && P.Offset == Offset;
135
136 assert(isBlockPointer());
137 return P.BS.Pointee == BS.Pointee && P.BS.Base == BS.Base &&
138 P.Offset == Offset;
139 }
140
141 bool operator!=(const Pointer &P) const { return !(P == *this); }
142
143 /// Converts the pointer to an APValue.
144 APValue toAPValue(const ASTContext &ASTCtx) const;
145
146 /// Converts the pointer to a string usable in diagnostics.
147 std::string toDiagnosticString(const ASTContext &Ctx) const;
148
149 uint64_t getIntegerRepresentation() const {
150 if (isIntegralPointer())
151 return Int.Value + (Offset * elemSize());
152 if (isFunctionPointer())
153 return reinterpret_cast<uint64_t>(Fn.Func) + Offset;
154 return reinterpret_cast<uint64_t>(BS.Pointee) + Offset;
155 }
156
157 /// Converts the pointer to an APValue that is an rvalue.
158 std::optional<APValue> toRValue(const Context &Ctx,
159 QualType ResultType) const;
160
161 /// Offsets a pointer inside an array.
162 [[nodiscard]] Pointer atIndex(uint64_t Idx) const {
163 if (isIntegralPointer())
164 return Pointer(Int.Value, Int.Desc, Idx);
165 if (isFunctionPointer())
166 return Pointer(Fn.Func, Idx);
167
168 if (BS.Base == RootPtrMark)
169 return Pointer(BS.Pointee, RootPtrMark, getDeclDesc()->getSize());
170 uint64_t Off = Idx * elemSize();
171 if (getFieldDesc()->ElemDesc)
172 Off += sizeof(InlineDescriptor);
173 else
174 Off += sizeof(InitMapPtr);
175 return Pointer(BS.Pointee, BS.Base, BS.Base + Off);
176 }
177
178 /// Creates a pointer to a field.
179 [[nodiscard]] Pointer atField(unsigned Off) const {
180 assert(isBlockPointer());
181 unsigned Field = Offset + Off;
182 return Pointer(BS.Pointee, Field, Field);
183 }
184
185 /// Subtract the given offset from the current Base and Offset
186 /// of the pointer.
187 [[nodiscard]] Pointer atFieldSub(unsigned Off) const {
188 assert(Offset >= Off);
189 unsigned O = Offset - Off;
190 return Pointer(BS.Pointee, O, O);
191 }
192
193 /// Restricts the scope of an array element pointer.
194 [[nodiscard]] Pointer narrow() const {
195 if (!isBlockPointer())
196 return *this;
197 assert(isBlockPointer());
198 // Null pointers cannot be narrowed.
199 if (isZero() || isUnknownSizeArray())
200 return *this;
201
202 unsigned Base = BS.Base;
203 // Pointer to an array of base types - enter block.
204 if (Base == RootPtrMark)
205 return Pointer(BS.Pointee, sizeof(InlineDescriptor),
206 Offset == 0 ? Offset : PastEndMark);
207
208 if (inArray()) {
209 // Pointer is one past end - magic offset marks that.
210 if (isOnePastEnd())
211 return Pointer(BS.Pointee, Base, PastEndMark);
212
213 if (Offset != Base) {
214 // If we're pointing to a primitive array element, there's nothing to
215 // do.
216 if (inPrimitiveArray())
217 return *this;
218 // Pointer is to a composite array element - enter it.
219 return Pointer(BS.Pointee, Offset, Offset);
220 }
221 }
222
223 // Otherwise, we're pointing to a non-array element or
224 // are already narrowed to a composite array element. Nothing to do.
225 return *this;
226 }
227
228 /// Expands a pointer to the containing array, undoing narrowing.
229 [[nodiscard]] Pointer expand() const {
230 if (!isBlockPointer())
231 return *this;
232 assert(isBlockPointer());
233 Block *Pointee = BS.Pointee;
234
235 if (isElementPastEnd()) {
236 // Revert to an outer one-past-end pointer.
237 unsigned Adjust;
238 if (inPrimitiveArray())
239 Adjust = sizeof(InitMapPtr);
240 else
241 Adjust = sizeof(InlineDescriptor);
242 return Pointer(Pointee, BS.Base, BS.Base + getSize() + Adjust);
243 }
244
245 // Do not step out of array elements.
246 if (BS.Base != Offset)
247 return *this;
248
249 if (isRoot())
250 return Pointer(Pointee, BS.Base, BS.Base);
251
252 // Step into the containing array, if inside one.
253 unsigned Next = BS.Base - getInlineDesc()->Offset;
254 const Descriptor *Desc =
255 (Next == Pointee->getDescriptor()->getMetadataSize())
256 ? getDeclDesc()
257 : getDescriptor(Offset: Next)->Desc;
258 if (!Desc->IsArray)
259 return *this;
260 return Pointer(Pointee, Next, Offset);
261 }
262
263 /// Checks if the pointer is null.
264 bool isZero() const {
265 switch (StorageKind) {
266 case Storage::Int:
267 return Int.Value == 0 && Offset == 0;
268 case Storage::Block:
269 return BS.Pointee == nullptr;
270 case Storage::Fn:
271 return !Fn.Func;
272 case Storage::Typeid:
273 return false;
274 }
275 llvm_unreachable("Unknown clang::interp::Storage enum");
276 }
277 /// Checks if the pointer is live.
278 bool isLive() const {
279 if (!isBlockPointer())
280 return true;
281 return BS.Pointee && !BS.Pointee->isDead();
282 }
283 /// Checks if the item is a field in an object.
284 bool isField() const {
285 if (!isBlockPointer())
286 return false;
287
288 return !isRoot() && getFieldDesc()->asDecl();
289 }
290
291 /// Accessor for information about the declaration site.
292 const Descriptor *getDeclDesc() const {
293 if (isIntegralPointer())
294 return Int.Desc;
295 if (isFunctionPointer() || isTypeidPointer())
296 return nullptr;
297
298 assert(isBlockPointer());
299 assert(BS.Pointee);
300 return BS.Pointee->Desc;
301 }
302 SourceLocation getDeclLoc() const { return getDeclDesc()->getLocation(); }
303
304 /// Returns the expression or declaration the pointer has been created for.
305 DeclTy getSource() const {
306 if (isBlockPointer())
307 return getDeclDesc()->getSource();
308 if (isFunctionPointer()) {
309 const Function *F = Fn.Func;
310 return F ? F->getDecl() : DeclTy();
311 }
312 assert(isIntegralPointer());
313 return Int.Desc ? Int.Desc->getSource() : DeclTy();
314 }
315
316 /// Returns a pointer to the object of which this pointer is a field.
317 [[nodiscard]] Pointer getBase() const {
318 if (BS.Base == RootPtrMark) {
319 assert(Offset == PastEndMark && "cannot get base of a block");
320 return Pointer(BS.Pointee, BS.Base, 0);
321 }
322 unsigned NewBase = BS.Base - getInlineDesc()->Offset;
323 return Pointer(BS.Pointee, NewBase, NewBase);
324 }
325 /// Returns the parent array.
326 [[nodiscard]] Pointer getArray() const {
327 if (BS.Base == RootPtrMark) {
328 assert(Offset != 0 && Offset != PastEndMark && "not an array element");
329 return Pointer(BS.Pointee, BS.Base, 0);
330 }
331 assert(Offset != BS.Base && "not an array element");
332 return Pointer(BS.Pointee, BS.Base, BS.Base);
333 }
334
335 /// Accessors for information about the innermost field.
336 const Descriptor *getFieldDesc() const {
337 if (isIntegralPointer())
338 return Int.Desc;
339
340 if (isRoot())
341 return getDeclDesc();
342 return getInlineDesc()->Desc;
343 }
344
345 /// Returns the type of the innermost field.
346 QualType getType() const {
347 if (isTypeidPointer())
348 return QualType(Typeid.TypeInfoType, 0);
349 if (isFunctionPointer())
350 return Fn.Func->getDecl()->getType();
351
352 if (inPrimitiveArray() && Offset != BS.Base) {
353 // Unfortunately, complex and vector types are not array types in clang,
354 // but they are for us.
355 if (const auto *AT = getFieldDesc()->getType()->getAsArrayTypeUnsafe())
356 return AT->getElementType();
357 if (const auto *CT = getFieldDesc()->getType()->getAs<ComplexType>())
358 return CT->getElementType();
359 if (const auto *CT = getFieldDesc()->getType()->getAs<VectorType>())
360 return CT->getElementType();
361 }
362
363 return getFieldDesc()->getDataElemType();
364 }
365
366 [[nodiscard]] Pointer getDeclPtr() const { return Pointer(BS.Pointee); }
367
368 /// Returns the element size of the innermost field.
369 size_t elemSize() const {
370 if (isIntegralPointer()) {
371 if (!Int.Desc)
372 return 1;
373 return Int.Desc->getElemSize();
374 }
375
376 if (BS.Base == RootPtrMark)
377 return getDeclDesc()->getSize();
378 return getFieldDesc()->getElemSize();
379 }
380 /// Returns the total size of the innermost field.
381 size_t getSize() const {
382 assert(isBlockPointer());
383 return getFieldDesc()->getSize();
384 }
385
386 /// Returns the offset into an array.
387 unsigned getOffset() const {
388 assert(Offset != PastEndMark && "invalid offset");
389 assert(isBlockPointer());
390 if (BS.Base == RootPtrMark)
391 return Offset;
392
393 unsigned Adjust = 0;
394 if (Offset != BS.Base) {
395 if (getFieldDesc()->ElemDesc)
396 Adjust = sizeof(InlineDescriptor);
397 else
398 Adjust = sizeof(InitMapPtr);
399 }
400 return Offset - BS.Base - Adjust;
401 }
402
403 /// Whether this array refers to an array, but not
404 /// to the first element.
405 bool isArrayRoot() const { return inArray() && Offset == BS.Base; }
406
407 /// Checks if the innermost field is an array.
408 bool inArray() const {
409 if (isBlockPointer())
410 return getFieldDesc()->IsArray;
411 return false;
412 }
413 bool inUnion() const {
414 if (isBlockPointer() && BS.Base >= sizeof(InlineDescriptor))
415 return getInlineDesc()->InUnion;
416 return false;
417 };
418
419 /// Checks if the structure is a primitive array.
420 bool inPrimitiveArray() const {
421 if (isBlockPointer())
422 return getFieldDesc()->isPrimitiveArray();
423 return false;
424 }
425 /// Checks if the structure is an array of unknown size.
426 bool isUnknownSizeArray() const {
427 if (!isBlockPointer())
428 return false;
429 return getFieldDesc()->isUnknownSizeArray();
430 }
431 /// Checks if the pointer points to an array.
432 bool isArrayElement() const {
433 if (!isBlockPointer())
434 return false;
435
436 const BlockPointer &BP = BS;
437 if (inArray() && BP.Base != Offset)
438 return true;
439
440 // Might be a narrow()'ed element in a composite array.
441 // Check the inline descriptor.
442 if (BP.Base >= sizeof(InlineDescriptor) && getInlineDesc()->IsArrayElement)
443 return true;
444
445 return false;
446 }
447 /// Pointer points directly to a block.
448 bool isRoot() const {
449 if (isZero() || !isBlockPointer())
450 return true;
451 return (BS.Base == BS.Pointee->getDescriptor()->getMetadataSize() ||
452 BS.Base == 0);
453 }
454 /// If this pointer has an InlineDescriptor we can use to initialize.
455 bool canBeInitialized() const {
456 if (!isBlockPointer())
457 return false;
458
459 return BS.Pointee && BS.Base > 0;
460 }
461
462 [[nodiscard]] const BlockPointer &asBlockPointer() const {
463 assert(isBlockPointer());
464 return BS;
465 }
466 [[nodiscard]] const IntPointer &asIntPointer() const {
467 assert(isIntegralPointer());
468 return Int;
469 }
470 [[nodiscard]] const FunctionPointer &asFunctionPointer() const {
471 assert(isFunctionPointer());
472 return Fn;
473 }
474 [[nodiscard]] const TypeidPointer &asTypeidPointer() const {
475 assert(isTypeidPointer());
476 return Typeid;
477 }
478
479 bool isBlockPointer() const { return StorageKind == Storage::Block; }
480 bool isIntegralPointer() const { return StorageKind == Storage::Int; }
481 bool isFunctionPointer() const { return StorageKind == Storage::Fn; }
482 bool isTypeidPointer() const { return StorageKind == Storage::Typeid; }
483
484 /// Returns the record descriptor of a class.
485 const Record *getRecord() const { return getFieldDesc()->ElemRecord; }
486 /// Returns the element record type, if this is a non-primive array.
487 const Record *getElemRecord() const {
488 const Descriptor *ElemDesc = getFieldDesc()->ElemDesc;
489 return ElemDesc ? ElemDesc->ElemRecord : nullptr;
490 }
491 /// Returns the field information.
492 const FieldDecl *getField() const {
493 if (const Descriptor *FD = getFieldDesc())
494 return FD->asFieldDecl();
495 return nullptr;
496 }
497
498 /// Checks if the storage is extern.
499 bool isExtern() const {
500 if (isBlockPointer())
501 return BS.Pointee && BS.Pointee->isExtern();
502 return false;
503 }
504 /// Checks if the storage is static.
505 bool isStatic() const {
506 if (!isBlockPointer())
507 return true;
508 assert(BS.Pointee);
509 return BS.Pointee->isStatic();
510 }
511 /// Checks if the storage is temporary.
512 bool isTemporary() const {
513 if (isBlockPointer()) {
514 assert(BS.Pointee);
515 return BS.Pointee->isTemporary();
516 }
517 return false;
518 }
519 /// Checks if the storage has been dynamically allocated.
520 bool isDynamic() const {
521 if (isBlockPointer()) {
522 assert(BS.Pointee);
523 return BS.Pointee->isDynamic();
524 }
525 return false;
526 }
527 /// Checks if the storage is a static temporary.
528 bool isStaticTemporary() const { return isStatic() && isTemporary(); }
529
530 /// Checks if the field is mutable.
531 bool isMutable() const {
532 if (!isBlockPointer())
533 return false;
534 return !isRoot() && getInlineDesc()->IsFieldMutable;
535 }
536
537 bool isWeak() const {
538 if (isFunctionPointer()) {
539 if (!Fn.Func || !Fn.Func->getDecl())
540 return false;
541
542 return Fn.Func->getDecl()->isWeak();
543 }
544 if (!isBlockPointer())
545 return false;
546
547 assert(isBlockPointer());
548 return BS.Pointee->isWeak();
549 }
550 /// Checks if the object is active.
551 bool isActive() const {
552 if (!isBlockPointer())
553 return true;
554 return isRoot() || getInlineDesc()->IsActive;
555 }
556 /// Checks if a structure is a base class.
557 bool isBaseClass() const { return isField() && getInlineDesc()->IsBase; }
558 bool isVirtualBaseClass() const {
559 return isField() && getInlineDesc()->IsVirtualBase;
560 }
561 /// Checks if the pointer points to a dummy value.
562 bool isDummy() const {
563 if (!isBlockPointer())
564 return false;
565
566 if (const Block *Pointee = BS.Pointee)
567 return Pointee->isDummy();
568 return false;
569 }
570
571 /// Checks if an object or a subfield is mutable.
572 bool isConst() const {
573 if (isIntegralPointer())
574 return true;
575 return isRoot() ? getDeclDesc()->IsConst : getInlineDesc()->IsConst;
576 }
577 bool isConstInMutable() const {
578 if (!isBlockPointer())
579 return false;
580 return isRoot() ? false : getInlineDesc()->IsConstInMutable;
581 }
582
583 /// Checks if an object or a subfield is volatile.
584 bool isVolatile() const {
585 if (!isBlockPointer())
586 return false;
587 return isRoot() ? getDeclDesc()->IsVolatile : getInlineDesc()->IsVolatile;
588 }
589
590 /// Returns the declaration ID.
591 UnsignedOrNone getDeclID() const {
592 if (isBlockPointer()) {
593 assert(BS.Pointee);
594 return BS.Pointee->getDeclID();
595 }
596 return std::nullopt;
597 }
598
599 /// Returns the byte offset from the start.
600 uint64_t getByteOffset() const {
601 if (isIntegralPointer())
602 return Int.Value + Offset;
603 if (isTypeidPointer())
604 return reinterpret_cast<uintptr_t>(Typeid.TypePtr) + Offset;
605 if (isOnePastEnd())
606 return PastEndMark;
607 return Offset;
608 }
609
610 /// Returns the number of elements.
611 unsigned getNumElems() const {
612 if (!isBlockPointer())
613 return ~0u;
614 return getSize() / elemSize();
615 }
616
617 const Block *block() const { return BS.Pointee; }
618
619 /// If backed by actual data (i.e. a block pointer), return
620 /// an address to that data.
621 const std::byte *getRawAddress() const {
622 assert(isBlockPointer());
623 return BS.Pointee->rawData() + Offset;
624 }
625
626 /// Returns the index into an array.
627 int64_t getIndex() const {
628 if (!isBlockPointer())
629 return getIntegerRepresentation();
630
631 if (isZero())
632 return 0;
633
634 // narrow()ed element in a composite array.
635 if (BS.Base > sizeof(InlineDescriptor) && BS.Base == Offset)
636 return 0;
637
638 if (auto ElemSize = elemSize())
639 return getOffset() / ElemSize;
640 return 0;
641 }
642
643 /// Checks if the index is one past end.
644 bool isOnePastEnd() const {
645 if (!isBlockPointer())
646 return false;
647
648 if (!BS.Pointee)
649 return false;
650
651 if (isUnknownSizeArray())
652 return false;
653
654 return isPastEnd() || (getSize() == getOffset());
655 }
656
657 /// Checks if the pointer points past the end of the object.
658 bool isPastEnd() const {
659 if (isIntegralPointer())
660 return false;
661
662 return !isZero() && Offset > BS.Pointee->getSize();
663 }
664
665 /// Checks if the pointer is an out-of-bounds element pointer.
666 bool isElementPastEnd() const { return Offset == PastEndMark; }
667
668 /// Checks if the pointer is pointing to a zero-size array.
669 bool isZeroSizeArray() const {
670 if (isFunctionPointer())
671 return false;
672 if (const auto *Desc = getFieldDesc())
673 return Desc->isZeroSizeArray();
674 return false;
675 }
676
677 /// Checks whether the pointer can be dereferenced to the given PrimType.
678 bool canDeref(PrimType T) const {
679 if (const Descriptor *FieldDesc = getFieldDesc()) {
680 return (FieldDesc->isPrimitive() || FieldDesc->isPrimitiveArray()) &&
681 FieldDesc->getPrimType() == T;
682 }
683 return false;
684 }
685
686 /// Dereferences the pointer, if it's live.
687 template <typename T> T &deref() const {
688 assert(isLive() && "Invalid pointer");
689 assert(isBlockPointer());
690 assert(BS.Pointee);
691 assert(isDereferencable());
692 assert(Offset + sizeof(T) <= BS.Pointee->getDescriptor()->getAllocSize());
693
694 if (isArrayRoot())
695 return *reinterpret_cast<T *>(BS.Pointee->rawData() + BS.Base +
696 sizeof(InitMapPtr));
697
698 return *reinterpret_cast<T *>(BS.Pointee->rawData() + Offset);
699 }
700
701 /// Dereferences the element at index \p I.
702 /// This is equivalent to atIndex(I).deref<T>().
703 template <typename T> T &elem(unsigned I) const {
704 assert(isLive() && "Invalid pointer");
705 assert(isBlockPointer());
706 assert(BS.Pointee);
707 assert(isDereferencable());
708 assert(getFieldDesc()->isPrimitiveArray());
709 assert(I < getFieldDesc()->getNumElems());
710
711 unsigned ElemByteOffset = I * getFieldDesc()->getElemSize();
712 unsigned ReadOffset = BS.Base + sizeof(InitMapPtr) + ElemByteOffset;
713 assert(ReadOffset + sizeof(T) <=
714 BS.Pointee->getDescriptor()->getAllocSize());
715
716 return *reinterpret_cast<T *>(BS.Pointee->rawData() + ReadOffset);
717 }
718
719 /// Whether this block can be read from at all. This is only true for
720 /// block pointers that point to a valid location inside that block.
721 bool isDereferencable() const {
722 if (!isBlockPointer())
723 return false;
724 if (isPastEnd())
725 return false;
726
727 return true;
728 }
729
730 /// Initializes a field.
731 void initialize() const;
732 /// Initialized the given element of a primitive array.
733 void initializeElement(unsigned Index) const;
734 /// Initialize all elements of a primitive array at once. This can be
735 /// used in situations where we *know* we have initialized *all* elements
736 /// of a primtive array.
737 void initializeAllElements() const;
738 /// Checks if an object was initialized.
739 bool isInitialized() const;
740 /// Like isInitialized(), but for primitive arrays.
741 bool isElementInitialized(unsigned Index) const;
742 bool allElementsInitialized() const;
743 bool allElementsAlive() const;
744 bool isElementAlive(unsigned Index) const;
745
746 /// Activats a field.
747 void activate() const;
748 /// Deactivates an entire strurcutre.
749 void deactivate() const;
750
751 Lifetime getLifetime() const {
752 if (!isBlockPointer())
753 return Lifetime::Started;
754 if (BS.Base < sizeof(InlineDescriptor))
755 return Lifetime::Started;
756
757 if (inArray() && !isArrayRoot()) {
758 InitMapPtr &IM = getInitMap();
759
760 if (!IM.hasInitMap()) {
761 if (IM.allInitialized())
762 return Lifetime::Started;
763 return getArray().getLifetime();
764 }
765
766 return IM->isElementAlive(I: getIndex()) ? Lifetime::Started
767 : Lifetime::Ended;
768 }
769
770 return getInlineDesc()->LifeState;
771 }
772
773 /// Start the lifetime of this pointer. This works for pointer with an
774 /// InlineDescriptor as well as primitive array elements. Pointers are usually
775 /// alive by default, unless the underlying object has been allocated with
776 /// std::allocator. This function is used by std::construct_at.
777 void startLifetime() const;
778 /// Ends the lifetime of the pointer. This works for pointer with an
779 /// InlineDescriptor as well as primitive array elements. This function is
780 /// used by std::destroy_at.
781 void endLifetime() const;
782
783 /// Strip base casts from this Pointer.
784 /// The result is either a root pointer or something
785 /// that isn't a base class anymore.
786 [[nodiscard]] Pointer stripBaseCasts() const {
787 Pointer P = *this;
788 while (P.isBaseClass())
789 P = P.getBase();
790 return P;
791 }
792
793 /// Compare two pointers.
794 ComparisonCategoryResult compare(const Pointer &Other) const {
795 if (!hasSameBase(A: *this, B: Other))
796 return ComparisonCategoryResult::Unordered;
797
798 if (Offset < Other.Offset)
799 return ComparisonCategoryResult::Less;
800 if (Offset > Other.Offset)
801 return ComparisonCategoryResult::Greater;
802
803 return ComparisonCategoryResult::Equal;
804 }
805
806 /// Checks if two pointers are comparable.
807 static bool hasSameBase(const Pointer &A, const Pointer &B);
808 /// Checks if two pointers can be subtracted.
809 static bool hasSameArray(const Pointer &A, const Pointer &B);
810 /// Checks if both given pointers point to the same block.
811 static bool pointToSameBlock(const Pointer &A, const Pointer &B);
812
813 static std::optional<std::pair<Pointer, Pointer>>
814 computeSplitPoint(const Pointer &A, const Pointer &B);
815
816 /// Whether this points to a block that's been created for a "literal lvalue",
817 /// i.e. a non-MaterializeTemporaryExpr Expr.
818 bool pointsToLiteral() const;
819 bool pointsToStringLiteral() const;
820
821 /// Prints the pointer.
822 void print(llvm::raw_ostream &OS) const;
823
824 /// Compute an integer that can be used to compare this pointer to
825 /// another one. This is usually NOT the same as the pointer offset
826 /// regarding the AST record layout.
827 size_t computeOffsetForComparison(const ASTContext &ASTCtx) const;
828
829private:
830 friend class Block;
831 friend class DeadBlock;
832 friend class MemberPointer;
833 friend class InterpState;
834 friend class DynamicAllocator;
835 friend class Program;
836
837 /// Returns the embedded descriptor preceding a field.
838 InlineDescriptor *getInlineDesc() const {
839 assert(isBlockPointer());
840 assert(BS.Base != sizeof(GlobalInlineDescriptor));
841 assert(BS.Base <= BS.Pointee->getSize());
842 assert(BS.Base >= sizeof(InlineDescriptor));
843 return getDescriptor(Offset: BS.Base);
844 }
845
846 /// Returns a descriptor at a given offset.
847 InlineDescriptor *getDescriptor(unsigned Offset) const {
848 assert(Offset != 0 && "Not a nested pointer");
849 assert(isBlockPointer());
850 assert(!isZero());
851 return reinterpret_cast<InlineDescriptor *>(BS.Pointee->rawData() +
852 Offset) -
853 1;
854 }
855
856 /// Returns a reference to the InitMapPtr which stores the initialization map.
857 InitMapPtr &getInitMap() const {
858 assert(isBlockPointer());
859 assert(!isZero());
860 return *reinterpret_cast<InitMapPtr *>(BS.Pointee->rawData() + BS.Base);
861 }
862
863 /// Offset into the storage.
864 uint64_t Offset = 0;
865
866 Storage StorageKind = Storage::Int;
867 union {
868 IntPointer Int;
869 BlockPointer BS;
870 FunctionPointer Fn;
871 TypeidPointer Typeid;
872 };
873};
874
875inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Pointer &P) {
876 P.print(OS);
877 OS << ' ';
878 if (const Descriptor *D = P.getFieldDesc())
879 D->dump(OS);
880 if (P.isArrayElement()) {
881 if (P.isOnePastEnd())
882 OS << " one-past-the-end";
883 else
884 OS << " index " << P.getIndex();
885 }
886 if (P.isBlockPointer() && P.block() && P.block()->isDummy())
887 OS << " dummy";
888 if (!P.isLive())
889 OS << " dead";
890 return OS;
891}
892
893} // namespace interp
894} // namespace clang
895
896#endif
897