Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/clang/lib/AST/Interp/Pointer.h
35292 views
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 "InterpBlock.h"
18
#include "clang/AST/ComparisonCategories.h"
19
#include "clang/AST/Decl.h"
20
#include "clang/AST/DeclCXX.h"
21
#include "clang/AST/Expr.h"
22
#include "llvm/Support/raw_ostream.h"
23
24
namespace clang {
25
namespace interp {
26
class Block;
27
class DeadBlock;
28
class Pointer;
29
class Context;
30
template <unsigned A, bool B> class Integral;
31
enum PrimType : unsigned;
32
33
class Pointer;
34
inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Pointer &P);
35
36
struct BlockPointer {
37
/// The block the pointer is pointing to.
38
Block *Pointee;
39
/// Start of the current subfield.
40
unsigned Base;
41
};
42
43
struct IntPointer {
44
const Descriptor *Desc;
45
uint64_t Value;
46
};
47
48
enum class Storage { Block, Int };
49
50
/// A pointer to a memory block, live or dead.
51
///
52
/// This object can be allocated into interpreter stack frames. If pointing to
53
/// a live block, it is a link in the chain of pointers pointing to the block.
54
///
55
/// In the simplest form, a Pointer has a Block* (the pointee) and both Base
56
/// and Offset are 0, which means it will point to raw data.
57
///
58
/// The Base field is used to access metadata about the data. For primitive
59
/// arrays, the Base is followed by an InitMap. In a variety of cases, the
60
/// Base is preceded by an InlineDescriptor, which is used to track the
61
/// initialization state, among other things.
62
///
63
/// The Offset field is used to access the actual data. In other words, the
64
/// data the pointer decribes can be found at
65
/// Pointee->rawData() + Pointer.Offset.
66
///
67
///
68
/// Pointee Offset
69
/// │ │
70
/// │ │
71
/// ▼ ▼
72
/// ┌───────┬────────────┬─────────┬────────────────────────────┐
73
/// │ Block │ InlineDesc │ InitMap │ Actual Data │
74
/// └───────┴────────────┴─────────┴────────────────────────────┘
75
/// ▲
76
/// │
77
/// │
78
/// Base
79
class Pointer {
80
private:
81
static constexpr unsigned PastEndMark = ~0u;
82
static constexpr unsigned RootPtrMark = ~0u;
83
84
public:
85
Pointer() {
86
StorageKind = Storage::Int;
87
PointeeStorage.Int.Value = 0;
88
PointeeStorage.Int.Desc = nullptr;
89
}
90
Pointer(Block *B);
91
Pointer(Block *B, uint64_t BaseAndOffset);
92
Pointer(const Pointer &P);
93
Pointer(Pointer &&P);
94
Pointer(uint64_t Address, const Descriptor *Desc, uint64_t Offset = 0)
95
: Offset(Offset), StorageKind(Storage::Int) {
96
PointeeStorage.Int.Value = Address;
97
PointeeStorage.Int.Desc = Desc;
98
}
99
~Pointer();
100
101
void operator=(const Pointer &P);
102
void operator=(Pointer &&P);
103
104
/// Equality operators are just for tests.
105
bool operator==(const Pointer &P) const {
106
if (P.StorageKind != StorageKind)
107
return false;
108
if (isIntegralPointer())
109
return P.asIntPointer().Value == asIntPointer().Value &&
110
Offset == P.Offset;
111
112
assert(isBlockPointer());
113
return P.asBlockPointer().Pointee == asBlockPointer().Pointee &&
114
P.asBlockPointer().Base == asBlockPointer().Base &&
115
Offset == P.Offset;
116
}
117
118
bool operator!=(const Pointer &P) const { return !(P == *this); }
119
120
/// Converts the pointer to an APValue.
121
APValue toAPValue(const ASTContext &ASTCtx) const;
122
123
/// Converts the pointer to a string usable in diagnostics.
124
std::string toDiagnosticString(const ASTContext &Ctx) const;
125
126
uint64_t getIntegerRepresentation() const {
127
if (isIntegralPointer())
128
return asIntPointer().Value + (Offset * elemSize());
129
return reinterpret_cast<uint64_t>(asBlockPointer().Pointee) + Offset;
130
}
131
132
/// Converts the pointer to an APValue that is an rvalue.
133
std::optional<APValue> toRValue(const Context &Ctx,
134
QualType ResultType) const;
135
136
/// Offsets a pointer inside an array.
137
[[nodiscard]] Pointer atIndex(uint64_t Idx) const {
138
if (isIntegralPointer())
139
return Pointer(asIntPointer().Value, asIntPointer().Desc, Idx);
140
141
if (asBlockPointer().Base == RootPtrMark)
142
return Pointer(asBlockPointer().Pointee, RootPtrMark,
143
getDeclDesc()->getSize());
144
uint64_t Off = Idx * elemSize();
145
if (getFieldDesc()->ElemDesc)
146
Off += sizeof(InlineDescriptor);
147
else
148
Off += sizeof(InitMapPtr);
149
return Pointer(asBlockPointer().Pointee, asBlockPointer().Base,
150
asBlockPointer().Base + Off);
151
}
152
153
/// Creates a pointer to a field.
154
[[nodiscard]] Pointer atField(unsigned Off) const {
155
unsigned Field = Offset + Off;
156
if (isIntegralPointer())
157
return Pointer(asIntPointer().Value + Field, asIntPointer().Desc);
158
return Pointer(asBlockPointer().Pointee, Field, Field);
159
}
160
161
/// Subtract the given offset from the current Base and Offset
162
/// of the pointer.
163
[[nodiscard]] Pointer atFieldSub(unsigned Off) const {
164
assert(Offset >= Off);
165
unsigned O = Offset - Off;
166
return Pointer(asBlockPointer().Pointee, O, O);
167
}
168
169
/// Restricts the scope of an array element pointer.
170
[[nodiscard]] Pointer narrow() const {
171
if (!isBlockPointer())
172
return *this;
173
assert(isBlockPointer());
174
// Null pointers cannot be narrowed.
175
if (isZero() || isUnknownSizeArray())
176
return *this;
177
178
// Pointer to an array of base types - enter block.
179
if (asBlockPointer().Base == RootPtrMark)
180
return Pointer(asBlockPointer().Pointee, sizeof(InlineDescriptor),
181
Offset == 0 ? Offset : PastEndMark);
182
183
// Pointer is one past end - magic offset marks that.
184
if (isOnePastEnd())
185
return Pointer(asBlockPointer().Pointee, asBlockPointer().Base,
186
PastEndMark);
187
188
// Primitive arrays are a bit special since they do not have inline
189
// descriptors. If Offset != Base, then the pointer already points to
190
// an element and there is nothing to do. Otherwise, the pointer is
191
// adjusted to the first element of the array.
192
if (inPrimitiveArray()) {
193
if (Offset != asBlockPointer().Base)
194
return *this;
195
return Pointer(asBlockPointer().Pointee, asBlockPointer().Base,
196
Offset + sizeof(InitMapPtr));
197
}
198
199
// Pointer is to a field or array element - enter it.
200
if (Offset != asBlockPointer().Base)
201
return Pointer(asBlockPointer().Pointee, Offset, Offset);
202
203
// Enter the first element of an array.
204
if (!getFieldDesc()->isArray())
205
return *this;
206
207
const unsigned NewBase = asBlockPointer().Base + sizeof(InlineDescriptor);
208
return Pointer(asBlockPointer().Pointee, NewBase, NewBase);
209
}
210
211
/// Expands a pointer to the containing array, undoing narrowing.
212
[[nodiscard]] Pointer expand() const {
213
assert(isBlockPointer());
214
Block *Pointee = asBlockPointer().Pointee;
215
216
if (isElementPastEnd()) {
217
// Revert to an outer one-past-end pointer.
218
unsigned Adjust;
219
if (inPrimitiveArray())
220
Adjust = sizeof(InitMapPtr);
221
else
222
Adjust = sizeof(InlineDescriptor);
223
return Pointer(Pointee, asBlockPointer().Base,
224
asBlockPointer().Base + getSize() + Adjust);
225
}
226
227
// Do not step out of array elements.
228
if (asBlockPointer().Base != Offset)
229
return *this;
230
231
// If at base, point to an array of base types.
232
if (isRoot())
233
return Pointer(Pointee, RootPtrMark, 0);
234
235
// Step into the containing array, if inside one.
236
unsigned Next = asBlockPointer().Base - getInlineDesc()->Offset;
237
const Descriptor *Desc =
238
(Next == Pointee->getDescriptor()->getMetadataSize())
239
? getDeclDesc()
240
: getDescriptor(Next)->Desc;
241
if (!Desc->IsArray)
242
return *this;
243
return Pointer(Pointee, Next, Offset);
244
}
245
246
/// Checks if the pointer is null.
247
bool isZero() const {
248
if (isBlockPointer())
249
return asBlockPointer().Pointee == nullptr;
250
assert(isIntegralPointer());
251
return asIntPointer().Value == 0 && Offset == 0;
252
}
253
/// Checks if the pointer is live.
254
bool isLive() const {
255
if (isIntegralPointer())
256
return true;
257
return asBlockPointer().Pointee && !asBlockPointer().Pointee->IsDead;
258
}
259
/// Checks if the item is a field in an object.
260
bool isField() const {
261
if (isIntegralPointer())
262
return false;
263
264
return !isRoot() && getFieldDesc()->asDecl();
265
}
266
267
/// Accessor for information about the declaration site.
268
const Descriptor *getDeclDesc() const {
269
if (isIntegralPointer())
270
return asIntPointer().Desc;
271
272
assert(isBlockPointer());
273
assert(asBlockPointer().Pointee);
274
return asBlockPointer().Pointee->Desc;
275
}
276
SourceLocation getDeclLoc() const { return getDeclDesc()->getLocation(); }
277
278
/// Returns the expression or declaration the pointer has been created for.
279
DeclTy getSource() const {
280
if (isBlockPointer())
281
return getDeclDesc()->getSource();
282
283
assert(isIntegralPointer());
284
return asIntPointer().Desc ? asIntPointer().Desc->getSource() : DeclTy();
285
}
286
287
/// Returns a pointer to the object of which this pointer is a field.
288
[[nodiscard]] Pointer getBase() const {
289
if (asBlockPointer().Base == RootPtrMark) {
290
assert(Offset == PastEndMark && "cannot get base of a block");
291
return Pointer(asBlockPointer().Pointee, asBlockPointer().Base, 0);
292
}
293
unsigned NewBase = asBlockPointer().Base - getInlineDesc()->Offset;
294
return Pointer(asBlockPointer().Pointee, NewBase, NewBase);
295
}
296
/// Returns the parent array.
297
[[nodiscard]] Pointer getArray() const {
298
if (asBlockPointer().Base == RootPtrMark) {
299
assert(Offset != 0 && Offset != PastEndMark && "not an array element");
300
return Pointer(asBlockPointer().Pointee, asBlockPointer().Base, 0);
301
}
302
assert(Offset != asBlockPointer().Base && "not an array element");
303
return Pointer(asBlockPointer().Pointee, asBlockPointer().Base,
304
asBlockPointer().Base);
305
}
306
307
/// Accessors for information about the innermost field.
308
const Descriptor *getFieldDesc() const {
309
if (isIntegralPointer())
310
return asIntPointer().Desc;
311
312
if (isRoot())
313
return getDeclDesc();
314
return getInlineDesc()->Desc;
315
}
316
317
/// Returns the type of the innermost field.
318
QualType getType() const {
319
if (inPrimitiveArray() && Offset != asBlockPointer().Base) {
320
// Unfortunately, complex and vector types are not array types in clang,
321
// but they are for us.
322
if (const auto *AT = getFieldDesc()->getType()->getAsArrayTypeUnsafe())
323
return AT->getElementType();
324
if (const auto *CT = getFieldDesc()->getType()->getAs<ComplexType>())
325
return CT->getElementType();
326
if (const auto *CT = getFieldDesc()->getType()->getAs<VectorType>())
327
return CT->getElementType();
328
}
329
return getFieldDesc()->getType();
330
}
331
332
[[nodiscard]] Pointer getDeclPtr() const {
333
return Pointer(asBlockPointer().Pointee);
334
}
335
336
/// Returns the element size of the innermost field.
337
size_t elemSize() const {
338
if (isIntegralPointer()) {
339
if (!asIntPointer().Desc)
340
return 1;
341
return asIntPointer().Desc->getElemSize();
342
}
343
344
if (asBlockPointer().Base == RootPtrMark)
345
return getDeclDesc()->getSize();
346
return getFieldDesc()->getElemSize();
347
}
348
/// Returns the total size of the innermost field.
349
size_t getSize() const {
350
assert(isBlockPointer());
351
return getFieldDesc()->getSize();
352
}
353
354
/// Returns the offset into an array.
355
unsigned getOffset() const {
356
assert(Offset != PastEndMark && "invalid offset");
357
if (asBlockPointer().Base == RootPtrMark)
358
return Offset;
359
360
unsigned Adjust = 0;
361
if (Offset != asBlockPointer().Base) {
362
if (getFieldDesc()->ElemDesc)
363
Adjust = sizeof(InlineDescriptor);
364
else
365
Adjust = sizeof(InitMapPtr);
366
}
367
return Offset - asBlockPointer().Base - Adjust;
368
}
369
370
/// Whether this array refers to an array, but not
371
/// to the first element.
372
bool isArrayRoot() const {
373
return inArray() && Offset == asBlockPointer().Base;
374
}
375
376
/// Checks if the innermost field is an array.
377
bool inArray() const {
378
if (isBlockPointer())
379
return getFieldDesc()->IsArray;
380
return false;
381
}
382
/// Checks if the structure is a primitive array.
383
bool inPrimitiveArray() const {
384
if (isBlockPointer())
385
return getFieldDesc()->isPrimitiveArray();
386
return false;
387
}
388
/// Checks if the structure is an array of unknown size.
389
bool isUnknownSizeArray() const {
390
if (!isBlockPointer())
391
return false;
392
return getFieldDesc()->isUnknownSizeArray();
393
}
394
/// Checks if the pointer points to an array.
395
bool isArrayElement() const {
396
if (isBlockPointer())
397
return inArray() && asBlockPointer().Base != Offset;
398
return false;
399
}
400
/// Pointer points directly to a block.
401
bool isRoot() const {
402
if (isZero() || isIntegralPointer())
403
return true;
404
return (asBlockPointer().Base ==
405
asBlockPointer().Pointee->getDescriptor()->getMetadataSize() ||
406
asBlockPointer().Base == 0);
407
}
408
/// If this pointer has an InlineDescriptor we can use to initialize.
409
bool canBeInitialized() const {
410
if (!isBlockPointer())
411
return false;
412
413
return asBlockPointer().Pointee && asBlockPointer().Base > 0;
414
}
415
416
[[nodiscard]] const BlockPointer &asBlockPointer() const {
417
assert(isBlockPointer());
418
return PointeeStorage.BS;
419
}
420
[[nodiscard]] const IntPointer &asIntPointer() const {
421
assert(isIntegralPointer());
422
return PointeeStorage.Int;
423
}
424
bool isBlockPointer() const { return StorageKind == Storage::Block; }
425
bool isIntegralPointer() const { return StorageKind == Storage::Int; }
426
427
/// Returns the record descriptor of a class.
428
const Record *getRecord() const { return getFieldDesc()->ElemRecord; }
429
/// Returns the element record type, if this is a non-primive array.
430
const Record *getElemRecord() const {
431
const Descriptor *ElemDesc = getFieldDesc()->ElemDesc;
432
return ElemDesc ? ElemDesc->ElemRecord : nullptr;
433
}
434
/// Returns the field information.
435
const FieldDecl *getField() const { return getFieldDesc()->asFieldDecl(); }
436
437
/// Checks if the object is a union.
438
bool isUnion() const;
439
440
/// Checks if the storage is extern.
441
bool isExtern() const {
442
if (isBlockPointer())
443
return asBlockPointer().Pointee && asBlockPointer().Pointee->isExtern();
444
return false;
445
}
446
/// Checks if the storage is static.
447
bool isStatic() const {
448
if (isIntegralPointer())
449
return true;
450
assert(asBlockPointer().Pointee);
451
return asBlockPointer().Pointee->isStatic();
452
}
453
/// Checks if the storage is temporary.
454
bool isTemporary() const {
455
if (isBlockPointer()) {
456
assert(asBlockPointer().Pointee);
457
return asBlockPointer().Pointee->isTemporary();
458
}
459
return false;
460
}
461
/// Checks if the storage is a static temporary.
462
bool isStaticTemporary() const { return isStatic() && isTemporary(); }
463
464
/// Checks if the field is mutable.
465
bool isMutable() const {
466
if (!isBlockPointer())
467
return false;
468
return !isRoot() && getInlineDesc()->IsFieldMutable;
469
}
470
471
bool isWeak() const {
472
if (isIntegralPointer())
473
return false;
474
475
assert(isBlockPointer());
476
if (const ValueDecl *VD = getDeclDesc()->asValueDecl())
477
return VD->isWeak();
478
return false;
479
}
480
/// Checks if an object was initialized.
481
bool isInitialized() const;
482
/// Checks if the object is active.
483
bool isActive() const {
484
if (!isBlockPointer())
485
return true;
486
return isRoot() || getInlineDesc()->IsActive;
487
}
488
/// Checks if a structure is a base class.
489
bool isBaseClass() const { return isField() && getInlineDesc()->IsBase; }
490
bool isVirtualBaseClass() const {
491
return isField() && getInlineDesc()->IsVirtualBase;
492
}
493
/// Checks if the pointer points to a dummy value.
494
bool isDummy() const {
495
if (!isBlockPointer())
496
return false;
497
498
if (!asBlockPointer().Pointee)
499
return false;
500
501
return getDeclDesc()->isDummy();
502
}
503
504
/// Checks if an object or a subfield is mutable.
505
bool isConst() const {
506
if (isIntegralPointer())
507
return true;
508
return isRoot() ? getDeclDesc()->IsConst : getInlineDesc()->IsConst;
509
}
510
511
/// Returns the declaration ID.
512
std::optional<unsigned> getDeclID() const {
513
if (isBlockPointer()) {
514
assert(asBlockPointer().Pointee);
515
return asBlockPointer().Pointee->getDeclID();
516
}
517
return std::nullopt;
518
}
519
520
/// Returns the byte offset from the start.
521
unsigned getByteOffset() const {
522
if (isIntegralPointer())
523
return asIntPointer().Value + Offset;
524
if (isOnePastEnd())
525
return PastEndMark;
526
return Offset;
527
}
528
529
/// Returns the number of elements.
530
unsigned getNumElems() const {
531
if (isIntegralPointer())
532
return ~unsigned(0);
533
return getSize() / elemSize();
534
}
535
536
const Block *block() const { return asBlockPointer().Pointee; }
537
538
/// Returns the index into an array.
539
int64_t getIndex() const {
540
if (!isBlockPointer())
541
return 0;
542
543
if (isZero())
544
return 0;
545
546
// narrow()ed element in a composite array.
547
if (asBlockPointer().Base > sizeof(InlineDescriptor) &&
548
asBlockPointer().Base == Offset)
549
return 0;
550
551
if (auto ElemSize = elemSize())
552
return getOffset() / ElemSize;
553
return 0;
554
}
555
556
/// Checks if the index is one past end.
557
bool isOnePastEnd() const {
558
if (isIntegralPointer())
559
return false;
560
561
if (!asBlockPointer().Pointee)
562
return false;
563
564
if (isUnknownSizeArray())
565
return false;
566
567
return isElementPastEnd() || isPastEnd() ||
568
(getSize() == getOffset() && !isZeroSizeArray());
569
}
570
571
/// Checks if the pointer points past the end of the object.
572
bool isPastEnd() const {
573
if (isIntegralPointer())
574
return false;
575
576
return !isZero() && Offset > PointeeStorage.BS.Pointee->getSize();
577
}
578
579
/// Checks if the pointer is an out-of-bounds element pointer.
580
bool isElementPastEnd() const { return Offset == PastEndMark; }
581
582
/// Checks if the pointer is pointing to a zero-size array.
583
bool isZeroSizeArray() const { return getFieldDesc()->isZeroSizeArray(); }
584
585
/// Dereferences the pointer, if it's live.
586
template <typename T> T &deref() const {
587
assert(isLive() && "Invalid pointer");
588
assert(isBlockPointer());
589
assert(asBlockPointer().Pointee);
590
assert(isDereferencable());
591
assert(Offset + sizeof(T) <=
592
asBlockPointer().Pointee->getDescriptor()->getAllocSize());
593
594
if (isArrayRoot())
595
return *reinterpret_cast<T *>(asBlockPointer().Pointee->rawData() +
596
asBlockPointer().Base + sizeof(InitMapPtr));
597
598
return *reinterpret_cast<T *>(asBlockPointer().Pointee->rawData() + Offset);
599
}
600
601
/// Dereferences a primitive element.
602
template <typename T> T &elem(unsigned I) const {
603
assert(I < getNumElems());
604
assert(isBlockPointer());
605
assert(asBlockPointer().Pointee);
606
return reinterpret_cast<T *>(asBlockPointer().Pointee->data() +
607
sizeof(InitMapPtr))[I];
608
}
609
610
/// Whether this block can be read from at all. This is only true for
611
/// block pointers that point to a valid location inside that block.
612
bool isDereferencable() const {
613
if (!isBlockPointer())
614
return false;
615
if (isPastEnd())
616
return false;
617
618
return true;
619
}
620
621
/// Initializes a field.
622
void initialize() const;
623
/// Activats a field.
624
void activate() const;
625
/// Deactivates an entire strurcutre.
626
void deactivate() const;
627
628
/// Compare two pointers.
629
ComparisonCategoryResult compare(const Pointer &Other) const {
630
if (!hasSameBase(*this, Other))
631
return ComparisonCategoryResult::Unordered;
632
633
if (Offset < Other.Offset)
634
return ComparisonCategoryResult::Less;
635
else if (Offset > Other.Offset)
636
return ComparisonCategoryResult::Greater;
637
638
return ComparisonCategoryResult::Equal;
639
}
640
641
/// Checks if two pointers are comparable.
642
static bool hasSameBase(const Pointer &A, const Pointer &B);
643
/// Checks if two pointers can be subtracted.
644
static bool hasSameArray(const Pointer &A, const Pointer &B);
645
646
/// Prints the pointer.
647
void print(llvm::raw_ostream &OS) const;
648
649
private:
650
friend class Block;
651
friend class DeadBlock;
652
friend class MemberPointer;
653
friend class InterpState;
654
friend struct InitMap;
655
friend class DynamicAllocator;
656
657
Pointer(Block *Pointee, unsigned Base, uint64_t Offset);
658
659
/// Returns the embedded descriptor preceding a field.
660
InlineDescriptor *getInlineDesc() const {
661
assert(asBlockPointer().Base != sizeof(GlobalInlineDescriptor));
662
assert(asBlockPointer().Base <= asBlockPointer().Pointee->getSize());
663
return getDescriptor(asBlockPointer().Base);
664
}
665
666
/// Returns a descriptor at a given offset.
667
InlineDescriptor *getDescriptor(unsigned Offset) const {
668
assert(Offset != 0 && "Not a nested pointer");
669
assert(isBlockPointer());
670
assert(!isZero());
671
return reinterpret_cast<InlineDescriptor *>(
672
asBlockPointer().Pointee->rawData() + Offset) -
673
1;
674
}
675
676
/// Returns a reference to the InitMapPtr which stores the initialization map.
677
InitMapPtr &getInitMap() const {
678
assert(isBlockPointer());
679
assert(!isZero());
680
return *reinterpret_cast<InitMapPtr *>(asBlockPointer().Pointee->rawData() +
681
asBlockPointer().Base);
682
}
683
684
/// Offset into the storage.
685
uint64_t Offset = 0;
686
687
/// Previous link in the pointer chain.
688
Pointer *Prev = nullptr;
689
/// Next link in the pointer chain.
690
Pointer *Next = nullptr;
691
692
union {
693
BlockPointer BS;
694
IntPointer Int;
695
} PointeeStorage;
696
Storage StorageKind = Storage::Int;
697
};
698
699
inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Pointer &P) {
700
P.print(OS);
701
return OS;
702
}
703
704
} // namespace interp
705
} // namespace clang
706
707
#endif
708
709