Skip to content

Commit 0d2a5f3

Browse files
committed
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.
1 parent 299a8e2 commit 0d2a5f3

File tree

6 files changed

+180
-69
lines changed

6 files changed

+180
-69
lines changed

gcc/cp/call.cc

Lines changed: 108 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14154,6 +14154,20 @@ make_temporary_var_for_ref_to_temp (tree decl, tree type)
1415414154
return pushdecl (var);
1415514155
}
1415614156

14157+
/* Data for extend_temps_r, mostly matching the parameters of
14158+
extend_ref_init_temps. */
14159+
14160+
struct extend_temps_data
14161+
{
14162+
tree decl;
14163+
tree init;
14164+
vec<tree, va_gc> **cleanups;
14165+
tree* cond_guard;
14166+
hash_set<tree> *pset;
14167+
};
14168+
14169+
static tree extend_temps_r (tree *, int *, void *);
14170+
1415714171
/* EXPR is the initializer for a variable DECL of reference or
1415814172
std::initializer_list type. Create, push and return a new VAR_DECL
1415914173
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)
1416214176

1416314177
static tree
1416414178
set_up_extended_ref_temp (tree decl, tree expr, vec<tree, va_gc> **cleanups,
14165-
tree *initp, tree *cond_guard)
14179+
tree *initp, tree *cond_guard,
14180+
extend_temps_data *walk_data)
1416614181
{
1416714182
tree init;
1416814183
tree type;
@@ -14198,10 +14213,16 @@ set_up_extended_ref_temp (tree decl, tree expr, vec<tree, va_gc> **cleanups,
1419814213
suppress_warning (decl);
1419914214
}
1420014215

14201-
/* Recursively extend temps in this initializer. */
14202-
TARGET_EXPR_INITIAL (expr)
14203-
= extend_ref_init_temps (decl, TARGET_EXPR_INITIAL (expr), cleanups,
14204-
cond_guard);
14216+
/* Recursively extend temps in this initializer. The recursion needs to come
14217+
after creating the variable to conform to the mangling ABI, and before
14218+
maybe_constant_init because the extension might change its result. */
14219+
if (walk_data)
14220+
cp_walk_tree (&TARGET_EXPR_INITIAL (expr), extend_temps_r,
14221+
walk_data, walk_data->pset);
14222+
else
14223+
TARGET_EXPR_INITIAL (expr)
14224+
= extend_ref_init_temps (decl, TARGET_EXPR_INITIAL (expr), cleanups,
14225+
cond_guard);
1420514226

1420614227
/* Any reference temp has a non-trivial initializer. */
1420714228
DECL_NONTRIVIALLY_INITIALIZED_P (var) = true;
@@ -14801,7 +14822,8 @@ extend_ref_init_temps_1 (tree decl, tree init, vec<tree, va_gc> **cleanups,
1480114822
if (TREE_CODE (*p) == TARGET_EXPR)
1480214823
{
1480314824
tree subinit = NULL_TREE;
14804-
*p = set_up_extended_ref_temp (decl, *p, cleanups, &subinit, cond_guard);
14825+
*p = set_up_extended_ref_temp (decl, *p, cleanups, &subinit,
14826+
cond_guard, nullptr);
1480514827
recompute_tree_invariant_for_addr_expr (sub);
1480614828
if (init != sub)
1480714829
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,
1481114833
return init;
1481214834
}
1481314835

14836+
/* Tree walk function for extend_all_temps. Generally parallel to
14837+
extend_ref_init_temps_1, but adapted for walk_tree. */
14838+
14839+
tree
14840+
extend_temps_r (tree *tp, int *walk_subtrees, void *data)
14841+
{
14842+
extend_temps_data *d = (extend_temps_data *)data;
14843+
14844+
if (TYPE_P (*tp) || TREE_CODE (*tp) == CLEANUP_POINT_EXPR)
14845+
{
14846+
*walk_subtrees = 0;
14847+
return NULL_TREE;
14848+
}
14849+
14850+
if (TREE_CODE (*tp) == COND_EXPR)
14851+
{
14852+
cp_walk_tree (&TREE_OPERAND (*tp, 0), extend_temps_r, d, d->pset);
14853+
14854+
auto walk_arm = [d](tree &op)
14855+
{
14856+
tree cur_cond_guard = NULL_TREE;
14857+
auto ov = make_temp_override (d->cond_guard, &cur_cond_guard);
14858+
cp_walk_tree (&op, extend_temps_r, d, d->pset);
14859+
if (cur_cond_guard)
14860+
{
14861+
tree set = build2 (MODIFY_EXPR, boolean_type_node,
14862+
cur_cond_guard, boolean_true_node);
14863+
op = add_stmt_to_compound (set, op);
14864+
}
14865+
};
14866+
walk_arm (TREE_OPERAND (*tp, 1));
14867+
walk_arm (TREE_OPERAND (*tp, 2));
14868+
14869+
*walk_subtrees = 0;
14870+
return NULL_TREE;
14871+
}
14872+
14873+
if (TREE_CODE (*tp) == ADDR_EXPR
14874+
/* A discarded-value temporary. */
14875+
|| (TREE_CODE (*tp) == CONVERT_EXPR
14876+
&& VOID_TYPE_P (TREE_TYPE (*tp))))
14877+
{
14878+
tree *p;
14879+
for (p = &TREE_OPERAND (*tp, 0);
14880+
TREE_CODE (*p) == COMPONENT_REF || TREE_CODE (*p) == ARRAY_REF; )
14881+
p = &TREE_OPERAND (*p, 0);
14882+
if (TREE_CODE (*p) == TARGET_EXPR)
14883+
{
14884+
tree subinit = NULL_TREE;
14885+
*p = set_up_extended_ref_temp (d->decl, *p, d->cleanups, &subinit,
14886+
d->cond_guard, d);
14887+
if (TREE_CODE (*tp) == ADDR_EXPR)
14888+
recompute_tree_invariant_for_addr_expr (*tp);
14889+
if (subinit)
14890+
*tp = cp_build_compound_expr (subinit, *tp, tf_none);
14891+
}
14892+
}
14893+
14894+
/* TARGET_EXPRs that aren't handled by the above are implementation details
14895+
that shouldn't be ref-extended, like the build_vec_init iterator. */
14896+
14897+
return NULL_TREE;
14898+
}
14899+
14900+
/* Extend all the temporaries in a for-range-initializer. */
14901+
14902+
static tree
14903+
extend_all_temps (tree decl, tree init, vec<tree, va_gc> **cleanups)
14904+
{
14905+
hash_set<tree> pset;
14906+
extend_temps_data d = { decl, init, cleanups, nullptr, &pset };
14907+
cp_walk_tree (&init, extend_temps_r, &d, &pset);
14908+
return init;
14909+
}
14910+
1481414911
/* INIT is part of the initializer for DECL. If there are any
1481514912
reference or initializer lists being initialized, extend their
1481614913
lifetime to match that of DECL. */
@@ -14823,11 +14920,13 @@ extend_ref_init_temps (tree decl, tree init, vec<tree, va_gc> **cleanups,
1482314920
if (processing_template_decl)
1482414921
return init;
1482514922

14826-
/* P2718R0 - ignore temporaries in C++23 for-range-initializer, those
14827-
have all extended lifetime. */
14923+
/* P2718R0 - in C++23 for-range-initializer, extend all temps. */
1482814924
if (DECL_NAME (decl) == for_range__identifier
1482914925
&& flag_range_for_ext_temps)
14830-
return init;
14926+
{
14927+
gcc_checking_assert (!cond_guard);
14928+
return extend_all_temps (decl, init, cleanups);
14929+
}
1483114930

1483214931
maybe_warn_dangling_reference (decl, init);
1483314932

gcc/cp/decl.cc

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8310,11 +8310,6 @@ initialize_local_var (tree decl, tree init, bool decomp)
83108310
code emitted by cp_finish_decomp. */
83118311
if (decomp)
83128312
current_stmt_tree ()->stmts_are_full_exprs_p = 0;
8313-
/* P2718R0 - avoid CLEANUP_POINT_EXPR for range-for-initializer,
8314-
temporaries from there should have lifetime extended. */
8315-
else if (DECL_NAME (decl) == for_range__identifier
8316-
&& flag_range_for_ext_temps)
8317-
current_stmt_tree ()->stmts_are_full_exprs_p = 0;
83188313
else
83198314
current_stmt_tree ()->stmts_are_full_exprs_p = 1;
83208315
finish_expr_stmt (init);

gcc/cp/parser.cc

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -14844,15 +14844,6 @@ cp_convert_range_for (tree statement, tree range_decl, tree range_expr,
1484414844
{
1484514845
range_temp = build_range_temp (range_expr);
1484614846
pushdecl (range_temp);
14847-
if (flag_range_for_ext_temps)
14848-
{
14849-
/* P2718R0 - put the range_temp declaration and everything
14850-
until end of the range for body into an extra STATEMENT_LIST
14851-
which will have CLEANUP_POINT_EXPR around it, so that all
14852-
temporaries are destroyed at the end of it. */
14853-
gcc_assert (FOR_INIT_STMT (statement) == NULL_TREE);
14854-
FOR_INIT_STMT (statement) = push_stmt_list ();
14855-
}
1485614847
cp_finish_decl (range_temp, range_expr,
1485714848
/*is_constant_init*/false, NULL_TREE,
1485814849
LOOKUP_ONLYCONVERTING);
@@ -46719,20 +46710,12 @@ cp_parser_omp_loop_nest (cp_parser *parser, bool *if_p)
4671946710

4672046711
/* Pop and remember the init block. */
4672146712
if (sl)
46722-
{
46723-
sl = pop_stmt_list (sl);
46724-
/* P2718R0 - Add CLEANUP_POINT_EXPR so that temporaries in
46725-
for-range-initializer whose lifetime is extended are destructed
46726-
here. */
46727-
if (flag_range_for_ext_temps
46728-
&& is_range_for
46729-
&& !processing_template_decl)
46730-
sl = maybe_cleanup_point_expr_void (sl);
46731-
add_stmt (sl);
46732-
}
46713+
add_stmt (pop_stmt_list (sl));
46714+
4673346715
tree range_for_decl[3] = { NULL_TREE, NULL_TREE, NULL_TREE };
4673446716
if (is_range_for && !processing_template_decl)
4673546717
find_range_for_decls (range_for_decl);
46718+
4673646719
finish_compound_stmt (init_scope);
4673746720
init_block = pop_stmt_list (init_block);
4673846721
omp_for_parse_state->init_blockv[depth] = init_block;

gcc/cp/pt.cc

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -19478,18 +19478,6 @@ tsubst_stmt (tree t, tree args, tsubst_flags_t complain, tree in_decl)
1947819478
RECUR (OMP_FOR_PRE_BODY (t));
1947919479
pre_body = pop_stmt_list (pre_body);
1948019480

19481-
tree sl = NULL_TREE;
19482-
if (flag_range_for_ext_temps
19483-
&& OMP_FOR_INIT (t) != NULL_TREE
19484-
&& !processing_template_decl)
19485-
for (i = 0; i < TREE_VEC_LENGTH (OMP_FOR_INIT (t)); i++)
19486-
if (TREE_VEC_ELT (OMP_FOR_INIT (t), i)
19487-
&& TREE_VEC_ELT (OMP_FOR_COND (t), i) == global_namespace)
19488-
{
19489-
sl = push_stmt_list ();
19490-
break;
19491-
}
19492-
1949319481
if (OMP_FOR_INIT (t) != NULL_TREE)
1949419482
for (i = 0; i < TREE_VEC_LENGTH (OMP_FOR_INIT (t)); i++)
1949519483
{
@@ -19545,16 +19533,6 @@ tsubst_stmt (tree t, tree args, tsubst_flags_t complain, tree in_decl)
1954519533
add_stmt (t);
1954619534
}
1954719535

19548-
if (sl)
19549-
{
19550-
/* P2718R0 - Add CLEANUP_POINT_EXPR so that temporaries in
19551-
for-range-initializer whose lifetime is extended are destructed
19552-
here. */
19553-
sl = pop_stmt_list (sl);
19554-
sl = maybe_cleanup_point_expr_void (sl);
19555-
add_stmt (sl);
19556-
}
19557-
1955819536
add_stmt (finish_omp_for_block (finish_omp_structured_block (stmt),
1955919537
t));
1956019538
pop_omp_privatization_clauses (r);

gcc/cp/semantics.cc

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1736,19 +1736,6 @@ finish_for_stmt (tree for_stmt)
17361736
tree range_for_decl[3] = { NULL_TREE, NULL_TREE, NULL_TREE };
17371737
find_range_for_decls (range_for_decl);
17381738

1739-
/* P2718R0 - Add CLEANUP_POINT_EXPR so that temporaries in
1740-
for-range-initializer whose lifetime is extended are destructed
1741-
here. */
1742-
if (flag_range_for_ext_temps
1743-
&& range_for_decl[0]
1744-
&& FOR_INIT_STMT (for_stmt))
1745-
{
1746-
tree stmt = pop_stmt_list (FOR_INIT_STMT (for_stmt));
1747-
FOR_INIT_STMT (for_stmt) = NULL_TREE;
1748-
stmt = maybe_cleanup_point_expr_void (stmt);
1749-
add_stmt (stmt);
1750-
}
1751-
17521739
add_stmt (do_poplevel (scope));
17531740

17541741
/* If we're being called from build_vec_init, don't mess with the names of
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// PR c++/118574
2+
// { dg-do run { target c++20 } }
3+
// { dg-additional-options -frange-for-ext-temps }
4+
5+
#include <coroutine>
6+
7+
struct A {
8+
const char **a = nullptr;
9+
int n = 0;
10+
void push_back (const char *x) { if (!a) a = new const char *[2]; a[n++] = x; }
11+
const char **begin () const { return a; }
12+
const char **end () const { return a + n; }
13+
~A () { delete[] a; }
14+
};
15+
16+
struct B {
17+
long ns;
18+
bool await_ready () const noexcept { return false; }
19+
void await_suspend (std::coroutine_handle<> h) const noexcept {
20+
volatile int v = 0;
21+
while (v < ns)
22+
v = v + 1;
23+
h.resume ();
24+
}
25+
void await_resume () const noexcept {}
26+
};
27+
28+
struct C {
29+
struct promise_type {
30+
const char *value;
31+
std::suspend_never initial_suspend () { return {}; }
32+
std::suspend_always final_suspend () noexcept { return {}; }
33+
void return_value (const char *v) { value = v; }
34+
void unhandled_exception () { __builtin_abort (); }
35+
C get_return_object () { return C{this}; }
36+
};
37+
promise_type *p;
38+
explicit C (promise_type *p) : p(p) {}
39+
const char *get () { return p->value; }
40+
};
41+
42+
A
43+
foo ()
44+
{
45+
A a;
46+
a.push_back ("foo");
47+
a.push_back ("bar");
48+
return a;
49+
}
50+
51+
C
52+
bar ()
53+
{
54+
A ret;
55+
for (const auto &item : foo ())
56+
{
57+
co_await B{200000};
58+
ret.push_back (item);
59+
}
60+
co_return "foobar";
61+
}
62+
63+
int
64+
main ()
65+
{
66+
auto task = bar ();
67+
if (__builtin_strcmp (task.get (), "foobar"))
68+
__builtin_abort ();
69+
}

0 commit comments

Comments
 (0)