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