On Fri, 15 May 2026, Marek Polacek wrote:
> Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk/branches?
>
> -- >8 --
> 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.
> ---
> 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(-)
> create mode 100644 gcc/testsuite/g++.dg/cpp2a/no_unique_address17.C
> create mode 100644 gcc/testsuite/g++.dg/cpp2a/no_unique_address18.C
>
> diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc
> index 1fc8a56503c..84896170d05 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. */
> @@ -6892,7 +6896,7 @@ cxx_eval_bare_aggregate (const constexpr_ctx *ctx, tree
> t,
> gcc_assert (is_empty_class (TREE_TYPE (TREE_TYPE (index))));
> 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 00000000000..11099698a9b
> --- /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" }
The "used but never defined" warning implies the testcase is UB right?
Would be nice to have a valid testcase that triggers this bug.
> +
> +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 00000000000..bea8a65b8a0
> --- /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) }};
>
> base-commit: f108e23d2bff50d0ee8cf0d25a08be5432b702b6
> --
> 2.54.0
>
>