public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
From: Jason Merrill <jason@redhat.com>
To: gcc-patches@gcc.gnu.org
Subject: [pushed 02/11] c++: loop over array elts w/o explicit init [PR92385]
Date: Thu,  6 Jan 2022 19:21:47 -0500	[thread overview]
Message-ID: <20220107002156.2992278-2-jason@redhat.com> (raw)
In-Reply-To: <20220107002156.2992278-1-jason@redhat.com>

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/cp-tree.h                    |  1 +
 gcc/cp/constexpr.c                  | 35 ++++++++++++++++++++---
 gcc/cp/cp-gimplify.c                | 13 ++-------
 gcc/cp/init.c                       | 11 +++++++-
 gcc/cp/tree.c                       | 44 ++++++++++++++++++++---------
 gcc/cp/typeck2.c                    | 23 +++++++++++++--
 gcc/testsuite/g++.dg/init/array61.C | 16 +++++++++++
 7 files changed, 112 insertions(+), 31 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/init/array61.C

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 10ca8098a85..0a3697f2f98 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -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);
diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index 72be45c9e87..4a4b347c31d 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -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;
diff --git a/gcc/cp/cp-gimplify.c b/gcc/cp/cp-gimplify.c
index 27e78ee3d24..e4f24428629 100644
--- a/gcc/cp/cp-gimplify.c
+++ b/gcc/cp/cp-gimplify.c
@@ -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;
 
diff --git a/gcc/cp/init.c b/gcc/cp/init.c
index 2cab4b4cdce..2a7dfe2b505 100644
--- a/gcc/cp/init.c
+++ b/gcc/cp/init.c
@@ -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
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index 61998019312..8bd1964e867 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -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.  */
 
diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c
index 84cef54e112..3d4d35e13c6 100644
--- a/gcc/cp/typeck2.c
+++ b/gcc/cp/typeck2.c
@@ -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
index 00000000000..eaf535c2546
--- /dev/null
+++ b/gcc/testsuite/g++.dg/init/array61.C
@@ -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{} {}
-- 
2.27.0


  reply	other threads:[~2022-01-07  0:22 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-01-07  0:21 [pushed 01/11] c++: don't preevaluate new-initializer Jason Merrill
2022-01-07  0:21 ` Jason Merrill [this message]
2022-01-07  0:21 ` [pushed 03/11] c++: temporary lifetime with aggregate init [PR94041] Jason Merrill
2022-01-07  0:21 ` [pushed 04/11] c++: temporary lifetime with array aggr " Jason Merrill
2022-01-07  0:21 ` [pushed 05/11] c++: EH and partially constructed aggr temp [PR66139] Jason Merrill
2022-01-07  0:21 ` [pushed 06/11] c++: don't cleanup the last aggregate elt Jason Merrill
2022-01-07  0:21 ` [pushed 07/11] c++: keep destroying array after one dtor throws [PR66451] Jason Merrill
2022-01-07  0:21 ` [pushed 08/11] c++: clean up ref-extended temp on throwing dtor [PR53868] Jason Merrill
2022-01-07  0:21 ` [pushed 09/11] c++: destroy retval on throwing cleanup in try [PR33799] Jason Merrill
2022-01-07  0:21 ` [pushed 10/11] c++: nested catch in ctor fn-try-block [PR61611] Jason Merrill
2022-01-07  0:21 ` [pushed 11/11] c++: when delegating constructor throws [PR103711] Jason Merrill

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20220107002156.2992278-2-jason@redhat.com \
    --to=jason@redhat.com \
    --cc=gcc-patches@gcc.gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).