https://gcc.gnu.org/g:fdbc5ff61b471076cc9c758fb6c30d62f7ef1c56

commit r16-2432-gfdbc5ff61b471076cc9c758fb6c30d62f7ef1c56
Author: Jason Merrill <ja...@redhat.com>
Date:   Wed Jul 16 11:52:45 2025 -0400

    c++: constexpr union placement new [PR121068]
    
    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.

Diff:
---
 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(-)

diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc
index ee06858f7153..1a77954b5acc 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 000000000000..12d8a465bbf9
--- /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 00bda531e596..ab8c979dc96c 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 }

Reply via email to