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;
+}

Reply via email to