Hi, At one stage, use cases were proposed for allowing the promise type to contain both return_value and return_void. That was not accepted into C++20; so we should reject it as per the PR.
Tested on x86_64-darwin, x86_64-linux-gnu, OK for master? (although this is technically an ‘accepts invalid’ not sure it’s serious enough to backport to 10.x - but I’m happy to do so if anyone thinks it should be). thanks Iain gcc/cp/ChangeLog: PR c++/97438 * coroutines.cc (struct coroutine_info): Add a field to record that we emitted a promise type error. (coro_promise_type_found_p): Check for the case that the promise type contains both return_void and return_value. Emit an error if so, with information about the wrong type methods. gcc/testsuite/ChangeLog: PR c++/97438 * g++.dg/coroutines/pr97438.C: New test. --- gcc/cp/coroutines.cc | 25 +++++++++++++++++++ gcc/testsuite/g++.dg/coroutines/pr97438.C | 30 +++++++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 gcc/testsuite/g++.dg/coroutines/pr97438.C diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc index ba813454a0b..9b9141e51fd 100644 --- a/gcc/cp/coroutines.cc +++ b/gcc/cp/coroutines.cc @@ -94,6 +94,7 @@ struct GTY((for_user)) coroutine_info /* Flags to avoid repeated errors for per-function issues. */ bool coro_ret_type_error_emitted; bool coro_promise_error_emitted; + bool coro_co_return_error_emitted; }; struct coroutine_info_hasher : ggc_ptr_hash<coroutine_info> @@ -470,6 +471,30 @@ coro_promise_type_found_p (tree fndecl, location_t loc) return false; } + /* Test for errors in the promise type that can be determined now. */ + tree has_ret_void = lookup_member (coro_info->promise_type, + coro_return_void_identifier, + /*protect=*/1, /*want_type=*/0, + tf_none); + tree has_ret_val = lookup_member (coro_info->promise_type, + coro_return_value_identifier, + /*protect=*/1, /*want_type=*/0, + tf_none); + if (has_ret_void && has_ret_val) + { + location_t ploc = DECL_SOURCE_LOCATION (fndecl); + if (!coro_info->coro_co_return_error_emitted) + error_at (ploc, "the coroutine promise type %qT declares both" + " %<return_value%> and %<return_void%>", + coro_info->promise_type); + inform (DECL_SOURCE_LOCATION (BASELINK_FUNCTIONS (has_ret_void)), + "%<return_void%> declared here"); + inform (DECL_SOURCE_LOCATION (BASELINK_FUNCTIONS (has_ret_val)), + "%<return_value%> declared here"); + coro_info->coro_co_return_error_emitted = true; + return false; + } + /* Try to find the handle type for the promise. */ tree handle_type = instantiate_coro_handle_for_promise_type (loc, coro_info->promise_type); diff --git a/gcc/testsuite/g++.dg/coroutines/pr97438.C b/gcc/testsuite/g++.dg/coroutines/pr97438.C new file mode 100644 index 00000000000..95376648ed7 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/pr97438.C @@ -0,0 +1,30 @@ +#if __has_include(<coroutine>) +#include <coroutine> +#else +#include <experimental/coroutine> +namespace std { using namespace experimental; } +#endif + +struct dummy_coroutine {}; + +namespace std { + +template<> +class coroutine_traits<::dummy_coroutine> { +public: + struct promise_type { + void return_value(int x) { } + void return_void() {} + std::suspend_never initial_suspend() noexcept { return {}; } + std::suspend_never final_suspend() noexcept { return {}; } + dummy_coroutine get_return_object() { return {}; } + void unhandled_exception() {} + }; +}; + +} + +dummy_coroutine +foo() { // { dg-error {the coroutine promise type 'std::__n4861::coroutine_traits<dummy_coroutine>::promise_type' declares both 'return_value' and 'return_void'} } + co_return 17; +} -- 2.24.1