From: Iain Sandoe <iain@sandoe.co.uk>
To: GCC Patches <gcc-patches@gcc.gnu.org>, libstdc++ <libstdc++@gcc.gnu.org>
Subject: [C++ coroutines 6/6] Testsuite.
Date: Sun, 17 Nov 2019 10:28:00 -0000 [thread overview]
Message-ID: <BCAC2431-0DF3-4592-AC8B-2C8912743D4A@sandoe.co.uk> (raw)
In-Reply-To: <3D9E9C45-F7A2-42E4-B0B0-51B03A1041F0@sandoe.co.uk>
There are two categories of test:
1. Checks for correctly formed source code and the error reporting.
2. Checks for transformation and code-gen.
The second set are run as 'torture' tests for the standard options
set, including LTO. These are also intentionally run with no options
provided (from the coroutines.exp script).
gcc/testsuite/ChangeLog:
2019-11-17 Iain Sandoe <iain@sandoe.co.uk>
* g++.dg/coroutines/co-yield-syntax-1.C: New test.
* g++.dg/coroutines/co-yield-syntax-2.C: New test.
* g++.dg/coroutines/co-yield-syntax-3.C: New test.
* g++.dg/coroutines/coro-auto-fn.C: New test.
* g++.dg/coroutines/coro-await-context-auto-fn.C: New test.
* g++.dg/coroutines/coro-bad-return.C: New test.
* g++.dg/coroutines/coro-builtins.C: New test.
* g++.dg/coroutines/coro-constexpr-fn.C: New test.
* g++.dg/coroutines/coro-context-ctor-dtor.C: New test.
* g++.dg/coroutines/coro-context-main.C: New test.
* g++.dg/coroutines/coro-context-vararg.C: New test.
* g++.dg/coroutines/coro-missing-gro.C: New test.
* g++.dg/coroutines/coro-missing-promise-yield.C: New test.
* g++.dg/coroutines/coro-missing-ret-value.C: New test.
* g++.dg/coroutines/coro-missing-ret-void.C: New test.
* g++.dg/coroutines/coro-missing-ueh-1.C: New test.
* g++.dg/coroutines/coro-missing-ueh-2.C: New test.
* g++.dg/coroutines/coro-missing-ueh-3.C: New test.
* g++.dg/coroutines/coro-missing-ueh.h: New test.
* g++.dg/coroutines/coro-pre-proc.C: New test.
* g++.dg/coroutines/coro.h: New test.
* g++.dg/coroutines/coroutines.exp: New file.
* g++.dg/coroutines/torture/co-await-0-triv.C: New test.
* g++.dg/coroutines/torture/co-await-1-value.C: New test.
* g++.dg/coroutines/torture/co-await-2-xform.C: New test.
* g++.dg/coroutines/torture/co-await-3-rhs-op.C: New test.
* g++.dg/coroutines/torture/co-await-4-control-flow.C: New test.
* g++.dg/coroutines/torture/co-await-5-loop.C: New test.
* g++.dg/coroutines/torture/co-await-6-ovl.C: New test.
* g++.dg/coroutines/torture/co-await-7-tmpl.C: New test.
* g++.dg/coroutines/torture/co-await-8-cascade.C: New test.
* g++.dg/coroutines/torture/co-await-9-pair.C: New test.
* g++.dg/coroutines/torture/co-ret-3.C: New test.
* g++.dg/coroutines/torture/co-ret-4.C: New test.
* g++.dg/coroutines/torture/co-ret-5.C: New test.
* g++.dg/coroutines/torture/co-ret-6.C: New test.
* g++.dg/coroutines/torture/co-ret-7.C: New test.
* g++.dg/coroutines/torture/co-ret-8.C: New test.
* g++.dg/coroutines/torture/co-ret-9.C: New test.
* g++.dg/coroutines/torture/co-ret-void-is-ready.C: New test.
* g++.dg/coroutines/torture/co-ret-void-is-suspend.C: New test.
* g++.dg/coroutines/torture/co-yield-0-triv.C: New test.
* g++.dg/coroutines/torture/co-yield-1-multi.C: New test.
* g++.dg/coroutines/torture/co-yield-2-loop.C: New test.
* g++.dg/coroutines/torture/co-yield-3-tmpl.C: New test.
* g++.dg/coroutines/torture/co-yield-strings.C: New test.
* g++.dg/coroutines/torture/coro-torture.exp: New file.
* g++.dg/coroutines/torture/exceptions-test-0.C: New test.
* g++.dg/coroutines/torture/func-params-0.C: New test.
* g++.dg/coroutines/torture/func-params-1.C: New test.
* g++.dg/coroutines/torture/func-params-2.C: New test.
* g++.dg/coroutines/torture/func-params-3.C: New test.
* g++.dg/coroutines/torture/func-params-4.C: New test.
* g++.dg/coroutines/torture/func-params-5.C: New test.
* g++.dg/coroutines/torture/gro_on_alloc_fail_0.C: New test.
* g++.dg/coroutines/torture/local-var-0.C: New test.
* g++.dg/coroutines/torture/local-var-1.C: New test.
* g++.dg/coroutines/torture/local-var-2.C: New test.
* g++.dg/coroutines/torture/local-var-3.C: New test.
* g++.dg/coroutines/torture/local-var-4.C: New test.
* g++.dg/coroutines/torture/mid-suspend-destruction-0.C: New test.
---
.../g++.dg/coroutines/co-yield-syntax-1.C | 6 +
.../g++.dg/coroutines/co-yield-syntax-2.C | 6 +
.../g++.dg/coroutines/co-yield-syntax-3.C | 37 ++++
gcc/testsuite/g++.dg/coroutines/coro-auto-fn.C | 12 ++
.../g++.dg/coroutines/coro-await-context-auto-fn.C | 16 ++
gcc/testsuite/g++.dg/coroutines/coro-bad-return.C | 48 ++++++
gcc/testsuite/g++.dg/coroutines/coro-builtins.C | 17 ++
.../g++.dg/coroutines/coro-constexpr-fn.C | 12 ++
.../g++.dg/coroutines/coro-context-ctor-dtor.C | 8 +
.../g++.dg/coroutines/coro-context-main.C | 7 +
.../g++.dg/coroutines/coro-context-vararg.C | 13 ++
gcc/testsuite/g++.dg/coroutines/coro-missing-gro.C | 32 ++++
.../g++.dg/coroutines/coro-missing-promise-yield.C | 35 ++++
.../g++.dg/coroutines/coro-missing-ret-value.C | 34 ++++
.../g++.dg/coroutines/coro-missing-ret-void.C | 34 ++++
.../g++.dg/coroutines/coro-missing-ueh-1.C | 14 ++
.../g++.dg/coroutines/coro-missing-ueh-2.C | 16 ++
.../g++.dg/coroutines/coro-missing-ueh-3.C | 17 ++
gcc/testsuite/g++.dg/coroutines/coro-missing-ueh.h | 25 +++
gcc/testsuite/g++.dg/coroutines/coro-pre-proc.C | 9 +
gcc/testsuite/g++.dg/coroutines/coro.h | 123 ++++++++++++++
gcc/testsuite/g++.dg/coroutines/coroutines.exp | 50 ++++++
.../g++.dg/coroutines/torture/co-await-0-triv.C | 144 ++++++++++++++++
.../g++.dg/coroutines/torture/co-await-1-value.C | 149 ++++++++++++++++
.../g++.dg/coroutines/torture/co-await-2-xform.C | 155 +++++++++++++++++
.../g++.dg/coroutines/torture/co-await-3-rhs-op.C | 155 +++++++++++++++++
.../coroutines/torture/co-await-4-control-flow.C | 148 ++++++++++++++++
.../g++.dg/coroutines/torture/co-await-5-loop.C | 147 ++++++++++++++++
.../g++.dg/coroutines/torture/co-await-6-ovl.C | 153 +++++++++++++++++
.../g++.dg/coroutines/torture/co-await-7-tmpl.C | 149 ++++++++++++++++
.../g++.dg/coroutines/torture/co-await-8-cascade.C | 157 +++++++++++++++++
.../g++.dg/coroutines/torture/co-await-9-pair.C | 155 +++++++++++++++++
gcc/testsuite/g++.dg/coroutines/torture/co-ret-3.C | 112 ++++++++++++
gcc/testsuite/g++.dg/coroutines/torture/co-ret-4.C | 129 ++++++++++++++
gcc/testsuite/g++.dg/coroutines/torture/co-ret-5.C | 122 +++++++++++++
gcc/testsuite/g++.dg/coroutines/torture/co-ret-6.C | 125 ++++++++++++++
gcc/testsuite/g++.dg/coroutines/torture/co-ret-7.C | 114 +++++++++++++
gcc/testsuite/g++.dg/coroutines/torture/co-ret-8.C | 126 ++++++++++++++
gcc/testsuite/g++.dg/coroutines/torture/co-ret-9.C | 118 +++++++++++++
.../coroutines/torture/co-ret-void-is-ready.C | 107 ++++++++++++
.../coroutines/torture/co-ret-void-is-suspend.C | 111 ++++++++++++
.../g++.dg/coroutines/torture/co-yield-0-triv.C | 146 ++++++++++++++++
.../g++.dg/coroutines/torture/co-yield-1-multi.C | 159 +++++++++++++++++
.../g++.dg/coroutines/torture/co-yield-2-loop.C | 153 +++++++++++++++++
.../g++.dg/coroutines/torture/co-yield-3-tmpl.C | 160 +++++++++++++++++
.../g++.dg/coroutines/torture/co-yield-strings.C | 182 ++++++++++++++++++++
.../g++.dg/coroutines/torture/coro-torture.exp | 19 +++
.../g++.dg/coroutines/torture/exceptions-test-0.C | 189 +++++++++++++++++++++
.../g++.dg/coroutines/torture/func-params-0.C | 126 ++++++++++++++
.../g++.dg/coroutines/torture/func-params-1.C | 130 ++++++++++++++
.../g++.dg/coroutines/torture/func-params-2.C | 134 +++++++++++++++
.../g++.dg/coroutines/torture/func-params-3.C | 133 +++++++++++++++
.../g++.dg/coroutines/torture/func-params-4.C | 141 +++++++++++++++
.../g++.dg/coroutines/torture/func-params-5.C | 141 +++++++++++++++
.../coroutines/torture/gro_on_alloc_fail_0.C | 137 +++++++++++++++
.../g++.dg/coroutines/torture/local-var-0.C | 123 ++++++++++++++
.../g++.dg/coroutines/torture/local-var-1.C | 123 ++++++++++++++
.../g++.dg/coroutines/torture/local-var-2.C | 135 +++++++++++++++
.../g++.dg/coroutines/torture/local-var-3.C | 153 +++++++++++++++++
.../g++.dg/coroutines/torture/local-var-4.C | 162 ++++++++++++++++++
.../coroutines/torture/mid-suspend-destruction-0.C | 127 ++++++++++++++
61 files changed, 5920 insertions(+)
create mode 100644 gcc/testsuite/g++.dg/coroutines/co-yield-syntax-1.C
create mode 100644 gcc/testsuite/g++.dg/coroutines/co-yield-syntax-2.C
create mode 100644 gcc/testsuite/g++.dg/coroutines/co-yield-syntax-3.C
create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-auto-fn.C
create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-await-context-auto-fn.C
create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-bad-return.C
create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-builtins.C
create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-constexpr-fn.C
create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-context-ctor-dtor.C
create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-context-main.C
create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-context-vararg.C
create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-gro.C
create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-promise-yield.C
create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-ret-value.C
create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-ret-void.C
create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-1.C
create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-2.C
create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-3.C
create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-ueh.h
create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-pre-proc.C
create mode 100644 gcc/testsuite/g++.dg/coroutines/coro.h
create mode 100644 gcc/testsuite/g++.dg/coroutines/coroutines.exp
create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-0-triv.C
create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-1-value.C
create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-2-xform.C
create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-3-rhs-op.C
create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-4-control-flow.C
create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-5-loop.C
create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-6-ovl.C
create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-7-tmpl.C
create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-8-cascade.C
create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-9-pair.C
create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-3.C
create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-4.C
create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-5.C
create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-6.C
create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-7.C
create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-8.C
create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-9.C
create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-void-is-ready.C
create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-void-is-suspend.C
create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-0-triv.C
create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-1-multi.C
create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-2-loop.C
create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-3-tmpl.C
create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-strings.C
create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/coro-torture.exp
create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/exceptions-test-0.C
create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/func-params-0.C
create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/func-params-1.C
create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/func-params-2.C
create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/func-params-3.C
create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/func-params-4.C
create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/func-params-5.C
create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/gro_on_alloc_fail_0.C
create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/local-var-0.C
create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/local-var-1.C
create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/local-var-2.C
create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/local-var-3.C
create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/local-var-4.C
create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/mid-suspend-destruction-0.C
diff --git a/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-1.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-1.C
new file mode 100644
index 0000000..30db0e9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-1.C
@@ -0,0 +1,6 @@
+// { dg-additional-options "-fsyntax-only -w" }
+
+#include "coro.h"
+
+auto f (int x = co_yield 5); // { dg-error {'co_yield' cannot be used outside a function} }
+
diff --git a/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-2.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-2.C
new file mode 100644
index 0000000..71e119f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-2.C
@@ -0,0 +1,6 @@
+// { dg-additional-options "-fsyntax-only -w" }
+
+#include "coro.h"
+
+int a[] = { co_yield 21 }; // { dg-error {'co_yield' cannot be used outside a function} }
+
diff --git a/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-3.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-3.C
new file mode 100644
index 0000000..20a5b56
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-3.C
@@ -0,0 +1,37 @@
+// { dg-additional-options "-fsyntax-only -w" }
+#include "coro.h"
+
+namespace coro = std::experimental;
+
+/* Diagose missing return_void() in the promise type. */
+struct DummyYield {
+ coro::coroutine_handle<> handle;
+ DummyYield () : handle (nullptr) {}
+ DummyYield (coro::coroutine_handle<> handle) : handle (handle) {}
+ struct dummy_yield {
+ coro::suspend_never initial_suspend() { return {}; }
+ coro::suspend_never final_suspend() { return {}; }
+ DummyYield get_return_object() {
+ return DummyYield (coro::coroutine_handle<dummy_yield>::from_promise (*this));
+ }
+ void yield_value (int v) {}
+ void return_value (int v) {}
+ void unhandled_exception() { /*std::terminate();*/ };
+ };
+};
+
+template<> struct coro::coroutine_traits<DummyYield> {
+ using promise_type = DummyYield::dummy_yield;
+};
+
+DummyYield
+bar ()
+{
+ co_yield; // { dg-error {expected primary-expression before} }
+ co_return 0;
+}
+
+int main (int ac, char *av[]) {
+ DummyYield x = bar ();
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/coro-auto-fn.C b/gcc/testsuite/g++.dg/coroutines/coro-auto-fn.C
new file mode 100644
index 0000000..93a04dc
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coro-auto-fn.C
@@ -0,0 +1,12 @@
+// { dg-additional-options "-fsyntax-only -w" }
+
+#include "coro.h"
+
+auto bar () {
+ co_return 5; // { dg-error "cannot be used in a function with a deduced return type" }
+}
+
+int main () {
+ bar ();
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/coro-await-context-auto-fn.C b/gcc/testsuite/g++.dg/coroutines/coro-await-context-auto-fn.C
new file mode 100644
index 0000000..7f4ed9a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coro-await-context-auto-fn.C
@@ -0,0 +1,16 @@
+// { dg-additional-options "-fsyntax-only -w" }
+
+#include "coro.h"
+
+extern struct awaitable *aw ();
+
+auto bar () {
+ int x = 1 + co_await *aw(); // { dg-error "cannot be used in a function with a deduced return type" }
+
+ return x;
+}
+
+int main () {
+ bar ();
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/coro-bad-return.C b/gcc/testsuite/g++.dg/coroutines/coro-bad-return.C
new file mode 100644
index 0000000..e5b848d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coro-bad-return.C
@@ -0,0 +1,48 @@
+// { dg-additional-options "-fsyntax-only -w" }
+#if __clang__
+# include <experimental/coroutine>
+# include <utility>
+#else
+# include "coro.h"
+#endif
+namespace coro = std::experimental;
+
+struct Coro {
+ struct promise_type;
+ using handle_type = coro::coroutine_handle<Coro::promise_type>;
+ handle_type handle;
+ Coro () : handle(0) {}
+ Coro (handle_type _handle) : handle(_handle) {}
+ Coro (Coro &&s) : handle(s.handle) { s.handle = nullptr; }
+ Coro &operator = (Coro &&s) {
+ handle = s.handle;
+ s.handle = nullptr;
+ return *this;
+ }
+ Coro (const Coro &) = delete;
+ ~Coro() {
+ if ( handle )
+ handle.destroy();
+ }
+ struct promise_type {
+ promise_type() {}
+ ~promise_type() {}
+ Coro get_return_object () { return Coro (handle_type::from_promise (*this)); }
+ auto initial_suspend () { return coro::suspend_always{}; }
+ auto final_suspend () { return coro::suspend_always{}; }
+ void return_void () { }
+ void unhandled_exception() { }
+ };
+};
+
+extern int x;
+
+// Diagnose disallowed "return" in coroutine.
+Coro
+bar () // { dg-error "return statement not allowed" }
+{
+ if (x)
+ return Coro();
+ else
+ co_return;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/coro-builtins.C b/gcc/testsuite/g++.dg/coroutines/coro-builtins.C
new file mode 100644
index 0000000..d7c4883
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coro-builtins.C
@@ -0,0 +1,17 @@
+// { dg-additional-options "-fsyntax-only " }
+
+typedef __SIZE_TYPE__ size_t;
+
+int main ()
+{
+ void *co_h;
+ void *promise;
+ const size_t co_align = 16;
+
+ bool d = __builtin_coro_done (co_h);
+ __builtin_coro_resume (co_h);
+ __builtin_coro_destroy (co_h);
+ promise = __builtin_coro_promise (co_h, co_align, true);
+
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/coro-constexpr-fn.C b/gcc/testsuite/g++.dg/coroutines/coro-constexpr-fn.C
new file mode 100644
index 0000000..69b109f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coro-constexpr-fn.C
@@ -0,0 +1,12 @@
+// { dg-additional-options "-fsyntax-only -w" }
+
+#include "coro.h"
+
+constexpr int bar () {
+ co_return 5; // { dg-error "cannot be used in a .constexpr. function" }
+ return 42; /* Suppress the "no return" error. */
+}
+
+int main () {
+ return bar ();
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/coro-context-ctor-dtor.C b/gcc/testsuite/g++.dg/coroutines/coro-context-ctor-dtor.C
new file mode 100644
index 0000000..9396432
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coro-context-ctor-dtor.C
@@ -0,0 +1,8 @@
+// { dg-additional-options "-fsyntax-only -w" }
+
+#include "coro.h"
+
+struct Foo {
+ Foo () { co_return; } // { dg-error "cannot be used in a constructor" }
+ ~Foo () { co_return 5; } // { dg-error "cannot be used in a destructor" }
+};
diff --git a/gcc/testsuite/g++.dg/coroutines/coro-context-main.C b/gcc/testsuite/g++.dg/coroutines/coro-context-main.C
new file mode 100644
index 0000000..40d7e4e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coro-context-main.C
@@ -0,0 +1,7 @@
+// { dg-additional-options "-fsyntax-only -w" }
+
+#include "coro.h"
+
+int main (int ac, char *av[]) {
+ co_return 0; // { dg-error "cannot be used in the .main. function" }
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/coro-context-vararg.C b/gcc/testsuite/g++.dg/coroutines/coro-context-vararg.C
new file mode 100644
index 0000000..55a0295
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coro-context-vararg.C
@@ -0,0 +1,13 @@
+// { dg-additional-options "-fsyntax-only -w" }
+#include "coro.h"
+
+int
+bar (int x, ...)
+{
+ co_return 1; // { dg-error "cannot be used in a varargs function" }
+}
+
+int main (int ac, char *av[]) {
+ bar (5, ac);
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-gro.C b/gcc/testsuite/g++.dg/coroutines/coro-missing-gro.C
new file mode 100644
index 0000000..8bedb77
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-gro.C
@@ -0,0 +1,32 @@
+// { dg-additional-options "-fsyntax-only -w" }
+#include "coro.h"
+
+namespace coro = std::experimental;
+
+/* Diagose missing return_void() in the promise type. */
+struct MissingGRO {
+ coro::coroutine_handle<> handle;
+ MissingGRO () : handle (nullptr) {}
+ MissingGRO (coro::coroutine_handle<> handle) : handle (handle) {}
+ struct missing_gro {
+ coro::suspend_never initial_suspend() { return {}; }
+ coro::suspend_never final_suspend() { return {}; }
+ void return_void () {}
+ void unhandled_exception() { /*std::terminate();*/ };
+ };
+};
+
+template<> struct coro::coroutine_traits<MissingGRO> {
+ using promise_type = MissingGRO::missing_gro;
+};
+
+MissingGRO
+bar () // { dg-error {no member named 'get_return_object' in} }
+{
+ co_return;
+}
+
+int main (int ac, char *av[]) {
+ MissingGRO x = bar ();
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-promise-yield.C b/gcc/testsuite/g++.dg/coroutines/coro-missing-promise-yield.C
new file mode 100644
index 0000000..95f567f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-promise-yield.C
@@ -0,0 +1,35 @@
+// { dg-additional-options "-fsyntax-only -w" }
+#include "coro.h"
+
+namespace coro = std::experimental;
+
+struct MissingPromiseYield {
+ coro::coroutine_handle<> handle;
+ MissingPromiseYield () : handle (nullptr) {}
+ MissingPromiseYield (coro::coroutine_handle<> handle) : handle (handle) {}
+ struct missing_yield {
+ coro::suspend_never initial_suspend() { return {}; }
+ coro::suspend_never final_suspend() { return {}; }
+ MissingPromiseYield get_return_object() {
+ return MissingPromiseYield (coro::coroutine_handle<missing_yield>::from_promise (*this));
+ }
+ void return_value (int v) {}
+ void unhandled_exception() { /*std::terminate();*/ };
+ };
+};
+
+template<> struct coro::coroutine_traits<MissingPromiseYield> {
+ using promise_type = MissingPromiseYield::missing_yield;
+};
+
+MissingPromiseYield
+bar ()
+{
+ co_yield 22; // { dg-error {no member named 'yield_value' in} }
+ co_return 0;
+}
+
+int main (int ac, char *av[]) {
+ MissingPromiseYield x = bar ();
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-ret-value.C b/gcc/testsuite/g++.dg/coroutines/coro-missing-ret-value.C
new file mode 100644
index 0000000..fd81ce7
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-ret-value.C
@@ -0,0 +1,34 @@
+// { dg-additional-options "-fsyntax-only -w" }
+#include "coro.h"
+
+namespace coro = std::experimental;
+
+/* Diagose missing return_void() in the promise type. */
+struct MissingRetValue {
+ coro::coroutine_handle<> handle;
+ MissingRetValue () : handle (nullptr) {}
+ MissingRetValue (coro::coroutine_handle<> handle) : handle (handle) {}
+ struct missing_retvoid {
+ coro::suspend_never initial_suspend() { return {}; }
+ coro::suspend_never final_suspend() { return {}; }
+ MissingRetValue get_return_object() {
+ return MissingRetValue (coro::coroutine_handle<missing_retvoid>::from_promise (*this));
+ }
+ void unhandled_exception() { /*std::terminate();*/ };
+ };
+};
+
+template<> struct coro::coroutine_traits<MissingRetValue> {
+ using promise_type = MissingRetValue::missing_retvoid;
+};
+
+MissingRetValue
+bar ()
+{
+ co_return 6174; // { dg-error {no member named 'return_value' in} }
+}
+
+int main (int ac, char *av[]) {
+ MissingRetValue x = bar ();
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-ret-void.C b/gcc/testsuite/g++.dg/coroutines/coro-missing-ret-void.C
new file mode 100644
index 0000000..17a2180
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-ret-void.C
@@ -0,0 +1,34 @@
+// { dg-additional-options "-fsyntax-only -w" }
+#include "coro.h"
+
+namespace coro = std::experimental;
+
+/* Diagose missing return_void() in the promise type. */
+struct MissingRetVoid {
+ coro::coroutine_handle<> handle;
+ MissingRetVoid () : handle (nullptr) {}
+ MissingRetVoid (coro::coroutine_handle<> handle) : handle (handle) {}
+ struct missing_retvoid {
+ coro::suspend_never initial_suspend() { return {}; }
+ coro::suspend_never final_suspend() { return {}; }
+ MissingRetVoid get_return_object() {
+ return MissingRetVoid (coro::coroutine_handle<missing_retvoid>::from_promise (*this));
+ }
+ void unhandled_exception() { /*std::terminate();*/ };
+ };
+};
+
+template<> struct coro::coroutine_traits<MissingRetVoid> {
+ using promise_type = MissingRetVoid::missing_retvoid;
+};
+
+MissingRetVoid
+bar ()
+{
+ co_return; // { dg-error "no member named .return_void. in" }
+}
+
+int main (int ac, char *av[]) {
+ MissingRetVoid x = bar ();
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-1.C b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-1.C
new file mode 100644
index 0000000..964b9f1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-1.C
@@ -0,0 +1,14 @@
+// { dg-additional-options "-fsyntax-only -fexceptions -w" }
+#include "coro.h"
+#include "coro-missing-ueh.h"
+
+MissingUEH
+bar () // { dg-error {no member named 'unhandled_exception' in} }
+{
+ co_return;
+}
+
+int main (int ac, char *av[]) {
+ MissingUEH x = bar ();
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-2.C b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-2.C
new file mode 100644
index 0000000..caf8fb9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-2.C
@@ -0,0 +1,16 @@
+// { dg-additional-options "-fsyntax-only -fno-exceptions " }
+#include "coro.h"
+#include "coro-missing-ueh.h"
+
+// The missing method is warned for when exceptions are off and pedantic
+// is on (default in the testsuite).
+MissingUEH
+bar () // { dg-warning {no member named 'unhandled_exception' in} }
+{
+ co_return;
+}
+
+int main (int ac, char *av[]) {
+ MissingUEH x = bar ();
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-3.C b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-3.C
new file mode 100644
index 0000000..f6ca196
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-3.C
@@ -0,0 +1,17 @@
+// { dg-additional-options "-fsyntax-only -fno-exceptions -Wno-pedantic" }
+#include "coro.h"
+#include "coro-missing-ueh.h"
+
+/* We don't warn about the missing method, unless in pedantic mode, so
+ this compile should be clean. */
+
+MissingUEH
+bar ()
+{
+ co_return;
+}
+
+int main (int ac, char *av[]) {
+ MissingUEH x = bar ();
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh.h b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh.h
new file mode 100644
index 0000000..7a32354
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh.h
@@ -0,0 +1,25 @@
+#ifndef __MissingUEH_H
+#define __MissingUEH_H
+
+namespace coro = std::experimental;
+
+/* Diagose missing unhandled_exception() in the promise type. */
+struct MissingUEH {
+ coro::coroutine_handle<> handle;
+ MissingUEH () : handle (nullptr) {}
+ MissingUEH (coro::coroutine_handle<> handle) : handle (handle) {}
+ struct missing_ueh {
+ coro::suspend_never initial_suspend() { return {}; }
+ coro::suspend_never final_suspend() { return {}; }
+ MissingUEH get_return_object() {
+ return MissingUEH (coro::coroutine_handle<missing_ueh>::from_promise (*this));
+ }
+ void return_void () {}
+ };
+};
+
+template<> struct coro::coroutine_traits<MissingUEH> {
+ using promise_type = MissingUEH::missing_ueh;
+};
+
+#endif
diff --git a/gcc/testsuite/g++.dg/coroutines/coro-pre-proc.C b/gcc/testsuite/g++.dg/coroutines/coro-pre-proc.C
new file mode 100644
index 0000000..f22a5e0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coro-pre-proc.C
@@ -0,0 +1,9 @@
+// Only need to compile this, with the default options from the .exp.
+
+#ifndef __cpp_coroutines
+#error "coroutines should engaged."
+#endif
+
+#if __cpp_coroutines != 201902L
+#error "coroutine version out of sync."
+#endif
diff --git a/gcc/testsuite/g++.dg/coroutines/coro.h b/gcc/testsuite/g++.dg/coroutines/coro.h
new file mode 100644
index 0000000..64df497
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coro.h
@@ -0,0 +1,123 @@
+#if __has_include(<experimental/coroutine>)
+
+#include <experimental/coroutine>
+
+#else
+#ifndef __CORO_H_n4835
+#define __CORO_H_n4835
+
+// Fragments (with short-cuts) to mimic enough of the library header to
+// make some progress.
+
+#if __cpp_coroutines
+
+namespace std {
+namespace experimental {
+inline namespace coroutines_n4835 {
+
+// 21.11.1 coroutine traits
+template<typename _R, typename...> struct coroutine_traits {
+ using promise_type = typename _R::promise_type;
+};
+
+// 21.11.2 coroutine handle
+template <typename Promise = void> struct coroutine_handle;
+
+template <> struct coroutine_handle<void> {
+ public:
+ // 21.11.2.1 construct/reset
+ constexpr coroutine_handle () noexcept
+ : __fr_ptr (0) {}
+ constexpr coroutine_handle (decltype(nullptr) __h) noexcept
+ : __fr_ptr (__h) {}
+ coroutine_handle &operator= (decltype(nullptr)) noexcept {
+ __fr_ptr = nullptr;
+ return *this;
+ }
+
+ public:
+ // 21.11.2.2 export/import
+ constexpr void *address () const noexcept { return __fr_ptr; }
+ constexpr static coroutine_handle from_address (void *__a) noexcept {
+ coroutine_handle __self;
+ __self.__fr_ptr = __a;
+ return __self;
+ }
+ public:
+ // 21.11.2.3 observers
+ constexpr explicit operator bool () const noexcept {
+ return bool (__fr_ptr);
+ }
+ bool done () const noexcept {
+ return __builtin_coro_done (__fr_ptr);
+ }
+ // 21.11.2.4 resumption
+ void operator () () const { resume (); }
+ void resume () const {
+ __builtin_coro_resume (__fr_ptr);
+ }
+ void destroy () const {
+ __builtin_coro_destroy (__fr_ptr);
+ }
+ bool suspended_p () const {
+ return __builtin_coro_is_suspended (__fr_ptr);
+ }
+ protected:
+ void *__fr_ptr;
+
+ private:
+ bool __is_suspended() const noexcept {
+ return __builtin_coro_is_suspended (__fr_ptr);
+ }
+};
+
+template <class _Promise>
+struct coroutine_handle : coroutine_handle<> {
+ // 21.11.2.1 construct/reset
+ using coroutine_handle<>::coroutine_handle;
+ static coroutine_handle from_promise(_Promise &p) {
+ coroutine_handle __self;
+ __self.__fr_ptr =
+ __builtin_coro_promise((char *)&p, __alignof(_Promise), true);
+ return __self;
+ }
+ coroutine_handle& operator=(decltype(nullptr)) noexcept {
+ coroutine_handle<>::operator=(nullptr);
+ return *this;
+ }
+ // 21.11.2.2 export/import
+ constexpr static coroutine_handle from_address(void* __a){
+ coroutine_handle __self;
+ __self.__fr_ptr = __a;
+ return __self;
+ }
+ // 21.11.2.5 promise access
+ _Promise& promise() const {
+ void * __t = __builtin_coro_promise(this->__fr_ptr,
+ __alignof(_Promise), false);
+ return *static_cast<_Promise*>(__t);
+ }
+};
+
+// n4760 - 21.11.5 trivial awaitables
+
+struct suspend_always {
+ bool await_ready() { return false; }
+ void await_suspend(coroutine_handle<>) {}
+ void await_resume() {}
+};
+
+struct suspend_never {
+ bool await_ready() { return true; }
+ void await_suspend(coroutine_handle<>) {}
+ void await_resume() {}
+};
+
+}}} // namespace std::experimental::coroutines_n4775
+
+#else
+# error "coro.h requires support for coroutines, add -fcoroutines"
+#endif
+#endif // __CORO_H_n4835
+
+#endif
diff --git a/gcc/testsuite/g++.dg/coroutines/coroutines.exp b/gcc/testsuite/g++.dg/coroutines/coroutines.exp
new file mode 100644
index 0000000..0696cc8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coroutines.exp
@@ -0,0 +1,50 @@
+# Copyright (C) 2018-2019 Free Software Foundation, Inc.
+
+# Contributed by Iain Sandoe <iain@sandoe.co.uk> under contract to Facebook.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3. If not see
+# <http://www.gnu.org/licenses/>.
+
+# Test C++ coroutines, requires c++17; doesn't, at present, seem much
+# point in repeating these for other versions.
+
+# Load support procs.
+load_lib g++-dg.exp
+
+# If a testcase doesn't have special options, use these.
+global DEFAULT_CXXFLAGS
+if ![info exists DEFAULT_CXXFLAGS] then {
+ set DEFAULT_CXXFLAGS " -pedantic-errors -Wno-long-long"
+}
+
+set DEFAULT_COROFLAGS $DEFAULT_CXXFLAGS
+lappend DEFAULT_COROFLAGS "-std=c++17" "-fcoroutines"
+
+dg-init
+
+# Run the tests.
+# g++-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.C]] \
+# "" $DEFAULT_COROFLAGS
+
+foreach test [lsort [find $srcdir/$subdir {*.[CH]}]] {
+ if [runtest_file_p $runtests $test] {
+ set nshort [file tail [file dirname $test]]/[file tail $test]
+ verbose "Testing $nshort $DEFAULT_COROFLAGS" 1
+ dg-test $test "" $DEFAULT_COROFLAGS
+ set testcase [string range $test [string length "$srcdir/"] end]
+ }
+}
+
+# done.
+dg-finish
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-0-triv.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-0-triv.C
new file mode 100644
index 0000000..000d083
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-0-triv.C
@@ -0,0 +1,144 @@
+// { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+# define PRINT(X)
+# define PRINTF (void)
+#else
+# define PRINT(X) puts(X)
+# define PRINTF printf
+#endif
+
+struct coro1 {
+ struct promise_type;
+ using handle_type = coro::coroutine_handle<coro1::promise_type>;
+ handle_type handle;
+ coro1 () : handle(0) {}
+ coro1 (handle_type _handle)
+ : handle(_handle) {
+ puts("Created coro1 object from handle");
+ }
+ coro1 (const coro1 &) = delete; // no copying
+ coro1 (coro1 &&s) : handle(s.handle) {
+ s.handle = nullptr;
+ puts("Moved coro1");
+ }
+ coro1 &operator = (coro1 &&s) {
+ handle = s.handle;
+ s.handle = nullptr;
+ return *this;
+ }
+ ~coro1() {
+ puts("Destroyed coro1");
+ if ( handle )
+ handle.destroy();
+ }
+
+ struct suspend_never_prt {
+ ~suspend_never_prt() {}
+ bool await_ready() const noexcept { return true; }
+ void await_suspend(handle_type h) const noexcept { PRINT ("susp-never-susp");}
+ void await_resume() const noexcept {PRINT ("susp-never-resume");}
+ };
+
+ struct suspend_always_prt {
+ int x;
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp");}
+ void await_resume() const noexcept {PRINT ("susp-always-resume");}
+ };
+
+ /* This returns an int. */
+ struct suspend_always_intprt {
+ int x;
+ suspend_always_intprt() : x(5) {}
+ ~suspend_always_intprt() {}
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(coro::coroutine_handle<>) const noexcept { puts ("susp-always-susp-int");}
+ int await_resume() const noexcept {puts ("susp-always-resume-int"); return x;}
+ };
+
+ struct promise_type {
+ int value;
+ promise_type() { PRINT ("Created Promise"); }
+ ~promise_type() { PRINT ("Destroyed Promise"); }
+
+ coro1 get_return_object() {
+ PRINT ("get_return_object: from handle from promise");
+ return coro1 (handle_type::from_promise (*this));
+ }
+
+ auto initial_suspend() {
+ PRINT ("get initial_suspend ");
+ return suspend_never_prt{};
+ }
+
+ auto final_suspend() {
+ PRINT ("get final_suspend");
+ return suspend_always_prt{};
+ }
+
+ void return_value (int v) {
+ PRINTF ("return_value () %d\n",v);
+ value = v;
+ }
+
+ int get_value () { return value; }
+ // Placeholder to satisfy parser, not doing exceptions yet.
+ void unhandled_exception() { /*exit(1);*/ }
+ };
+};
+
+/* The simplest valued co_await we can do. */
+int gX = 1;
+
+coro1
+f ()
+{
+ co_await coro1::suspend_always_prt{};
+ co_return gX + 10;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 f_coro = f ();
+ PRINT ("main: got coro1 - checking gX");
+ if (gX != 1)
+ {
+ PRINTF ("main: gX is wrong : %d, should be 1\n", gX);
+ abort ();
+ }
+ if (f_coro.handle.done())
+ {
+ PRINT ("main: we should not be 'done' [1]");
+ abort ();
+ }
+ PRINT ("main: resuming [1]");
+ f_coro.handle.resume();
+ /* we should now have returned with the co_return (15) */
+ if (!f_coro.handle.done())
+ {
+ PRINT ("main: we should be 'done' ");
+ abort ();
+ }
+ int y = f_coro.handle.promise().get_value();
+ if (y != 11)
+ {
+ PRINTF ("main: y is wrong : %d, should be 11\n", y);
+ abort ();
+ }
+ puts ("main: done");
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-1-value.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-1-value.C
new file mode 100644
index 0000000..cdc7b92
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-1-value.C
@@ -0,0 +1,149 @@
+// { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+# define PRINT(X)
+# define PRINTF (void)
+#else
+# define PRINT(X) puts(X)
+# define PRINTF printf
+#endif
+
+struct coro1 {
+ struct promise_type;
+ using handle_type = coro::coroutine_handle<coro1::promise_type>;
+ handle_type handle;
+ coro1 () : handle(0) {}
+ coro1 (handle_type _handle)
+ : handle(_handle) {
+ puts("Created coro1 object from handle");
+ }
+ coro1 (const coro1 &) = delete; // no copying
+ coro1 (coro1 &&s) : handle(s.handle) {
+ s.handle = nullptr;
+ PRINT("Moved coro1");
+ }
+ coro1 &operator = (coro1 &&s) {
+ handle = s.handle;
+ s.handle = nullptr;
+ return *this;
+ }
+ ~coro1() {
+ PRINT("Destroyed coro1");
+ if ( handle )
+ handle.destroy();
+ }
+
+ struct suspend_never_prt {
+ ~suspend_never_prt() {}
+ bool await_ready() const noexcept { return true; }
+ void await_suspend(handle_type h) const noexcept { PRINT ("susp-never-susp");}
+ void await_resume() const noexcept {PRINT ("susp-never-resume");}
+ };
+
+ struct suspend_always_prt {
+ int x;
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp");}
+ void await_resume() const noexcept {PRINT ("susp-always-resume");}
+ };
+
+ /* This returns an int. */
+ struct suspend_always_intprt {
+ int x;
+ suspend_always_intprt() : x(5) {}
+ ~suspend_always_intprt() {}
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(coro::coroutine_handle<>) const noexcept { puts ("susp-always-susp-int");}
+ int await_resume() const noexcept {puts ("susp-always-resume-int"); return x;}
+ };
+
+ struct promise_type {
+ int value;
+ promise_type() { PRINT ("Created Promise"); }
+ ~promise_type() { PRINT ("Destroyed Promise"); }
+
+ coro1 get_return_object() {
+ PRINT ("get_return_object: from handle from promise");
+ return coro1 (handle_type::from_promise (*this));
+ }
+
+ auto initial_suspend() {
+ PRINT ("get initial_suspend ");
+ return suspend_never_prt{};
+ }
+
+ auto final_suspend() {
+ PRINT ("get final_suspend");
+ return suspend_always_prt{};
+ }
+
+ void return_value (int v) {
+ PRINTF ("return_value () %d\n",v);
+ value = v;
+ }
+
+ int get_value () { return value; }
+ // Placeholder to satisfy parser, not doing exceptions yet.
+ void unhandled_exception() { /*exit(1);*/ }
+ };
+};
+
+/* The simplest valued co_await we can do. */
+int gX = 1;
+
+coro1
+f ()
+{
+ gX = co_await coro1::suspend_always_intprt{};
+ co_return gX + 10;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 f_coro = f ();
+ PRINT ("main: got coro1 - checking gX");
+ if (gX != 1)
+ {
+ PRINTF ("main: gX is wrong : %d, should be 1\n", gX);
+ abort ();
+ }
+ if (f_coro.handle.done())
+ {
+ PRINT ("main: we should not be 'done' [1]");
+ abort ();
+ }
+ PRINT ("main: resuming [1]");
+ f_coro.handle.resume();
+ if (gX != 5)
+ {
+ PRINTF ("main: gX is wrong : %d, should be 5\n", gX);
+ abort ();
+ }
+ /* we should now have returned with the co_return (15) */
+ if (!f_coro.handle.done())
+ {
+ PRINT ("main: we should be 'done' ");
+ abort ();
+ }
+ int y = f_coro.handle.promise().get_value();
+ if (y != 15)
+ {
+ PRINTF ("main: y is wrong : %d, should be 15\n", y);
+ abort ();
+ }
+ puts ("main: done");
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-2-xform.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-2-xform.C
new file mode 100644
index 0000000..3b1bb71
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-2-xform.C
@@ -0,0 +1,155 @@
+// { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+# define PRINT(X)
+# define PRINTF (void)
+#else
+# define PRINT(X) puts(X)
+# define PRINTF printf
+#endif
+
+struct coro1 {
+ struct promise_type;
+ using handle_type = coro::coroutine_handle<coro1::promise_type>;
+ handle_type handle;
+ coro1 () : handle(0) {}
+ coro1 (handle_type _handle)
+ : handle(_handle) {
+ PRINT ("Created coro1 object from handle");
+ }
+ coro1 (const coro1 &) = delete; // no copying
+ coro1 (coro1 &&s) : handle(s.handle) {
+ s.handle = nullptr;
+ PRINT ("Moved coro1");
+ }
+ coro1 &operator = (coro1 &&s) {
+ handle = s.handle;
+ s.handle = nullptr;
+ return *this;
+ }
+ ~coro1() {
+ PRINT ("Destroyed coro1");
+ if ( handle )
+ handle.destroy();
+ }
+
+ struct suspend_never_prt {
+ ~suspend_never_prt() {}
+ bool await_ready() const noexcept { return true; }
+ void await_suspend(handle_type h) const noexcept { PRINT ("susp-never-susp");}
+ void await_resume() const noexcept {PRINT ("susp-never-resume");}
+ };
+
+ struct suspend_always_prt {
+ int x;
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp");}
+ void await_resume() const noexcept {PRINT ("susp-always-resume");}
+ };
+
+ /* This returns an int. */
+ struct suspend_always_intprt {
+ int x;
+ suspend_always_intprt() : x(5) { PRINT ("suspend_always_intprt def ctor"); }
+ suspend_always_intprt(int _x) : x(_x) { PRINTF ("suspend_always_intprt ctor with %d\n", x); }
+ ~suspend_always_intprt() {}
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(coro::coroutine_handle<>) const noexcept { PRINT ("susp-always-susp-int");}
+ int await_resume() const noexcept { PRINT ("susp-always-resume-int"); return x;}
+ };
+
+ struct promise_type {
+ int value;
+ promise_type() { PRINT ("Created Promise"); }
+ ~promise_type() { PRINT ("Destroyed Promise"); }
+
+ coro1 get_return_object() {
+ PRINT ("get_return_object: from handle from promise");
+ return coro1 (handle_type::from_promise (*this));
+ }
+
+ auto initial_suspend() {
+ PRINT ("get initial_suspend ");
+ return suspend_never_prt{};
+ }
+
+ auto final_suspend() {
+ PRINT ("get final_suspend");
+ return suspend_always_prt{};
+ }
+
+ void return_value (int v) {
+ PRINTF ("return_value () %d\n",v);
+ value = v;
+ }
+
+ auto await_transform (int v) {
+ PRINTF ("await_transform an int () %d\n",v);
+ return suspend_always_intprt (v);
+ }
+
+ int get_value () { return value; }
+ // Placeholder to satisfy parser, not doing exceptions yet.
+ void unhandled_exception() { /*exit(1);*/ }
+ };
+};
+
+/* Valued with an await_transform. */
+int gX = 1;
+
+coro1
+f ()
+{
+ gX = co_await 11;
+ co_return gX + 31;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 f_coro = f ();
+ PRINT ("main: got coro1 - checking gX");
+ if (gX != 1)
+ {
+ PRINTF ("main: gX is wrong : %d, should be 1\n", gX);
+ abort ();
+ }
+ if (f_coro.handle.done())
+ {
+ PRINT ("main: we should not be 'done' [1]");
+ abort ();
+ }
+ PRINT ("main: resuming [1]");
+ f_coro.handle.resume();
+ if (gX != 11)
+ {
+ PRINTF ("main: gX is wrong : %d, should be 11\n", gX);
+ abort ();
+ }
+ /* we should now have returned with the co_return (15) */
+ if (!f_coro.handle.done())
+ {
+ PRINT ("main: we should be 'done' ");
+ abort ();
+ }
+ int y = f_coro.handle.promise().get_value();
+ if (y != 42)
+ {
+ PRINTF ("main: y is wrong : %d, should be 42\n", y);
+ abort ();
+ }
+ puts ("main: done");
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-3-rhs-op.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-3-rhs-op.C
new file mode 100644
index 0000000..1fb54f9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-3-rhs-op.C
@@ -0,0 +1,155 @@
+// { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+# define PRINT(X)
+# define PRINTF (void)
+#else
+# define PRINT(X) puts(X)
+# define PRINTF printf
+#endif
+
+struct coro1 {
+ struct promise_type;
+ using handle_type = coro::coroutine_handle<coro1::promise_type>;
+ handle_type handle;
+ coro1 () : handle(0) {}
+ coro1 (handle_type _handle)
+ : handle(_handle) {
+ PRINT ("Created coro1 object from handle");
+ }
+ coro1 (const coro1 &) = delete; // no copying
+ coro1 (coro1 &&s) : handle(s.handle) {
+ s.handle = nullptr;
+ PRINT ("Moved coro1");
+ }
+ coro1 &operator = (coro1 &&s) {
+ handle = s.handle;
+ s.handle = nullptr;
+ return *this;
+ }
+ ~coro1() {
+ PRINT ("Destroyed coro1");
+ if ( handle )
+ handle.destroy();
+ }
+
+ struct suspend_never_prt {
+ ~suspend_never_prt() {}
+ bool await_ready() const noexcept { return true; }
+ void await_suspend(handle_type h) const noexcept { PRINT ("susp-never-susp");}
+ void await_resume() const noexcept {PRINT ("susp-never-resume");}
+ };
+
+ struct suspend_always_prt {
+ int x;
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp");}
+ void await_resume() const noexcept {PRINT ("susp-always-resume");}
+ };
+
+ /* This returns an int. */
+ struct suspend_always_intprt {
+ int x;
+ suspend_always_intprt() : x(5) { PRINT ("suspend_always_intprt def ctor"); }
+ suspend_always_intprt(int _x) : x(_x) { PRINTF ("suspend_always_intprt ctor with %d\n", x); }
+ ~suspend_always_intprt() {}
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(coro::coroutine_handle<>) const noexcept { PRINT ("susp-always-susp-int");}
+ int await_resume() const noexcept { PRINT ("susp-always-resume-int"); return x;}
+ };
+
+ struct promise_type {
+ int value;
+ promise_type() { PRINT ("Created Promise"); }
+ ~promise_type() { PRINT ("Destroyed Promise"); }
+
+ coro1 get_return_object() {
+ PRINT ("get_return_object: from handle from promise");
+ return coro1 (handle_type::from_promise (*this));
+ }
+
+ auto initial_suspend() {
+ PRINT ("get initial_suspend ");
+ return suspend_never_prt{};
+ }
+
+ auto final_suspend() {
+ PRINT ("get final_suspend");
+ return suspend_always_prt{};
+ }
+
+ void return_value (int v) {
+ PRINTF ("return_value () %d\n",v);
+ value = v;
+ }
+
+ auto await_transform (int v) {
+ PRINTF ("await_transform an int () %d\n",v);
+ return suspend_always_intprt (v);
+ }
+
+ int get_value () { return value; }
+ // Placeholder to satisfy parser, not doing exceptions yet.
+ void unhandled_exception() { /*exit(1);*/ }
+ };
+};
+
+/* Valued with an await_transform. */
+int gX = 1;
+
+coro1
+f ()
+{
+ gX = co_await 11 + 15;
+ co_return gX + 31;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 f_coro = f ();
+ PRINT ("main: got coro1 - checking gX");
+ if (gX != 1)
+ {
+ PRINTF ("main: gX is wrong : %d, should be 1\n", gX);
+ abort ();
+ }
+ if (f_coro.handle.done())
+ {
+ PRINT ("main: we should not be 'done' [1]");
+ abort ();
+ }
+ PRINT ("main: resuming [1]");
+ f_coro.handle.resume();
+ if (gX != 26)
+ {
+ PRINTF ("main: gX is wrong : %d, should be 26\n", gX);
+ abort ();
+ }
+ /* we should now have returned with the co_return (15) */
+ if (!f_coro.handle.done())
+ {
+ PRINT ("main: we should be 'done' ");
+ abort ();
+ }
+ int y = f_coro.handle.promise().get_value();
+ if (y != 57)
+ {
+ PRINTF ("main: y is wrong : %d, should be 57\n", y);
+ abort ();
+ }
+ puts ("main: done");
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-4-control-flow.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-4-control-flow.C
new file mode 100644
index 0000000..594dfe5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-4-control-flow.C
@@ -0,0 +1,148 @@
+// { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+# define PRINT(X)
+# define PRINTF (void)
+#else
+# define PRINT(X) puts(X)
+# define PRINTF printf
+#endif
+
+struct coro1 {
+ struct promise_type;
+ using handle_type = coro::coroutine_handle<coro1::promise_type>;
+ handle_type handle;
+ coro1 () : handle(0) {}
+ coro1 (handle_type _handle)
+ : handle(_handle) {
+ PRINT ("Created coro1 object from handle");
+ }
+ coro1 (const coro1 &) = delete; // no copying
+ coro1 (coro1 &&s) : handle(s.handle) {
+ s.handle = nullptr;
+ PRINT ("Moved coro1");
+ }
+ coro1 &operator = (coro1 &&s) {
+ handle = s.handle;
+ s.handle = nullptr;
+ return *this;
+ }
+ ~coro1() {
+ PRINT ("Destroyed coro1");
+ if ( handle )
+ handle.destroy();
+ }
+
+ struct suspend_never_prt {
+ ~suspend_never_prt() {}
+ bool await_ready() const noexcept { return true; }
+ void await_suspend(handle_type h) const noexcept { PRINT ("susp-never-susp");}
+ void await_resume() const noexcept {PRINT ("susp-never-resume");}
+ };
+
+ struct suspend_always_prt {
+ int x;
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp");}
+ void await_resume() const noexcept {PRINT ("susp-always-resume");}
+ };
+
+ /* This returns an int. */
+ struct suspend_always_intprt {
+ int x;
+ suspend_always_intprt() : x(5) { PRINT ("suspend_always_intprt def ctor"); }
+ suspend_always_intprt(int _x) : x(_x) { PRINTF ("suspend_always_intprt ctor with %d\n", x); }
+ ~suspend_always_intprt() {}
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(coro::coroutine_handle<>) const noexcept { PRINT ("susp-always-susp-int");}
+ int await_resume() const noexcept { PRINT ("susp-always-resume-int"); return x;}
+ };
+
+ struct promise_type {
+ int value;
+ promise_type() { PRINT ("Created Promise"); }
+ ~promise_type() { PRINT ("Destroyed Promise"); }
+
+ coro1 get_return_object() {
+ PRINT ("get_return_object: from handle from promise");
+ return coro1 (handle_type::from_promise (*this));
+ }
+
+ auto initial_suspend() {
+ PRINT ("get initial_suspend ");
+ return suspend_never_prt{};
+ }
+
+ auto final_suspend() {
+ PRINT ("get final_suspend");
+ return suspend_always_prt{};
+ }
+
+ void return_value (int v) {
+ PRINTF ("return_value () %d\n",v);
+ value = v;
+ }
+
+ auto await_transform (int v) {
+ PRINTF ("await_transform an int () %d\n",v);
+ return suspend_always_intprt (v);
+ }
+
+ int get_value () { return value; }
+ // Placeholder to satisfy parser, not doing exceptions yet.
+ void unhandled_exception() { /*exit(1);*/ }
+ };
+};
+
+/* Valued with an await_transform. */
+int gX = 1;
+int y = 30;
+
+coro1
+f ()
+{
+ if (gX < 12) {
+ gX += y;
+ gX += co_await 11;
+ } else
+ gX += co_await 12;
+
+ co_return gX;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 f_coro = f ();
+ PRINT ("main: got coro1 - checking gX");
+ if (gX != 1)
+ {
+ PRINTF ("main: gX is wrong : %d, should be 1\n", gX);
+ // abort ();
+ }
+ PRINT ("main: gX OK -- looping");
+ do {
+ //PRINTF ("main: gX : %d \n", gX);
+ f_coro.handle.resume();
+ } while (!f_coro.handle.done());
+ int y = f_coro.handle.promise().get_value();
+ if (y != 42)
+ {
+ PRINTF ("main: y is wrong : %d, should be 42\n", y);
+ //abort ();
+ }
+ puts ("main: done");
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-5-loop.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-5-loop.C
new file mode 100644
index 0000000..86223be
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-5-loop.C
@@ -0,0 +1,147 @@
+// { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+# define PRINT(X)
+# define PRINTF (void)
+#else
+# define PRINT(X) puts(X)
+# define PRINTF printf
+#endif
+
+struct coro1 {
+ struct promise_type;
+ using handle_type = coro::coroutine_handle<coro1::promise_type>;
+ handle_type handle;
+ coro1 () : handle(0) {}
+ coro1 (handle_type _handle)
+ : handle(_handle) {
+ PRINT ("Created coro1 object from handle");
+ }
+ coro1 (const coro1 &) = delete; // no copying
+ coro1 (coro1 &&s) : handle(s.handle) {
+ s.handle = nullptr;
+ PRINT ("Moved coro1");
+ }
+ coro1 &operator = (coro1 &&s) {
+ handle = s.handle;
+ s.handle = nullptr;
+ return *this;
+ }
+ ~coro1() {
+ PRINT ("Destroyed coro1");
+ if ( handle )
+ handle.destroy();
+ }
+
+ struct suspend_never_prt {
+ ~suspend_never_prt() {}
+ bool await_ready() const noexcept { return true; }
+ void await_suspend(handle_type h) const noexcept { PRINT ("susp-never-susp");}
+ void await_resume() const noexcept {PRINT ("susp-never-resume");}
+ };
+
+ struct suspend_always_prt {
+ int x;
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp");}
+ void await_resume() const noexcept {PRINT ("susp-always-resume");}
+ };
+
+ /* This returns an int. */
+ struct suspend_always_intprt {
+ int x;
+ suspend_always_intprt() : x(5) { PRINT ("suspend_always_intprt def ctor"); }
+ suspend_always_intprt(int _x) : x(_x) { PRINTF ("suspend_always_intprt ctor with %d\n", x); }
+ ~suspend_always_intprt() {}
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(coro::coroutine_handle<>) const noexcept { PRINT ("susp-always-susp-int");}
+ int await_resume() const noexcept { PRINT ("susp-always-resume-int"); return x;}
+ };
+
+ struct promise_type {
+ int value;
+ promise_type() { PRINT ("Created Promise"); }
+ ~promise_type() { PRINT ("Destroyed Promise"); }
+
+ coro1 get_return_object() {
+ PRINT ("get_return_object: from handle from promise");
+ return coro1 (handle_type::from_promise (*this));
+ }
+
+ auto initial_suspend() {
+ PRINT ("get initial_suspend ");
+ return suspend_never_prt{};
+ }
+
+ auto final_suspend() {
+ PRINT ("get final_suspend");
+ return suspend_always_prt{};
+ }
+
+ void return_value (int v) {
+ PRINTF ("return_value () %d\n",v);
+ value = v;
+ }
+
+ auto await_transform (int v) {
+ PRINTF ("await_transform an int () %d\n",v);
+ return suspend_always_intprt (v);
+ }
+
+ int get_value () { return value; }
+ // Placeholder to satisfy parser, not doing exceptions yet.
+ void unhandled_exception() { /*exit(1);*/ }
+ };
+};
+
+/* Valued with an await_transform. */
+int gX = 1;
+
+coro1
+f ()
+{
+ for (;;)
+ {
+ gX += co_await 11;
+ if (gX > 100)
+ break;
+ }
+ co_return gX;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 f_coro = f ();
+ PRINT ("main: got coro1 - checking gX");
+ if (gX != 1)
+ {
+ PRINTF ("main: gX is wrong : %d, should be 1\n", gX);
+ // abort ();
+ }
+ PRINT ("main: gX OK -- looping");
+ do {
+ //PRINTF ("main: gX : %d \n", gX);
+ f_coro.handle.resume();
+ } while (!f_coro.handle.done());
+ int y = f_coro.handle.promise().get_value();
+ if (y != 42)
+ {
+ PRINTF ("main: y is wrong : %d, should be 42\n", y);
+ //abort ();
+ }
+ puts ("main: done");
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-6-ovl.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-6-ovl.C
new file mode 100644
index 0000000..fc67246
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-6-ovl.C
@@ -0,0 +1,153 @@
+// { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+# define PRINT(X)
+# define PRINTF (void)
+#else
+# define PRINT(X) puts(X)
+# define PRINTF printf
+#endif
+
+
+struct coro1 {
+ struct promise_type;
+ using handle_type = coro::coroutine_handle<coro1::promise_type>;
+ handle_type handle;
+ coro1 () : handle(0) {}
+ coro1 (handle_type _handle)
+ : handle(_handle) {
+ PRINT("Created coro1 object from handle");
+ }
+ coro1 (const coro1 &) = delete; // no copying
+ coro1 (coro1 &&s) : handle(s.handle) {
+ s.handle = nullptr;
+ PRINT("Moved coro1");
+ }
+ coro1 &operator = (coro1 &&s) {
+ handle = s.handle;
+ s.handle = nullptr;
+ return *this;
+ }
+ ~coro1() {
+ PRINT("Destroyed coro1");
+ if ( handle )
+ handle.destroy();
+ }
+
+ struct suspend_never_prt {
+ ~suspend_never_prt() {}
+ bool await_ready() const noexcept { return true; }
+ void await_suspend(handle_type h) const noexcept { PRINT ("susp-never-susp");}
+ void await_resume() const noexcept {PRINT ("susp-never-resume");}
+ };
+
+ struct suspend_always_prt {
+ int x;
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp");}
+ void await_resume() const noexcept {PRINT ("susp-always-resume");}
+ };
+
+ /* This returns an int. */
+ struct suspend_always_intprt {
+ int x;
+ suspend_always_intprt() : x(5) {}
+ ~suspend_always_intprt() {}
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(coro::coroutine_handle<>) const noexcept { puts ("susp-always-susp-int");}
+ int await_resume() const noexcept {puts ("susp-always-resume-int"); return x;}
+ };
+
+ struct promise_type {
+ int value;
+ promise_type() { PRINT ("Created Promise"); }
+ ~promise_type() { PRINT ("Destroyed Promise"); }
+
+ coro1 get_return_object() {
+ PRINT ("get_return_object: from handle from promise");
+ return coro1 (handle_type::from_promise (*this));
+ }
+
+ auto initial_suspend() {
+ PRINT ("get initial_suspend ");
+ return suspend_never_prt{};
+ }
+
+ auto final_suspend() {
+ PRINT ("get final_suspend");
+ return suspend_always_prt{};
+ }
+
+ void return_value (int v) {
+ PRINTF ("return_value () %d\n",v);
+ value = v;
+ }
+
+ int get_value () { return value; }
+ // Placeholder to satisfy parser, not doing exceptions yet.
+ void unhandled_exception() { /*exit(1);*/ }
+ };
+
+ struct empty {
+ auto operator co_await() const noexcept {
+ return suspend_always_prt{};
+ }
+ };
+};
+
+
+/* The simplest valued co_await we can do. */
+int gX = 1;
+coro1::empty e{};
+
+coro1
+f ()
+{
+ co_await (e); /* overload */
+ co_return gX + 10;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 f_coro = f ();
+ PRINT ("main: got coro1 - checking gX");
+ if (gX != 1)
+ {
+ PRINTF ("main: gX is wrong : %d, should be 1\n", gX);
+ abort ();
+ }
+ if (f_coro.handle.done())
+ {
+ PRINT ("main: we should not be 'done' [1]");
+ abort ();
+ }
+ PRINT ("main: resuming [1]");
+ f_coro.handle.resume();
+ /* we should now have returned with the co_return (15) */
+ if (!f_coro.handle.done())
+ {
+ PRINT ("main: we should be 'done' ");
+ abort ();
+ }
+ int y = f_coro.handle.promise().get_value();
+ if (y != 11)
+ {
+ PRINTF ("main: y is wrong : %d, should be 11\n", y);
+ abort ();
+ }
+ puts ("main: done");
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-7-tmpl.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-7-tmpl.C
new file mode 100644
index 0000000..032e35f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-7-tmpl.C
@@ -0,0 +1,149 @@
+// { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+# define PRINT(X)
+# define PRINTF (void)
+#else
+# define PRINT(X) puts(X)
+# define PRINTF printf
+#endif
+
+template <typename T>
+struct coro1 {
+ struct promise_type;
+ using handle_type = coro::coroutine_handle<coro1::promise_type>;
+ handle_type handle;
+ coro1 () : handle(0) {}
+ coro1 (handle_type _handle)
+ : handle(_handle) {
+ PRINT ("Created coro1 object from handle");
+ }
+ coro1 (const coro1 &) = delete; // no copying
+ coro1 (coro1 &&s) : handle(s.handle) {
+ s.handle = nullptr;
+ PRINT ("Moved coro1");
+ }
+ coro1 &operator = (coro1 &&s) {
+ handle = s.handle;
+ s.handle = nullptr;
+ return *this;
+ }
+ ~coro1() {
+ PRINT ("Destroyed coro1");
+ if ( handle )
+ handle.destroy();
+ }
+
+ struct suspend_never_prt {
+ ~suspend_never_prt() {}
+ bool await_ready() const noexcept { return true; }
+ void await_suspend(handle_type h) const noexcept { PRINT ("susp-never-susp");}
+ void await_resume() const noexcept {PRINT ("susp-never-resume");}
+ };
+
+ struct suspend_always_prt {
+ T x;
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp");}
+ void await_resume() const noexcept {PRINT ("susp-always-resume");}
+ };
+
+ /* This returns an int. */
+ struct suspend_always_intprt {
+ T x;
+ suspend_always_intprt() : x(5) { PRINT ("suspend_always_intprt def ctor"); }
+ suspend_always_intprt(T _x) : x(_x)
+ { PRINTF ("suspend_always_intprt ctor with %ld\n", (long)x); }
+ ~suspend_always_intprt() {}
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(coro::coroutine_handle<>) const noexcept { PRINT ("susp-always-susp-int");}
+ int await_resume() const noexcept { PRINT ("susp-always-resume-int"); return x;}
+ };
+
+ struct promise_type {
+ T value;
+ promise_type() { PRINT ("Created Promise"); }
+ ~promise_type() { PRINT ("Destroyed Promise"); }
+
+ coro1 get_return_object() {
+ PRINT ("get_return_object: from handle from promise");
+ return coro1 (handle_type::from_promise (*this));
+ }
+
+ auto initial_suspend() {
+ PRINT ("get initial_suspend ");
+ return suspend_never_prt{};
+ }
+
+ auto final_suspend() {
+ PRINT ("get final_suspend");
+ return suspend_always_prt{};
+ }
+
+ void return_value (int v) {
+ PRINTF ("return_value () %ld\n", (long) v);
+ value = v;
+ }
+
+ auto await_transform (T v) {
+ PRINTF ("await_transform a T () %ld\n", (long)v);
+ return suspend_always_intprt (v);
+ }
+
+ T get_value () { return value; }
+ // Placeholder to satisfy parser, not doing exceptions yet.
+ void unhandled_exception() { /*exit(1);*/ }
+ };
+};
+
+/* Valued with an await_transform. */
+int gX = 2;
+
+template <typename T>
+coro1<T> f ()
+{
+ for (int i = 0; i < 4; ++i)
+ {
+ gX += co_await 10;
+ }
+ co_return gX;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ auto f_coro = f<int>();
+
+ PRINT ("main: got coro1 - checking gX");
+ if (gX != 2)
+ {
+ PRINTF ("main: gX is wrong : %d, should be 2\n", gX);
+ abort ();
+ }
+ PRINT ("main: gX OK -- looping");
+ do {
+ f_coro.handle.resume();
+ } while (!f_coro.handle.done());
+
+ int y = f_coro.handle.promise().get_value();
+
+ if (y != 42)
+ {
+ PRINTF ("main: y is wrong : %d, should be 42\n", y);
+ abort ();
+ }
+ puts ("main: done");
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-8-cascade.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-8-cascade.C
new file mode 100644
index 0000000..f3504d0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-8-cascade.C
@@ -0,0 +1,157 @@
+// { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+# define PRINT(X)
+# define PRINTF (void)
+#else
+# define PRINT(X) puts(X)
+# define PRINTF printf
+#endif
+
+struct coro1 {
+ struct promise_type;
+ using handle_type = coro::coroutine_handle<coro1::promise_type>;
+ handle_type handle;
+ coro1 () : handle(0) {}
+ coro1 (handle_type _handle)
+ : handle(_handle) {
+ PRINT ("Created coro1 object from handle");
+ }
+ coro1 (const coro1 &) = delete; // no copying
+ coro1 (coro1 &&s) : handle(s.handle) {
+ s.handle = nullptr;
+ PRINT ("Moved coro1");
+ }
+ coro1 &operator = (coro1 &&s) {
+ handle = s.handle;
+ s.handle = nullptr;
+ return *this;
+ }
+ ~coro1() {
+ PRINT ("Destroyed coro1");
+ if ( handle )
+ handle.destroy();
+ }
+
+ struct suspend_never_prt {
+ ~suspend_never_prt() {}
+ bool await_ready() const noexcept { return true; }
+ void await_suspend(handle_type h) const noexcept { PRINT ("susp-never-susp");}
+ void await_resume() const noexcept {PRINT ("susp-never-resume");}
+ };
+
+ struct suspend_always_prt {
+ int x;
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp");}
+ void await_resume() const noexcept {PRINT ("susp-always-resume");}
+ };
+
+ /* This returns an int. */
+ struct suspend_always_intprt {
+ int x;
+ suspend_always_intprt() : x(5) { PRINT ("suspend_always_intprt def ctor"); }
+ suspend_always_intprt(int _x) : x(_x) { PRINTF ("suspend_always_intprt ctor with %d\n", x); }
+ ~suspend_always_intprt() {}
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(coro::coroutine_handle<>) const noexcept { PRINT ("susp-always-susp-int");}
+ int await_resume() const noexcept { PRINT ("susp-always-resume-int"); return x * x;}
+ };
+
+ struct promise_type {
+ int value;
+ promise_type() { PRINT ("Created Promise"); }
+ ~promise_type() { PRINT ("Destroyed Promise"); }
+
+ coro1 get_return_object() {
+ PRINT ("get_return_object: from handle from promise");
+ return coro1 (handle_type::from_promise (*this));
+ }
+
+ auto initial_suspend() {
+ PRINT ("get initial_suspend ");
+ return suspend_never_prt{};
+ }
+
+ auto final_suspend() {
+ PRINT ("get final_suspend");
+ return suspend_always_prt{};
+ }
+
+ void return_value (int v) {
+ PRINTF ("return_value () %d\n",v);
+ value = v;
+ }
+
+ auto await_transform (int v) {
+ PRINTF ("await_transform an int () %d\n",v);
+ return suspend_always_intprt (v);
+ }
+
+ int get_value () { return value; }
+ // Placeholder to satisfy parser, not doing exceptions yet.
+ void unhandled_exception() { /*exit(1);*/ }
+ };
+};
+
+/* Valued with an await_transform. */
+int gX = 1;
+coro1 f ()
+{
+ /* the await transform takes an int, the await_resume squares it.
+ so we get 11 ** 4, 14641. */
+ gX = co_await co_await 11;
+ co_return gX + 31;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 f_coro = f ();
+ PRINT ("main: got coro1 - checking gX");
+ if (gX != 1)
+ {
+ PRINTF ("main: gX is wrong : %d, should be 1\n", gX);
+ abort ();
+ }
+ if (f_coro.handle.done())
+ {
+ PRINT ("main: we should not be 'done' [1]");
+ abort ();
+ }
+ PRINT ("main: resuming [1] - nested");
+ f_coro.handle.resume();
+ PRINT ("main: resuming [2] - outer");
+ f_coro.handle.resume();
+ if (gX != 14641)
+ {
+ PRINTF ("main: gX is wrong : %d, should be 14641\n", gX);
+ abort ();
+ }
+ /* we should now have returned with the co_return (14672) */
+ if (!f_coro.handle.done())
+ {
+ PRINT ("main: we should be 'done' ");
+ abort ();
+ }
+ int y = f_coro.handle.promise().get_value();
+ if (y != 14672)
+ {
+ PRINTF ("main: y is wrong : %d, should be 14672\n", y);
+ abort ();
+ }
+ puts ("main: done");
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-9-pair.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-9-pair.C
new file mode 100644
index 0000000..8713adf
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-9-pair.C
@@ -0,0 +1,155 @@
+// { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+# define PRINT(X)
+# define PRINTF (void)
+#else
+# define PRINT(X) puts(X)
+# define PRINTF printf
+#endif
+
+struct coro1 {
+ struct promise_type;
+ using handle_type = coro::coroutine_handle<coro1::promise_type>;
+ handle_type handle;
+ coro1 () : handle(0) {}
+ coro1 (handle_type _handle)
+ : handle(_handle) {
+ PRINT ("Created coro1 object from handle");
+ }
+ coro1 (const coro1 &) = delete; // no copying
+ coro1 (coro1 &&s) : handle(s.handle) {
+ s.handle = nullptr;
+ PRINT ("Moved coro1");
+ }
+ coro1 &operator = (coro1 &&s) {
+ handle = s.handle;
+ s.handle = nullptr;
+ return *this;
+ }
+ ~coro1() {
+ PRINT ("Destroyed coro1");
+ if ( handle )
+ handle.destroy();
+ }
+
+ struct suspend_never_prt {
+ ~suspend_never_prt() {}
+ bool await_ready() const noexcept { return true; }
+ void await_suspend(handle_type h) const noexcept { PRINT ("susp-never-susp");}
+ void await_resume() const noexcept {PRINT ("susp-never-resume");}
+ };
+
+ struct suspend_always_prt {
+ int x;
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp");}
+ void await_resume() const noexcept {PRINT ("susp-always-resume");}
+ };
+
+ /* This returns an int. */
+ struct suspend_always_intprt {
+ int x;
+ suspend_always_intprt() : x(5) { PRINT ("suspend_always_intprt def ctor"); }
+ suspend_always_intprt(int _x) : x(_x) { PRINTF ("suspend_always_intprt ctor with %d\n", x); }
+ ~suspend_always_intprt() {}
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(coro::coroutine_handle<>) const noexcept { PRINT ("susp-always-susp-int");}
+ int await_resume() const noexcept { PRINT ("susp-always-resume-int"); return x;}
+ };
+
+ struct promise_type {
+ int value;
+ promise_type() { PRINT ("Created Promise"); }
+ ~promise_type() { PRINT ("Destroyed Promise"); }
+
+ coro1 get_return_object() {
+ PRINT ("get_return_object: from handle from promise");
+ return coro1 (handle_type::from_promise (*this));
+ }
+
+ auto initial_suspend() {
+ PRINT ("get initial_suspend ");
+ return suspend_never_prt{};
+ }
+
+ auto final_suspend() {
+ PRINT ("get final_suspend");
+ return suspend_always_prt{};
+ }
+
+ void return_value (int v) {
+ PRINTF ("return_value () %d\n",v);
+ value = v;
+ }
+
+ auto await_transform (int v) {
+ PRINTF ("await_transform an int () %d\n",v);
+ return suspend_always_intprt (v);
+ }
+
+ int get_value () { return value; }
+ // Placeholder to satisfy parser, not doing exceptions yet.
+ void unhandled_exception() { /*exit(1);*/ }
+ };
+};
+
+/* Valued with an await_transform. */
+int gX = 1;
+coro1 f ()
+{
+ gX = co_await 11 + co_await 15;
+ co_return gX + 31;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 f_coro = f ();
+ PRINT ("main: got coro1 - checking gX");
+ if (gX != 1)
+ {
+ PRINTF ("main: gX is wrong : %d, should be 1\n", gX);
+ abort ();
+ }
+ if (f_coro.handle.done())
+ {
+ PRINT ("main: we should not be 'done' [1]");
+ abort ();
+ }
+ PRINT ("main: resuming [1] one side of add");
+ f_coro.handle.resume();
+ PRINT ("main: resuming [1] other side of add");
+ f_coro.handle.resume();
+ if (gX != 26)
+ {
+ PRINTF ("main: gX is wrong : %d, should be 26\n", gX);
+ abort ();
+ }
+ /* we should now have returned with the co_return (57) */
+ if (!f_coro.handle.done())
+ {
+ PRINT ("main: we should be 'done' ");
+ abort ();
+ }
+ int y = f_coro.handle.promise().get_value();
+ if (y != 57)
+ {
+ PRINTF ("main: y is wrong : %d, should be 57\n", y);
+ abort ();
+ }
+ puts ("main: done");
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-3.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-3.C
new file mode 100644
index 0000000..f57c4e9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-3.C
@@ -0,0 +1,112 @@
+// { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+# include <utility>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+// GRO differs from the eventual return type.
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+# define PRINT(X)
+#else
+# define PRINT(X) puts(X)
+#endif
+
+struct coro1 {
+ struct promise_type;
+ using handle_type = coro::coroutine_handle<coro1::promise_type>;
+ handle_type handle;
+ coro1 () : handle(0) {}
+ coro1 (handle_type _handle)
+ : handle(_handle) {
+ PRINT("Created coro1 object from handle");
+ }
+ coro1 (const coro1 &) = delete; // no copying
+ coro1 (coro1 &&s) : handle(s.handle) {
+ s.handle = nullptr;
+ PRINT("coro1 mv ctor ");
+ }
+ coro1 &operator = (coro1 &&s) {
+ handle = s.handle;
+ s.handle = nullptr;
+ PRINT("coro1 op= ");
+ return *this;
+ }
+ ~coro1() {
+ PRINT("Destroyed coro1");
+ if ( handle )
+ handle.destroy();
+ }
+
+ struct suspend_never_prt {
+ bool await_ready() const noexcept { return true; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp");}
+ void await_resume() const noexcept { PRINT ("susp-never-resume");}
+ ~suspend_never_prt() {};
+ };
+
+ struct suspend_always_prt {
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp");}
+ void await_resume() const noexcept { PRINT ("susp-always-resume");}
+ };
+
+ struct promise_type {
+ promise_type() { PRINT ("Created Promise"); }
+ ~promise_type() { PRINT ("Destroyed Promise"); }
+
+ auto get_return_object () {
+ PRINT ("get_return_object: handle from promise");
+ return handle_type::from_promise (*this);
+ }
+ auto initial_suspend () {
+ PRINT ("get initial_suspend (always)");
+ return suspend_always_prt{};
+ }
+ auto final_suspend () {
+ PRINT ("get final_suspend (always)");
+ return suspend_always_prt{};
+ }
+ void return_void () {
+ PRINT ("return_void ()");
+ }
+ // Placeholder to satisfy parser, not doing exceptions yet.
+ void unhandled_exception() { /*exit(1);*/ }
+ };
+ //int x;
+};
+
+struct coro1
+f () noexcept
+{
+ PRINT ("coro1: about to return");
+ co_return;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 x = f ();
+ PRINT ("main: got coro1 - resuming");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ PRINT ("main: after resume");
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ //x.handle.resume();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-4.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-4.C
new file mode 100644
index 0000000..d85199e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-4.C
@@ -0,0 +1,129 @@
+// { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+# include <utility>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+// GRO differs from eventual return type and has non-trivial dtor.
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+# define PRINT(X)
+#else
+# define PRINT(X) puts(X)
+#endif
+
+struct coro1 {
+ struct promise_type;
+ using handle_type = coro::coroutine_handle<coro1::promise_type>;
+ handle_type handle;
+
+ struct nontriv {
+ handle_type handle;
+ nontriv () : handle(0) {PRINT("nontriv nul ctor");}
+ nontriv (handle_type _handle)
+ : handle(_handle) {
+ PRINT("Created nontriv object from handle");
+ }
+ ~nontriv () {
+ PRINT("Destroyed nontriv");
+ }
+ };
+
+ coro1 () : handle(0) {}
+ coro1 (handle_type _handle)
+ : handle(_handle) {
+ PRINT("Created coro1 object from handle");
+ }
+ coro1 (nontriv _nt)
+ : handle(_nt.handle) {
+ PRINT("Created coro1 object from nontriv");
+ }
+ coro1 (const coro1 &) = delete; // no copying
+ coro1 (coro1 &&s) : handle(s.handle) {
+ s.handle = nullptr;
+ PRINT("coro1 mv ctor ");
+ }
+ coro1 &operator = (coro1 &&s) {
+ handle = s.handle;
+ s.handle = nullptr;
+ PRINT("coro1 op= ");
+ return *this;
+ }
+ ~coro1() {
+ PRINT("Destroyed coro1");
+ if ( handle )
+ handle.destroy();
+ }
+
+ struct suspend_never_prt {
+ bool await_ready() const noexcept { return true; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp");}
+ void await_resume() const noexcept { PRINT ("susp-never-resume");}
+ ~suspend_never_prt() {};
+ };
+
+ struct suspend_always_prt {
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp");}
+ void await_resume() const noexcept { PRINT ("susp-always-resume");}
+ };
+
+ struct promise_type {
+ promise_type() { PRINT ("Created Promise"); }
+ ~promise_type() { PRINT ("Destroyed Promise"); }
+
+ auto get_return_object () {
+ PRINT ("get_return_object: handle from promise");
+ return nontriv(handle_type::from_promise (*this));
+ }
+ auto initial_suspend () {
+ PRINT ("get initial_suspend (always)");
+ return suspend_always_prt{};
+ }
+ auto final_suspend () {
+ PRINT ("get final_suspend (always)");
+ return suspend_always_prt{};
+ }
+ void return_void () {
+ PRINT ("return_void ()");
+ }
+ // Placeholder to satisfy parser, not doing exceptions yet.
+ void unhandled_exception() { /*exit(1);*/ }
+ };
+ //int x;
+};
+
+struct coro1
+f () noexcept
+{
+ PRINT ("coro1: about to return");
+ co_return;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 x = f ();
+ PRINT ("main: got coro1 - resuming");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ PRINT ("main: after resume");
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ //x.handle.resume();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-5.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-5.C
new file mode 100644
index 0000000..0eb0055
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-5.C
@@ -0,0 +1,122 @@
+// { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+# include <utility>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+// Test returning an int.
+// We will use the promise to contain this to avoid having to include
+// additional C++ headers.
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+# define PRINT(X)
+# define PRINTF (void)
+#else
+# define PRINT(X) puts(X)
+# define PRINTF printf
+#endif
+
+struct coro1 {
+ struct promise_type;
+ using handle_type = coro::coroutine_handle<coro1::promise_type>;
+ handle_type handle;
+ coro1 () : handle(0) {}
+ coro1 (handle_type _handle)
+ : handle(_handle) {
+ PRINT("Created coro1 object from handle");
+ }
+ coro1 (const coro1 &) = delete; // no copying
+ coro1 (coro1 &&s) : handle(s.handle) {
+ s.handle = nullptr;
+ PRINT("coro1 mv ctor ");
+ }
+ coro1 &operator = (coro1 &&s) {
+ handle = s.handle;
+ s.handle = nullptr;
+ PRINT("coro1 op= ");
+ return *this;
+ }
+ ~coro1() {
+ PRINT("Destroyed coro1");
+ if ( handle )
+ handle.destroy();
+ }
+
+ struct suspend_never_prt {
+ bool await_ready() const noexcept { return true; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp"); }
+ void await_resume() const noexcept { PRINT ("susp-never-resume");}
+ };
+
+ /* NOTE: this has a DTOR to test that pathway. */
+ struct suspend_always_prt {
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp"); }
+ void await_resume() const noexcept { PRINT ("susp-always-resume"); }
+ ~suspend_always_prt() { PRINT ("susp-always-DTOR"); }
+ };
+
+ struct promise_type {
+ int value;
+ promise_type() { PRINT ("Created Promise"); }
+ ~promise_type() { PRINT ("Destroyed Promise"); }
+
+ auto get_return_object () {
+ PRINT ("get_return_object: handle from promise");
+ return handle_type::from_promise (*this);
+ }
+ auto initial_suspend () {
+ PRINT ("get initial_suspend (always)");
+ return suspend_always_prt{};
+ }
+ auto final_suspend () {
+ PRINT ("get final_suspend (always)");
+ return suspend_always_prt{};
+ }
+ void return_value (int v) {
+ PRINTF ("return_value () %d\n",v);
+ value = v;
+ }
+ int get_value (void) { return value; }
+ // Placeholder to satisfy parser, not doing exceptions yet.
+ void unhandled_exception() { /*exit(1);*/ }
+ };
+};
+
+struct coro1
+f () noexcept
+{
+ PRINT ("coro1: about to return");
+ co_return 42;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 x = f ();
+ PRINT ("main: got coro1 - resuming");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ PRINT ("main: after resume");
+ int y = x.handle.promise().get_value();
+ if ( y != 42 )
+ abort ();
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ //x.handle.resume();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-6.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-6.C
new file mode 100644
index 0000000..1aad1b1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-6.C
@@ -0,0 +1,125 @@
+// { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+# include <utility>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+// Test returning a T.
+// We will use the promise to contain this to avoid having to include
+// additional C++ headers.
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+# define PRINT(X)
+# define PRINTF (void)
+#else
+# define PRINT(X) puts(X)
+# define PRINTF printf
+#endif
+
+struct suspend_never_prt {
+ bool await_ready() const noexcept { return true; }
+ void await_suspend(coro::coroutine_handle<>) const noexcept
+ { PRINT ("susp-never-susp"); }
+ void await_resume() const noexcept { PRINT ("susp-never-resume");}
+};
+
+/* NOTE: this has a DTOR to test that pathway. */
+struct suspend_always_prt {
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(coro::coroutine_handle<>) const noexcept
+ { PRINT ("susp-always-susp"); }
+ void await_resume() const noexcept { PRINT ("susp-always-resume"); }
+ ~suspend_always_prt() { PRINT ("susp-always-DTOR"); }
+};
+
+template <typename T>
+struct coro1 {
+ struct promise_type;
+ using handle_type = coro::coroutine_handle<coro1::promise_type>;
+ handle_type handle;
+ coro1 () : handle(0) {}
+ coro1 (handle_type _handle)
+ : handle(_handle) {
+ PRINT("Created coro1 object from handle");
+ }
+ coro1 (const coro1 &) = delete; // no copying
+ coro1 (coro1 &&s) : handle(s.handle) {
+ s.handle = nullptr;
+ PRINT("coro1 mv ctor ");
+ }
+ coro1 &operator = (coro1 &&s) {
+ handle = s.handle;
+ s.handle = nullptr;
+ PRINT("coro1 op= ");
+ return *this;
+ }
+ ~coro1() {
+ PRINT("Destroyed coro1");
+ if ( handle )
+ handle.destroy();
+ }
+
+ struct promise_type {
+ T value;
+ promise_type() { PRINT ("Created Promise"); }
+ ~promise_type() { PRINT ("Destroyed Promise"); }
+
+ auto get_return_object () {
+ PRINT ("get_return_object: handle from promise");
+ return handle_type::from_promise (*this);
+ }
+
+ auto initial_suspend () const {
+ PRINT ("get initial_suspend (always)");
+ return suspend_always_prt{};
+ }
+ auto final_suspend () const {
+ PRINT ("get final_suspend (always)");
+ return suspend_always_prt{};
+ }
+ void return_value (T v) {
+ PRINTF ("return_value () %d\n",v);
+ value = v;
+ }
+ T get_value (void) { return value; }
+ // Placeholder to satisfy parser, not doing exceptions yet.
+ void unhandled_exception() { /*exit(1);*/ }
+ };
+};
+
+coro1<float>
+f () noexcept
+{
+ PRINT ("coro1: about to return");
+ co_return (float) 42;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ coro1<float> x = f ();
+ PRINT ("main: got coro1 - resuming");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ PRINT ("main: after resume");
+ int y = x.handle.promise().get_value();
+ if ( y != (float)42 )
+ abort ();
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-7.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-7.C
new file mode 100644
index 0000000..0af0922
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-7.C
@@ -0,0 +1,114 @@
+// { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+# include <utility>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+# define PRINT(X)
+#else
+# define PRINT(X) puts(X)
+#endif
+
+struct coro1 {
+ struct promise_type;
+ using handle_type = coro::coroutine_handle<coro1::promise_type>;
+ handle_type handle;
+ coro1 () : handle(0) {}
+ coro1 (handle_type _handle)
+ : handle(_handle) {
+ PRINT("Created coro1 object from handle");
+ }
+ coro1 (const coro1 &) = delete; // no copying
+ coro1 (coro1 &&s) : handle(s.handle) {
+ s.handle = nullptr;
+ PRINT("coro1 mv ctor ");
+ }
+ coro1 &operator = (coro1 &&s) {
+ handle = s.handle;
+ s.handle = nullptr;
+ PRINT("coro1 op= ");
+ return *this;
+ }
+ ~coro1() {
+ PRINT("Destroyed coro1");
+ if ( handle )
+ handle.destroy();
+ }
+
+ struct suspend_never_prt {
+ bool await_ready() const noexcept { return true; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp");}
+ void await_resume() const noexcept { PRINT ("susp-never-resume");}
+ };
+
+ struct suspend_always_prt {
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp");}
+ void await_resume() const noexcept { PRINT ("susp-always-resume");}
+ ~suspend_always_prt() { PRINT ("susp-always-dtor"); }
+ };
+
+ struct promise_type {
+ promise_type() { PRINT ("Created Promise"); }
+ ~promise_type() { PRINT ("Destroyed Promise"); }
+
+ auto get_return_object () {
+ PRINT ("get_return_object: handle from promise");
+ return handle_type::from_promise (*this);
+ }
+ auto initial_suspend () {
+ PRINT ("get initial_suspend (always)");
+ return suspend_always_prt{};
+ //return std::experimental::suspend_always{};
+ }
+ auto final_suspend () {
+ PRINT ("get final_suspend (always)");
+ return suspend_always_prt{};
+ }
+ void return_void () {
+ PRINT ("return_void ()");
+ }
+ // Placeholder to satisfy parser, not doing exceptions yet.
+ void unhandled_exception() { /*exit(1);*/ }
+ };
+ //int x;
+};
+
+__attribute__((__noinline__))
+int foo (void) { PRINT ("called the int fn foo"); return 2; }
+
+struct coro1
+f () noexcept
+{
+ PRINT ("coro1: about to return");
+ co_return (void)foo();
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 x = f ();
+ PRINT ("main: got coro1 - resuming");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ PRINT ("main: after resume");
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ //x.handle.resume();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-8.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-8.C
new file mode 100644
index 0000000..f79201c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-8.C
@@ -0,0 +1,126 @@
+// { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+# include <utility>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+// Test templated co-return.
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#define BROKEN
+
+#ifndef OUTPUT
+# define PRINT(X)
+# define PRINTF (void)
+#else
+# define PRINT(X) puts(X)
+# define PRINTF printf
+#endif
+
+struct suspend_never_prt {
+ bool await_ready() const noexcept { return true; }
+ void await_suspend(coro::coroutine_handle<>) const noexcept
+ { PRINT ("susp-never-susp"); }
+ void await_resume() const noexcept { PRINT ("susp-never-resume");}
+};
+
+/* NOTE: this has a DTOR to test that pathway. */
+struct suspend_always_prt {
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(coro::coroutine_handle<>) const noexcept
+ { PRINT ("susp-always-susp"); }
+ void await_resume() const noexcept { PRINT ("susp-always-resume"); }
+ ~suspend_always_prt() { PRINT ("susp-always-DTOR"); }
+};
+
+template <typename T>
+struct coro1 {
+ struct promise_type;
+ using handle_type = coro::coroutine_handle<coro1::promise_type>;
+ handle_type handle;
+ coro1 () : handle(0) {}
+ coro1 (handle_type _handle)
+ : handle(_handle) {
+ PRINT("Created coro1 object from handle");
+ }
+ coro1 (const coro1 &) = delete; // no copying
+ coro1 (coro1 &&s) : handle(s.handle) {
+ s.handle = nullptr;
+ PRINT("coro1 mv ctor ");
+ }
+ coro1 &operator = (coro1 &&s) {
+ handle = s.handle;
+ s.handle = nullptr;
+ PRINT("coro1 op= ");
+ return *this;
+ }
+ ~coro1() {
+ PRINT("Destroyed coro1");
+ if ( handle )
+ handle.destroy();
+ }
+
+ struct promise_type {
+ T value;
+ promise_type() { PRINT ("Created Promise"); }
+ ~promise_type() { PRINT ("Destroyed Promise"); }
+
+ auto get_return_object () {
+ PRINT ("get_return_object: handle from promise");
+ return handle_type::from_promise (*this);
+ }
+ suspend_always_prt initial_suspend () const {
+ PRINT ("get initial_suspend (always)");
+ return suspend_always_prt{};
+ }
+ suspend_always_prt final_suspend () const {
+ PRINT ("get final_suspend (always)");
+ return suspend_always_prt{};
+ }
+ void return_value (T v) {
+ PRINTF ("return_value () %d\n",v);
+ value = v;
+ }
+ T get_value (void) { return value; }
+ // Placeholder to satisfy parser, not doing exceptions yet.
+ void unhandled_exception() { /*exit(1);*/ }
+ };
+};
+
+template <typename T>
+coro1<T> f () noexcept
+{
+ PRINT ("coro1: about to return");
+ co_return (T)42;
+}
+
+// The test will only really for int, but that's OK here.
+int main ()
+{
+ PRINT ("main: create coro1");
+ auto x = f<int>();
+ PRINT ("main: got coro1 - resuming");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ PRINT ("main: after resume");
+ int y = x.handle.promise().get_value();
+ if ( y != 42 )
+ abort ();
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ //x.handle.resume();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-9.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-9.C
new file mode 100644
index 0000000..b2dd776
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-9.C
@@ -0,0 +1,118 @@
+// { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+# include <utility>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+/* boolean return from await_suspend (). */
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+# define PRINT(X)
+#else
+# define PRINT(X) puts(X)
+#endif
+
+struct coro1 {
+ struct promise_type;
+ using handle_type = coro::coroutine_handle<coro1::promise_type>;
+ handle_type handle;
+ coro1 () : handle(0) {}
+ coro1 (handle_type _handle)
+ : handle(_handle) {
+ PRINT("Created coro1 object from handle");
+ }
+ coro1 (const coro1 &) = delete; // no copying
+ coro1 (coro1 &&s) : handle(s.handle) {
+ s.handle = nullptr;
+ PRINT("coro1 mv ctor ");
+ }
+ coro1 &operator = (coro1 &&s) {
+ handle = s.handle;
+ s.handle = nullptr;
+ PRINT("coro1 op= ");
+ return *this;
+ }
+ ~coro1() {
+ PRINT("Destroyed coro1");
+ if ( handle )
+ handle.destroy();
+ }
+
+ struct suspend_never_prt {
+ bool await_ready() const noexcept { return true; }
+ bool await_suspend(handle_type) const noexcept {
+ PRINT ("susp-never-susp"); // never executed.
+ return true; // ...
+ }
+ void await_resume() const noexcept {PRINT ("susp-never-resume");}
+ ~suspend_never_prt() {};
+ };
+
+ struct suspend_always_prt {
+ bool await_ready() const noexcept { return false; }
+ bool await_suspend(handle_type) const noexcept {
+ PRINT ("susp-always-susp, but we're going to continue.. ");
+ return false; // not going to suspend.
+ }
+ void await_resume() const noexcept { PRINT ("susp-always-resume");}
+ };
+
+
+ struct promise_type {
+ promise_type() { PRINT ("Created Promise"); }
+ ~promise_type() { PRINT ("Destroyed Promise"); }
+
+ coro1 get_return_object () {
+ PRINT ("get_return_object: from handle from promise");
+ return coro1 (handle_type::from_promise (*this));
+ }
+ auto initial_suspend () {
+ PRINT ("get initial_suspend (always, but really never) ");
+ return suspend_always_prt{};
+ }
+ auto final_suspend () {
+ PRINT ("get final_suspend (always, but never) ");
+ return suspend_always_prt{};
+ }
+ void return_void () {
+ PRINT ("return_void ()");
+ }
+ // Placeholder to satisfy parser, not doing exceptions yet.
+ void unhandled_exception() { /*exit(1);*/ }
+ };
+ //int x;
+};
+
+struct coro1
+f () noexcept
+{
+ PRINT ("coro1: about to return");
+ co_return;
+}
+
+int main ()
+{
+ //__builtin_coro_promise ((void*)0, 16, true);
+ PRINT ("main: create coro1");
+ struct coro1 x = f ();
+ auto p = x.handle.promise ();
+ auto aw = p.initial_suspend();
+ auto f = aw.await_suspend(coro::coroutine_handle<coro1::promise_type>::from_address ((void *)&x));
+ PRINT ("main: got coro1 - should be done");
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently was not done...");
+ abort ();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-void-is-ready.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-void-is-ready.C
new file mode 100644
index 0000000..f5662d1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-void-is-ready.C
@@ -0,0 +1,107 @@
+// { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+# include <utility>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+# define PRINT(X)
+#else
+# define PRINT(X) puts(X)
+#endif
+
+struct coro1 {
+ struct promise_type;
+ using handle_type = coro::coroutine_handle<coro1::promise_type>;
+ handle_type handle;
+ coro1 () : handle(0) {}
+ coro1 (handle_type _handle)
+ : handle(_handle) {
+ PRINT("Created coro1 object from handle");
+ }
+ coro1 (const coro1 &) = delete; // no copying
+ coro1 (coro1 &&s) : handle(s.handle) {
+ s.handle = nullptr;
+ PRINT("coro1 mv ctor ");
+ }
+ coro1 &operator = (coro1 &&s) {
+ handle = s.handle;
+ s.handle = nullptr;
+ PRINT("coro1 op= ");
+ return *this;
+ }
+ ~coro1() {
+ PRINT("Destroyed coro1");
+ if ( handle )
+ handle.destroy();
+ }
+
+ struct suspend_never_prt {
+ bool await_ready() const noexcept { return true; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp");}
+ void await_resume() const noexcept {PRINT ("susp-never-resume");}
+ ~suspend_never_prt() {};
+ };
+
+ struct suspend_always_prt {
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp");}
+ void await_resume() const noexcept { PRINT ("susp-always-resume");}
+ };
+
+
+ struct promise_type {
+ promise_type() { PRINT ("Created Promise"); }
+ ~promise_type() { PRINT ("Destroyed Promise"); }
+
+ coro1 get_return_object () {
+ PRINT ("get_return_object: from handle from promise");
+ return coro1 (handle_type::from_promise (*this));
+ }
+ auto initial_suspend () {
+ PRINT ("get initial_suspend (never) ");
+ return suspend_never_prt{};
+ }
+ auto final_suspend () {
+ PRINT ("get final_suspend (always) ");
+ return suspend_always_prt{};
+ }
+ void return_void () {
+ PRINT ("return_void ()");
+ }
+ // Placeholder to satisfy parser, not doing exceptions yet.
+ void unhandled_exception() { /*exit(1);*/ }
+ };
+ //int x;
+};
+
+struct coro1
+f () noexcept
+{
+ PRINT ("coro1: about to return");
+ co_return;
+}
+
+int main ()
+{
+ //__builtin_coro_promise ((void*)0, 16, true);
+ PRINT ("main: create coro1");
+ struct coro1 x = f ();
+ PRINT ("main: got coro1 - should be done");
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently was not done...");
+ abort ();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-void-is-suspend.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-void-is-suspend.C
new file mode 100644
index 0000000..bc2ab1e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-void-is-suspend.C
@@ -0,0 +1,111 @@
+// { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+# include <utility>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+# define PRINT(X)
+#else
+# define PRINT(X) puts(X)
+#endif
+
+struct coro1 {
+ struct promise_type;
+ using handle_type = coro::coroutine_handle<coro1::promise_type>;
+ handle_type handle;
+ coro1 () : handle(0) {}
+ coro1 (handle_type _handle)
+ : handle(_handle) {
+ PRINT("Created coro1 object from handle");
+ }
+ coro1 (const coro1 &) = delete; // no copying
+ coro1 (coro1 &&s) : handle(s.handle) {
+ s.handle = nullptr;
+ PRINT("coro1 mv ctor ");
+ }
+ coro1 &operator = (coro1 &&s) {
+ handle = s.handle;
+ s.handle = nullptr;
+ PRINT("coro1 op= ");
+ return *this;
+ }
+ ~coro1() {
+ PRINT("Destroyed coro1");
+ if ( handle )
+ handle.destroy();
+ }
+
+ struct suspend_never_prt {
+ bool await_ready() const noexcept { return true; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp");}
+ void await_resume() const noexcept { PRINT ("susp-never-resume");}
+ ~suspend_never_prt() {};
+ };
+
+ struct suspend_always_prt {
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp");}
+ void await_resume() const noexcept { PRINT ("susp-always-resume");}
+ };
+
+
+ struct promise_type {
+ promise_type() { PRINT ("Created Promise"); }
+ ~promise_type() { PRINT ("Destroyed Promise"); }
+
+ coro1 get_return_object () {
+ PRINT ("get_return_object: from handle from promise");
+ return coro1 (handle_type::from_promise (*this));
+ }
+ auto initial_suspend () {
+ PRINT ("get initial_suspend (always)");
+ return suspend_always_prt{};
+ }
+ auto final_suspend () {
+ PRINT ("get final_suspend (always)");
+ return suspend_always_prt{};
+ }
+ void return_void () {
+ PRINT ("return_void ()");
+ }
+ // Placeholder to satisfy parser, not doing exceptions yet.
+ void unhandled_exception() { /*exit(1);*/ }
+ };
+ //int x;
+};
+
+struct coro1
+f () noexcept
+{
+ PRINT ("coro1: about to return");
+ co_return;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 x = f ();
+ PRINT ("main: got coro1 - resuming");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ PRINT ("main: after resume");
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ //x.handle.resume();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-yield-0-triv.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-0-triv.C
new file mode 100644
index 0000000..4d76a99
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-0-triv.C
@@ -0,0 +1,146 @@
+// { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+# include <utility>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+// Test returning an int.
+// We will use the promise to contain this to avoid having to include
+// additional C++ headers.
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+# define PRINT(X)
+# define PRINTF (void)
+#else
+# define PRINT(X) puts(X)
+# define PRINTF printf
+#endif
+
+struct coro1 {
+ struct promise_type;
+ using handle_type = coro::coroutine_handle<coro1::promise_type>;
+ handle_type handle;
+ coro1 () : handle(0) {}
+ coro1 (handle_type _handle)
+ : handle(_handle) {
+ PRINT("Created coro1 object from handle");
+ }
+ coro1 (const coro1 &) = delete; // no copying
+ coro1 (coro1 &&s) : handle(s.handle) {
+ s.handle = nullptr;
+ PRINT("coro1 mv ctor ");
+ }
+ coro1 &operator = (coro1 &&s) {
+ handle = s.handle;
+ s.handle = nullptr;
+ PRINT("coro1 op= ");
+ return *this;
+ }
+ ~coro1() {
+ PRINT("Destroyed coro1");
+ if ( handle )
+ handle.destroy();
+ }
+
+ struct suspend_never_prt {
+ bool await_ready() const noexcept { return true; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp"); }
+ void await_resume() const noexcept { PRINT ("susp-never-resume");}
+ };
+
+ /* NOTE: this has a DTOR to test that pathway. */
+ struct suspend_always_prt {
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp"); }
+ void await_resume() const noexcept { PRINT ("susp-always-resume"); }
+ ~suspend_always_prt() { PRINT ("susp-always-DTOR"); }
+ };
+
+ struct promise_type {
+ int value;
+ promise_type() { PRINT ("Created Promise"); }
+ ~promise_type() { PRINT ("Destroyed Promise"); }
+
+ auto get_return_object () {
+ PRINT ("get_return_object: handle from promise");
+ return handle_type::from_promise (*this);
+ }
+ auto initial_suspend () {
+ PRINT ("get initial_suspend (always)");
+ return suspend_always_prt{};
+ }
+ auto final_suspend () {
+ PRINT ("get final_suspend (always)");
+ return suspend_always_prt{};
+ }
+ void return_value (int v) {
+ PRINTF ("return_value () %d\n",v);
+ value = v;
+ }
+ auto yield_value (int v) {
+ PRINTF ("yield_value () %d and suspend always\n",v);
+ value = v;
+ return suspend_always_prt{};
+ }
+ /* Some non-matching overloads. */
+ auto yield_value (suspend_always_prt s, int x) {
+ return s;
+ }
+ auto yield_value (void) {
+ return 42;
+ }
+ int get_value (void) { return value; }
+ // Placeholder to satisfy parser, not doing exceptions yet.
+ void unhandled_exception() { /*exit(1);*/ }
+ };
+};
+
+struct coro1
+f () noexcept
+{
+ PRINT ("f: about to yield 42");
+ co_yield 42;
+
+ PRINT ("f: about to return 6174");
+ co_return 6174;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 x = f ();
+ PRINT ("main: got coro1 - resuming (1)");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ PRINT ("main: after resume (1)");
+ int y = x.handle.promise().get_value();
+ if ( y != 42 )
+ abort ();
+ PRINT ("main: apparently got 42");
+ PRINT ("main: got coro1 - resuming (2)");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ PRINT ("main: after resume (2)");
+ y = x.handle.promise().get_value();
+ if ( y != 6174 )
+ abort ();
+ PRINT ("main: apparently got 6174");
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-yield-1-multi.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-1-multi.C
new file mode 100644
index 0000000..341af61
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-1-multi.C
@@ -0,0 +1,159 @@
+// { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+# include <utility>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+// Test returning an int.
+// We will use the promise to contain this to avoid having to include
+// additional C++ headers.
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+# define PRINT(X)
+# define PRINTF (void)
+#else
+# define PRINT(X) puts(X)
+# define PRINTF printf
+#endif
+
+struct coro1 {
+ struct promise_type;
+ using handle_type = coro::coroutine_handle<coro1::promise_type>;
+ handle_type handle;
+ coro1 () : handle(0) {}
+ coro1 (handle_type _handle)
+ : handle(_handle) {
+ PRINT("Created coro1 object from handle");
+ }
+ coro1 (const coro1 &) = delete; // no copying
+ coro1 (coro1 &&s) : handle(s.handle) {
+ s.handle = nullptr;
+ PRINT("coro1 mv ctor ");
+ }
+ coro1 &operator = (coro1 &&s) {
+ handle = s.handle;
+ s.handle = nullptr;
+ PRINT("coro1 op= ");
+ return *this;
+ }
+ ~coro1() {
+ PRINT("Destroyed coro1");
+ if ( handle )
+ handle.destroy();
+ }
+
+ struct suspend_never_prt {
+ bool await_ready() const noexcept { return true; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp"); }
+ void await_resume() const noexcept { PRINT ("susp-never-resume");}
+ };
+
+ /* NOTE: this has a DTOR to test that pathway. */
+ struct suspend_always_prt {
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp"); }
+ void await_resume() const noexcept { PRINT ("susp-always-resume"); }
+ ~suspend_always_prt() { PRINT ("susp-always-DTOR"); }
+ };
+
+ struct promise_type {
+ int value;
+ promise_type() { PRINT ("Created Promise"); }
+ ~promise_type() { PRINT ("Destroyed Promise"); }
+
+ auto get_return_object () {
+ PRINT ("get_return_object: handle from promise");
+ return handle_type::from_promise (*this);
+ }
+ auto initial_suspend () {
+ PRINT ("get initial_suspend (always)");
+ return suspend_always_prt{};
+ }
+ auto final_suspend () {
+ PRINT ("get final_suspend (always)");
+ return suspend_always_prt{};
+ }
+ void return_value (int v) {
+ PRINTF ("return_value () %d\n",v);
+ value = v;
+ }
+ auto yield_value (int v) {
+ PRINTF ("yield_value () %d and suspend always\n",v);
+ value = v;
+ return suspend_always_prt{};
+ }
+ /* Some non-matching overloads. */
+ auto yield_value (suspend_always_prt s, int x) {
+ return s;
+ }
+ auto yield_value (void) {
+ return 42;
+ }
+ int get_value (void) { return value; }
+ // Placeholder to satisfy parser, not doing exceptions yet.
+ void unhandled_exception() { /*exit(1);*/ }
+ };
+};
+
+struct coro1
+f () noexcept
+{
+ PRINT ("f: about to yield 42");
+ co_yield 42;
+
+ PRINT ("f: about to yield 11");
+ co_yield 11;
+
+ PRINT ("f: about to return 6174");
+ co_return 6174;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 x = f ();
+ PRINT ("main: got coro1 - resuming (1)");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ PRINT ("main: after resume (1)");
+ int y = x.handle.promise().get_value();
+ if ( y != 42 )
+ abort ();
+ PRINT ("main: apparently got 42 - resuming (2)");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ PRINT ("main: after resume (2)");
+ y = x.handle.promise().get_value();
+ if ( y != 11 )
+ abort ();
+ PRINT ("main: apparently got 11 - resuming (3)");
+ if (x.handle.done())
+ {
+ PRINT ("main: done?");
+ abort();
+ }
+ x.handle.resume();
+ PRINT ("main: after resume (2) checking return");
+ y = x.handle.promise().get_value();
+ if ( y != 6174 )
+ abort ();
+ PRINT ("main: apparently got 6174");
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-yield-2-loop.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-2-loop.C
new file mode 100644
index 0000000..53851d9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-2-loop.C
@@ -0,0 +1,153 @@
+// { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+# include <utility>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+// Test returning an int.
+// We will use the promise to contain this to avoid having to include
+// additional C++ headers.
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+# define PRINT(X)
+# define PRINTF (void)
+#else
+# define PRINT(X) puts(X)
+# define PRINTF printf
+#endif
+
+struct coro1 {
+ struct promise_type;
+ using handle_type = coro::coroutine_handle<coro1::promise_type>;
+ handle_type handle;
+ coro1 () : handle(0) {}
+ coro1 (handle_type _handle)
+ : handle(_handle) {
+ PRINT("Created coro1 object from handle");
+ }
+ coro1 (const coro1 &) = delete; // no copying
+ coro1 (coro1 &&s) : handle(s.handle) {
+ s.handle = nullptr;
+ PRINT("coro1 mv ctor ");
+ }
+ coro1 &operator = (coro1 &&s) {
+ handle = s.handle;
+ s.handle = nullptr;
+ PRINT("coro1 op= ");
+ return *this;
+ }
+ ~coro1() {
+ PRINT("Destroyed coro1");
+ if ( handle )
+ handle.destroy();
+ }
+
+ struct suspend_never_prt {
+ bool await_ready() const noexcept { return true; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp"); }
+ void await_resume() const noexcept { PRINT ("susp-never-resume");}
+ };
+
+ /* NOTE: this has a DTOR to test that pathway. */
+ struct suspend_always_prt {
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp"); }
+ void await_resume() const noexcept { PRINT ("susp-always-resume"); }
+ ~suspend_always_prt() { PRINT ("susp-always-DTOR"); }
+ };
+
+ struct promise_type {
+ int value;
+ promise_type() { PRINT ("Created Promise"); }
+ ~promise_type() { PRINT ("Destroyed Promise"); }
+
+ auto get_return_object () {
+ PRINT ("get_return_object: handle from promise");
+ return handle_type::from_promise (*this);
+ }
+ auto initial_suspend () {
+ PRINT ("get initial_suspend (always)");
+ return suspend_always_prt{};
+ }
+ auto final_suspend () {
+ PRINT ("get final_suspend (always)");
+ return suspend_always_prt{};
+ }
+ void return_value (int v) {
+ PRINTF ("return_value () %d\n",v);
+ value = v;
+ }
+ auto yield_value (int v) {
+ PRINTF ("yield_value () %d and suspend always\n",v);
+ value = v;
+ return suspend_always_prt{};
+ }
+ /* Some non-matching overloads. */
+ auto yield_value (suspend_always_prt s, int x) {
+ return s;
+ }
+ auto yield_value (void) {
+ return 42;//suspend_always_prt{};
+ }
+ int get_value (void) { return value; }
+ // Placeholder to satisfy parser, not doing exceptions yet.
+ void unhandled_exception() { /*exit(1);*/ }
+ };
+};
+
+int gX ;
+
+struct coro1
+f () noexcept
+{
+ for (gX = 5; gX < 10 ; gX++)
+ {
+ PRINTF ("f: about to yield %d\n", gX);
+ co_yield gX;
+ }
+
+ PRINT ("f: about to return 6174");
+ co_return 6174;
+
+ //co_return 0;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 f_coro = f ();
+ PRINT ("main: got coro1 - resuming (1)");
+ if (f_coro.handle.done())
+ abort();
+ f_coro.handle.resume();
+ PRINT ("main: after resume (1)");
+ int y = f_coro.handle.promise().get_value();
+
+ PRINT ("main: gX OK -- looping");
+ do {
+ //PRINTF ("main: gX : %d \n", gX);
+ f_coro.handle.resume();
+ } while (!f_coro.handle.done());
+
+ y = f_coro.handle.promise().get_value();
+ if ( y != 6174 )
+ abort ();
+ PRINT ("main: apparently got 6174");
+ if (!f_coro.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ //x.handle.resume();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-yield-3-tmpl.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-3-tmpl.C
new file mode 100644
index 0000000..b1659e8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-3-tmpl.C
@@ -0,0 +1,160 @@
+// { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+# include <utility>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+// Test co_yield in templated code.
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+# define PRINT(X)
+# define PRINTF (void)
+#else
+# define PRINT(X) puts(X)
+# define PRINTF printf
+#endif
+
+template <typename T>
+struct looper {
+
+ struct promise_type {
+ T value;
+ promise_type() { PRINT ("Created Promise"); }
+ ~promise_type() { PRINT ("Destroyed Promise"); }
+
+ auto get_return_object () {
+ PRINT ("get_return_object: handle from promise");
+ return handle_type::from_promise (*this);
+ }
+
+ auto initial_suspend () {
+ PRINT ("get initial_suspend (always)");
+ return suspend_always_prt{};
+ }
+
+ auto final_suspend () {
+ PRINT ("get final_suspend (always)");
+ return suspend_always_prt{};
+ }
+
+ void return_value (T v) {
+ PRINTF ("return_value () %lf\n", (double)v);
+ value = v;
+ }
+
+ auto yield_value (T v) {
+ PRINTF ("yield_value () %lf and suspend always\n", (double)v);
+ value = v;
+ return suspend_always_prt{};
+ }
+
+ T get_value (void) { return value; }
+
+ // Placeholder to satisfy parser, not doing exceptions yet.
+ void unhandled_exception() { /*exit(1);*/ }
+ };
+
+ using handle_type = coro::coroutine_handle<looper::promise_type>;
+ handle_type handle;
+
+ looper () : handle(0) {}
+ looper (handle_type _handle)
+ : handle(_handle) {
+ PRINT("Created coro1 object from handle");
+ }
+ looper (const looper &) = delete; // no copying
+ looper (looper &&s) : handle(s.handle) {
+ s.handle = nullptr;
+ PRINT("looper mv ctor ");
+ }
+ looper &operator = (looper &&s) {
+ handle = s.handle;
+ s.handle = nullptr;
+ PRINT("looper op= ");
+ return *this;
+ }
+ ~looper() {
+ PRINT("Destroyed coro1");
+ if ( handle )
+ handle.destroy();
+ }
+
+ struct suspend_never_prt {
+ bool await_ready() const noexcept { return true; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp"); }
+ void await_resume() const noexcept { PRINT ("susp-never-resume");}
+ };
+
+ /* NOTE: this has a DTOR to test that pathway. */
+ struct suspend_always_prt {
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp"); }
+ void await_resume() const noexcept { PRINT ("susp-always-resume"); }
+ ~suspend_always_prt() { PRINT ("susp-always-DTOR"); }
+ };
+
+};
+
+// Contrived to avoid non-scalar state across the yield.
+template <typename T>
+looper<T> f () noexcept
+{
+ for (int i = 5; i < 10 ; ++i)
+ {
+ PRINTF ("f: about to yield %d\n", i);
+ co_yield (T) i;
+ }
+
+ PRINT ("f: about to return 6174");
+ co_return 6174;
+}
+
+// contrived, only going to work for an int.
+int main ()
+{
+ PRINT ("main: create int looper");
+ auto f_coro = f<int> ();
+
+ if (f_coro.handle.done())
+ {
+ PRINT ("main: said we were done, but we hadn't started!");
+ abort();
+ }
+
+ PRINT ("main: OK -- looping");
+ int y, test = 5;
+ do {
+ f_coro.handle.resume();
+ if (f_coro.handle.done())
+ break;
+ y = f_coro.handle.promise().get_value();
+ if (y != test)
+ {
+ PRINTF ("main: failed for test %d, got %d\n", test, y);
+ abort();
+ }
+ test++;
+ } while (test < 20);
+
+ y = f_coro.handle.promise().get_value();
+ if ( y != 6174 )
+ abort ();
+
+ PRINT ("main: apparently got 6174");
+ if (!f_coro.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-yield-strings.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-strings.C
new file mode 100644
index 0000000..9c953ba
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-strings.C
@@ -0,0 +1,182 @@
+// { dg-do run }
+
+#if __clang__
+# include <experimental/coroutine>
+# include <utility>
+#else
+# include "../coro.h"
+#endif
+
+#include <vector>
+#include <string>
+
+namespace coro = std::experimental;
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+# define PRINT(X)
+# define PRINTF (void)
+#else
+# define PRINT(X) puts(X)
+# define PRINTF printf
+#endif
+
+template <typename T>
+struct looper {
+
+ struct promise_type {
+ T value;
+ promise_type() { PRINT ("Created Promise"); }
+ ~promise_type() { PRINT ("Destroyed Promise"); }
+
+ auto get_return_object () {
+ PRINT ("get_return_object: handle from promise");
+ return handle_type::from_promise (*this);
+ }
+
+ auto initial_suspend () {
+ PRINT ("get initial_suspend (always)");
+ return suspend_always_prt{};
+ }
+
+ auto final_suspend () {
+ PRINT ("get final_suspend (always)");
+ return suspend_always_prt{};
+ }
+
+ void return_value (T v) {
+ PRINTF ("return_value () %s\n", v.c_str());
+ value = v;
+ }
+
+ auto yield_value (T v) {
+ PRINTF ("yield_value () %s and suspend always\n", v.c_str());
+ value = v;
+ return suspend_always_prt{};
+ }
+
+ T get_value (void) { return value; }
+
+ // Placeholder to satisfy parser, not doing exceptions yet.
+ void unhandled_exception() { /*exit(1);*/ }
+ };
+
+ using handle_type = coro::coroutine_handle<looper::promise_type>;
+ handle_type handle;
+
+ looper () : handle(0) {}
+ looper (handle_type _handle)
+ : handle(_handle) {
+ PRINT("Created coro1 object from handle");
+ }
+ looper (const looper &) = delete; // no copying
+ looper (looper &&s) : handle(s.handle) {
+ s.handle = nullptr;
+ PRINT("looper mv ctor ");
+ }
+ looper &operator = (looper &&s) {
+ handle = s.handle;
+ s.handle = nullptr;
+ PRINT("looper op= ");
+ return *this;
+ }
+ ~looper() {
+ PRINT("Destroyed coro1");
+ if ( handle )
+ handle.destroy();
+ }
+
+ struct suspend_never_prt {
+ bool await_ready() const noexcept { return true; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp"); }
+ void await_resume() const noexcept { PRINT ("susp-never-resume");}
+ };
+
+ /* NOTE: this has a DTOR to test that pathway. */
+ struct suspend_always_prt {
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp"); }
+ void await_resume() const noexcept { PRINT ("susp-always-resume"); }
+ ~suspend_always_prt() { PRINT ("susp-always-DTOR"); }
+ };
+
+};
+
+int gX ;
+
+struct mycounter
+{
+ mycounter () : v(0) { PRINT ("mycounter CTOR"); }
+ ~mycounter () { gX = 6174; PRINT ("mycounter DTOR"); }
+ int value () { return v; }
+ void incr () { v++; }
+ int v;
+};
+
+template <typename T>
+looper<T> with_ctorable_state (std::vector<T> d) noexcept
+{
+ std::vector<T> loc;
+ unsigned lim = d.size()-1;
+ mycounter c;
+ for (unsigned i = 0; i < lim ; ++i)
+ {
+ loc.push_back(d[i]);
+ c.incr();
+ PRINTF ("f: about to yield value %d \n", i);
+ co_yield loc[i];
+ }
+ loc.push_back(d[lim]);
+
+ PRINT ("f: done");
+ co_return loc[lim];
+}
+
+int main ()
+{
+ PRINT ("main: create looper");
+ std::vector<std::string> input = {"first", "the", "quick", "reddish", "fox", "done" };
+ auto f_coro = with_ctorable_state<std::string> (input);
+
+ PRINT ("main: got looper - resuming (1)");
+ if (f_coro.handle.done())
+ abort();
+
+ f_coro.handle.resume();
+ std::string s = f_coro.handle.promise().get_value();
+ if ( s != "first" )
+ abort ();
+
+ PRINTF ("main: got : %s\n", s.c_str());
+ unsigned check = 1;
+ do {
+ f_coro.handle.resume();
+ s = f_coro.handle.promise().get_value();
+ if (s != input[check++])
+ abort ();
+ PRINTF ("main: got : %s\n", s.c_str());
+ } while (!f_coro.handle.done());
+
+ if ( s != "done" )
+ abort ();
+
+ PRINT ("main: should be done");
+ if (!f_coro.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ //x.handle.resume();
+ }
+
+ if (gX != 6174)
+ {
+ PRINT ("main: apparently we didn't run mycounter DTOR...");
+ abort ();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/coro-torture.exp b/gcc/testsuite/g++.dg/coroutines/torture/coro-torture.exp
new file mode 100644
index 0000000..d2463b2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/coro-torture.exp
@@ -0,0 +1,19 @@
+# This harness is for tests that should be run at all optimisation levels.
+
+load_lib g++-dg.exp
+load_lib torture-options.exp
+
+global DG_TORTURE_OPTIONS LTO_TORTURE_OPTIONS
+
+dg-init
+torture-init
+
+set DEFAULT_COROFLAGS $DEFAULT_CXXFLAGS
+lappend DEFAULT_COROFLAGS "-std=c++17" "-fcoroutines"
+
+set-torture-options [concat $DG_TORTURE_OPTIONS $LTO_TORTURE_OPTIONS]
+
+gcc-dg-runtest [lsort [glob $srcdir/$subdir/*.C]] "" $DEFAULT_COROFLAGS
+
+torture-finish
+dg-finish
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/exceptions-test-0.C b/gcc/testsuite/g++.dg/coroutines/torture/exceptions-test-0.C
new file mode 100644
index 0000000..9601564
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/exceptions-test-0.C
@@ -0,0 +1,189 @@
+// { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+# include <utility>
+#else
+# include "../coro.h"
+#endif
+#include <exception>
+
+namespace coro = std::experimental;
+
+// Test returning an int.
+// We will use the promise to contain this to avoid having to include
+// additional C++ headers.
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+# define PRINT(X)
+# define PRINTF (void)
+#else
+# define PRINT(X) puts(X)
+# define PRINTF printf
+#endif
+
+int gX = 0;
+
+struct coro1 {
+ struct promise_type;
+ using handle_type = coro::coroutine_handle<coro1::promise_type>;
+ handle_type handle;
+ coro1 () : handle(0) {}
+ coro1 (handle_type _handle)
+ : handle(_handle) {
+ PRINT("Created coro1 object from handle");
+ }
+ coro1 (const coro1 &) = delete; // no copying
+ coro1 (coro1 &&s) : handle(s.handle) {
+ s.handle = nullptr;
+ PRINT("coro1 mv ctor ");
+ }
+ coro1 &operator = (coro1 &&s) {
+ handle = s.handle;
+ s.handle = nullptr;
+ PRINT("coro1 op= ");
+ return *this;
+ }
+ ~coro1() {
+ PRINT("Destroyed coro1");
+ if ( handle )
+ handle.destroy();
+ }
+
+ struct suspend_never_prt {
+ bool await_ready() const noexcept { return true; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp"); }
+ void await_resume() const noexcept { PRINT ("susp-never-resume");}
+ };
+
+ /* NOTE: this has a DTOR to test that pathway. */
+ struct suspend_always_prt {
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp"); }
+ void await_resume() const noexcept { PRINT ("susp-always-resume"); }
+ ~suspend_always_prt() { PRINT ("susp-always-DTOR"); }
+ };
+
+ struct promise_type {
+ int value;
+ promise_type() { PRINT ("Created Promise"); }
+ ~promise_type() { PRINT ("Destroyed Promise"); }
+
+ auto get_return_object () {
+ PRINT ("get_return_object: handle from promise");
+ return handle_type::from_promise (*this);
+ }
+ auto initial_suspend () {
+ PRINT ("get initial_suspend (always)");
+ return suspend_always_prt{};
+ }
+ auto final_suspend () {
+ PRINT ("get final_suspend (always)");
+ return suspend_always_prt{};
+ }
+ void return_value (int v) {
+ PRINTF ("return_value () %d\n",v);
+ value = v;
+ }
+ auto yield_value (int v) {
+ PRINTF ("yield_value () %d and suspend always\n",v);
+ value = v;
+ return suspend_always_prt{};
+ }
+ /* Some non-matching overloads. */
+ auto yield_value (suspend_always_prt s, int x) {
+ return s;
+ }
+ auto yield_value (void) {
+ return 42;//suspend_always_prt{};
+ }
+ int get_value (void) { return value; }
+ // Placeholder to satisfy parser, not doing exceptions yet.
+ void unhandled_exception() {
+ PRINT ("unhandled_exception: caught one!");/*exit(1);*/
+ gX = -1;
+ // returning from here should end up in final_suspend.
+ //std::terminate ();
+ }
+ };
+};
+
+// So we want to check that the internal behaviour of try/catch is
+// working OK - and that if we have an unhandled exception it is caught
+// by the wrapper that we add to the rewritten func.
+
+struct coro1 throw_and_catch () noexcept
+{
+ int caught = 0;
+
+ try {
+ PRINT ("f: about to yield 42");
+ co_yield 42;
+
+ throw (20);
+
+ PRINT ("f: about to yield 6174");
+ co_return 6174;
+
+ } catch (int x) {
+ PRINTF ("f: caught %d\n", x);
+ caught = x;
+ }
+
+ PRINTF ("f: about to yield what we caught %d\n", caught);
+ co_yield caught;
+
+ throw ("bah");
+
+ PRINT ("f: about to return 22");
+ co_return 22;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 x = throw_and_catch ();
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ PRINT ("main: got coro, resuming..");
+ int y = x.handle.promise().get_value();
+ if ( y != 42 )
+ abort ();
+ PRINT ("main: apparently got the expected 42");
+ if (x.handle.done())
+ abort();
+ PRINT ("main: resuming...");
+ x.handle.resume();
+
+ y = x.handle.promise().get_value();
+ if ( y != 20 )
+ abort ();
+ PRINT ("main: apparently got 20, which we expected");
+ if (x.handle.done())
+ abort();
+
+ PRINT ("main: resuming...");
+ x.handle.resume();
+ // This should cause the throw of "bah" which is unhandled.
+ // We should catch the unhandled exception and then fall through
+ // to the final suspend point... thus be "done".
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ }
+ // When we caught the unhandled exception we flagged it instead of
+ // std::terminate-ing.
+ if (gX != -1)
+ {
+ PRINT ("main: apparently failed to catch");
+ abort ();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-0.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-0.C
new file mode 100644
index 0000000..308f7ef
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-0.C
@@ -0,0 +1,126 @@
+// { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+# include <utility>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+// Test promise construction from function args list.
+// We will use the promise to contain this to avoid having to include
+// additional C++ headers.
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+# define PRINT(X)
+# define PRINTF (void)
+#else
+# define PRINT(X) puts(X)
+# define PRINTF printf
+#endif
+
+struct coro1 {
+ struct promise_type;
+ using handle_type = coro::coroutine_handle<coro1::promise_type>;
+ handle_type handle;
+ coro1 () : handle(0) {}
+ coro1 (handle_type _handle)
+ : handle(_handle) {
+ PRINT("Created coro1 object from handle");
+ }
+ coro1 (const coro1 &) = delete; // no copying
+ coro1 (coro1 &&s) : handle(s.handle) {
+ s.handle = nullptr;
+ PRINT("coro1 mv ctor ");
+ }
+ coro1 &operator = (coro1 &&s) {
+ handle = s.handle;
+ s.handle = nullptr;
+ PRINT("coro1 op= ");
+ return *this;
+ }
+ ~coro1() {
+ PRINT("Destroyed coro1");
+ if ( handle )
+ handle.destroy();
+ }
+
+ struct suspend_never_prt {
+ bool await_ready() const noexcept { return true; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp"); }
+ void await_resume() const noexcept { PRINT ("susp-never-resume");}
+ };
+
+ /* NOTE: this has a DTOR to test that pathway. */
+ struct suspend_always_prt {
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp"); }
+ void await_resume() const noexcept { PRINT ("susp-always-resume"); }
+ ~suspend_always_prt() { PRINT ("susp-always-DTOR"); }
+ };
+
+ struct promise_type {
+ int value;
+ promise_type(int x) : value (x) { PRINTF ("Created Promise with %d\n", x); }
+ promise_type() { PRINT ("Created Promise"); }
+ ~promise_type() { PRINT ("Destroyed Promise"); }
+
+ auto get_return_object () {
+ PRINT ("get_return_object: handle from promise");
+ return handle_type::from_promise (*this);
+ }
+ auto initial_suspend () {
+ PRINT ("get initial_suspend (always)");
+ return suspend_always_prt{};
+ }
+ auto final_suspend () {
+ PRINT ("get final_suspend (always)");
+ return suspend_always_prt{};
+ }
+ void return_value (int v) {
+ PRINTF ("return_value () %d\n",v);
+ value = v;
+ }
+ int get_value (void) { return value; }
+ // Placeholder to satisfy parser, not doing exceptions yet.
+ void unhandled_exception() { /*exit(1);*/ }
+ };
+};
+
+struct coro1
+f (int x) noexcept
+{
+ PRINT ("coro1: about to return");
+ co_return 42;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 x = f (555);
+ int y = x.handle.promise().get_value();
+ if ( y != 555 )
+ abort ();
+ PRINTF ("main: after coro1 ctor %d - now resuming\n", y);
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ PRINT ("main: after resume");
+ y = x.handle.promise().get_value();
+ if ( y != 42 )
+ abort ();
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ //x.handle.resume();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-1.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-1.C
new file mode 100644
index 0000000..4408ae1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-1.C
@@ -0,0 +1,130 @@
+// { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+# include <utility>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+// Test returning an int.
+// We will use the promise to contain this to avoid having to include
+// additional C++ headers.
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+# define PRINT(X)
+# define PRINTF (void)
+#else
+# define PRINT(X) puts(X)
+# define PRINTF printf
+#endif
+
+struct coro1 {
+ struct promise_type;
+ using handle_type = coro::coroutine_handle<coro1::promise_type>;
+ handle_type handle;
+ coro1 () : handle(0) {}
+ coro1 (handle_type _handle)
+ : handle(_handle) {
+ PRINT("Created coro1 object from handle");
+ }
+ coro1 (const coro1 &) = delete; // no copying
+ coro1 (coro1 &&s) : handle(s.handle) {
+ s.handle = nullptr;
+ PRINT("coro1 mv ctor ");
+ }
+ coro1 &operator = (coro1 &&s) {
+ handle = s.handle;
+ s.handle = nullptr;
+ PRINT("coro1 op= ");
+ return *this;
+ }
+ ~coro1() {
+ PRINT("Destroyed coro1");
+ if ( handle )
+ handle.destroy();
+ }
+
+ struct suspend_never_prt {
+ bool await_ready() const noexcept { return true; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp"); }
+ void await_resume() const noexcept { PRINT ("susp-never-resume");}
+ };
+
+ /* NOTE: this has a DTOR to test that pathway. */
+ struct suspend_always_prt {
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp"); }
+ void await_resume() const noexcept { PRINT ("susp-always-resume"); }
+ ~suspend_always_prt() { PRINT ("susp-always-DTOR"); }
+ };
+
+ struct promise_type {
+ int value;
+ promise_type() { PRINT ("Created Promise"); }
+ ~promise_type() { PRINT ("Destroyed Promise"); }
+
+ auto get_return_object () {
+ PRINT ("get_return_object: handle from promise");
+ return handle_type::from_promise (*this);
+ }
+ auto initial_suspend () {
+ PRINT ("get initial_suspend (always)");
+ return suspend_always_prt{};
+ }
+ auto final_suspend () {
+ PRINT ("get final_suspend (always)");
+ return suspend_always_prt{};
+ }
+ void return_value (int v) {
+ PRINTF ("return_value () %d\n",v);
+ value = v;
+ }
+ int get_value (void) { return value; }
+ // Placeholder to satisfy parser, not doing exceptions yet.
+ void unhandled_exception() { /*exit(1);*/ }
+ };
+};
+
+struct coro1
+f (int x) noexcept
+{
+ if (x > 20)
+ {
+ PRINT ("coro1: about to return k");
+ co_return 6174;
+ }
+ else
+ {
+ PRINT ("coro1: about to return the answer");
+ co_return 42;
+ }
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 x = f (32);
+ PRINT ("main: got coro1 - resuming");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ PRINT ("main: after resume");
+ int y = x.handle.promise().get_value();
+ if ( y != 6174 )
+ abort ();
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ //x.handle.resume();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-2.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-2.C
new file mode 100644
index 0000000..2d97e76
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-2.C
@@ -0,0 +1,134 @@
+// { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+# include <utility>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+// Test that we correctly re-write multiple uses of a function param
+// in the body.
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+# define PRINT(X)
+# define PRINTF (void)
+#else
+# define PRINT(X) puts(X)
+# define PRINTF printf
+#endif
+
+struct coro1 {
+ struct promise_type;
+ using handle_type = coro::coroutine_handle<coro1::promise_type>;
+ handle_type handle;
+ coro1 () : handle(0) {}
+ coro1 (handle_type _handle)
+ : handle(_handle) {
+ PRINT("Created coro1 object from handle");
+ }
+ coro1 (const coro1 &) = delete; // no copying
+ coro1 (coro1 &&s) : handle(s.handle) {
+ s.handle = nullptr;
+ PRINT("coro1 mv ctor ");
+ }
+ coro1 &operator = (coro1 &&s) {
+ handle = s.handle;
+ s.handle = nullptr;
+ PRINT("coro1 op= ");
+ return *this;
+ }
+ ~coro1() {
+ PRINT("Destroyed coro1");
+ if ( handle )
+ handle.destroy();
+ }
+
+ struct suspend_never_prt {
+ bool await_ready() const noexcept { return true; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp"); }
+ void await_resume() const noexcept { PRINT ("susp-never-resume");}
+ };
+
+ /* NOTE: this has a DTOR to test that pathway. */
+ struct suspend_always_prt {
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp"); }
+ void await_resume() const noexcept { PRINT ("susp-always-resume"); }
+ ~suspend_always_prt() { PRINT ("susp-always-DTOR"); }
+ };
+
+ struct promise_type {
+ int value;
+ promise_type() { PRINT ("Created Promise"); }
+ ~promise_type() { PRINT ("Destroyed Promise"); }
+
+ auto get_return_object () {
+ PRINT ("get_return_object: handle from promise");
+ return handle_type::from_promise (*this);
+ }
+ auto initial_suspend () {
+ PRINT ("get initial_suspend (always)");
+ return suspend_always_prt{};
+ }
+ auto final_suspend () {
+ PRINT ("get final_suspend (always)");
+ return suspend_always_prt{};
+ }
+ void return_value (int v) {
+ PRINTF ("return_value () %d\n",v);
+ value = v;
+ }
+ int get_value (void) { return value; }
+ // Placeholder to satisfy parser, not doing exceptions yet.
+ void unhandled_exception() { /*exit(1);*/ }
+ };
+};
+
+struct coro1
+f (int x) noexcept
+{
+ if (x > 30)
+ {
+ PRINT ("coro1: about to return k");
+ co_return 6174;
+ }
+ else if (x > 20)
+ {
+ PRINT ("coro1: about to return the answer");
+ co_return 42;
+ }
+ else
+ {
+ PRINT ("coro1: about to return 0");
+ co_return 0;
+ }
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 x = f (25);
+ PRINT ("main: got coro1 - resuming");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ PRINT ("main: after resume");
+ int y = x.handle.promise().get_value();
+ if ( y != 42 )
+ abort ();
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ //x.handle.resume();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-3.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-3.C
new file mode 100644
index 0000000..1242b13
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-3.C
@@ -0,0 +1,133 @@
+// { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+# include <utility>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+// Test that we can use a function param in a co_xxxx status.
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+# define PRINT(X)
+# define PRINTF (void)
+#else
+# define PRINT(X) puts(X)
+# define PRINTF printf
+#endif
+
+struct coro1 {
+ struct promise_type;
+ using handle_type = coro::coroutine_handle<coro1::promise_type>;
+ handle_type handle;
+ coro1 () : handle(0) {}
+ coro1 (handle_type _handle)
+ : handle(_handle) {
+ PRINT("Created coro1 object from handle");
+ }
+ coro1 (const coro1 &) = delete; // no copying
+ coro1 (coro1 &&s) : handle(s.handle) {
+ s.handle = nullptr;
+ PRINT("coro1 mv ctor ");
+ }
+ coro1 &operator = (coro1 &&s) {
+ handle = s.handle;
+ s.handle = nullptr;
+ PRINT("coro1 op= ");
+ return *this;
+ }
+ ~coro1() {
+ PRINT("Destroyed coro1");
+ if ( handle )
+ handle.destroy();
+ }
+
+ struct suspend_never_prt {
+ bool await_ready() const noexcept { return true; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp"); }
+ void await_resume() const noexcept { PRINT ("susp-never-resume");}
+ };
+
+ /* NOTE: this has a DTOR to test that pathway. */
+ struct suspend_always_prt {
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp"); }
+ void await_resume() const noexcept { PRINT ("susp-always-resume"); }
+ ~suspend_always_prt() { PRINT ("susp-always-DTOR"); }
+ };
+
+ struct promise_type {
+ int value;
+ promise_type() { PRINT ("Created Promise"); }
+ ~promise_type() { PRINT ("Destroyed Promise"); }
+
+ auto get_return_object () {
+ PRINT ("get_return_object: handle from promise");
+ return handle_type::from_promise (*this);
+ }
+ auto initial_suspend () {
+ PRINT ("get initial_suspend (always)");
+ return suspend_always_prt{};
+ }
+ auto final_suspend () {
+ PRINT ("get final_suspend (always)");
+ return suspend_always_prt{};
+ }
+ void return_value (int v) {
+ PRINTF ("return_value () %d\n",v);
+ value = v;
+ }
+ int get_value (void) { return value; }
+ // Placeholder to satisfy parser, not doing exceptions yet.
+ void unhandled_exception() { /*exit(1);*/ }
+ };
+};
+
+struct coro1
+f (int x) noexcept
+{
+ if (x > 30)
+ {
+ PRINT ("coro1: about to return k");
+ co_return 6174;
+ }
+ else if (x > 20)
+ {
+ PRINTF ("coro1: about to co-return %d", x);
+ co_return x;
+ }
+ else
+ {
+ PRINT ("coro1: about to return 0");
+ co_return 0;
+ }
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 x = f (25);
+ PRINT ("main: got coro1 - resuming");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ PRINT ("main: after resume");
+ int y = x.handle.promise().get_value();
+ if ( y != 25 )
+ abort ();
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ //x.handle.resume();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-4.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-4.C
new file mode 100644
index 0000000..729aae6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-4.C
@@ -0,0 +1,141 @@
+// { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+# include <utility>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+// Test that we can manage a constructed param copy.
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+# define PRINT(X)
+# define PRINTF (void)
+#else
+# define PRINT(X) puts(X)
+# define PRINTF printf
+#endif
+
+struct coro1 {
+ struct promise_type;
+ using handle_type = coro::coroutine_handle<coro1::promise_type>;
+ handle_type handle;
+ coro1 () : handle(0) {}
+ coro1 (handle_type _handle)
+ : handle(_handle) {
+ PRINT("Created coro1 object from handle");
+ }
+ coro1 (const coro1 &) = delete; // no copying
+ coro1 (coro1 &&s) : handle(s.handle) {
+ s.handle = nullptr;
+ PRINT("coro1 mv ctor ");
+ }
+ coro1 &operator = (coro1 &&s) {
+ handle = s.handle;
+ s.handle = nullptr;
+ PRINT("coro1 op= ");
+ return *this;
+ }
+ ~coro1() {
+ PRINT("Destroyed coro1");
+ if ( handle )
+ handle.destroy();
+ }
+
+ struct suspend_never_prt {
+ bool await_ready() const noexcept { return true; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp"); }
+ void await_resume() const noexcept { PRINT ("susp-never-resume");}
+ };
+
+ /* NOTE: this has a DTOR to test that pathway. */
+ struct suspend_always_prt {
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp"); }
+ void await_resume() const noexcept { PRINT ("susp-always-resume"); }
+ ~suspend_always_prt() { PRINT ("susp-always-DTOR"); }
+ };
+
+ struct promise_type {
+ int value;
+ promise_type() { PRINT ("Created Promise"); }
+ ~promise_type() { PRINT ("Destroyed Promise"); }
+
+ auto get_return_object () {
+ PRINT ("get_return_object: handle from promise");
+ return handle_type::from_promise (*this);
+ }
+ auto initial_suspend () {
+ PRINT ("get initial_suspend (always)");
+ return suspend_always_prt{};
+ }
+ auto final_suspend () {
+ PRINT ("get final_suspend (always)");
+ return suspend_always_prt{};
+ }
+ void return_value (int v) {
+ PRINTF ("return_value () %d\n",v);
+ value = v;
+ }
+ int get_value (void) { return value; }
+ // Placeholder to satisfy parser, not doing exceptions yet.
+ void unhandled_exception() { /*exit(1);*/ }
+ };
+};
+
+// Require a ctor.
+struct nontriv {
+ int a, b, c;
+ nontriv (int _a, int _b, int _c) : a(_a), b(_b), c(_c) {}
+ virtual int getA () { return a; }
+};
+
+struct coro1
+f (nontriv t) noexcept
+{
+ if (t.a > 30)
+ {
+ PRINTF ("coro1: about to return %d", t.b);
+ co_return t.b;
+ }
+ else if (t.a > 20)
+ {
+ PRINTF ("coro1: about to co-return %d", t.c);
+ co_return t.c;
+ }
+ else
+ {
+ PRINT ("coro1: about to return 0");
+ co_return 0;
+ }
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ nontriv test (25, 6174, 42);
+ struct coro1 x = f (test);
+ PRINT ("main: got coro1 - resuming");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ PRINT ("main: after resume");
+ int y = x.handle.promise().get_value();
+ if ( y != 42 )
+ abort ();
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ //x.handle.resume();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-5.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-5.C
new file mode 100644
index 0000000..e0c3d2f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-5.C
@@ -0,0 +1,141 @@
+// { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+# include <utility>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+// Test that we can manage a constructed param copy.
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+# define PRINT(X)
+# define PRINTF (void)
+#else
+# define PRINT(X) puts(X)
+# define PRINTF printf
+#endif
+
+struct coro1 {
+ struct promise_type;
+ using handle_type = coro::coroutine_handle<coro1::promise_type>;
+ handle_type handle;
+ coro1 () : handle(0) {}
+ coro1 (handle_type _handle)
+ : handle(_handle) {
+ PRINT("Created coro1 object from handle");
+ }
+ coro1 (const coro1 &) = delete; // no copying
+ coro1 (coro1 &&s) : handle(s.handle) {
+ s.handle = nullptr;
+ PRINT("coro1 mv ctor ");
+ }
+ coro1 &operator = (coro1 &&s) {
+ handle = s.handle;
+ s.handle = nullptr;
+ PRINT("coro1 op= ");
+ return *this;
+ }
+ ~coro1() {
+ PRINT("Destroyed coro1");
+ if ( handle )
+ handle.destroy();
+ }
+
+ struct suspend_never_prt {
+ bool await_ready() const noexcept { return true; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp"); }
+ void await_resume() const noexcept { PRINT ("susp-never-resume");}
+ };
+
+ /* NOTE: this has a DTOR to test that pathway. */
+ struct suspend_always_prt {
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp"); }
+ void await_resume() const noexcept { PRINT ("susp-always-resume"); }
+ ~suspend_always_prt() { PRINT ("susp-always-DTOR"); }
+ };
+
+ struct promise_type {
+ int value;
+ promise_type() { PRINT ("Created Promise"); }
+ ~promise_type() { PRINT ("Destroyed Promise"); }
+
+ auto get_return_object () {
+ PRINT ("get_return_object: handle from promise");
+ return handle_type::from_promise (*this);
+ }
+ auto initial_suspend () {
+ PRINT ("get initial_suspend (always)");
+ return suspend_always_prt{};
+ }
+ auto final_suspend () {
+ PRINT ("get final_suspend (always)");
+ return suspend_always_prt{};
+ }
+ void return_value (int v) {
+ PRINTF ("return_value () %d\n",v);
+ value = v;
+ }
+ int get_value (void) { return value; }
+ // Placeholder to satisfy parser, not doing exceptions yet.
+ void unhandled_exception() { /*exit(1);*/ }
+ };
+};
+
+// Require a ctor.
+struct nontriv {
+ int a, b, c;
+ nontriv (int _a, int _b, int _c) : a(_a), b(_b), c(_c) {}
+ virtual int getA () { return a; }
+};
+
+struct coro1
+f (nontriv &t) noexcept
+{
+ if (t.a > 30)
+ {
+ PRINTF ("coro1: about to return %d", t.b);
+ co_return t.b;
+ }
+ else if (t.a > 20)
+ {
+ PRINTF ("coro1: about to co-return %d", t.c);
+ co_return t.c;
+ }
+ else
+ {
+ PRINT ("coro1: about to return 0");
+ co_return 0;
+ }
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ nontriv test (25, 6174, 42);
+ struct coro1 x = f (test);
+ PRINT ("main: got coro1 - resuming");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ PRINT ("main: after resume");
+ int y = x.handle.promise().get_value();
+ if ( y != 42 )
+ abort ();
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ //x.handle.resume();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/gro_on_alloc_fail_0.C b/gcc/testsuite/g++.dg/coroutines/torture/gro_on_alloc_fail_0.C
new file mode 100644
index 0000000..40a3620
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/gro_on_alloc_fail_0.C
@@ -0,0 +1,137 @@
+// { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+# include <utility>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+// check the code-gen for the failed alloc return.
+
+#if __has_include(<new>)
+# include <new>
+#else
+
+// Required when get_return_object_on_allocation_failure() is defined by
+// the promise.
+// we need a no-throw new, and new etc. build the relevant pieces here to
+// avoid needing the headers in the test.
+
+namespace std {
+ struct nothrow_t {};
+ constexpr nothrow_t nothrow = {};
+ typedef __SIZE_TYPE__ size_t;
+} // end namespace std
+
+void* operator new(std::size_t, const std::nothrow_t&) noexcept;
+void operator delete(void* __p, const std::nothrow_t&) noexcept;
+#endif
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+# define PRINT(X)
+#else
+# define PRINT(X) puts(X)
+#endif
+
+struct coro1 {
+ struct promise_type;
+ using handle_type = coro::coroutine_handle<coro1::promise_type>;
+ handle_type handle;
+ coro1 () noexcept : handle(0) {}
+ coro1 (handle_type _handle) noexcept
+ : handle(_handle) {
+ PRINT("Created coro1 object from handle");
+ }
+ coro1 (const coro1 &) = delete; // no copying
+ coro1 (coro1 &&s) noexcept : handle(s.handle) {
+ s.handle = nullptr;
+ PRINT("coro1 mv ctor ");
+ }
+ coro1 &operator = (coro1 &&s) noexcept {
+ handle = s.handle;
+ s.handle = nullptr;
+ PRINT("coro1 op= ");
+ return *this;
+ }
+ ~coro1() noexcept {
+ PRINT("Destroyed coro1");
+ if ( handle )
+ handle.destroy();
+ }
+
+ struct suspend_never_prt {
+ bool await_ready() const noexcept { return true; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp");}
+ void await_resume() const noexcept { PRINT ("susp-never-resume");}
+ ~suspend_never_prt() {};
+ };
+
+ struct suspend_always_prt {
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp");}
+ void await_resume() const noexcept { PRINT ("susp-always-resume");}
+ };
+
+ struct promise_type {
+ promise_type() { PRINT ("Created Promise"); }
+ ~promise_type() { PRINT ("Destroyed Promise"); }
+
+ auto get_return_object () {
+ PRINT ("get_return_object: handle from promise");
+ return handle_type::from_promise (*this);
+ }
+ auto initial_suspend () {
+ PRINT ("get initial_suspend (always)");
+ return suspend_always_prt{};
+ }
+ auto final_suspend () {
+ PRINT ("get final_suspend (always)");
+ return suspend_always_prt{};
+ }
+ void return_void () {
+ PRINT ("return_void ()");
+ }
+ // Placeholder to satisfy parser, not doing exceptions yet.
+ void unhandled_exception() { /*exit(1);*/ }
+ static coro1 get_return_object_on_allocation_failure () noexcept;
+ }; // promise
+}; // coro1
+
+coro1 coro1::promise_type::
+get_return_object_on_allocation_failure () noexcept {
+ PRINT ("alloc fail return");
+ return coro1 (nullptr);
+}
+
+struct coro1
+f () noexcept
+{
+ PRINT ("coro1: about to return");
+ co_return;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 x = f ();
+ PRINT ("main: got coro1 - resuming");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ PRINT ("main: after resume");
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ //x.handle.resume();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/local-var-0.C b/gcc/testsuite/g++.dg/coroutines/torture/local-var-0.C
new file mode 100644
index 0000000..f78787b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/local-var-0.C
@@ -0,0 +1,123 @@
+// { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+# include <utility>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+// Test returning an int.
+// We will use the promise to contain this to avoid having to include
+// additional C++ headers.
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+# define PRINT(X)
+# define PRINTF (void)
+#else
+# define PRINT(X) puts(X)
+# define PRINTF printf
+#endif
+
+struct coro1 {
+ struct promise_type;
+ using handle_type = coro::coroutine_handle<coro1::promise_type>;
+ handle_type handle;
+ coro1 () : handle(0) {}
+ coro1 (handle_type _handle)
+ : handle(_handle) {
+ PRINT("Created coro1 object from handle");
+ }
+ coro1 (const coro1 &) = delete; // no copying
+ coro1 (coro1 &&s) : handle(s.handle) {
+ s.handle = nullptr;
+ PRINT("coro1 mv ctor ");
+ }
+ coro1 &operator = (coro1 &&s) {
+ handle = s.handle;
+ s.handle = nullptr;
+ PRINT("coro1 op= ");
+ return *this;
+ }
+ ~coro1() {
+ PRINT("Destroyed coro1");
+ if ( handle )
+ handle.destroy();
+ }
+
+ struct suspend_never_prt {
+ bool await_ready() const noexcept { return true; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp"); }
+ void await_resume() const noexcept { PRINT ("susp-never-resume");}
+ };
+
+ /* NOTE: this has a DTOR to test that pathway. */
+ struct suspend_always_prt {
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp"); }
+ void await_resume() const noexcept { PRINT ("susp-always-resume"); }
+ ~suspend_always_prt() { PRINT ("susp-always-DTOR"); }
+ };
+
+ struct promise_type {
+ int value;
+ promise_type() { PRINT ("Created Promise"); }
+ ~promise_type() { PRINT ("Destroyed Promise"); }
+
+ auto get_return_object () {
+ PRINT ("get_return_object: handle from promise");
+ return handle_type::from_promise (*this);
+ }
+ auto initial_suspend () {
+ PRINT ("get initial_suspend (always)");
+ return suspend_always_prt{};
+ }
+ auto final_suspend () {
+ PRINT ("get final_suspend (always)");
+ return suspend_always_prt{};
+ }
+ void return_value (int v) {
+ PRINTF ("return_value () %d\n",v);
+ value = v;
+ }
+ int get_value (void) { return value; }
+ // Placeholder to satisfy parser, not doing exceptions yet.
+ void unhandled_exception() { /*exit(1);*/ }
+ };
+};
+
+struct coro1
+f () noexcept
+{
+ const int answer = 42;
+ PRINTF ("coro1: about to return %d\n", answer);
+ co_return answer;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 x = f ();
+ PRINT ("main: got coro1 - resuming");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ PRINT ("main: after resume");
+ int y = x.handle.promise().get_value();
+ if ( y != 42 )
+ abort ();
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ //x.handle.resume();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/local-var-1.C b/gcc/testsuite/g++.dg/coroutines/torture/local-var-1.C
new file mode 100644
index 0000000..9dd7ab3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/local-var-1.C
@@ -0,0 +1,123 @@
+// { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+# include <utility>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+// Test returning an int.
+// We will use the promise to contain this to avoid having to include
+// additional C++ headers.
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+# define PRINT(X)
+# define PRINTF (void)
+#else
+# define PRINT(X) puts(X)
+# define PRINTF printf
+#endif
+
+struct coro1 {
+ struct promise_type;
+ using handle_type = coro::coroutine_handle<coro1::promise_type>;
+ handle_type handle;
+ coro1 () : handle(0) {}
+ coro1 (handle_type _handle)
+ : handle(_handle) {
+ PRINT("Created coro1 object from handle");
+ }
+ coro1 (const coro1 &) = delete; // no copying
+ coro1 (coro1 &&s) : handle(s.handle) {
+ s.handle = nullptr;
+ PRINT("coro1 mv ctor ");
+ }
+ coro1 &operator = (coro1 &&s) {
+ handle = s.handle;
+ s.handle = nullptr;
+ PRINT("coro1 op= ");
+ return *this;
+ }
+ ~coro1() {
+ PRINT("Destroyed coro1");
+ if ( handle )
+ handle.destroy();
+ }
+
+ struct suspend_never_prt {
+ bool await_ready() const noexcept { return true; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp"); }
+ void await_resume() const noexcept { PRINT ("susp-never-resume");}
+ };
+
+ /* NOTE: this has a DTOR to test that pathway. */
+ struct suspend_always_prt {
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp"); }
+ void await_resume() const noexcept { PRINT ("susp-always-resume"); }
+ ~suspend_always_prt() { PRINT ("susp-always-DTOR"); }
+ };
+
+ struct promise_type {
+ int value;
+ promise_type() { PRINT ("Created Promise"); }
+ ~promise_type() { PRINT ("Destroyed Promise"); }
+
+ auto get_return_object () {
+ PRINT ("get_return_object: handle from promise");
+ return handle_type::from_promise (*this);
+ }
+ auto initial_suspend () {
+ PRINT ("get initial_suspend (always)");
+ return suspend_always_prt{};
+ }
+ auto final_suspend () {
+ PRINT ("get final_suspend (always)");
+ return suspend_always_prt{};
+ }
+ void return_value (int v) {
+ PRINTF ("return_value () %d\n",v);
+ value = v;
+ }
+ int get_value (void) { return value; }
+ // Placeholder to satisfy parser, not doing exceptions yet.
+ void unhandled_exception() { /*exit(1);*/ }
+ };
+};
+
+struct coro1
+f (int x) noexcept
+{
+ int answer = x + 6132;
+ PRINTF ("coro1: about to return %d\n", answer);
+ co_return answer;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 x = f (42);
+ PRINT ("main: got coro1 - resuming");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ PRINT ("main: after resume");
+ int y = x.handle.promise().get_value();
+ if ( y != 6174 )
+ abort ();
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ //x.handle.resume();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/local-var-2.C b/gcc/testsuite/g++.dg/coroutines/torture/local-var-2.C
new file mode 100644
index 0000000..886c913
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/local-var-2.C
@@ -0,0 +1,135 @@
+// { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+# include <utility>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+// Test returning an int.
+// We will use the promise to contain this to avoid having to include
+// additional C++ headers.
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+# define PRINT(X)
+# define PRINTF (void)
+#else
+# define PRINT(X) puts(X)
+# define PRINTF printf
+#endif
+
+struct coro1 {
+ struct promise_type;
+ using handle_type = coro::coroutine_handle<coro1::promise_type>;
+ handle_type handle;
+ coro1 () : handle(0) {}
+ coro1 (handle_type _handle)
+ : handle(_handle) {
+ PRINT("Created coro1 object from handle");
+ }
+ coro1 (const coro1 &) = delete; // no copying
+ coro1 (coro1 &&s) : handle(s.handle) {
+ s.handle = nullptr;
+ PRINT("coro1 mv ctor ");
+ }
+ coro1 &operator = (coro1 &&s) {
+ handle = s.handle;
+ s.handle = nullptr;
+ PRINT("coro1 op= ");
+ return *this;
+ }
+ ~coro1() {
+ PRINT("Destroyed coro1");
+ if ( handle )
+ handle.destroy();
+ }
+
+ struct suspend_never_prt {
+ bool await_ready() const noexcept { return true; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp"); }
+ void await_resume() const noexcept { PRINT ("susp-never-resume");}
+ };
+
+ /* NOTE: this has a DTOR to test that pathway. */
+ struct suspend_always_prt {
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp"); }
+ void await_resume() const noexcept { PRINT ("susp-always-resume"); }
+ ~suspend_always_prt() { PRINT ("susp-always-DTOR"); }
+ };
+
+ struct promise_type {
+ int value;
+ promise_type() { PRINT ("Created Promise"); }
+ ~promise_type() { PRINT ("Destroyed Promise"); }
+
+ auto get_return_object () {
+ PRINT ("get_return_object: handle from promise");
+ return handle_type::from_promise (*this);
+ }
+ auto initial_suspend () {
+ PRINT ("get initial_suspend (always)");
+ return suspend_always_prt{};
+ }
+ auto final_suspend () {
+ PRINT ("get final_suspend (always)");
+ return suspend_always_prt{};
+ }
+ void return_value (int v) {
+ PRINTF ("return_value () %d\n",v);
+ value = v;
+ }
+ int get_value (void) { return value; }
+ // Placeholder to satisfy parser, not doing exceptions yet.
+ void unhandled_exception() { /*exit(1);*/ }
+ };
+};
+
+struct coro1
+f (int x) noexcept
+{
+ int y = x;
+ const int test = 20;
+ if (y > test)
+ {
+ int fred = y - 20;
+ PRINTF ("coro1: about to return %d\n", fred);
+ co_return fred;
+ }
+ else
+ {
+ PRINT ("coro1: about to return the answer\n");
+ co_return y;
+ }
+
+ co_return x;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 x = f (6194);
+ PRINT ("main: got coro1 - resuming");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ PRINT ("main: after resume");
+ int y = x.handle.promise().get_value();
+ if ( y != 6174 )
+ abort ();
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ //x.handle.resume();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/local-var-3.C b/gcc/testsuite/g++.dg/coroutines/torture/local-var-3.C
new file mode 100644
index 0000000..53b7e71
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/local-var-3.C
@@ -0,0 +1,153 @@
+// { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+# include <utility>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+// Test modifying a local var and yielding several instances of it.
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+# define PRINT(X)
+# define PRINTF (void)
+#else
+# define PRINT(X) puts(X)
+# define PRINTF printf
+#endif
+
+struct coro1 {
+ struct promise_type;
+ using handle_type = coro::coroutine_handle<coro1::promise_type>;
+ handle_type handle;
+ coro1 () : handle(0) {}
+ coro1 (handle_type _handle)
+ : handle(_handle) {
+ PRINT("Created coro1 object from handle");
+ }
+ coro1 (const coro1 &) = delete; // no copying
+ coro1 (coro1 &&s) : handle(s.handle) {
+ s.handle = nullptr;
+ PRINT("coro1 mv ctor ");
+ }
+ coro1 &operator = (coro1 &&s) {
+ handle = s.handle;
+ s.handle = nullptr;
+ PRINT("coro1 op= ");
+ return *this;
+ }
+ ~coro1() {
+ PRINT("Destroyed coro1");
+ if ( handle )
+ handle.destroy();
+ }
+
+ struct suspend_never_prt {
+ bool await_ready() const noexcept { return true; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp"); }
+ void await_resume() const noexcept { PRINT ("susp-never-resume");}
+ };
+
+ /* NOTE: this has a DTOR to test that pathway. */
+ struct suspend_always_prt {
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp"); }
+ void await_resume() const noexcept { PRINT ("susp-always-resume"); }
+ ~suspend_always_prt() { PRINT ("susp-always-DTOR"); }
+ };
+
+ struct promise_type {
+ int value;
+ promise_type() { PRINT ("Created Promise"); }
+ ~promise_type() { PRINT ("Destroyed Promise"); }
+
+ auto get_return_object () {
+ PRINT ("get_return_object: handle from promise");
+ return handle_type::from_promise (*this);
+ }
+ auto initial_suspend () {
+ PRINT ("get initial_suspend (always)");
+ return suspend_always_prt{};
+ }
+ auto final_suspend () {
+ PRINT ("get final_suspend (always)");
+ return suspend_always_prt{};
+ }
+ void return_value (int v) {
+ PRINTF ("return_value () %d\n",v);
+ value = v;
+ }
+ auto yield_value (int v) {
+ PRINTF ("yield_value () %d and suspend always\n",v);
+ value = v;
+ return suspend_always_prt{};
+ }
+ int get_value (void) { return value; }
+ // Placeholder to satisfy parser, not doing exceptions yet.
+ void unhandled_exception() { /*exit(1);*/ }
+ };
+};
+
+struct coro1
+f (int start) noexcept
+{
+ int value = start;
+ PRINT ("f: about to yield start");
+ co_yield start;
+
+ value -= 31;
+ PRINT ("f: about to yield (value-31)");
+ co_yield value;
+
+ value += 6163;
+ PRINT ("f: about to return (value+6163)");
+ co_return value;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 x = f (42);
+ PRINT ("main: got coro1 - resuming (1)");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ PRINT ("main: after resume (1)");
+ int y = x.handle.promise().get_value();
+ if ( y != 42 )
+ abort ();
+ PRINT ("main: apparently got 42 - resuming (2)");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ PRINT ("main: after resume (2)");
+ y = x.handle.promise().get_value();
+ if ( y != 11 )
+ abort ();
+ PRINT ("main: apparently got 11 - resuming (3)");
+ if (x.handle.done())
+ {
+ PRINT ("main: done?");
+ abort();
+ }
+ x.handle.resume();
+ PRINT ("main: after resume (2) checking return");
+ y = x.handle.promise().get_value();
+ if ( y != 6174 )
+ abort ();
+ PRINT ("main: apparently got 6174");
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/local-var-4.C b/gcc/testsuite/g++.dg/coroutines/torture/local-var-4.C
new file mode 100644
index 0000000..5d4c0ff
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/local-var-4.C
@@ -0,0 +1,162 @@
+// { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+# include <utility>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+// Test modifying a local var and yielding several instances of it.
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+# define PRINT(X)
+# define PRINTF (void)
+#else
+# define PRINT(X) puts(X)
+# define PRINTF printf
+#endif
+
+struct coro1 {
+ struct promise_type;
+ using handle_type = coro::coroutine_handle<coro1::promise_type>;
+ handle_type handle;
+ coro1 () : handle(0) {}
+ coro1 (handle_type _handle)
+ : handle(_handle) {
+ PRINT("Created coro1 object from handle");
+ }
+ coro1 (const coro1 &) = delete; // no copying
+ coro1 (coro1 &&s) : handle(s.handle) {
+ s.handle = nullptr;
+ PRINT("coro1 mv ctor ");
+ }
+ coro1 &operator = (coro1 &&s) {
+ handle = s.handle;
+ s.handle = nullptr;
+ PRINT("coro1 op= ");
+ return *this;
+ }
+ ~coro1() {
+ PRINT("Destroyed coro1");
+ if ( handle )
+ handle.destroy();
+ }
+
+ struct suspend_never_prt {
+ bool await_ready() const noexcept { return true; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp"); }
+ void await_resume() const noexcept { PRINT ("susp-never-resume");}
+ };
+
+ /* NOTE: this has a DTOR to test that pathway. */
+ struct suspend_always_prt {
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp"); }
+ void await_resume() const noexcept { PRINT ("susp-always-resume"); }
+ ~suspend_always_prt() { PRINT ("susp-always-DTOR"); }
+ };
+
+ struct promise_type {
+ int value;
+ promise_type() { PRINT ("Created Promise"); }
+ ~promise_type() { PRINT ("Destroyed Promise"); }
+
+ auto get_return_object () {
+ PRINT ("get_return_object: handle from promise");
+ return handle_type::from_promise (*this);
+ }
+ auto initial_suspend () {
+ PRINT ("get initial_suspend (always)");
+ return suspend_always_prt{};
+ }
+ auto final_suspend () {
+ PRINT ("get final_suspend (always)");
+ return suspend_always_prt{};
+ }
+ void return_value (int v) {
+ PRINTF ("return_value () %d\n",v);
+ value = v;
+ }
+ auto yield_value (int v) {
+ PRINTF ("yield_value () %d and suspend always\n",v);
+ value = v;
+ return suspend_always_prt{};
+ }
+ int get_value (void) { return value; }
+ // Placeholder to satisfy parser, not doing exceptions yet.
+ void unhandled_exception() { /*exit(1);*/ }
+ };
+};
+
+struct coro1
+f (int start) noexcept
+{
+ int value = start;
+ {
+ int value = start + 5;
+ {
+ int value = start + 20;
+ }
+ {
+ int value = start + 1;
+ PRINT ("f: about to yield start");
+ co_yield value;
+ }
+ }
+
+ value -= 31;
+ PRINT ("f: about to yield (value-31)");
+ co_yield value;
+
+ value += 6163;
+ PRINT ("f: about to return (value+6163)");
+ co_return value;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 x = f (42);
+ PRINT ("main: got coro1 - resuming (1)");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ PRINT ("main: after resume (1)");
+ int y = x.handle.promise().get_value();
+ if ( y != 43 )
+ abort ();
+ PRINT ("main: apparently got 42 - resuming (2)");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ PRINT ("main: after resume (2)");
+ y = x.handle.promise().get_value();
+ if ( y != 11 )
+ abort ();
+ PRINT ("main: apparently got 11 - resuming (3)");
+ if (x.handle.done())
+ {
+ PRINT ("main: done?");
+ abort();
+ }
+ x.handle.resume();
+ PRINT ("main: after resume (2) checking return");
+ y = x.handle.promise().get_value();
+ if ( y != 6174 )
+ abort ();
+ PRINT ("main: apparently got 6174");
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/mid-suspend-destruction-0.C b/gcc/testsuite/g++.dg/coroutines/torture/mid-suspend-destruction-0.C
new file mode 100644
index 0000000..6e68688
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/mid-suspend-destruction-0.C
@@ -0,0 +1,127 @@
+// { dg-do run }
+// { dg-output "main: returning\n" }
+// { dg-output "Destroyed coro1\n" }
+// { dg-output "Destroyed suspend_always_prt\n" }
+// { dg-output "Destroyed Promise\n" }
+
+// Check that we still get the right DTORs run when we let a suspended coro
+// go out of scope.
+
+#if __clang__
+# include <experimental/coroutine>
+# include <utility>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+// GRO differs from the eventual return type.
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+# define PRINT(X)
+#else
+# define PRINT(X) puts(X)
+#endif
+
+struct coro1 {
+ struct promise_type;
+ using handle_type = coro::coroutine_handle<coro1::promise_type>;
+ handle_type handle;
+ coro1 () : handle(0) {}
+ coro1 (handle_type _handle)
+ : handle(_handle) {
+ PRINT("Created coro1 object from handle");
+ }
+ coro1 (const coro1 &) = delete; // no copying
+ coro1 (coro1 &&s) : handle(s.handle) {
+ s.handle = nullptr;
+ PRINT("coro1 mv ctor ");
+ }
+ coro1 &operator = (coro1 &&s) {
+ handle = s.handle;
+ s.handle = nullptr;
+ PRINT("coro1 op= ");
+ return *this;
+ }
+ ~coro1() {
+ printf ("Destroyed coro1\n");
+ if ( handle )
+ handle.destroy();
+ }
+
+ struct suspend_never_prt {
+ bool await_ready() const noexcept { return true; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp");}
+ void await_resume() const noexcept { PRINT ("susp-never-resume");}
+ ~suspend_never_prt() {};
+ };
+
+ struct suspend_always_prt {
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp");}
+ void await_resume() const noexcept { PRINT ("susp-always-resume");}
+ ~suspend_always_prt() { printf ("Destroyed suspend_always_prt\n"); }
+ };
+
+ struct promise_type {
+ promise_type() { PRINT ("Created Promise"); }
+ ~promise_type() { printf ("Destroyed Promise\n"); }
+
+ auto get_return_object () {
+ PRINT ("get_return_object: handle from promise");
+ return handle_type::from_promise (*this);
+ }
+ auto initial_suspend () {
+ PRINT ("get initial_suspend (always)");
+ return suspend_always_prt{};
+ }
+ auto final_suspend () {
+ PRINT ("get final_suspend (always)");
+ return suspend_always_prt{};
+ }
+ void return_void () {
+ PRINT ("return_void ()");
+ }
+ // Placeholder to satisfy parser, not doing exceptions yet.
+ void unhandled_exception() { /*exit(1);*/ }
+ };
+};
+
+struct coro1
+f () noexcept
+{
+ PRINT ("coro1: about to return");
+ co_return;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 x = f ();
+ PRINT ("main: got coro1 - resuming");
+ if (x.handle.done())
+ {
+ PRINT ("main: f() should be suspended, says it's done");
+ abort();
+ }
+
+#if __has_builtin (__builtin_coro_suspended)
+ if (! x.handle.suspended_p())
+ {
+ PRINT ("main: f() should be suspended, but says it isn't");
+ abort();
+ }
+#endif
+
+ /* We are suspended... so let everything out of scope and therefore
+ destroy it. */
+
+ puts ("main: returning");
+ return 0;
+}
--
2.8.1
next prev parent reply other threads:[~2019-11-17 10:28 UTC|newest]
Thread overview: 30+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-11-17 10:23 [C++ coroutines 0/6] Implement C++ coroutines Iain Sandoe
2019-11-17 10:24 ` [C++ coroutines 1/6] Common code and base definitions Iain Sandoe
2019-11-17 10:24 ` [C++ coroutines 2/6] Define builtins and internal functions Iain Sandoe
2019-11-17 10:26 ` [C++ coroutines 3/6] Front end parsing and transforms Iain Sandoe
2019-11-17 10:26 ` [C++ coroutines 4/6] Middle end expanders " Iain Sandoe
2019-11-17 10:27 ` [C++ coroutines 5/6] Standard library header Iain Sandoe
2019-11-17 10:28 ` Iain Sandoe [this message]
2019-11-19 10:01 ` [C++ coroutines 6/6] Testsuite Richard Biener
2020-01-09 12:40 ` [C++ coroutines 6/7, v2] Testsuite Iain Sandoe
2020-01-09 13:00 ` Iain Sandoe
2020-01-09 13:17 ` Richard Biener
2019-11-20 11:13 ` [C++ coroutines 6/6] Testsuite JunMa
2019-11-20 11:22 ` Iain Sandoe
2019-11-20 13:11 ` JunMa
2019-11-17 16:19 ` [C++ coroutines 5/6] Standard library header Jonathan Wakely
2020-01-09 12:39 ` [C++ coroutines 5/7, v2] " Iain Sandoe
2020-01-09 13:50 ` Jonathan Wakely
2020-01-09 19:57 ` [C++ coroutines 5/7, v3] " Iain Sandoe
2020-01-09 21:21 ` Jonathan Wakely
2019-11-19 10:00 ` [C++ coroutines 4/6] Middle end expanders and transforms Richard Biener
2020-01-09 12:38 ` [C++ coroutines 4/7, v2] " Iain Sandoe
2020-01-09 14:38 ` Richard Biener
2019-11-18 13:23 ` [C++ coroutines 3/6] Front end parsing " Nathan Sidwell
2019-11-19 18:40 ` Nathan Sidwell
2020-01-09 12:37 ` [C++ coroutines 3/7, v2] " Iain Sandoe
2019-11-17 16:54 ` [C++ coroutines 2/6] Define builtins and internal functions Jeff Law
2020-01-09 12:36 ` [C++ coroutines 2/7, v2] " Iain Sandoe
2019-11-17 15:49 ` [C++ coroutines 1/6] Common code and base definitions Jeff Law
2020-01-09 12:36 ` [C++ coroutines 1/7] " Iain Sandoe
2019-11-18 12:35 ` [C++ coroutines 0/6] Implement C++ coroutines Nathan Sidwell
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=BCAC2431-0DF3-4592-AC8B-2C8912743D4A@sandoe.co.uk \
--to=iain@sandoe.co.uk \
--cc=gcc-patches@gcc.gnu.org \
--cc=libstdc++@gcc.gnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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).