Message ID | 4D28389F-09A8-4777-9CCD-8E91E7C24524@sandoe.co.uk |
---|---|
State | New |
Headers | show |
Series | [C++,coroutines,1/7] Common code and base definitions. | expand |
On Thu, Jan 9, 2020 at 1:39 PM Iain Sandoe <iain@sandoe.co.uk> wrote: > > Richard Biener <richard.guenther@gmail.com> wrote: > > > 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. > > Amended version attached, for the record, with cleanups and more tests as > needed to cover the other changes in the series. > > SVN commit IDs noted in the revised header. > Still OK? Sure. > Iain > > ====== > > 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). > > Squashed commits: > > r278441 - Remove reference to an unused builtin from coro.h > r278442 - Fix a code-gen error in coro_maybe_expand_co_return. > r278453 - Address review comments, updated error message, > r278480 - Address review comments, new tests > r278776 - JAddress review comments, update to make move of coro header easier > r279020 - Fix bug in handling co_return co_await. > r279046 - more tidyup, also factor code. > r279049 - add more co-await tests and rename to consistent. > r279050 - Add more co-yield syntax tests. > r279078 - More testsuite refactoring. > r279080 - Fix a bug in type dependency checks. > r279095 - More testsuite refactoring. > r279099 - Update operator overload tests. > r279190 - Update co-return tests. > r279191 - Just fix a format warning. > r279198 - Improve test coverage, initial class tests. > r279245 - Test syntax error from lambda with auto return. > r279318 - Test function-like lambdas. > r279383 - Use correct dg options for new compile tests. > r279385 - Fix PR 92933 unnamed locals. > r279396 - Fix lambda capture of references. > r279403 - Add testcase to cover non-constant param refs. > r279462 - Fix a bug where await_resume methods return references. > r279693 - Rename co-await testcases to be consistent [NFC]. > r279694 - Rename co-return testcases to be consistent [NFC]. > r279711 - Rename co-yield testcases to be consistent [NFC]. > r279712 - Rename func params testcases to be consistent [NFC]. > r279719 - Fix a typo in a testcase name. > r279805 - Address review comments, prepare to move coroutine header. > r279817 - Update copyright year. > r280004 - Add promise overloads of new and delete. > > gcc/testsuite/ChangeLog: > > 2020-01-09 Iain Sandoe <iain@sandoe.co.uk> > > * g++.dg/coroutines/co-await-syntax-00-needs-expr.C: New test. > * g++.dg/coroutines/co-await-syntax-01-outside-fn.C: New test. > * g++.dg/coroutines/co-await-syntax-02-outside-fn.C: New test. > * g++.dg/coroutines/co-await-syntax-03-auto.C: New test. > * g++.dg/coroutines/co-await-syntax-04-ctor-dtor.C: New test. > * g++.dg/coroutines/co-await-syntax-05-constexpr.C: New test. > * g++.dg/coroutines/co-await-syntax-06-main.C: New test. > * g++.dg/coroutines/co-await-syntax-07-varargs.C: New test. > * g++.dg/coroutines/co-await-syntax-08-lambda-auto.C: New test. > * g++.dg/coroutines/co-return-syntax-01-outside-fn.C: New test. > * g++.dg/coroutines/co-return-syntax-02-outside-fn.C: New test. > * g++.dg/coroutines/co-return-syntax-03-auto.C: New test. > * g++.dg/coroutines/co-return-syntax-04-ctor-dtor.C: New test. > * g++.dg/coroutines/co-return-syntax-05-constexpr-fn.C: New test. > * g++.dg/coroutines/co-return-syntax-06-main.C: New test. > * g++.dg/coroutines/co-return-syntax-07-vararg.C: New test. > * g++.dg/coroutines/co-return-syntax-08-bad-return.C: New test. > * g++.dg/coroutines/co-return-syntax-09-lambda-auto.C: New test. > * g++.dg/coroutines/co-yield-syntax-00-needs-expr.C: New test. > * g++.dg/coroutines/co-yield-syntax-01-outside-fn.C: New test. > * g++.dg/coroutines/co-yield-syntax-02-outside-fn.C: New test. > * g++.dg/coroutines/co-yield-syntax-03-auto.C: New test. > * g++.dg/coroutines/co-yield-syntax-04-ctor-dtor.C: New test. > * g++.dg/coroutines/co-yield-syntax-05-constexpr.C: New test. > * g++.dg/coroutines/co-yield-syntax-06-main.C: New test. > * g++.dg/coroutines/co-yield-syntax-07-varargs.C: New test. > * g++.dg/coroutines/co-yield-syntax-08-needs-expr.C: New test. > * g++.dg/coroutines/co-yield-syntax-09-lambda-auto.C: New test. > * g++.dg/coroutines/coro-builtins.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 file. > * g++.dg/coroutines/coro1-ret-int-yield-int.h: New file. > * g++.dg/coroutines/coroutines.exp: New file. > * g++.dg/coroutines/torture/alloc-00-gro-on-alloc-fail.C: New test. > * g++.dg/coroutines/torture/alloc-01-overload-newdel.C: New test. > * g++.dg/coroutines/torture/call-00-co-aw-arg.C: New test. > * g++.dg/coroutines/torture/call-01-multiple-co-aw.C: New test. > * g++.dg/coroutines/torture/call-02-temp-co-aw.C: New test. > * g++.dg/coroutines/torture/call-03-temp-ref-co-aw.C: New test. > * g++.dg/coroutines/torture/class-00-co-ret.C: New test. > * g++.dg/coroutines/torture/class-01-co-ret-parm.C: New test. > * g++.dg/coroutines/torture/class-02-templ-parm.C: New test. > * g++.dg/coroutines/torture/class-03-operator-templ-parm.C: New test. > * g++.dg/coroutines/torture/class-04-lambda-1.C: New test. > * g++.dg/coroutines/torture/class-05-lambda-capture-copy-local.C: New test. > * g++.dg/coroutines/torture/class-06-lambda-capture-ref.C: New test. > * g++.dg/coroutines/torture/co-await-00-trivial.C: New test. > * g++.dg/coroutines/torture/co-await-01-with-value.C: New test. > * g++.dg/coroutines/torture/co-await-02-xform.C: New test. > * g++.dg/coroutines/torture/co-await-03-rhs-op.C: New test. > * g++.dg/coroutines/torture/co-await-04-control-flow.C: New test. > * g++.dg/coroutines/torture/co-await-05-loop.C: New test. > * g++.dg/coroutines/torture/co-await-06-ovl.C: New test. > * g++.dg/coroutines/torture/co-await-07-tmpl.C: New test. > * g++.dg/coroutines/torture/co-await-08-cascade.C: New test. > * g++.dg/coroutines/torture/co-await-09-pair.C: New test. > * g++.dg/coroutines/torture/co-await-10-template-fn-arg.C: New test. > * g++.dg/coroutines/torture/co-await-11-forwarding.C: New test. > * g++.dg/coroutines/torture/co-await-12-operator-2.C: New test. > * g++.dg/coroutines/torture/co-await-13-return-ref.C: New test. > * g++.dg/coroutines/torture/co-ret-00-void-return-is-ready.C: New test. > * g++.dg/coroutines/torture/co-ret-01-void-return-is-suspend.C: New test. > * g++.dg/coroutines/torture/co-ret-03-different-GRO-type.C: New test. > * g++.dg/coroutines/torture/co-ret-04-GRO-nontriv.C: New test. > * g++.dg/coroutines/torture/co-ret-05-return-value.C: New test. > * g++.dg/coroutines/torture/co-ret-06-template-promise-val-1.C: New test. > * g++.dg/coroutines/torture/co-ret-07-void-cast-expr.C: New test. > * g++.dg/coroutines/torture/co-ret-08-template-cast-ret.C: New test. > * g++.dg/coroutines/torture/co-ret-09-bool-await-susp.C: New test. > * g++.dg/coroutines/torture/co-ret-10-expression-evaluates-once.C: New test. > * g++.dg/coroutines/torture/co-ret-11-co-ret-co-await.C: New test. > * g++.dg/coroutines/torture/co-ret-12-co-ret-fun-co-await.C: New test. > * g++.dg/coroutines/torture/co-ret-13-template-2.C: New test. > * g++.dg/coroutines/torture/co-ret-14-template-3.C: New test. > * g++.dg/coroutines/torture/co-yield-00-triv.C: New test. > * g++.dg/coroutines/torture/co-yield-01-multi.C: New test. > * g++.dg/coroutines/torture/co-yield-02-loop.C: New test. > * g++.dg/coroutines/torture/co-yield-03-tmpl.C: New test. > * g++.dg/coroutines/torture/co-yield-04-complex-local-state.C: New test. > * g++.dg/coroutines/torture/co-yield-05-co-aw.C: New test. > * g++.dg/coroutines/torture/co-yield-06-fun-parm.C: New test. > * g++.dg/coroutines/torture/co-yield-07-template-fn-param.C: New test. > * g++.dg/coroutines/torture/co-yield-08-more-refs.C: New test. > * g++.dg/coroutines/torture/co-yield-09-more-templ-refs.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-00.C: New test. > * g++.dg/coroutines/torture/func-params-01.C: New test. > * g++.dg/coroutines/torture/func-params-02.C: New test. > * g++.dg/coroutines/torture/func-params-03.C: New test. > * g++.dg/coroutines/torture/func-params-04.C: New test. > * g++.dg/coroutines/torture/func-params-05.C: New test. > * g++.dg/coroutines/torture/func-params-06.C: New test. > * g++.dg/coroutines/torture/lambda-00-co-ret.C: New test. > * g++.dg/coroutines/torture/lambda-01-co-ret-parm.C: New test. > * g++.dg/coroutines/torture/lambda-02-co-yield-values.C: New test. > * g++.dg/coroutines/torture/lambda-03-auto-parm-1.C: New test. > * g++.dg/coroutines/torture/lambda-04-templ-parm.C: New test. > * g++.dg/coroutines/torture/lambda-05-capture-copy-local.C: New test. > * g++.dg/coroutines/torture/lambda-06-multi-capture.C: New test. > * g++.dg/coroutines/torture/lambda-07-multi-yield.C: New test. > * g++.dg/coroutines/torture/lambda-08-co-ret-parm-ref.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/torture/pr92933.C: New test. > --- > .../coroutines/co-await-syntax-00-needs-expr.C | 7 + > .../coroutines/co-await-syntax-01-outside-fn.C | 5 + > .../coroutines/co-await-syntax-02-outside-fn.C | 5 + > .../g++.dg/coroutines/co-await-syntax-03-auto.C | 16 ++ > .../coroutines/co-await-syntax-04-ctor-dtor.C | 8 + > .../coroutines/co-await-syntax-05-constexpr.C | 12 ++ > .../g++.dg/coroutines/co-await-syntax-06-main.C | 7 + > .../g++.dg/coroutines/co-await-syntax-07-varargs.C | 14 ++ > .../coroutines/co-await-syntax-08-lambda-auto.C | 19 +++ > .../coroutines/co-return-syntax-01-outside-fn.C | 6 + > .../coroutines/co-return-syntax-02-outside-fn.C | 5 + > .../g++.dg/coroutines/co-return-syntax-03-auto.C | 12 ++ > .../coroutines/co-return-syntax-04-ctor-dtor.C | 8 + > .../coroutines/co-return-syntax-05-constexpr-fn.C | 12 ++ > .../g++.dg/coroutines/co-return-syntax-06-main.C | 7 + > .../g++.dg/coroutines/co-return-syntax-07-vararg.C | 14 ++ > .../coroutines/co-return-syntax-08-bad-return.C | 43 ++++++ > .../coroutines/co-return-syntax-09-lambda-auto.C | 19 +++ > .../coroutines/co-yield-syntax-00-needs-expr.C | 7 + > .../coroutines/co-yield-syntax-01-outside-fn.C | 6 + > .../coroutines/co-yield-syntax-02-outside-fn.C | 6 + > .../g++.dg/coroutines/co-yield-syntax-03-auto.C | 12 ++ > .../coroutines/co-yield-syntax-04-ctor-dtor.C | 8 + > .../coroutines/co-yield-syntax-05-constexpr.C | 12 ++ > .../g++.dg/coroutines/co-yield-syntax-06-main.C | 7 + > .../g++.dg/coroutines/co-yield-syntax-07-varargs.C | 14 ++ > .../coroutines/co-yield-syntax-08-needs-expr.C | 37 +++++ > .../coroutines/co-yield-syntax-09-lambda-auto.C | 19 +++ > gcc/testsuite/g++.dg/coroutines/coro-builtins.C | 17 +++ > gcc/testsuite/g++.dg/coroutines/coro-missing-gro.C | 32 ++++ > .../g++.dg/coroutines/coro-missing-promise-yield.C | 33 ++++ > .../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 | 17 +++ > .../g++.dg/coroutines/coro-missing-ueh-2.C | 18 +++ > .../g++.dg/coroutines/coro-missing-ueh-3.C | 18 +++ > gcc/testsuite/g++.dg/coroutines/coro-missing-ueh.h | 23 +++ > gcc/testsuite/g++.dg/coroutines/coro-pre-proc.C | 9 ++ > gcc/testsuite/g++.dg/coroutines/coro.h | 151 +++++++++++++++++++ > .../g++.dg/coroutines/coro1-ret-int-yield-int.h | 133 ++++++++++++++++ > gcc/testsuite/g++.dg/coroutines/coroutines.exp | 50 ++++++ > .../torture/alloc-00-gro-on-alloc-fail.C | 118 +++++++++++++++ > .../coroutines/torture/alloc-01-overload-newdel.C | 120 +++++++++++++++ > .../g++.dg/coroutines/torture/call-00-co-aw-arg.C | 73 +++++++++ > .../coroutines/torture/call-01-multiple-co-aw.C | 73 +++++++++ > .../g++.dg/coroutines/torture/call-02-temp-co-aw.C | 72 +++++++++ > .../coroutines/torture/call-03-temp-ref-co-aw.C | 72 +++++++++ > .../g++.dg/coroutines/torture/class-00-co-ret.C | 41 +++++ > .../coroutines/torture/class-01-co-ret-parm.C | 57 +++++++ > .../coroutines/torture/class-02-templ-parm.C | 52 +++++++ > .../torture/class-03-operator-templ-parm.C | 52 +++++++ > .../g++.dg/coroutines/torture/class-04-lambda-1.C | 58 +++++++ > .../torture/class-05-lambda-capture-copy-local.C | 59 ++++++++ > .../torture/class-06-lambda-capture-ref.C | 59 ++++++++ > .../coroutines/torture/co-await-00-trivial.C | 52 +++++++ > .../coroutines/torture/co-await-01-with-value.C | 57 +++++++ > .../g++.dg/coroutines/torture/co-await-02-xform.C | 58 +++++++ > .../g++.dg/coroutines/torture/co-await-03-rhs-op.C | 58 +++++++ > .../coroutines/torture/co-await-04-control-flow.C | 50 ++++++ > .../g++.dg/coroutines/torture/co-await-05-loop.C | 51 +++++++ > .../g++.dg/coroutines/torture/co-await-06-ovl.C | 65 ++++++++ > .../g++.dg/coroutines/torture/co-await-07-tmpl.C | 132 ++++++++++++++++ > .../coroutines/torture/co-await-08-cascade.C | 63 ++++++++ > .../g++.dg/coroutines/torture/co-await-09-pair.C | 57 +++++++ > .../torture/co-await-10-template-fn-arg.C | 60 ++++++++ > .../coroutines/torture/co-await-11-forwarding.C | 43 ++++++ > .../coroutines/torture/co-await-12-operator-2.C | 66 ++++++++ > .../coroutines/torture/co-await-13-return-ref.C | 58 +++++++ > .../torture/co-ret-00-void-return-is-ready.C | 90 +++++++++++ > .../torture/co-ret-01-void-return-is-suspend.C | 94 ++++++++++++ > .../torture/co-ret-03-different-GRO-type.C | 92 ++++++++++++ > .../coroutines/torture/co-ret-04-GRO-nontriv.C | 109 ++++++++++++++ > .../coroutines/torture/co-ret-05-return-value.C | 38 +++++ > .../torture/co-ret-06-template-promise-val-1.C | 105 +++++++++++++ > .../coroutines/torture/co-ret-07-void-cast-expr.C | 44 ++++++ > .../torture/co-ret-08-template-cast-ret.C | 104 +++++++++++++ > .../coroutines/torture/co-ret-09-bool-await-susp.C | 97 ++++++++++++ > .../torture/co-ret-10-expression-evaluates-once.C | 49 ++++++ > .../coroutines/torture/co-ret-11-co-ret-co-await.C | 40 +++++ > .../torture/co-ret-12-co-ret-fun-co-await.C | 48 ++++++ > .../coroutines/torture/co-ret-13-template-2.C | 56 +++++++ > .../coroutines/torture/co-ret-14-template-3.C | 58 +++++++ > .../g++.dg/coroutines/torture/co-yield-00-triv.C | 129 ++++++++++++++++ > .../g++.dg/coroutines/torture/co-yield-01-multi.C | 64 ++++++++ > .../g++.dg/coroutines/torture/co-yield-02-loop.C | 68 +++++++++ > .../g++.dg/coroutines/torture/co-yield-03-tmpl.C | 140 +++++++++++++++++ > .../torture/co-yield-04-complex-local-state.C | 162 ++++++++++++++++++++ > .../g++.dg/coroutines/torture/co-yield-05-co-aw.C | 55 +++++++ > .../coroutines/torture/co-yield-06-fun-parm.C | 64 ++++++++ > .../torture/co-yield-07-template-fn-param.C | 71 +++++++++ > .../coroutines/torture/co-yield-08-more-refs.C | 68 +++++++++ > .../torture/co-yield-09-more-templ-refs.C | 68 +++++++++ > .../g++.dg/coroutines/torture/coro-torture.exp | 19 +++ > .../g++.dg/coroutines/torture/exceptions-test-0.C | 167 +++++++++++++++++++++ > .../g++.dg/coroutines/torture/func-params-00.C | 42 ++++++ > .../g++.dg/coroutines/torture/func-params-01.C | 45 ++++++ > .../g++.dg/coroutines/torture/func-params-02.C | 50 ++++++ > .../g++.dg/coroutines/torture/func-params-03.C | 49 ++++++ > .../g++.dg/coroutines/torture/func-params-04.C | 57 +++++++ > .../g++.dg/coroutines/torture/func-params-05.C | 57 +++++++ > .../g++.dg/coroutines/torture/func-params-06.C | 47 ++++++ > .../g++.dg/coroutines/torture/lambda-00-co-ret.C | 35 +++++ > .../coroutines/torture/lambda-01-co-ret-parm.C | 48 ++++++ > .../coroutines/torture/lambda-02-co-yield-values.C | 64 ++++++++ > .../coroutines/torture/lambda-03-auto-parm-1.C | 46 ++++++ > .../coroutines/torture/lambda-04-templ-parm.C | 47 ++++++ > .../torture/lambda-05-capture-copy-local.C | 66 ++++++++ > .../coroutines/torture/lambda-06-multi-capture.C | 48 ++++++ > .../coroutines/torture/lambda-07-multi-yield.C | 46 ++++++ > .../coroutines/torture/lambda-08-co-ret-parm-ref.C | 59 ++++++++ > .../g++.dg/coroutines/torture/local-var-0.C | 37 +++++ > .../g++.dg/coroutines/torture/local-var-1.C | 37 +++++ > .../g++.dg/coroutines/torture/local-var-2.C | 50 ++++++ > .../g++.dg/coroutines/torture/local-var-3.C | 65 ++++++++ > .../g++.dg/coroutines/torture/local-var-4.C | 75 +++++++++ > .../coroutines/torture/mid-suspend-destruction-0.C | 107 +++++++++++++ > gcc/testsuite/g++.dg/coroutines/torture/pr92933.C | 18 +++ > 117 files changed, 5986 insertions(+) > create mode 100644 gcc/testsuite/g++.dg/coroutines/co-await-syntax-00-needs-expr.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/co-await-syntax-01-outside-fn.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/co-await-syntax-02-outside-fn.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/co-await-syntax-03-auto.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/co-await-syntax-04-ctor-dtor.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/co-await-syntax-05-constexpr.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/co-await-syntax-06-main.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/co-await-syntax-07-varargs.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/co-await-syntax-08-lambda-auto.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/co-return-syntax-01-outside-fn.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/co-return-syntax-02-outside-fn.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/co-return-syntax-03-auto.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/co-return-syntax-04-ctor-dtor.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/co-return-syntax-05-constexpr-fn.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/co-return-syntax-06-main.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/co-return-syntax-07-vararg.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/co-return-syntax-08-bad-return.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/co-return-syntax-09-lambda-auto.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/co-yield-syntax-00-needs-expr.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/co-yield-syntax-01-outside-fn.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/co-yield-syntax-02-outside-fn.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/co-yield-syntax-03-auto.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/co-yield-syntax-04-ctor-dtor.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/co-yield-syntax-05-constexpr.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/co-yield-syntax-06-main.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/co-yield-syntax-07-varargs.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/co-yield-syntax-08-needs-expr.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/co-yield-syntax-09-lambda-auto.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-builtins.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/coro1-ret-int-yield-int.h > create mode 100644 gcc/testsuite/g++.dg/coroutines/coroutines.exp > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/alloc-00-gro-on-alloc-fail.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/alloc-01-overload-newdel.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/call-00-co-aw-arg.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/call-01-multiple-co-aw.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/call-02-temp-co-aw.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/call-03-temp-ref-co-aw.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/class-00-co-ret.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/class-01-co-ret-parm.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/class-02-templ-parm.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/class-03-operator-templ-parm.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/class-04-lambda-1.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/class-05-lambda-capture-copy-local.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/class-06-lambda-capture-ref.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-00-trivial.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-01-with-value.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-02-xform.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-03-rhs-op.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-04-control-flow.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-05-loop.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-06-ovl.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-07-tmpl.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-08-cascade.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-09-pair.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-10-template-fn-arg.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-11-forwarding.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-12-operator-2.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-13-return-ref.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-00-void-return-is-ready.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-01-void-return-is-suspend.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-03-different-GRO-type.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-04-GRO-nontriv.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-05-return-value.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-06-template-promise-val-1.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-07-void-cast-expr.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-08-template-cast-ret.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-09-bool-await-susp.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-10-expression-evaluates-once.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-11-co-ret-co-await.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-12-co-ret-fun-co-await.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-13-template-2.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-14-template-3.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-00-triv.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-01-multi.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-02-loop.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-03-tmpl.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-04-complex-local-state.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-05-co-aw.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-06-fun-parm.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-07-template-fn-param.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-08-more-refs.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-09-more-templ-refs.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-00.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/func-params-01.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/func-params-02.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/func-params-03.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/func-params-04.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/func-params-05.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/func-params-06.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/lambda-00-co-ret.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/lambda-01-co-ret-parm.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/lambda-02-co-yield-values.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/lambda-03-auto-parm-1.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/lambda-04-templ-parm.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/lambda-05-capture-copy-local.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/lambda-06-multi-capture.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/lambda-07-multi-yield.C > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/lambda-08-co-ret-parm-ref.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 > create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/pr92933.C > > diff --git a/gcc/testsuite/g++.dg/coroutines/co-await-syntax-00-needs-expr.C b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-00-needs-expr.C > new file mode 100644 > index 0000000000..d068c3d19a > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-00-needs-expr.C > @@ -0,0 +1,7 @@ > +// { dg-additional-options "-fsyntax-only -w" } > + > +#include "coro.h" > + > +void bar () { > + co_await; // { dg-error "expected primary-expression before" } > +} > diff --git a/gcc/testsuite/g++.dg/coroutines/co-await-syntax-01-outside-fn.C b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-01-outside-fn.C > new file mode 100644 > index 0000000000..484859c706 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-01-outside-fn.C > @@ -0,0 +1,5 @@ > +// { dg-additional-options "-fsyntax-only -w" } > + > +#include "coro.h" > + > +int x = co_await coro::suspend_always{}; // { dg-error {'co_await' cannot be used outside a function} } > diff --git a/gcc/testsuite/g++.dg/coroutines/co-await-syntax-02-outside-fn.C b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-02-outside-fn.C > new file mode 100644 > index 0000000000..4ce5c2e04a > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-02-outside-fn.C > @@ -0,0 +1,5 @@ > +// { dg-additional-options "-fsyntax-only -w" } > + > +#include "coro.h" > + > +auto f (int x = co_await coro::suspend_always{}); // { dg-error {'co_await' cannot be used outside a function} } > diff --git a/gcc/testsuite/g++.dg/coroutines/co-await-syntax-03-auto.C b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-03-auto.C > new file mode 100644 > index 0000000000..7f4ed9afef > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-03-auto.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/co-await-syntax-04-ctor-dtor.C b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-04-ctor-dtor.C > new file mode 100644 > index 0000000000..ac0ba2e54f > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-04-ctor-dtor.C > @@ -0,0 +1,8 @@ > +// { dg-additional-options "-fsyntax-only -w" } > + > +#include "coro.h" > + > +struct Foo { > + Foo () { co_await coro::suspend_always{}; } // { dg-error "cannot be used in a constructor" } > + ~Foo () { co_await coro::suspend_always{}; } // { dg-error "cannot be used in a destructor" } > +}; > diff --git a/gcc/testsuite/g++.dg/coroutines/co-await-syntax-05-constexpr.C b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-05-constexpr.C > new file mode 100644 > index 0000000000..73a0b1499d > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-05-constexpr.C > @@ -0,0 +1,12 @@ > +// { dg-additional-options "-fsyntax-only -w" } > + > +#include "coro.h" > + > +constexpr int bar () { > + co_await coro::suspend_always{}; // { 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/co-await-syntax-06-main.C b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-06-main.C > new file mode 100644 > index 0000000000..ab520baaff > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-06-main.C > @@ -0,0 +1,7 @@ > +// { dg-additional-options "-fsyntax-only -w" } > + > +#include "coro.h" > + > +int main (int ac, char *av[]) { > + co_await coro::suspend_always{}; // { dg-error "cannot be used in the .main. function" } > +} > diff --git a/gcc/testsuite/g++.dg/coroutines/co-await-syntax-07-varargs.C b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-07-varargs.C > new file mode 100644 > index 0000000000..4e41dd3be4 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-07-varargs.C > @@ -0,0 +1,14 @@ > +// { dg-additional-options "-fsyntax-only -w" } > + > +#include "coro.h" > + > +int > +bar (int x, ...) > +{ > + co_await coro::suspend_always{}; // { 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/co-await-syntax-08-lambda-auto.C b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-08-lambda-auto.C > new file mode 100644 > index 0000000000..61db5feed3 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-08-lambda-auto.C > @@ -0,0 +1,19 @@ > +// { dg-additional-options "-fsyntax-only -w" } > + > +// Check that we decline return type deduction for lambda coroutines. > + > +#include "coro.h" > + > +// boiler-plate for tests of codegen > +#include "coro1-ret-int-yield-int.h" > + > +int main () > +{ > + /* Attempt to deduce the return type for a lambda coroutine. */ > + auto f = []() > + { > + co_await coro::suspend_always{}; // { dg-error "cannot be used in a function with a deduced return type" } > + }; > + > + return 0; > +} > diff --git a/gcc/testsuite/g++.dg/coroutines/co-return-syntax-01-outside-fn.C b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-01-outside-fn.C > new file mode 100644 > index 0000000000..3fcd8dd104 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-01-outside-fn.C > @@ -0,0 +1,6 @@ > +// { dg-additional-options "-fsyntax-only -w" } > + > +#include "coro.h" > + > +co_return; // { dg-error {expected unqualified-id before 'co_return'} } > + > diff --git a/gcc/testsuite/g++.dg/coroutines/co-return-syntax-02-outside-fn.C b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-02-outside-fn.C > new file mode 100644 > index 0000000000..cda36eb2a3 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-02-outside-fn.C > @@ -0,0 +1,5 @@ > +// { dg-additional-options "-fsyntax-only -w" } > + > +#include "coro.h" > + > +auto f (co_return); // { dg-error {expected primary-expression before 'co_return'} } > diff --git a/gcc/testsuite/g++.dg/coroutines/co-return-syntax-03-auto.C b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-03-auto.C > new file mode 100644 > index 0000000000..93a04dc459 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-03-auto.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/co-return-syntax-04-ctor-dtor.C b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-04-ctor-dtor.C > new file mode 100644 > index 0000000000..9396432e8b > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-04-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/co-return-syntax-05-constexpr-fn.C b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-05-constexpr-fn.C > new file mode 100644 > index 0000000000..69b109fb60 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-05-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/co-return-syntax-06-main.C b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-06-main.C > new file mode 100644 > index 0000000000..40d7e4e362 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-06-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/co-return-syntax-07-vararg.C b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-07-vararg.C > new file mode 100644 > index 0000000000..0aea17a1db > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-07-vararg.C > @@ -0,0 +1,14 @@ > +// { 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/co-return-syntax-08-bad-return.C b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-08-bad-return.C > new file mode 100644 > index 0000000000..4bfa41cd4a > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-08-bad-return.C > @@ -0,0 +1,43 @@ > +// { dg-additional-options "-fsyntax-only -w" } > + > +#include "coro.h" > + > +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 {a 'return' statement is not allowed} } > +{ > + if (x) > + return Coro(); > + else > + co_return; > +} > diff --git a/gcc/testsuite/g++.dg/coroutines/co-return-syntax-09-lambda-auto.C b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-09-lambda-auto.C > new file mode 100644 > index 0000000000..8fe52361ba > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-09-lambda-auto.C > @@ -0,0 +1,19 @@ > +// { dg-additional-options "-fsyntax-only -w" } > + > +// Check that we decline return type deduction for lambda coroutines. > + > +#include "coro.h" > + > +// boiler-plate for tests of codegen > +#include "coro1-ret-int-yield-int.h" > + > +int main () > +{ > + /* Attempt to deduce the return type for a lambda coroutine. */ > + auto f = []() > + { > + co_return 42; // { dg-error "cannot be used in a function with a deduced return type" } > + }; > + > + return 0; > +} > diff --git a/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-00-needs-expr.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-00-needs-expr.C > new file mode 100644 > index 0000000000..547f1a31c0 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-00-needs-expr.C > @@ -0,0 +1,7 @@ > +// { dg-additional-options "-fsyntax-only -w" } > + > +#include "coro.h" > + > +void foo () { > + co_yield; // { dg-error "expected primary-expression before" } > +} > diff --git a/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-01-outside-fn.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-01-outside-fn.C > new file mode 100644 > index 0000000000..30db0e963b > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-01-outside-fn.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-02-outside-fn.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-02-outside-fn.C > new file mode 100644 > index 0000000000..71e119fbef > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-02-outside-fn.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-03-auto.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-03-auto.C > new file mode 100644 > index 0000000000..808a07f5e1 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-03-auto.C > @@ -0,0 +1,12 @@ > +// { dg-additional-options "-fsyntax-only -w" } > + > +#include "coro.h" > + > +auto bar () { > + co_yield 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/co-yield-syntax-04-ctor-dtor.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-04-ctor-dtor.C > new file mode 100644 > index 0000000000..cc46e01d77 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-04-ctor-dtor.C > @@ -0,0 +1,8 @@ > +// { dg-additional-options "-fsyntax-only -w" } > + > +#include "coro.h" > + > +struct Foo { > + Foo () { co_yield 4; } // { dg-error "cannot be used in a constructor" } > + ~Foo () { co_yield 4; } // { dg-error "cannot be used in a destructor" } > +}; > diff --git a/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-05-constexpr.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-05-constexpr.C > new file mode 100644 > index 0000000000..39ef19c63b > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-05-constexpr.C > @@ -0,0 +1,12 @@ > +// { dg-additional-options "-fsyntax-only -w" } > + > +#include "coro.h" > + > +constexpr int bar () { > + co_yield 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/co-yield-syntax-06-main.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-06-main.C > new file mode 100644 > index 0000000000..dcc3dbce75 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-06-main.C > @@ -0,0 +1,7 @@ > +// { dg-additional-options "-fsyntax-only -w" } > + > +#include "coro.h" > + > +int main (int ac, char *av[]) { > + co_yield 0; // { dg-error "cannot be used in the .main. function" } > +} > diff --git a/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-07-varargs.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-07-varargs.C > new file mode 100644 > index 0000000000..f0b568335e > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-07-varargs.C > @@ -0,0 +1,14 @@ > +// { dg-additional-options "-fsyntax-only -w" } > + > +#include "coro.h" > + > +int > +bar (int x, ...) > +{ > + co_yield 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/co-yield-syntax-08-needs-expr.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-08-needs-expr.C > new file mode 100644 > index 0000000000..86969f781e > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-08-needs-expr.C > @@ -0,0 +1,37 @@ > +// { dg-additional-options "-fsyntax-only -w" } > + > +// Check syntax for missing expr in a coroutine context. > + > +#include "coro.h" > + > +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/co-yield-syntax-09-lambda-auto.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-09-lambda-auto.C > new file mode 100644 > index 0000000000..5190face00 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-09-lambda-auto.C > @@ -0,0 +1,19 @@ > +// { dg-additional-options "-fsyntax-only -w" } > + > +// Check that we decline return type deduction for lambda coroutines. > + > +#include "coro.h" > + > +// boiler-plate for tests of codegen > +#include "coro1-ret-int-yield-int.h" > + > +int main () > +{ > + /* Attempt to deduce the return type for a lambda coroutine. */ > + auto f = []() > + { > + co_yield 42; // { dg-error "cannot be used in a function with a deduced return type" } > + }; > + > + return 0; > +} > 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 0000000000..d7c4883384 > --- /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-missing-gro.C b/gcc/testsuite/g++.dg/coroutines/coro-missing-gro.C > new file mode 100644 > index 0000000000..fb02e9d580 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-gro.C > @@ -0,0 +1,32 @@ > +// { dg-additional-options "-fsyntax-only -w" } > + > +// Diagose missing get_return_object() in the promise type. > + > +#include "coro.h" > + > +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 0000000000..d489c3953a > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-promise-yield.C > @@ -0,0 +1,33 @@ > +// { dg-additional-options "-fsyntax-only -w" } > +#include "coro.h" > + > +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 0000000000..f238c4b9a3 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-ret-value.C > @@ -0,0 +1,34 @@ > +// { dg-additional-options "-fsyntax-only -w" } > + > +// Diagose missing return_value() in the promise type. > + > +#include "coro.h" > + > +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 0000000000..c9f84e5902 > --- /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" > + > +// 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 0000000000..3943e78d9d > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-1.C > @@ -0,0 +1,17 @@ > +// { dg-additional-options "-fsyntax-only -fexceptions -w" } > + > +// Diagose missing unhandled_exception() in the promise type. > + > +#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 0000000000..0f105c4c2d > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-2.C > @@ -0,0 +1,18 @@ > +// { dg-additional-options "-fsyntax-only -fno-exceptions " } > + > +// The missing method is warned for when exceptions are off and pedantic > +// is on (default in the testsuite). > + > +#include "coro.h" > +#include "coro-missing-ueh.h" > + > +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 0000000000..d775d8a630 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-3.C > @@ -0,0 +1,18 @@ > +// { dg-additional-options "-fsyntax-only -fno-exceptions -Wno-pedantic" } > + > +/* We don't warn about the missing method, unless in pedantic mode, so > + this compile should be clean. */ > + > +#include "coro.h" > +#include "coro-missing-ueh.h" > + > +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 0000000000..51e6135b8a > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh.h > @@ -0,0 +1,23 @@ > +#ifndef __MissingUEH_H > +#define __MissingUEH_H > + > +/* Common code for testing missing unhandled_exception. */ > +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 0000000000..f22a5e0833 > --- /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 0000000000..ca12d2689e > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/coro.h > @@ -0,0 +1,151 @@ > +#if __has_include(<coroutine>) > + > +#include <coroutine> > + > +# if __clang__ > +# include <utility> > +# endif > + > +namespace coro = std; > + > +#elif __has_include(<experimental/coroutine>) > + > +#include <experimental/coroutine> > + > +# if __clang__ > +# include <utility> > +# endif > + > +namespace coro = std::experimental; > + > +#else > + > +#warning "no installed coroutine headers found, using test-suite local one" > + > +/* Dummy version to allow tests without an installed header. */ > +# ifndef __TESTSUITE_CORO_H_n4835 > +# define __TESTSUITE_CORO_H_n4835 > + > +// Fragments (with short-cuts) to mimic enough of the library header to > +// make some progress. > + > +# if __cpp_coroutines > + > +namespace std { > +inline namespace __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); > + } > + protected: > + void *__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 __n4835 > +} // namespace std > + > +namespace coro = std; > + > +# else > +# error "coro.h requires support for coroutines, add -fcoroutines" > +# endif > +# endif // __TESTSUITE_CORO_H_n4835 > + > +#endif // __has_include(<experimental/coroutine>) > + > +/* 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 > diff --git a/gcc/testsuite/g++.dg/coroutines/coro1-ret-int-yield-int.h b/gcc/testsuite/g++.dg/coroutines/coro1-ret-int-yield-int.h > new file mode 100644 > index 0000000000..b961755e47 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/coro1-ret-int-yield-int.h > @@ -0,0 +1,133 @@ > +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(); > + } > + > + // Some awaitables to use in tests. > + // With progress printing for debug. > + 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 suspend_always_intprt { > + int x; > + suspend_always_intprt() : x(5) {} > + suspend_always_intprt(int __x) : x(__x) {} > + ~suspend_always_intprt() {} > + bool await_ready() const noexcept { return false; } > + void await_suspend(coro::coroutine_handle<>) const noexcept { PRINT ("susp-always-susp-intprt");} > + int await_resume() const noexcept { PRINT ("susp-always-resume-intprt"); return x;} > + }; > + > + /* This returns the square of the int that it was constructed with. */ > + struct suspend_always_longprtsq { > + long x; > + suspend_always_longprtsq() : x(12L) { PRINT ("suspend_always_longprtsq def ctor"); } > + suspend_always_longprtsq(long _x) : x(_x) { PRINTF ("suspend_always_longprtsq ctor with %ld\n", x); } > + ~suspend_always_longprtsq() {} > + bool await_ready() const noexcept { return false; } > + void await_suspend(coro::coroutine_handle<>) const noexcept { PRINT ("susp-always-susp-longsq");} > + long await_resume() const noexcept { PRINT ("susp-always-resume-longsq"); return x * x;} > + }; > + > + struct suspend_always_intrefprt { > + int& x; > + suspend_always_intrefprt(int& __x) : x(__x) {} > + ~suspend_always_intrefprt() {} > + bool await_ready() const noexcept { return false; } > + void await_suspend(coro::coroutine_handle<>) const noexcept { PRINT ("susp-always-susp-intprt");} > + int& await_resume() const noexcept { PRINT ("susp-always-resume-intprt"); return x;} > + }; > + > + struct promise_type { > + > + promise_type() : vv(-1) { PRINT ("Created Promise"); } > + promise_type(int __x) : vv(__x) { PRINTF ("Created Promise with %d\n",__x); } > + ~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{}; > + } > + > +#ifdef USE_AWAIT_TRANSFORM > + > + auto await_transform (int v) { > + PRINTF ("await_transform an int () %d\n",v); > + return suspend_always_intprt (v); > + } > + > + auto await_transform (long v) { > + PRINTF ("await_transform a long () %ld\n",v); > + return suspend_always_longprtsq (v); > + } > + > +#endif > + > + auto yield_value (int v) { > + PRINTF ("yield_value (%d)\n", v); > + vv = v; > + return suspend_always_prt{}; > + } > + > +#ifdef RETURN_VOID > + > + void return_void () { > + PRINT ("return_void ()"); > + } > + > +#else > + > + void return_value (int v) { > + PRINTF ("return_value (%d)\n", v); > + vv = v; > + } > + > +#endif > + void unhandled_exception() { PRINT ("** unhandled exception"); } > + > + int get_value () { return vv; } > + private: > + int vv; > + }; > + > +}; > diff --git a/gcc/testsuite/g++.dg/coroutines/coroutines.exp b/gcc/testsuite/g++.dg/coroutines/coroutines.exp > new file mode 100644 > index 0000000000..e7fd4dac46 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/coroutines.exp > @@ -0,0 +1,50 @@ > +# Copyright (C) 2018-2020 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/alloc-00-gro-on-alloc-fail.C b/gcc/testsuite/g++.dg/coroutines/torture/alloc-00-gro-on-alloc-fail.C > new file mode 100644 > index 0000000000..8430d053c6 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/alloc-00-gro-on-alloc-fail.C > @@ -0,0 +1,118 @@ > +// { dg-do run } > + > +// check the code-gen for the failed alloc return. > + > +#include "../coro.h" > + > +#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 > + > +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 ()"); > + } > + void unhandled_exception() { PRINT ("** unhandled exception"); } > + 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 (); > + } > + PRINT ("main: returning"); > + return 0; > +} > diff --git a/gcc/testsuite/g++.dg/coroutines/torture/alloc-01-overload-newdel.C b/gcc/testsuite/g++.dg/coroutines/torture/alloc-01-overload-newdel.C > new file mode 100644 > index 0000000000..f779f6e486 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/alloc-01-overload-newdel.C > @@ -0,0 +1,120 @@ > +// { dg-do run } > + > +// check codegen for overloaded operator new/delete. > + > +#include "../coro.h" > + > +int used_ovl_new = 0; > +int used_ovl_del = 0; > + > +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"); } > + > + void *operator new (std::size_t sz) { > + PRINT ("promise_type: used overloaded operator new"); > + used_ovl_new++; > + return ::operator new(sz); > + } > + > + void operator delete (void *p) { > + PRINT ("promise_type: used overloaded operator delete"); > + used_ovl_del++; > + return ::operator delete(p); > + } > + > + 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 ()"); > + } > + void unhandled_exception() { PRINT ("** unhandled exception"); } > + }; // promise > +}; // coro1 > + > +struct coro1 > +f () noexcept > +{ > + PRINT ("coro1: about to return"); > + co_return; > +} > + > +int main () > +{ > + // Nest a scope so that we can inspect the flags after the DTORs run. > + { > + 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 (); > + } > + } > + if (used_ovl_new != 1) > + { > + PRINT ("main: failed to call overloaded operator new"); > + abort (); > + } > + if (used_ovl_del != 1) > + { > + PRINT ("main: failed to call overloaded operator delete"); > + abort (); > + } > + PRINT ("main: returning"); > + return 0; > +} > diff --git a/gcc/testsuite/g++.dg/coroutines/torture/call-00-co-aw-arg.C b/gcc/testsuite/g++.dg/coroutines/torture/call-00-co-aw-arg.C > new file mode 100644 > index 0000000000..ee108072f6 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/call-00-co-aw-arg.C > @@ -0,0 +1,73 @@ > +// { dg-do run } > + > +// Check that we can use co_await as a call parm. > + > +#include "../coro.h" > + > +// boiler-plate for tests of codegen > +#define USE_AWAIT_TRANSFORM > +#include "../coro1-ret-int-yield-int.h" > + > +int gX = 1; > + > +__attribute__((__noinline__)) > +static int > +foo (int x) > +{ > + return x + 2; > +} > + > +/* Function with a single await. */ > +coro1 > +f () > +{ > + gX = foo (co_await 9); > + 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] (initial suspend)"); > + f_coro.handle.resume(); > + PRINT ("main: resuming [2] (await 9 parm)"); > + 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 11 + 31) */ > + 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; > +} > \ No newline at end of file > diff --git a/gcc/testsuite/g++.dg/coroutines/torture/call-01-multiple-co-aw.C b/gcc/testsuite/g++.dg/coroutines/torture/call-01-multiple-co-aw.C > new file mode 100644 > index 0000000000..0f5785163f > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/call-01-multiple-co-aw.C > @@ -0,0 +1,73 @@ > +// { dg-do run } > + > +// Check that we can use multiple co_awaits as a call parm. > + > +#include "../coro.h" > + > +// boiler-plate for tests of codegen > +#define USE_AWAIT_TRANSFORM > +#include "../coro1-ret-int-yield-int.h" > + > +int gX = 1; > + > +__attribute__((__noinline__)) > +static int > +bar (int x, int y) > +{ > + return x + y; > +} > + > +/* Function with a multiple awaits. */ > +coro1 > +g () > +{ > + gX = bar (co_await 9, co_await 2); > + co_return gX + 31; > +} > + > +int main () > +{ > + PRINT ("main: create coro1"); > + struct coro1 g_coro = g (); > + PRINT ("main: got coro1 - checking gX"); > + if (gX != 1) > + { > + PRINTF ("main: gX is wrong : %d, should be 1\n", gX); > + abort (); > + } > + if (g_coro.handle.done()) > + { > + PRINT ("main: we should not be 'done' [1]"); > + abort (); > + } > + > + PRINT ("main: resuming [1] (initial suspend)"); > + g_coro.handle.resume(); > + > + PRINT ("main: resuming [2] (parm 1)"); > + g_coro.handle.resume(); > + PRINT ("main: resuming [2] (parm 2)"); > + g_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 11 + 31) */ > + if (!g_coro.handle.done()) > + { > + PRINT ("main: we should be 'done'"); > + abort (); > + } > + > + int y = g_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; > +} > \ No newline at end of file > diff --git a/gcc/testsuite/g++.dg/coroutines/torture/call-02-temp-co-aw.C b/gcc/testsuite/g++.dg/coroutines/torture/call-02-temp-co-aw.C > new file mode 100644 > index 0000000000..4982c49d79 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/call-02-temp-co-aw.C > @@ -0,0 +1,72 @@ > +// { dg-do run } > + > +// Check foo (compiler temp, co_await). > + > +#include "../coro.h" > + > +// boiler-plate for tests of codegen > +#define USE_AWAIT_TRANSFORM > +#include "../coro1-ret-int-yield-int.h" > + > +int gX = 1; > + > +__attribute__((__noinline__)) > +static int > +bar (int x, int y) > +{ > + return x + y; > +} > + > +/* Function with a compiler temporary and a co_await. */ > +coro1 > +g () > +{ > + gX = bar (gX + 8, co_await 2); > + co_return gX + 31; > +} > + > +int main () > +{ > + PRINT ("main: create coro1"); > + struct coro1 g_coro = g (); > + PRINT ("main: got coro1 - checking gX"); > + if (gX != 1) > + { > + PRINTF ("main: gX is wrong : %d, should be 1\n", gX); > + abort (); > + } > + if (g_coro.handle.done()) > + { > + PRINT ("main: we should not be 'done' [1]"); > + abort (); > + } > + > + PRINT ("main: resuming [1] (initial suspend)"); > + g_coro.handle.resume(); > + > + PRINT ("main: resuming [2] (parm 1)"); > + g_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 11 + 31) */ > + if (!g_coro.handle.done()) > + { > + PRINT ("main: we should be 'done'"); > + abort (); > + } > + > + int y = g_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; > +} > \ No newline at end of file > diff --git a/gcc/testsuite/g++.dg/coroutines/torture/call-03-temp-ref-co-aw.C b/gcc/testsuite/g++.dg/coroutines/torture/call-03-temp-ref-co-aw.C > new file mode 100644 > index 0000000000..d0bb4667ac > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/call-03-temp-ref-co-aw.C > @@ -0,0 +1,72 @@ > +// { dg-do run } > + > +// Check foo (compiler temp, co_await). > + > +#include "../coro.h" > + > +// boiler-plate for tests of codegen > +#define USE_AWAIT_TRANSFORM > +#include "../coro1-ret-int-yield-int.h" > + > +int gX = 1; > + > +__attribute__((__noinline__)) > +static int > +bar (int x, const int& y) > +{ > + return x + y; > +} > + > +/* Function with a compiler temporary and a co_await. */ > +coro1 > +g () > +{ > + gX = bar (gX + 8, co_await 2); > + co_return gX + 31; > +} > + > +int main () > +{ > + PRINT ("main: create coro1"); > + struct coro1 g_coro = g (); > + PRINT ("main: got coro1 - checking gX"); > + if (gX != 1) > + { > + PRINTF ("main: gX is wrong : %d, should be 1\n", gX); > + abort (); > + } > + if (g_coro.handle.done()) > + { > + PRINT ("main: we should not be 'done' [1]"); > + abort (); > + } > + > + PRINT ("main: resuming [1] (initial suspend)"); > + g_coro.handle.resume(); > + > + PRINT ("main: resuming [2] (parm 1)"); > + g_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 11 + 31) */ > + if (!g_coro.handle.done()) > + { > + PRINT ("main: we should be 'done'"); > + abort (); > + } > + > + int y = g_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; > +} > \ No newline at end of file > diff --git a/gcc/testsuite/g++.dg/coroutines/torture/class-00-co-ret.C b/gcc/testsuite/g++.dg/coroutines/torture/class-00-co-ret.C > new file mode 100644 > index 0000000000..932fe4b283 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/class-00-co-ret.C > @@ -0,0 +1,41 @@ > +// { dg-do run } > + > +// Simplest class. > + > +#include "../coro.h" > + > +// boiler-plate for tests of codegen > +#include "../coro1-ret-int-yield-int.h" > + > +class foo > +{ > + public: > + coro1 meth () > + { > + PRINT ("coro1: about to return"); > + co_return 42; > + } > +}; > + > +int main () > +{ > + foo inst; > + > + PRINT ("main: create coro1"); > + coro1 x = inst.meth (); > + 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 (); > + } > + PRINT ("main: returning"); > + return 0; > +} > diff --git a/gcc/testsuite/g++.dg/coroutines/torture/class-01-co-ret-parm.C b/gcc/testsuite/g++.dg/coroutines/torture/class-01-co-ret-parm.C > new file mode 100644 > index 0000000000..0bd477044b > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/class-01-co-ret-parm.C > @@ -0,0 +1,57 @@ > +// { dg-do run } > + > +// Class with parm capture > + > +#include "../coro.h" > + > +// boiler-plate for tests of codegen > +#include "../coro1-ret-int-yield-int.h" > + > +class foo > +{ > + public: > + coro1 meth (int x) > + { > + 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 () > +{ > + foo inst; > + > + PRINT ("main: create coro1"); > + coro1 x = inst.meth (25); > + if (x.handle.done()) > + abort(); > + > + x.handle.resume(); > + PRINT ("main: after resume"); > + int y = x.handle.promise().get_value(); > + if ( y != 42 ) > + { > + PRINTF ("main: wrong result (%d)", y); > + 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/class-02-templ-parm.C b/gcc/testsuite/g++.dg/coroutines/torture/class-02-templ-parm.C > new file mode 100644 > index 0000000000..0cc6069c32 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/class-02-templ-parm.C > @@ -0,0 +1,52 @@ > +// { dg-do run } > + > +// template parm in a class > + > +#include "../coro.h" > + > +// boiler-plate for tests of codegen > +#define USE_AWAIT_TRANSFORM > +#include "../coro1-ret-int-yield-int.h" > + > +template <typename T> > +class foo > +{ > + public: > + coro1 meth (T y) > + { > + PRINT ("coro1: about to return"); > + T x = y; > + co_return co_await x + 3; > + } > +}; > + > +int main () > +{ > + foo<int> inst {}; > + PRINT ("main: create coro1"); > + coro1 x = inst.meth (17); > + if (x.handle.done()) > + abort(); > + > + x.handle.resume(); > + PRINT ("main: after resume (initial suspend)"); > + > + x.handle.resume(); > + PRINT ("main: after resume (co_await)"); > + > + /* Now we should have the co_returned value. */ > + int y = x.handle.promise().get_value(); > + if ( y != 20 ) > + { > + PRINTF ("main: wrong result (%d).", y); > + 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/class-03-operator-templ-parm.C b/gcc/testsuite/g++.dg/coroutines/torture/class-03-operator-templ-parm.C > new file mode 100644 > index 0000000000..2d888a7455 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/class-03-operator-templ-parm.C > @@ -0,0 +1,52 @@ > +// { dg-do run } > + > +// template parm in a class > + > +#include "../coro.h" > + > +// boiler-plate for tests of codegen > +#define USE_AWAIT_TRANSFORM > +#include "../coro1-ret-int-yield-int.h" > + > +template <typename T> > +class foo > +{ > + public: > + coro1 operator()(T y) > + { > + PRINT ("coro1: about to return"); > + T x = y; > + co_return co_await x + 3; > + } > +}; > + > +int main () > +{ > + foo<int> inst {}; > + PRINT ("main: create coro1"); > + coro1 x = inst.operator()(17); > + if (x.handle.done()) > + abort(); > + > + x.handle.resume(); > + PRINT ("main: after resume (initial suspend)"); > + > + x.handle.resume(); > + PRINT ("main: after resume (co_await)"); > + > + /* Now we should have the co_returned value. */ > + int y = x.handle.promise().get_value(); > + if ( y != 20 ) > + { > + PRINTF ("main: wrong result (%d).", y); > + 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/class-04-lambda-1.C b/gcc/testsuite/g++.dg/coroutines/torture/class-04-lambda-1.C > new file mode 100644 > index 0000000000..e191c20ac0 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/class-04-lambda-1.C > @@ -0,0 +1,58 @@ > +// { dg-do run } > + > +// template parm in a class > + > +#include "../coro.h" > + > +// boiler-plate for tests of codegen > +#define USE_AWAIT_TRANSFORM > +#include "../coro1-ret-int-yield-int.h" > + > +template <typename T> > +class foo > +{ > + public: > + auto get_lam () > + { > + auto l = [](T y) -> coro1 > + { > + T x = y; > + co_return co_await x + 3; > + }; > + return l; > + } > +}; > + > +int main () > +{ > + foo<int> inst {}; > + auto ll = inst.get_lam (); > + > + PRINT ("main: create coro1"); > + int arg = 17; // avoid a dangling reference > + coro1 x = ll (arg); > + if (x.handle.done()) > + abort(); > + > + x.handle.resume(); > + PRINT ("main: after resume (initial suspend)"); > + > + x.handle.resume(); > + PRINT ("main: after resume (co_await)"); > + > + /* Now we should have the co_returned value. */ > + int y = x.handle.promise().get_value(); > + if ( y != 20 ) > + { > + PRINTF ("main: wrong result (%d).", y); > + 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/class-05-lambda-capture-copy-local.C b/gcc/testsuite/g++.dg/coroutines/torture/class-05-lambda-capture-copy-local.C > new file mode 100644 > index 0000000000..968940f505 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/class-05-lambda-capture-copy-local.C > @@ -0,0 +1,59 @@ > +// { dg-do run } > + > +// template parm in a class > + > +#include "../coro.h" > + > +// boiler-plate for tests of codegen > +#define USE_AWAIT_TRANSFORM > +#include "../coro1-ret-int-yield-int.h" > + > +template <typename T> > +class foo > +{ > + public: > + auto get_lam (int parm) > + { > + int local = 3; > + auto l = [=](T y) -> coro1 > + { > + T x = y; > + co_return co_await x + local; > + }; > + return l; > + } > +}; > + > +int main () > +{ > + foo<int> inst {}; > + auto ll = inst.get_lam (10); > + > + PRINT ("main: create coro1"); > + int arg = 17; // avoid a dangling reference > + coro1 x = ll (arg); > + if (x.handle.done()) > + abort(); > + > + x.handle.resume(); > + PRINT ("main: after resume (initial suspend)"); > + > + x.handle.resume(); > + PRINT ("main: after resume (co_await)"); > + > + /* Now we should have the co_returned value. */ > + int y = x.handle.promise().get_value(); > + if ( y != 20 ) > + { > + PRINTF ("main: wrong result (%d).", y); > + 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/class-06-lambda-capture-ref.C b/gcc/testsuite/g++.dg/coroutines/torture/class-06-lambda-capture-ref.C > new file mode 100644 > index 0000000000..db60132b0e > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/class-06-lambda-capture-ref.C > @@ -0,0 +1,59 @@ > +// { dg-do run } > + > +// template parm in a class > + > +#include "../coro.h" > + > +// boiler-plate for tests of codegen > +#define USE_AWAIT_TRANSFORM > +#include "../coro1-ret-int-yield-int.h" > + > +template <typename T> > +class foo > +{ > + public: > + void use_lambda () > + { > + int a_copy = 20; > + int a_ref = 10; > + > + auto f = [&, a_copy]() -> coro1 > + { > + co_yield a_ref + a_copy; > + co_return a_ref + a_copy; > + }; > + > + coro1 A = f (); > + A.handle.resume(); // Initial suspend. > + PRINT ("main: [a_copy = 20, a_ref = 10]"); > + > + int y = A.handle.promise().get_value(); > + if (y != 30) > + { > + PRINTF ("main: co-yield = %d, should be 30\n", y); > + abort (); > + } > + > + a_copy = 5; > + a_ref = 7; > + > + A.handle.resume(); // from the yield. > + PRINT ("main: [a_copy = 5, a_ref = 7]"); > + > + y = A.handle.promise().get_value(); > + if (y != 27) > + { > + PRINTF ("main: co-ret = %d, should be 27\n", y); > + abort (); > + } > + PRINT ("use_lambda: about to return"); > + } > + ~foo () { PRINT ("foo: DTOR"); } > +}; > + > +int main () > +{ > + foo<int> inst; > + inst.use_lambda(); > + return 0; > +} > diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-00-trivial.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-00-trivial.C > new file mode 100644 > index 0000000000..a24c261599 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-00-trivial.C > @@ -0,0 +1,52 @@ > +// { dg-do run } > + > +// The simplest co_await we can do. > + > +#include "../coro.h" > + > +// boiler-plate for tests of codegen > +#include "../coro1-ret-int-yield-int.h" > + > +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] initial suspend"); > + f_coro.handle.resume(); > + PRINT ("main: resuming [2] co_await"); > + 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-01-with-value.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-01-with-value.C > new file mode 100644 > index 0000000000..db5c90224d > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-01-with-value.C > @@ -0,0 +1,57 @@ > +// { dg-do run } > + > +/* The simplest valued co_await we can do. */ > + > +#include "../coro.h" > + > +// boiler-plate for tests of codegen > +#include "../coro1-ret-int-yield-int.h" > + > +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] initial suspend"); > + f_coro.handle.resume(); > + PRINT ("main: resuming [2] co_await suspend_always_intprt"); > + 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-02-xform.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-02-xform.C > new file mode 100644 > index 0000000000..79ee6e1714 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-02-xform.C > @@ -0,0 +1,58 @@ > +// { dg-do run } > + > +// Test of basic await transform, no local state. > + > +#include "../coro.h" > + > +// boiler-plate for tests of codegen > +#define USE_AWAIT_TRANSFORM > +#include "../coro1-ret-int-yield-int.h" > + > +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] initial suspend"); > + f_coro.handle.resume(); > + PRINT ("main: resuming [2] co_await"); > + 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-03-rhs-op.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-03-rhs-op.C > new file mode 100644 > index 0000000000..6408432573 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-03-rhs-op.C > @@ -0,0 +1,58 @@ > +// { dg-do run } > + > +// Basic check of co_await with an expression to await transform. > + > +#include "../coro.h" > + > +// boiler-plate for tests of codegen > +#define USE_AWAIT_TRANSFORM > +#include "../coro1-ret-int-yield-int.h" > + > +int gX = 1; > + > +coro1 > +f () > +{ > + gX = co_await 11 + 15; > + co_return gX + 16; > +} > + > +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] initial suspend"); > + f_coro.handle.resume(); > + PRINT ("main: resuming [1] await"); > + 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 (26+16) */ > + 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-04-control-flow.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-04-control-flow.C > new file mode 100644 > index 0000000000..9bc99e875d > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-04-control-flow.C > @@ -0,0 +1,50 @@ > +// { dg-do run } > + > +// Check correct operation of await transform. > + > +#include "../coro.h" > + > +// boiler-plate for tests of codegen > +#define USE_AWAIT_TRANSFORM > +#include "../coro1-ret-int-yield-int.h" > + > +/* 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-05-loop.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-05-loop.C > new file mode 100644 > index 0000000000..34af740c99 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-05-loop.C > @@ -0,0 +1,51 @@ > +// { dg-do run } > + > +// Check correct operation of co_await in a loop without local state. > + > +#include "../coro.h" > + > +// boiler-plate for tests of codegen > +#define USE_AWAIT_TRANSFORM > +#include "../coro1-ret-int-yield-int.h" > + > +/* 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(); > + // first value above 100 is 10*11 + 1. > + if (y != 111) > + { > + PRINTF ("main: y is wrong : %d, should be 111\n", y); > + abort (); > + } > + puts ("main: done"); > + return 0; > +} > diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-06-ovl.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-06-ovl.C > new file mode 100644 > index 0000000000..14945faffd > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-06-ovl.C > @@ -0,0 +1,65 @@ > +// { dg-do run } > + > +// Basic check of the co_await operator overload. > + > +#include "../coro.h" > + > +// boiler-plate for tests of codegen > +#include "../coro1-ret-int-yield-int.h" > + > +/* A very simple overload. */ > +struct empty > +{ > + auto operator co_await() const & noexcept { > + return coro1::suspend_always_intprt{}; > + } > +}; > + > +int gX = 1; > +empty e{}; > + > +coro1 > +f () > +{ > + int a = co_await(e); /* operator ovl. */ > + co_return gX + 5 + a; > +} > + > +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'"); > + abort (); > + } > + > + PRINT ("main: resuming [1] initial suspend"); > + f_coro.handle.resume(); > + > + PRINT ("main: resuming [2] co_await"); > + f_coro.handle.resume(); > + > + /* we should now have returned with the co_return (11) */ > + 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-07-tmpl.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-07-tmpl.C > new file mode 100644 > index 0000000000..33f8e99d8c > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-07-tmpl.C > @@ -0,0 +1,132 @@ > +// { dg-do run } > + > +// Check that we correctly operate when the coroutine object is templated. > + > +#include "../coro.h" > + > +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 the int it was constructed with. */ > + struct suspend_always_intprt { > + T x; > + suspend_always_intprt() : x((T)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; } > + void unhandled_exception() { PRINT ("** unhandled exception"); } > + }; > +}; > + > +/* 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-08-cascade.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-08-cascade.C > new file mode 100644 > index 0000000000..d34619d6b6 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-08-cascade.C > @@ -0,0 +1,63 @@ > +// { dg-do run } > + > +// Check cascaded co_await operations. > + > +#include "../coro.h" > + > +#define USE_AWAIT_TRANSFORM > +#include "../coro1-ret-int-yield-int.h" > + > +/* Valued with an await_transform. */ > +int gX = 1; > +coro1 f () > +{ > + /* We are going to use an await transform that takes a long, the > + await_resume squares it. > + so we get 11 ** 4, 14641. */ > + gX = (int) co_await co_await 11L; > + 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] - inital suspend"); > + f_coro.handle.resume(); > + > + PRINT ("main: resuming [2] - nested"); > + f_coro.handle.resume(); > + PRINT ("main: resuming [3] - 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-09-pair.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-09-pair.C > new file mode 100644 > index 0000000000..525c6fc467 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-09-pair.C > @@ -0,0 +1,57 @@ > +// { dg-do run } > + > +#include "../coro.h" > + > +// boiler-plate for tests of codegen > +#define USE_AWAIT_TRANSFORM > +#include "../coro1-ret-int-yield-int.h" > + > +/* 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] (initial suspend)"); > + f_coro.handle.resume(); > + PRINT ("main: resuming [2] one side of add"); > + f_coro.handle.resume(); > + PRINT ("main: resuming [3] 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-await-10-template-fn-arg.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-10-template-fn-arg.C > new file mode 100644 > index 0000000000..71a5b18c3c > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-10-template-fn-arg.C > @@ -0,0 +1,60 @@ > +// { dg-do run } > + > +// Check type dependent function parms. > + > +#include "../coro.h" > + > +// boiler-plate for tests of codegen > +// there is a promise ctor that takes a single int. > +#define USE_AWAIT_TRANSFORM > +#include "../coro1-ret-int-yield-int.h" > + > +template <typename T> > +coro1 > +f (T y) noexcept > +{ > + PRINT ("coro1: about to return"); > + T x = y; > + co_return co_await x + 3; > +} > + > +int main () > +{ > + PRINT ("main: create coro1"); > + struct coro1 x = f<int>(17); > + > + /* We should have created the promise with an initial value of > + 17. */ > + int y = x.handle.promise().get_value(); > + if ( y != 17 ) > + { > + PRINTF ("main: wrong promise init (%d).", y); > + abort (); > + } > + > + PRINT ("main: got coro1 - resuming"); > + if (x.handle.done()) > + abort(); > + > + x.handle.resume(); > + PRINT ("main: after resume (initial suspend)"); > + > + x.handle.resume(); > + PRINT ("main: after resume (co_await)"); > + > + /* Now we should have the co_returned value. */ > + y = x.handle.promise().get_value(); > + if ( y != 20 ) > + { > + PRINTF ("main: wrong result (%d).", y); > + 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-await-11-forwarding.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-11-forwarding.C > new file mode 100644 > index 0000000000..78c88ed14e > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-11-forwarding.C > @@ -0,0 +1,43 @@ > +// { dg-do run } > + > +// Test of forwarding a templated awaitable to co_await. > + > +#include "../coro.h" > + > +// boiler-plate for tests of codegen > +#include "../coro1-ret-int-yield-int.h" > + > +/* Valued with an await_transform. */ > + > +template< typename AWAITABLE > > +coro1 > +test_fwd (AWAITABLE&& awaitable) > +{ > + // the await_resume() just returns the saved int value. > + int a = co_await std::forward<AWAITABLE>(awaitable); > + // Which we co-return to the promise so that it can be > + // retrieved. > + co_return a; > +} > + > +int main () > +{ > + // We have an awaitable that stores the int it was constructed with. > + coro1::suspend_always_intprt g(15); > + struct coro1 g_coro = test_fwd (g); > + > + PRINT ("main: resuming g [1] (initial suspend)"); > + g_coro.handle.resume(); > + > + PRINT ("main: resuming g [2] co_await"); > + g_coro.handle.resume(); > + > + int y = g_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-12-operator-2.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-12-operator-2.C > new file mode 100644 > index 0000000000..189332b78e > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-12-operator-2.C > @@ -0,0 +1,66 @@ > +// { dg-do run } > + > +// Basic check of the co_await operator overload. > + > +#include "../coro.h" > + > +// boiler-plate for tests of codegen > +#include "../coro1-ret-int-yield-int.h" > + > +/* A very simple overload. */ > +struct empty > +{ > + auto operator co_await() & noexcept { > + return coro1::suspend_always_intprt{}; > + } > + auto operator co_await() && noexcept { > + return coro1::suspend_always_longprtsq(3L); > + } > +}; > + > +empty e{}; > + > +coro1 > +f () > +{ > + int a = co_await e; /* operator ovl lv. */ > + int b = co_await empty{}; /* operator ovl rv. */ > + co_return b + a; > +} > + > +int main () > +{ > + PRINT ("main: create coro1"); > + struct coro1 f_coro = f (); > + > + if (f_coro.handle.done()) > + { > + PRINT ("main: we should not be 'done'"); > + abort (); > + } > + > + PRINT ("main: resuming [1] initial suspend"); > + f_coro.handle.resume(); > + > + PRINT ("main: resuming [2] co_await a"); > + f_coro.handle.resume(); > + > + PRINT ("main: resuming [3] co_await b"); > + f_coro.handle.resume(); > + > + /* we should now have returned with the co_return (14) */ > + if (!f_coro.handle.done()) > + { > + PRINT ("main: we should be 'done' "); > + abort (); > + } > + > + int y = f_coro.handle.promise().get_value(); > + if (y != 14) > + { > + PRINTF ("main: y is wrong : %d, should be 14\n", y); > + abort (); > + } > + puts ("main: done"); > + return 0; > +} > diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-13-return-ref.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-13-return-ref.C > new file mode 100644 > index 0000000000..339ebe4ff2 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-13-return-ref.C > @@ -0,0 +1,58 @@ > +// { dg-do run } > + > +/* The simplest valued co_await we can do. */ > + > +#include "../coro.h" > + > +// boiler-plate for tests of codegen > +#include "../coro1-ret-int-yield-int.h" > + > +int gX = 1; > + > +coro1 > +f () > +{ > + int t = 5; > + gX = co_await coro1::suspend_always_intrefprt{t}; > + co_return t + 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] initial suspend"); > + f_coro.handle.resume(); > + PRINT ("main: resuming [2] co_await suspend_always_intprt"); > + 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-ret-00-void-return-is-ready.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-00-void-return-is-ready.C > new file mode 100644 > index 0000000000..f551c6e760 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-00-void-return-is-ready.C > @@ -0,0 +1,90 @@ > +// { dg-do run } > + > +// Basic functionality check, co_return. > +// Here we check the case that initial suspend is "never", so that the co- > +// routine runs to completion immediately. > + > +#include "../coro.h" > + > +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 ()"); > + } > + void unhandled_exception() { PRINT ("** unhandled exception"); } > + }; > +}; > + > +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 - 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-01-void-return-is-suspend.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-01-void-return-is-suspend.C > new file mode 100644 > index 0000000000..03fc6eeb84 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-01-void-return-is-suspend.C > @@ -0,0 +1,94 @@ > +// { dg-do run } > + > +// Basic functionality check, co_return. > +// Here we check the case that initial suspend is "always". > + > +#include "../coro.h" > + > +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 ()"); > + } > + void unhandled_exception() { PRINT ("** unhandled exception"); } > + }; > +}; > + > +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 (); > + } > + PRINT ("main: returning"); > + return 0; > +} > diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-03-different-GRO-type.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-03-different-GRO-type.C > new file mode 100644 > index 0000000000..36da680f7f > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-03-different-GRO-type.C > @@ -0,0 +1,92 @@ > +// { dg-do run } > + > +// GRO differs from the eventual return type. > + > +# include "../coro.h" > + > +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 ()"); > + } > + void unhandled_exception() { PRINT ("** unhandled exception"); } > + }; > +}; > + > +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 (); > + } > + PRINT ("main: returning"); > + return 0; > +} > diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-04-GRO-nontriv.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-04-GRO-nontriv.C > new file mode 100644 > index 0000000000..29fb9424f8 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-04-GRO-nontriv.C > @@ -0,0 +1,109 @@ > +// { dg-do run } > + > +// GRO differs from eventual return type and has non-trivial dtor. > + > +#include "../coro.h" > + > +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 ()"); > + } > + void unhandled_exception() { PRINT ("** unhandled exception"); } > + }; > +}; > + > +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 (); > + } > + PRINT ("main: returning"); > + return 0; > +} > diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-05-return-value.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-05-return-value.C > new file mode 100644 > index 0000000000..42b80ff6bb > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-05-return-value.C > @@ -0,0 +1,38 @@ > +// { dg-do run } > + > +// Test returning an int. > +// We will use the promise to contain this to avoid having to include > +// additional C++ headers. > + > +#include "../coro.h" > + > +// boiler-plate for tests of codegen > +#include "../coro1-ret-int-yield-int.h" > + > +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 (); > + } > + PRINT ("main: returning"); > + return 0; > +} > diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-06-template-promise-val-1.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-06-template-promise-val-1.C > new file mode 100644 > index 0000000000..5b1acb8145 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-06-template-promise-val-1.C > @@ -0,0 +1,105 @@ > +// { dg-do run } > + > +// Test returning a T. > +// We will use the promise to contain this to avoid having to include > +// additional C++ headers. > + > +#include "../coro.h" > + > +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; } > + void unhandled_exception() { PRINT ("** unhandled exception"); } > + }; > +}; > + > +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-07-void-cast-expr.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-07-void-cast-expr.C > new file mode 100644 > index 0000000000..b1a06f2849 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-07-void-cast-expr.C > @@ -0,0 +1,44 @@ > +// { dg-do run } > + > +// Check that "co_return (void)expression;" evaluates expression once. > + > +#include "../coro.h" > + > +// boiler-plate for tests of codegen > +#define RETURN_VOID > +#include "../coro1-ret-int-yield-int.h" > + > +int gX = 1; > +__attribute__((__noinline__)) > +int foo (void) { PRINT ("called the int fn foo"); gX +=1 ; return gX; } > + > +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(); > + // We want to check that foo() was called exactly once. > + if (gX != 2) > + { > + PRINT ("main: failed check for a single call to foo()"); > + abort (); > + } > + PRINT ("main: after resume"); > + 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-08-template-cast-ret.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-08-template-cast-ret.C > new file mode 100644 > index 0000000000..266bc7b3b2 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-08-template-cast-ret.C > @@ -0,0 +1,104 @@ > +// { dg-do run } > + > +// Test templated co-return. > + > +#include "../coro.h" > + > +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; } > + void unhandled_exception() { PRINT ("** unhandled exception"); } > + }; > +}; > + > +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-09-bool-await-susp.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-09-bool-await-susp.C > new file mode 100644 > index 0000000000..91f3f14cc0 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-09-bool-await-susp.C > @@ -0,0 +1,97 @@ > +// { dg-do run } > + > +// test boolean return from await_suspend (). > + > +#include "../coro.h" > + > +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 ()"); > + } > + void unhandled_exception() { PRINT ("** unhandled exception"); } > + }; > +}; > + > +struct coro1 > +f () noexcept > +{ > + PRINT ("coro1: about to return"); > + co_return; > +} > + > +int main () > +{ > + 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-10-expression-evaluates-once.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-10-expression-evaluates-once.C > new file mode 100644 > index 0000000000..7b07be5f44 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-10-expression-evaluates-once.C > @@ -0,0 +1,49 @@ > +// { dg-do run } > + > +// Check that "co_return expression;" only evaluates expression once. > + > +#include "../coro.h" > + > +// boiler-plate for tests of codegen > +#include "../coro1-ret-int-yield-int.h" > + > +/* Give foo() a measureable side-effect. */ > +int gX = 1; > +__attribute__((__noinline__)) > +int foo (void) > +{ > + PRINT ("called the int fn foo"); > + gX += 1; > + return gX; > +} > + > +struct coro1 > +f () noexcept > +{ > + PRINT ("coro1: about to return"); > + co_return foo(); > +} > + > +int main () > +{ > + PRINT ("main: create coro1"); > + struct coro1 x = f (); > + PRINT ("main: got coro1 - resuming"); > + if (x.handle.done()) > + abort(); > + x.handle.resume(); > + // We want to check that foo() was called exactly once. > + if (gX != 2) > + { > + PRINT ("main: failed check for a single call to foo()"); > + abort (); > + } > + PRINT ("main: after resume"); > + 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-11-co-ret-co-await.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-11-co-ret-co-await.C > new file mode 100644 > index 0000000000..06939107d8 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-11-co-ret-co-await.C > @@ -0,0 +1,40 @@ > +// { dg-do run } > + > +// Check co_return co_await > + > +#include "../coro.h" > + > +// boiler-plate for tests of codegen > +#include "../coro1-ret-int-yield-int.h" > + > +struct coro1 > +f () noexcept > +{ > + PRINT ("coro1: about to return"); > + co_return co_await coro1::suspend_always_intprt{}; > +} > + > +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 1 (initial suspend)"); > + x.handle.resume(); > + PRINT ("main: after resume 2 (await intprt)"); > + > + int y = x.handle.promise().get_value(); > + if ( y != 5 ) > + 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-12-co-ret-fun-co-await.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-12-co-ret-fun-co-await.C > new file mode 100644 > index 0000000000..50124c080b > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-12-co-ret-fun-co-await.C > @@ -0,0 +1,48 @@ > +// { dg-do run } > + > +// Check co_return function (co_await) > + > +#include "../coro.h" > + > +// boiler-plate for tests of codegen > +#define USE_AWAIT_TRANSFORM > +#include "../coro1-ret-int-yield-int.h" > + > +__attribute__((__noinline__)) > +static int > +foo (int x) > +{ > + return x + 2; > +} > + > +struct coro1 > +f () noexcept > +{ > + PRINT ("coro1: about to return"); > + co_return foo (co_await 5); > +} > + > +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 1 (initial suspend)"); > + x.handle.resume(); > + PRINT ("main: after resume 2 (await parm)"); > + > + int y = x.handle.promise().get_value(); > + if ( y != 7 ) > + 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-13-template-2.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-13-template-2.C > new file mode 100644 > index 0000000000..9d4a4de8eb > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-13-template-2.C > @@ -0,0 +1,56 @@ > +// { dg-do run } > + > +// Check type dependent function parms. > + > +#include "../coro.h" > + > +// boiler-plate for tests of codegen > +// there is a promise ctor that takes a single int. > +#include "../coro1-ret-int-yield-int.h" > + > +template <typename T> > +coro1 > +f (T y) noexcept > +{ > + PRINT ("coro1: about to return"); > + T x = y; > + co_return 3; > +} > + > +int main () > +{ > + PRINT ("main: create coro1"); > + struct coro1 x = f<int>(17); > + > + /* We should have created the promise with an initial value of > + 17. */ > + int y = x.handle.promise().get_value(); > + if ( y != 17 ) > + { > + PRINT ("main: wrong promise init."); > + abort (); > + } > + > + PRINT ("main: got coro1 - resuming"); > + if (x.handle.done()) > + abort(); > + > + x.handle.resume(); > + PRINT ("main: after resume (initial suspend)"); > + > + /* Now we should have the co_returned value. */ > + y = x.handle.promise().get_value(); > + if ( y != 3 ) > + { > + PRINT ("main: wrong answer."); > + 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-14-template-3.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-14-template-3.C > new file mode 100644 > index 0000000000..ebc1adba82 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-14-template-3.C > @@ -0,0 +1,58 @@ > +// { dg-do run } > + > +// Check type dependent function parms. > + > +#include "../coro.h" > + > +// boiler-plate for tests of codegen > +// there is a promise ctor that takes a single int. > +#include "../coro1-ret-int-yield-int.h" > + > +template <typename T, typename U, typename V> > +coro1 > +f (T x, U y, V z) noexcept > +{ > + PRINT ("coro1: about to return"); > + T xi = (T) y; > + T yi = (T) z; > + T zi = x; > + co_return 3 + xi + yi + zi; > +} > + > +int main () > +{ > + PRINT ("main: create coro1"); > + struct coro1 x = f<int, float, double>(2, 18.0F, 19.0); > + > + /* We should be using the default promise ctor, which sets the value > + to -1. */ > + int y = x.handle.promise().get_value(); > + if ( y != -1 ) > + { > + PRINT ("main: wrong promise init."); > + abort (); > + } > + > + PRINT ("main: got coro1 - resuming"); > + if (x.handle.done()) > + abort(); > + > + x.handle.resume(); > + PRINT ("main: after resume (initial suspend)"); > + > + /* Now we should have the co_returned value. */ > + y = x.handle.promise().get_value(); > + if ( y != 42 ) > + { > + PRINT ("main: wrong answer."); > + 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-yield-00-triv.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-00-triv.C > new file mode 100644 > index 0000000000..586b6b2571 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-00-triv.C > @@ -0,0 +1,129 @@ > +// { dg-do run } > + > +// Test yielding an int. > + > +// We will use the promise to contain this to avoid having to include > +// additional C++ headers. > + > +// Check that we resolve the correct overload for the yield_value method. > + > +#include "../coro.h" > + > +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; } > + void unhandled_exception() { PRINT ("** unhandled exception"); } > + }; > +}; > + > +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-01-multi.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-01-multi.C > new file mode 100644 > index 0000000000..5df69c7f15 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-01-multi.C > @@ -0,0 +1,64 @@ > +// { dg-do run } > + > +// Test yielding an int. > +// We will use the promise to contain this to avoid having to include > +// additional C++ headers. > + > +#include "../coro.h" > + > +// boiler-plate for tests of codegen > +#include "../coro1-ret-int-yield-int.h" > + > +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-02-loop.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-02-loop.C > new file mode 100644 > index 0000000000..8d4f1d5d82 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-02-loop.C > @@ -0,0 +1,68 @@ > +// { dg-do run } > + > +// Test co_yield in a loop with no local state. > + > +#include "../coro.h" > + > +// boiler-plate for tests of codegen > +#include "../coro1-ret-int-yield-int.h" > + > +int gX = 1; > + > +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; > +} > + > +int main () > +{ > + PRINT ("main: create coro1"); > + struct coro1 f_coro = f (); > + PRINT ("main: got coro1 - resuming (1)"); > + if (gX != 1) > + { > + PRINTF ("main: gX is wrong : %d, should be 1\n", gX); > + abort (); > + } > + if (f_coro.handle.done()) > + abort(); > + f_coro.handle.resume(); > + PRINT ("main: after resume (1)"); > + int y = f_coro.handle.promise().get_value(); > + if (y != 5) > + { > + PRINTF ("main: got %d not 5.\n",y); > + abort (); > + } > + PRINT ("main: gX OK -- looping"); > + do { > + y = f_coro.handle.promise().get_value(); > + if (y != gX) > + { > + PRINTF ("main: got %d not %d.\n",y, gX); > + abort (); > + } > + 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 (); > + } > + PRINT ("main: returning"); > + return 0; > +} > diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-yield-03-tmpl.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-03-tmpl.C > new file mode 100644 > index 0000000000..cceee1f19e > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-03-tmpl.C > @@ -0,0 +1,140 @@ > +// { dg-do run } > + > +// Test co_yield in templated code. > + > +#include "../coro.h" > + > +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; } > + > + void unhandled_exception() { PRINT ("** unhandled exception"); } > + }; > + > + 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-04-complex-local-state.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-04-complex-local-state.C > new file mode 100644 > index 0000000000..d9330b33b7 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-04-complex-local-state.C > @@ -0,0 +1,162 @@ > +// { dg-do run } > + > +// using non-trivial types in the coro. > + > +# include "../coro.h" > + > +#include <vector> > +#include <string> > + > +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; } > + > + void unhandled_exception() { PRINT ("** unhandled exception"); } > + }; > + > + 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 (); > + } > + > + 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/co-yield-05-co-aw.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-05-co-aw.C > new file mode 100644 > index 0000000000..043f97b6e1 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-05-co-aw.C > @@ -0,0 +1,55 @@ > +// { dg-do run } > + > +// Check co_return co_await > + > +#include "../coro.h" > + > +// boiler-plate for tests of codegen > +#include "../coro1-ret-int-yield-int.h" > + > +struct coro1 > +f () noexcept > +{ > + PRINT ("f: about to yield"); > + co_yield co_await coro1::suspend_always_intprt(42); > + > + PRINT ("f: about to return 6174"); > + co_return 6174; > +} > + > +int main () > +{ > + PRINT ("main: create coro1"); > + struct coro1 x = f (); > + if (x.handle.done()) > + abort(); > + > + PRINT ("main: resuming (initial suspend)"); > + x.handle.resume(); > + PRINT ("main: resuming (await intprt)"); > + x.handle.resume(); > + > + PRINT ("main: after resume (2)"); > + int y = x.handle.promise().get_value(); > + if ( y != 42 ) > + abort (); > + PRINT ("main: apparently got 42"); > + > + PRINT ("main: got coro1 - resuming (co_yield)"); > + if (x.handle.done()) > + abort(); > + x.handle.resume(); > + > + PRINT ("main: after resume (co_yield)"); > + 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-06-fun-parm.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-06-fun-parm.C > new file mode 100644 > index 0000000000..c74e44d15d > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-06-fun-parm.C > @@ -0,0 +1,64 @@ > +// { dg-do run } > + > +// Check co_return co_await > + > +#include "../coro.h" > + > +// boiler-plate for tests of codegen > +#define USE_AWAIT_TRANSFORM > +#include "../coro1-ret-int-yield-int.h" > + > +__attribute__((__noinline__)) > +static int > +foo (int x) > +{ > + return x + 2; > +} > + > +/* Function with a single await. */ > +struct coro1 > +f () noexcept > +{ > + PRINT ("f: about to yield"); > + co_yield foo (co_await 40); > + > + PRINT ("f: about to return 6174"); > + co_return 6174; > +} > + > +int main () > +{ > + PRINT ("main: create coro1"); > + struct coro1 x = f (); > + if (x.handle.done()) > + abort(); > + > + PRINT ("main: resuming (initial suspend)"); > + x.handle.resume(); > + PRINT ("main: resuming (await intprt)"); > + x.handle.resume(); > + > + PRINT ("main: after resume (2)"); > + int y = x.handle.promise().get_value(); > + if ( y != 42 ) > + abort (); > + PRINT ("main: apparently got 42"); > + > + PRINT ("main: got coro1 - resuming (co_yield)"); > + if (x.handle.done()) > + abort(); > + x.handle.resume(); > + > + PRINT ("main: after resume (co_yield)"); > + 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-07-template-fn-param.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-07-template-fn-param.C > new file mode 100644 > index 0000000000..74dae63395 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-07-template-fn-param.C > @@ -0,0 +1,71 @@ > +// { dg-do run } > + > +// Check type dependent function parms. > + > +#include "../coro.h" > + > +// boiler-plate for tests of codegen > +// there is a promise ctor that takes a single int. > + > +#include "../coro1-ret-int-yield-int.h" > + > +template <typename T> > +coro1 > +f (T y) noexcept > +{ > + PRINT ("coro1: about to return"); > + T x = y; > + co_yield x + 3; > + co_return 42; > +} > + > +int main () > +{ > + PRINT ("main: create coro1"); > + struct coro1 x = f<int>(17); > + > + /* We should have created the promise with an initial value of > + 17. */ > + int y = x.handle.promise().get_value(); > + if ( y != 17 ) > + { > + PRINTF ("main: wrong promise init (%d).", y); > + abort (); > + } > + if (x.handle.done()) > + abort(); > + > + PRINT ("main: got coro1 - resuming"); > + x.handle.resume(); > + PRINT ("main: after resume (initial suspend)"); > + > + if (x.handle.done()) > + abort(); > + > + /* Now we should have the co_yielded value. */ > + y = x.handle.promise().get_value(); > + if ( y != 20 ) > + { > + PRINTF ("main: wrong result (%d).", y); > + abort (); > + } > + > + PRINT ("main: after resume (co_yield)"); > + x.handle.resume(); > + > + /* now we should have the co_returned value. */ > + y = x.handle.promise().get_value(); > + if ( y != 42 ) > + { > + PRINTF ("main: wrong result (%d).", y); > + 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-yield-08-more-refs.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-08-more-refs.C > new file mode 100644 > index 0000000000..8e39127a1a > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-08-more-refs.C > @@ -0,0 +1,68 @@ > +// { dg-do run } > + > +// Check co_return co_await > + > +#include "../coro.h" > + > +// boiler-plate for tests of codegen > +#include "../coro1-ret-int-yield-int.h" > + > +/* Tests for . */ > +struct test > +{ > + auto operator co_await() & noexcept { > + return coro1::suspend_always_intprt{}; > + } > + > + auto operator co_await() && noexcept { > + return coro1::suspend_always_longprtsq(3L); > + } > +}; > + > +struct coro1 > +f (test thing) noexcept > +{ > + co_yield co_await static_cast<test&&>(thing); > + co_return 6174; > +} > + > +int main () > +{ > + PRINT ("main: create coro1"); > + > + struct coro1 x = f (test{}); > + if (x.handle.done()) > + abort(); > + > + PRINT ("main: resuming (initial suspend)"); > + x.handle.resume(); > + PRINT ("main: resuming (await intprt)"); > + x.handle.resume(); > + > + int y = x.handle.promise().get_value(); > + if ( y != 9 ) > + { > + PRINTF ("main: co-yield gave %d, should be 9\n", y); > + abort (); > + } > + > + PRINT ("main: got coro1 - resuming (co_yield)"); > + if (x.handle.done()) > + abort(); > + x.handle.resume(); > + > + y = x.handle.promise().get_value(); > + if ( y != 6174 ) > + { > + PRINTF ("main: co-return gave %d, should be 9\n", y); > + 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-yield-09-more-templ-refs.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-09-more-templ-refs.C > new file mode 100644 > index 0000000000..3abbe1c43a > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-09-more-templ-refs.C > @@ -0,0 +1,68 @@ > +// { dg-do run } > + > +// Check co_return co_await > + > +#include "../coro.h" > + > +// boiler-plate for tests of codegen > +#include "../coro1-ret-int-yield-int.h" > + > +/* A very simple overload. */ > +struct test > +{ > + auto operator co_await() & noexcept { > + return coro1::suspend_always_intprt{}; > + } > + > + auto operator co_await() && noexcept { > + return coro1::suspend_always_longprtsq(3L); > + } > +}; > + > +template<typename RESULT, typename PARAM> > +RESULT > +f (PARAM thing) noexcept > +{ > + co_yield co_await static_cast<PARAM&&>(thing); > + co_return 6174; > +} > + > +int main () > +{ > + PRINT ("main: create coro1"); > + struct coro1 x = f<coro1, test> (test{}); > + if (x.handle.done()) > + abort(); > + > + PRINT ("main: resuming (initial suspend)"); > + x.handle.resume(); > + PRINT ("main: resuming (await intprt)"); > + x.handle.resume(); > + > + int y = x.handle.promise().get_value(); > + if ( y != 9 ) > + { > + PRINTF ("main: co-yield gave %d, should be 9\n", y); > + abort (); > + } > + > + PRINT ("main: got coro1 - resuming (co_yield)"); > + if (x.handle.done()) > + abort(); > + x.handle.resume(); > + > + y = x.handle.promise().get_value(); > + if ( y != 6174 ) > + { > + PRINTF ("main: co-return gave %d, should be 9\n", y); > + 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/coro-torture.exp b/gcc/testsuite/g++.dg/coroutines/torture/coro-torture.exp > new file mode 100644 > index 0000000000..d2463b2798 > --- /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 0000000000..164c804797 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/exceptions-test-0.C > @@ -0,0 +1,167 @@ > +// { dg-do run } > + > +// Test exceptions. > + > +#include "../coro.h" > +#include <exception> > + > +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; } > + > + void unhandled_exception() { > + PRINT ("unhandled_exception: caught one!"); > + gX = -1; > + // returning from here should end up in final_suspend. > + } > + }; > +}; > + > +// 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-00.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-00.C > new file mode 100644 > index 0000000000..b5716972d4 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-00.C > @@ -0,0 +1,42 @@ > +// { dg-do run } > + > +// Test promise construction from function args list. > + > +#include "../coro.h" > + > +// boiler-plate for tests of codegen > +#include "../coro1-ret-int-yield-int.h" > + > +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 ) > + { > + PRINT ("main: incorrect ctor value"); > + 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 (); > + } > + PRINT ("main: returning"); > + return 0; > +} > diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-01.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-01.C > new file mode 100644 > index 0000000000..f530431a6b > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-01.C > @@ -0,0 +1,45 @@ > +// { dg-do run } > + > +// Simplest test that we correctly handle function params in the body > +// of the coroutine. No local state, just the parm. > + > +#include "../coro.h" > + > +// boiler-plate for tests of codegen > +#include "../coro1-ret-int-yield-int.h" > + > +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 (); > + } > + PRINT ("main: returning"); > + return 0; > +} > diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-02.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-02.C > new file mode 100644 > index 0000000000..396b438cb2 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-02.C > @@ -0,0 +1,50 @@ > +// { dg-do run } > + > +// Test that we correctly re-write multiple uses of a function param > +// in the body. > + > +#include "../coro.h" > + > +// boiler-plate for tests of codegen > +#include "../coro1-ret-int-yield-int.h" > + > +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 (); > + } > + PRINT ("main: returning"); > + return 0; > +} > diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-03.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-03.C > new file mode 100644 > index 0000000000..bf699722a1 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-03.C > @@ -0,0 +1,49 @@ > +// { dg-do run } > + > +// Test that we can use a function param in a co_xxxx status. > + > +#include "../coro.h" > + > +// boiler-plate for tests of codegen > +#include "../coro1-ret-int-yield-int.h" > + > +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 (); > + } > + PRINT ("main: returning"); > + return 0; > +} > diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-04.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-04.C > new file mode 100644 > index 0000000000..789e2c05b6 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-04.C > @@ -0,0 +1,57 @@ > +// { dg-do run } > + > +// Test that we can manage a constructed param copy. > + > +#include "../coro.h" > + > +// boiler-plate for tests of codegen > +#include "../coro1-ret-int-yield-int.h" > + > +// 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 (); > + } > + PRINT ("main: returning"); > + return 0; > +} > diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-05.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-05.C > new file mode 100644 > index 0000000000..8bdb2b5d0f > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-05.C > @@ -0,0 +1,57 @@ > +// { dg-do run } > + > +// Test that we can manage a constructed param reference > + > +#include "../coro.h" > + > +// boiler-plate for tests of codegen > +#include "../coro1-ret-int-yield-int.h" > + > +// 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 (); > + } > + PRINT ("main: returning"); > + return 0; > +} > diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-06.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-06.C > new file mode 100644 > index 0000000000..cbcfe67ff1 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-06.C > @@ -0,0 +1,47 @@ > +// { dg-do run } > + > +// check references are handled as expected. > + > +#include "../coro.h" > + > +// boiler-plate for tests of codegen > +#include "../coro1-ret-int-yield-int.h" > + > +coro1 > +f (int& a_ref, int a_copy) > +{ > + co_yield a_ref + a_copy; > + co_return a_ref + a_copy; > +} > + > +int main () > +{ > + int a_copy = 20; > + int a_ref = 10; > + > + coro1 A = f (a_ref, a_copy); > + A.handle.resume(); // Initial suspend. > + PRINT ("main: [a_copy = 20, a_ref = 10]"); > + > + int y = A.handle.promise().get_value(); > + if (y != 30) > + { > + PRINTF ("main: co-yield = %d, should be 30\n", y); > + abort (); > + } > + > + a_copy = 5; > + a_ref = 7; > + > + A.handle.resume(); > + PRINT ("main: [a_copy = 5, a_ref = 7]"); > + > + y = A.handle.promise().get_value(); > + if (y != 27) > + { > + PRINTF ("main: co-ret = %d, should be 27\n", y); > + abort (); > + } > + > + return 0; > +} > diff --git a/gcc/testsuite/g++.dg/coroutines/torture/lambda-00-co-ret.C b/gcc/testsuite/g++.dg/coroutines/torture/lambda-00-co-ret.C > new file mode 100644 > index 0000000000..61e284d5c8 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/lambda-00-co-ret.C > @@ -0,0 +1,35 @@ > +// { dg-do run } > + > +// Simplest lambda > + > +#include "../coro.h" > + > +// boiler-plate for tests of codegen > +#include "../coro1-ret-int-yield-int.h" > + > +int main () > +{ > + auto f = []() -> coro1 > + { > + PRINT ("coro1: about to return"); > + co_return 42; > + }; > + > + PRINT ("main: create coro1"); > + 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 (); > + } > + PRINT ("main: returning"); > + return 0; > +} > diff --git a/gcc/testsuite/g++.dg/coroutines/torture/lambda-01-co-ret-parm.C b/gcc/testsuite/g++.dg/coroutines/torture/lambda-01-co-ret-parm.C > new file mode 100644 > index 0000000000..378eedc6d8 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/lambda-01-co-ret-parm.C > @@ -0,0 +1,48 @@ > +// { dg-do run } > + > +// Lambda with parm > + > +#include "../coro.h" > + > +// boiler-plate for tests of codegen > +#include "../coro1-ret-int-yield-int.h" > + > +int main () > +{ > + auto f = [](int x) -> coro1 > + { > + 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; > + } > + }; > + > + PRINT ("main: create coro1"); > + 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 (); > + } > + PRINT ("main: returning"); > + return 0; > +} > diff --git a/gcc/testsuite/g++.dg/coroutines/torture/lambda-02-co-yield-values.C b/gcc/testsuite/g++.dg/coroutines/torture/lambda-02-co-yield-values.C > new file mode 100644 > index 0000000000..a6f592cd77 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/lambda-02-co-yield-values.C > @@ -0,0 +1,64 @@ > +// { dg-do run } > + > +// lambda with parm and local state > + > +#include "../coro.h" > + > +// boiler-plate for tests of codegen > +#include "../coro1-ret-int-yield-int.h" > + > +int main () > +{ > + auto f = [](int start) -> coro1 > + { > + 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; > + }; > + > + PRINT ("main: create coro1"); > + 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/lambda-03-auto-parm-1.C b/gcc/testsuite/g++.dg/coroutines/torture/lambda-03-auto-parm-1.C > new file mode 100644 > index 0000000000..bfa5400225 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/lambda-03-auto-parm-1.C > @@ -0,0 +1,46 @@ > +// { dg-do run } > + > +// generic Lambda with auto parm (c++14) > + > +#include "../coro.h" > + > +// boiler-plate for tests of codegen > +#define USE_AWAIT_TRANSFORM > +#include "../coro1-ret-int-yield-int.h" > + > +int main () > +{ > + auto f = [](auto y) -> coro1 > + { > + PRINT ("coro1: about to return"); > + auto x = y; > + co_return co_await x + 3; > + }; > + > + PRINT ("main: create coro1"); > + struct coro1 x = f((int)17); > + if (x.handle.done()) > + abort(); > + > + x.handle.resume(); > + PRINT ("main: after resume (initial suspend)"); > + > + x.handle.resume(); > + PRINT ("main: after resume (co_await)"); > + > + /* Now we should have the co_returned value. */ > + int y = x.handle.promise().get_value(); > + if ( y != 20 ) > + { > + PRINTF ("main: wrong result (%d).", y); > + 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/lambda-04-templ-parm.C b/gcc/testsuite/g++.dg/coroutines/torture/lambda-04-templ-parm.C > new file mode 100644 > index 0000000000..adf31e22db > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/lambda-04-templ-parm.C > @@ -0,0 +1,47 @@ > +// { dg-do run } > +// { dg-additional-options "-std=c++2a" } > + > +// generic Lambda with template parm (from c++20) > + > +#include "../coro.h" > + > +// boiler-plate for tests of codegen > +#define USE_AWAIT_TRANSFORM > +#include "../coro1-ret-int-yield-int.h" > + > +int main () > +{ > + auto f = []<typename T>(T y) -> coro1 > + { > + PRINT ("coro1: about to return"); > + T x = y; > + co_return co_await x + 3; > + }; > + > + PRINT ("main: create coro1"); > + coro1 x = f.operator()<int>(17); > + if (x.handle.done()) > + abort(); > + > + x.handle.resume(); > + PRINT ("main: after resume (initial suspend)"); > + > + x.handle.resume(); > + PRINT ("main: after resume (co_await)"); > + > + /* Now we should have the co_returned value. */ > + int y = x.handle.promise().get_value(); > + if ( y != 20 ) > + { > + PRINTF ("main: wrong result (%d).", y); > + 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/lambda-05-capture-copy-local.C b/gcc/testsuite/g++.dg/coroutines/torture/lambda-05-capture-copy-local.C > new file mode 100644 > index 0000000000..7cd6648cca > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/lambda-05-capture-copy-local.C > @@ -0,0 +1,66 @@ > +// { dg-do run } > + > +// lambda with parm and local state > + > +#include "../coro.h" > + > +// boiler-plate for tests of codegen > +#include "../coro1-ret-int-yield-int.h" > + > +int main () > +{ > + int local = 31; > + > + auto f = [=](int start) -> coro1 > + { > + int value = start; > + PRINT ("f: about to yield start"); > + co_yield start; > + > + value -= local; > + PRINT ("f: about to yield (value-31)"); > + co_yield value; > + > + value += 6163; > + PRINT ("f: about to return (value+6163)"); > + co_return value; > + }; > + > + PRINT ("main: create coro1"); > + 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/lambda-06-multi-capture.C b/gcc/testsuite/g++.dg/coroutines/torture/lambda-06-multi-capture.C > new file mode 100644 > index 0000000000..7b445d3d9c > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/lambda-06-multi-capture.C > @@ -0,0 +1,48 @@ > +// { dg-do run } > + > +// lambda with parm and local state > + > +#include "../coro.h" > + > +// boiler-plate for tests of codegen > +#include "../coro1-ret-int-yield-int.h" > + > +int main () > +{ > + int a_copy = 20; > + int a_ref = 10; > + > + auto f = [&, a_copy]() -> coro1 > + { > + co_return a_ref + a_copy; > + }; > + > + { > + coro1 A = f (); > + A.handle.resume(); > + PRINT ("main: [a_copy = 20, a_ref = 10]"); > + > + int y = A.handle.promise().get_value(); > + if (y != 30) > + { > + PRINTF ("main: A co-ret = %d, should be 30\n", y); > + abort (); > + } > + } > + > + a_copy = 5; > + a_ref = 7; > + > + coro1 B = f (); > + B.handle.resume(); > + PRINT ("main: [a_copy = 5, a_ref = 7]"); > + > + int y = B.handle.promise().get_value(); > + if (y != 27) > + { > + PRINTF ("main: B co-ret = %d, should be 27\n", y); > + abort (); > + } > + > + return 0; > +} > diff --git a/gcc/testsuite/g++.dg/coroutines/torture/lambda-07-multi-yield.C b/gcc/testsuite/g++.dg/coroutines/torture/lambda-07-multi-yield.C > new file mode 100644 > index 0000000000..2bd58cbf2e > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/lambda-07-multi-yield.C > @@ -0,0 +1,46 @@ > +// { dg-do run } > + > +// lambda with parm and local state > + > +#include "../coro.h" > + > +// boiler-plate for tests of codegen > +#include "../coro1-ret-int-yield-int.h" > + > +int main () > +{ > + int a_copy = 20; > + int a_ref = 10; > + > + auto f = [&, a_copy]() -> coro1 > + { > + co_yield a_ref + a_copy; > + co_return a_ref + a_copy; > + }; > + > + coro1 A = f (); > + A.handle.resume(); // Initial suspend. > + PRINT ("main: [a_copy = 20, a_ref = 10]"); > + > + int y = A.handle.promise().get_value(); > + if (y != 30) > + { > + PRINTF ("main: co-yield = %d, should be 30\n", y); > + abort (); > + } > + > + a_copy = 5; > + a_ref = 7; > + > + A.handle.resume(); > + PRINT ("main: [a_copy = 5, a_ref = 7]"); > + > + y = A.handle.promise().get_value(); > + if (y != 27) > + { > + PRINTF ("main: co-ret = %d, should be 27\n", y); > + abort (); > + } > + > + return 0; > +} > diff --git a/gcc/testsuite/g++.dg/coroutines/torture/lambda-08-co-ret-parm-ref.C b/gcc/testsuite/g++.dg/coroutines/torture/lambda-08-co-ret-parm-ref.C > new file mode 100644 > index 0000000000..4d5a44fe29 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/lambda-08-co-ret-parm-ref.C > @@ -0,0 +1,59 @@ > +// { dg-do run } > + > +// Test that we can use a function param in a co_xxxx status. > + > +#include "../coro.h" > + > +// boiler-plate for tests of codegen > +#include "../coro1-ret-int-yield-int.h" > + > +int main () > +{ > + int val; > + > + auto f = [&] (int x) -> coro1 > + { > + if (val + x > 25) > + { > + PRINT ("coro1: about to return k"); > + co_return 6174; > + } > + else if (val + x > 20) > + { > + PRINTF ("coro1: about to co-return %d\n", val + x); > + co_return val + x; > + } > + else if (val + x > 5) > + { > + PRINTF ("coro1: about to co-return %d\n", val); > + co_return val; > + } > + else > + { > + PRINT ("coro1: about to return 0"); > + co_return 0; > + } > + }; > + > + PRINT ("main: create coro1"); > + > + val = 20; // We should get this by ref. > + int arg = 5; // and this as a regular parm. > + > + coro1 x = f (arg); > + 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 (); > + } > + 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 0000000000..a8956457dc > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/local-var-0.C > @@ -0,0 +1,37 @@ > +// { dg-do run } > + > +// Simplest local decl. > + > +#include "../coro.h" > + > +// boiler-plate for tests of codegen > +#include "../coro1-ret-int-yield-int.h" > + > +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 (); > + } > + 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 0000000000..69a5b70756 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/local-var-1.C > @@ -0,0 +1,37 @@ > +// { dg-do run } > + > +// Simplest local var > + > +#include "../coro.h" > + > +// boiler-plate for tests of codegen > +#include "../coro1-ret-int-yield-int.h" > + > +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 (); > + } > + 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 0000000000..f232edabda > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/local-var-2.C > @@ -0,0 +1,50 @@ > +// { dg-do run } > + > +// Test local vars in nested scopes > + > +#include "../coro.h" > + > +// boiler-plate for tests of codegen > +#include "../coro1-ret-int-yield-int.h" > + > +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 0000000000..bd06db53d4 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/local-var-3.C > @@ -0,0 +1,65 @@ > +// { dg-do run } > + > +// Test modifying a local var and yielding several instances of it. > + > +#include "../coro.h" > + > +// boiler-plate for tests of codegen > +#include "../coro1-ret-int-yield-int.h" > + > +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 0000000000..419eb6b646 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/local-var-4.C > @@ -0,0 +1,75 @@ > +// { dg-do run } > + > +// Test modifying a local var across nested scopes containing vars > +// hiding those at outer scopes. > + > +#include "../coro.h" > + > +// boiler-plate for tests of codegen > +#include "../coro1-ret-int-yield-int.h" > + > +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 0000000000..934fb19de7 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/mid-suspend-destruction-0.C > @@ -0,0 +1,107 @@ > +// { 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. > + > +#include "../coro.h" > + > +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 ()"); > + } > + > + void unhandled_exception() { PRINT ("** unhandled exception"); } > + }; > +}; > + > +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 (! __builtin_coro_suspended(handle)) > + { > + 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; > +} > diff --git a/gcc/testsuite/g++.dg/coroutines/torture/pr92933.C b/gcc/testsuite/g++.dg/coroutines/torture/pr92933.C > new file mode 100644 > index 0000000000..b2f1be78b3 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/torture/pr92933.C > @@ -0,0 +1,18 @@ > +// { dg-do compile } > + > +// Test that we compile the simple case described in PR 92933 > + > +#include "../coro.h" > + > +#define RETURN_VOID > +#include "../coro1-ret-int-yield-int.h" > + > +struct some_error {}; > + > +coro1 > +foo() { > + try { > + co_return; > + } catch (some_error) { > + } > +} > -- > 2.14.3
diff --git a/gcc/testsuite/g++.dg/coroutines/co-await-syntax-00-needs-expr.C b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-00-needs-expr.C new file mode 100644 index 0000000000..d068c3d19a --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-00-needs-expr.C @@ -0,0 +1,7 @@ +// { dg-additional-options "-fsyntax-only -w" } + +#include "coro.h" + +void bar () { + co_await; // { dg-error "expected primary-expression before" } +} diff --git a/gcc/testsuite/g++.dg/coroutines/co-await-syntax-01-outside-fn.C b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-01-outside-fn.C new file mode 100644 index 0000000000..484859c706 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-01-outside-fn.C @@ -0,0 +1,5 @@ +// { dg-additional-options "-fsyntax-only -w" } + +#include "coro.h" + +int x = co_await coro::suspend_always{}; // { dg-error {'co_await' cannot be used outside a function} } diff --git a/gcc/testsuite/g++.dg/coroutines/co-await-syntax-02-outside-fn.C b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-02-outside-fn.C new file mode 100644 index 0000000000..4ce5c2e04a --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-02-outside-fn.C @@ -0,0 +1,5 @@ +// { dg-additional-options "-fsyntax-only -w" } + +#include "coro.h" + +auto f (int x = co_await coro::suspend_always{}); // { dg-error {'co_await' cannot be used outside a function} } diff --git a/gcc/testsuite/g++.dg/coroutines/co-await-syntax-03-auto.C b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-03-auto.C new file mode 100644 index 0000000000..7f4ed9afef --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-03-auto.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/co-await-syntax-04-ctor-dtor.C b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-04-ctor-dtor.C new file mode 100644 index 0000000000..ac0ba2e54f --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-04-ctor-dtor.C @@ -0,0 +1,8 @@ +// { dg-additional-options "-fsyntax-only -w" } + +#include "coro.h" + +struct Foo { + Foo () { co_await coro::suspend_always{}; } // { dg-error "cannot be used in a constructor" } + ~Foo () { co_await coro::suspend_always{}; } // { dg-error "cannot be used in a destructor" } +}; diff --git a/gcc/testsuite/g++.dg/coroutines/co-await-syntax-05-constexpr.C b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-05-constexpr.C new file mode 100644 index 0000000000..73a0b1499d --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-05-constexpr.C @@ -0,0 +1,12 @@ +// { dg-additional-options "-fsyntax-only -w" } + +#include "coro.h" + +constexpr int bar () { + co_await coro::suspend_always{}; // { 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/co-await-syntax-06-main.C b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-06-main.C new file mode 100644 index 0000000000..ab520baaff --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-06-main.C @@ -0,0 +1,7 @@ +// { dg-additional-options "-fsyntax-only -w" } + +#include "coro.h" + +int main (int ac, char *av[]) { + co_await coro::suspend_always{}; // { dg-error "cannot be used in the .main. function" } +} diff --git a/gcc/testsuite/g++.dg/coroutines/co-await-syntax-07-varargs.C b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-07-varargs.C new file mode 100644 index 0000000000..4e41dd3be4 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-07-varargs.C @@ -0,0 +1,14 @@ +// { dg-additional-options "-fsyntax-only -w" } + +#include "coro.h" + +int +bar (int x, ...) +{ + co_await coro::suspend_always{}; // { 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/co-await-syntax-08-lambda-auto.C b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-08-lambda-auto.C new file mode 100644 index 0000000000..61db5feed3 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-08-lambda-auto.C @@ -0,0 +1,19 @@ +// { dg-additional-options "-fsyntax-only -w" } + +// Check that we decline return type deduction for lambda coroutines. + +#include "coro.h" + +// boiler-plate for tests of codegen +#include "coro1-ret-int-yield-int.h" + +int main () +{ + /* Attempt to deduce the return type for a lambda coroutine. */ + auto f = []() + { + co_await coro::suspend_always{}; // { dg-error "cannot be used in a function with a deduced return type" } + }; + + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/co-return-syntax-01-outside-fn.C b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-01-outside-fn.C new file mode 100644 index 0000000000..3fcd8dd104 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-01-outside-fn.C @@ -0,0 +1,6 @@ +// { dg-additional-options "-fsyntax-only -w" } + +#include "coro.h" + +co_return; // { dg-error {expected unqualified-id before 'co_return'} } + diff --git a/gcc/testsuite/g++.dg/coroutines/co-return-syntax-02-outside-fn.C b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-02-outside-fn.C new file mode 100644 index 0000000000..cda36eb2a3 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-02-outside-fn.C @@ -0,0 +1,5 @@ +// { dg-additional-options "-fsyntax-only -w" } + +#include "coro.h" + +auto f (co_return); // { dg-error {expected primary-expression before 'co_return'} } diff --git a/gcc/testsuite/g++.dg/coroutines/co-return-syntax-03-auto.C b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-03-auto.C new file mode 100644 index 0000000000..93a04dc459 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-03-auto.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/co-return-syntax-04-ctor-dtor.C b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-04-ctor-dtor.C new file mode 100644 index 0000000000..9396432e8b --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-04-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/co-return-syntax-05-constexpr-fn.C b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-05-constexpr-fn.C new file mode 100644 index 0000000000..69b109fb60 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-05-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/co-return-syntax-06-main.C b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-06-main.C new file mode 100644 index 0000000000..40d7e4e362 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-06-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/co-return-syntax-07-vararg.C b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-07-vararg.C new file mode 100644 index 0000000000..0aea17a1db --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-07-vararg.C @@ -0,0 +1,14 @@ +// { 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/co-return-syntax-08-bad-return.C b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-08-bad-return.C new file mode 100644 index 0000000000..4bfa41cd4a --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-08-bad-return.C @@ -0,0 +1,43 @@ +// { dg-additional-options "-fsyntax-only -w" } + +#include "coro.h" + +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 {a 'return' statement is not allowed} } +{ + if (x) + return Coro(); + else + co_return; +} diff --git a/gcc/testsuite/g++.dg/coroutines/co-return-syntax-09-lambda-auto.C b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-09-lambda-auto.C new file mode 100644 index 0000000000..8fe52361ba --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-09-lambda-auto.C @@ -0,0 +1,19 @@ +// { dg-additional-options "-fsyntax-only -w" } + +// Check that we decline return type deduction for lambda coroutines. + +#include "coro.h" + +// boiler-plate for tests of codegen +#include "coro1-ret-int-yield-int.h" + +int main () +{ + /* Attempt to deduce the return type for a lambda coroutine. */ + auto f = []() + { + co_return 42; // { dg-error "cannot be used in a function with a deduced return type" } + }; + + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-00-needs-expr.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-00-needs-expr.C new file mode 100644 index 0000000000..547f1a31c0 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-00-needs-expr.C @@ -0,0 +1,7 @@ +// { dg-additional-options "-fsyntax-only -w" } + +#include "coro.h" + +void foo () { + co_yield; // { dg-error "expected primary-expression before" } +} diff --git a/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-01-outside-fn.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-01-outside-fn.C new file mode 100644 index 0000000000..30db0e963b --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-01-outside-fn.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-02-outside-fn.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-02-outside-fn.C new file mode 100644 index 0000000000..71e119fbef --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-02-outside-fn.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-03-auto.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-03-auto.C new file mode 100644 index 0000000000..808a07f5e1 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-03-auto.C @@ -0,0 +1,12 @@ +// { dg-additional-options "-fsyntax-only -w" } + +#include "coro.h" + +auto bar () { + co_yield 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/co-yield-syntax-04-ctor-dtor.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-04-ctor-dtor.C new file mode 100644 index 0000000000..cc46e01d77 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-04-ctor-dtor.C @@ -0,0 +1,8 @@ +// { dg-additional-options "-fsyntax-only -w" } + +#include "coro.h" + +struct Foo { + Foo () { co_yield 4; } // { dg-error "cannot be used in a constructor" } + ~Foo () { co_yield 4; } // { dg-error "cannot be used in a destructor" } +}; diff --git a/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-05-constexpr.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-05-constexpr.C new file mode 100644 index 0000000000..39ef19c63b --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-05-constexpr.C @@ -0,0 +1,12 @@ +// { dg-additional-options "-fsyntax-only -w" } + +#include "coro.h" + +constexpr int bar () { + co_yield 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/co-yield-syntax-06-main.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-06-main.C new file mode 100644 index 0000000000..dcc3dbce75 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-06-main.C @@ -0,0 +1,7 @@ +// { dg-additional-options "-fsyntax-only -w" } + +#include "coro.h" + +int main (int ac, char *av[]) { + co_yield 0; // { dg-error "cannot be used in the .main. function" } +} diff --git a/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-07-varargs.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-07-varargs.C new file mode 100644 index 0000000000..f0b568335e --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-07-varargs.C @@ -0,0 +1,14 @@ +// { dg-additional-options "-fsyntax-only -w" } + +#include "coro.h" + +int +bar (int x, ...) +{ + co_yield 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/co-yield-syntax-08-needs-expr.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-08-needs-expr.C new file mode 100644 index 0000000000..86969f781e --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-08-needs-expr.C @@ -0,0 +1,37 @@ +// { dg-additional-options "-fsyntax-only -w" } + +// Check syntax for missing expr in a coroutine context. + +#include "coro.h" + +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/co-yield-syntax-09-lambda-auto.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-09-lambda-auto.C new file mode 100644 index 0000000000..5190face00 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-09-lambda-auto.C @@ -0,0 +1,19 @@ +// { dg-additional-options "-fsyntax-only -w" } + +// Check that we decline return type deduction for lambda coroutines. + +#include "coro.h" + +// boiler-plate for tests of codegen +#include "coro1-ret-int-yield-int.h" + +int main () +{ + /* Attempt to deduce the return type for a lambda coroutine. */ + auto f = []() + { + co_yield 42; // { dg-error "cannot be used in a function with a deduced return type" } + }; + + return 0; +} 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 0000000000..d7c4883384 --- /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-missing-gro.C b/gcc/testsuite/g++.dg/coroutines/coro-missing-gro.C new file mode 100644 index 0000000000..fb02e9d580 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-gro.C @@ -0,0 +1,32 @@ +// { dg-additional-options "-fsyntax-only -w" } + +// Diagose missing get_return_object() in the promise type. + +#include "coro.h" + +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 0000000000..d489c3953a --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-promise-yield.C @@ -0,0 +1,33 @@ +// { dg-additional-options "-fsyntax-only -w" } +#include "coro.h" + +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 0000000000..f238c4b9a3 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-ret-value.C @@ -0,0 +1,34 @@ +// { dg-additional-options "-fsyntax-only -w" } + +// Diagose missing return_value() in the promise type. + +#include "coro.h" + +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 0000000000..c9f84e5902 --- /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" + +// 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 0000000000..3943e78d9d --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-1.C @@ -0,0 +1,17 @@ +// { dg-additional-options "-fsyntax-only -fexceptions -w" } + +// Diagose missing unhandled_exception() in the promise type. + +#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 0000000000..0f105c4c2d --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-2.C @@ -0,0 +1,18 @@ +// { dg-additional-options "-fsyntax-only -fno-exceptions " } + +// The missing method is warned for when exceptions are off and pedantic +// is on (default in the testsuite). + +#include "coro.h" +#include "coro-missing-ueh.h" + +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 0000000000..d775d8a630 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-3.C @@ -0,0 +1,18 @@ +// { dg-additional-options "-fsyntax-only -fno-exceptions -Wno-pedantic" } + +/* We don't warn about the missing method, unless in pedantic mode, so + this compile should be clean. */ + +#include "coro.h" +#include "coro-missing-ueh.h" + +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 0000000000..51e6135b8a --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh.h @@ -0,0 +1,23 @@ +#ifndef __MissingUEH_H +#define __MissingUEH_H + +/* Common code for testing missing unhandled_exception. */ +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 0000000000..f22a5e0833 --- /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 0000000000..ca12d2689e --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/coro.h @@ -0,0 +1,151 @@ +#if __has_include(<coroutine>) + +#include <coroutine> + +# if __clang__ +# include <utility> +# endif + +namespace coro = std; + +#elif __has_include(<experimental/coroutine>) + +#include <experimental/coroutine> + +# if __clang__ +# include <utility> +# endif + +namespace coro = std::experimental; + +#else + +#warning "no installed coroutine headers found, using test-suite local one" + +/* Dummy version to allow tests without an installed header. */ +# ifndef __TESTSUITE_CORO_H_n4835 +# define __TESTSUITE_CORO_H_n4835 + +// Fragments (with short-cuts) to mimic enough of the library header to +// make some progress. + +# if __cpp_coroutines + +namespace std { +inline namespace __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); + } + protected: + void *__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 __n4835 +} // namespace std + +namespace coro = std; + +# else +# error "coro.h requires support for coroutines, add -fcoroutines" +# endif +# endif // __TESTSUITE_CORO_H_n4835 + +#endif // __has_include(<experimental/coroutine>) + +/* 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 diff --git a/gcc/testsuite/g++.dg/coroutines/coro1-ret-int-yield-int.h b/gcc/testsuite/g++.dg/coroutines/coro1-ret-int-yield-int.h new file mode 100644 index 0000000000..b961755e47 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/coro1-ret-int-yield-int.h @@ -0,0 +1,133 @@ +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(); + } + + // Some awaitables to use in tests. + // With progress printing for debug. + 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 suspend_always_intprt { + int x; + suspend_always_intprt() : x(5) {} + suspend_always_intprt(int __x) : x(__x) {} + ~suspend_always_intprt() {} + bool await_ready() const noexcept { return false; } + void await_suspend(coro::coroutine_handle<>) const noexcept { PRINT ("susp-always-susp-intprt");} + int await_resume() const noexcept { PRINT ("susp-always-resume-intprt"); return x;} + }; + + /* This returns the square of the int that it was constructed with. */ + struct suspend_always_longprtsq { + long x; + suspend_always_longprtsq() : x(12L) { PRINT ("suspend_always_longprtsq def ctor"); } + suspend_always_longprtsq(long _x) : x(_x) { PRINTF ("suspend_always_longprtsq ctor with %ld\n", x); } + ~suspend_always_longprtsq() {} + bool await_ready() const noexcept { return false; } + void await_suspend(coro::coroutine_handle<>) const noexcept { PRINT ("susp-always-susp-longsq");} + long await_resume() const noexcept { PRINT ("susp-always-resume-longsq"); return x * x;} + }; + + struct suspend_always_intrefprt { + int& x; + suspend_always_intrefprt(int& __x) : x(__x) {} + ~suspend_always_intrefprt() {} + bool await_ready() const noexcept { return false; } + void await_suspend(coro::coroutine_handle<>) const noexcept { PRINT ("susp-always-susp-intprt");} + int& await_resume() const noexcept { PRINT ("susp-always-resume-intprt"); return x;} + }; + + struct promise_type { + + promise_type() : vv(-1) { PRINT ("Created Promise"); } + promise_type(int __x) : vv(__x) { PRINTF ("Created Promise with %d\n",__x); } + ~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{}; + } + +#ifdef USE_AWAIT_TRANSFORM + + auto await_transform (int v) { + PRINTF ("await_transform an int () %d\n",v); + return suspend_always_intprt (v); + } + + auto await_transform (long v) { + PRINTF ("await_transform a long () %ld\n",v); + return suspend_always_longprtsq (v); + } + +#endif + + auto yield_value (int v) { + PRINTF ("yield_value (%d)\n", v); + vv = v; + return suspend_always_prt{}; + } + +#ifdef RETURN_VOID + + void return_void () { + PRINT ("return_void ()"); + } + +#else + + void return_value (int v) { + PRINTF ("return_value (%d)\n", v); + vv = v; + } + +#endif + void unhandled_exception() { PRINT ("** unhandled exception"); } + + int get_value () { return vv; } + private: + int vv; + }; + +}; diff --git a/gcc/testsuite/g++.dg/coroutines/coroutines.exp b/gcc/testsuite/g++.dg/coroutines/coroutines.exp new file mode 100644 index 0000000000..e7fd4dac46 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/coroutines.exp @@ -0,0 +1,50 @@ +# Copyright (C) 2018-2020 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/alloc-00-gro-on-alloc-fail.C b/gcc/testsuite/g++.dg/coroutines/torture/alloc-00-gro-on-alloc-fail.C new file mode 100644 index 0000000000..8430d053c6 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/alloc-00-gro-on-alloc-fail.C @@ -0,0 +1,118 @@ +// { dg-do run } + +// check the code-gen for the failed alloc return. + +#include "../coro.h" + +#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 + +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 ()"); + } + void unhandled_exception() { PRINT ("** unhandled exception"); } + 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 (); + } + PRINT ("main: returning"); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/torture/alloc-01-overload-newdel.C b/gcc/testsuite/g++.dg/coroutines/torture/alloc-01-overload-newdel.C new file mode 100644 index 0000000000..f779f6e486 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/alloc-01-overload-newdel.C @@ -0,0 +1,120 @@ +// { dg-do run } + +// check codegen for overloaded operator new/delete. + +#include "../coro.h" + +int used_ovl_new = 0; +int used_ovl_del = 0; + +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"); } + + void *operator new (std::size_t sz) { + PRINT ("promise_type: used overloaded operator new"); + used_ovl_new++; + return ::operator new(sz); + } + + void operator delete (void *p) { + PRINT ("promise_type: used overloaded operator delete"); + used_ovl_del++; + return ::operator delete(p); + } + + 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 ()"); + } + void unhandled_exception() { PRINT ("** unhandled exception"); } + }; // promise +}; // coro1 + +struct coro1 +f () noexcept +{ + PRINT ("coro1: about to return"); + co_return; +} + +int main () +{ + // Nest a scope so that we can inspect the flags after the DTORs run. + { + 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 (); + } + } + if (used_ovl_new != 1) + { + PRINT ("main: failed to call overloaded operator new"); + abort (); + } + if (used_ovl_del != 1) + { + PRINT ("main: failed to call overloaded operator delete"); + abort (); + } + PRINT ("main: returning"); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/torture/call-00-co-aw-arg.C b/gcc/testsuite/g++.dg/coroutines/torture/call-00-co-aw-arg.C new file mode 100644 index 0000000000..ee108072f6 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/call-00-co-aw-arg.C @@ -0,0 +1,73 @@ +// { dg-do run } + +// Check that we can use co_await as a call parm. + +#include "../coro.h" + +// boiler-plate for tests of codegen +#define USE_AWAIT_TRANSFORM +#include "../coro1-ret-int-yield-int.h" + +int gX = 1; + +__attribute__((__noinline__)) +static int +foo (int x) +{ + return x + 2; +} + +/* Function with a single await. */ +coro1 +f () +{ + gX = foo (co_await 9); + 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] (initial suspend)"); + f_coro.handle.resume(); + PRINT ("main: resuming [2] (await 9 parm)"); + 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 11 + 31) */ + 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; +} \ No newline at end of file diff --git a/gcc/testsuite/g++.dg/coroutines/torture/call-01-multiple-co-aw.C b/gcc/testsuite/g++.dg/coroutines/torture/call-01-multiple-co-aw.C new file mode 100644 index 0000000000..0f5785163f --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/call-01-multiple-co-aw.C @@ -0,0 +1,73 @@ +// { dg-do run } + +// Check that we can use multiple co_awaits as a call parm. + +#include "../coro.h" + +// boiler-plate for tests of codegen +#define USE_AWAIT_TRANSFORM +#include "../coro1-ret-int-yield-int.h" + +int gX = 1; + +__attribute__((__noinline__)) +static int +bar (int x, int y) +{ + return x + y; +} + +/* Function with a multiple awaits. */ +coro1 +g () +{ + gX = bar (co_await 9, co_await 2); + co_return gX + 31; +} + +int main () +{ + PRINT ("main: create coro1"); + struct coro1 g_coro = g (); + PRINT ("main: got coro1 - checking gX"); + if (gX != 1) + { + PRINTF ("main: gX is wrong : %d, should be 1\n", gX); + abort (); + } + if (g_coro.handle.done()) + { + PRINT ("main: we should not be 'done' [1]"); + abort (); + } + + PRINT ("main: resuming [1] (initial suspend)"); + g_coro.handle.resume(); + + PRINT ("main: resuming [2] (parm 1)"); + g_coro.handle.resume(); + PRINT ("main: resuming [2] (parm 2)"); + g_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 11 + 31) */ + if (!g_coro.handle.done()) + { + PRINT ("main: we should be 'done'"); + abort (); + } + + int y = g_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; +} \ No newline at end of file diff --git a/gcc/testsuite/g++.dg/coroutines/torture/call-02-temp-co-aw.C b/gcc/testsuite/g++.dg/coroutines/torture/call-02-temp-co-aw.C new file mode 100644 index 0000000000..4982c49d79 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/call-02-temp-co-aw.C @@ -0,0 +1,72 @@ +// { dg-do run } + +// Check foo (compiler temp, co_await). + +#include "../coro.h" + +// boiler-plate for tests of codegen +#define USE_AWAIT_TRANSFORM +#include "../coro1-ret-int-yield-int.h" + +int gX = 1; + +__attribute__((__noinline__)) +static int +bar (int x, int y) +{ + return x + y; +} + +/* Function with a compiler temporary and a co_await. */ +coro1 +g () +{ + gX = bar (gX + 8, co_await 2); + co_return gX + 31; +} + +int main () +{ + PRINT ("main: create coro1"); + struct coro1 g_coro = g (); + PRINT ("main: got coro1 - checking gX"); + if (gX != 1) + { + PRINTF ("main: gX is wrong : %d, should be 1\n", gX); + abort (); + } + if (g_coro.handle.done()) + { + PRINT ("main: we should not be 'done' [1]"); + abort (); + } + + PRINT ("main: resuming [1] (initial suspend)"); + g_coro.handle.resume(); + + PRINT ("main: resuming [2] (parm 1)"); + g_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 11 + 31) */ + if (!g_coro.handle.done()) + { + PRINT ("main: we should be 'done'"); + abort (); + } + + int y = g_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; +} \ No newline at end of file diff --git a/gcc/testsuite/g++.dg/coroutines/torture/call-03-temp-ref-co-aw.C b/gcc/testsuite/g++.dg/coroutines/torture/call-03-temp-ref-co-aw.C new file mode 100644 index 0000000000..d0bb4667ac --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/call-03-temp-ref-co-aw.C @@ -0,0 +1,72 @@ +// { dg-do run } + +// Check foo (compiler temp, co_await). + +#include "../coro.h" + +// boiler-plate for tests of codegen +#define USE_AWAIT_TRANSFORM +#include "../coro1-ret-int-yield-int.h" + +int gX = 1; + +__attribute__((__noinline__)) +static int +bar (int x, const int& y) +{ + return x + y; +} + +/* Function with a compiler temporary and a co_await. */ +coro1 +g () +{ + gX = bar (gX + 8, co_await 2); + co_return gX + 31; +} + +int main () +{ + PRINT ("main: create coro1"); + struct coro1 g_coro = g (); + PRINT ("main: got coro1 - checking gX"); + if (gX != 1) + { + PRINTF ("main: gX is wrong : %d, should be 1\n", gX); + abort (); + } + if (g_coro.handle.done()) + { + PRINT ("main: we should not be 'done' [1]"); + abort (); + } + + PRINT ("main: resuming [1] (initial suspend)"); + g_coro.handle.resume(); + + PRINT ("main: resuming [2] (parm 1)"); + g_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 11 + 31) */ + if (!g_coro.handle.done()) + { + PRINT ("main: we should be 'done'"); + abort (); + } + + int y = g_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; +} \ No newline at end of file diff --git a/gcc/testsuite/g++.dg/coroutines/torture/class-00-co-ret.C b/gcc/testsuite/g++.dg/coroutines/torture/class-00-co-ret.C new file mode 100644 index 0000000000..932fe4b283 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/class-00-co-ret.C @@ -0,0 +1,41 @@ +// { dg-do run } + +// Simplest class. + +#include "../coro.h" + +// boiler-plate for tests of codegen +#include "../coro1-ret-int-yield-int.h" + +class foo +{ + public: + coro1 meth () + { + PRINT ("coro1: about to return"); + co_return 42; + } +}; + +int main () +{ + foo inst; + + PRINT ("main: create coro1"); + coro1 x = inst.meth (); + 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 (); + } + PRINT ("main: returning"); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/torture/class-01-co-ret-parm.C b/gcc/testsuite/g++.dg/coroutines/torture/class-01-co-ret-parm.C new file mode 100644 index 0000000000..0bd477044b --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/class-01-co-ret-parm.C @@ -0,0 +1,57 @@ +// { dg-do run } + +// Class with parm capture + +#include "../coro.h" + +// boiler-plate for tests of codegen +#include "../coro1-ret-int-yield-int.h" + +class foo +{ + public: + coro1 meth (int x) + { + 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 () +{ + foo inst; + + PRINT ("main: create coro1"); + coro1 x = inst.meth (25); + if (x.handle.done()) + abort(); + + x.handle.resume(); + PRINT ("main: after resume"); + int y = x.handle.promise().get_value(); + if ( y != 42 ) + { + PRINTF ("main: wrong result (%d)", y); + 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/class-02-templ-parm.C b/gcc/testsuite/g++.dg/coroutines/torture/class-02-templ-parm.C new file mode 100644 index 0000000000..0cc6069c32 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/class-02-templ-parm.C @@ -0,0 +1,52 @@ +// { dg-do run } + +// template parm in a class + +#include "../coro.h" + +// boiler-plate for tests of codegen +#define USE_AWAIT_TRANSFORM +#include "../coro1-ret-int-yield-int.h" + +template <typename T> +class foo +{ + public: + coro1 meth (T y) + { + PRINT ("coro1: about to return"); + T x = y; + co_return co_await x + 3; + } +}; + +int main () +{ + foo<int> inst {}; + PRINT ("main: create coro1"); + coro1 x = inst.meth (17); + if (x.handle.done()) + abort(); + + x.handle.resume(); + PRINT ("main: after resume (initial suspend)"); + + x.handle.resume(); + PRINT ("main: after resume (co_await)"); + + /* Now we should have the co_returned value. */ + int y = x.handle.promise().get_value(); + if ( y != 20 ) + { + PRINTF ("main: wrong result (%d).", y); + 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/class-03-operator-templ-parm.C b/gcc/testsuite/g++.dg/coroutines/torture/class-03-operator-templ-parm.C new file mode 100644 index 0000000000..2d888a7455 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/class-03-operator-templ-parm.C @@ -0,0 +1,52 @@ +// { dg-do run } + +// template parm in a class + +#include "../coro.h" + +// boiler-plate for tests of codegen +#define USE_AWAIT_TRANSFORM +#include "../coro1-ret-int-yield-int.h" + +template <typename T> +class foo +{ + public: + coro1 operator()(T y) + { + PRINT ("coro1: about to return"); + T x = y; + co_return co_await x + 3; + } +}; + +int main () +{ + foo<int> inst {}; + PRINT ("main: create coro1"); + coro1 x = inst.operator()(17); + if (x.handle.done()) + abort(); + + x.handle.resume(); + PRINT ("main: after resume (initial suspend)"); + + x.handle.resume(); + PRINT ("main: after resume (co_await)"); + + /* Now we should have the co_returned value. */ + int y = x.handle.promise().get_value(); + if ( y != 20 ) + { + PRINTF ("main: wrong result (%d).", y); + 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/class-04-lambda-1.C b/gcc/testsuite/g++.dg/coroutines/torture/class-04-lambda-1.C new file mode 100644 index 0000000000..e191c20ac0 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/class-04-lambda-1.C @@ -0,0 +1,58 @@ +// { dg-do run } + +// template parm in a class + +#include "../coro.h" + +// boiler-plate for tests of codegen +#define USE_AWAIT_TRANSFORM +#include "../coro1-ret-int-yield-int.h" + +template <typename T> +class foo +{ + public: + auto get_lam () + { + auto l = [](T y) -> coro1 + { + T x = y; + co_return co_await x + 3; + }; + return l; + } +}; + +int main () +{ + foo<int> inst {}; + auto ll = inst.get_lam (); + + PRINT ("main: create coro1"); + int arg = 17; // avoid a dangling reference + coro1 x = ll (arg); + if (x.handle.done()) + abort(); + + x.handle.resume(); + PRINT ("main: after resume (initial suspend)"); + + x.handle.resume(); + PRINT ("main: after resume (co_await)"); + + /* Now we should have the co_returned value. */ + int y = x.handle.promise().get_value(); + if ( y != 20 ) + { + PRINTF ("main: wrong result (%d).", y); + 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/class-05-lambda-capture-copy-local.C b/gcc/testsuite/g++.dg/coroutines/torture/class-05-lambda-capture-copy-local.C new file mode 100644 index 0000000000..968940f505 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/class-05-lambda-capture-copy-local.C @@ -0,0 +1,59 @@ +// { dg-do run } + +// template parm in a class + +#include "../coro.h" + +// boiler-plate for tests of codegen +#define USE_AWAIT_TRANSFORM +#include "../coro1-ret-int-yield-int.h" + +template <typename T> +class foo +{ + public: + auto get_lam (int parm) + { + int local = 3; + auto l = [=](T y) -> coro1 + { + T x = y; + co_return co_await x + local; + }; + return l; + } +}; + +int main () +{ + foo<int> inst {}; + auto ll = inst.get_lam (10); + + PRINT ("main: create coro1"); + int arg = 17; // avoid a dangling reference + coro1 x = ll (arg); + if (x.handle.done()) + abort(); + + x.handle.resume(); + PRINT ("main: after resume (initial suspend)"); + + x.handle.resume(); + PRINT ("main: after resume (co_await)"); + + /* Now we should have the co_returned value. */ + int y = x.handle.promise().get_value(); + if ( y != 20 ) + { + PRINTF ("main: wrong result (%d).", y); + 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/class-06-lambda-capture-ref.C b/gcc/testsuite/g++.dg/coroutines/torture/class-06-lambda-capture-ref.C new file mode 100644 index 0000000000..db60132b0e --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/class-06-lambda-capture-ref.C @@ -0,0 +1,59 @@ +// { dg-do run } + +// template parm in a class + +#include "../coro.h" + +// boiler-plate for tests of codegen +#define USE_AWAIT_TRANSFORM +#include "../coro1-ret-int-yield-int.h" + +template <typename T> +class foo +{ + public: + void use_lambda () + { + int a_copy = 20; + int a_ref = 10; + + auto f = [&, a_copy]() -> coro1 + { + co_yield a_ref + a_copy; + co_return a_ref + a_copy; + }; + + coro1 A = f (); + A.handle.resume(); // Initial suspend. + PRINT ("main: [a_copy = 20, a_ref = 10]"); + + int y = A.handle.promise().get_value(); + if (y != 30) + { + PRINTF ("main: co-yield = %d, should be 30\n", y); + abort (); + } + + a_copy = 5; + a_ref = 7; + + A.handle.resume(); // from the yield. + PRINT ("main: [a_copy = 5, a_ref = 7]"); + + y = A.handle.promise().get_value(); + if (y != 27) + { + PRINTF ("main: co-ret = %d, should be 27\n", y); + abort (); + } + PRINT ("use_lambda: about to return"); + } + ~foo () { PRINT ("foo: DTOR"); } +}; + +int main () +{ + foo<int> inst; + inst.use_lambda(); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-00-trivial.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-00-trivial.C new file mode 100644 index 0000000000..a24c261599 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-00-trivial.C @@ -0,0 +1,52 @@ +// { dg-do run } + +// The simplest co_await we can do. + +#include "../coro.h" + +// boiler-plate for tests of codegen +#include "../coro1-ret-int-yield-int.h" + +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] initial suspend"); + f_coro.handle.resume(); + PRINT ("main: resuming [2] co_await"); + 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-01-with-value.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-01-with-value.C new file mode 100644 index 0000000000..db5c90224d --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-01-with-value.C @@ -0,0 +1,57 @@ +// { dg-do run } + +/* The simplest valued co_await we can do. */ + +#include "../coro.h" + +// boiler-plate for tests of codegen +#include "../coro1-ret-int-yield-int.h" + +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] initial suspend"); + f_coro.handle.resume(); + PRINT ("main: resuming [2] co_await suspend_always_intprt"); + 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-02-xform.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-02-xform.C new file mode 100644 index 0000000000..79ee6e1714 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-02-xform.C @@ -0,0 +1,58 @@ +// { dg-do run } + +// Test of basic await transform, no local state. + +#include "../coro.h" + +// boiler-plate for tests of codegen +#define USE_AWAIT_TRANSFORM +#include "../coro1-ret-int-yield-int.h" + +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] initial suspend"); + f_coro.handle.resume(); + PRINT ("main: resuming [2] co_await"); + 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-03-rhs-op.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-03-rhs-op.C new file mode 100644 index 0000000000..6408432573 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-03-rhs-op.C @@ -0,0 +1,58 @@ +// { dg-do run } + +// Basic check of co_await with an expression to await transform. + +#include "../coro.h" + +// boiler-plate for tests of codegen +#define USE_AWAIT_TRANSFORM +#include "../coro1-ret-int-yield-int.h" + +int gX = 1; + +coro1 +f () +{ + gX = co_await 11 + 15; + co_return gX + 16; +} + +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] initial suspend"); + f_coro.handle.resume(); + PRINT ("main: resuming [1] await"); + 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 (26+16) */ + 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-04-control-flow.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-04-control-flow.C new file mode 100644 index 0000000000..9bc99e875d --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-04-control-flow.C @@ -0,0 +1,50 @@ +// { dg-do run } + +// Check correct operation of await transform. + +#include "../coro.h" + +// boiler-plate for tests of codegen +#define USE_AWAIT_TRANSFORM +#include "../coro1-ret-int-yield-int.h" + +/* 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-05-loop.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-05-loop.C new file mode 100644 index 0000000000..34af740c99 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-05-loop.C @@ -0,0 +1,51 @@ +// { dg-do run } + +// Check correct operation of co_await in a loop without local state. + +#include "../coro.h" + +// boiler-plate for tests of codegen +#define USE_AWAIT_TRANSFORM +#include "../coro1-ret-int-yield-int.h" + +/* 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(); + // first value above 100 is 10*11 + 1. + if (y != 111) + { + PRINTF ("main: y is wrong : %d, should be 111\n", y); + abort (); + } + puts ("main: done"); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-06-ovl.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-06-ovl.C new file mode 100644 index 0000000000..14945faffd --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-06-ovl.C @@ -0,0 +1,65 @@ +// { dg-do run } + +// Basic check of the co_await operator overload. + +#include "../coro.h" + +// boiler-plate for tests of codegen +#include "../coro1-ret-int-yield-int.h" + +/* A very simple overload. */ +struct empty +{ + auto operator co_await() const & noexcept { + return coro1::suspend_always_intprt{}; + } +}; + +int gX = 1; +empty e{}; + +coro1 +f () +{ + int a = co_await(e); /* operator ovl. */ + co_return gX + 5 + a; +} + +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'"); + abort (); + } + + PRINT ("main: resuming [1] initial suspend"); + f_coro.handle.resume(); + + PRINT ("main: resuming [2] co_await"); + f_coro.handle.resume(); + + /* we should now have returned with the co_return (11) */ + 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-07-tmpl.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-07-tmpl.C new file mode 100644 index 0000000000..33f8e99d8c --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-07-tmpl.C @@ -0,0 +1,132 @@ +// { dg-do run } + +// Check that we correctly operate when the coroutine object is templated. + +#include "../coro.h" + +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 the int it was constructed with. */ + struct suspend_always_intprt { + T x; + suspend_always_intprt() : x((T)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; } + void unhandled_exception() { PRINT ("** unhandled exception"); } + }; +}; + +/* 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-08-cascade.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-08-cascade.C new file mode 100644 index 0000000000..d34619d6b6 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-08-cascade.C @@ -0,0 +1,63 @@ +// { dg-do run } + +// Check cascaded co_await operations. + +#include "../coro.h" + +#define USE_AWAIT_TRANSFORM +#include "../coro1-ret-int-yield-int.h" + +/* Valued with an await_transform. */ +int gX = 1; +coro1 f () +{ + /* We are going to use an await transform that takes a long, the + await_resume squares it. + so we get 11 ** 4, 14641. */ + gX = (int) co_await co_await 11L; + 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] - inital suspend"); + f_coro.handle.resume(); + + PRINT ("main: resuming [2] - nested"); + f_coro.handle.resume(); + PRINT ("main: resuming [3] - 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-09-pair.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-09-pair.C new file mode 100644 index 0000000000..525c6fc467 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-09-pair.C @@ -0,0 +1,57 @@ +// { dg-do run } + +#include "../coro.h" + +// boiler-plate for tests of codegen +#define USE_AWAIT_TRANSFORM +#include "../coro1-ret-int-yield-int.h" + +/* 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] (initial suspend)"); + f_coro.handle.resume(); + PRINT ("main: resuming [2] one side of add"); + f_coro.handle.resume(); + PRINT ("main: resuming [3] 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-await-10-template-fn-arg.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-10-template-fn-arg.C new file mode 100644 index 0000000000..71a5b18c3c --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-10-template-fn-arg.C @@ -0,0 +1,60 @@ +// { dg-do run } + +// Check type dependent function parms. + +#include "../coro.h" + +// boiler-plate for tests of codegen +// there is a promise ctor that takes a single int. +#define USE_AWAIT_TRANSFORM +#include "../coro1-ret-int-yield-int.h" + +template <typename T> +coro1 +f (T y) noexcept +{ + PRINT ("coro1: about to return"); + T x = y; + co_return co_await x + 3; +} + +int main () +{ + PRINT ("main: create coro1"); + struct coro1 x = f<int>(17); + + /* We should have created the promise with an initial value of + 17. */ + int y = x.handle.promise().get_value(); + if ( y != 17 ) + { + PRINTF ("main: wrong promise init (%d).", y); + abort (); + } + + PRINT ("main: got coro1 - resuming"); + if (x.handle.done()) + abort(); + + x.handle.resume(); + PRINT ("main: after resume (initial suspend)"); + + x.handle.resume(); + PRINT ("main: after resume (co_await)"); + + /* Now we should have the co_returned value. */ + y = x.handle.promise().get_value(); + if ( y != 20 ) + { + PRINTF ("main: wrong result (%d).", y); + 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-await-11-forwarding.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-11-forwarding.C new file mode 100644 index 0000000000..78c88ed14e --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-11-forwarding.C @@ -0,0 +1,43 @@ +// { dg-do run } + +// Test of forwarding a templated awaitable to co_await. + +#include "../coro.h" + +// boiler-plate for tests of codegen +#include "../coro1-ret-int-yield-int.h" + +/* Valued with an await_transform. */ + +template< typename AWAITABLE > +coro1 +test_fwd (AWAITABLE&& awaitable) +{ + // the await_resume() just returns the saved int value. + int a = co_await std::forward<AWAITABLE>(awaitable); + // Which we co-return to the promise so that it can be + // retrieved. + co_return a; +} + +int main () +{ + // We have an awaitable that stores the int it was constructed with. + coro1::suspend_always_intprt g(15); + struct coro1 g_coro = test_fwd (g); + + PRINT ("main: resuming g [1] (initial suspend)"); + g_coro.handle.resume(); + + PRINT ("main: resuming g [2] co_await"); + g_coro.handle.resume(); + + int y = g_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-12-operator-2.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-12-operator-2.C new file mode 100644 index 0000000000..189332b78e --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-12-operator-2.C @@ -0,0 +1,66 @@ +// { dg-do run } + +// Basic check of the co_await operator overload. + +#include "../coro.h" + +// boiler-plate for tests of codegen +#include "../coro1-ret-int-yield-int.h" + +/* A very simple overload. */ +struct empty +{ + auto operator co_await() & noexcept { + return coro1::suspend_always_intprt{}; + } + auto operator co_await() && noexcept { + return coro1::suspend_always_longprtsq(3L); + } +}; + +empty e{}; + +coro1 +f () +{ + int a = co_await e; /* operator ovl lv. */ + int b = co_await empty{}; /* operator ovl rv. */ + co_return b + a; +} + +int main () +{ + PRINT ("main: create coro1"); + struct coro1 f_coro = f (); + + if (f_coro.handle.done()) + { + PRINT ("main: we should not be 'done'"); + abort (); + } + + PRINT ("main: resuming [1] initial suspend"); + f_coro.handle.resume(); + + PRINT ("main: resuming [2] co_await a"); + f_coro.handle.resume(); + + PRINT ("main: resuming [3] co_await b"); + f_coro.handle.resume(); + + /* we should now have returned with the co_return (14) */ + if (!f_coro.handle.done()) + { + PRINT ("main: we should be 'done' "); + abort (); + } + + int y = f_coro.handle.promise().get_value(); + if (y != 14) + { + PRINTF ("main: y is wrong : %d, should be 14\n", y); + abort (); + } + puts ("main: done"); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-13-return-ref.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-13-return-ref.C new file mode 100644 index 0000000000..339ebe4ff2 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-13-return-ref.C @@ -0,0 +1,58 @@ +// { dg-do run } + +/* The simplest valued co_await we can do. */ + +#include "../coro.h" + +// boiler-plate for tests of codegen +#include "../coro1-ret-int-yield-int.h" + +int gX = 1; + +coro1 +f () +{ + int t = 5; + gX = co_await coro1::suspend_always_intrefprt{t}; + co_return t + 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] initial suspend"); + f_coro.handle.resume(); + PRINT ("main: resuming [2] co_await suspend_always_intprt"); + 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-ret-00-void-return-is-ready.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-00-void-return-is-ready.C new file mode 100644 index 0000000000..f551c6e760 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-00-void-return-is-ready.C @@ -0,0 +1,90 @@ +// { dg-do run } + +// Basic functionality check, co_return. +// Here we check the case that initial suspend is "never", so that the co- +// routine runs to completion immediately. + +#include "../coro.h" + +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 ()"); + } + void unhandled_exception() { PRINT ("** unhandled exception"); } + }; +}; + +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 - 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-01-void-return-is-suspend.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-01-void-return-is-suspend.C new file mode 100644 index 0000000000..03fc6eeb84 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-01-void-return-is-suspend.C @@ -0,0 +1,94 @@ +// { dg-do run } + +// Basic functionality check, co_return. +// Here we check the case that initial suspend is "always". + +#include "../coro.h" + +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 ()"); + } + void unhandled_exception() { PRINT ("** unhandled exception"); } + }; +}; + +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 (); + } + PRINT ("main: returning"); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-03-different-GRO-type.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-03-different-GRO-type.C new file mode 100644 index 0000000000..36da680f7f --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-03-different-GRO-type.C @@ -0,0 +1,92 @@ +// { dg-do run } + +// GRO differs from the eventual return type. + +# include "../coro.h" + +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 ()"); + } + void unhandled_exception() { PRINT ("** unhandled exception"); } + }; +}; + +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 (); + } + PRINT ("main: returning"); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-04-GRO-nontriv.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-04-GRO-nontriv.C new file mode 100644 index 0000000000..29fb9424f8 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-04-GRO-nontriv.C @@ -0,0 +1,109 @@ +// { dg-do run } + +// GRO differs from eventual return type and has non-trivial dtor. + +#include "../coro.h" + +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 ()"); + } + void unhandled_exception() { PRINT ("** unhandled exception"); } + }; +}; + +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 (); + } + PRINT ("main: returning"); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-05-return-value.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-05-return-value.C new file mode 100644 index 0000000000..42b80ff6bb --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-05-return-value.C @@ -0,0 +1,38 @@ +// { dg-do run } + +// Test returning an int. +// We will use the promise to contain this to avoid having to include +// additional C++ headers. + +#include "../coro.h" + +// boiler-plate for tests of codegen +#include "../coro1-ret-int-yield-int.h" + +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 (); + } + PRINT ("main: returning"); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-06-template-promise-val-1.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-06-template-promise-val-1.C new file mode 100644 index 0000000000..5b1acb8145 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-06-template-promise-val-1.C @@ -0,0 +1,105 @@ +// { dg-do run } + +// Test returning a T. +// We will use the promise to contain this to avoid having to include +// additional C++ headers. + +#include "../coro.h" + +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; } + void unhandled_exception() { PRINT ("** unhandled exception"); } + }; +}; + +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-07-void-cast-expr.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-07-void-cast-expr.C new file mode 100644 index 0000000000..b1a06f2849 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-07-void-cast-expr.C @@ -0,0 +1,44 @@ +// { dg-do run } + +// Check that "co_return (void)expression;" evaluates expression once. + +#include "../coro.h" + +// boiler-plate for tests of codegen +#define RETURN_VOID +#include "../coro1-ret-int-yield-int.h" + +int gX = 1; +__attribute__((__noinline__)) +int foo (void) { PRINT ("called the int fn foo"); gX +=1 ; return gX; } + +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(); + // We want to check that foo() was called exactly once. + if (gX != 2) + { + PRINT ("main: failed check for a single call to foo()"); + abort (); + } + PRINT ("main: after resume"); + 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-08-template-cast-ret.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-08-template-cast-ret.C new file mode 100644 index 0000000000..266bc7b3b2 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-08-template-cast-ret.C @@ -0,0 +1,104 @@ +// { dg-do run } + +// Test templated co-return. + +#include "../coro.h" + +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; } + void unhandled_exception() { PRINT ("** unhandled exception"); } + }; +}; + +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-09-bool-await-susp.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-09-bool-await-susp.C new file mode 100644 index 0000000000..91f3f14cc0 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-09-bool-await-susp.C @@ -0,0 +1,97 @@ +// { dg-do run } + +// test boolean return from await_suspend (). + +#include "../coro.h" + +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 ()"); + } + void unhandled_exception() { PRINT ("** unhandled exception"); } + }; +}; + +struct coro1 +f () noexcept +{ + PRINT ("coro1: about to return"); + co_return; +} + +int main () +{ + 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-10-expression-evaluates-once.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-10-expression-evaluates-once.C new file mode 100644 index 0000000000..7b07be5f44 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-10-expression-evaluates-once.C @@ -0,0 +1,49 @@ +// { dg-do run } + +// Check that "co_return expression;" only evaluates expression once. + +#include "../coro.h" + +// boiler-plate for tests of codegen +#include "../coro1-ret-int-yield-int.h" + +/* Give foo() a measureable side-effect. */ +int gX = 1; +__attribute__((__noinline__)) +int foo (void) +{ + PRINT ("called the int fn foo"); + gX += 1; + return gX; +} + +struct coro1 +f () noexcept +{ + PRINT ("coro1: about to return"); + co_return foo(); +} + +int main () +{ + PRINT ("main: create coro1"); + struct coro1 x = f (); + PRINT ("main: got coro1 - resuming"); + if (x.handle.done()) + abort(); + x.handle.resume(); + // We want to check that foo() was called exactly once. + if (gX != 2) + { + PRINT ("main: failed check for a single call to foo()"); + abort (); + } + PRINT ("main: after resume"); + 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-11-co-ret-co-await.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-11-co-ret-co-await.C new file mode 100644 index 0000000000..06939107d8 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-11-co-ret-co-await.C @@ -0,0 +1,40 @@ +// { dg-do run } + +// Check co_return co_await + +#include "../coro.h" + +// boiler-plate for tests of codegen +#include "../coro1-ret-int-yield-int.h" + +struct coro1 +f () noexcept +{ + PRINT ("coro1: about to return"); + co_return co_await coro1::suspend_always_intprt{}; +} + +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 1 (initial suspend)"); + x.handle.resume(); + PRINT ("main: after resume 2 (await intprt)"); + + int y = x.handle.promise().get_value(); + if ( y != 5 ) + 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-12-co-ret-fun-co-await.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-12-co-ret-fun-co-await.C new file mode 100644 index 0000000000..50124c080b --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-12-co-ret-fun-co-await.C @@ -0,0 +1,48 @@ +// { dg-do run } + +// Check co_return function (co_await) + +#include "../coro.h" + +// boiler-plate for tests of codegen +#define USE_AWAIT_TRANSFORM +#include "../coro1-ret-int-yield-int.h" + +__attribute__((__noinline__)) +static int +foo (int x) +{ + return x + 2; +} + +struct coro1 +f () noexcept +{ + PRINT ("coro1: about to return"); + co_return foo (co_await 5); +} + +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 1 (initial suspend)"); + x.handle.resume(); + PRINT ("main: after resume 2 (await parm)"); + + int y = x.handle.promise().get_value(); + if ( y != 7 ) + 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-13-template-2.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-13-template-2.C new file mode 100644 index 0000000000..9d4a4de8eb --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-13-template-2.C @@ -0,0 +1,56 @@ +// { dg-do run } + +// Check type dependent function parms. + +#include "../coro.h" + +// boiler-plate for tests of codegen +// there is a promise ctor that takes a single int. +#include "../coro1-ret-int-yield-int.h" + +template <typename T> +coro1 +f (T y) noexcept +{ + PRINT ("coro1: about to return"); + T x = y; + co_return 3; +} + +int main () +{ + PRINT ("main: create coro1"); + struct coro1 x = f<int>(17); + + /* We should have created the promise with an initial value of + 17. */ + int y = x.handle.promise().get_value(); + if ( y != 17 ) + { + PRINT ("main: wrong promise init."); + abort (); + } + + PRINT ("main: got coro1 - resuming"); + if (x.handle.done()) + abort(); + + x.handle.resume(); + PRINT ("main: after resume (initial suspend)"); + + /* Now we should have the co_returned value. */ + y = x.handle.promise().get_value(); + if ( y != 3 ) + { + PRINT ("main: wrong answer."); + 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-14-template-3.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-14-template-3.C new file mode 100644 index 0000000000..ebc1adba82 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-14-template-3.C @@ -0,0 +1,58 @@ +// { dg-do run } + +// Check type dependent function parms. + +#include "../coro.h" + +// boiler-plate for tests of codegen +// there is a promise ctor that takes a single int. +#include "../coro1-ret-int-yield-int.h" + +template <typename T, typename U, typename V> +coro1 +f (T x, U y, V z) noexcept +{ + PRINT ("coro1: about to return"); + T xi = (T) y; + T yi = (T) z; + T zi = x; + co_return 3 + xi + yi + zi; +} + +int main () +{ + PRINT ("main: create coro1"); + struct coro1 x = f<int, float, double>(2, 18.0F, 19.0); + + /* We should be using the default promise ctor, which sets the value + to -1. */ + int y = x.handle.promise().get_value(); + if ( y != -1 ) + { + PRINT ("main: wrong promise init."); + abort (); + } + + PRINT ("main: got coro1 - resuming"); + if (x.handle.done()) + abort(); + + x.handle.resume(); + PRINT ("main: after resume (initial suspend)"); + + /* Now we should have the co_returned value. */ + y = x.handle.promise().get_value(); + if ( y != 42 ) + { + PRINT ("main: wrong answer."); + 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-yield-00-triv.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-00-triv.C new file mode 100644 index 0000000000..586b6b2571 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-00-triv.C @@ -0,0 +1,129 @@ +// { dg-do run } + +// Test yielding an int. + +// We will use the promise to contain this to avoid having to include +// additional C++ headers. + +// Check that we resolve the correct overload for the yield_value method. + +#include "../coro.h" + +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; } + void unhandled_exception() { PRINT ("** unhandled exception"); } + }; +}; + +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-01-multi.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-01-multi.C new file mode 100644 index 0000000000..5df69c7f15 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-01-multi.C @@ -0,0 +1,64 @@ +// { dg-do run } + +// Test yielding an int. +// We will use the promise to contain this to avoid having to include +// additional C++ headers. + +#include "../coro.h" + +// boiler-plate for tests of codegen +#include "../coro1-ret-int-yield-int.h" + +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-02-loop.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-02-loop.C new file mode 100644 index 0000000000..8d4f1d5d82 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-02-loop.C @@ -0,0 +1,68 @@ +// { dg-do run } + +// Test co_yield in a loop with no local state. + +#include "../coro.h" + +// boiler-plate for tests of codegen +#include "../coro1-ret-int-yield-int.h" + +int gX = 1; + +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; +} + +int main () +{ + PRINT ("main: create coro1"); + struct coro1 f_coro = f (); + PRINT ("main: got coro1 - resuming (1)"); + if (gX != 1) + { + PRINTF ("main: gX is wrong : %d, should be 1\n", gX); + abort (); + } + if (f_coro.handle.done()) + abort(); + f_coro.handle.resume(); + PRINT ("main: after resume (1)"); + int y = f_coro.handle.promise().get_value(); + if (y != 5) + { + PRINTF ("main: got %d not 5.\n",y); + abort (); + } + PRINT ("main: gX OK -- looping"); + do { + y = f_coro.handle.promise().get_value(); + if (y != gX) + { + PRINTF ("main: got %d not %d.\n",y, gX); + abort (); + } + 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 (); + } + PRINT ("main: returning"); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-yield-03-tmpl.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-03-tmpl.C new file mode 100644 index 0000000000..cceee1f19e --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-03-tmpl.C @@ -0,0 +1,140 @@ +// { dg-do run } + +// Test co_yield in templated code. + +#include "../coro.h" + +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; } + + void unhandled_exception() { PRINT ("** unhandled exception"); } + }; + + 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-04-complex-local-state.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-04-complex-local-state.C new file mode 100644 index 0000000000..d9330b33b7 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-04-complex-local-state.C @@ -0,0 +1,162 @@ +// { dg-do run } + +// using non-trivial types in the coro. + +# include "../coro.h" + +#include <vector> +#include <string> + +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; } + + void unhandled_exception() { PRINT ("** unhandled exception"); } + }; + + 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 (); + } + + 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/co-yield-05-co-aw.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-05-co-aw.C new file mode 100644 index 0000000000..043f97b6e1 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-05-co-aw.C @@ -0,0 +1,55 @@ +// { dg-do run } + +// Check co_return co_await + +#include "../coro.h" + +// boiler-plate for tests of codegen +#include "../coro1-ret-int-yield-int.h" + +struct coro1 +f () noexcept +{ + PRINT ("f: about to yield"); + co_yield co_await coro1::suspend_always_intprt(42); + + PRINT ("f: about to return 6174"); + co_return 6174; +} + +int main () +{ + PRINT ("main: create coro1"); + struct coro1 x = f (); + if (x.handle.done()) + abort(); + + PRINT ("main: resuming (initial suspend)"); + x.handle.resume(); + PRINT ("main: resuming (await intprt)"); + x.handle.resume(); + + PRINT ("main: after resume (2)"); + int y = x.handle.promise().get_value(); + if ( y != 42 ) + abort (); + PRINT ("main: apparently got 42"); + + PRINT ("main: got coro1 - resuming (co_yield)"); + if (x.handle.done()) + abort(); + x.handle.resume(); + + PRINT ("main: after resume (co_yield)"); + 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-06-fun-parm.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-06-fun-parm.C new file mode 100644 index 0000000000..c74e44d15d --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-06-fun-parm.C @@ -0,0 +1,64 @@ +// { dg-do run } + +// Check co_return co_await + +#include "../coro.h" + +// boiler-plate for tests of codegen +#define USE_AWAIT_TRANSFORM +#include "../coro1-ret-int-yield-int.h" + +__attribute__((__noinline__)) +static int +foo (int x) +{ + return x + 2; +} + +/* Function with a single await. */ +struct coro1 +f () noexcept +{ + PRINT ("f: about to yield"); + co_yield foo (co_await 40); + + PRINT ("f: about to return 6174"); + co_return 6174; +} + +int main () +{ + PRINT ("main: create coro1"); + struct coro1 x = f (); + if (x.handle.done()) + abort(); + + PRINT ("main: resuming (initial suspend)"); + x.handle.resume(); + PRINT ("main: resuming (await intprt)"); + x.handle.resume(); + + PRINT ("main: after resume (2)"); + int y = x.handle.promise().get_value(); + if ( y != 42 ) + abort (); + PRINT ("main: apparently got 42"); + + PRINT ("main: got coro1 - resuming (co_yield)"); + if (x.handle.done()) + abort(); + x.handle.resume(); + + PRINT ("main: after resume (co_yield)"); + 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-07-template-fn-param.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-07-template-fn-param.C new file mode 100644 index 0000000000..74dae63395 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-07-template-fn-param.C @@ -0,0 +1,71 @@ +// { dg-do run } + +// Check type dependent function parms. + +#include "../coro.h" + +// boiler-plate for tests of codegen +// there is a promise ctor that takes a single int. + +#include "../coro1-ret-int-yield-int.h" + +template <typename T> +coro1 +f (T y) noexcept +{ + PRINT ("coro1: about to return"); + T x = y; + co_yield x + 3; + co_return 42; +} + +int main () +{ + PRINT ("main: create coro1"); + struct coro1 x = f<int>(17); + + /* We should have created the promise with an initial value of + 17. */ + int y = x.handle.promise().get_value(); + if ( y != 17 ) + { + PRINTF ("main: wrong promise init (%d).", y); + abort (); + } + if (x.handle.done()) + abort(); + + PRINT ("main: got coro1 - resuming"); + x.handle.resume(); + PRINT ("main: after resume (initial suspend)"); + + if (x.handle.done()) + abort(); + + /* Now we should have the co_yielded value. */ + y = x.handle.promise().get_value(); + if ( y != 20 ) + { + PRINTF ("main: wrong result (%d).", y); + abort (); + } + + PRINT ("main: after resume (co_yield)"); + x.handle.resume(); + + /* now we should have the co_returned value. */ + y = x.handle.promise().get_value(); + if ( y != 42 ) + { + PRINTF ("main: wrong result (%d).", y); + 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-yield-08-more-refs.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-08-more-refs.C new file mode 100644 index 0000000000..8e39127a1a --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-08-more-refs.C @@ -0,0 +1,68 @@ +// { dg-do run } + +// Check co_return co_await + +#include "../coro.h" + +// boiler-plate for tests of codegen +#include "../coro1-ret-int-yield-int.h" + +/* Tests for . */ +struct test +{ + auto operator co_await() & noexcept { + return coro1::suspend_always_intprt{}; + } + + auto operator co_await() && noexcept { + return coro1::suspend_always_longprtsq(3L); + } +}; + +struct coro1 +f (test thing) noexcept +{ + co_yield co_await static_cast<test&&>(thing); + co_return 6174; +} + +int main () +{ + PRINT ("main: create coro1"); + + struct coro1 x = f (test{}); + if (x.handle.done()) + abort(); + + PRINT ("main: resuming (initial suspend)"); + x.handle.resume(); + PRINT ("main: resuming (await intprt)"); + x.handle.resume(); + + int y = x.handle.promise().get_value(); + if ( y != 9 ) + { + PRINTF ("main: co-yield gave %d, should be 9\n", y); + abort (); + } + + PRINT ("main: got coro1 - resuming (co_yield)"); + if (x.handle.done()) + abort(); + x.handle.resume(); + + y = x.handle.promise().get_value(); + if ( y != 6174 ) + { + PRINTF ("main: co-return gave %d, should be 9\n", y); + 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-yield-09-more-templ-refs.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-09-more-templ-refs.C new file mode 100644 index 0000000000..3abbe1c43a --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-09-more-templ-refs.C @@ -0,0 +1,68 @@ +// { dg-do run } + +// Check co_return co_await + +#include "../coro.h" + +// boiler-plate for tests of codegen +#include "../coro1-ret-int-yield-int.h" + +/* A very simple overload. */ +struct test +{ + auto operator co_await() & noexcept { + return coro1::suspend_always_intprt{}; + } + + auto operator co_await() && noexcept { + return coro1::suspend_always_longprtsq(3L); + } +}; + +template<typename RESULT, typename PARAM> +RESULT +f (PARAM thing) noexcept +{ + co_yield co_await static_cast<PARAM&&>(thing); + co_return 6174; +} + +int main () +{ + PRINT ("main: create coro1"); + struct coro1 x = f<coro1, test> (test{}); + if (x.handle.done()) + abort(); + + PRINT ("main: resuming (initial suspend)"); + x.handle.resume(); + PRINT ("main: resuming (await intprt)"); + x.handle.resume(); + + int y = x.handle.promise().get_value(); + if ( y != 9 ) + { + PRINTF ("main: co-yield gave %d, should be 9\n", y); + abort (); + } + + PRINT ("main: got coro1 - resuming (co_yield)"); + if (x.handle.done()) + abort(); + x.handle.resume(); + + y = x.handle.promise().get_value(); + if ( y != 6174 ) + { + PRINTF ("main: co-return gave %d, should be 9\n", y); + 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/coro-torture.exp b/gcc/testsuite/g++.dg/coroutines/torture/coro-torture.exp new file mode 100644 index 0000000000..d2463b2798 --- /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 0000000000..164c804797 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/exceptions-test-0.C @@ -0,0 +1,167 @@ +// { dg-do run } + +// Test exceptions. + +#include "../coro.h" +#include <exception> + +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; } + + void unhandled_exception() { + PRINT ("unhandled_exception: caught one!"); + gX = -1; + // returning from here should end up in final_suspend. + } + }; +}; + +// 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-00.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-00.C new file mode 100644 index 0000000000..b5716972d4 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-00.C @@ -0,0 +1,42 @@ +// { dg-do run } + +// Test promise construction from function args list. + +#include "../coro.h" + +// boiler-plate for tests of codegen +#include "../coro1-ret-int-yield-int.h" + +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 ) + { + PRINT ("main: incorrect ctor value"); + 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 (); + } + PRINT ("main: returning"); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-01.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-01.C new file mode 100644 index 0000000000..f530431a6b --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-01.C @@ -0,0 +1,45 @@ +// { dg-do run } + +// Simplest test that we correctly handle function params in the body +// of the coroutine. No local state, just the parm. + +#include "../coro.h" + +// boiler-plate for tests of codegen +#include "../coro1-ret-int-yield-int.h" + +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 (); + } + PRINT ("main: returning"); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-02.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-02.C new file mode 100644 index 0000000000..396b438cb2 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-02.C @@ -0,0 +1,50 @@ +// { dg-do run } + +// Test that we correctly re-write multiple uses of a function param +// in the body. + +#include "../coro.h" + +// boiler-plate for tests of codegen +#include "../coro1-ret-int-yield-int.h" + +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 (); + } + PRINT ("main: returning"); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-03.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-03.C new file mode 100644 index 0000000000..bf699722a1 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-03.C @@ -0,0 +1,49 @@ +// { dg-do run } + +// Test that we can use a function param in a co_xxxx status. + +#include "../coro.h" + +// boiler-plate for tests of codegen +#include "../coro1-ret-int-yield-int.h" + +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 (); + } + PRINT ("main: returning"); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-04.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-04.C new file mode 100644 index 0000000000..789e2c05b6 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-04.C @@ -0,0 +1,57 @@ +// { dg-do run } + +// Test that we can manage a constructed param copy. + +#include "../coro.h" + +// boiler-plate for tests of codegen +#include "../coro1-ret-int-yield-int.h" + +// 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 (); + } + PRINT ("main: returning"); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-05.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-05.C new file mode 100644 index 0000000000..8bdb2b5d0f --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-05.C @@ -0,0 +1,57 @@ +// { dg-do run } + +// Test that we can manage a constructed param reference + +#include "../coro.h" + +// boiler-plate for tests of codegen +#include "../coro1-ret-int-yield-int.h" + +// 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 (); + } + PRINT ("main: returning"); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-06.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-06.C new file mode 100644 index 0000000000..cbcfe67ff1 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-06.C @@ -0,0 +1,47 @@ +// { dg-do run } + +// check references are handled as expected. + +#include "../coro.h" + +// boiler-plate for tests of codegen +#include "../coro1-ret-int-yield-int.h" + +coro1 +f (int& a_ref, int a_copy) +{ + co_yield a_ref + a_copy; + co_return a_ref + a_copy; +} + +int main () +{ + int a_copy = 20; + int a_ref = 10; + + coro1 A = f (a_ref, a_copy); + A.handle.resume(); // Initial suspend. + PRINT ("main: [a_copy = 20, a_ref = 10]"); + + int y = A.handle.promise().get_value(); + if (y != 30) + { + PRINTF ("main: co-yield = %d, should be 30\n", y); + abort (); + } + + a_copy = 5; + a_ref = 7; + + A.handle.resume(); + PRINT ("main: [a_copy = 5, a_ref = 7]"); + + y = A.handle.promise().get_value(); + if (y != 27) + { + PRINTF ("main: co-ret = %d, should be 27\n", y); + abort (); + } + + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/torture/lambda-00-co-ret.C b/gcc/testsuite/g++.dg/coroutines/torture/lambda-00-co-ret.C new file mode 100644 index 0000000000..61e284d5c8 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/lambda-00-co-ret.C @@ -0,0 +1,35 @@ +// { dg-do run } + +// Simplest lambda + +#include "../coro.h" + +// boiler-plate for tests of codegen +#include "../coro1-ret-int-yield-int.h" + +int main () +{ + auto f = []() -> coro1 + { + PRINT ("coro1: about to return"); + co_return 42; + }; + + PRINT ("main: create coro1"); + 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 (); + } + PRINT ("main: returning"); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/torture/lambda-01-co-ret-parm.C b/gcc/testsuite/g++.dg/coroutines/torture/lambda-01-co-ret-parm.C new file mode 100644 index 0000000000..378eedc6d8 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/lambda-01-co-ret-parm.C @@ -0,0 +1,48 @@ +// { dg-do run } + +// Lambda with parm + +#include "../coro.h" + +// boiler-plate for tests of codegen +#include "../coro1-ret-int-yield-int.h" + +int main () +{ + auto f = [](int x) -> coro1 + { + 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; + } + }; + + PRINT ("main: create coro1"); + 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 (); + } + PRINT ("main: returning"); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/torture/lambda-02-co-yield-values.C b/gcc/testsuite/g++.dg/coroutines/torture/lambda-02-co-yield-values.C new file mode 100644 index 0000000000..a6f592cd77 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/lambda-02-co-yield-values.C @@ -0,0 +1,64 @@ +// { dg-do run } + +// lambda with parm and local state + +#include "../coro.h" + +// boiler-plate for tests of codegen +#include "../coro1-ret-int-yield-int.h" + +int main () +{ + auto f = [](int start) -> coro1 + { + 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; + }; + + PRINT ("main: create coro1"); + 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/lambda-03-auto-parm-1.C b/gcc/testsuite/g++.dg/coroutines/torture/lambda-03-auto-parm-1.C new file mode 100644 index 0000000000..bfa5400225 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/lambda-03-auto-parm-1.C @@ -0,0 +1,46 @@ +// { dg-do run } + +// generic Lambda with auto parm (c++14) + +#include "../coro.h" + +// boiler-plate for tests of codegen +#define USE_AWAIT_TRANSFORM +#include "../coro1-ret-int-yield-int.h" + +int main () +{ + auto f = [](auto y) -> coro1 + { + PRINT ("coro1: about to return"); + auto x = y; + co_return co_await x + 3; + }; + + PRINT ("main: create coro1"); + struct coro1 x = f((int)17); + if (x.handle.done()) + abort(); + + x.handle.resume(); + PRINT ("main: after resume (initial suspend)"); + + x.handle.resume(); + PRINT ("main: after resume (co_await)"); + + /* Now we should have the co_returned value. */ + int y = x.handle.promise().get_value(); + if ( y != 20 ) + { + PRINTF ("main: wrong result (%d).", y); + 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/lambda-04-templ-parm.C b/gcc/testsuite/g++.dg/coroutines/torture/lambda-04-templ-parm.C new file mode 100644 index 0000000000..adf31e22db --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/lambda-04-templ-parm.C @@ -0,0 +1,47 @@ +// { dg-do run } +// { dg-additional-options "-std=c++2a" } + +// generic Lambda with template parm (from c++20) + +#include "../coro.h" + +// boiler-plate for tests of codegen +#define USE_AWAIT_TRANSFORM +#include "../coro1-ret-int-yield-int.h" + +int main () +{ + auto f = []<typename T>(T y) -> coro1 + { + PRINT ("coro1: about to return"); + T x = y; + co_return co_await x + 3; + }; + + PRINT ("main: create coro1"); + coro1 x = f.operator()<int>(17); + if (x.handle.done()) + abort(); + + x.handle.resume(); + PRINT ("main: after resume (initial suspend)"); + + x.handle.resume(); + PRINT ("main: after resume (co_await)"); + + /* Now we should have the co_returned value. */ + int y = x.handle.promise().get_value(); + if ( y != 20 ) + { + PRINTF ("main: wrong result (%d).", y); + 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/lambda-05-capture-copy-local.C b/gcc/testsuite/g++.dg/coroutines/torture/lambda-05-capture-copy-local.C new file mode 100644 index 0000000000..7cd6648cca --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/lambda-05-capture-copy-local.C @@ -0,0 +1,66 @@ +// { dg-do run } + +// lambda with parm and local state + +#include "../coro.h" + +// boiler-plate for tests of codegen +#include "../coro1-ret-int-yield-int.h" + +int main () +{ + int local = 31; + + auto f = [=](int start) -> coro1 + { + int value = start; + PRINT ("f: about to yield start"); + co_yield start; + + value -= local; + PRINT ("f: about to yield (value-31)"); + co_yield value; + + value += 6163; + PRINT ("f: about to return (value+6163)"); + co_return value; + }; + + PRINT ("main: create coro1"); + 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/lambda-06-multi-capture.C b/gcc/testsuite/g++.dg/coroutines/torture/lambda-06-multi-capture.C new file mode 100644 index 0000000000..7b445d3d9c --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/lambda-06-multi-capture.C @@ -0,0 +1,48 @@ +// { dg-do run } + +// lambda with parm and local state + +#include "../coro.h" + +// boiler-plate for tests of codegen +#include "../coro1-ret-int-yield-int.h" + +int main () +{ + int a_copy = 20; + int a_ref = 10; + + auto f = [&, a_copy]() -> coro1 + { + co_return a_ref + a_copy; + }; + + { + coro1 A = f (); + A.handle.resume(); + PRINT ("main: [a_copy = 20, a_ref = 10]"); + + int y = A.handle.promise().get_value(); + if (y != 30) + { + PRINTF ("main: A co-ret = %d, should be 30\n", y); + abort (); + } + } + + a_copy = 5; + a_ref = 7; + + coro1 B = f (); + B.handle.resume(); + PRINT ("main: [a_copy = 5, a_ref = 7]"); + + int y = B.handle.promise().get_value(); + if (y != 27) + { + PRINTF ("main: B co-ret = %d, should be 27\n", y); + abort (); + } + + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/torture/lambda-07-multi-yield.C b/gcc/testsuite/g++.dg/coroutines/torture/lambda-07-multi-yield.C new file mode 100644 index 0000000000..2bd58cbf2e --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/lambda-07-multi-yield.C @@ -0,0 +1,46 @@ +// { dg-do run } + +// lambda with parm and local state + +#include "../coro.h" + +// boiler-plate for tests of codegen +#include "../coro1-ret-int-yield-int.h" + +int main () +{ + int a_copy = 20; + int a_ref = 10; + + auto f = [&, a_copy]() -> coro1 + { + co_yield a_ref + a_copy; + co_return a_ref + a_copy; + }; + + coro1 A = f (); + A.handle.resume(); // Initial suspend. + PRINT ("main: [a_copy = 20, a_ref = 10]"); + + int y = A.handle.promise().get_value(); + if (y != 30) + { + PRINTF ("main: co-yield = %d, should be 30\n", y); + abort (); + } + + a_copy = 5; + a_ref = 7; + + A.handle.resume(); + PRINT ("main: [a_copy = 5, a_ref = 7]"); + + y = A.handle.promise().get_value(); + if (y != 27) + { + PRINTF ("main: co-ret = %d, should be 27\n", y); + abort (); + } + + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/torture/lambda-08-co-ret-parm-ref.C b/gcc/testsuite/g++.dg/coroutines/torture/lambda-08-co-ret-parm-ref.C new file mode 100644 index 0000000000..4d5a44fe29 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/lambda-08-co-ret-parm-ref.C @@ -0,0 +1,59 @@ +// { dg-do run } + +// Test that we can use a function param in a co_xxxx status. + +#include "../coro.h" + +// boiler-plate for tests of codegen +#include "../coro1-ret-int-yield-int.h" + +int main () +{ + int val; + + auto f = [&] (int x) -> coro1 + { + if (val + x > 25) + { + PRINT ("coro1: about to return k"); + co_return 6174; + } + else if (val + x > 20) + { + PRINTF ("coro1: about to co-return %d\n", val + x); + co_return val + x; + } + else if (val + x > 5) + { + PRINTF ("coro1: about to co-return %d\n", val); + co_return val; + } + else + { + PRINT ("coro1: about to return 0"); + co_return 0; + } + }; + + PRINT ("main: create coro1"); + + val = 20; // We should get this by ref. + int arg = 5; // and this as a regular parm. + + coro1 x = f (arg); + 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 (); + } + 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 0000000000..a8956457dc --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/local-var-0.C @@ -0,0 +1,37 @@ +// { dg-do run } + +// Simplest local decl. + +#include "../coro.h" + +// boiler-plate for tests of codegen +#include "../coro1-ret-int-yield-int.h" + +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 (); + } + 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 0000000000..69a5b70756 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/local-var-1.C @@ -0,0 +1,37 @@ +// { dg-do run } + +// Simplest local var + +#include "../coro.h" + +// boiler-plate for tests of codegen +#include "../coro1-ret-int-yield-int.h" + +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 (); + } + 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 0000000000..f232edabda --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/local-var-2.C @@ -0,0 +1,50 @@ +// { dg-do run } + +// Test local vars in nested scopes + +#include "../coro.h" + +// boiler-plate for tests of codegen +#include "../coro1-ret-int-yield-int.h" + +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 0000000000..bd06db53d4 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/local-var-3.C @@ -0,0 +1,65 @@ +// { dg-do run } + +// Test modifying a local var and yielding several instances of it. + +#include "../coro.h" + +// boiler-plate for tests of codegen +#include "../coro1-ret-int-yield-int.h" + +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 0000000000..419eb6b646 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/local-var-4.C @@ -0,0 +1,75 @@ +// { dg-do run } + +// Test modifying a local var across nested scopes containing vars +// hiding those at outer scopes. + +#include "../coro.h" + +// boiler-plate for tests of codegen +#include "../coro1-ret-int-yield-int.h" + +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 0000000000..934fb19de7 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/mid-suspend-destruction-0.C @@ -0,0 +1,107 @@ +// { 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. + +#include "../coro.h" + +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 ()"); + } + + void unhandled_exception() { PRINT ("** unhandled exception"); } + }; +}; + +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 (! __builtin_coro_suspended(handle)) + { + 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; +} diff --git a/gcc/testsuite/g++.dg/coroutines/torture/pr92933.C b/gcc/testsuite/g++.dg/coroutines/torture/pr92933.C new file mode 100644 index 0000000000..b2f1be78b3 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/pr92933.C @@ -0,0 +1,18 @@ +// { dg-do compile } + +// Test that we compile the simple case described in PR 92933 + +#include "../coro.h" + +#define RETURN_VOID +#include "../coro1-ret-int-yield-int.h" + +struct some_error {}; + +coro1 +foo() { + try { + co_return; + } catch (some_error) { + } +}