diff --git a/clang/lib/AST/Interp/Interp.cpp b/clang/lib/AST/Interp/Interp.cpp index 588680adb4616f..89ac6938931133 100644 --- a/clang/lib/AST/Interp/Interp.cpp +++ b/clang/lib/AST/Interp/Interp.cpp @@ -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 { @@ -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); diff --git a/clang/lib/AST/Interp/Pointer.cpp b/clang/lib/AST/Interp/Pointer.cpp index f1f7a27c1400dd..466e61666c76e9 100644 --- a/clang/lib/AST/Interp/Pointer.cpp +++ b/clang/lib/AST/Interp/Pointer.cpp @@ -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()); diff --git a/clang/test/AST/Interp/unions.cpp b/clang/test/AST/Interp/unions.cpp index b0b279831405de..a51f30cd9185b4 100644 --- a/clang/test/AST/Interp/unions.cpp +++ b/clang/test/AST/Interp/unions.cpp @@ -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