From 445d8da5fbd10e32f8ea470bd9ac02faba8fd718 Mon Sep 17 00:00:00 2001 From: Iain Sandoe Date: Sat, 20 Jun 2020 14:01:21 +0100 Subject: [PATCH] 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. --- gcc/cp/coroutines.cc | 51 +++++++++++++++---------------- gcc/testsuite/g++.dg/coroutines/pr95505.C | 26 ++++++++++++++++ 2 files changed, 51 insertions(+), 26 deletions(-) create mode 100644 gcc/testsuite/g++.dg/coroutines/pr95505.C diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc index 11fca99..3aaa3d4 100644 --- a/gcc/cp/coroutines.cc +++ b/gcc/cp/coroutines.cc @@ -3913,30 +3913,25 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer) tree grooaf = NULL_TREE; 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)) - { - /* ... but, if the lookup succeeds, then the function must be - usable. - build_new_method_call () wants a valid pointer to (an empty) args - list in this case. */ - vec *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); - } + grooaf = build_new_method_call (dummy_promise, grooaf_meth, NULL, + NULL_TREE, LOOKUP_NORMAL, NULL, + tf_warning_or_error); /* Allocate the frame, this has several possibilities: [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 fns = lookup_promise_method (orig, nwname, fn_start, - /*musthave=*/false); tree new_fn = NULL_TREE; - if (fns && BASELINK_P (fns)) + + if (TYPE_HAS_NEW_OPERATOR (promise_type)) { + 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 type, overload resolution is performed on a function call created by @@ -3966,30 +3961,29 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer) 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; - /* 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, LOOKUP_NORMAL, &func, tf_none); release_tree_vector (args); - if (!new_fn || new_fn == error_mark_node) + if (new_fn == error_mark_node) { /* [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. */ - args = make_tree_vector (); - vec_safe_push (args, resizeable); /* Space needed. */ + args = make_tree_vector_single (resizeable); /* Space needed. */ new_fn = build_new_method_call (dummy_promise, fns, &args, NULL_TREE, LOOKUP_NORMAL, &func, tf_none); release_tree_vector (args); } - /* However, if the initial lookup succeeded, then one of these two - options must be available. */ - if (!new_fn || new_fn == error_mark_node) + /* However, if the promise provides an operator new, then one of these + two options must be available. */ + if (new_fn == error_mark_node) { error_at (fn_start, "%qE is provided by %qT but is not usable with" " the function signature %qD", nwname, promise_type, orig); @@ -3999,7 +3993,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" " % or %", grooaf, promise_type, nwname); } - else + else /* No operator new in the promise. */ { /* [dcl.fct.def.coroutine] / 9 (part 4) If this lookup fails, the allocation function’s name is looked up in @@ -4009,7 +4003,6 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer) /* build_operator_new_call () will insert size needed as element 0 of this, and we might need to append the std::nothrow constant. */ vec_alloc (args, 2); - if (grooaf) { /* [dcl.fct.def.coroutine] / 10 (part 2) @@ -4023,6 +4016,9 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer) tree std_nt = lookup_qualified_name (std_node, get_identifier ("nothrow"), 0, /*complain=*/true, false); + if (!std_nt || std_nt == error_mark_node) + error_at (fn_start, "%qE is provided by %qT but % " + "cannot be found", grooaf, promise_type); vec_safe_push (args, std_nt); } @@ -4037,7 +4033,10 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer) tf_warning_or_error); resizeable = build_call_expr_internal_loc (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); } diff --git a/gcc/testsuite/g++.dg/coroutines/pr95505.C b/gcc/testsuite/g++.dg/coroutines/pr95505.C new file mode 100644 index 0000000..a76b827 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/pr95505.C @@ -0,0 +1,26 @@ +#if __has_include () +#include +using namespace std; +#elif defined (__clang__) && __has_include () +#include +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; +} + -- 2.7.4