In the first testcase below, expand_aggr_init_1 sets up t's default
constructor such that the ctor first zero-initializes the entire base b,
followed by calling b's default constructor, the latter of which just
default-initializes the array member b::m via a VEC_INIT_EXPR.

So upon constexpr evaluation of this latter VEC_INIT_EXPR, ctx->ctor is
nonempty due to the prior zero-initialization, and we proceed in
cxx_eval_vec_init to append new constructor_elts to the end of ctx->ctor
without first checking if a matching constructor_elt already exists.
This leads to ctx->ctor having two matching constructor_elts for each
index.

This patch partially fixes this issue by making the RANGE_EXPR
optimization in cxx_eval_vec_init truncate ctx->ctor before adding the
single RANGE_EXPR constructor_elt.  This isn't a complete fix because
the RANGE_EXPR optimization applies only when the constant initializer
is relocatable, so whenever it's not relocatable we can still build up
an invalid CONSTRUCTOR, e.g. if in the first testcase we add an NSDMI
such as 'e *p = this;' to struct e, then the ICE still occurs even with
this patch.

A side benefit of the approach taken by this patch is that constexpr
evaluation of a simple VEC_INIT_EXPR for a high-dimensional array no
longer scales exponentially with the number of dimensions.  This is
because after the RANGE_EXPR optimization, the CONSTRUCTOR for each
array dimension now consists of a single constructor_elt (with index
0...max-1) instead of two constructor_elts (with indexes 0 and 1...max-1
respectively).  This is verified by the second testcase below.

Bootstrapped and regtested on x86_64-pc-linux-gnu.  Does this look OK
for trunk and perhaps for backports?

gcc/cp/ChangeLog:

        PR c++/96282
        * constexpr.c (cxx_eval_vec_init_1): Move the i == 0 test to the
        if statement that guards the RANGE_EXPR optimization. Consider the
        RANGE_EXPR optimization before we append the first element.
        Truncate ctx->ctor when performing the RANGE_EXPR optimization.
        Make the built RANGE_EXPR start at index 0 instead of 1.  Don't
        call unshare_constructor.

gcc/testsuite/ChangeLog:

        PR c++/96282
        * g++.dg/cpp0x/constexpr-array26.C: New test.
        * g++.dg/cpp0x/constexpr-array27.C: New test.
---
 gcc/cp/constexpr.c                            | 36 ++++++++++---------
 .../g++.dg/cpp0x/constexpr-array26.C          | 13 +++++++
 .../g++.dg/cpp0x/constexpr-array27.C          | 18 ++++++++++
 3 files changed, 51 insertions(+), 16 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/constexpr-array26.C
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/constexpr-array27.C

diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index b1c1d249c6e..3808a0713ba 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -4189,7 +4189,7 @@ cxx_eval_vec_init_1 (const constexpr_ctx *ctx, tree 
atype, tree init,
          if (value_init || init == NULL_TREE)
            {
              eltinit = NULL_TREE;
-             reuse = i == 0;
+             reuse = true;
            }
          else
            eltinit = cp_build_array_ref (input_location, init, idx, complain);
@@ -4206,7 +4206,7 @@ cxx_eval_vec_init_1 (const constexpr_ctx *ctx, tree 
atype, tree init,
            return ctx->ctor;
          eltinit = cxx_eval_constant_expression (&new_ctx, init, lval,
                                                  non_constant_p, overflow_p);
-         reuse = i == 0;
+         reuse = true;
        }
       else
        {
@@ -4222,33 +4222,37 @@ cxx_eval_vec_init_1 (const constexpr_ctx *ctx, tree 
atype, tree init,
        }
       if (*non_constant_p && !ctx->quiet)
        break;
-      if (new_ctx.ctor != ctx->ctor)
-       {
-         /* We appended this element above; update the value.  */
-         gcc_assert ((*p)->last().index == idx);
-         (*p)->last().value = eltinit;
-       }
-      else
-       CONSTRUCTOR_APPEND_ELT (*p, idx, eltinit);
+
       /* Reuse the result of cxx_eval_constant_expression call
         from the first iteration to all others if it is a constant
         initializer that doesn't require relocations.  */
-      if (reuse
-         && max > 1
+      if (i == 0
+         && reuse
          && (eltinit == NULL_TREE
              || (initializer_constant_valid_p (eltinit, TREE_TYPE (eltinit))
                  == null_pointer_node)))
        {
          if (new_ctx.ctor != ctx->ctor)
            eltinit = new_ctx.ctor;
-         tree range = build2 (RANGE_EXPR, size_type_node,
-                              build_int_cst (size_type_node, 1),
-                              build_int_cst (size_type_node, max - 1));
-         CONSTRUCTOR_APPEND_ELT (*p, range, unshare_constructor (eltinit));
+         vec_safe_truncate (*p, 0);
+         if (max > 1)
+           idx = build2 (RANGE_EXPR, size_type_node,
+                         build_int_cst (size_type_node, 0),
+                         build_int_cst (size_type_node, max - 1));
+         CONSTRUCTOR_APPEND_ELT (*p, idx, eltinit);
          break;
        }
       else if (i == 0)
        vec_safe_reserve (*p, max);
+
+      if (new_ctx.ctor != ctx->ctor)
+       {
+         /* We appended this element above; update the value.  */
+         gcc_assert ((*p)->last().index == idx);
+         (*p)->last().value = eltinit;
+       }
+      else
+       CONSTRUCTOR_APPEND_ELT (*p, idx, eltinit);
     }
 
   if (!*non_constant_p)
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-array26.C 
b/gcc/testsuite/g++.dg/cpp0x/constexpr-array26.C
new file mode 100644
index 00000000000..274f55a88bf
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-array26.C
@@ -0,0 +1,13 @@
+// PR c++/96282
+// { dg-do compile { target c++11 } }
+
+struct e { bool v = true; };
+
+template<int N>
+struct b { e m[N]; };
+
+template<int N>
+struct t : b<N> { constexpr t() : b<N>() {} };
+
+constexpr t<1> h1;
+constexpr t<42> h2;
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-array27.C 
b/gcc/testsuite/g++.dg/cpp0x/constexpr-array27.C
new file mode 100644
index 00000000000..a5ce3f7be08
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-array27.C
@@ -0,0 +1,18 @@
+// Verify that default initialization an array of aggregates within an 
aggregate
+// does not scale exponentially with the number of dimensions.
+
+// { dg-do compile { target c++11 } }
+// Pass -fsyntax-only to stress only the performance of the frontend.
+// { dg-additional-options "-fsyntax-only" }
+
+struct A
+{
+  int a = 42;
+};
+
+struct B
+{
+  A b[2][2][2][2][2][2][2][2][2][2][2][2][2][2][2][2][2][2][2][2][2][2][2][2];
+};
+
+constexpr B c;
-- 
2.28.0.89.g85b4e0a6dc

Reply via email to