Commit a59a15bc by Iain Sandoe

coroutines: Update handling and failure for g-r-o-o-a-f [PR95505]

The actual issue is that (in the testcase) std::nothrow is not
available.  So update the handling of the get-return-on-alloc-fail
to include the possibility that std::nothrow might not be
available.

gcc/cp/ChangeLog:

	PR c++/95505
	* coroutines.cc (morph_fn_to_coro): Update handling of
	get-return-object-on-allocation-fail and diagnose missing
	std::nothrow.

gcc/testsuite/ChangeLog:

	PR c++/95505
	* g++.dg/coroutines/pr95505.C: New test.

(cherry picked from commit 445d8da5fbd10e32f8ea470bd9ac02faba8fd718)
parent e80ee62b
...@@ -3924,31 +3924,26 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer) ...@@ -3924,31 +3924,26 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
tree grooaf = NULL_TREE; tree grooaf = NULL_TREE;
tree dummy_promise = build_dummy_object (get_coroutine_promise_type (orig)); tree dummy_promise = build_dummy_object (get_coroutine_promise_type (orig));
/* We don't require this, so lookup_promise_method can return NULL... */ /* We don't require this, so lookup_promise_method can return NULL,
but, if the lookup succeeds, then the function must be usable. */
if (grooaf_meth && BASELINK_P (grooaf_meth)) if (grooaf_meth && BASELINK_P (grooaf_meth))
{ grooaf = build_new_method_call (dummy_promise, grooaf_meth, NULL,
/* ... but, if the lookup succeeds, then the function must be NULL_TREE, LOOKUP_NORMAL, NULL,
usable. tf_warning_or_error);
build_new_method_call () wants a valid pointer to (an empty) args
list in this case. */
vec<tree, va_gc> *args = make_tree_vector ();
grooaf = build_new_method_call (dummy_promise, grooaf_meth, &args,
NULL_TREE, LOOKUP_NORMAL, NULL,
tf_warning_or_error);
release_tree_vector (args);
}
/* Allocate the frame, this has several possibilities: /* Allocate the frame, this has several possibilities:
n4849 [dcl.fct.def.coroutine] / 9 (part 1) n4849 [dcl.fct.def.coroutine] / 9 (part 1)
The allocation function’s name is looked up in the scope of the promise 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. */ type. It's not a failure for it to be absent see part 4, below. */
tree nwname = ovl_op_identifier (false, NEW_EXPR); tree nwname = ovl_op_identifier (false, NEW_EXPR);
tree fns = lookup_promise_method (orig, nwname, fn_start,
/*musthave=*/false);
tree new_fn = NULL_TREE; tree new_fn = NULL_TREE;
if (fns && BASELINK_P (fns))
if (TYPE_HAS_NEW_OPERATOR (promise_type))
{ {
/* n4849 [dcl.fct.def.coroutine] / 9 (part 2) tree fns = lookup_promise_method (orig, nwname, fn_start,
/*musthave=*/true);
/* [dcl.fct.def.coroutine] / 9 (part 2)
If the lookup finds an allocation function in the scope of the promise If the lookup finds an allocation function in the scope of the promise
type, overload resolution is performed on a function call created by type, overload resolution is performed on a function call created by
assembling an argument list. The first argument is the amount of space assembling an argument list. The first argument is the amount of space
...@@ -3977,30 +3972,29 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer) ...@@ -3977,30 +3972,29 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
vec_safe_push (args, arg); vec_safe_push (args, arg);
} }
/* We might need to check that the provided function is nothrow. */ /* Note the function selected; we test to see if it's NOTHROW. */
tree func; tree func;
/* Failure is OK for the first attempt. */ /* Failure is not an error for this attempt. */
new_fn = build_new_method_call (dummy_promise, fns, &args, NULL, new_fn = build_new_method_call (dummy_promise, fns, &args, NULL,
LOOKUP_NORMAL, &func, tf_none); LOOKUP_NORMAL, &func, tf_none);
release_tree_vector (args); release_tree_vector (args);
if (!new_fn || new_fn == error_mark_node) if (new_fn == error_mark_node)
{ {
/* n4849 [dcl.fct.def.coroutine] / 9 (part 3) /* n4849 [dcl.fct.def.coroutine] / 9 (part 3)
If no viable function is found, overload resolution is performed If no viable function is found, overload resolution is performed
again on a function call created by passing just the amount of again on a function call created by passing just the amount of
space required as an argument of type std::size_t. */ space required as an argument of type std::size_t. */
args = make_tree_vector (); args = make_tree_vector_single (resizeable); /* Space needed. */
vec_safe_push (args, resizeable); /* Space needed. */
new_fn = build_new_method_call (dummy_promise, fns, &args, new_fn = build_new_method_call (dummy_promise, fns, &args,
NULL_TREE, LOOKUP_NORMAL, &func, NULL_TREE, LOOKUP_NORMAL, &func,
tf_none); tf_none);
release_tree_vector (args); release_tree_vector (args);
} }
/* However, if the initial lookup succeeded, then one of these two /* However, if the promise provides an operator new, then one of these
options must be available. */ two options must be available. */
if (!new_fn || new_fn == error_mark_node) if (new_fn == error_mark_node)
{ {
error_at (fn_start, "%qE is provided by %qT but is not usable with" error_at (fn_start, "%qE is provided by %qT but is not usable with"
" the function signature %qD", nwname, promise_type, orig); " the function signature %qD", nwname, promise_type, orig);
...@@ -4010,7 +4004,7 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer) ...@@ -4010,7 +4004,7 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
error_at (fn_start, "%qE is provided by %qT but %qE is not marked" error_at (fn_start, "%qE is provided by %qT but %qE is not marked"
" %<throw()%> or %<noexcept%>", grooaf, promise_type, nwname); " %<throw()%> or %<noexcept%>", grooaf, promise_type, nwname);
} }
else else /* No operator new in the promise. */
{ {
/* n4849 [dcl.fct.def.coroutine] / 9 (part 4) /* n4849 [dcl.fct.def.coroutine] / 9 (part 4)
If this lookup fails, the allocation function’s name is looked up in If this lookup fails, the allocation function’s name is looked up in
...@@ -4020,7 +4014,6 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer) ...@@ -4020,7 +4014,6 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
/* build_operator_new_call () will insert size needed as element 0 of /* build_operator_new_call () will insert size needed as element 0 of
this, and we might need to append the std::nothrow constant. */ this, and we might need to append the std::nothrow constant. */
vec_alloc (args, 2); vec_alloc (args, 2);
if (grooaf) if (grooaf)
{ {
/* n4849 [dcl.fct.def.coroutine] / 10 (part 2) /* n4849 [dcl.fct.def.coroutine] / 10 (part 2)
...@@ -4034,6 +4027,9 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer) ...@@ -4034,6 +4027,9 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
tree std_nt = lookup_qualified_name (std_node, tree std_nt = lookup_qualified_name (std_node,
get_identifier ("nothrow"), get_identifier ("nothrow"),
0, /*complain=*/true, false); 0, /*complain=*/true, false);
if (!std_nt || std_nt == error_mark_node)
error_at (fn_start, "%qE is provided by %qT but %<std::nothrow%> "
"cannot be found", grooaf, promise_type);
vec_safe_push (args, std_nt); vec_safe_push (args, std_nt);
} }
...@@ -4048,7 +4044,10 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer) ...@@ -4048,7 +4044,10 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
tf_warning_or_error); tf_warning_or_error);
resizeable = build_call_expr_internal_loc resizeable = build_call_expr_internal_loc
(fn_start, IFN_CO_FRAME, size_type_node, 2, frame_size, coro_fp); (fn_start, IFN_CO_FRAME, size_type_node, 2, frame_size, coro_fp);
CALL_EXPR_ARG (new_fn, 0) = resizeable; /* If the operator call fails for some reason, then don't try to
amend it. */
if (new_fn != error_mark_node)
CALL_EXPR_ARG (new_fn, 0) = resizeable;
release_tree_vector (args); release_tree_vector (args);
} }
......
#if __has_include (<coroutine>)
#include <coroutine>
using namespace std;
#elif defined (__clang__) && __has_include (<experimental/coroutine>)
#include <experimental/coroutine>
namespace std { using namespace experimental; }
#endif
struct dummy
{
struct promise_type
{
dummy get_return_object() const noexcept { return {}; }
static dummy get_return_object_on_allocation_failure() noexcept { return {}; }
std::suspend_always initial_suspend() const noexcept { return {}; }
std::suspend_never final_suspend() const noexcept { return {}; }
void return_void() const noexcept {}
void unhandled_exception() const noexcept {}
};
};
dummy foo() // { dg-error {dummy::promise_type::get_return_object_on_allocation_failure.*but 'std::nothrow' cannot be found} }
{
co_return;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment