Tested x86_64-pc-linux-gnu, applying to trunk.

-- 8< --

The note and example in [class.union] p6 think that placement new can be
used to change the active member of a union, but we didn't support that for
array members in constant-evaluation even after implementing P1330 and
P2747.

First I tried to address this by introducing a CLOBBER_BEGIN_OBJECT for the
entire array, but that broke the resolution of LWG3436, which invokes 'new
T[1]' for an array T, and trying to clobber a multidimensional array when
the actual object is single-dimensional breaks.  So I've raised that issue
with the committee.  Until that is resolved, this patch takes a simpler
approach: allow initialization of an element of an array to make the array
the active member of a union.

        PR c++/121068

gcc/cp/ChangeLog:

        * constexpr.cc (cxx_eval_store_expression): Allow ARRAY_REFs
        when activating an array member of a union.

gcc/testsuite/ChangeLog:

        * g++.dg/cpp2a/constexpr-union6.C: Expect x5 to work.
        * g++.dg/cpp26/constexpr-new4.C: New test.
---
 gcc/cp/constexpr.cc                           | 13 +++++++++++-
 gcc/testsuite/g++.dg/cpp26/constexpr-new4.C   | 21 +++++++++++++++++++
 gcc/testsuite/g++.dg/cpp2a/constexpr-union6.C |  4 ++--
 3 files changed, 35 insertions(+), 3 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp26/constexpr-new4.C

diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc
index ee06858f715..1a77954b5ac 100644
--- a/gcc/cp/constexpr.cc
+++ b/gcc/cp/constexpr.cc
@@ -7736,13 +7736,24 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, 
tree t,
          CONSTRUCTOR_APPEND_ELT (CONSTRUCTOR_ELTS (*valp), first, NULL_TREE);
 
       /* Check for implicit change of active member for a union.  */
+
+      /* LWG3436, CWG2675, c++/121068: The array object model is confused.  For
+        now allow initializing an array element to activate the array.  */
+      auto only_array_refs = [](const releasing_vec &refs)
+      {
+       for (unsigned i = 1; i < refs->length(); i += 3)
+         if (TREE_CODE ((*refs)[i]) != INTEGER_CST)
+           return false;
+       return true;
+      };
+
       if (code == UNION_TYPE
          && (CONSTRUCTOR_NELTS (*valp) == 0
              || CONSTRUCTOR_ELT (*valp, 0)->index != index)
          /* An INIT_EXPR of the last member in an access chain is always OK,
             but still check implicit change of members earlier on; see
             cpp2a/constexpr-union6.C.  */
-         && !(TREE_CODE (t) == INIT_EXPR && refs->is_empty ()))
+         && !(TREE_CODE (t) == INIT_EXPR && only_array_refs (refs)))
        {
          bool has_active_member = CONSTRUCTOR_NELTS (*valp) != 0;
          tree inner = strip_array_types (reftype);
diff --git a/gcc/testsuite/g++.dg/cpp26/constexpr-new4.C 
b/gcc/testsuite/g++.dg/cpp26/constexpr-new4.C
new file mode 100644
index 00000000000..12d8a465bbf
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp26/constexpr-new4.C
@@ -0,0 +1,21 @@
+// PR c++/121068
+// { dg-do compile { target c++26 } }
+
+constexpr void *operator new (__SIZE_TYPE__, void *p) { return p; }
+constexpr void *operator new[] (__SIZE_TYPE__, void *p) { return p; }
+
+consteval int
+foo()
+{
+    using T = int;
+    union { T arr[3]; };
+    new(arr) T[3]; // makes arr active
+    for (int i = 0; i < 3; ++i)
+      arr[i].~T();
+
+    new (arr + 2) T{10}; // A
+
+    return 1;
+};
+
+constexpr int g = foo();
diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-union6.C 
b/gcc/testsuite/g++.dg/cpp2a/constexpr-union6.C
index 00bda531e59..ab8c979dc96 100644
--- a/gcc/testsuite/g++.dg/cpp2a/constexpr-union6.C
+++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-union6.C
@@ -45,9 +45,9 @@ constexpr int test5() {
   union {
     int data[1];
   } u;
-  std::construct_at(u.data, 0);  // { dg-message "in .constexpr. expansion" }
+  std::construct_at(u.data, 0);  // { dg-bogus "in .constexpr. expansion" }
   return 0;
 }
-constexpr int x5 = test5();  // { dg-message "in .constexpr. expansion" }
+constexpr int x5 = test5();  // { dg-bogus "in .constexpr. expansion" }
 
 // { dg-error "accessing (uninitialized member|.* member instead of)" "" { 
target *-*-* } 0 }

base-commit: 9feecd152fd14a7ba1f4d60fbb988864f07f967e
-- 
2.50.0

Reply via email to