return pushdecl (var);
}
+/* Data for extend_temps_r, mostly matching the parameters of
+ extend_ref_init_temps. */
+
+struct extend_temps_data
+{
+ tree decl;
+ tree init;
+ vec<tree, va_gc> **cleanups;
+ tree* cond_guard;
+ hash_set<tree> *pset;
+};
+
+static tree extend_temps_r (tree *, int *, void *);
+
/* EXPR is the initializer for a variable DECL of reference or
std::initializer_list type. Create, push and return a new VAR_DECL
for the initializer so that it will live as long as DECL. Any
static tree
set_up_extended_ref_temp (tree decl, tree expr, vec<tree, va_gc> **cleanups,
- tree *initp, tree *cond_guard)
+ tree *initp, tree *cond_guard,
+ extend_temps_data *walk_data)
{
tree init;
tree type;
suppress_warning (decl);
}
- /* Recursively extend temps in this initializer. */
- TARGET_EXPR_INITIAL (expr)
- = extend_ref_init_temps (decl, TARGET_EXPR_INITIAL (expr), cleanups,
- cond_guard);
+ /* Recursively extend temps in this initializer. The recursion needs to come
+ after creating the variable to conform to the mangling ABI, and before
+ maybe_constant_init because the extension might change its result. */
+ if (walk_data)
+ cp_walk_tree (&TARGET_EXPR_INITIAL (expr), extend_temps_r,
+ walk_data, walk_data->pset);
+ else
+ TARGET_EXPR_INITIAL (expr)
+ = extend_ref_init_temps (decl, TARGET_EXPR_INITIAL (expr), cleanups,
+ cond_guard);
/* Any reference temp has a non-trivial initializer. */
DECL_NONTRIVIALLY_INITIALIZED_P (var) = true;
if (TREE_CODE (*p) == TARGET_EXPR)
{
tree subinit = NULL_TREE;
- *p = set_up_extended_ref_temp (decl, *p, cleanups, &subinit, cond_guard);
+ *p = set_up_extended_ref_temp (decl, *p, cleanups, &subinit,
+ cond_guard, nullptr);
recompute_tree_invariant_for_addr_expr (sub);
if (init != sub)
init = fold_convert (TREE_TYPE (init), sub);
return init;
}
+/* Tree walk function for extend_all_temps. Generally parallel to
+ extend_ref_init_temps_1, but adapted for walk_tree. */
+
+tree
+extend_temps_r (tree *tp, int *walk_subtrees, void *data)
+{
+ extend_temps_data *d = (extend_temps_data *)data;
+
+ if (TYPE_P (*tp) || TREE_CODE (*tp) == CLEANUP_POINT_EXPR)
+ {
+ *walk_subtrees = 0;
+ return NULL_TREE;
+ }
+
+ if (TREE_CODE (*tp) == COND_EXPR)
+ {
+ cp_walk_tree (&TREE_OPERAND (*tp, 0), extend_temps_r, d, d->pset);
+
+ auto walk_arm = [d](tree &op)
+ {
+ tree cur_cond_guard = NULL_TREE;
+ auto ov = make_temp_override (d->cond_guard, &cur_cond_guard);
+ cp_walk_tree (&op, extend_temps_r, d, d->pset);
+ if (cur_cond_guard)
+ {
+ tree set = build2 (MODIFY_EXPR, boolean_type_node,
+ cur_cond_guard, boolean_true_node);
+ op = add_stmt_to_compound (set, op);
+ }
+ };
+ walk_arm (TREE_OPERAND (*tp, 1));
+ walk_arm (TREE_OPERAND (*tp, 2));
+
+ *walk_subtrees = 0;
+ return NULL_TREE;
+ }
+
+ if (TREE_CODE (*tp) == ADDR_EXPR
+ /* A discarded-value temporary. */
+ || (TREE_CODE (*tp) == CONVERT_EXPR
+ && VOID_TYPE_P (TREE_TYPE (*tp))))
+ {
+ tree *p;
+ for (p = &TREE_OPERAND (*tp, 0);
+ TREE_CODE (*p) == COMPONENT_REF || TREE_CODE (*p) == ARRAY_REF; )
+ p = &TREE_OPERAND (*p, 0);
+ if (TREE_CODE (*p) == TARGET_EXPR)
+ {
+ tree subinit = NULL_TREE;
+ *p = set_up_extended_ref_temp (d->decl, *p, d->cleanups, &subinit,
+ d->cond_guard, d);
+ if (TREE_CODE (*tp) == ADDR_EXPR)
+ recompute_tree_invariant_for_addr_expr (*tp);
+ if (subinit)
+ *tp = cp_build_compound_expr (subinit, *tp, tf_none);
+ }
+ }
+
+ /* TARGET_EXPRs that aren't handled by the above are implementation details
+ that shouldn't be ref-extended, like the build_vec_init iterator. */
+
+ return NULL_TREE;
+}
+
+/* Extend all the temporaries in a for-range-initializer. */
+
+static tree
+extend_all_temps (tree decl, tree init, vec<tree, va_gc> **cleanups)
+{
+ hash_set<tree> pset;
+ extend_temps_data d = { decl, init, cleanups, nullptr, &pset };
+ cp_walk_tree (&init, extend_temps_r, &d, &pset);
+ return init;
+}
+
/* INIT is part of the initializer for DECL. If there are any
reference or initializer lists being initialized, extend their
lifetime to match that of DECL. */
if (processing_template_decl)
return init;
- /* P2718R0 - ignore temporaries in C++23 for-range-initializer, those
- have all extended lifetime. */
+ /* P2718R0 - in C++23 for-range-initializer, extend all temps. */
if (DECL_NAME (decl) == for_range__identifier
&& flag_range_for_ext_temps)
- return init;
+ {
+ gcc_checking_assert (!cond_guard);
+ return extend_all_temps (decl, init, cleanups);
+ }
maybe_warn_dangling_reference (decl, init);
{
range_temp = build_range_temp (range_expr);
pushdecl (range_temp);
- if (flag_range_for_ext_temps)
- {
- /* P2718R0 - put the range_temp declaration and everything
- until end of the range for body into an extra STATEMENT_LIST
- which will have CLEANUP_POINT_EXPR around it, so that all
- temporaries are destroyed at the end of it. */
- gcc_assert (FOR_INIT_STMT (statement) == NULL_TREE);
- FOR_INIT_STMT (statement) = push_stmt_list ();
- }
cp_finish_decl (range_temp, range_expr,
/*is_constant_init*/false, NULL_TREE,
LOOKUP_ONLYCONVERTING);
/* Pop and remember the init block. */
if (sl)
- {
- sl = pop_stmt_list (sl);
- /* P2718R0 - Add CLEANUP_POINT_EXPR so that temporaries in
- for-range-initializer whose lifetime is extended are destructed
- here. */
- if (flag_range_for_ext_temps
- && is_range_for
- && !processing_template_decl)
- sl = maybe_cleanup_point_expr_void (sl);
- add_stmt (sl);
- }
+ add_stmt (pop_stmt_list (sl));
+
tree range_for_decl[3] = { NULL_TREE, NULL_TREE, NULL_TREE };
if (is_range_for && !processing_template_decl)
find_range_for_decls (range_for_decl);
+
finish_compound_stmt (init_scope);
init_block = pop_stmt_list (init_block);
omp_for_parse_state->init_blockv[depth] = init_block;