type. */
tree functyp = TREE_TYPE (fndecl);
+ tree arg = DECL_ARGUMENTS (fndecl);
+ bool lambda_p = LAMBDA_FUNCTION_P (fndecl);
tree arg_node = TYPE_ARG_TYPES (functyp);
tree argtypes = make_tree_vec (list_length (arg_node)-1);
unsigned p = 0;
while (arg_node != NULL_TREE && !VOID_TYPE_P (TREE_VALUE (arg_node)))
{
- TREE_VEC_ELT (argtypes, p++) = TREE_VALUE (arg_node);
+ /* See PR94807, as to why we must exclude lambda here. */
+ if (is_this_parameter (arg) && !lambda_p)
+ {
+ /* We pass a reference to *this to the param preview. */
+ tree ct = TREE_TYPE (TREE_TYPE (arg));
+ TREE_VEC_ELT (argtypes, p++) = cp_build_reference_type (ct, false);
+ }
+ else
+ TREE_VEC_ELT (argtypes, p++) = TREE_VALUE (arg_node);
+
arg_node = TREE_CHAIN (arg_node);
+ arg = DECL_CHAIN (arg);
}
tree argtypepack = cxx_make_type (TYPE_ARGUMENT_PACK);
{
/* Get the coroutine traits template class instance for the function
signature we have - coroutine_traits <R, ...> */
- tree return_type = TREE_TYPE (TREE_TYPE (fndecl));
- if (!CLASS_TYPE_P (return_type))
- {
- /* It makes more sense to show the function header for this, even
- though we will have encountered it when processing a keyword.
- Only emit the error once, not for every keyword we encounter. */
- if (!coro_info->coro_ret_type_error_emitted)
- error_at (DECL_SOURCE_LOCATION (fndecl), "coroutine return type"
- " %qT is not a class", return_type);
- coro_info->coro_ret_type_error_emitted = true;
- return false;
- }
tree templ_class = instantiate_coro_traits (fndecl, loc);
tree o;
if (MAYBE_CLASS_TYPE_P (TREE_TYPE (a)))
{
- tree overload = NULL_TREE;
o = build_new_op (loc, CO_AWAIT_EXPR, LOOKUP_NORMAL, a, NULL_TREE,
- NULL_TREE, &overload, tf_warning_or_error);
+ NULL_TREE, NULL, tf_warning_or_error);
/* If no viable functions are found, o is a. */
if (!o || o == error_mark_node)
o = a;
if (INDIRECT_REF_P (e_proxy))
e_proxy = TREE_OPERAND (e_proxy, 0);
if (TREE_CODE (e_proxy) == PARM_DECL
- || (TREE_CODE (e_proxy) == VAR_DECL && !DECL_ARTIFICIAL (e_proxy)))
+ || (VAR_P (e_proxy) && (!DECL_ARTIFICIAL (e_proxy)
+ || DECL_HAS_VALUE_EXPR_P (e_proxy))))
e_proxy = o;
else
{
tree awaiter_calls = make_tree_vec (3);
TREE_VEC_ELT (awaiter_calls, 0) = awrd_call; /* await_ready(). */
TREE_VEC_ELT (awaiter_calls, 1) = awsp_call; /* await_suspend(). */
+ tree te = NULL_TREE;
+ if (TREE_CODE (awrs_call) == TARGET_EXPR)
+ {
+ te = awrs_call;
+ awrs_call = TREE_OPERAND (awrs_call, 1);
+ }
TREE_VEC_ELT (awaiter_calls, 2) = awrs_call; /* await_resume(). */
tree await_expr = build5_loc (loc, CO_AWAIT_EXPR,
a, e_proxy, o, awaiter_calls,
build_int_cst (integer_type_node,
(int) suspend_kind));
- return convert_from_reference (await_expr);
+ if (te)
+ {
+ TREE_OPERAND (te, 1) = await_expr;
+ await_expr = te;
+ }
+ tree t = convert_from_reference (await_expr);
+ return t;
}
tree
if (at_meth)
{
/* try to build a = p.await_transform (e). */
- tree at_fn = NULL_TREE;
vec<tree, va_gc> *args = make_tree_vector_single (expr);
a = build_new_method_call (get_coroutine_promise_proxy (
current_function_decl),
at_meth, &args, NULL_TREE, LOOKUP_NORMAL,
- &at_fn, tf_warning_or_error);
+ NULL, tf_warning_or_error);
/* As I read the section.
We saw an await_transform method, so it's mandatory that we replace
expr with p.await_transform (expr), therefore if the method call fails
(presumably, we don't have suitable arguments) then this part of the
process fails. */
- if (!at_fn || a == error_mark_node)
+ if (a == error_mark_node)
return error_mark_node;
}
if (!y_meth || y_meth == error_mark_node)
return error_mark_node;
- tree yield_fn = NULL_TREE;
+ /* [expr.yield] / 1
+ Let e be the operand of the yield-expression and p be an lvalue naming
+ the promise object of the enclosing coroutine, then the yield-expression
+ is equivalent to the expression co_await p.yield_value(e).
+ build p.yield_value(e): */
vec<tree, va_gc> *args = make_tree_vector_single (expr);
- tree yield_call = build_new_method_call (
- get_coroutine_promise_proxy (current_function_decl), y_meth, &args,
- NULL_TREE, LOOKUP_NORMAL, &yield_fn, tf_warning_or_error);
-
- if (!yield_fn || yield_call == error_mark_node)
- return error_mark_node;
+ tree yield_call = build_new_method_call
+ (get_coroutine_promise_proxy (current_function_decl), y_meth, &args,
+ NULL_TREE, LOOKUP_NORMAL, NULL, tf_warning_or_error);
- /* So now we have the type of p.yield_value (e).
- Now we want to build co_await p.yield_value (e).
+ /* Now build co_await p.yield_value (e).
Noting that for co_yield, there is no evaluation of any potential
- promise transform_await(). */
+ promise transform_await(), so we call build_co_await directly. */
tree op = build_co_await (kw, yield_call, CO_YIELD_SUSPEND_POINT);
if (op != error_mark_node)
{
- op = build2_loc (kw, CO_YIELD_EXPR, TREE_TYPE (op), expr, op);
+ if (REFERENCE_REF_P (op))
+ op = TREE_OPERAND (op, 0);
+ /* If the await expression is wrapped in a TARGET_EXPR, then transfer
+ that wrapper to the CO_YIELD_EXPR, since this is just a proxy for
+ its contained await. Otherwise, just build the CO_YIELD_EXPR. */
+ if (TREE_CODE (op) == TARGET_EXPR)
+ {
+ tree t = TREE_OPERAND (op, 1);
+ t = build2_loc (kw, CO_YIELD_EXPR, TREE_TYPE (t), expr, t);
+ TREE_OPERAND (op, 1) = t;
+ }
+ else
+ op = build2_loc (kw, CO_YIELD_EXPR, TREE_TYPE (op), expr, op);
TREE_SIDE_EFFECTS (op) = 1;
+ op = convert_from_reference (op);
}
return op;
}
-/* Check that it's valid to have a co_return keyword here.
+/* Check and build a co_return statememt.
+ First that it's valid to have a co_return keyword here.
If it is, then check and build the p.return_{void(),value(expr)}.
- These are built against the promise proxy, but saved for expand time. */
+ These are built against a proxy for the promise, which will be filled
+ in with the actual frame version when the function is transformed. */
tree
finish_co_return_stmt (location_t kw, tree expr)
{
- if (expr == error_mark_node)
+ if (expr)
+ STRIP_ANY_LOCATION_WRAPPER (expr);
+
+ if (error_operand_p (expr))
return error_mark_node;
+ /* If it fails the following test, the function is not permitted to be a
+ coroutine, so the co_return statement is erroneous. */
if (!coro_common_keyword_context_valid_p (current_function_decl, kw,
"co_return"))
return error_mark_node;
already. */
DECL_COROUTINE_P (current_function_decl) = 1;
- if (processing_template_decl)
- {
- current_function_returns_value = 1;
+ /* This function will appear to have no return statement, even if it
+ is declared to return non-void (most likely). This is correct - we
+ synthesize the return for the ramp in the compiler. So suppress any
+ extraneous warnings during substitution. */
+ TREE_NO_WARNING (current_function_decl) = true;
- if (check_for_bare_parameter_packs (expr))
- return error_mark_node;
+ if (processing_template_decl
+ && check_for_bare_parameter_packs (expr))
+ return error_mark_node;
- tree functype = TREE_TYPE (current_function_decl);
- /* If we don't know the promise type, we can't proceed, return the
- expression as it is. */
- if (dependent_type_p (functype) || type_dependent_expression_p (expr))
- {
- expr
- = build2_loc (kw, CO_RETURN_EXPR, void_type_node, expr, NULL_TREE);
- expr = maybe_cleanup_point_expr_void (expr);
- expr = add_stmt (expr);
- return expr;
- }
+ /* If we don't know the promise type, we can't proceed, build the
+ co_return with the expression unchanged. */
+ tree functype = TREE_TYPE (current_function_decl);
+ if (dependent_type_p (functype) || type_dependent_expression_p (expr))
+ {
+ /* co_return expressions are always void type, regardless of the
+ expression type. */
+ expr = build2_loc (kw, CO_RETURN_EXPR, void_type_node,
+ expr, NULL_TREE);
+ expr = maybe_cleanup_point_expr_void (expr);
+ return add_stmt (expr);
}
if (!coro_promise_type_found_p (current_function_decl, kw))
return error_mark_node;
- if (error_operand_p (expr))
- return error_mark_node;
-
/* Suppress -Wreturn-type for co_return, we need to check indirectly
whether the promise type has a suitable return_void/return_value. */
TREE_NO_WARNING (current_function_decl) = true;
if (!processing_template_decl && warn_sequence_point)
verify_sequence_points (expr);
+ if (expr)
+ {
+ /* If we had an id-expression obfuscated by force_paren_expr, we need
+ to undo it so we can try to treat it as an rvalue below. */
+ expr = maybe_undo_parenthesized_ref (expr);
+
+ if (processing_template_decl)
+ expr = build_non_dependent_expr (expr);
+
+ if (error_operand_p (expr))
+ return error_mark_node;
+ }
+
/* If the promise object doesn't have the correct return call then
there's a mis-match between the co_return <expr> and this. */
- tree co_ret_call = NULL_TREE;
+ tree co_ret_call = error_mark_node;
if (expr == NULL_TREE || VOID_TYPE_P (TREE_TYPE (expr)))
{
tree crv_meth
= lookup_promise_method (current_function_decl,
coro_return_void_identifier, kw,
/*musthave=*/true);
- if (!crv_meth || crv_meth == error_mark_node)
+ if (crv_meth == error_mark_node)
return error_mark_node;
co_ret_call = build_new_method_call (
= lookup_promise_method (current_function_decl,
coro_return_value_identifier, kw,
/*musthave=*/true);
- if (!crv_meth || crv_meth == error_mark_node)
+ if (crv_meth == error_mark_node)
return error_mark_node;
- vec<tree, va_gc> *args = make_tree_vector_single (expr);
- co_ret_call = build_new_method_call (
- get_coroutine_promise_proxy (current_function_decl), crv_meth, &args,
- NULL_TREE, LOOKUP_NORMAL, NULL, tf_warning_or_error);
+ /* [class.copy.elision] / 3.
+ An implicitly movable entity is a variable of automatic storage
+ duration that is either a non-volatile object or an rvalue reference
+ to a non-volatile object type. For such objects in the context of
+ the co_return, the overload resolution should be carried out first
+ treating the object as an rvalue, if that fails, then we fall back
+ to regular overload resolution. */
+
+ if (treat_lvalue_as_rvalue_p (expr, /*parm_ok*/true)
+ && CLASS_TYPE_P (TREE_TYPE (expr))
+ && !TYPE_VOLATILE (TREE_TYPE (expr)))
+ {
+ vec<tree, va_gc> *args = make_tree_vector_single (move (expr));
+ /* It's OK if this fails... */
+ co_ret_call = build_new_method_call
+ (get_coroutine_promise_proxy (current_function_decl), crv_meth,
+ &args, NULL_TREE, LOOKUP_NORMAL|LOOKUP_PREFER_RVALUE,
+ NULL, tf_none);
+ }
+
+ if (co_ret_call == error_mark_node)
+ {
+ vec<tree, va_gc> *args = make_tree_vector_single (expr);
+ /* ... but this must succeed if we didn't get the move variant. */
+ co_ret_call = build_new_method_call
+ (get_coroutine_promise_proxy (current_function_decl), crv_meth,
+ &args, NULL_TREE, LOOKUP_NORMAL, NULL, tf_warning_or_error);
+ }
}
/* Makes no sense for a co-routine really. */
"function declared %<noreturn%> has a"
" %<co_return%> statement");
- if (!co_ret_call || co_ret_call == error_mark_node)
- return error_mark_node;
-
expr = build2_loc (kw, CO_RETURN_EXPR, void_type_node, expr, co_ret_call);
expr = maybe_cleanup_point_expr_void (expr);
- expr = add_stmt (expr);
- return expr;
+ return add_stmt (expr);
}
/* We need to validate the arguments to __builtin_coro_promise, since the
&& DECL_CONTEXT (*stmt) != xform->actor_fn)
DECL_CONTEXT (*stmt) = xform->actor_fn;
- if (TREE_CODE (*stmt) != CO_AWAIT_EXPR && TREE_CODE (*stmt) != CO_YIELD_EXPR)
+ /* We should have already lowered co_yields to their co_await. */
+ gcc_checking_assert (TREE_CODE (*stmt) != CO_YIELD_EXPR);
+ if (TREE_CODE (*stmt) != CO_AWAIT_EXPR)
return NULL_TREE;
tree await_expr = *stmt;
tree frame_type; /* The type used to represent this parm in the frame. */
tree orig_type; /* The original type of the parm (not as passed). */
bool by_ref; /* Was passed by reference. */
- bool rv_ref; /* Was an rvalue reference. */
bool pt_ref; /* Was a pointer to object. */
bool trivial_dtor; /* The frame type has a trivial DTOR. */
bool this_ptr; /* Is 'this' */
+ bool lambda_cobj; /* Lambda capture object */
};
struct local_var_info
/* Re-write the variable's context to be in the actor func. */
DECL_CONTEXT (lvar) = lvd->context;
- /* we need to walk some of the decl trees, which might contain
- references to vars replaced at a higher level. */
- cp_walk_tree (&DECL_INITIAL (lvar), transform_local_var_uses, d,
- NULL);
- cp_walk_tree (&DECL_SIZE (lvar), transform_local_var_uses, d, NULL);
- cp_walk_tree (&DECL_SIZE_UNIT (lvar), transform_local_var_uses, d,
- NULL);
-
/* For capture proxies, this could include the decl value expr. */
if (local_var.is_lambda_capture || local_var.has_value_expr_p)
{
lvd->actor_frame, fld_ref, NULL_TREE);
local_var.field_idx = fld_idx;
}
+ /* FIXME: we should be able to do this in the loop above, but (at least
+ for range for) there are cases where the DECL_INITIAL contains
+ forward references.
+ So, now we've built the revised var in the frame, substitute uses of
+ it in initializers and the bind expr body. */
+ for (lvar = BIND_EXPR_VARS (*stmt); lvar != NULL;
+ lvar = DECL_CHAIN (lvar))
+ {
+ /* we need to walk some of the decl trees, which might contain
+ references to vars replaced at a higher level. */
+ cp_walk_tree (&DECL_INITIAL (lvar), transform_local_var_uses, d,
+ NULL);
+ cp_walk_tree (&DECL_SIZE (lvar), transform_local_var_uses, d, NULL);
+ cp_walk_tree (&DECL_SIZE_UNIT (lvar), transform_local_var_uses, d,
+ NULL);
+ }
cp_walk_tree (&BIND_EXPR_BODY (*stmt), transform_local_var_uses, d, NULL);
/* Now we have processed and removed references to the original vars,
current_stmt_tree ()->stmts_are_full_exprs_p = 1;
tree stmt = begin_compound_stmt (BCS_FN_BODY);
- /* ??? Can we dispense with the enclosing bind if the function body does
- not start with a bind_expr? (i.e. there's no contained scopes). */
tree actor_bind = build3 (BIND_EXPR, void_type_node, NULL, NULL, NULL);
tree top_block = make_node (BLOCK);
BIND_EXPR_BLOCK (actor_bind) = top_block;
if (parm.pt_ref)
fld_idx = build1_loc (loc, CONVERT_EXPR, TREE_TYPE (arg), fld_idx);
- /* We expect an rvalue ref. here. */
- if (parm.rv_ref)
- fld_idx = convert_to_reference (DECL_ARG_TYPE (arg), fld_idx,
- CONV_STATIC, LOOKUP_NORMAL,
- NULL_TREE, tf_warning_or_error);
-
int i;
tree *puse;
FOR_EACH_VEC_ELT (*parm.body_uses, i, puse)
/* Expand co_returns in the saved function body */
fnbody = expand_co_returns (&fnbody, promise_proxy, ap, fs_label);
- /* n4849 adds specific behaviour to treat exceptions thrown by the
- await_resume () of the initial suspend expression. In order to
- implement this, we need to treat the initial_suspend expression
- as if it were part of the user-authored function body. This
- only applies if exceptions are enabled. */
+ /* Specific behaviour to treat exceptions thrown by the await_resume ()
+ of the initial suspend expression. In order to implement this, we
+ need to treat the initial_suspend expression as if it were part of the
+ user-authored function body. This only applies if exceptions are
+ enabled. */
if (flag_exceptions)
{
tree outer = fnbody;
}
}
- /* n4849 [dcl.fct.def.coroutine] / 12
+ /* [dcl.fct.def.coroutine] / 12
The deallocation function’s name is looked up in the scope of the promise
type. If this lookup fails, the deallocation function’s name is looked up
in the global scope. If deallocation function lookup finds both a usual
continue_label, continuation, 2};
cp_walk_tree (&actor_body, await_statement_expander, &data, NULL);
- actor_body = pop_stmt_list (actor_body);
- BIND_EXPR_BODY (actor_bind) = actor_body;
+ BIND_EXPR_BODY (actor_bind) = pop_stmt_list (actor_body);
+ TREE_SIDE_EFFECTS (actor_bind) = true;
finish_compound_stmt (stmt);
DECL_SAVED_TREE (actor) = pop_stmt_list (actor_outer);
static tree
captures_temporary (tree *stmt, int *do_subtree, void *d)
{
+ /* We should have already lowered co_yields to their co_await. */
+ gcc_checking_assert (TREE_CODE (*stmt) != CO_YIELD_EXPR);
+
/* Stop recursing if we see an await expression, the subtrees
of that will be handled when it is processed. */
- if (TREE_CODE (*stmt) == CO_AWAIT_EXPR || TREE_CODE (*stmt) == CO_YIELD_EXPR)
+ if (TREE_CODE (*stmt) == CO_AWAIT_EXPR)
{
*do_subtree = 0;
return NULL_TREE;
}
/* This isn't a temporary. */
- if ((TREE_CODE (parm) == VAR_DECL && !DECL_ARTIFICIAL (parm))
+ if ((VAR_P (parm)
+ && (!DECL_ARTIFICIAL (parm) || DECL_HAS_VALUE_EXPR_P (parm)))
|| TREE_CODE (parm) == PARM_DECL
|| TREE_CODE (parm) == NON_LVALUE_EXPR)
continue;
{
susp_frame_data *data = (susp_frame_data *) d;
- if (TREE_CODE (*stmt) != CO_AWAIT_EXPR && TREE_CODE (*stmt) != CO_YIELD_EXPR)
+ /* We should have already lowered co_yields to their co_await. */
+ gcc_checking_assert (TREE_CODE (*stmt) != CO_YIELD_EXPR);
+
+ if (TREE_CODE (*stmt) != CO_AWAIT_EXPR)
return NULL_TREE;
tree aw_expr = *stmt;
location_t aw_loc = EXPR_LOCATION (aw_expr); /* location of the co_xxxx. */
- /* co_yield is syntactic sugar, re-write it to co_await. */
- if (TREE_CODE (aw_expr) == CO_YIELD_EXPR)
- {
- aw_expr = TREE_OPERAND (aw_expr, 1);
- *stmt = aw_expr;
- }
/* If the awaitable is a parm or a local variable, then we already have
a frame copy, so don't make a new one. */
if (INDIRECT_REF_P (aw))
aw = TREE_OPERAND (aw, 0);
if (TREE_CODE (aw) == PARM_DECL
- || (TREE_CODE (aw) == VAR_DECL && !DECL_ARTIFICIAL (aw)))
+ || (VAR_P (aw) && (!DECL_ARTIFICIAL (aw)
+ || DECL_HAS_VALUE_EXPR_P (aw))))
; /* Don't make an additional copy. */
else
{
free (nam);
}
+ tree o = TREE_OPERAND (aw_expr, 2); /* Initialiser for the frame var. */
+ /* If this is a target expression, then we need to remake it to strip off
+ any extra cleanups added. */
+ if (TREE_CODE (o) == TARGET_EXPR)
+ TREE_OPERAND (aw_expr, 2) = get_target_expr (TREE_OPERAND (o, 1));
+
+ tree v = TREE_OPERAND (aw_expr, 3);
+ o = TREE_VEC_ELT (v, 1);
+ if (TREE_CODE (o) == TARGET_EXPR)
+ TREE_VEC_ELT (v, 1) = get_target_expr (TREE_OPERAND (o, 1));
+
register_await_info (aw_expr, aw_field_type, aw_field_nam);
/* Count how many awaits the current expression contains. */
}
}
BIND_EXPR_BLOCK (aw_bind) = b_block;
+ TREE_SIDE_EFFECTS (aw_bind) = TREE_SIDE_EFFECTS (BIND_EXPR_BODY (aw_bind));
*stmt = aw_bind;
}
{
gcc_checking_assert (orig && TREE_CODE (orig) == FUNCTION_DECL);
+ *resumer = error_mark_node;
+ *destroyer = error_mark_node;
if (!coro_function_valid_p (orig))
- return false;
-
- /* The ramp function does return a value. */
- current_function_returns_value = 1;
+ {
+ /* For early errors, we do not want a diagnostic about the missing
+ ramp return value, since the user cannot fix this - a 'return' is
+ not allowed in a coroutine. */
+ TREE_NO_WARNING (orig) = true;
+ /* Discard the body, we can't process it further. */
+ pop_stmt_list (DECL_SAVED_TREE (orig));
+ DECL_SAVED_TREE (orig) = push_stmt_list ();
+ return false;
+ }
/* We can't validly get here with an empty statement list, since there's no
way for the FE to decide it's a coroutine in the absence of any code. */
tree fnbody = pop_stmt_list (DECL_SAVED_TREE (orig));
- if (fnbody == NULL_TREE)
- return false;
+ gcc_checking_assert (fnbody != NULL_TREE);
/* 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).
if (body_start == NULL_TREE || body_start == error_mark_node)
{
DECL_SAVED_TREE (orig) = push_stmt_list ();
- append_to_statement_list (DECL_SAVED_TREE (orig), &fnbody);
+ append_to_statement_list (fnbody, &DECL_SAVED_TREE (orig));
+ /* Suppress warnings about the missing return value. */
+ TREE_NO_WARNING (orig) = true;
return false;
}
{
tree tlist = NULL_TREE;
append_to_statement_list_force (fnbody, &tlist);
+ TREE_SIDE_EFFECTS (tlist) = TREE_SIDE_EFFECTS (fnbody);
BIND_EXPR_BODY (update_body) = tlist;
}
tree new_body_list = NULL_TREE;
- append_to_statement_list_force (update_body, &new_body_list);
+ TREE_SIDE_EFFECTS (update_body) = true;
+ append_to_statement_list (update_body, &new_body_list);
+ TREE_SIDE_EFFECTS (new_body_list) = true;
fnbody = new_body_list;
}
1. Types we already know. */
tree fn_return_type = TREE_TYPE (TREE_TYPE (orig));
- gcc_assert (!VOID_TYPE_P (fn_return_type));
tree handle_type = get_coroutine_handle_type (orig);
tree promise_type = get_coroutine_promise_type (orig);
the coroutine promise. */
tree initial_await = build_init_or_final_await (fn_start, false);
if (initial_await == error_mark_node)
- return false;
+ {
+ /* Suppress warnings about the missing return value. */
+ TREE_NO_WARNING (orig) = true;
+ return false;
+ }
/* The type of the frame var for this is the type of its temp proxy. */
tree initial_suspend_type = TREE_TYPE (TREE_OPERAND (initial_await, 1));
tree final_await = build_init_or_final_await (fn_start, true);
if (final_await == error_mark_node)
- return false;
+ {
+ /* Suppress warnings about the missing return value. */
+ TREE_NO_WARNING (orig) = true;
+ return false;
+ }
/* The type of the frame var for this is the type of its temp proxy. */
tree final_suspend_type = TREE_TYPE (TREE_OPERAND (final_await, 1));
The second two entries start out empty - and only get populated
when we see uses. */
param_uses = new hash_map<tree, param_info>;
+ bool lambda_p = LAMBDA_FUNCTION_P (orig);
unsigned no_name_parm = 0;
for (tree arg = DECL_ARGUMENTS (orig); arg != NULL;
if (actual_type == NULL_TREE)
actual_type = error_mark_node;
parm.orig_type = actual_type;
- parm.by_ref = parm.rv_ref = parm.pt_ref = false;
- if (TREE_CODE (actual_type) == REFERENCE_TYPE
- && TYPE_REF_IS_RVALUE (DECL_ARG_TYPE (arg)))
- {
- parm.rv_ref = true;
- actual_type = TREE_TYPE (actual_type);
- parm.frame_type = actual_type;
- }
- else if (TREE_CODE (actual_type) == REFERENCE_TYPE)
+ parm.by_ref = parm.pt_ref = false;
+ if (TREE_CODE (actual_type) == REFERENCE_TYPE)
{
/* If the user passes by reference, then we will save the
pointer to the original. As noted in
referenced item ends and then the coroutine is resumed,
we have UB; well, the user asked for it. */
actual_type = build_pointer_type (TREE_TYPE (actual_type));
- parm.frame_type = actual_type;
parm.pt_ref = true;
}
else if (TYPE_REF_P (DECL_ARG_TYPE (arg)))
+ parm.by_ref = true;
+
+ parm.frame_type = actual_type;
+
+ parm.this_ptr = is_this_parameter (arg);
+ /* See PR94807. When a lambda is in a template instantiation, the
+ closure object is named 'this' instead of '__closure'. */
+ if (lambda_p)
{
- parm.by_ref = true;
- parm.frame_type = actual_type;
+ parm.lambda_cobj = DECL_NAME (arg) == closure_identifier;
+ gcc_checking_assert (!parm.this_ptr);
}
else
- parm.frame_type = actual_type;
- parm.this_ptr = is_this_parameter (arg);
+ parm.lambda_cobj = false;
+
parm.trivial_dtor = TYPE_HAS_TRIVIAL_DESTRUCTOR (parm.frame_type);
char *buf;
if (DECL_NAME (arg))
tree ramp_bind = build3 (BIND_EXPR, void_type_node, NULL, NULL, NULL);
add_stmt (ramp_bind);
tree ramp_body = push_stmt_list ();
- tree empty_list = build_empty_stmt (fn_start);
tree coro_fp = build_lang_decl (VAR_DECL, get_identifier ("coro.frameptr"),
coro_frame_ptr);
/* The decl_expr for the coro frame pointer, initialize to zero so that we
can pass it to the IFN_CO_FRAME (since there's no way to pass a type,
directly apparently). This avoids a "used uninitialized" warning. */
- tree r = build_stmt (fn_start, DECL_EXPR, coro_fp);
tree zeroinit = build1 (CONVERT_EXPR, coro_frame_ptr, integer_zero_node);
- r = build2 (INIT_EXPR, TREE_TYPE (coro_fp), coro_fp, zeroinit);
- r = coro_build_cvt_void_expr_stmt (r, fn_start);
- add_stmt (r);
+ DECL_INITIAL (coro_fp) = zeroinit;
+ add_decl_expr (coro_fp);
/* The CO_FRAME internal function is a mechanism to allow the middle end
to adjust the allocation in response to optimisations. We provide the
= build_call_expr_internal_loc (fn_start, IFN_CO_FRAME, size_type_node, 2,
frame_size, coro_fp);
- /* n4849 [dcl.fct.def.coroutine] / 10 (part1)
+ /* [dcl.fct.def.coroutine] / 10 (part1)
The unqualified-id get_return_object_on_allocation_failure is looked up
in the scope of the promise type by class member access lookup. */
}
/* Allocate the frame, this has several possibilities:
- n4849 [dcl.fct.def.coroutine] / 9 (part 1)
+ [dcl.fct.def.coroutine] / 9 (part 1)
The allocation function’s name is looked up in the scope of the promise
type. It's not a failure for it to be absent see part 4, below. */
tree nwname = ovl_op_identifier (false, NEW_EXPR);
tree new_fn = NULL_TREE;
if (fns && BASELINK_P (fns))
{
- /* n4849 [dcl.fct.def.coroutine] / 9 (part 2)
+ /* [dcl.fct.def.coroutine] / 9 (part 2)
If the lookup finds an allocation function in the scope of the promise
type, overload resolution is performed on a function call created by
assembling an argument list. The first argument is the amount of space
those of the original function. */
vec<tree, va_gc> *args = make_tree_vector ();
vec_safe_push (args, resizeable); /* Space needed. */
+
for (tree arg = DECL_ARGUMENTS (orig); arg != NULL;
arg = DECL_CHAIN (arg))
- vec_safe_push (args, arg);
+ {
+ param_info *parm_i = param_uses->get (arg);
+ gcc_checking_assert (parm_i);
+ if (parm_i->lambda_cobj)
+ vec_safe_push (args, arg);
+ else if (parm_i->this_ptr)
+ {
+ /* We pass a reference to *this to the allocator lookup. */
+ tree tt = TREE_TYPE (TREE_TYPE (arg));
+ tree this_ref = build1 (INDIRECT_REF, tt, arg);
+ tt = cp_build_reference_type (tt, false);
+ this_ref = convert_to_reference (tt, this_ref, CONV_STATIC,
+ LOOKUP_NORMAL , NULL_TREE,
+ tf_warning_or_error);
+ vec_safe_push (args, this_ref);
+ }
+ else
+ vec_safe_push (args, arg);
+ }
/* We might need to check that the provided function is nothrow. */
tree func;
if (!new_fn || new_fn == error_mark_node)
{
- /* n4849 [dcl.fct.def.coroutine] / 9 (part 3)
+ /* [dcl.fct.def.coroutine] / 9 (part 3)
If no viable function is found, overload resolution is performed
again on a function call created by passing just the amount of
space required as an argument of type std::size_t. */
}
else
{
- /* n4849 [dcl.fct.def.coroutine] / 9 (part 4)
+ /* [dcl.fct.def.coroutine] / 9 (part 4)
If this lookup fails, the allocation function’s name is looked up in
the global scope. */
if (grooaf)
{
- /* n4849 [dcl.fct.def.coroutine] / 10 (part 2)
+ /* [dcl.fct.def.coroutine] / 10 (part 2)
If any declarations (of the get return on allocation fail) are
found, then the result of a call to an allocation function used
to obtain storage for the coroutine state is assumed to return
}
tree allocated = build1 (CONVERT_EXPR, coro_frame_ptr, new_fn);
- r = build2 (INIT_EXPR, TREE_TYPE (coro_fp), coro_fp, allocated);
+ tree r = build2 (INIT_EXPR, TREE_TYPE (coro_fp), coro_fp, allocated);
r = coro_build_cvt_void_expr_stmt (r, fn_start);
add_stmt (r);
if (grooaf)
{
- /* n4849 [dcl.fct.def.coroutine] / 10 (part 3)
+ /* [dcl.fct.def.coroutine] / 10 (part 3)
If the allocation function returns nullptr,the coroutine returns
control to the caller of the coroutine and the return value is
obtained by a call to T::get_return_object_on_allocation_failure(),
where T is the promise type. */
- tree cfra_label
- = create_named_label_with_ctx (fn_start, "coro.frame.active",
- current_scope ());
- tree early_ret_list = NULL;
- /* init the retval using the user's func. */
- r = build2 (INIT_EXPR, TREE_TYPE (DECL_RESULT (orig)), DECL_RESULT (orig),
- grooaf);
- r = coro_build_cvt_void_expr_stmt (r, fn_start);
- append_to_statement_list (r, &early_ret_list);
- /* We know it's the correct type. */
- r = DECL_RESULT (orig);
- r = build_stmt (fn_start, RETURN_EXPR, r);
- TREE_NO_WARNING (r) |= 1;
- r = maybe_cleanup_point_expr_void (r);
- append_to_statement_list (r, &early_ret_list);
-
- tree goto_st = NULL;
- r = build1 (GOTO_EXPR, void_type_node, cfra_label);
- append_to_statement_list (r, &goto_st);
-
- tree ckk = build1 (CONVERT_EXPR, coro_frame_ptr, integer_zero_node);
- tree ckz = build2 (EQ_EXPR, boolean_type_node, coro_fp, ckk);
- r = build3 (COND_EXPR, void_type_node, ckz, early_ret_list, empty_list);
- add_stmt (r);
- cfra_label = build_stmt (fn_start, LABEL_EXPR, cfra_label);
- add_stmt (cfra_label);
+ gcc_checking_assert (same_type_p (fn_return_type, TREE_TYPE (grooaf)));
+ tree if_stmt = begin_if_stmt ();
+ tree cond = build1 (CONVERT_EXPR, coro_frame_ptr, integer_zero_node);
+ cond = build2 (EQ_EXPR, boolean_type_node, coro_fp, cond);
+ finish_if_stmt_cond (cond, if_stmt);
+ if (VOID_TYPE_P (fn_return_type))
+ {
+ /* Execute the get-return-object-on-alloc-fail call... */
+ finish_expr_stmt (grooaf);
+ /* ... but discard the result, since we return void. */
+ finish_return_stmt (NULL_TREE);
+ }
+ else
+ {
+ /* Get the fallback return object. */
+ r = build_cplus_new (fn_return_type, grooaf, tf_warning_or_error);
+ finish_return_stmt (r);
+ }
+ finish_then_clause (if_stmt);
+ finish_if_stmt (if_stmt);
}
/* deref the frame pointer, to use in member access code. */
r = coro_build_cvt_void_expr_stmt (r, fn_start);
add_stmt (r);
- /* n4849 [dcl.fct.def.coroutine] /13
+ /* [dcl.fct.def.coroutine] /13
When a coroutine is invoked, a copy is created for each coroutine
parameter. Each such copy is an object with automatic storage duration
that is direct-initialized from an lvalue referring to the corresponding
false, tf_warning_or_error);
/* Add this to the promise CTOR arguments list, accounting for
- refs and this ptr. */
- if (parm.this_ptr)
+ refs and special handling for method this ptr. */
+ if (parm.lambda_cobj)
+ vec_safe_push (promise_args, arg);
+ else if (parm.this_ptr)
{
/* We pass a reference to *this to the param preview. */
tree tt = TREE_TYPE (arg);
}
else if (parm.by_ref)
vec_safe_push (promise_args, fld_idx);
- else if (parm.rv_ref)
- vec_safe_push (promise_args, rvalue (fld_idx));
else
vec_safe_push (promise_args, arg);
if (TYPE_NEEDS_CONSTRUCTING (parm.frame_type))
{
vec<tree, va_gc> *p_in;
- if (parm.by_ref
- && classtype_has_non_deleted_move_ctor (parm.frame_type)
- && !classtype_has_non_deleted_copy_ctor (parm.frame_type))
+ if (CLASS_TYPE_P (parm.frame_type)
+ && classtype_has_non_deleted_move_ctor (parm.frame_type))
+ p_in = make_tree_vector_single (move (arg));
+ else if (lvalue_p (arg))
p_in = make_tree_vector_single (rvalue (arg));
else
p_in = make_tree_vector_single (arg);
}
else
{
- if (parm.rv_ref)
- r = convert_from_reference (arg);
- else if (!same_type_p (parm.frame_type, DECL_ARG_TYPE (arg)))
+ if (!same_type_p (parm.frame_type, DECL_ARG_TYPE (arg)))
r = build1_loc (DECL_SOURCE_LOCATION (arg), CONVERT_EXPR,
parm.frame_type, arg);
else
{
BIND_EXPR_BODY (ramp_bind) = pop_stmt_list (ramp_body);
DECL_SAVED_TREE (orig) = newbody;
+ /* Suppress warnings about the missing return value. */
+ TREE_NO_WARNING (orig) = true;
return false;
}
tree gro_context_body = push_stmt_list ();
- tree gro = build_lang_decl (VAR_DECL, get_identifier ("coro.gro"),
- TREE_TYPE (get_ro));
- DECL_CONTEXT (gro) = current_scope ();
- add_decl_expr (gro);
- tree gro_bind_vars = gro;
+ bool gro_is_void_p = VOID_TYPE_P (TREE_TYPE (get_ro));
+ tree gro = NULL_TREE;
+ tree gro_bind_vars = NULL_TREE;
/* We have to sequence the call to get_return_object before initial
suspend. */
- r = build2_loc (fn_start, INIT_EXPR, TREE_TYPE (gro), gro, get_ro);
- r = coro_build_cvt_void_expr_stmt (r, fn_start);
- add_stmt (r);
+ if (gro_is_void_p)
+ finish_expr_stmt (get_ro);
+ else
+ {
+ gro = build_lang_decl (VAR_DECL, get_identifier ("coro.gro"),
+ TREE_TYPE (get_ro));
+ DECL_CONTEXT (gro) = current_scope ();
+ DECL_ARTIFICIAL (gro) = true;
+ DECL_IGNORED_P (gro) = true;
+ add_decl_expr (gro);
+ gro_bind_vars = gro;
+
+ r = build2_loc (fn_start, INIT_EXPR, TREE_TYPE (gro), gro, get_ro);
+ r = coro_build_cvt_void_expr_stmt (r, fn_start);
+ add_stmt (r);
+ }
/* Initialize the resume_idx_name to 0, meaning "not started". */
tree resume_idx_m
/* The ramp is done, we just need the return value. */
if (!same_type_p (TREE_TYPE (get_ro), fn_return_type))
{
- /* construct the return value with a single GRO param. */
- vec<tree, va_gc> *args = make_tree_vector_single (gro);
+ /* construct the return value with a single GRO param, if it's not
+ void. */
+ vec<tree, va_gc> *args = NULL;
+ vec<tree, va_gc> **arglist = NULL;
+ if (!gro_is_void_p)
+ {
+ args = make_tree_vector_single (gro);
+ arglist = &args;
+ }
r = build_special_member_call (NULL_TREE,
- complete_ctor_identifier, &args,
+ complete_ctor_identifier, arglist,
fn_return_type, LOOKUP_NORMAL,
tf_warning_or_error);
r = build_cplus_new (fn_return_type, r, tf_warning_or_error);
}
- else
+ else if (!gro_is_void_p)
r = rvalue (gro); /* The GRO is the return value. */
+ else
+ r = NULL_TREE;
finish_return_stmt (r);
/* Finish up the ramp function. */
BIND_EXPR_VARS (gro_context_bind) = gro_bind_vars;
BIND_EXPR_BODY (gro_context_bind) = pop_stmt_list (gro_context_body);
+ TREE_SIDE_EFFECTS (gro_context_bind) = true;
BIND_EXPR_BODY (ramp_bind) = pop_stmt_list (ramp_body);
+ TREE_SIDE_EFFECTS (ramp_bind) = true;
/* We know the "real" promise and have a frame layout with a slot for each
suspend point, so we can build an actor function (which contains the
BLOCK_SUPERCONTEXT (replace_blk) = tcb_block;
BLOCK_SUBBLOCKS (tcb_block) = replace_blk;
BIND_EXPR_BLOCK (fnbody) = tcb_block;
+ TREE_SIDE_EFFECTS (fnbody) = true;
}
}
else if (pedantic)