coroutines: Emit error for invalid promise return types [PR97438].

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.

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.
This commit is contained in:
Iain Sandoe 2020-10-17 11:21:11 +01:00
parent 619f91eaa8
commit b003c4b14b
2 changed files with 55 additions and 0 deletions

View file

@ -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);

View file

@ -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;
}