Re: [PATCH] c++: fix array initialization wrong code [PR126335]
Patrick Palka <[email protected]>
| Newsgroups | gmane.comp.gcc.patches |
|---|---|
| Message-ID | <b196860f-e14b-99e8-2ac4-42312f265d68@idea> |
On Fri, 24 Jul 2026, Marek Polacek wrote:
> Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?
>
> -- >8 --
> This is a wrong-code problem starting with the recent check_initializer
> simplification (r17-1661). I thought the fix would be to bring some of
> those dropped conditions back, but now I think the change just uncovered
> a latent bug.
FWIW it seems the aforementioned latent bug causes PR126472 and your
patch fixes it on trunk! I wonder if backporting this patch to the 16
branch would make sense without r17-1661?
>
> Since r17-1661, when initializing 'm' of type 'M[2]' we no longer call
> build_aggr_init_full_exprs in check_initializer; instead, we go on to
> store_init_value -> split_nonconstant_init. There we arrive with:
>
> {{.a=TARGET_EXPR <D.3136, <<< Unknown tree: aggr_init_expr
> 3
> operator""_s
> D.3136 >>>>, .b=TARGET_EXPR <D.3139, <<< Unknown tree: aggr_init_expr
> 3
> operator""_s
> D.3139 >>>>}, {.a={.p=&empty.str}, .b={.p=&empty.str}}}
>
> which so far seems OK. The type is an array so split_nonconstant_init_1
> delegates to build_vec_init and returns true which, as the comment says,
> should mean that "the whole of the value was initialized by the generated
> statements". This is inaccurate: since try_const and do_static_init are
> both true in build_vec_init, we have split out the constant initializer
> (the {.a={.p=&empty.str}, .b={.p=&empty.str}} part) into DECL_INITIAL:
>
> 5374 else if (do_static_init && !vec_safe_is_empty (const_vec))
> 5375 DECL_INITIAL (obase) = build_constructor (atype, const_vec);
>
> so we have both dynamic and static initializers. But since
> split_nonconstant_init_1 returns bool, it's not ready to signal this case
> to split_nonconstant_init, which then does:
>
> 943 if (split_nonconstant_init_1 (dest, init, true, &flags))
> 944 init = NULL_TREE;
>
> and then overwrites DECL_INITIAL (dest). So we've lost a half of the
> initializer and got wrong-code as the result.
>
> This patch fixes it by not throwing away the DECL_INITIAL that
> build_vec_init set for us. I suppose another approach would be
> to somehow change split_nonconstant_init_1/ARRAY_TYPE to follow
> the element pruning/add_stmt like the rest of the function, but that
> seems more complicated.
>
> PR c++/126335
>
> gcc/cp/ChangeLog:
>
> * typeck2.cc (split_nonconstant_init): Don't clear DECL_INITIAL
> if build_vec_init set it.
>
> gcc/testsuite/ChangeLog:
>
> * g++.dg/init/array68.C: New test.
> ---
> gcc/cp/typeck2.cc | 9 ++++++++-
> gcc/testsuite/g++.dg/init/array68.C | 19 +++++++++++++++++++
> 2 files changed, 27 insertions(+), 1 deletion(-)
> create mode 100644 gcc/testsuite/g++.dg/init/array68.C
>
> diff --git a/gcc/cp/typeck2.cc b/gcc/cp/typeck2.cc
> index 2d7c65c3a51..720528e2505 100644
> --- a/gcc/cp/typeck2.cc
> +++ b/gcc/cp/typeck2.cc
> @@ -950,7 +950,14 @@ split_nonconstant_init (tree dest, tree init)
> code = pop_stmt_list (code);
> if (VAR_P (dest) && !is_local_temp (dest))
> {
> - DECL_INITIAL (dest) = init;
> + /* If we are initializing an array, split_nonconstant_init_1
> + might've delegated to build_vec_init in which case it always
> + returns true so we clear INIT. But if we're initializing
> + a static array, build_vec_init can put constant initializers
> + into DECL_INITIAL. Clearing it would mean losing some of the
> + initializers as in c++/126335. */
> + if (init)
> + DECL_INITIAL (dest) = init;
> TREE_READONLY (dest) = 0;
> }
> else if (init)
> diff --git a/gcc/testsuite/g++.dg/init/array68.C b/gcc/testsuite/g++.dg/init/array68.C
> new file mode 100644
> index 00000000000..f9afda7eb53
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/init/array68.C
> @@ -0,0 +1,19 @@
> +// PR c++/126335
> +// { dg-do run { target c++20 } }
> +
> +struct D {};
> +struct Lit {
> + constexpr Lit(char const *) {}
> + D str;
> +};
> +auto empty = Lit("");
> +struct S {
> + D * p;
> + constexpr S() { p = &empty.str; }
> + S(Lit) {}
> + ~S() {}
> +};
> +template<Lit L> S operator ""_s() { return L; }
> +struct M { S a, b; };
> +static M m[2]{{""_s, ""_s}, {}};
> +int main() { return m[1].a.p == nullptr; }
>
> base-commit: b5e6f5d2d435aba79ed8568f19da25378c716714
> --
> 2.55.0
>
>