c++, coroutines: Handle statement expressions part 1.

Message ID 20241129134319.51846-1-iain@sandoe.co.uk
State New
Headers
Series c++, coroutines: Handle statement expressions part 1. |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gcc_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_gcc_check--master-arm success Test passed
linaro-tcwg-bot/tcwg_gcc_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_gcc_check--master-aarch64 success Test passed

Commit Message

Iain Sandoe Nov. 29, 2024, 1:43 p.m. UTC
  Tested on x86_64-darwin, x86_64/powerpc64-linux, on folly and more
widely by Sam.  There are possibly additional BZ dups that will be
covered (this fixes 117231, a P1). OK for trunk?
thanks
Iain

--- 8< ---

In the current implementation, statement expressions were intentionally
unsupported (as a C++ extension).  However since they are quite heavily
used by end-users and also now emitted by the compiler in some cases
we are now working to add them.  This first patch ensures that we
recurse into statement expressions (and therefore handle coroutine
keywords that might appear inside them).

	PR c++/115851
	PR c++/116914
	PR c++/117231

gcc/cp/ChangeLog:

	* coroutines.cc (await_statement_expander): Walk into
	statement expressions.
	(await_statement_walker): Likewise.

gcc/testsuite/ChangeLog:

	* g++.dg/coroutines/pr115851.C: New test.
	* g++.dg/coroutines/pr116914.C: New test.
	* g++.dg/coroutines/pr117231.C: New test.

Signed-off-by: Iain Sandoe <iain@sandoe.co.uk>
---
 gcc/cp/coroutines.cc                       | 22 ++++++++++++
 gcc/testsuite/g++.dg/coroutines/pr115851.C | 35 +++++++++++++++++++
 gcc/testsuite/g++.dg/coroutines/pr116914.C | 40 ++++++++++++++++++++++
 gcc/testsuite/g++.dg/coroutines/pr117231.C | 21 ++++++++++++
 4 files changed, 118 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/coroutines/pr115851.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/pr116914.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/pr117231.C
  

Patch

diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
index cdf61d89109..5764475a7de 100644
--- a/gcc/cp/coroutines.cc
+++ b/gcc/cp/coroutines.cc
@@ -2128,6 +2128,14 @@  await_statement_expander (tree *stmt, int *do_subtree, void *d)
     }
   else if (EXPR_P (*stmt))
     {
+      /* Look for ({}) at the top level - just recurse into these.  */
+      if (TREE_CODE (*stmt) == EXPR_STMT)
+	{
+	  tree inner = EXPR_STMT_EXPR (*stmt);
+	  if (TREE_CODE (inner) == STATEMENT_LIST
+	      || TREE_CODE (inner) == BIND_EXPR)
+	    return NULL_TREE; // process contents
+	}
       process_one_statement (stmt, d);
       *do_subtree = 0; /* Done subtrees.  */
     }
@@ -3857,6 +3865,20 @@  await_statement_walker (tree *stmt, int *do_subtree, void *d)
       if (!(cp_walk_tree (stmt, find_any_await, &await_ptr, &visited)))
 	return NULL_TREE; /* Nothing special to do here.  */
 
+      /* Handle statement expressions.  */
+      if (TREE_CODE (expr) == EXPR_STMT)
+	{
+	  tree inner = EXPR_STMT_EXPR (expr);
+	  if (TREE_CODE (inner) == STATEMENT_LIST
+	      || TREE_CODE (inner) == BIND_EXPR)
+	    {
+	      res = cp_walk_tree (&EXPR_STMT_EXPR (expr),
+				  await_statement_walker, d, NULL);
+	      *do_subtree = 0;
+	      return res;
+	    }
+	}
+
       visited.empty ();
       awpts->saw_awaits = 0;
       hash_set<tree> truth_aoif_to_expand;
diff --git a/gcc/testsuite/g++.dg/coroutines/pr115851.C b/gcc/testsuite/g++.dg/coroutines/pr115851.C
new file mode 100644
index 00000000000..0e251760574
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/pr115851.C
@@ -0,0 +1,35 @@ 
+// { dg-additional-options "-Wno-pedantic " }
+#include <coroutine>
+
+struct SuspendNever {
+    bool await_ready() noexcept;
+    void await_suspend(std::coroutine_handle<>) noexcept;
+    void await_resume() noexcept;
+};
+
+struct Coroutine;
+
+struct PromiseType {
+    Coroutine get_return_object();
+    SuspendNever initial_suspend();
+    SuspendNever final_suspend() noexcept;
+    void unhandled_exception () {}
+};
+
+struct Coroutine {
+    using promise_type = PromiseType;
+};
+
+struct ErrorOr {
+    int release_error();
+};
+
+void warnln(int const&);
+
+Coroutine __async_test_input_basic() {
+    ({
+        co_await SuspendNever{};
+	ErrorOr _temporary_result2;
+        warnln(_temporary_result2.release_error());
+    });
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/pr116914.C b/gcc/testsuite/g++.dg/coroutines/pr116914.C
new file mode 100644
index 00000000000..8f1310380fc
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/pr116914.C
@@ -0,0 +1,40 @@ 
+//  { dg-additional-options "-std=gnu++20 -fpreprocessed" }
+
+namespace std {
+template <typename a, typename> struct coroutine_traits : a {};
+template <typename = void> struct coroutine_handle {
+  static coroutine_handle from_address(void *);
+  operator coroutine_handle<>();
+  void *address();
+};
+struct b {
+  int await_ready() noexcept;
+  void await_suspend(coroutine_handle<>) noexcept;
+  void await_resume() noexcept;
+};
+} // namespace std
+struct c;
+struct d {
+  c get_return_object();
+  std::b initial_suspend();
+  std::b final_suspend() noexcept;
+  void unhandled_exception();
+  std::b yield_value(int);
+};
+struct e {
+  void operator++();
+  int operator*();
+  int operator!=(e);
+};
+struct c {
+  using promise_type = d;
+  e begin();
+  e end();
+  c f() {
+    c g;
+    for (auto h : g) {
+      auto i = 1;
+      co_yield i;
+    }
+  }
+};
diff --git a/gcc/testsuite/g++.dg/coroutines/pr117231.C b/gcc/testsuite/g++.dg/coroutines/pr117231.C
new file mode 100644
index 00000000000..8be80a44f17
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/pr117231.C
@@ -0,0 +1,21 @@ 
+//  { dg-additional-options "-std=c++23 " }
+//  { dg-do run }
+#include <generator>
+//#include <print>
+#include <vector>
+
+std::generator<int> get_seq()
+{
+  std::vector<int> data_{1, 2, 3};
+  for (auto item : data_)
+    co_yield item;
+}
+
+int main()
+{
+  int res = 0;
+  for (auto item : get_seq())
+    res = item;   //std::println("{}", item);
+  if (res != 3)
+    __builtin_abort ();
+}