https://gcc.gnu.org/g:cce6cc2a791b5617716568a7ae33ef5f71e313cd
commit r17-602-gcce6cc2a791b5617716568a7ae33ef5f71e313cd Author: Marek Polacek <[email protected]> Date: Fri May 15 14:51:29 2026 -0400 c++: another constexpr nested empty object [PR125336] When looking into 125315 I came up with another test that crashes due to an empty object. It still crashes even after Jason's patch. Here we have a subobject nested in an empty object: {.w = {.v = TARGET_EXPR <f(s)>}} w's type is W, an empty union due to [[no_unique_address]], so init_subob_ctx clears the ctor, but then we recurse to {.v = TARGET_EXPR <f(s)>} with a null ctx->ctor so the call to get_or_insert_ctor_field in cxx_eval_bare_aggregate crashes. This fixes the crash similarly to c++/125315. no_unique_address18.C worked fine even before this patch because there we don't have an empty object. But let's test it also. PR c++/125336 gcc/cp/ChangeLog: * constexpr.cc (cxx_eval_bare_aggregate): Don't call get_or_insert_ctor_field when there is no CONSTRUCTOR. Assert is_empty_class. gcc/testsuite/ChangeLog: * g++.dg/cpp2a/no_unique_address17.C: New test. * g++.dg/cpp2a/no_unique_address18.C: New test. Reviewed-by: Jason Merrill <[email protected]> Diff: --- gcc/cp/constexpr.cc | 8 ++++++-- gcc/testsuite/g++.dg/cpp2a/no_unique_address17.C | 12 ++++++++++++ gcc/testsuite/g++.dg/cpp2a/no_unique_address18.C | 12 ++++++++++++ 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc index 701269cd3f60..6c6d2f3fa994 100644 --- a/gcc/cp/constexpr.cc +++ b/gcc/cp/constexpr.cc @@ -6841,7 +6841,11 @@ cxx_eval_bare_aggregate (const constexpr_ctx *ctx, tree t, /* Like in cxx_eval_store_expression, omit entries for empty fields. */ bool no_slot = new_ctx.ctor == NULL_TREE; int pos_hint = -1; - if (new_ctx.ctor != ctx->ctor && !no_slot) + if (!ctx->ctor) + /* The enclosing object could be an empty subobject so we have no + CONSTRUCTOR (c++/125336). */ + gcc_checking_assert (is_empty_class (type)); + else if (new_ctx.ctor != ctx->ctor && !no_slot) { /* If we built a new CONSTRUCTOR, attach it now so that other initializers can refer to it. */ @@ -6883,7 +6887,7 @@ cxx_eval_bare_aggregate (const constexpr_ctx *ctx, tree t, /* This is an initializer for an empty field; now that we've checked that it's constant, we can ignore it. */ changed = true; - else + else if (ctx->ctor) { if (TREE_CODE (type) == UNION_TYPE && (*p)->last().index != index) diff --git a/gcc/testsuite/g++.dg/cpp2a/no_unique_address17.C b/gcc/testsuite/g++.dg/cpp2a/no_unique_address17.C new file mode 100644 index 000000000000..11099698a9bb --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/no_unique_address17.C @@ -0,0 +1,12 @@ +// PR c++/125336 +// { dg-do compile { target c++20 } } +// { dg-prune-output "used but never defined" } + +struct S{ }; +constexpr S& f (S&); +union W { + [[no_unique_address]] S v; +}; +struct R { W w; }; +S s; +auto x = R{{ f(s) }}; diff --git a/gcc/testsuite/g++.dg/cpp2a/no_unique_address18.C b/gcc/testsuite/g++.dg/cpp2a/no_unique_address18.C new file mode 100644 index 000000000000..bea8a65b8a0d --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/no_unique_address18.C @@ -0,0 +1,12 @@ +// PR c++/125336 +// { dg-do compile { target c++20 } } +// { dg-prune-output "used but never defined" } + +struct S { int i; }; +constexpr S& f (S&); +union W { + [[no_unique_address]] S v; +}; +struct R { W w; }; +S s; +auto x = R{{ f(s) }};
