Skip to content

Commit dff2ca4

Browse files
authored
[clang][bytecode] Add special case for anonymous unions (#128681)
This fixes the expected output to match the one of the current interpreter.
1 parent f8948d3 commit dff2ca4

File tree

4 files changed

+48
-18
lines changed

4 files changed

+48
-18
lines changed

clang/lib/AST/ByteCode/Interp.cpp

+14-7
Original file line numberDiff line numberDiff line change
@@ -139,17 +139,19 @@ static bool CheckActive(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
139139

140140
Pointer U = Ptr.getBase();
141141
Pointer C = Ptr;
142-
while (!U.isRoot() && U.inUnion() && !U.isActive()) {
143-
if (U.getField())
144-
C = U;
142+
while (!U.isRoot() && !U.isActive()) {
143+
// A little arbitrary, but this is what the current interpreter does.
144+
// See the AnonymousUnion test in test/AST/ByteCode/unions.cpp.
145+
// GCC's output is more similar to what we would get without
146+
// this condition.
147+
if (U.getRecord() && U.getRecord()->isAnonymousUnion())
148+
break;
149+
150+
C = U;
145151
U = U.getBase();
146152
}
147153
assert(C.isField());
148154

149-
// Get the inactive field descriptor.
150-
const FieldDecl *InactiveField = C.getField();
151-
assert(InactiveField);
152-
153155
// Consider:
154156
// union U {
155157
// struct {
@@ -165,6 +167,11 @@ static bool CheckActive(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
165167
if (!U.getFieldDesc()->isUnion())
166168
return true;
167169

170+
// Get the inactive field descriptor.
171+
assert(!C.isActive());
172+
const FieldDecl *InactiveField = C.getField();
173+
assert(InactiveField);
174+
168175
// Find the active field of the union.
169176
const Record *R = U.getRecord();
170177
assert(R && R->isUnion() && "Not a union");

clang/lib/AST/ByteCode/Pointer.cpp

+15-8
Original file line numberDiff line numberDiff line change
@@ -437,28 +437,35 @@ void Pointer::activate() const {
437437
if (!getInlineDesc()->InUnion)
438438
return;
439439

440-
getInlineDesc()->IsActive = true;
440+
auto activate = [](Pointer &P) -> void {
441+
P.getInlineDesc()->IsActive = true;
442+
};
443+
auto deactivate = [](Pointer &P) -> void {
444+
P.getInlineDesc()->IsActive = false;
445+
};
441446

442-
// Get the union, iterate over its fields and DEactivate all others.
447+
// Unions might be nested etc., so find the topmost Pointer that's
448+
// not in a union anymore.
443449
Pointer UnionPtr = getBase();
444-
while (!UnionPtr.getFieldDesc()->isUnion())
450+
while (!UnionPtr.isRoot() && UnionPtr.inUnion())
445451
UnionPtr = UnionPtr.getBase();
446452

453+
assert(UnionPtr.getFieldDesc()->isUnion());
454+
447455
const Record *UnionRecord = UnionPtr.getRecord();
448456
for (const Record::Field &F : UnionRecord->fields()) {
449457
Pointer FieldPtr = UnionPtr.atField(F.Offset);
450458
if (FieldPtr == *this) {
451459
} else {
452-
FieldPtr.getInlineDesc()->IsActive = false;
460+
deactivate(FieldPtr);
453461
// FIXME: Recurse.
454462
}
455463
}
456464

457-
Pointer B = getBase();
458-
while (!B.isRoot() && B.inUnion()) {
465+
Pointer B = *this;
466+
while (B != UnionPtr) {
467+
activate(B);
459468
// FIXME: Need to de-activate other fields of parent records.
460-
B.getInlineDesc()->IsActive = true;
461-
assert(B.isActive());
462469
B = B.getBase();
463470
}
464471
}

clang/lib/AST/ByteCode/Pointer.h

-3
Original file line numberDiff line numberDiff line change
@@ -494,9 +494,6 @@ class Pointer {
494494
/// Returns the field information.
495495
const FieldDecl *getField() const { return getFieldDesc()->asFieldDecl(); }
496496

497-
/// Checks if the object is a union.
498-
bool isUnion() const;
499-
500497
/// Checks if the storage is extern.
501498
bool isExtern() const {
502499
if (isBlockPointer())

clang/test/AST/ByteCode/unions.cpp

+19
Original file line numberDiff line numberDiff line change
@@ -485,4 +485,23 @@ namespace IFD {
485485
}
486486
static_assert(test());
487487
}
488+
489+
namespace AnonymousUnion {
490+
struct A {
491+
int x;
492+
union { int p, q; };
493+
};
494+
union B {
495+
A a;
496+
int bb;
497+
};
498+
499+
constexpr B return_init_all() {
500+
B b = {.bb = 1};
501+
b.a.x = 2;
502+
return b;
503+
}
504+
static_assert(return_init_all().a.p == 7); // both-error {{}} \
505+
// both-note {{read of member 'p' of union with no active member}}
506+
}
488507
#endif

0 commit comments

Comments
 (0)