Skip to content

Commit 9a227ba

Browse files
authored
[clang][Interp] Start implementing unions and changing the active member (#102723)
1 parent a52e486 commit 9a227ba

File tree

10 files changed

+197
-34
lines changed

10 files changed

+197
-34
lines changed

clang/lib/AST/Interp/Compiler.cpp

+10-6
Original file line numberDiff line numberDiff line change
@@ -4739,7 +4739,8 @@ bool Compiler<Emitter>::visitFunc(const FunctionDecl *F) {
47394739
// Classify the return type.
47404740
ReturnType = this->classify(F->getReturnType());
47414741

4742-
auto emitFieldInitializer = [&](const Record::Field *F, unsigned FieldOffset,
4742+
auto emitFieldInitializer = [&](const Record *R, const Record::Field *F,
4743+
unsigned FieldOffset,
47434744
const Expr *InitExpr) -> bool {
47444745
// We don't know what to do with these, so just return false.
47454746
if (InitExpr->getType().isNull())
@@ -4751,6 +4752,8 @@ bool Compiler<Emitter>::visitFunc(const FunctionDecl *F) {
47514752

47524753
if (F->isBitField())
47534754
return this->emitInitThisBitField(*T, F, FieldOffset, InitExpr);
4755+
if (R->isUnion())
4756+
return this->emitInitThisFieldActive(*T, FieldOffset, InitExpr);
47544757
return this->emitInitThisField(*T, FieldOffset, InitExpr);
47554758
}
47564759
// Non-primitive case. Get a pointer to the field-to-initialize
@@ -4762,7 +4765,7 @@ bool Compiler<Emitter>::visitFunc(const FunctionDecl *F) {
47624765
if (!this->visitInitializer(InitExpr))
47634766
return false;
47644767

4765-
return this->emitPopPtr(InitExpr);
4768+
return this->emitFinishInitPop(InitExpr);
47664769
};
47674770

47684771
// Emit custom code if this is a lambda static invoker.
@@ -4786,7 +4789,7 @@ bool Compiler<Emitter>::visitFunc(const FunctionDecl *F) {
47864789
if (const FieldDecl *Member = Init->getMember()) {
47874790
const Record::Field *F = R->getField(Member);
47884791

4789-
if (!emitFieldInitializer(F, F->Offset, InitExpr))
4792+
if (!emitFieldInitializer(R, F, F->Offset, InitExpr))
47904793
return false;
47914794
} else if (const Type *Base = Init->getBaseClass()) {
47924795
const auto *BaseDecl = Base->getAsCXXRecordDecl();
@@ -4814,11 +4817,11 @@ bool Compiler<Emitter>::visitFunc(const FunctionDecl *F) {
48144817
assert(IFD->getChainingSize() >= 2);
48154818

48164819
unsigned NestedFieldOffset = 0;
4820+
const Record *FieldRecord = nullptr;
48174821
const Record::Field *NestedField = nullptr;
48184822
for (const NamedDecl *ND : IFD->chain()) {
48194823
const auto *FD = cast<FieldDecl>(ND);
4820-
const Record *FieldRecord =
4821-
this->P.getOrCreateRecord(FD->getParent());
4824+
FieldRecord = this->P.getOrCreateRecord(FD->getParent());
48224825
assert(FieldRecord);
48234826

48244827
NestedField = FieldRecord->getField(FD);
@@ -4828,7 +4831,8 @@ bool Compiler<Emitter>::visitFunc(const FunctionDecl *F) {
48284831
}
48294832
assert(NestedField);
48304833

4831-
if (!emitFieldInitializer(NestedField, NestedFieldOffset, InitExpr))
4834+
if (!emitFieldInitializer(FieldRecord, NestedField, NestedFieldOffset,
4835+
InitExpr))
48324836
return false;
48334837
} else {
48344838
assert(Init->isDelegatingInitializer());

clang/lib/AST/Interp/Descriptor.cpp

+30-19
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ using namespace clang;
2020
using namespace clang::interp;
2121

2222
template <typename T>
23-
static void ctorTy(Block *, std::byte *Ptr, bool, bool, bool,
23+
static void ctorTy(Block *, std::byte *Ptr, bool, bool, bool, bool,
2424
const Descriptor *) {
2525
new (Ptr) T();
2626
}
@@ -40,7 +40,7 @@ static void moveTy(Block *, const std::byte *Src, std::byte *Dst,
4040
}
4141

4242
template <typename T>
43-
static void ctorArrayTy(Block *, std::byte *Ptr, bool, bool, bool,
43+
static void ctorArrayTy(Block *, std::byte *Ptr, bool, bool, bool, bool,
4444
const Descriptor *D) {
4545
new (Ptr) InitMapPtr(std::nullopt);
4646

@@ -83,7 +83,8 @@ static void moveArrayTy(Block *, const std::byte *Src, std::byte *Dst,
8383
}
8484

8585
static void ctorArrayDesc(Block *B, std::byte *Ptr, bool IsConst,
86-
bool IsMutable, bool IsActive, const Descriptor *D) {
86+
bool IsMutable, bool IsActive, bool InUnion,
87+
const Descriptor *D) {
8788
const unsigned NumElems = D->getNumElems();
8889
const unsigned ElemSize =
8990
D->ElemDesc->getAllocSize() + sizeof(InlineDescriptor);
@@ -102,9 +103,11 @@ static void ctorArrayDesc(Block *B, std::byte *Ptr, bool IsConst,
102103
Desc->IsActive = IsActive;
103104
Desc->IsConst = IsConst || D->IsConst;
104105
Desc->IsFieldMutable = IsMutable || D->IsMutable;
106+
Desc->InUnion = InUnion;
107+
105108
if (auto Fn = D->ElemDesc->CtorFn)
106109
Fn(B, ElemLoc, Desc->IsConst, Desc->IsFieldMutable, IsActive,
107-
D->ElemDesc);
110+
Desc->InUnion || SD->isUnion(), D->ElemDesc);
108111
}
109112
}
110113

@@ -146,25 +149,26 @@ static void moveArrayDesc(Block *B, const std::byte *Src, std::byte *Dst,
146149
}
147150

148151
static void initField(Block *B, std::byte *Ptr, bool IsConst, bool IsMutable,
149-
bool IsActive, bool IsUnion, const Descriptor *D,
150-
unsigned FieldOffset) {
152+
bool IsActive, bool IsUnionField, bool InUnion,
153+
const Descriptor *D, unsigned FieldOffset) {
151154
auto *Desc = reinterpret_cast<InlineDescriptor *>(Ptr + FieldOffset) - 1;
152155
Desc->Offset = FieldOffset;
153156
Desc->Desc = D;
154157
Desc->IsInitialized = D->IsArray;
155158
Desc->IsBase = false;
156-
Desc->IsActive = IsActive && !IsUnion;
159+
Desc->IsActive = IsActive && !IsUnionField;
160+
Desc->InUnion = InUnion;
157161
Desc->IsConst = IsConst || D->IsConst;
158162
Desc->IsFieldMutable = IsMutable || D->IsMutable;
159163

160164
if (auto Fn = D->CtorFn)
161165
Fn(B, Ptr + FieldOffset, Desc->IsConst, Desc->IsFieldMutable,
162-
Desc->IsActive, D);
166+
Desc->IsActive, InUnion || D->isUnion(), D);
163167
}
164168

165169
static void initBase(Block *B, std::byte *Ptr, bool IsConst, bool IsMutable,
166-
bool IsActive, const Descriptor *D, unsigned FieldOffset,
167-
bool IsVirtualBase) {
170+
bool IsActive, bool InUnion, const Descriptor *D,
171+
unsigned FieldOffset, bool IsVirtualBase) {
168172
assert(D);
169173
assert(D->ElemRecord);
170174

@@ -180,21 +184,26 @@ static void initBase(Block *B, std::byte *Ptr, bool IsConst, bool IsMutable,
180184
Desc->IsFieldMutable = IsMutable || D->IsMutable;
181185

182186
for (const auto &V : D->ElemRecord->bases())
183-
initBase(B, Ptr + FieldOffset, IsConst, IsMutable, IsActive, V.Desc,
184-
V.Offset, false);
187+
initBase(B, Ptr + FieldOffset, IsConst, IsMutable, IsActive, InUnion,
188+
V.Desc, V.Offset, false);
185189
for (const auto &F : D->ElemRecord->fields())
186-
initField(B, Ptr + FieldOffset, IsConst, IsMutable, IsActive, IsUnion,
187-
F.Desc, F.Offset);
190+
initField(B, Ptr + FieldOffset, IsConst, IsMutable, IsActive, InUnion,
191+
IsUnion, F.Desc, F.Offset);
188192
}
189193

190194
static void ctorRecord(Block *B, std::byte *Ptr, bool IsConst, bool IsMutable,
191-
bool IsActive, const Descriptor *D) {
195+
bool IsActive, bool InUnion, const Descriptor *D) {
192196
for (const auto &V : D->ElemRecord->bases())
193-
initBase(B, Ptr, IsConst, IsMutable, IsActive, V.Desc, V.Offset, false);
194-
for (const auto &F : D->ElemRecord->fields())
195-
initField(B, Ptr, IsConst, IsMutable, IsActive, D->ElemRecord->isUnion(), F.Desc, F.Offset);
197+
initBase(B, Ptr, IsConst, IsMutable, IsActive, false, V.Desc, V.Offset,
198+
false);
199+
for (const auto &F : D->ElemRecord->fields()) {
200+
bool IsUnionField = D->isUnion();
201+
initField(B, Ptr, IsConst, IsMutable, IsActive, IsUnionField,
202+
InUnion || IsUnionField, F.Desc, F.Offset);
203+
}
196204
for (const auto &V : D->ElemRecord->virtual_bases())
197-
initBase(B, Ptr, IsConst, IsMutable, IsActive, V.Desc, V.Offset, true);
205+
initBase(B, Ptr, IsConst, IsMutable, IsActive, false, V.Desc, V.Offset,
206+
true);
198207
}
199208

200209
static void destroyField(Block *B, std::byte *Ptr, const Descriptor *D,
@@ -403,6 +412,8 @@ SourceLocation Descriptor::getLocation() const {
403412
llvm_unreachable("Invalid descriptor type");
404413
}
405414

415+
bool Descriptor::isUnion() const { return isRecord() && ElemRecord->isUnion(); }
416+
406417
InitMap::InitMap(unsigned N)
407418
: UninitFields(N), Data(std::make_unique<T[]>(numFields(N))) {
408419
std::fill_n(data(), numFields(N), 0);

clang/lib/AST/Interp/Descriptor.h

+7-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ using InitMapPtr = std::optional<std::pair<bool, std::shared_ptr<InitMap>>>;
3232
/// inline descriptors of all fields and array elements. It also initializes
3333
/// all the fields which contain non-trivial types.
3434
using BlockCtorFn = void (*)(Block *Storage, std::byte *FieldPtr, bool IsConst,
35-
bool IsMutable, bool IsActive,
35+
bool IsMutable, bool IsActive, bool InUnion,
3636
const Descriptor *FieldDesc);
3737

3838
/// Invoked when a block is destroyed. Invokes the destructors of all
@@ -83,11 +83,15 @@ struct InlineDescriptor {
8383
/// Flag indicating if the field is an embedded base class.
8484
LLVM_PREFERRED_TYPE(bool)
8585
unsigned IsBase : 1;
86+
/// Flag inidcating if the field is a virtual base class.
8687
LLVM_PREFERRED_TYPE(bool)
8788
unsigned IsVirtualBase : 1;
8889
/// Flag indicating if the field is the active member of a union.
8990
LLVM_PREFERRED_TYPE(bool)
9091
unsigned IsActive : 1;
92+
/// Flat indicating if this field is in a union (even if nested).
93+
unsigned InUnion : 1;
94+
LLVM_PREFERRED_TYPE(bool)
9195
/// Flag indicating if the field is mutable (if in a record).
9296
LLVM_PREFERRED_TYPE(bool)
9397
unsigned IsFieldMutable : 1;
@@ -250,6 +254,8 @@ struct Descriptor final {
250254
bool isArray() const { return IsArray; }
251255
/// Checks if the descriptor is of a record.
252256
bool isRecord() const { return !IsArray && ElemRecord; }
257+
/// Checks if the descriptor is of a union.
258+
bool isUnion() const;
253259
/// Checks if this is a dummy descriptor.
254260
bool isDummy() const { return IsDummy; }
255261

clang/lib/AST/Interp/Disasm.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,8 @@ LLVM_DUMP_METHOD void Descriptor::dump(llvm::raw_ostream &OS) const {
226226
OS << " primitive-array";
227227
else if (isCompositeArray())
228228
OS << " composite-array";
229+
else if (isUnion())
230+
OS << " union";
229231
else if (isRecord())
230232
OS << " record";
231233
else if (isPrimitive())
@@ -250,6 +252,7 @@ LLVM_DUMP_METHOD void InlineDescriptor::dump(llvm::raw_ostream &OS) const {
250252
OS << "IsInitialized: " << IsInitialized << "\n";
251253
OS << "IsBase: " << IsBase << "\n";
252254
OS << "IsActive: " << IsActive << "\n";
255+
OS << "InUnion: " << InUnion << "\n";
253256
OS << "IsFieldMutable: " << IsFieldMutable << "\n";
254257
OS << "Desc: ";
255258
if (Desc)

clang/lib/AST/Interp/Interp.cpp

+18-3
Original file line numberDiff line numberDiff line change
@@ -127,16 +127,31 @@ static bool CheckActive(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
127127

128128
// Get the inactive field descriptor.
129129
const FieldDecl *InactiveField = Ptr.getField();
130+
assert(InactiveField);
130131

131-
// Walk up the pointer chain to find the union which is not active.
132+
// Walk up the pointer chain to find the closest union.
132133
Pointer U = Ptr.getBase();
133-
while (!U.isActive()) {
134+
while (!U.getFieldDesc()->isUnion())
134135
U = U.getBase();
135-
}
136136

137137
// Find the active field of the union.
138138
const Record *R = U.getRecord();
139139
assert(R && R->isUnion() && "Not a union");
140+
141+
// Consider:
142+
// union U {
143+
// struct {
144+
// int x;
145+
// int y;
146+
// } a;
147+
// }
148+
//
149+
// When activating x, we will also activate a. If we now try to read
150+
// from y, we will get to CheckActive, because y is not active. In that
151+
// case we return here and let later code handle this.
152+
if (!llvm::is_contained(R->getDecl()->fields(), InactiveField))
153+
return true;
154+
140155
const FieldDecl *ActiveField = nullptr;
141156
for (unsigned I = 0, N = R->getNumFields(); I < N; ++I) {
142157
const Pointer &Field = U.atField(R->getField(I)->Offset);

clang/lib/AST/Interp/Interp.h

+6-2
Original file line numberDiff line numberDiff line change
@@ -1719,8 +1719,10 @@ bool Store(InterpState &S, CodePtr OpPC) {
17191719
const Pointer &Ptr = S.Stk.peek<Pointer>();
17201720
if (!CheckStore(S, OpPC, Ptr))
17211721
return false;
1722-
if (Ptr.canBeInitialized())
1722+
if (Ptr.canBeInitialized()) {
17231723
Ptr.initialize();
1724+
Ptr.activate();
1725+
}
17241726
Ptr.deref<T>() = Value;
17251727
return true;
17261728
}
@@ -1731,8 +1733,10 @@ bool StorePop(InterpState &S, CodePtr OpPC) {
17311733
const Pointer &Ptr = S.Stk.pop<Pointer>();
17321734
if (!CheckStore(S, OpPC, Ptr))
17331735
return false;
1734-
if (Ptr.canBeInitialized())
1736+
if (Ptr.canBeInitialized()) {
17351737
Ptr.initialize();
1738+
Ptr.activate();
1739+
}
17361740
Ptr.deref<T>() = Value;
17371741
return true;
17381742
}

clang/lib/AST/Interp/InterpBlock.h

+3-2
Original file line numberDiff line numberDiff line change
@@ -110,9 +110,10 @@ class Block final {
110110
void invokeCtor() {
111111
assert(!IsInitialized);
112112
std::memset(rawData(), 0, Desc->getAllocSize());
113-
if (Desc->CtorFn)
113+
if (Desc->CtorFn) {
114114
Desc->CtorFn(this, data(), Desc->IsConst, Desc->IsMutable,
115-
/*isActive=*/true, Desc);
115+
/*isActive=*/true, /*InUnion=*/false, Desc);
116+
}
116117
IsInitialized = true;
117118
}
118119

clang/lib/AST/Interp/Pointer.cpp

+26-1
Original file line numberDiff line numberDiff line change
@@ -388,12 +388,37 @@ void Pointer::initialize() const {
388388
void Pointer::activate() const {
389389
// Field has its bit in an inline descriptor.
390390
assert(PointeeStorage.BS.Base != 0 &&
391-
"Only composite fields can be initialised");
391+
"Only composite fields can be activated");
392392

393393
if (isRoot() && PointeeStorage.BS.Base == sizeof(GlobalInlineDescriptor))
394394
return;
395+
if (!getInlineDesc()->InUnion)
396+
return;
395397

396398
getInlineDesc()->IsActive = true;
399+
400+
// Get the union, iterate over its fields and DEactivate all others.
401+
Pointer UnionPtr = getBase();
402+
while (!UnionPtr.getFieldDesc()->isUnion())
403+
UnionPtr = UnionPtr.getBase();
404+
405+
const Record *UnionRecord = UnionPtr.getRecord();
406+
for (const Record::Field &F : UnionRecord->fields()) {
407+
Pointer FieldPtr = UnionPtr.atField(F.Offset);
408+
if (FieldPtr == *this) {
409+
} else {
410+
FieldPtr.getInlineDesc()->IsActive = false;
411+
// FIXME: Recurse.
412+
}
413+
}
414+
415+
Pointer B = getBase();
416+
while (!B.getFieldDesc()->isUnion()) {
417+
// FIXME: Need to de-activate other fields of parent records.
418+
B.getInlineDesc()->IsActive = true;
419+
assert(B.isActive());
420+
B = B.getBase();
421+
}
397422
}
398423

399424
void Pointer::deactivate() const {

clang/lib/AST/Interp/Pointer.h

+6
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,12 @@ class Pointer {
400400
return getFieldDesc()->IsArray;
401401
return false;
402402
}
403+
bool inUnion() const {
404+
if (isBlockPointer())
405+
return getInlineDesc()->InUnion;
406+
return false;
407+
};
408+
403409
/// Checks if the structure is a primitive array.
404410
bool inPrimitiveArray() const {
405411
if (isBlockPointer())

0 commit comments

Comments
 (0)