This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
Re: [C++ coroutines 6/6] Testsuite.
On Sun, Nov 17, 2019 at 11:29 AM Iain Sandoe <iain@sandoe.co.uk> wrote:
>
>
> 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).
OK once the rest is approved.
Richard.
> 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
>
>