Skip to content

Commit 3b57f6b

Browse files
authored
[clang][Interp] Handle nested unions (#102743)
1 parent 86691f8 commit 3b57f6b

File tree

3 files changed

+70
-13
lines changed

3 files changed

+70
-13
lines changed

clang/lib/AST/Interp/Interp.cpp

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -125,18 +125,15 @@ static bool CheckActive(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
125125
if (Ptr.isActive())
126126
return true;
127127

128-
// Get the inactive field descriptor.
129-
const FieldDecl *InactiveField = Ptr.getField();
130-
assert(InactiveField);
131-
132-
// Walk up the pointer chain to find the closest union.
133128
Pointer U = Ptr.getBase();
134-
while (!U.getFieldDesc()->isUnion())
129+
Pointer C = Ptr;
130+
while (!U.isRoot() && U.inUnion() && !U.isActive()) {
131+
C = U;
135132
U = U.getBase();
136-
137-
// Find the active field of the union.
138-
const Record *R = U.getRecord();
139-
assert(R && R->isUnion() && "Not a union");
133+
}
134+
// Get the inactive field descriptor.
135+
const FieldDecl *InactiveField = C.getField();
136+
assert(InactiveField);
140137

141138
// Consider:
142139
// union U {
@@ -148,10 +145,15 @@ static bool CheckActive(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
148145
//
149146
// When activating x, we will also activate a. If we now try to read
150147
// 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))
148+
// case, our U will be a (not a union). We return here and let later code
149+
// handle this.
150+
if (!U.getFieldDesc()->isUnion())
153151
return true;
154152

153+
// Find the active field of the union.
154+
const Record *R = U.getRecord();
155+
assert(R && R->isUnion() && "Not a union");
156+
155157
const FieldDecl *ActiveField = nullptr;
156158
for (unsigned I = 0, N = R->getNumFields(); I < N; ++I) {
157159
const Pointer &Field = U.atField(R->getField(I)->Offset);

clang/lib/AST/Interp/Pointer.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -413,7 +413,7 @@ void Pointer::activate() const {
413413
}
414414

415415
Pointer B = getBase();
416-
while (!B.getFieldDesc()->isUnion()) {
416+
while (!B.isRoot() && B.inUnion()) {
417417
// FIXME: Need to de-activate other fields of parent records.
418418
B.getInlineDesc()->IsActive = true;
419419
assert(B.isActive());

clang/test/AST/Interp/unions.cpp

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,4 +198,59 @@ namespace UnionMemberDtor {
198198
}
199199
static_assert(foo() == 100);
200200
}
201+
202+
namespace Nested {
203+
union U {
204+
int a;
205+
int b;
206+
};
207+
208+
union U2 {
209+
U u;
210+
U u2;
211+
int x;
212+
int y;
213+
};
214+
215+
constexpr int foo() { // ref-error {{constexpr function never produces a constant expression}}
216+
U2 u;
217+
u.u.a = 10;
218+
int a = u.y; // both-note {{read of member 'y' of union with active member 'u' is not allowed in a constant expression}} \
219+
// ref-note {{read of member 'y' of union with active member 'u' is not allowed in a constant expression}}
220+
221+
return 1;
222+
}
223+
static_assert(foo() == 1); // both-error {{not an integral constant expression}} \
224+
// both-note {{in call to}}
225+
226+
constexpr int foo2() {
227+
U2 u;
228+
u.u.a = 10;
229+
return u.u.a;
230+
}
231+
static_assert(foo2() == 10);
232+
233+
constexpr int foo3() { // ref-error {{constexpr function never produces a constant expression}}
234+
U2 u;
235+
u.u.a = 10;
236+
int a = u.u.b; // both-note {{read of member 'b' of union with active member 'a' is not allowed in a constant expression}} \
237+
// ref-note {{read of member 'b' of union with active member 'a' is not allowed in a constant expression}}
238+
239+
return 1;
240+
}
241+
static_assert(foo3() == 1); // both-error {{not an integral constant expression}} \
242+
// both-note {{in call to}}
243+
244+
constexpr int foo4() { // ref-error {{constexpr function never produces a constant expression}}
245+
U2 u;
246+
247+
u.x = 10;
248+
249+
return u.u.a;// both-note {{read of member 'u' of union with active member 'x' is not allowed in a constant expression}} \
250+
// ref-note {{read of member 'u' of union with active member 'x' is not allowed in a constant expression}}
251+
}
252+
static_assert(foo4() == 1); // both-error {{not an integral constant expression}} \
253+
// both-note {{in call to}}
254+
255+
}
201256
#endif

0 commit comments

Comments
 (0)