c++: change implementation of -frange-for-ext-temps [PR118574]
The implementation in r15-3840 used a novel technique of wrapping the entire range-for loop in a CLEANUP_POINT_EXPR, which confused the coroutines transformation. Instead let's use the existing extend_ref_init_temps mechanism. This does not revert all of r15-3840, only the parts that change how CLEANUP_POINT_EXPRs are applied to range-for declarations. PR c++/118574 PR c++/107637 gcc/cp/ChangeLog: * call.cc (struct extend_temps_data): New. (extend_temps_r, extend_all_temps): New. (set_up_extended_ref_temp): Handle tree walk case. (extend_ref_init_temps): Cal extend_all_temps. * decl.cc (initialize_local_var): Revert ext-temps change. * parser.cc (cp_convert_range_for): Likewise. (cp_parser_omp_loop_nest): Likewise. * pt.cc (tsubst_stmt): Likewise. * semantics.cc (finish_for_stmt): Likewise. gcc/testsuite/ChangeLog: * g++.dg/coroutines/range-for1.C: New test.
This commit is contained in:
parent
299a8e2dc6
commit
0d2a5f3cb7
6 changed files with 180 additions and 69 deletions
117
gcc/cp/call.cc
117
gcc/cp/call.cc
|
@ -14154,6 +14154,20 @@ make_temporary_var_for_ref_to_temp (tree decl, tree type)
|
|||
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
|
||||
|
@ -14162,7 +14176,8 @@ make_temporary_var_for_ref_to_temp (tree decl, tree type)
|
|||
|
||||
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;
|
||||
|
@ -14198,10 +14213,16 @@ set_up_extended_ref_temp (tree decl, tree expr, vec<tree, va_gc> **cleanups,
|
|||
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;
|
||||
|
@ -14801,7 +14822,8 @@ extend_ref_init_temps_1 (tree decl, tree init, vec<tree, va_gc> **cleanups,
|
|||
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);
|
||||
|
@ -14811,6 +14833,81 @@ extend_ref_init_temps_1 (tree decl, tree init, vec<tree, va_gc> **cleanups,
|
|||
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. */
|
||||
|
@ -14823,11 +14920,13 @@ extend_ref_init_temps (tree decl, tree init, vec<tree, va_gc> **cleanups,
|
|||
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);
|
||||
|
||||
|
|
|
@ -8310,11 +8310,6 @@ initialize_local_var (tree decl, tree init, bool decomp)
|
|||
code emitted by cp_finish_decomp. */
|
||||
if (decomp)
|
||||
current_stmt_tree ()->stmts_are_full_exprs_p = 0;
|
||||
/* P2718R0 - avoid CLEANUP_POINT_EXPR for range-for-initializer,
|
||||
temporaries from there should have lifetime extended. */
|
||||
else if (DECL_NAME (decl) == for_range__identifier
|
||||
&& flag_range_for_ext_temps)
|
||||
current_stmt_tree ()->stmts_are_full_exprs_p = 0;
|
||||
else
|
||||
current_stmt_tree ()->stmts_are_full_exprs_p = 1;
|
||||
finish_expr_stmt (init);
|
||||
|
|
|
@ -14844,15 +14844,6 @@ cp_convert_range_for (tree statement, tree range_decl, tree range_expr,
|
|||
{
|
||||
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);
|
||||
|
@ -46719,20 +46710,12 @@ cp_parser_omp_loop_nest (cp_parser *parser, bool *if_p)
|
|||
|
||||
/* 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;
|
||||
|
|
22
gcc/cp/pt.cc
22
gcc/cp/pt.cc
|
@ -19478,18 +19478,6 @@ tsubst_stmt (tree t, tree args, tsubst_flags_t complain, tree in_decl)
|
|||
RECUR (OMP_FOR_PRE_BODY (t));
|
||||
pre_body = pop_stmt_list (pre_body);
|
||||
|
||||
tree sl = NULL_TREE;
|
||||
if (flag_range_for_ext_temps
|
||||
&& OMP_FOR_INIT (t) != NULL_TREE
|
||||
&& !processing_template_decl)
|
||||
for (i = 0; i < TREE_VEC_LENGTH (OMP_FOR_INIT (t)); i++)
|
||||
if (TREE_VEC_ELT (OMP_FOR_INIT (t), i)
|
||||
&& TREE_VEC_ELT (OMP_FOR_COND (t), i) == global_namespace)
|
||||
{
|
||||
sl = push_stmt_list ();
|
||||
break;
|
||||
}
|
||||
|
||||
if (OMP_FOR_INIT (t) != NULL_TREE)
|
||||
for (i = 0; i < TREE_VEC_LENGTH (OMP_FOR_INIT (t)); i++)
|
||||
{
|
||||
|
@ -19545,16 +19533,6 @@ tsubst_stmt (tree t, tree args, tsubst_flags_t complain, tree in_decl)
|
|||
add_stmt (t);
|
||||
}
|
||||
|
||||
if (sl)
|
||||
{
|
||||
/* P2718R0 - Add CLEANUP_POINT_EXPR so that temporaries in
|
||||
for-range-initializer whose lifetime is extended are destructed
|
||||
here. */
|
||||
sl = pop_stmt_list (sl);
|
||||
sl = maybe_cleanup_point_expr_void (sl);
|
||||
add_stmt (sl);
|
||||
}
|
||||
|
||||
add_stmt (finish_omp_for_block (finish_omp_structured_block (stmt),
|
||||
t));
|
||||
pop_omp_privatization_clauses (r);
|
||||
|
|
|
@ -1736,19 +1736,6 @@ finish_for_stmt (tree for_stmt)
|
|||
tree range_for_decl[3] = { NULL_TREE, NULL_TREE, NULL_TREE };
|
||||
find_range_for_decls (range_for_decl);
|
||||
|
||||
/* 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
|
||||
&& range_for_decl[0]
|
||||
&& FOR_INIT_STMT (for_stmt))
|
||||
{
|
||||
tree stmt = pop_stmt_list (FOR_INIT_STMT (for_stmt));
|
||||
FOR_INIT_STMT (for_stmt) = NULL_TREE;
|
||||
stmt = maybe_cleanup_point_expr_void (stmt);
|
||||
add_stmt (stmt);
|
||||
}
|
||||
|
||||
add_stmt (do_poplevel (scope));
|
||||
|
||||
/* If we're being called from build_vec_init, don't mess with the names of
|
||||
|
|
69
gcc/testsuite/g++.dg/coroutines/range-for1.C
Normal file
69
gcc/testsuite/g++.dg/coroutines/range-for1.C
Normal file
|
@ -0,0 +1,69 @@
|
|||
// PR c++/118574
|
||||
// { dg-do run { target c++20 } }
|
||||
// { dg-additional-options -frange-for-ext-temps }
|
||||
|
||||
#include <coroutine>
|
||||
|
||||
struct A {
|
||||
const char **a = nullptr;
|
||||
int n = 0;
|
||||
void push_back (const char *x) { if (!a) a = new const char *[2]; a[n++] = x; }
|
||||
const char **begin () const { return a; }
|
||||
const char **end () const { return a + n; }
|
||||
~A () { delete[] a; }
|
||||
};
|
||||
|
||||
struct B {
|
||||
long ns;
|
||||
bool await_ready () const noexcept { return false; }
|
||||
void await_suspend (std::coroutine_handle<> h) const noexcept {
|
||||
volatile int v = 0;
|
||||
while (v < ns)
|
||||
v = v + 1;
|
||||
h.resume ();
|
||||
}
|
||||
void await_resume () const noexcept {}
|
||||
};
|
||||
|
||||
struct C {
|
||||
struct promise_type {
|
||||
const char *value;
|
||||
std::suspend_never initial_suspend () { return {}; }
|
||||
std::suspend_always final_suspend () noexcept { return {}; }
|
||||
void return_value (const char *v) { value = v; }
|
||||
void unhandled_exception () { __builtin_abort (); }
|
||||
C get_return_object () { return C{this}; }
|
||||
};
|
||||
promise_type *p;
|
||||
explicit C (promise_type *p) : p(p) {}
|
||||
const char *get () { return p->value; }
|
||||
};
|
||||
|
||||
A
|
||||
foo ()
|
||||
{
|
||||
A a;
|
||||
a.push_back ("foo");
|
||||
a.push_back ("bar");
|
||||
return a;
|
||||
}
|
||||
|
||||
C
|
||||
bar ()
|
||||
{
|
||||
A ret;
|
||||
for (const auto &item : foo ())
|
||||
{
|
||||
co_await B{200000};
|
||||
ret.push_back (item);
|
||||
}
|
||||
co_return "foobar";
|
||||
}
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
auto task = bar ();
|
||||
if (__builtin_strcmp (task.get (), "foobar"))
|
||||
__builtin_abort ();
|
||||
}
|
Loading…
Add table
Reference in a new issue