On Thu, 19 Mar 2020, Patrick Palka wrote:

> 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.

If we can't rely on CONSTRUCTOR_NO_CLEARING to be set iff a CONSTRUCTOR
is in the process of being initialized, then here's an alternative patch
for consideration, that detects this UB in an indirect way and after the
fact.

-- >8 --

This patch adds checks to detect whether an active member of a union has been
changed during initialization of the union.

It add checks in three places, one in cxx_eval_bare_aggregate and two in
cxx_eval_store_expression (for preeval and !preeval).  These checks are indirect
and only detect this UB after the whole initializer has been evaluated and not
at e.g. the exact assignment in the initializer through which the active union
member gets changed, so the source locations attached to the error messages are
suboptimal.

gcc/cp/ChangeLog:

        PR c++/94066
        * constexpr.c (cxx_eval_bare_aggregate): Check whether the active
        member of the union has been changed during evaluation of its
        initializer, and diagnose this if so.
        (cxx_eval_store_expression): Likewise for both preeval and !preeval
        cases.

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                     | 45 +++++++++++++++++++++++++-
 gcc/testsuite/g++.dg/cpp1y/pr94066-2.C | 19 +++++++++++
 gcc/testsuite/g++.dg/cpp1y/pr94066-3.C | 21 ++++++++++++
 gcc/testsuite/g++.dg/cpp1y/pr94066.C   | 18 +++++++++++
 4 files changed, 102 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..bcb6b758980 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -3784,7 +3784,19 @@ cxx_eval_bare_aggregate (const constexpr_ctx *ctx, tree 
t,
        }
       else
        {
-         if (new_ctx.ctor != ctx->ctor)
+         if (TREE_CODE (type) == UNION_TYPE
+             && vec_safe_length (*p)
+             && (**p)[0].index != index)
+           {
+             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",
+                         index,
+                         (**p)[0].index);
+             *non_constant_p = true;
+           }
+         else if (new_ctx.ctor != ctx->ctor)
            {
              /* We appended this element above; update the value.  */
              gcc_assert ((*p)->last().index == index);
@@ -4647,6 +4659,16 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, 
tree t,
                              index);
                  *non_constant_p = true;
                }
+             else if (preeval && TREE_CODE (t) == INIT_EXPR)
+               {
+                 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",
+                             index,
+                             CONSTRUCTOR_ELT (*valp, 0)->index);
+                 *non_constant_p = true;
+               }
              /* Changing active member.  */
              vec_safe_truncate (CONSTRUCTOR_ELTS (*valp), 0);
              no_zero_init = true;
@@ -4761,6 +4783,27 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, 
tree t,
       if (ctors->is_empty())
        /* The hash table might have moved since the get earlier.  */
        valp = ctx->global->values.get (object);
+      else if (TREE_CODE (t) == INIT_EXPR)
+       {
+         tree ctor = ctors->last();
+         if (TREE_CODE (TREE_TYPE (ctor)) == UNION_TYPE)
+           {
+             tree index = TREE_OPERAND (target, 1);
+             gcc_assert (CONSTRUCTOR_NELTS (ctor));
+             gcc_assert (TREE_CODE (index) == FIELD_DECL
+                         && DECL_CONTEXT (index) == TREE_TYPE (ctor));
+             if (CONSTRUCTOR_ELT (ctor, 0)->index != index)
+               {
+                 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",
+                             index,
+                             CONSTRUCTOR_ELT (ctor, 0)->index);
+                 *non_constant_p = true;
+               }
+           }
+       }
     }
 
   /* Don't share a CONSTRUCTOR that might be changed later.  */
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..438e2e436d8
--- /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'" "" { target 
c++17_down } }
+  return {42};
+}
+
+extern constexpr U u = {}; // { dg-error "'U::a' to 'U::y' during 
initialization" }
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..d74e222b421
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/pr94066-3.C
@@ -0,0 +1,21 @@
+// PR c++/94066
+// { dg-do compile { target c++14 } }
+
+struct A { long x; };
+
+union U;
+constexpr int foo(U *up);
+
+union U {
+  U() = default;
+  // { dg-error "'U::a' to 'U::y' during initialization" "" { target c++2a } 
.-1 }
+  int a = foo(this); int y;
+};
+
+constexpr int foo(U *up) {
+  up->y = 11;
+  return {42};
+}
+
+extern constexpr U u = {};
+// { dg-error "'U::a' to 'U::y' during initialization" "" { target c++17_down 
} .-1 }
diff --git a/gcc/testsuite/g++.dg/cpp1y/pr94066.C 
b/gcc/testsuite/g++.dg/cpp1y/pr94066.C
new file mode 100644
index 00000000000..3d981b5540e
--- /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'" "" { target 
c++17_down } }
+  return {42};
+}
+
+extern constexpr U u = {}; // { dg-error "'U::a' to 'U::y' during 
initialization" }
-- 
2.26.0.rc1.11.g30e9940356

Reply via email to