Skip to content

Commit

Permalink
[clang][Interp] Handle nested unions (#102743)
Browse files Browse the repository at this point in the history
  • Loading branch information
tbaederr authored Aug 10, 2024
1 parent 86691f8 commit 3b57f6b
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 13 deletions.
26 changes: 14 additions & 12 deletions clang/lib/AST/Interp/Interp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -125,18 +125,15 @@ static bool CheckActive(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
if (Ptr.isActive())
return true;

// Get the inactive field descriptor.
const FieldDecl *InactiveField = Ptr.getField();
assert(InactiveField);

// Walk up the pointer chain to find the closest union.
Pointer U = Ptr.getBase();
while (!U.getFieldDesc()->isUnion())
Pointer C = Ptr;
while (!U.isRoot() && U.inUnion() && !U.isActive()) {
C = U;
U = U.getBase();

// Find the active field of the union.
const Record *R = U.getRecord();
assert(R && R->isUnion() && "Not a union");
}
// Get the inactive field descriptor.
const FieldDecl *InactiveField = C.getField();
assert(InactiveField);

// Consider:
// union U {
Expand All @@ -148,10 +145,15 @@ static bool CheckActive(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
//
// When activating x, we will also activate a. If we now try to read
// from y, we will get to CheckActive, because y is not active. In that
// case we return here and let later code handle this.
if (!llvm::is_contained(R->getDecl()->fields(), InactiveField))
// case, our U will be a (not a union). We return here and let later code
// handle this.
if (!U.getFieldDesc()->isUnion())
return true;

// Find the active field of the union.
const Record *R = U.getRecord();
assert(R && R->isUnion() && "Not a union");

const FieldDecl *ActiveField = nullptr;
for (unsigned I = 0, N = R->getNumFields(); I < N; ++I) {
const Pointer &Field = U.atField(R->getField(I)->Offset);
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/AST/Interp/Pointer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,7 @@ void Pointer::activate() const {
}

Pointer B = getBase();
while (!B.getFieldDesc()->isUnion()) {
while (!B.isRoot() && B.inUnion()) {
// FIXME: Need to de-activate other fields of parent records.
B.getInlineDesc()->IsActive = true;
assert(B.isActive());
Expand Down
55 changes: 55 additions & 0 deletions clang/test/AST/Interp/unions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -198,4 +198,59 @@ namespace UnionMemberDtor {
}
static_assert(foo() == 100);
}

namespace Nested {
union U {
int a;
int b;
};

union U2 {
U u;
U u2;
int x;
int y;
};

constexpr int foo() { // ref-error {{constexpr function never produces a constant expression}}
U2 u;
u.u.a = 10;
int a = u.y; // both-note {{read of member 'y' of union with active member 'u' is not allowed in a constant expression}} \
// ref-note {{read of member 'y' of union with active member 'u' is not allowed in a constant expression}}

return 1;
}
static_assert(foo() == 1); // both-error {{not an integral constant expression}} \
// both-note {{in call to}}

constexpr int foo2() {
U2 u;
u.u.a = 10;
return u.u.a;
}
static_assert(foo2() == 10);

constexpr int foo3() { // ref-error {{constexpr function never produces a constant expression}}
U2 u;
u.u.a = 10;
int a = u.u.b; // both-note {{read of member 'b' of union with active member 'a' is not allowed in a constant expression}} \
// ref-note {{read of member 'b' of union with active member 'a' is not allowed in a constant expression}}

return 1;
}
static_assert(foo3() == 1); // both-error {{not an integral constant expression}} \
// both-note {{in call to}}

constexpr int foo4() { // ref-error {{constexpr function never produces a constant expression}}
U2 u;

u.x = 10;

return u.u.a;// both-note {{read of member 'u' of union with active member 'x' is not allowed in a constant expression}} \
// ref-note {{read of member 'u' of union with active member 'x' is not allowed in a constant expression}}
}
static_assert(foo4() == 1); // both-error {{not an integral constant expression}} \
// both-note {{in call to}}

}
#endif

0 comments on commit 3b57f6b

Please sign in to comment.