This patch adds a check to detect changing the active union member during initialization of the union. It uses the CONSTRUCTOR_NO_CLEARING flag as a proxy for whether the non-empty CONSTRUCTOR of UNION_TYPE we're assigning to in cxx_eval_store_expression is in the process of being initialized, which seems to work well.
In order for this check to work reliably, we also have to adjust cxx_eval_bare_aggregate to set the active union member before processing the initializer. Does this look OK to commit after testing? gcc/cp/ChangeLog: PR c++/94066 * constexpr.c (cxx_eval_bare_aggregate): When constructing a union, always set the active union member before evaluating the initializer. Relax assertion that verifies the index of the constructor element we're initializing hasn't been changed. (cxx_eval_store_expression): Diagnose changing the active union member while the union is in the process of being initialized. gcc/testsuite/ChangeLog: PR c++/94066 * g++.dg/cpp1y/pr94066.C: New test. * g++.dg/cpp1y/pr94066-2.C: New test. * g++.dg/cpp1y/pr94066-3.C: New test. --- gcc/cp/constexpr.c | 25 ++++++++++++++++++++++++- gcc/testsuite/g++.dg/cpp1y/pr94066-2.C | 19 +++++++++++++++++++ gcc/testsuite/g++.dg/cpp1y/pr94066-3.C | 18 ++++++++++++++++++ gcc/testsuite/g++.dg/cpp1y/pr94066.C | 18 ++++++++++++++++++ 4 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 gcc/testsuite/g++.dg/cpp1y/pr94066-2.C create mode 100644 gcc/testsuite/g++.dg/cpp1y/pr94066-3.C create mode 100644 gcc/testsuite/g++.dg/cpp1y/pr94066.C diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c index 192face9a3a..97fe5572f71 100644 --- a/gcc/cp/constexpr.c +++ b/gcc/cp/constexpr.c @@ -3751,6 +3751,11 @@ cxx_eval_bare_aggregate (const constexpr_ctx *ctx, tree t, /* If we built a new CONSTRUCTOR, attach it now so that other initializers can refer to it. */ CONSTRUCTOR_APPEND_ELT (*p, index, new_ctx.ctor); + else if (TREE_CODE (type) == UNION_TYPE) + /* If we're constructing a union, set the active union member now so + that we can later detect if the initializer attempts to activate + another member. */ + CONSTRUCTOR_APPEND_ELT (*p, index, NULL_TREE); tree elt = cxx_eval_constant_expression (&new_ctx, value, lval, non_constant_p, overflow_p); @@ -3784,7 +3789,12 @@ cxx_eval_bare_aggregate (const constexpr_ctx *ctx, tree t, } else { - if (new_ctx.ctor != ctx->ctor) + if (TREE_CODE (type) == UNION_TYPE + && (*p)->last().index != index) + /* The initializer may have erroneously changed the active union + member we were initializing. */ + gcc_assert (*non_constant_p); + else if (new_ctx.ctor != ctx->ctor) { /* We appended this element above; update the value. */ gcc_assert ((*p)->last().index == index); @@ -4647,6 +4657,19 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t, index); *non_constant_p = true; } + else if (TREE_CODE (t) == MODIFY_EXPR + && CONSTRUCTOR_NO_CLEARING (*valp)) + { + /* Diagnose changing the active union member while the union + is in the process of being initialized. */ + if (!ctx->quiet) + error_at (cp_expr_loc_or_input_loc (t), + "change of the active member of a union " + "from %qD to %qD during initialization", + CONSTRUCTOR_ELT (*valp, 0)->index, + index); + *non_constant_p = true; + } /* Changing active member. */ vec_safe_truncate (CONSTRUCTOR_ELTS (*valp), 0); no_zero_init = true; diff --git a/gcc/testsuite/g++.dg/cpp1y/pr94066-2.C b/gcc/testsuite/g++.dg/cpp1y/pr94066-2.C new file mode 100644 index 00000000000..1c00b650961 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/pr94066-2.C @@ -0,0 +1,19 @@ +// PR c++/94066 +// { dg-do compile { target c++14 } } + +struct A { long x; }; + +union U; +constexpr A foo(U *up); + +union U { + U() = default; + A a = foo(this); int y; +}; + +constexpr A foo(U *up) { + up->y = 11; // { dg-error "'U::a' to 'U::y'" } + return {42}; +} + +extern constexpr U u = {}; diff --git a/gcc/testsuite/g++.dg/cpp1y/pr94066-3.C b/gcc/testsuite/g++.dg/cpp1y/pr94066-3.C new file mode 100644 index 00000000000..6bf1ec81885 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/pr94066-3.C @@ -0,0 +1,18 @@ +// PR c++/94066 +// { dg-do compile { target c++14 } } + +struct A { long x; }; + +union U; +constexpr int foo(U *up); + +union U { + int a = foo(this); int y; +}; + +constexpr int foo(U *up) { + up->y = 11; // { dg-error "'U::a' to 'U::y'" } + return {42}; +} + +extern constexpr U u = {}; diff --git a/gcc/testsuite/g++.dg/cpp1y/pr94066.C b/gcc/testsuite/g++.dg/cpp1y/pr94066.C new file mode 100644 index 00000000000..6725c8c737f --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/pr94066.C @@ -0,0 +1,18 @@ +// PR c++/94066 +// { dg-do compile { target c++14 } } + +struct A { long x; }; + +union U; +constexpr A foo(U *up); + +union U { + A a = foo(this); int y; +}; + +constexpr A foo(U *up) { + up->y = 11; // { dg-error "'U::a' to 'U::y'" } + return {42}; +} + +extern constexpr U u = {}; -- 2.26.0.rc1.11.g30e9940356