clang 23.0.0git
Pointer.h
Go to the documentation of this file.
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"
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
37 /// The block the pointer is pointing to.
39 /// Start of the current subfield.
40 unsigned Base;
41 /// Previous link in the pointer chain.
43 /// Next link in the pointer chain.
45};
46
47struct IntPointer {
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
57 const Function *Func;
58};
59
61 const Type *TypePtr;
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{nullptr, 0} {}
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, Address} {}
112 Pointer(const Function *F, uint64_t Offset = 0)
113 : Offset(Offset), StorageKind(Storage::Fn), Fn{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);
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(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;
296 return nullptr;
297
298 assert(isBlockPointer());
299 assert(BS.Pointee);
300 return BS.Pointee->Desc;
301 }
303
304 /// Returns the expression or declaration the pointer has been created for.
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.
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;
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.
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())
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
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
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.
795 if (!hasSameBase(*this, Other))
797
798 if (Offset < Other.Offset)
800 if (Offset > Other.Offset)
802
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(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 {
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
Defines the C++ Decl subclasses, other than those for templates (found in DeclTemplate....
FormatToken * Next
The next token in the unwrapped line.
APValue - This class implements a discriminated union of [uninitialized] [APSInt] [APFloat],...
Definition APValue.h:122
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition ASTContext.h:226
Represents a member of a struct/union/class.
Definition Decl.h:3160
A (possibly-)qualified type.
Definition TypeBase.h:937
Encodes a location in the source.
The base class of the type hierarchy.
Definition TypeBase.h:1866
A memory block, either on the stack or in the heap.
Definition InterpBlock.h:44
const Descriptor * getDescriptor() const
Returns the block's descriptor.
Definition InterpBlock.h:73
std::byte * rawData()
Returns a pointer to the raw data, including metadata.
bool isDummy() const
Definition InterpBlock.h:84
Holds all information required to evaluate constexpr code in a module.
Definition Context.h:47
Descriptor for a dead block.
Bytecode function.
Definition Function.h:99
const FunctionDecl * getDecl() const
Returns the original FunctionDecl.
Definition Function.h:133
A pointer to a memory block, live or dead.
Definition Pointer.h:97
static bool hasSameBase(const Pointer &A, const Pointer &B)
Checks if two pointers are comparable.
Definition Pointer.cpp:692
Pointer narrow() const
Restricts the scope of an array element pointer.
Definition Pointer.h:194
friend class Program
Definition Pointer.h:835
UnsignedOrNone getDeclID() const
Returns the declaration ID.
Definition Pointer.h:591
Pointer stripBaseCasts() const
Strip base casts from this Pointer.
Definition Pointer.h:786
void deactivate() const
Deactivates an entire strurcutre.
Definition Pointer.cpp:688
bool isVolatile() const
Checks if an object or a subfield is volatile.
Definition Pointer.h:584
bool isInitialized() const
Checks if an object was initialized.
Definition Pointer.cpp:442
bool isStatic() const
Checks if the storage is static.
Definition Pointer.h:505
bool isDynamic() const
Checks if the storage has been dynamically allocated.
Definition Pointer.h:520
bool inUnion() const
Definition Pointer.h:413
bool isZeroSizeArray() const
Checks if the pointer is pointing to a zero-size array.
Definition Pointer.h:669
bool isElementInitialized(unsigned Index) const
Like isInitialized(), but for primitive arrays.
Definition Pointer.cpp:464
FunctionPointer Fn
Definition Pointer.h:870
Pointer atIndex(uint64_t Idx) const
Offsets a pointer inside an array.
Definition Pointer.h:162
bool isDummy() const
Checks if the pointer points to a dummy value.
Definition Pointer.h:562
Pointer atFieldSub(unsigned Off) const
Subtract the given offset from the current Base and Offset of the pointer.
Definition Pointer.h:187
bool inPrimitiveArray() const
Checks if the structure is a primitive array.
Definition Pointer.h:420
void print(llvm::raw_ostream &OS) const
Prints the pointer.
Definition Pointer.cpp:326
bool isExtern() const
Checks if the storage is extern.
Definition Pointer.h:499
int64_t getIndex() const
Returns the index into an array.
Definition Pointer.h:627
friend class MemberPointer
Definition Pointer.h:832
bool isActive() const
Checks if the object is active.
Definition Pointer.h:551
void startLifetime() const
Start the lifetime of this pointer.
Definition Pointer.cpp:506
bool canDeref(PrimType T) const
Checks whether the pointer can be dereferenced to the given PrimType.
Definition Pointer.h:678
bool isConst() const
Checks if an object or a subfield is mutable.
Definition Pointer.h:572
Pointer atField(unsigned Off) const
Creates a pointer to a field.
Definition Pointer.h:179
bool isWeak() const
Definition Pointer.h:537
T & deref() const
Dereferences the pointer, if it's live.
Definition Pointer.h:687
Pointer(IntPointer &&IntPtr)
Definition Pointer.h:104
bool isMutable() const
Checks if the field is mutable.
Definition Pointer.h:531
bool isConstInMutable() const
Definition Pointer.h:577
DeclTy getSource() const
Returns the expression or declaration the pointer has been created for.
Definition Pointer.h:305
unsigned getNumElems() const
Returns the number of elements.
Definition Pointer.h:611
Pointer getArray() const
Returns the parent array.
Definition Pointer.h:326
bool isUnknownSizeArray() const
Checks if the structure is an array of unknown size.
Definition Pointer.h:426
void activate() const
Activats a field.
Definition Pointer.cpp:634
static std::optional< std::pair< Pointer, Pointer > > computeSplitPoint(const Pointer &A, const Pointer &B)
Definition Pointer.cpp:744
const TypeidPointer & asTypeidPointer() const
Definition Pointer.h:474
bool isIntegralPointer() const
Definition Pointer.h:480
QualType getType() const
Returns the type of the innermost field.
Definition Pointer.h:346
bool operator==(const Pointer &P) const
Equality operators are just for tests.
Definition Pointer.h:126
bool isArrayElement() const
Checks if the pointer points to an array.
Definition Pointer.h:432
void initializeAllElements() const
Initialize all elements of a primitive array at once.
Definition Pointer.cpp:593
bool pointsToStringLiteral() const
Definition Pointer.cpp:732
bool isArrayRoot() const
Whether this array refers to an array, but not to the first element.
Definition Pointer.h:405
size_t computeOffsetForComparison(const ASTContext &ASTCtx) const
Compute an integer that can be used to compare this pointer to another one.
Definition Pointer.cpp:369
bool isLive() const
Checks if the pointer is live.
Definition Pointer.h:278
bool inArray() const
Checks if the innermost field is an array.
Definition Pointer.h:408
bool isElementAlive(unsigned Index) const
Definition Pointer.cpp:493
bool isStaticTemporary() const
Checks if the storage is a static temporary.
Definition Pointer.h:528
Pointer(const Type *TypePtr, const Type *TypeInfoType, uint64_t Offset=0)
Definition Pointer.h:114
T & elem(unsigned I) const
Dereferences the element at index I.
Definition Pointer.h:703
bool pointsToLiteral() const
Whether this points to a block that's been created for a "literal lvalue", i.e.
Definition Pointer.cpp:721
Pointer(uint64_t Address, const Descriptor *Desc, uint64_t Offset=0)
Definition Pointer.h:110
bool allElementsAlive() const
Definition Pointer.cpp:617
Pointer getBase() const
Returns a pointer to the object of which this pointer is a field.
Definition Pointer.h:317
uint64_t getByteOffset() const
Returns the byte offset from the start.
Definition Pointer.h:600
bool isTypeidPointer() const
Definition Pointer.h:482
std::string toDiagnosticString(const ASTContext &Ctx) const
Converts the pointer to a string usable in diagnostics.
Definition Pointer.cpp:432
bool isZero() const
Checks if the pointer is null.
Definition Pointer.h:264
Pointer & operator=(const Pointer &P)
Definition Pointer.cpp:94
ComparisonCategoryResult compare(const Pointer &Other) const
Compare two pointers.
Definition Pointer.h:794
const IntPointer & asIntPointer() const
Definition Pointer.h:466
bool isRoot() const
Pointer points directly to a block.
Definition Pointer.h:448
const Descriptor * getDeclDesc() const
Accessor for information about the declaration site.
Definition Pointer.h:292
const Record * getElemRecord() const
Returns the element record type, if this is a non-primive array.
Definition Pointer.h:487
static bool pointToSameBlock(const Pointer &A, const Pointer &B)
Checks if both given pointers point to the same block.
Definition Pointer.cpp:710
APValue toAPValue(const ASTContext &ASTCtx) const
Converts the pointer to an APValue.
Definition Pointer.cpp:172
unsigned getOffset() const
Returns the offset into an array.
Definition Pointer.h:387
friend class DynamicAllocator
Definition Pointer.h:834
bool isOnePastEnd() const
Checks if the index is one past end.
Definition Pointer.h:644
friend class InterpState
Definition Pointer.h:833
static bool hasSameArray(const Pointer &A, const Pointer &B)
Checks if two pointers can be subtracted.
Definition Pointer.cpp:716
uint64_t getIntegerRepresentation() const
Definition Pointer.h:149
bool isPastEnd() const
Checks if the pointer points past the end of the object.
Definition Pointer.h:658
Pointer(const Function *F, uint64_t Offset=0)
Definition Pointer.h:112
const FieldDecl * getField() const
Returns the field information.
Definition Pointer.h:492
Pointer expand() const
Expands a pointer to the containing array, undoing narrowing.
Definition Pointer.h:229
friend class Block
Definition Pointer.h:830
bool isElementPastEnd() const
Checks if the pointer is an out-of-bounds element pointer.
Definition Pointer.h:666
bool isDereferencable() const
Whether this block can be read from at all.
Definition Pointer.h:721
bool isBlockPointer() const
Definition Pointer.h:479
bool operator!=(const Pointer &P) const
Definition Pointer.h:141
BlockPointer BS
Definition Pointer.h:869
friend class DeadBlock
Definition Pointer.h:831
TypeidPointer Typeid
Definition Pointer.h:871
std::optional< APValue > toRValue(const Context &Ctx, QualType ResultType) const
Converts the pointer to an APValue that is an rvalue.
Definition Pointer.cpp:785
size_t getSize() const
Returns the total size of the innermost field.
Definition Pointer.h:381
bool isTemporary() const
Checks if the storage is temporary.
Definition Pointer.h:512
const FunctionPointer & asFunctionPointer() const
Definition Pointer.h:470
bool allElementsInitialized() const
Definition Pointer.cpp:600
SourceLocation getDeclLoc() const
Definition Pointer.h:302
const Block * block() const
Definition Pointer.h:617
bool isFunctionPointer() const
Definition Pointer.h:481
void endLifetime() const
Ends the lifetime of the pointer.
Definition Pointer.cpp:526
Pointer getDeclPtr() const
Definition Pointer.h:366
const Descriptor * getFieldDesc() const
Accessors for information about the innermost field.
Definition Pointer.h:336
bool isVirtualBaseClass() const
Definition Pointer.h:558
bool isBaseClass() const
Checks if a structure is a base class.
Definition Pointer.h:557
size_t elemSize() const
Returns the element size of the innermost field.
Definition Pointer.h:369
bool canBeInitialized() const
If this pointer has an InlineDescriptor we can use to initialize.
Definition Pointer.h:455
Lifetime getLifetime() const
Definition Pointer.h:751
const BlockPointer & asBlockPointer() const
Definition Pointer.h:462
void initialize() const
Initializes a field.
Definition Pointer.cpp:546
const std::byte * getRawAddress() const
If backed by actual data (i.e.
Definition Pointer.h:621
bool isField() const
Checks if the item is a field in an object.
Definition Pointer.h:284
void initializeElement(unsigned Index) const
Initialized the given element of a primitive array.
Definition Pointer.cpp:572
const Record * getRecord() const
Returns the record descriptor of a class.
Definition Pointer.h:485
Structure/Class descriptor.
Definition Record.h:25
llvm::PointerUnion< const Decl *, const Expr * > DeclTy
Definition Descriptor.h:29
llvm::raw_ostream & operator<<(llvm::raw_ostream &OS, const Boolean &B)
Definition Boolean.h:154
PrimType
Enumeration of the primitive types of the VM.
Definition PrimType.h:33
The JSON file list parser is used to communicate input to InstallAPI.
nullptr
This class represents a compute construct, representing a 'Kind' of ‘parallel’, 'serial',...
ComparisonCategoryResult
An enumeration representing the possible results of a three-way comparison.
@ Other
Other implicit parameter.
Definition Decl.h:1746
__UINTPTR_TYPE__ uintptr_t
An unsigned integer type with the property that any valid pointer to void can be converted to this ty...
#define false
Definition stdbool.h:26
Pointer * Prev
Previous link in the pointer chain.
Definition Pointer.h:42
Pointer * Next
Next link in the pointer chain.
Definition Pointer.h:44
unsigned Base
Start of the current subfield.
Definition Pointer.h:40
Block * Pointee
The block the pointer is pointing to.
Definition Pointer.h:38
Describes a memory block created by an allocation site.
Definition Descriptor.h:121
const bool IsConst
Flag indicating if the block is mutable.
Definition Descriptor.h:160
unsigned getSize() const
Returns the size of the object without metadata.
Definition Descriptor.h:235
const DeclTy & getSource() const
Definition Descriptor.h:212
const Decl * asDecl() const
Definition Descriptor.h:210
const Descriptor *const ElemDesc
Descriptor of the array element.
Definition Descriptor.h:154
SourceLocation getLocation() const
bool isUnknownSizeArray() const
Checks if the descriptor is of an array of unknown size.
Definition Descriptor.h:264
unsigned getElemSize() const
returns the size of an element when the structure is viewed as an array.
Definition Descriptor.h:248
const bool IsArray
Flag indicating if the block is an array.
Definition Descriptor.h:167
bool isPrimitiveArray() const
Checks if the descriptor is of an array of primitives.
Definition Descriptor.h:258
QualType getDataElemType() const
const Record *const ElemRecord
Pointer to the record, if block contains records.
Definition Descriptor.h:152
const Function * Func
Definition Pointer.h:57
Descriptor used for global variables.
Definition Descriptor.h:50
A pointer-sized struct we use to allocate into data storage.
Definition InitMap.h:79
bool hasInitMap() const
Definition InitMap.h:88
bool allInitialized() const
Are all elements in the array already initialized?
Definition InitMap.h:92
bool isElementAlive(unsigned I) const
Definition InitMap.h:52
Inline descriptor embedded in structures and arrays.
Definition Descriptor.h:66
std::optional< IntPointer > atOffset(const ASTContext &ASTCtx, unsigned Offset) const
Definition Pointer.cpp:979
IntPointer baseCast(const ASTContext &ASTCtx, unsigned BaseOffset) const
Definition Pointer.cpp:1009
const Descriptor * Desc
Definition Pointer.h:48
const Type * TypeInfoType
Definition Pointer.h:62