diff mbox series

[9/9] c++, coroutines: Look through initial_await target exprs [PR110635].

Message ID 83199df1749de7a7a63a3fdf219ae0d9db59896d.1724267239.git.iain@sandoe.co.uk
State New
Headers show
Series c++, coroutines: Patch set for ramp function fixes. | expand

Commit Message

Iain Sandoe Aug. 21, 2024, 7:10 p.m. UTC
In the case that the initial awaiter returns an object, the initial await
can be a target expression and we need to look at its initializer to cast
the await_resume() to void and to wrap in a compoun expression that sets
the initial_await_resume_called flag.

	PR c++/110635

gcc/cp/ChangeLog:

	* coroutines.cc
	(cp_coroutine_transform::wrap_original_function_body): Look through
	initial await target expressions to find the actual co_await_expr
	that we need to update.

gcc/testsuite/ChangeLog:

	* g++.dg/coroutines/pr110635.C: New test.

Signed-off-by: Iain Sandoe <iain@sandoe.co.uk>
---
 gcc/cp/coroutines.cc                       | 10 ++-
 gcc/testsuite/g++.dg/coroutines/pr110635.C | 72 ++++++++++++++++++++++
 2 files changed, 81 insertions(+), 1 deletion(-)
 create mode 100644 gcc/testsuite/g++.dg/coroutines/pr110635.C

Comments

Jason Merrill Aug. 22, 2024, 4:12 p.m. UTC | #1
On 8/21/24 3:10 PM, Iain Sandoe wrote:
> In the case that the initial awaiter returns an object, the initial await
> can be a target expression and we need to look at its initializer to cast
> the await_resume() to void and to wrap in a compoun expression that sets
> the initial_await_resume_called flag.
> 
> 	PR c++/110635
> 
> gcc/cp/ChangeLog:
> 
> 	* coroutines.cc
> 	(cp_coroutine_transform::wrap_original_function_body): Look through
> 	initial await target expressions to find the actual co_await_expr
> 	that we need to update.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/coroutines/pr110635.C: New test.
> 
> Signed-off-by: Iain Sandoe <iain@sandoe.co.uk>
> ---
>   gcc/cp/coroutines.cc                       | 10 ++-
>   gcc/testsuite/g++.dg/coroutines/pr110635.C | 72 ++++++++++++++++++++++
>   2 files changed, 81 insertions(+), 1 deletion(-)
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/pr110635.C
> 
> diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
> index 1039d2f8515..fcd6d7f0a7d 100644
> --- a/gcc/cp/coroutines.cc
> +++ b/gcc/cp/coroutines.cc
> @@ -4287,7 +4287,15 @@ cp_coroutine_transform::wrap_original_function_body ()
>   	     a reference type, look past the indirection.  */
>   	  if (INDIRECT_REF_P (initial_await))
>   	    initial_await = TREE_OPERAND (initial_await, 0);
> -	  tree vec = TREE_OPERAND (initial_await, 3);
> +	  /* In the case that the initial_await returns a target expression
> +	     we might need to look through that to update the await expr.  */
> +	  tree iaw = initial_await;
> +	  if (TREE_CODE (iaw) == TARGET_EXPR)
> +	    {
> +	      iaw = TARGET_EXPR_INITIAL (iaw);
> +	      gcc_checking_assert (TREE_CODE (iaw) == CO_AWAIT_EXPR);
> +	    }

How about moving the assert out of the if?  OK with that change.

> +	  tree vec = TREE_OPERAND (iaw, 3);
>   	  tree aw_r = TREE_VEC_ELT (vec, 2);
>   	  aw_r = convert_to_void (aw_r, ICV_STATEMENT, tf_warning_or_error);
>   	  tree update = build2 (MODIFY_EXPR, boolean_type_node, i_a_r_c,
> diff --git a/gcc/testsuite/g++.dg/coroutines/pr110635.C b/gcc/testsuite/g++.dg/coroutines/pr110635.C
> new file mode 100644
> index 00000000000..ea4e0e853eb
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/pr110635.C
> @@ -0,0 +1,72 @@
> +
> +#define CASE 0
> +#include <coroutine>
> +#include <iostream>
> +
> +struct Coroutine {
> +
> +    struct promise_type;
> +
> +    using handler_type = std::coroutine_handle<promise_type>;
> +
> +    struct initial_suspend_awaiter {
> +
> +        bool await_ready() noexcept {
> +            std::cout << "await_ready" << std::endl;
> +            return false;
> +        }
> +
> +        void await_suspend(handler_type h) noexcept {
> +            std::cout << "await_suspend" << std::endl;
> +        }
> +
> +#if CASE == 0
> +        struct await_resume_return_object {
> +            await_resume_return_object() noexcept {
> +                std::cout << "await_resume_return_object" << std::endl;
> +            }
> +
> +            ~await_resume_return_object() noexcept {
> +                std::cout << "~await_resume_return_object" << std::endl;
> +            }
> +        };
> +#elif CASE == 1
> +        using await_resume_return_object = struct{};
> +#elif CASE == 2
> +        using await_resume_return_object = int;
> +#else
> +        using await_resume_return_object = void;
> +#endif
> +        await_resume_return_object await_resume() noexcept {
> +            std::cout << "await_resume" << std::endl;
> +#if CASE == 0 || CASE == 1 || CASE == 2
> +            return {};
> +#endif
> +        }
> +
> +        initial_suspend_awaiter() noexcept {
> +            std::cout << "initial_suspend_awaiter" << std::endl;
> +        }
> +
> +        ~initial_suspend_awaiter() noexcept {
> +            std::cout << "~initial_suspend_awaiter" << std::endl;
> +        }
> +    };
> +
> +    struct promise_type {
> +        void return_void() noexcept {}
> +        void unhandled_exception() noexcept { std::terminate();}
> +        initial_suspend_awaiter initial_suspend() noexcept { return {}; }
> +        std::suspend_never final_suspend() noexcept { return {}; }
> +        Coroutine get_return_object() {
> +            return Coroutine{handler_type::from_promise(*this)};
> +        }
> +    };
> +
> +    handler_type handler;
> +};
> +
> +int main() {
> +    auto coro = []()->Coroutine { co_return; }();
> +    coro.handler.resume();
> +}
diff mbox series

Patch

diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
index 1039d2f8515..fcd6d7f0a7d 100644
--- a/gcc/cp/coroutines.cc
+++ b/gcc/cp/coroutines.cc
@@ -4287,7 +4287,15 @@  cp_coroutine_transform::wrap_original_function_body ()
 	     a reference type, look past the indirection.  */
 	  if (INDIRECT_REF_P (initial_await))
 	    initial_await = TREE_OPERAND (initial_await, 0);
-	  tree vec = TREE_OPERAND (initial_await, 3);
+	  /* In the case that the initial_await returns a target expression
+	     we might need to look through that to update the await expr.  */
+	  tree iaw = initial_await;
+	  if (TREE_CODE (iaw) == TARGET_EXPR)
+	    {
+	      iaw = TARGET_EXPR_INITIAL (iaw);
+	      gcc_checking_assert (TREE_CODE (iaw) == CO_AWAIT_EXPR);
+	    }
+	  tree vec = TREE_OPERAND (iaw, 3);
 	  tree aw_r = TREE_VEC_ELT (vec, 2);
 	  aw_r = convert_to_void (aw_r, ICV_STATEMENT, tf_warning_or_error);
 	  tree update = build2 (MODIFY_EXPR, boolean_type_node, i_a_r_c,
diff --git a/gcc/testsuite/g++.dg/coroutines/pr110635.C b/gcc/testsuite/g++.dg/coroutines/pr110635.C
new file mode 100644
index 00000000000..ea4e0e853eb
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/pr110635.C
@@ -0,0 +1,72 @@ 
+
+#define CASE 0
+#include <coroutine>
+#include <iostream>
+
+struct Coroutine {
+
+    struct promise_type;
+
+    using handler_type = std::coroutine_handle<promise_type>;
+
+    struct initial_suspend_awaiter {
+
+        bool await_ready() noexcept {
+            std::cout << "await_ready" << std::endl;
+            return false;
+        }
+        
+        void await_suspend(handler_type h) noexcept {
+            std::cout << "await_suspend" << std::endl;
+        }
+
+#if CASE == 0
+        struct await_resume_return_object {
+            await_resume_return_object() noexcept {
+                std::cout << "await_resume_return_object" << std::endl;
+            }
+            
+            ~await_resume_return_object() noexcept {
+                std::cout << "~await_resume_return_object" << std::endl;
+            }
+        };
+#elif CASE == 1
+        using await_resume_return_object = struct{};
+#elif CASE == 2
+        using await_resume_return_object = int;
+#else
+        using await_resume_return_object = void;
+#endif
+        await_resume_return_object await_resume() noexcept {
+            std::cout << "await_resume" << std::endl;
+#if CASE == 0 || CASE == 1 || CASE == 2
+            return {};
+#endif
+        }
+
+        initial_suspend_awaiter() noexcept {
+            std::cout << "initial_suspend_awaiter" << std::endl;
+        }
+
+        ~initial_suspend_awaiter() noexcept {
+            std::cout << "~initial_suspend_awaiter" << std::endl;
+        }
+    };
+
+    struct promise_type {
+        void return_void() noexcept {}
+        void unhandled_exception() noexcept { std::terminate();}
+        initial_suspend_awaiter initial_suspend() noexcept { return {}; }
+        std::suspend_never final_suspend() noexcept { return {}; }
+        Coroutine get_return_object() {
+            return Coroutine{handler_type::from_promise(*this)};
+        }
+    };
+
+    handler_type handler;
+};
+
+int main() {
+    auto coro = []()->Coroutine { co_return; }();
+    coro.handler.resume();
+}