The problem in this PR is that we're trying to initialize an array with
members of itself:
int array[10] = { array[3]=5, array[7]=3 };
The C++ front-end, in store_init_value() via cxx_constant_value() and
friends will transform:
{array[3] = 5, array[7] = 3}
into:
{[3]=5, [0]=5, [7]=3, [1]=3}
which looks invalid to output_constructor*(), presumably because it
isn't sorted by increasing index.
Jakub has even gone further to show that for the following:
... = { array[3]=5, array[7]=3, array[7]=8, array[7] = 9 };
things get even worse, because we generate code to write twice into [3]:
{[3]=5, [0]=5, [7]=9, [1]=3, [2]=8, [3]=9}
I took the easy way in cxx_eval_store_expression() and marked the
expression as non_constant_p if we're trying to set an array from
members of itself. This causes us to to generate the initialization of
the self-referencing array[sub] elements as a CLEANUP_POINT_EXPR:
<<cleanup_point <<< Unknown tree: expr_stmt
array[0] = array[3] = 5 >>>>>;
<<cleanup_point <<< Unknown tree: expr_stmt
array[1] = array[7] = 3 >>>>>;
Ultimately this yields correct code:
array:
.zero 40
...
...
_GLOBAL__sub_I_array:
.LFB1:
.cfi_startproc
movl $5, array+12(%rip)
movl $5, array(%rip)
movl $3, array+28(%rip)
movl $3, array+4(%rip)
ret
Is this approach acceptable, or should we be marking this as
non-constant somewhere else in the chain? Or perhaps another approach
would be to handle such constructor holes in the in the varasm code?
Aldy
commit 295d93f60bcbec5b9959a7b3656f10aa0df71c9f
Author: Aldy Hernandez <al...@redhat.com>
Date: Tue Dec 20 06:13:26 2016 -0500
PR c++/78572
* constexpr.c (cxx_eval_store_expression): Avoid array self
references in initialization.
diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index aedd004..ac279ad 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -3313,6 +3313,15 @@ cxx_eval_store_expression (const constexpr_ctx *ctx,
tree t,
}
}
+ /* Initializing an array from a variant of itself is a non-constant.
+ This avoids attemps at generating incorrect self references in
+ something like: int foo[10] = { stuff[3]=8 }. */
+ if (TREE_CODE (target) == ARRAY_REF && object == ctx->object)
+ {
+ *non_constant_p = true;
+ return t;
+ }
+
/* And then find/build up our initializer for the path to the subobject
we're initializing. */
tree *valp;
diff --git a/gcc/testsuite/g++.dg/pr78572.C b/gcc/testsuite/g++.dg/pr78572.C
new file mode 100644
index 0000000..82ab4e3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/pr78572.C
@@ -0,0 +1,9 @@
+// { dg-do compile } */
+
+static int array[10] = { array[3]=5, array[7]=3 };
+
+int
+main ()
+{
+ return 0;
+}