aboutsummaryrefslogtreecommitdiff
path: root/gcc/cp
diff options
context:
space:
mode:
authorJason Merrill <jason@redhat.com>2020-03-05 15:50:45 -0500
committerJason Merrill <jason@redhat.com>2022-01-06 19:23:17 -0500
commit4f6bc28fc7dd86bd9e7408cbf28de1e973dd1eda (patch)
tree0a38c0720e5d43cd717dbef2244dad9d139dd830 /gcc/cp
parentbeaee0a871b6485d20573fe050b1fd425581e56a (diff)
downloadgcc-4f6bc28fc7dd86bd9e7408cbf28de1e973dd1eda.zip
gcc-4f6bc28fc7dd86bd9e7408cbf28de1e973dd1eda.tar.gz
gcc-4f6bc28fc7dd86bd9e7408cbf28de1e973dd1eda.tar.bz2
c++: EH and partially constructed aggr temp [PR66139]
Now that PR94041 is fixed, I can return to addressing PR66139, missing cleanups for partially constructed aggregate temporaries. My previous approach of calling split_nonconstant_init in cp_gimplify_init_expr broke because gimplification is too late to be introducing destructor calls. So instead I now call it under cp_fold_function, just before cp_genericize; doing it from cp_genericize itself ran into trouble with the rewriting of invisible references. So now the prediction in cp_gimplify_expr that cp_gimplify_init_expr might need to replace references to TARGET_EXPR_SLOT within TARGET_EXPR_INITIAL has come to pass. constexpr.c already had the simple search-and-replace tree walk I needed, but it needed to be fixed to actually replace all occurrences instead of just the first one. Handling of VEC_INIT_EXPR at gimplify time had similar issues that we worked around with build_vec_init_elt, so I'm moving that to cp_fold_function as well. But it seems that build_vec_init_elt is still useful for setting the VEC_INIT_EXPR_IS_CONSTEXPR flag, so I'm leaving it alone. This also fixes 52320, because build_aggr_init of each X from build_vec_init builds an INIT_EXPR rather than call split_nonconstant_init at that point, and now that INIT_EXPR gets split later. PR c++/66139 PR c++/52320 gcc/cp/ChangeLog: * constexpr.c (replace_decl): Rename from replace_result_decl. * cp-tree.h (replace_decl): Declare it. * cp-gimplify.c (cp_gimplify_init_expr): Call it. (cp_gimplify_expr): Don't handle VEC_INIT_EXPR. (cp_genericize_init, cp_genericize_init_expr) (cp_genericize_target_expr): New. (cp_fold_r): Call them. * tree.c (build_array_copy): Add a TARGET_EXPR. * typeck2.c (digest_init_r): Look through a TARGET_EXPR. gcc/testsuite/ChangeLog: * g++.dg/cpp0x/initlist116.C: New test. * g++.dg/cpp0x/initlist117.C: New test. * g++.dg/cpp0x/lambda/lambda-eh.C: New test. * g++.dg/eh/aggregate1.C: New test.
Diffstat (limited to 'gcc/cp')
-rw-r--r--gcc/cp/constexpr.c37
-rw-r--r--gcc/cp/cp-gimplify.c93
-rw-r--r--gcc/cp/cp-tree.h1
-rw-r--r--gcc/cp/tree.c15
-rw-r--r--gcc/cp/typeck2.c3
5 files changed, 106 insertions, 43 deletions
diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index 4a4b347..41594c7 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -2251,24 +2251,26 @@ cxx_eval_dynamic_cast_fn (const constexpr_ctx *ctx, tree call,
return cp_build_addr_expr (obj, complain);
}
-/* Data structure used by replace_result_decl and replace_result_decl_r. */
+/* Data structure used by replace_decl and replace_decl_r. */
-struct replace_result_decl_data
+struct replace_decl_data
{
- /* The RESULT_DECL we want to replace. */
+ /* The _DECL we want to replace. */
tree decl;
/* The replacement for DECL. */
tree replacement;
+ /* Trees we've visited. */
+ hash_set<tree> *pset;
/* Whether we've performed any replacements. */
bool changed;
};
-/* Helper function for replace_result_decl, called through cp_walk_tree. */
+/* Helper function for replace_decl, called through cp_walk_tree. */
static tree
-replace_result_decl_r (tree *tp, int *walk_subtrees, void *data)
+replace_decl_r (tree *tp, int *walk_subtrees, void *data)
{
- replace_result_decl_data *d = (replace_result_decl_data *) data;
+ replace_decl_data *d = (replace_decl_data *) data;
if (*tp == d->decl)
{
@@ -2276,24 +2278,25 @@ replace_result_decl_r (tree *tp, int *walk_subtrees, void *data)
d->changed = true;
*walk_subtrees = 0;
}
- else if (TYPE_P (*tp))
+ else if (TYPE_P (*tp)
+ || d->pset->add (*tp))
*walk_subtrees = 0;
return NULL_TREE;
}
-/* Replace every occurrence of DECL, a RESULT_DECL, with (an unshared copy of)
- REPLACEMENT within the reduced constant expression *TP. Returns true iff a
+/* Replace every occurrence of DECL with (an unshared copy of)
+ REPLACEMENT within the expression *TP. Returns true iff a
replacement was performed. */
-static bool
-replace_result_decl (tree *tp, tree decl, tree replacement)
+bool
+replace_decl (tree *tp, tree decl, tree replacement)
{
- gcc_checking_assert (TREE_CODE (decl) == RESULT_DECL
- && (same_type_ignoring_top_level_qualifiers_p
- (TREE_TYPE (decl), TREE_TYPE (replacement))));
- replace_result_decl_data data = { decl, replacement, false };
- cp_walk_tree_without_duplicates (tp, replace_result_decl_r, &data);
+ gcc_checking_assert (same_type_ignoring_top_level_qualifiers_p
+ (TREE_TYPE (decl), TREE_TYPE (replacement)));
+ hash_set<tree> pset;
+ replace_decl_data data = { decl, replacement, &pset, false };
+ cp_walk_tree (tp, replace_decl_r, &data, NULL);
return data.changed;
}
@@ -2962,7 +2965,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
if (!*non_constant_p && ctx->object
&& CLASS_TYPE_P (TREE_TYPE (res))
&& !is_empty_class (TREE_TYPE (res)))
- if (replace_result_decl (&result, res, ctx->object))
+ if (replace_decl (&result, res, ctx->object))
cacheable = false;
}
else
diff --git a/gcc/cp/cp-gimplify.c b/gcc/cp/cp-gimplify.c
index e4f2442..114fa78 100644
--- a/gcc/cp/cp-gimplify.c
+++ b/gcc/cp/cp-gimplify.c
@@ -239,12 +239,21 @@ cp_gimplify_init_expr (tree *expr_p)
tree to = TREE_OPERAND (*expr_p, 0);
tree t;
- /* What about code that pulls out the temp and uses it elsewhere? I
- think that such code never uses the TARGET_EXPR as an initializer. If
- I'm wrong, we'll abort because the temp won't have any RTL. In that
- case, I guess we'll need to replace references somehow. */
- if (TREE_CODE (from) == TARGET_EXPR && TARGET_EXPR_INITIAL (from))
- from = TARGET_EXPR_INITIAL (from);
+ if (TREE_CODE (from) == TARGET_EXPR)
+ if (tree init = TARGET_EXPR_INITIAL (from))
+ {
+ if (VOID_TYPE_P (TREE_TYPE (init))
+ && TREE_CODE (init) != AGGR_INIT_EXPR)
+ {
+ /* If this was changed by cp_genericize_target_expr, we need to
+ walk into it to replace uses of the slot. */
+ replace_decl (&init, TARGET_EXPR_SLOT (from), to);
+ *expr_p = init;
+ return;
+ }
+ else
+ from = init;
+ }
/* Look through any COMPOUND_EXPRs, since build_compound_expr pushes them
inside the TARGET_EXPR. */
@@ -460,19 +469,6 @@ cp_gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
ret = GS_OK;
break;
- case VEC_INIT_EXPR:
- {
- *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;
- }
- break;
-
case THROW_EXPR:
/* FIXME communicate throw type to back end, probably by moving
THROW_EXPR into ../tree.def. */
@@ -868,6 +864,57 @@ omp_cxx_notice_variable (struct cp_genericize_omp_taskreg *omp_ctx, tree decl)
}
}
+/* If we might need to clean up a partially constructed object, break down the
+ CONSTRUCTOR with split_nonconstant_init. Also expand VEC_INIT_EXPR at this
+ point. If initializing TO with FROM is non-trivial, overwrite *REPLACE with
+ the result. */
+
+static void
+cp_genericize_init (tree *replace, tree from, tree to)
+{
+ if (TREE_CODE (from) == VEC_INIT_EXPR)
+ {
+ tree init = expand_vec_init_expr (to, from, tf_warning_or_error);
+
+ /* Make cp_gimplify_init_expr call replace_decl. */
+ *replace = fold_convert (void_type_node, init);
+ }
+ else if (flag_exceptions
+ && TREE_CODE (from) == CONSTRUCTOR
+ && TREE_SIDE_EFFECTS (from)
+ && TYPE_HAS_NONTRIVIAL_DESTRUCTOR (TREE_TYPE (from)))
+ {
+ to = cp_stabilize_reference (to);
+ replace_placeholders (from, to);
+ *replace = split_nonconstant_init (to, from);
+ }
+}
+
+/* For an INIT_EXPR, replace the INIT_EXPR itself. */
+
+static void
+cp_genericize_init_expr (tree *stmt_p)
+{
+ tree to = TREE_OPERAND (*stmt_p, 0);
+ tree from = TREE_OPERAND (*stmt_p, 1);
+ if (SIMPLE_TARGET_EXPR_P (from)
+ /* Return gets confused if we clobber its INIT_EXPR this soon. */
+ && TREE_CODE (to) != RESULT_DECL)
+ from = TARGET_EXPR_INITIAL (from);
+ cp_genericize_init (stmt_p, from, to);
+}
+
+/* For a TARGET_EXPR, change the TARGET_EXPR_INITIAL. We will need to use
+ replace_decl later when we know what we're initializing. */
+
+static void
+cp_genericize_target_expr (tree *stmt_p)
+{
+ cp_genericize_init (&TARGET_EXPR_INITIAL (*stmt_p),
+ TARGET_EXPR_INITIAL (*stmt_p),
+ TARGET_EXPR_SLOT (*stmt_p));
+}
+
/* Genericization context. */
struct cp_genericize_data
@@ -1007,6 +1054,14 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data)
}
break;
+ case INIT_EXPR:
+ cp_genericize_init_expr (stmt_p);
+ break;
+
+ case TARGET_EXPR:
+ cp_genericize_target_expr (stmt_p);
+ break;
+
default:
break;
}
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index c75ecaf..56e6d66 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -8385,6 +8385,7 @@ extern tree fold_sizeof_expr (tree);
extern void clear_cv_and_fold_caches (void);
extern tree unshare_constructor (tree CXX_MEM_STAT_INFO);
extern bool decl_implicit_constexpr_p (tree);
+extern bool replace_decl (tree *, tree, tree);
/* An RAII sentinel used to restrict constexpr evaluation so that it
doesn't do anything that causes extra DECL_UID generation. */
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index 964e40e..4c1135b 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -737,12 +737,12 @@ build_cplus_new (tree type, tree init, tsubst_flags_t complain)
intialization as a proxy for the full array initialization to get things
marked as used and any appropriate diagnostics.
- Since we're deferring building the actual constructor calls until
- gimplification time, we need to build one now and throw it away so
- that the relevant constructor gets mark_used before cgraph decides
- what functions are needed. Here we assume that init is either
- NULL_TREE, void_type_node (indicating value-initialization), or
- another array to copy. */
+ This used to be necessary because we were deferring building the actual
+ constructor calls until gimplification time; now we only do it to set
+ VEC_INIT_EXPR_IS_CONSTEXPR.
+
+ We assume that init is either NULL_TREE, void_type_node (indicating
+ value-initialization), or another array to copy. */
static tree
build_vec_init_elt (tree type, tree init, tsubst_flags_t complain)
@@ -858,7 +858,8 @@ diagnose_non_constexpr_vec_init (tree expr)
tree
build_array_copy (tree init)
{
- return build_vec_init_expr (TREE_TYPE (init), init, tf_warning_or_error);
+ return get_target_expr (build_vec_init_expr
+ (TREE_TYPE (init), init, tf_warning_or_error));
}
/* Build a TARGET_EXPR using INIT to initialize a new temporary of the
diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c
index 7907c53..7e7fc7f 100644
--- a/gcc/cp/typeck2.c
+++ b/gcc/cp/typeck2.c
@@ -1282,6 +1282,9 @@ digest_init_r (tree type, tree init, int nested, int flags,
}
}
+ if (SIMPLE_TARGET_EXPR_P (stripped_init))
+ stripped_init = TARGET_EXPR_INITIAL (stripped_init);
+
if (BRACE_ENCLOSED_INITIALIZER_P (stripped_init)
&& !TYPE_NON_AGGREGATE_CLASS (type))
return process_init_constructor (type, stripped_init, nested, flags,