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

Reply via email to