summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Merrill <jason@redhat.com>2022-01-01 16:00:09 -0500
committerJason Merrill <jason@redhat.com>2022-01-06 19:23:16 -0500
commite948436eab818c527dd60b0ef939c4f42fbe8ba4 (patch)
tree71b7313390be11341e0c975d27076d8387b9c39c
parentad64a821970ef5547d77ece22d302b0e599ae0d2 (diff)
downloadgcc-e948436eab818c527dd60b0ef939c4f42fbe8ba4.tar.gz
c++: loop over array elts w/o explicit init [PR92385]
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.
-rw-r--r--gcc/cp/constexpr.c35
-rw-r--r--gcc/cp/cp-gimplify.c13
-rw-r--r--gcc/cp/cp-tree.h1
-rw-r--r--gcc/cp/init.c11
-rw-r--r--gcc/cp/tree.c44
-rw-r--r--gcc/cp/typeck2.c23
-rw-r--r--gcc/testsuite/g++.dg/init/array61.C16
7 files changed, 112 insertions, 31 deletions
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/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/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;
}
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{} {}