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