[Bug c++/95599] [coroutines] destructor for temporary operand to co_yield expression called before end of full-expression
cvs-commit at gcc dot gnu.org
Wed Jul 29 09:59:13 GMT 2020
--- Comment #7 from CVS Commits <cvs-commit at gcc dot gnu.org> ---
The releases/gcc-10 branch has been updated by Iain D Sandoe
Author: Iain Sandoe <email@example.com>
Date: Sun Jul 19 18:39:21 2020 +0100
coroutines: Correct frame capture of compiler temps [PR95591+4].
When a full expression contains a co_await (or co_yield), this means
that it might be suspended; which would destroy temporary variables
(on a stack for example). However the language guarantees that such
temporaries are live until the end of the expression.
In order to preserve this, we 'promote' temporaries where necessary
so that they are saved in the coroutine frame (which allows them to
remain live potentially until the frame is destroyed). In addition,
sub-expressions that produce control flow (such as TRUTH_AND/OR_IF
or COND_EXPR) must be handled specifically to ensure that await
expressions are properly expanded.
This patch corrects two mistakes in which we were (a) failing to
promote some temporaries and (b) we we failing to sequence DTORs for
the captures properly. This manifests in a number of related (but not
exact duplicate) PRs.
The revised code collects the actions into one place and maps all the
control flow into one form - a benefit of this is that co_returns are
now expanded earlier (which provides an opportunity to address PR95517
in some future patch).
We replace a statement that contains await expression(s) with a bind
scope that has a variable list describing the temporaries that have
been 'promoted' and a statement list that contains a series of cleanup
expressions for each of those. Where we encounter nested conditional
expressions, these are wrapped in a try-finally block with a guard var
for each sub-expression variable that needs a DTOR. The guards are all
declared and initialized to false before the first conditional sub-
expression. The 'finally' block contains a series of if blocks (one
per guard variable) enclosing the relevant DTOR.
Variables listed in a bind scope in this manner are automatically moved
to a coroutine frame version by existing code (so we re-use that rather
than having a separate mechanism).
* coroutines.cc (struct coro_ret_data): Delete.
(co_await_find_in_subtree): Remove unused name.
(build_actor_fn): Remove unused parm, remove handling
for co_return expansion.
(register_await_info): Demote duplicate info message to a
(coro_make_frame_entry): Move closer to use site.
(struct susp_frame_data): Add fields for final suspend label
and a flag to indicate await expressions with initializers.
(register_awaits): Remove unused code, update comments.
(struct interesting): New.
(struct var_nest_node): New.
(replace_statement_captures): Rename to...
(maybe_promote_temps): ... this.
(analyze_expression_awaits): Check for await expressions with
initializers. Simplify handling for truth-and/or-if.
(expand_one_truth_if): Simplify (map cases that need expansion
(await_statement_walker): Handle CO_RETURN_EXPR. Simplify the
handling for truth-and/or-if expressions.
(register_local_var_uses): Ensure that we create names in the
(morph_fn_to_coro): Add final suspend label to suspend frame
callback data and remove it from the build_actor_fn call.
* g++.dg/coroutines/pr95591.C: New test.
* g++.dg/coroutines/pr95599.C: New test.
* g++.dg/coroutines/pr95823.C: New test.
* g++.dg/coroutines/pr95824.C: New test.
(cherry picked from commit 0f66b8486cea8668020e4bd48f261b760cb579be)
More information about the Gcc-bugs