c++: fix array initialization wrong code [PR126335]
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.
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): Assert that DECL_INITIAL
is initially null. Don't clear DECL_INITIAL if build_vec_init
set it. Only clear TREE_READONLY if CODE has side-effects.
gcc/testsuite/ChangeLog:
* g++.dg/init/array68.C: New test.
Reviewed-by: Jason Merrill <jason@redhat.com>
diff --git a/gcc/cp/typeck2.cc b/gcc/cp/typeck2.cc
index 2d7c65c..6c4ed50 100644
--- a/gcc/cp/typeck2.cc
+++ b/gcc/cp/typeck2.cc
@@ -671,7 +671,7 @@
/* The recursive part of split_nonconstant_init. DEST is an lvalue
expression to which INIT should be assigned. INIT is a CONSTRUCTOR.
Return true if the whole of the value was initialized by the
- generated statements. */
+ generated statements or modifying DECL_INITIAL. */
static bool
split_nonconstant_init_1 (tree dest, tree init, bool last,
@@ -940,6 +940,13 @@
if (TREE_CODE (TREE_TYPE (dest)) != ARRAY_TYPE)
flags = make_tree_vector ();
+ /* We are about to call split_nonconstant_init_1 which might
+ set DECL_INITIAL, so make sure we aren't overwriting an
+ existing initializer. Also, if we split out everything,
+ we clear INIT so won't set DECL_INITIAL below. Make
+ sure it's null so that we're not forgetting to clear it. */
+ gcc_assert (!(VAR_P (dest) && DECL_INITIAL (dest)));
+
if (split_nonconstant_init_1 (dest, init, true, &flags))
init = NULL_TREE;
@@ -950,8 +957,16 @@
code = pop_stmt_list (code);
if (VAR_P (dest) && !is_local_temp (dest))
{
- DECL_INITIAL (dest) = init;
- TREE_READONLY (dest) = 0;
+ /* 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;
+ if (TREE_SIDE_EFFECTS (code))
+ 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 0000000..f9afda7
--- /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; }