From: Nathan Sidwell Date: Sun, 3 Apr 2022 10:35:03 +0000 (+0100) Subject: c++, coroutines: Avoid expanding within templates [PR103868] X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=9cb1f565a91e2dd57098c43593954b57c065a19b;p=test_jj.git c++, coroutines: Avoid expanding within templates [PR103868] This is a forward-port of a patch by Nathan (against 10.x) which fixes an open PR. We are ICEing because we ended up tsubst_copying something that had already been tsubst, leading to an assert failure (mostly such repeated tsubsting is harmless). We had a non-dependent co_await in a non-dependent-type template fn, so we processed it at definition time, and then reprocessed at instantiation time. We fix this here by deferring substitution while processing templates. Additional observations (for a better future fix, in the GCC13 timescale): Exprs only have dependent type if at least one operand is dependent which was what the current code was intending to do. Coroutines have the additional wrinkle, that the current fn's type is an implicit operand. So, if the coroutine function's type is not dependent, and the operand is not dependent, we should determine the type of the co_await expression using the DEPENDENT_EXPR wrapper machinery. That allows us to determine the subexpression type, but leave its operand unchanged and then instantiate it later. PR c++/103868 gcc/cp/ChangeLog: * coroutines.cc (finish_co_await_expr): Do not process non-dependent coroutine expressions at template definition time. (finish_co_yield_expr): Likewise. (finish_co_return_stmt): Likewise. gcc/testsuite/ChangeLog: * g++.dg/coroutines/pr103868.C: New test. Co-Authored-by: Iain Sandoe --- diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc index cdf6503..64f4b44 100644 --- a/gcc/cp/coroutines.cc +++ b/gcc/cp/coroutines.cc @@ -1148,10 +1148,13 @@ finish_co_await_expr (location_t kw, tree expr) extraneous warnings during substitution. */ suppress_warning (current_function_decl, OPT_Wreturn_type); - /* If we don't know the promise type, we can't proceed, build the - co_await with the expression unchanged. */ - tree functype = TREE_TYPE (current_function_decl); - if (dependent_type_p (functype) || type_dependent_expression_p (expr)) + /* Defer expansion when we are processing a template. + FIXME: If the coroutine function's type is not dependent, and the operand + is not dependent, we should determine the type of the co_await expression + using the DEPENDENT_EXPR wrapper machinery. That allows us to determine + the subexpression type, but leave its operand unchanged and then + instantiate it later. */ + if (processing_template_decl) { tree aw_expr = build5_loc (kw, CO_AWAIT_EXPR, unknown_type_node, expr, NULL_TREE, NULL_TREE, NULL_TREE, @@ -1222,10 +1225,9 @@ finish_co_yield_expr (location_t kw, tree expr) extraneous warnings during substitution. */ suppress_warning (current_function_decl, OPT_Wreturn_type); - /* If we don't know the promise type, we can't proceed, build the - co_await with the expression unchanged. */ - tree functype = TREE_TYPE (current_function_decl); - if (dependent_type_p (functype) || type_dependent_expression_p (expr)) + /* Defer expansion when we are processing a template; see FIXME in the + co_await code. */ + if (processing_template_decl) return build2_loc (kw, CO_YIELD_EXPR, unknown_type_node, expr, NULL_TREE); if (!coro_promise_type_found_p (current_function_decl, kw)) @@ -1307,10 +1309,9 @@ finish_co_return_stmt (location_t kw, tree expr) && check_for_bare_parameter_packs (expr)) return error_mark_node; - /* 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)) + /* Defer expansion when we are processing a template; see FIXME in the + co_await code. */ + if (processing_template_decl) { /* co_return expressions are always void type, regardless of the expression type. */ diff --git a/gcc/testsuite/g++.dg/coroutines/pr103868.C b/gcc/testsuite/g++.dg/coroutines/pr103868.C new file mode 100644 index 0000000..8687eff --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/pr103868.C @@ -0,0 +1,150 @@ +// { dg-additional-options "-fpreprocessed -std=gnu++20 -w -fconcepts" } +int _M_invoke___functor; +namespace std { +template struct integral_constant { + static constexpr bool value = __v; +}; +template struct decay { typedef int type; }; +template struct enable_if; +template struct enable_if { typedef _Tp type; }; +template +void __invoke_impl(_Fn __f, _Args... __args) { + __f(__args...); +} +template +void __invoke_r(_Callable __fn, _Args... __args) { + using __result = int; + using __type = __result; + __invoke_impl<__type>(__fn, __args...); +} +template class function; +template struct _Base_manager { + static _Functor *_M_get_pointer(int) {} +}; +template class _Function_handler; +template +struct _Function_handler<_Res(_ArgTypes...), _Functor> { + static _Res _M_invoke(_ArgTypes... __args) { + auto __trans_tmp_1 = + _Base_manager<_Functor>::_M_get_pointer(_M_invoke___functor); + __invoke_r<_Res>(*__trans_tmp_1, __args...); + } +}; +template +struct function<_Res(_ArgTypes...)> { + template function(_Functor) { + _Function_handler<_Res(_ArgTypes...), _Functor>::_M_invoke; + } +}; +template struct __shared_ptr_access { + using element_type = _Tp; + element_type *operator->(); +}; +template void make_shared(); +} // namespace std +namespace boost { +using std::decay; +using std::enable_if; +using std::integral_constant; +namespace asio { +namespace detail { +template struct is_completion_signature; +template +struct is_completion_signature : integral_constant {}; +} // namespace detail +template +concept completion_signature = detail::is_completion_signature::value; +template struct async_result; +namespace detail { +template +struct async_result_has_initiate_memfn : integral_constant<1> {}; +} // namespace detail +template +enable_if::value, + decltype(async_result::type, + Signatures...>::initiate(0))>::type +async_initiate(Initiation) {} +} // namespace asio +} // namespace boost +namespace malloy::websocket { +struct connection { + auto read(auto, auto done) { + auto wrapper = [] {}; + return boost::asio::async_initiate(wrapper); + } +}; +} // namespace malloy::websocket +namespace std { +template struct coroutine_traits; +template struct coroutine_handle { + operator coroutine_handle<>(); +}; +struct suspend_always { + bool await_ready(); + void await_suspend(coroutine_handle<>); + void await_resume(); +}; +} // namespace std +namespace boost { +namespace asio { +namespace detail { +using std::coroutine_handle; +using std::suspend_always; +template class awaitable_frame; +} // namespace detail +template struct awaitable { + bool await_ready(); + template + void await_suspend(detail::coroutine_handle>); + void await_resume(); +}; +namespace detail { +struct awaitable_frame_base { + auto initial_suspend() { return suspend_always(); } + auto final_suspend() noexcept { + struct result { + bool await_ready() noexcept; + void await_suspend(coroutine_handle<>) noexcept; + void await_resume() noexcept; + }; + return result{}; + } + void unhandled_exception(); + template auto await_transform(T a) { return a; } +}; +template <> struct awaitable_frame : awaitable_frame_base { + void get_return_object(); +}; +} // namespace detail +} // namespace asio +} // namespace boost +namespace std { +template +struct coroutine_traits, Args...> { + typedef boost::asio::detail::awaitable_frame promise_type; +}; +} // namespace std +namespace boost { +namespace asio { +struct use_awaitable_t { + use_awaitable_t(char, int, char); +} use_awaitable(0, 0, 0); +template +struct async_result { + template static awaitable initiate(Initiation); +}; +} // namespace asio +} // namespace boost +namespace malloy::test { +void roundtrip_coro(std::function); +} +using boost::asio::awaitable; +template awaitable client_ws_handler_coro() { + std::__shared_ptr_access conn; + auto buffer = std::make_shared; + co_await conn->read(buffer, boost::asio::use_awaitable); +} +void port() { + malloy::test::roundtrip_coro([](auto) { client_ws_handler_coro; }); +}