aboutsummaryrefslogtreecommitdiff
path: root/gcc/cp/coroutines.cc
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/cp/coroutines.cc')
-rw-r--r--gcc/cp/coroutines.cc762
1 files changed, 402 insertions, 360 deletions
diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
index 743da06..52cc186 100644
--- a/gcc/cp/coroutines.cc
+++ b/gcc/cp/coroutines.cc
@@ -191,8 +191,87 @@ static bool coro_promise_type_found_p (tree, location_t);
just syntactic sugar for a co_await).
We defer the analysis and transformation until template expansion is
- complete so that we have complete types at that time. */
+ complete so that we have complete types at that time.
+ ---------------------------------------------------------------------------
+
+ Coroutine state, and responsibility for its release.
+
+ As noted above, a coroutine has some state that persists across suspensions.
+
+ The state has two components:
+ * State that is specified by the standard and persists for the entire
+ life of the coroutine.
+ * Local state that is constructed/destructed as scopes in the original
+ function body are entered/exited. The destruction of local state is
+ always the responsibility of the body code.
+
+ The persistent state (and the overall storage for the state) must be
+ managed in two places:
+ * The ramp function (which allocates and builds this - and can, in some
+ cases, be responsible for destroying it)
+ * The re-written function body which can destroy it when that body
+ completes its final suspend - or when the handle.destroy () is called.
+
+ In all cases the ramp holds responsibility for constructing the standard-
+ mandated persistent state.
+
+ There are four ways in which the ramp might be re-entered after starting
+ the function body:
+ A The body could suspend (one might expect that to be the 'normal' case
+ for most coroutines).
+ B The body might complete either synchronously or via continuations.
+ C An exception might be thrown during the setup of the initial await
+ expression, before the initial awaiter resumes.
+ D An exception might be processed by promise.unhandled_exception () and
+ that, in turn, might re-throw it (or throw something else). In this
+ case, the coroutine is considered suspended at the final suspension
+ point.
+
+ Until the ramp return value has been constructed, the ramp is considered
+ to have a use of the state.
+
+ To manage these interacting conditions we allocate a reference counter
+ for the frame state. This is initialised to 1 by the ramp as part of its
+ startup (note that failures/exceptions in the startup code are handled
+ locally to the ramp).
+
+ When the body returns (either normally, or by exception) the ramp releases
+ its use.
+
+ Once the rewritten coroutine body is started, the body is considered to
+ have a use of the frame. This use (potentially) needs to be released if
+ an exception is thrown from the body. We implement this using an eh-only
+ cleanup around the initial await and function body. If we have the case
+ D above, then we do not release the use.
+
+ In case:
+
+ A, typically the ramp would be re-entered with the body holding a use,
+ and therefore the ramp should not destroy the state.
+
+ B, both the body and ramp will have released their uses, and the ramp
+ should destroy the state.
+
+ C, we must arrange for the body to release its use, because we require
+ the ramp to cleanup in this circumstance.
+
+ D is an outlier, since the responsibility for destruction of the state
+ now rests with the user's code (via a handle.destroy() call).
+
+ NOTE: In the case that the body has never suspended before such an
+ exception occurs, the only reasonable way for the user code to obtain the
+ necessary handle is if unhandled_exception() throws the handle or some
+ object that contains the handle. That is outside of the designs here -
+ if the user code might need this corner-case, then such provision will
+ have to be made.
+
+ In the ramp, we implement destruction for the persistent frame state by
+ means of cleanups. These are run conditionally when the reference count
+ is 0 signalling that both the body and the ramp have completed.
+
+ In the body, once we pass the final suspend, then we test the use and
+ delete the state if the use is 0. */
/* The state that we collect during parsing (and template expansion) for
a coroutine. */
@@ -206,11 +285,10 @@ struct GTY((for_user)) coroutine_info
tree traits_type; /* The cached traits type for this function. */
tree handle_type; /* The cached coroutine handle for this function. */
tree self_h_proxy; /* A handle instance that is used as the proxy for the
- one that will eventually be allocated in the coroutine
- frame. */
+ one that will eventually be built in lowering. */
tree promise_proxy; /* Likewise, a proxy promise instance. */
- tree from_address; /* handle_type from_address function. */
- tree return_void; /* The expression for p.return_void() if it exists. */
+ tree from_address; /* handle_type from_address() function. */
+ tree return_void; /* The expression for p.return_void(), if it exists. */
location_t first_coro_keyword; /* The location of the keyword that made this
function into a coroutine. */
@@ -348,6 +426,7 @@ static GTY(()) tree coro_resume_index_id;
static GTY(()) tree coro_self_handle_id;
static GTY(()) tree coro_actor_continue_id;
static GTY(()) tree coro_frame_i_a_r_c_id;
+static GTY(()) tree coro_frame_refcount_id;
/* Create the identifiers used by the coroutines library interfaces and
the implementation frame state. */
@@ -385,6 +464,7 @@ coro_init_identifiers ()
coro_resume_index_id = get_identifier ("_Coro_resume_index");
coro_self_handle_id = get_identifier ("_Coro_self_handle");
coro_actor_continue_id = get_identifier ("_Coro_actor_continue");
+ coro_frame_refcount_id = get_identifier ("_Coro_frame_refcount");
}
/* Trees we only need to set up once. */
@@ -1277,8 +1357,14 @@ build_co_await (location_t loc, tree a, suspend_point_kind suspend_kind,
if (TREE_CODE (o_type) != RECORD_TYPE)
{
- error_at (loc, "awaitable type %qT is not a structure",
- o_type);
+ if (suspend_kind == FINAL_SUSPEND_POINT)
+ error_at (loc, "%qs awaitable type %qT is not a structure",
+ "final_suspend()", o_type);
+ else if (suspend_kind == INITIAL_SUSPEND_POINT)
+ error_at (loc, "%qs awaitable type %qT is not a structure",
+ "initial_suspend()", o_type);
+ else
+ error_at (loc, "awaitable type %qT is not a structure", o_type);
return error_mark_node;
}
@@ -1461,6 +1547,12 @@ finish_co_await_expr (location_t kw, tree expr)
if (!expr || error_operand_p (expr))
return error_mark_node;
+ if (cp_unevaluated_operand)
+ {
+ error_at (kw, "%qs cannot be used in an unevaluated context","co_await");
+ return error_mark_node;
+ }
+
if (!coro_common_keyword_context_valid_p (current_function_decl, kw,
"co_await"))
return error_mark_node;
@@ -1541,6 +1633,12 @@ finish_co_yield_expr (location_t kw, tree expr)
if (!expr || error_operand_p (expr))
return error_mark_node;
+ if (cp_unevaluated_operand)
+ {
+ error_at (kw, "%qs cannot be used in an unevaluated context","co_yield");
+ return error_mark_node;
+ }
+
/* Check the general requirements and simple syntax errors. */
if (!coro_common_keyword_context_valid_p (current_function_decl, kw,
"co_yield"))
@@ -1964,12 +2062,13 @@ struct coro_aw_data
tree coro_fp; /* Frame pointer var. */
tree resume_idx; /* This is the index var in the frame. */
tree i_a_r_c; /* initial suspend await_resume() was called if true. */
- tree self_h; /* This is a handle to the current coro (frame var). */
tree cleanup; /* This is where to go once we complete local destroy. */
tree cororet; /* This is where to go if we suspend. */
tree corocont; /* This is where to go if we continue. */
tree dispatch; /* This is where we go if we restart the dispatch. */
tree conthand; /* This is the handle for a continuation. */
+ tree handle_type; /* Handle type for this coroutine... */
+ tree hfa_m; /* ... and handle.from_address() for this. */
unsigned index; /* This is our current resume index. */
};
@@ -2027,8 +2126,10 @@ expand_one_await_expression (tree *expr, tree *await_expr, void *d)
tree awaiter_calls = TREE_OPERAND (saved_co_await, 3);
tree source = TREE_OPERAND (saved_co_await, 4);
- bool is_final = (source
- && TREE_INT_CST_LOW (source) == (int) FINAL_SUSPEND_POINT);
+ bool is_final
+ = (source && TREE_INT_CST_LOW (source) == (int) FINAL_SUSPEND_POINT);
+ bool is_initial
+ = (source && TREE_INT_CST_LOW (source) == (int) INITIAL_SUSPEND_POINT);
/* Build labels for the destinations of the control flow when we are resuming
or destroying. */
@@ -2085,6 +2186,18 @@ expand_one_await_expression (tree *expr, tree *await_expr, void *d)
tree suspend = TREE_VEC_ELT (awaiter_calls, 1); /* await_suspend(). */
tree susp_type = TREE_TYPE (suspend);
+ tree susp_call = suspend;
+ if (TREE_CODE (suspend) == TARGET_EXPR)
+ susp_call = TARGET_EXPR_INITIAL (suspend);
+ gcc_checking_assert (TREE_CODE (susp_call) == CALL_EXPR);
+ tree dummy_ch = build_dummy_object (data->handle_type);
+ r = fold_convert (build_pointer_type (void_type_node), data->coro_fp);
+ vec<tree, va_gc> *args = make_tree_vector_single (r);
+ tree hfa = cp_fold_rvalue (
+ build_new_method_call (dummy_ch, data->hfa_m, &args, NULL_TREE,
+ LOOKUP_NORMAL, NULL, tf_warning_or_error));
+ release_tree_vector (args);
+ CALL_EXPR_ARG (susp_call, call_expr_nargs (susp_call) - 1) = hfa;
bool is_cont = false;
/* NOTE: final suspend can't resume; the "resume" label in that case
@@ -2156,6 +2269,13 @@ expand_one_await_expression (tree *expr, tree *await_expr, void *d)
/* Resume point. */
add_stmt (build_stmt (loc, LABEL_EXPR, resume_label));
+ if (is_initial && data->i_a_r_c)
+ {
+ r = cp_build_modify_expr (loc, data->i_a_r_c, NOP_EXPR, boolean_true_node,
+ tf_warning_or_error);
+ finish_expr_stmt (r);
+ }
+
/* This will produce the value (if one is provided) from the co_await
expression. */
tree resume_call = TREE_VEC_ELT (awaiter_calls, 2); /* await_resume(). */
@@ -2397,6 +2517,11 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
bool spf = start_preparsed_function (actor, NULL_TREE, SF_PRE_PARSED);
gcc_checking_assert (spf);
gcc_checking_assert (cfun && current_function_decl && TREE_STATIC (actor));
+ if (flag_exceptions)
+ /* We, unconditionally, add a try/catch and rethrow.
+ TODO: Determine if the combination of initial suspend and the original
+ body cannot throw, and elide these additions. */
+ cp_function_chain->can_throw = true;
tree stmt = begin_function_body ();
tree actor_bind = build3 (BIND_EXPR, void_type_node, NULL, NULL, NULL);
@@ -2543,8 +2668,8 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
/* Finish the resume dispatcher. */
finish_switch_stmt (dispatcher);
- finish_else_clause (lsb_if);
+ finish_else_clause (lsb_if);
finish_if_stmt (lsb_if);
/* If we reach here then we've hit UB. */
@@ -2554,21 +2679,16 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
/* Now we start building the rewritten function body. */
add_stmt (build_stmt (loc, LABEL_EXPR, actor_begin_label));
- /* actor's coroutine 'self handle'. */
- tree ash = coro_build_frame_access_expr (actor_frame, coro_self_handle_id,
- false, tf_warning_or_error);
- /* So construct the self-handle from the frame address. */
- tree hfa_m = get_coroutine_from_address (orig);
- /* Should have been set earlier by coro_promise_type_found_p. */
- gcc_assert (hfa_m);
-
- tree r = build1 (CONVERT_EXPR, build_pointer_type (void_type_node), actor_fp);
- vec<tree, va_gc> *args = make_tree_vector_single (r);
- tree hfa = build_new_method_call (ash, hfa_m, &args, NULL_TREE, LOOKUP_NORMAL,
- NULL, tf_warning_or_error);
- r = cp_build_init_expr (ash, hfa);
- finish_expr_stmt (r);
- release_tree_vector (args);
+ tree i_a_r_c = NULL_TREE;
+ if (flag_exceptions)
+ {
+ i_a_r_c
+ = coro_build_frame_access_expr (actor_frame, coro_frame_i_a_r_c_id,
+ false, tf_warning_or_error);
+ tree m = cp_build_modify_expr (loc, i_a_r_c, NOP_EXPR,
+ boolean_false_node, tf_warning_or_error);
+ finish_expr_stmt (m);
+ }
/* Now we know the real promise, and enough about the frame layout to
decide where to put things. */
@@ -2583,69 +2703,74 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
/* Add in our function body with the co_returns rewritten to final form. */
add_stmt (fnbody);
- /* now do the tail of the function. */
+ /* We are done with the frame, but if the ramp still has a hold on it
+ we should not cleanup. So decrement the refcount and then return to
+ the ramp if it is > 0. */
+ tree coro_frame_refcount
+ = coro_build_frame_access_expr (actor_frame, coro_frame_refcount_id,
+ false, tf_warning_or_error);
+ tree released = build2_loc (loc, MINUS_EXPR, short_unsigned_type_node,
+ coro_frame_refcount,
+ build_int_cst (short_unsigned_type_node, 1));
+ tree r = cp_build_modify_expr (loc, coro_frame_refcount, NOP_EXPR, released,
+ tf_warning_or_error);
+ finish_expr_stmt (r);
+ tree cond = build2_loc (loc, NE_EXPR, short_unsigned_type_node,
+ coro_frame_refcount,
+ build_int_cst (short_unsigned_type_node, 0));
+ tree ramp_cu_if = begin_if_stmt ();
+ finish_if_stmt_cond (cond, ramp_cu_if);
+ finish_return_stmt (NULL_TREE);
+ finish_then_clause (ramp_cu_if);
+ finish_if_stmt (ramp_cu_if);
+
+ /* Otherwise, do the tail of the function; first cleanups. */
r = build_stmt (loc, LABEL_EXPR, del_promise_label);
add_stmt (r);
- /* Destructors for the things we built explicitly. */
+ /* Destructors for the things we built explicitly.
+ promise... */
if (tree c = cxx_maybe_build_cleanup (promise_proxy, tf_warning_or_error))
- add_stmt (c);
+ finish_expr_stmt (c);
- tree del_frame_label
- = create_named_label_with_ctx (loc, "coro.delete.frame", actor);
- r = build_stmt (loc, LABEL_EXPR, del_frame_label);
- add_stmt (r);
-
- /* Here deallocate the frame (if we allocated it), which we will have at
- present. */
- tree fnf2_x
- = coro_build_frame_access_expr (actor_frame, coro_frame_needs_free_id,
- false, tf_warning_or_error);
-
- tree need_free_if = begin_if_stmt ();
- fnf2_x = build1 (CONVERT_EXPR, integer_type_node, fnf2_x);
- tree cmp = build2 (NE_EXPR, integer_type_node, fnf2_x, integer_zero_node);
- finish_if_stmt_cond (cmp, need_free_if);
+ /* Argument copies ... */
while (!param_dtor_list->is_empty ())
{
tree parm_id = param_dtor_list->pop ();
tree a = coro_build_frame_access_expr (actor_frame, parm_id, false,
tf_warning_or_error);
if (tree dtor = cxx_maybe_build_cleanup (a, tf_warning_or_error))
- add_stmt (dtor);
+ finish_expr_stmt (dtor);
}
+ /* Here deallocate the frame (if we allocated it), which we will have at
+ present. */
+ tree fnf2_x
+ = coro_build_frame_access_expr (actor_frame, coro_frame_needs_free_id,
+ false, tf_warning_or_error);
+ tree need_free_if = begin_if_stmt ();
+ finish_if_stmt_cond (fnf2_x, need_free_if);
+
/* Build the frame DTOR. */
tree del_coro_fr
= build_coroutine_frame_delete_expr (actor_fp, frame_size,
promise_type, loc);
finish_expr_stmt (del_coro_fr);
finish_then_clause (need_free_if);
- tree scope = IF_SCOPE (need_free_if);
- IF_SCOPE (need_free_if) = NULL;
- r = do_poplevel (scope);
- add_stmt (r);
+ finish_if_stmt (need_free_if);
- /* done. */
- r = build_stmt (loc, RETURN_EXPR, NULL);
- suppress_warning (r); /* We don't want a warning about this. */
- r = maybe_cleanup_point_expr_void (r);
- add_stmt (r);
+ /* Done. */
+ finish_return_stmt (NULL_TREE);
/* This is the suspend return point. */
- r = build_stmt (loc, LABEL_EXPR, ret_label);
- add_stmt (r);
+ add_stmt (build_stmt (loc, LABEL_EXPR, ret_label));
- r = build_stmt (loc, RETURN_EXPR, NULL);
- suppress_warning (r); /* We don't want a warning about this. */
- r = maybe_cleanup_point_expr_void (r);
- add_stmt (r);
+ finish_return_stmt (NULL_TREE);
/* This is the 'continuation' return point. For such a case we have a coro
handle (from the await_suspend() call) and we want handle.resume() to
execute as a tailcall allowing arbitrary chaining of coroutines. */
- r = build_stmt (loc, LABEL_EXPR, continue_label);
- add_stmt (r);
+ add_stmt (build_stmt (loc, LABEL_EXPR, continue_label));
/* Should have been set earlier by the coro_initialized code. */
gcc_assert (void_coro_handle_address);
@@ -2668,12 +2793,19 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
gcc_checking_assert (maybe_cleanup_point_expr_void (r) == r);
add_stmt (r);
+ /* How to construct the handle for this coroutine from the frame address. */
+ tree hfa_m = get_coroutine_from_address (orig);
+ /* Should have been set earlier by coro_promise_type_found_p. */
+ gcc_assert (hfa_m);
+ tree handle_type = TREE_TYPE (get_coroutine_self_handle_proxy (orig));
+
/* We've now rewritten the tree and added the initial and final
co_awaits. Now pass over the tree and expand the co_awaits. */
- coro_aw_data data = {actor, actor_fp, resume_idx_var, NULL_TREE,
- ash, del_promise_label, ret_label,
- continue_label, restart_dispatch_label, continuation, 2};
+ coro_aw_data data = {actor, actor_fp, resume_idx_var, i_a_r_c,
+ del_promise_label, ret_label,
+ continue_label, restart_dispatch_label, continuation,
+ handle_type, hfa_m, 2};
cp_walk_tree (&actor_body, await_statement_expander, &data, NULL);
BIND_EXPR_BODY (actor_bind) = pop_stmt_list (actor_body);
@@ -2865,8 +2997,8 @@ find_any_await (tree *stmt, int *dosub, void *d)
if (TREE_CODE (*stmt) == CO_AWAIT_EXPR)
{
*dosub = 0; /* We don't need to consider this any further. */
- tree **p = (tree **) d;
- *p = stmt;
+ if (d)
+ *(tree **)d = stmt;
return *stmt;
}
return NULL_TREE;
@@ -3116,7 +3248,9 @@ flatten_await_stmt (var_nest_node *n, hash_set<tree> *promoted,
bool already_present = promoted->add (var);
gcc_checking_assert (!already_present);
tree inner = TARGET_EXPR_INITIAL (init);
- gcc_checking_assert (TREE_CODE (inner) != COND_EXPR);
+ gcc_checking_assert
+ (TREE_CODE (inner) != COND_EXPR
+ || !cp_walk_tree (&inner, find_any_await, nullptr, nullptr));
init = cp_build_modify_expr (input_location, var, INIT_EXPR, init,
tf_warning_or_error);
/* Simplify for the case that we have an init containing the temp
@@ -3421,7 +3555,8 @@ maybe_promote_temps (tree *stmt, void *d)
return cp_walk_tree (stmt, register_awaits, d, &visited);
}
-/* Lightweight callback to determine two key factors:
+/* Relatively lightweight callback to do initial assessment:
+ 0) Rewrite some await expressions.
1) If the statement/expression contains any await expressions.
2) If the statement/expression potentially requires a re-write to handle
TRUTH_{AND,OR}IF_EXPRs since, in most cases, they will need expansion
@@ -3438,6 +3573,39 @@ analyze_expression_awaits (tree *stmt, int *do_subtree, void *d)
switch (TREE_CODE (*stmt))
{
default: return NULL_TREE;
+ case CALL_EXPR:
+ {
+ tree fn = cp_get_callee_fndecl_nofold (*stmt);
+ /* Special-cases where we want to re-write await expressions to some
+ other value before they are otherwise processed. */
+ if (fn && DECL_IS_BUILTIN_CONSTANT_P (fn))
+ {
+ gcc_checking_assert (call_expr_nargs (*stmt) == 1);
+ tree expr = CALL_EXPR_ARG (*stmt, 0);
+ if (cp_walk_tree (&expr, find_any_await, nullptr, NULL))
+ {
+ if (TREE_CONSTANT (maybe_constant_value (expr)))
+ *stmt = integer_one_node;
+ else
+ *stmt = integer_zero_node;
+ }
+ *do_subtree = 0;
+ }
+ else if (!fn && CALL_EXPR_IFN (*stmt) == IFN_ASSUME)
+ {
+ tree expr = CALL_EXPR_ARG (*stmt, 0);
+ if (TREE_SIDE_EFFECTS (expr))
+ {
+ location_t loc_e = cp_expr_location (expr);
+ location_t loc_s = cp_expr_location (*stmt);
+ location_t loc_n = make_location (loc_e, loc_s, loc_s);
+ warning_at (loc_n, OPT_Wattributes,"assumption ignored"
+ " because it contains an await-expression");
+ *stmt = build_empty_stmt (loc_n);
+ }
+ }
+ }
+ break;
case CO_YIELD_EXPR:
/* co_yield is syntactic sugar, re-write it to co_await. */
*stmt = TREE_OPERAND (*stmt, 1);
@@ -4028,12 +4196,14 @@ rewrite_param_uses (tree *stmt, int *do_subtree ATTRIBUTE_UNUSED, void *d)
}
/* Build up a set of info that determines how each param copy will be
- handled. */
+ handled. We store this in a hash map so that we can access it from
+ a tree walk callback that re-writes the original parameters to their
+ copies. */
-static void
-analyze_fn_parms (tree orig, hash_map<tree, param_info> *param_uses)
+void
+cp_coroutine_transform::analyze_fn_parms ()
{
- if (!DECL_ARGUMENTS (orig))
+ if (!DECL_ARGUMENTS (orig_fn_decl))
return;
/* Build a hash map with an entry for each param.
@@ -4043,19 +4213,19 @@ analyze_fn_parms (tree orig, hash_map<tree, param_info> *param_uses)
Then a tree list of the uses.
The second two entries start out empty - and only get populated
when we see uses. */
- bool lambda_p = LAMBDA_FUNCTION_P (orig);
+ bool lambda_p = LAMBDA_FUNCTION_P (orig_fn_decl);
/* Count the param copies from 1 as per the std. */
unsigned parm_num = 1;
- for (tree arg = DECL_ARGUMENTS (orig); arg != NULL;
+ for (tree arg = DECL_ARGUMENTS (orig_fn_decl); arg != NULL;
++parm_num, arg = DECL_CHAIN (arg))
{
bool existed;
- param_info &parm = param_uses->get_or_insert (arg, &existed);
+ param_info &parm = param_uses.get_or_insert (arg, &existed);
gcc_checking_assert (!existed);
parm.body_uses = NULL;
tree actual_type = TREE_TYPE (arg);
- actual_type = complete_type_or_else (actual_type, orig);
+ actual_type = complete_type_or_else (actual_type, orig_fn_decl);
if (actual_type == NULL_TREE)
actual_type = error_mark_node;
parm.orig_type = actual_type;
@@ -4089,17 +4259,7 @@ analyze_fn_parms (tree orig, hash_map<tree, param_info> *param_uses)
}
parm.field_id = name;
if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (parm.frame_type))
- {
- char *buf = xasprintf ("_Coro_q%u_%s_live", parm_num,
- DECL_NAME (arg) ? IDENTIFIER_POINTER (name)
- : "__unnamed");
- parm.guard_var
- = coro_build_artificial_var (UNKNOWN_LOCATION, get_identifier (buf),
- boolean_type_node, orig,
- boolean_false_node);
- free (buf);
- parm.trivial_dtor = false;
- }
+ parm.trivial_dtor = false;
else
parm.trivial_dtor = true;
}
@@ -4290,8 +4450,7 @@ cp_coroutine_transform::wrap_original_function_body ()
{
/* Avoid the code here attaching a location that makes the debugger jump. */
iloc_sentinel stable_input_loc (fn_start);
- location_t loc = UNKNOWN_LOCATION;
- input_location = loc;
+ location_t loc = fn_start;
/* This will be our new outer scope. */
tree update_body
@@ -4339,7 +4498,6 @@ cp_coroutine_transform::wrap_original_function_body ()
/* Wrap the function body in a try {} catch (...) {} block, if exceptions
are enabled. */
tree var_list = NULL_TREE;
- tree initial_await = build_init_or_final_await (fn_start, false);
/* [stmt.return.coroutine] / 3
If p.return_void() is a valid expression, flowing off the end of a
@@ -4378,16 +4536,6 @@ cp_coroutine_transform::wrap_original_function_body ()
var_list = promise;
add_decl_expr (promise);
- /* We need a handle to this coroutine, which is passed to every
- await_suspend(). This was created on demand when parsing we now link it
- into our scope. */
- var = get_coroutine_self_handle_proxy (orig_fn_decl);
- DECL_CONTEXT (var) = orig_fn_decl;
- DECL_SOURCE_LOCATION (var) = loc;
- DECL_CHAIN (var) = var_list;
- var_list = var;
- add_decl_expr (var);
-
/* If we have function parms, then these will be copied to the coroutine
frame as per [dcl.fct.def.coroutine] / 13.
Here, we create a local (proxy) variable for each parm, since the original
@@ -4431,27 +4579,44 @@ cp_coroutine_transform::wrap_original_function_body ()
var_list = resume_idx_var;
add_decl_expr (resume_idx_var);
+ tree coro_frame_refcount
+ = coro_build_artificial_var (loc, coro_frame_refcount_id,
+ short_unsigned_type_node, orig_fn_decl,
+ NULL_TREE);
+ DECL_CHAIN (coro_frame_refcount) = var_list;
+ var_list = coro_frame_refcount;
+ add_decl_expr (coro_frame_refcount);
+
/* If the coroutine has a frame that needs to be freed, this will be set by
the ramp. */
- var = coro_build_artificial_var (fn_start, coro_frame_needs_free_id,
+ var = coro_build_artificial_var (loc, coro_frame_needs_free_id,
boolean_type_node, orig_fn_decl, NULL_TREE);
DECL_CHAIN (var) = var_list;
var_list = var;
add_decl_expr (var);
+ /* We consider that the body has a use of the frame once we start to process
+ the initial suspend expression. (the use might be relinquished if we
+ encounter an exception before the body is finished). */
+ tree body_use
+ = build2_loc (loc, PLUS_EXPR, short_unsigned_type_node, coro_frame_refcount,
+ build_int_cst (short_unsigned_type_node, 1));
+ body_use = cp_build_modify_expr (loc, coro_frame_refcount, NOP_EXPR, body_use,
+ tf_warning_or_error);
+ finish_expr_stmt (body_use);
if (flag_exceptions)
{
/* Build promise.unhandled_exception(); */
tree ueh
= coro_build_promise_expression (orig_fn_decl, promise,
coro_unhandled_exception_identifier,
- fn_start, NULL, /*musthave=*/true);
+ loc, NULL, /*musthave=*/true);
/* Create and initialize the initial-await-resume-called variable per
[dcl.fct.def.coroutine] / 5.3. */
tree i_a_r_c
= coro_build_artificial_var (loc, coro_frame_i_a_r_c_id,
boolean_type_node, orig_fn_decl,
- boolean_false_node);
+ NULL_TREE);
DECL_CHAIN (i_a_r_c) = var_list;
var_list = i_a_r_c;
add_decl_expr (i_a_r_c);
@@ -4459,34 +4624,28 @@ cp_coroutine_transform::wrap_original_function_body ()
tree tcb = build_stmt (loc, TRY_BLOCK, NULL_TREE, NULL_TREE);
add_stmt (tcb);
TRY_STMTS (tcb) = push_stmt_list ();
- if (initial_await != error_mark_node)
- {
- /* Build a compound expression that sets the
- initial-await-resume-called variable true and then calls the
- initial suspend expression await resume.
- In the case that the user decides to make the initial await
- await_resume() return a value, we need to discard it and, it is
- a reference type, look past the indirection. */
- if (INDIRECT_REF_P (initial_await))
- initial_await = TREE_OPERAND (initial_await, 0);
- /* In the case that the initial_await returns a target expression
- we might need to look through that to update the await expr. */
- tree iaw = initial_await;
- if (TREE_CODE (iaw) == TARGET_EXPR)
- iaw = TARGET_EXPR_INITIAL (iaw);
- gcc_checking_assert (TREE_CODE (iaw) == CO_AWAIT_EXPR);
- tree vec = TREE_OPERAND (iaw, 3);
- tree aw_r = TREE_VEC_ELT (vec, 2);
- aw_r = convert_to_void (aw_r, ICV_STATEMENT, tf_warning_or_error);
- tree update = build2 (MODIFY_EXPR, boolean_type_node, i_a_r_c,
- boolean_true_node);
- aw_r = cp_build_compound_expr (update, aw_r, tf_warning_or_error);
- TREE_VEC_ELT (vec, 2) = aw_r;
- }
+ /* We need a new scope to handle the cleanup for the ramp use that is
+ needed for exceptions. */
+ tree except_scope = begin_compound_stmt (0);
+ current_binding_level->artificial = 1;
+ tree release
+ = build2_loc (loc, MINUS_EXPR, short_unsigned_type_node,
+ coro_frame_refcount, build_int_cst (short_unsigned_type_node, 1));
+ release = cp_build_modify_expr (loc, coro_frame_refcount, NOP_EXPR,
+ release, tf_warning_or_error);
+ /* Once we pass the initial await resume, the cleanup rules on exception
+ change so that the responsibility lies with the caller. */
+ release = build3 (COND_EXPR, void_type_node, i_a_r_c,
+ build_empty_stmt (loc), release);
+ push_cleanup (NULL_TREE, release, /*ehonly*/true);
/* Add the initial await to the start of the user-authored function. */
finish_expr_stmt (initial_await);
+ /* End the scope that handles the remove of frame-use on exception. */
+ finish_compound_stmt (except_scope);
+
/* Append the original function body. */
add_stmt (coroutine_body);
+
if (return_void)
add_stmt (return_void);
TRY_STMTS (tcb) = pop_stmt_list (TRY_STMTS (tcb));
@@ -4531,9 +4690,9 @@ cp_coroutine_transform::wrap_original_function_body ()
tree ueh_meth
= lookup_promise_method (orig_fn_decl,
coro_unhandled_exception_identifier,
- fn_start, /*musthave=*/false);
+ loc, /*musthave=*/false);
if (!ueh_meth || ueh_meth == error_mark_node)
- warning_at (fn_start, 0, "no member named %qE in %qT",
+ warning_at (loc, 0, "no member named %qE in %qT",
coro_unhandled_exception_identifier,
get_coroutine_promise_type (orig_fn_decl));
}
@@ -4546,6 +4705,10 @@ cp_coroutine_transform::wrap_original_function_body ()
add_stmt (return_void);
}
+ /* We are now doing actions associated with the end of the function, so
+ point to the closing brace. */
+ input_location = loc = fn_end;
+
/* co_return branches to the final_suspend label, so declare that now. */
fs_label
= create_named_label_with_ctx (loc, "final.suspend", NULL_TREE);
@@ -4557,7 +4720,8 @@ cp_coroutine_transform::wrap_original_function_body ()
zero_resume = build2_loc (loc, MODIFY_EXPR, act_des_fn_ptr_type,
resume_fn_ptr, zero_resume);
finish_expr_stmt (zero_resume);
- finish_expr_stmt (build_init_or_final_await (fn_start, true));
+ finish_expr_stmt (final_await);
+
BIND_EXPR_BODY (update_body) = pop_stmt_list (BIND_EXPR_BODY (update_body));
BIND_EXPR_VARS (update_body) = nreverse (var_list);
BLOCK_VARS (top_block) = BIND_EXPR_VARS (update_body);
@@ -4866,47 +5030,6 @@ cp_coroutine_transform::build_ramp_function ()
coro_fp = pushdecl (coro_fp);
add_decl_expr (coro_fp);
- tree coro_promise_live = NULL_TREE;
- tree coro_gro_live = NULL_TREE;
- if (flag_exceptions)
- {
- /* Signal that we need to clean up the promise object on exception. */
- coro_promise_live
- = coro_build_and_push_artificial_var (loc, "_Coro_promise_live",
- boolean_type_node, orig_fn_decl,
- boolean_false_node);
-
- /* When the get-return-object is in the RETURN slot, we need to arrange
- for cleanup on exception. */
- coro_gro_live
- = coro_build_and_push_artificial_var (loc, "_Coro_gro_live",
- boolean_type_node, orig_fn_decl,
- boolean_false_node);
-
- /* To signal that we need to cleanup copied function args. */
- if (DECL_ARGUMENTS (orig_fn_decl))
- for (tree arg = DECL_ARGUMENTS (orig_fn_decl); arg != NULL;
- arg = DECL_CHAIN (arg))
- {
- param_info *parm_i = param_uses.get (arg);
- if (parm_i->trivial_dtor)
- continue;
- parm_i->guard_var = pushdecl (parm_i->guard_var);
- add_decl_expr (parm_i->guard_var);
- }
- }
-
- /* deref the frame pointer, to use in member access code. */
- tree deref_fp
- = cp_build_indirect_ref (loc, coro_fp, RO_UNARY_STAR,
- tf_warning_or_error);
- tree frame_needs_free
- = coro_build_and_push_artificial_var_with_dve (loc,
- coro_frame_needs_free_id,
- boolean_type_node,
- orig_fn_decl, NULL_TREE,
- deref_fp);
-
/* Build the frame. */
/* The CO_FRAME internal function is a mechanism to allow the middle end
@@ -4950,37 +5073,35 @@ cp_coroutine_transform::build_ramp_function ()
finish_if_stmt (if_stmt);
}
+ /* Dereference the frame pointer, to use in member access code. */
+ tree deref_fp
+ = cp_build_indirect_ref (loc, coro_fp, RO_UNARY_STAR, tf_warning_or_error);
+
/* For now, once allocation has succeeded we always assume that this needs
destruction, there's no impl. for frame allocation elision. */
- r = cp_build_init_expr (frame_needs_free, boolean_true_node);
- finish_expr_stmt (r);
-
- /* Set up the promise. */
- tree p
- = coro_build_and_push_artificial_var_with_dve (loc, coro_promise_id,
- promise_type, orig_fn_decl,
- NULL_TREE, deref_fp);
+ tree frame_needs_free
+ = coro_build_and_push_artificial_var_with_dve (loc,
+ coro_frame_needs_free_id,
+ boolean_type_node,
+ orig_fn_decl,
+ boolean_true_node,
+ deref_fp);
+ /* Although it appears to be unused here the frame entry is needed and we
+ just set it true. */
+ TREE_USED (frame_needs_free) = true;
- /* Up to now any exception thrown will propagate directly to the caller.
- This is OK since the only source of such exceptions would be in allocation
- of the coroutine frame, and therefore the ramp will not have initialized
- any further state. From here, we will track state that needs explicit
- destruction in the case that promise or g.r.o setup fails or an exception
- is thrown from the initial suspend expression. */
- tree ramp_try_block = NULL_TREE;
- tree ramp_try_stmts = NULL_TREE;
- tree iarc_x = NULL_TREE;
- if (flag_exceptions)
- {
- iarc_x
- = coro_build_and_push_artificial_var_with_dve (loc,
- coro_frame_i_a_r_c_id,
- boolean_type_node,
- orig_fn_decl, NULL_TREE,
- deref_fp);
- ramp_try_block = begin_try_block ();
- ramp_try_stmts = begin_compound_stmt (BCS_TRY_BLOCK);
- }
+ tree coro_frame_refcount
+ = coro_build_and_push_artificial_var_with_dve (loc, coro_frame_refcount_id,
+ short_unsigned_type_node,
+ orig_fn_decl, NULL_TREE,
+ deref_fp);
+ /* Cleanup if both the ramp and the body have finished. */
+ tree cond
+ = build2_loc (loc, EQ_EXPR, short_unsigned_type_node, coro_frame_refcount,
+ build_int_cst (short_unsigned_type_node, 0));
+ r = build3 (COND_EXPR, void_type_node, cond, delete_frame_call,
+ build_empty_stmt (loc));
+ push_cleanup (coro_fp, r, /*eh_only*/false);
/* Put the resumer and destroyer functions in. */
@@ -5052,24 +5173,34 @@ cp_coroutine_transform::build_ramp_function ()
tf_warning_or_error);
}
finish_expr_stmt (r);
+
+ /* Arrange for parm copies to be cleaned up when an exception is
+ thrown before initial await resume. */
if (!parm.trivial_dtor)
{
- param_dtor_list.safe_push (parm.field_id);
- /* Cleanup this frame copy on exception. */
parm.fr_copy_dtor
= cxx_maybe_build_cleanup (fld_idx, tf_warning_or_error);
- if (flag_exceptions)
+ if (parm.fr_copy_dtor && parm.fr_copy_dtor != error_mark_node)
{
- /* This var is now live. */
- r = build_modify_expr (loc, parm.guard_var,
- boolean_type_node, INIT_EXPR, loc,
- boolean_true_node, boolean_type_node);
- finish_expr_stmt (r);
+ param_dtor_list.safe_push (parm.field_id);
+ cond
+ = build2_loc (loc, EQ_EXPR, short_unsigned_type_node,
+ coro_frame_refcount,
+ build_int_cst (short_unsigned_type_node, 0));
+ r = build3_loc (loc, COND_EXPR, void_type_node, cond,
+ parm.fr_copy_dtor, build_empty_stmt (loc));
+ push_cleanup (fld_idx, r, /*eh_only*/false);
}
}
}
}
+ /* Set up the promise. */
+ tree p
+ = coro_build_and_push_artificial_var_with_dve (loc, coro_promise_id,
+ promise_type, orig_fn_decl,
+ NULL_TREE, deref_fp);
+
if (type_build_ctor_call (promise_type))
{
/* Construct the promise object [dcl.fct.def.coroutine] / 5.7.
@@ -5103,11 +5234,16 @@ cp_coroutine_transform::build_ramp_function ()
finish_expr_stmt (r);
}
- tree promise_dtor = cxx_maybe_build_cleanup (p, tf_warning_or_error);;
- if (flag_exceptions && promise_dtor)
+ tree promise_dtor = cxx_maybe_build_cleanup (p, tf_warning_or_error);
+ /* If the promise is live, then run its dtor if that's available. */
+ if (promise_dtor && promise_dtor != error_mark_node)
{
- r = cp_build_init_expr (coro_promise_live, boolean_true_node);
- finish_expr_stmt (r);
+ cond = build2_loc (loc, EQ_EXPR, short_unsigned_type_node,
+ coro_frame_refcount,
+ build_int_cst (short_unsigned_type_node, 0));
+ r = build3 (COND_EXPR, void_type_node, cond, promise_dtor,
+ build_empty_stmt (loc));
+ push_cleanup (p, r, /*eh_only*/false);
}
tree get_ro
@@ -5122,8 +5258,11 @@ cp_coroutine_transform::build_ramp_function ()
/* Check for a bad get return object type.
[dcl.fct.def.coroutine] / 7 requires:
The expression promise.get_return_object() is used to initialize the
- returned reference or prvalue result object ... */
- tree gro_type = TREE_TYPE (get_ro);
+ returned reference or prvalue result object ...
+ When we use a local to hold this, it is decltype(auto). */
+ tree gro_type
+ = finish_decltype_type (get_ro, /*id_expression_or_member_access_p*/false,
+ tf_warning_or_error);
if (VOID_TYPE_P (gro_type) && !void_ramp_p)
{
error_at (fn_start, "no viable conversion from %<void%> provided by"
@@ -5136,169 +5275,62 @@ cp_coroutine_transform::build_ramp_function ()
(loc, coro_resume_index_id, short_unsigned_type_node, orig_fn_decl,
build_zero_cst (short_unsigned_type_node), deref_fp);
- if (flag_exceptions && iarc_x)
- {
- r = cp_build_init_expr (iarc_x, boolean_false_node);
- finish_expr_stmt (r);
- }
-
- /* Used for return objects in the RESULT slot. */
- tree ret_val_dtor = NULL_TREE;
- tree retval = NULL_TREE;
+ /* We must manage the cleanups ourselves, with the exception of the g_r_o,
+ because the responsibility for them changes after the initial suspend.
+ However, any use of cxx_maybe_build_cleanup () in preceding code can
+ set the throwing_cleanup flag. */
+ cp_function_chain->throwing_cleanup = false;
/* [dcl.fct.def.coroutine] / 7
The expression promise.get_return_object() is used to initialize the
glvalue result or prvalue result object of a call to a coroutine. */
- /* We must manage the cleanups ourselves, because the responsibility for
- them changes after the initial suspend. However, any use of
- cxx_maybe_build_cleanup () can set the throwing_cleanup flag. */
- cp_function_chain->throwing_cleanup = false;
+ tree coro_gro = NULL_TREE;
if (void_ramp_p)
/* We still want to call the method, even if the result is unused. */
- r = get_ro;
+ finish_expr_stmt (get_ro);
else
{
- /* The initial section of finish_return_expr (). */
- bool no_warning;
- bool dangling;
- /* Without a relevant location, bad conversions in check_return_expr
- result in unusable diagnostics, since there is not even a mention
- of the relevant function. Here we carry out the first part of
- finish_return_expr(). */
- input_location = fn_start;
- r = check_return_expr (get_ro, &no_warning, &dangling);
- input_location = UNKNOWN_LOCATION;
- gcc_checking_assert (!dangling);
- /* Check for bad things. */
- if (!r || r == error_mark_node)
- return false;
- if (!aggregate_value_p (fn_return_type, orig_fn_decl)
- && TREE_CODE (r) == INIT_EXPR)
- {
- /* If fn_return_type doesn't need to be returned in memory, normally
- gimplify_return_expr redirects the INIT_EXPR to a temporary. But
- r isn't wrapped in the RETURN_EXPR, so we need to do the
- redirection here as well. See PR118874. */
- tree temp = create_temporary_var (fn_return_type);
- add_decl_expr (temp);
- retval = copy_node (r);
- TREE_OPERAND (r, 0) = temp;
- TREE_OPERAND (retval, 1) = temp;
- }
- else
- retval = DECL_RESULT (orig_fn_decl);
- }
+ /* Per CWG2563, we keep the result of promise.get_return_object () in
+ a temp which is then used to intialize the return object, including
+ NVRO. */
- finish_expr_stmt (r);
-
- if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (fn_return_type))
- /* If some part of the initalization code (prior to the await_resume
- of the initial suspend expression), then we need to clean up the
- return value. */
- ret_val_dtor = cxx_maybe_build_cleanup (DECL_RESULT (orig_fn_decl),
- tf_warning_or_error);
+ /* Temporary var to hold the g_r_o across the function body. */
+ coro_gro
+ = coro_build_and_push_artificial_var (loc, "_Coro_gro", gro_type,
+ orig_fn_decl, NULL_TREE);
- /* If we have a live g.r.o in the return slot, then signal this for exception
- cleanup. */
- if (flag_exceptions && ret_val_dtor)
- {
- r = cp_build_init_expr (coro_gro_live, boolean_true_node);
+ r = cp_build_init_expr (coro_gro, STRIP_REFERENCE_REF (get_ro));
finish_expr_stmt (r);
+ tree coro_gro_cleanup
+ = cxx_maybe_build_cleanup (coro_gro, tf_warning_or_error);
+ if (coro_gro_cleanup)
+ push_cleanup (coro_gro, coro_gro_cleanup, /*eh_only*/false);
}
- /* Start the coroutine body. */
+ /* Start the coroutine body, we now have a use of the frame... */
+ r = cp_build_modify_expr (loc, coro_frame_refcount, NOP_EXPR,
+ build_int_cst (short_unsigned_type_node, 1),
+ tf_warning_or_error);
+ finish_expr_stmt (r);
+ /* ... but when we finish we want to release that, and we want to do that
+ before any of the other cleanups run. */
+ tree released
+ = build2_loc (loc, MINUS_EXPR, short_unsigned_type_node, coro_frame_refcount,
+ build_int_cst (short_unsigned_type_node, 1));
+ released = cp_build_modify_expr (loc, coro_frame_refcount, NOP_EXPR, released,
+ tf_warning_or_error);
+ push_cleanup (NULL_TREE, released, /*eh_only*/false);
+
r = build_call_expr_loc (fn_start, resumer, 1, coro_fp);
finish_expr_stmt (r);
/* The ramp is done, we just need the return statement, which we build from
the return object we constructed before we called the actor. */
- r = retval;
-
- /* The reminder of finish_return_expr (). */
- r = build_stmt (loc, RETURN_EXPR, r);
- r = maybe_cleanup_point_expr_void (r);
- r = add_stmt (r);
+ r = void_ramp_p ? NULL_TREE : convert_from_reference (coro_gro);
+ finish_return_stmt (r);
- if (flag_exceptions)
- {
- finish_compound_stmt (ramp_try_stmts);
- finish_try_block (ramp_try_block);
- tree handler = begin_handler ();
- finish_handler_parms (NULL_TREE, handler); /* catch (...) */
-
- /* If we have a live G.R.O in the return slot, then run its DTOR. */
- if (ret_val_dtor && ret_val_dtor != error_mark_node)
- {
- tree gro_d_if = begin_if_stmt ();
- finish_if_stmt_cond (coro_gro_live, gro_d_if);
- finish_expr_stmt (ret_val_dtor);
- finish_then_clause (gro_d_if);
- finish_if_stmt (gro_d_if);
- }
-
- /* Before initial resume is called, the responsibility for cleanup on
- exception falls to the ramp. After that, the coroutine body code
- should do the cleanup. This is signalled by the flag
- 'initial_await_resume_called'. */
-
- tree not_iarc
- = build1_loc (loc, TRUTH_NOT_EXPR, boolean_type_node, iarc_x);
- tree cleanup_if = begin_if_stmt ();
- finish_if_stmt_cond (not_iarc, cleanup_if);
- /* If the promise is live, then run its dtor if that's available. */
- if (promise_dtor && promise_dtor != error_mark_node)
- {
- tree promise_d_if = begin_if_stmt ();
- finish_if_stmt_cond (coro_promise_live, promise_d_if);
- finish_expr_stmt (promise_dtor);
- finish_then_clause (promise_d_if);
- finish_if_stmt (promise_d_if);
- }
-
- /* Clean up any frame copies of parms with non-trivial dtors.
- Do this in reverse order from their creation. */
- auto_vec<param_info *> worklist;
- if (DECL_ARGUMENTS (orig_fn_decl))
- for (tree arg = DECL_ARGUMENTS (orig_fn_decl); arg != NULL;
- arg = DECL_CHAIN (arg))
- {
- param_info *parm_i = param_uses.get (arg);
- if (parm_i->trivial_dtor)
- continue;
- worklist.safe_push (parm_i);
- }
- while (!worklist.is_empty ())
- {
- param_info *parm_i = worklist.pop ();
- if (parm_i->fr_copy_dtor && parm_i->fr_copy_dtor != error_mark_node)
- {
- tree dtor_if = begin_if_stmt ();
- finish_if_stmt_cond (parm_i->guard_var, dtor_if);
- finish_expr_stmt (parm_i->fr_copy_dtor);
- finish_then_clause (dtor_if);
- finish_if_stmt (dtor_if);
- }
- }
-
- /* No delete the frame if required. */
- tree fnf_if = begin_if_stmt ();
- finish_if_stmt_cond (frame_needs_free, fnf_if);
- finish_expr_stmt (delete_frame_call);
- finish_then_clause (fnf_if);
- finish_if_stmt (fnf_if);
-
- /* Finished cleanups conditional on "initial resume is not called". */
- finish_then_clause (cleanup_if);
- finish_if_stmt (cleanup_if);
-
- tree rethrow = build_throw (loc, NULL_TREE, tf_warning_or_error);
- suppress_warning (rethrow);
- finish_expr_stmt (rethrow);
- finish_handler (handler);
- finish_handler_sequence (ramp_try_block);
- }
finish_compound_stmt (ramp_fnbody);
return true;
}
@@ -5329,9 +5361,10 @@ cp_coroutine_transform::cp_coroutine_transform (tree _orig_fn, bool _inl)
}
/* We don't have the locus of the opening brace - it's filled in later (and
- there doesn't really seem to be any easy way to get at it).
- The closing brace is assumed to be input_location. */
+ there doesn't really seem to be any easy way to get at it). */
fn_start = DECL_SOURCE_LOCATION (orig_fn_decl);
+ /* The closing brace is assumed to be input_location. */
+ fn_end = input_location;
/* Build types we need. */
tree fr_name = get_fn_local_identifier (orig_fn_decl, "Frame");
@@ -5372,7 +5405,6 @@ cp_coroutine_transform::~cp_coroutine_transform ()
bool _Coro_frame_needs_free; free the coro frame mem if set.
bool _Coro_i_a_r_c; [dcl.fct.def.coroutine] / 5.3
short _Coro_resume_index;
- handle_type _Coro_self_handle;
parameter copies (were required).
local variables saved (including awaitables)
(maybe) trailing space.
@@ -5399,7 +5431,7 @@ cp_coroutine_transform::apply_transforms ()
/* Collect information on the original function params and their use in the
function body. */
- analyze_fn_parms (orig_fn_decl, &param_uses);
+ analyze_fn_parms ();
/* Declare the actor and destroyer functions, the following code needs to
see these. */
@@ -5410,6 +5442,16 @@ cp_coroutine_transform::apply_transforms ()
= coro_build_actor_or_destroy_function (orig_fn_decl, act_des_fn_type,
frame_ptr_type, false);
+ /* Avoid repeating diagnostics about promise or awaiter fails. */
+ if (!seen_error ())
+ {
+ iloc_sentinel stable_input_loc (fn_start);
+ initial_await = build_init_or_final_await (fn_start, false);
+ input_location = fn_end;
+ if (initial_await && initial_await != error_mark_node)
+ final_await = build_init_or_final_await (fn_end, true);
+ }
+
/* Transform the function body as per [dcl.fct.def.coroutine] / 5. */
wrap_original_function_body ();