public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc r12-8307] c++, coroutines: Avoid expanding within templates [PR103868]
@ 2022-04-28 12:51 Iain D Sandoe
  0 siblings, 0 replies; only message in thread
From: Iain D Sandoe @ 2022-04-28 12:51 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:9cb1f565a91e2dd57098c43593954b57c065a19b

commit r12-8307-g9cb1f565a91e2dd57098c43593954b57c065a19b
Author: Nathan Sidwell <nathan@acm.org>
Date:   Sun Apr 3 11:35:03 2022 +0100

    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 <iain@sandoe.co.uk>

Diff:
---
 gcc/cp/coroutines.cc                       |  25 ++---
 gcc/testsuite/g++.dg/coroutines/pr103868.C | 150 +++++++++++++++++++++++++++++
 2 files changed, 163 insertions(+), 12 deletions(-)

diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
index cdf6503c4d3..64f4b4412e5 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 00000000000..8687effb563
--- /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 <int __v> struct integral_constant {
+  static constexpr bool value = __v;
+};
+template <typename> struct decay { typedef int type; };
+template <bool, typename> struct enable_if;
+template <typename _Tp> struct enable_if<true, _Tp> { typedef _Tp type; };
+template <typename, typename _Fn, typename... _Args>
+void __invoke_impl(_Fn __f, _Args... __args) {
+  __f(__args...);
+}
+template <typename, typename _Callable, typename... _Args>
+void __invoke_r(_Callable __fn, _Args... __args) {
+  using __result = int;
+  using __type = __result;
+  __invoke_impl<__type>(__fn, __args...);
+}
+template <typename> class function;
+template <typename _Functor> struct _Base_manager {
+  static _Functor *_M_get_pointer(int) {}
+};
+template <typename, typename> class _Function_handler;
+template <typename _Res, typename _Functor, typename... _ArgTypes>
+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 <typename _Res, typename... _ArgTypes>
+struct function<_Res(_ArgTypes...)> {
+  template <typename _Functor> function(_Functor) {
+    _Function_handler<_Res(_ArgTypes...), _Functor>::_M_invoke;
+  }
+};
+template <typename _Tp> struct __shared_ptr_access {
+  using element_type = _Tp;
+  element_type *operator->();
+};
+template <typename> void make_shared();
+} // namespace std
+namespace boost {
+using std::decay;
+using std::enable_if;
+using std::integral_constant;
+namespace asio {
+namespace detail {
+template <typename> struct is_completion_signature;
+template <typename R, typename... Args>
+struct is_completion_signature<R(Args...)> : integral_constant<true> {};
+} // namespace detail
+template <typename T>
+concept completion_signature = detail::is_completion_signature<T>::value;
+template <typename...> struct async_result;
+namespace detail {
+template <completion_signature>
+struct async_result_has_initiate_memfn : integral_constant<1> {};
+} // namespace detail
+template <typename CompletionToken, completion_signature... Signatures,
+          typename Initiation>
+enable_if<detail::async_result_has_initiate_memfn<Signatures...>::value,
+          decltype(async_result<typename decay<CompletionToken>::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<decltype(done), void()>(wrapper);
+  }
+};
+} // namespace malloy::websocket
+namespace std {
+template <typename...> struct coroutine_traits;
+template <typename = void> 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 <typename> class awaitable_frame;
+} // namespace detail
+template <typename, typename = int> struct awaitable {
+  bool await_ready();
+  template <class U>
+  void await_suspend(detail::coroutine_handle<detail::awaitable_frame<U>>);
+  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 <typename T> auto await_transform(T a) { return a; }
+};
+template <> struct awaitable_frame<void> : awaitable_frame_base {
+  void get_return_object();
+};
+} // namespace detail
+} // namespace asio
+} // namespace boost
+namespace std {
+template <typename T, typename Executor, typename... Args>
+struct coroutine_traits<boost::asio::awaitable<T, Executor>, Args...> {
+  typedef boost::asio::detail::awaitable_frame<T> promise_type;
+};
+} // namespace std
+namespace boost {
+namespace asio {
+struct use_awaitable_t {
+  use_awaitable_t(char, int, char);
+} use_awaitable(0, 0, 0);
+template <typename Executor, typename R, typename... Args>
+struct async_result<Executor, R(Args...)> {
+  template <typename Initiation> static awaitable<int> initiate(Initiation);
+};
+} // namespace asio
+} // namespace boost
+namespace malloy::test {
+void roundtrip_coro(std::function<void(int)>);
+}
+using boost::asio::awaitable;
+template <int> awaitable<void> client_ws_handler_coro() {
+  std::__shared_ptr_access<malloy::websocket::connection> conn;
+  auto buffer = std::make_shared<int>;
+  co_await conn->read(buffer, boost::asio::use_awaitable);
+}
+void port() {
+  malloy::test::roundtrip_coro([](auto) { client_ws_handler_coro<false>; });
+}


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2022-04-28 12:51 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-04-28 12:51 [gcc r12-8307] c++, coroutines: Avoid expanding within templates [PR103868] Iain D Sandoe

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).