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