]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++: loop over array elts w/o explicit init [PR92385]
authorJason Merrill <jason@redhat.com>
Sat, 1 Jan 2022 21:00:09 +0000 (16:00 -0500)
committerJason Merrill <jason@redhat.com>
Fri, 7 Jan 2022 00:23:16 +0000 (19:23 -0500)
The PR complains that initializing a large array with {} takes a long time
to compile; this was because digest_init would turn {} into a long
CONSTRUCTOR with an initializer for each element, instead of more sensibly
generating a loop.  The standard doesn't specify this implementation, but it
does allow for it by specifying that a temporary created "when a default
constructor is called to initialize an element of an array with no
corresponding initializer" is destroyed "before the construction of the next
array element, if any." rather than living until the end of the complete
object initialization as usual.

This change is also needed before the PR94041 fix extends the lifetime of
temporaries from elements with explicit initializers.

To implement this, I change digest_init so that in cases where
initialization of trailing array elements isn't constant, we return a
VEC_INIT_EXPR instead of a bare CONSTRUCTOR; when it is encountered later,
we call build_vec_init to generate the actual initialization code.

PR c++/92385

gcc/cp/ChangeLog:

* typeck2.c (PICFLAG_VEC_INIT): New.
(process_init_constructor_array): Set it.
(process_init_constructor): Handle it.
(split_nonconstant_init_1): Handle VEC_INIT_EXPR.
* init.c (build_vec_init): Likewise.
* cp-gimplify.c (cp_gimplify_expr): Factor out...
* tree.c (expand_vec_init_expr): ...this function.
(build_vec_init_elt): Handle BRACE_ENCLOSED_INITIALIZER_P.
(build_vec_init_expr): Likewise.
* constexpr.c (cxx_eval_vec_init): Likewise.
(reduced_constant_expression_p): Check arrays before C++20.
* cp-tree.h (expand_vec_init_expr): Declare.

gcc/testsuite/ChangeLog:

* g++.dg/init/array61.C: New test.

gcc/cp/constexpr.c
gcc/cp/cp-gimplify.c
gcc/cp/cp-tree.h
gcc/cp/init.c
gcc/cp/tree.c
gcc/cp/typeck2.c
gcc/testsuite/g++.dg/init/array61.C [new file with mode: 0644]

index 72be45c9e87b7cefb3a0f193539c4e2afb726f20..4a4b347c31d86ebfe485da16a5758c22cf3e6bd7 100644 (file)
@@ -3020,8 +3020,7 @@ reduced_constant_expression_p (tree t)
          if (TREE_CODE (TREE_TYPE (t)) == VECTOR_TYPE)
            /* An initialized vector would have a VECTOR_CST.  */
            return false;
-         else if (cxx_dialect >= cxx20
-                  && TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE)
+         else if (TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE)
            {
              /* There must be a valid constant initializer at every array
                 index.  */
@@ -4955,8 +4954,36 @@ cxx_eval_vec_init (const constexpr_ctx *ctx, tree t,
 {
   tree atype = TREE_TYPE (t);
   tree init = VEC_INIT_EXPR_INIT (t);
-  tree r = cxx_eval_vec_init_1 (ctx, atype, init,
-                               VEC_INIT_EXPR_VALUE_INIT (t),
+  bool value_init = VEC_INIT_EXPR_VALUE_INIT (t);
+  if (!init || !BRACE_ENCLOSED_INITIALIZER_P (init))
+    ;
+  else if (CONSTRUCTOR_NELTS (init) == 0)
+    {
+      /* Handle {} as value-init.  */
+      init = NULL_TREE;
+      value_init = true;
+    }
+  else
+    {
+      /* This is a more complicated case, like needing to loop over trailing
+        elements; call build_vec_init and evaluate the result.  */
+      tsubst_flags_t complain = ctx->quiet ? tf_none : tf_warning_or_error;
+      constexpr_ctx new_ctx = *ctx;
+      if (!ctx->object)
+       {
+         /* We want to have an initialization target for an VEC_INIT_EXPR.
+            If we don't already have one in CTX, use the VEC_INIT_EXPR_SLOT.  */
+         new_ctx.object = VEC_INIT_EXPR_SLOT (t);
+         tree ctor = new_ctx.ctor = build_constructor (atype, NULL);
+         CONSTRUCTOR_NO_CLEARING (ctor) = true;
+         ctx->global->values.put (new_ctx.object, ctor);
+         ctx = &new_ctx;
+       }
+      init = expand_vec_init_expr (ctx->object, t, complain);
+      return cxx_eval_constant_expression (ctx, init, lval, non_constant_p,
+                                          overflow_p);
+    }
+  tree r = cxx_eval_vec_init_1 (ctx, atype, init, value_init,
                                lval, non_constant_p, overflow_p);
   if (*non_constant_p)
     return t;
index 27e78ee3d24221227c7a4c66b0e46dee729e5e7d..e4f244286292945369e31ef1cf7282b2a1b06895 100644 (file)
@@ -462,21 +462,14 @@ cp_gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
 
     case VEC_INIT_EXPR:
       {
-       location_t loc = input_location;
-       tree init = VEC_INIT_EXPR_INIT (*expr_p);
-       int from_array = (init && TREE_CODE (TREE_TYPE (init)) == ARRAY_TYPE);
-       gcc_assert (EXPR_HAS_LOCATION (*expr_p));
-       input_location = EXPR_LOCATION (*expr_p);
-       *expr_p = build_vec_init (VEC_INIT_EXPR_SLOT (*expr_p), NULL_TREE,
-                                 init, VEC_INIT_EXPR_VALUE_INIT (*expr_p),
-                                 from_array,
-                                 tf_warning_or_error);
+       *expr_p = expand_vec_init_expr (NULL_TREE, *expr_p,
+                                       tf_warning_or_error);
+
        hash_set<tree> pset;
        cp_walk_tree (expr_p, cp_fold_r, &pset, NULL);
        cp_genericize_tree (expr_p, false);
        copy_if_shared (expr_p);
        ret = GS_OK;
-       input_location = loc;
       }
       break;
 
index 10ca8098a851a4445355a723ed0b40d6b2b6dca1..0a3697f2f98b7cbf990542c9a00d968a6b82ca8a 100644 (file)
@@ -7779,6 +7779,7 @@ extern bool array_of_runtime_bound_p              (tree);
 extern bool vla_type_p                         (tree);
 extern tree build_array_copy                   (tree);
 extern tree build_vec_init_expr                        (tree, tree, tsubst_flags_t);
+extern tree expand_vec_init_expr               (tree, tree, tsubst_flags_t);
 extern void diagnose_non_constexpr_vec_init    (tree);
 extern tree hash_tree_cons                     (tree, tree, tree);
 extern tree hash_tree_chain                    (tree, tree);
index 2cab4b4cdcef72e4e20b9fb7df8b059cc7ffa99c..2a7dfe2b50561937e593280bbd0e8bc227e9a8ca 100644 (file)
@@ -4337,12 +4337,19 @@ build_vec_init (tree base, tree maxindex, tree init,
       && from_array != 2)
     init = TARGET_EXPR_INITIAL (init);
 
+  if (init && TREE_CODE (init) == VEC_INIT_EXPR)
+    {
+      gcc_checking_assert (false);
+      init = VEC_INIT_EXPR_INIT (init);
+    }
+
   bool direct_init = false;
   if (from_array && init && BRACE_ENCLOSED_INITIALIZER_P (init)
       && CONSTRUCTOR_NELTS (init) == 1)
     {
       tree elt = CONSTRUCTOR_ELT (init, 0)->value;
-      if (TREE_CODE (TREE_TYPE (elt)) == ARRAY_TYPE)
+      if (TREE_CODE (TREE_TYPE (elt)) == ARRAY_TYPE
+         && TREE_CODE (elt) != VEC_INIT_EXPR)
        {
          direct_init = DIRECT_LIST_INIT_P (init);
          init = elt;
@@ -4516,6 +4523,8 @@ build_vec_init (tree base, tree maxindex, tree init,
          current_stmt_tree ()->stmts_are_full_exprs_p = 1;
          if (digested)
            one_init = build2 (INIT_EXPR, type, baseref, elt);
+         else if (TREE_CODE (elt) == VEC_INIT_EXPR)
+           one_init = expand_vec_init_expr (baseref, elt, complain);
          else if (MAYBE_CLASS_TYPE_P (type) || TREE_CODE (type) == ARRAY_TYPE)
            one_init = build_aggr_init (baseref, elt, 0, complain);
          else
index 619980193127053b574aa84b3b4a6158671e7612..8bd1964e867035c839fabfc4c32621ea5f77b446 100644 (file)
@@ -756,13 +756,11 @@ build_vec_init_elt (tree type, tree init, tsubst_flags_t complain)
   else if (init == void_type_node)
     return build_value_init (inner_type, complain);
 
-  gcc_assert (init == NULL_TREE
-             || (same_type_ignoring_top_level_qualifiers_p
-                 (type, TREE_TYPE (init))));
-
   releasing_vec argvec;
-  if (init)
+  if (init && !BRACE_ENCLOSED_INITIALIZER_P (init))
     {
+      gcc_assert (same_type_ignoring_top_level_qualifiers_p
+                 (type, TREE_TYPE (init)));
       tree init_type = strip_array_types (TREE_TYPE (init));
       tree dummy = build_dummy_object (init_type);
       if (!lvalue_p (init))
@@ -788,25 +786,28 @@ build_vec_init_elt (tree type, tree init, tsubst_flags_t complain)
 tree
 build_vec_init_expr (tree type, tree init, tsubst_flags_t complain)
 {
-  tree slot;
-  bool value_init = false;
-  tree elt_init;
-  if (init && TREE_CODE (init) == CONSTRUCTOR)
+  if (init && TREE_CODE (init) == VEC_INIT_EXPR)
     {
-      gcc_assert (!BRACE_ENCLOSED_INITIALIZER_P (init));
-      /* We built any needed constructor calls in digest_init.  */
-      elt_init = init;
+      gcc_checking_assert (false);
+      return init;
     }
+
+  tree elt_init;
+  if (init && TREE_CODE (init) == CONSTRUCTOR
+      && !BRACE_ENCLOSED_INITIALIZER_P (init))
+    /* We built any needed constructor calls in digest_init.  */
+    elt_init = init;
   else
     elt_init = build_vec_init_elt (type, init, complain);
 
+  bool value_init = false;
   if (init == void_type_node)
     {
       value_init = true;
       init = NULL_TREE;
     }
 
-  slot = build_local_temp (type);
+  tree slot = build_local_temp (type);
   init = build2 (VEC_INIT_EXPR, type, slot, init);
   TREE_SIDE_EFFECTS (init) = true;
   SET_EXPR_LOCATION (init, input_location);
@@ -819,6 +820,23 @@ build_vec_init_expr (tree type, tree init, tsubst_flags_t complain)
   return init;
 }
 
+/* Call build_vec_init to expand VEC_INIT into TARGET (for which NULL_TREE
+   means VEC_INIT_EXPR_SLOT).  */
+
+tree
+expand_vec_init_expr (tree target, tree vec_init, tsubst_flags_t complain)
+{
+  iloc_sentinel ils = EXPR_LOCATION (vec_init);
+
+  if (!target)
+    target = VEC_INIT_EXPR_SLOT (vec_init);
+  tree init = VEC_INIT_EXPR_INIT (vec_init);
+  int from_array = (init && TREE_CODE (TREE_TYPE (init)) == ARRAY_TYPE);
+  return build_vec_init (target, NULL_TREE, init,
+                        VEC_INIT_EXPR_VALUE_INIT (vec_init),
+                        from_array, complain);
+}
+
 /* Give a helpful diagnostic for a non-constexpr VEC_INIT_EXPR in a context
    that requires a constant expression.  */
 
index 84cef54e112fc5f2b740e883d7fd0cb8d0af817a..3d4d35e13c626f5dd4a0d83b4b3c4ccf23e53177 100644 (file)
@@ -1317,6 +1317,7 @@ digest_nsdmi_init (tree decl, tree init, tsubst_flags_t complain)
 #define PICFLAG_NOT_ALL_CONSTANT 2
 #define PICFLAG_NOT_ALL_SIMPLE 4
 #define PICFLAG_SIDE_EFFECTS 8
+#define PICFLAG_VEC_INIT 16
 
 /* Given an initializer INIT, return the flag (PICFLAG_*) which better
    describe it.  */
@@ -1460,10 +1461,19 @@ process_init_constructor_array (tree type, tree init, int nested, int flags,
 
        if (next)
          {
-           picflags |= picflag_from_initializer (next);
-           if (len > i+1
+           if (next != error_mark_node
+               && ! seen_error () // Improves error-recovery on anew5.C.
                && (initializer_constant_valid_p (next, TREE_TYPE (next))
-                   == null_pointer_node))
+                   != null_pointer_node))
+             {
+               /* Use VEC_INIT_EXPR for non-constant initialization of
+                  trailing elements with no explicit initializers.  */
+               picflags |= PICFLAG_VEC_INIT;
+               break;
+             }
+
+           picflags |= picflag_from_initializer (next);
+           if (len > i+1)
              {
                tree range = build2 (RANGE_EXPR, size_type_node,
                                     build_int_cst (size_type_node, i),
@@ -1858,6 +1868,13 @@ process_init_constructor (tree type, tree init, int nested, int flags,
       if (!(picflags & PICFLAG_NOT_ALL_SIMPLE))
        TREE_STATIC (init) = 1;
     }
+  if (picflags & PICFLAG_VEC_INIT)
+    {
+      /* Defer default-initialization of array elements with no corresponding
+        initializer-clause until later so we can use a loop.  */
+      TREE_TYPE (init) = init_list_type_node;
+      init = build_vec_init_expr (type, init, complain);
+    }
   return init;
 }
 \f
diff --git a/gcc/testsuite/g++.dg/init/array61.C b/gcc/testsuite/g++.dg/init/array61.C
new file mode 100644 (file)
index 0000000..eaf535c
--- /dev/null
@@ -0,0 +1,16 @@
+// PR c++/92385
+// { dg-do compile { target c++11 } }
+// { dg-additional-options -fdump-tree-gimple }
+// { dg-final { scan-tree-dump-times "item::item" 1 "gimple" } }
+
+struct item {
+  int i;
+  item();
+};
+
+struct item_array {
+  item a[10];
+  item_array();
+};
+
+item_array::item_array() : a{} {}