From patchwork Sun Nov 17 10:28:26 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Iain Sandoe X-Patchwork-Id: 1196348 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=209.132.180.131; helo=sourceware.org; envelope-from=gcc-patches-return-513824-incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=sandoe.co.uk Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=gcc.gnu.org header.i=@gcc.gnu.org header.b="Z1Nv+OvT"; dkim-atps=neutral Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 47G7cY1kXjz9sP6 for ; Sun, 17 Nov 2019 21:29:32 +1100 (AEDT) DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:from :content-type:content-transfer-encoding:mime-version:subject :date:references:to:in-reply-to:message-id; q=dns; s=default; b= fW0CmPLLT5g3RUz/vrgdJxnGQwfQPNqi4dae7O8Tb8Yb+bNcpiVOQ6eIMZHXgdaq xirCnjfbMs8GHlY0RKgZzaCPT4SSmA/FgsZbkLqPNdtvpjS9D61c68fAdaPClDnk TByA3Y9CMYefMZomnT7MUAYVxDcv3aPkQkbXAIMVP2Q= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:from :content-type:content-transfer-encoding:mime-version:subject :date:references:to:in-reply-to:message-id; s=default; bh=TKUCd9 1WeyjBlvM5BxguMM6WbHc=; b=Z1Nv+OvTOans78IX2jivBpmlOylC26vhirBWMa aONjBnNiLoSjD7SdF1Glwpayk/I8NZCwWJjxMnBNhVEbfdws1+8anzZPHwAcIC2x Hkx8n/H2F8U4s1lsG2LhaQSauE8N0t8DRWdte59/Rj6Ivbu9f3FqLGCpA6KJzK63 7fJsU= Received: (qmail 35076 invoked by alias); 17 Nov 2019 10:28:54 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 35059 invoked by uid 89); 17 Nov 2019 10:28:54 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-22.7 required=5.0 tests=AWL, BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, KAM_COUK, KAM_SHORT, RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 spammy=Valued, nv, _nt, tb X-HELO: smtp1.wavenetuk.net Received: from smtp.wavenetuk.net (HELO smtp1.wavenetuk.net) (195.26.36.10) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Sun, 17 Nov 2019 10:28:35 +0000 Received: from [192.168.1.212] (host81-138-1-83.in-addr.btopenworld.com [81.138.1.83]) by smtp1.wavenetuk.net (Postfix) with ESMTPA id 6F5D8120062C; Sun, 17 Nov 2019 10:28:32 +0000 (GMT) From: Iain Sandoe Mime-Version: 1.0 (Mac OS X Mail 10.3 \(3273\)) Subject: [C++ coroutines 6/6] Testsuite. Date: Sun, 17 Nov 2019 10:28:26 +0000 References: <285E6AA6-17E6-4E7F-9F37-852707896DA1@sandoe.co.uk> <3D9E9C45-F7A2-42E4-B0B0-51B03A1041F0@sandoe.co.uk> To: GCC Patches , libstdc++ In-Reply-To: <3D9E9C45-F7A2-42E4-B0B0-51B03A1041F0@sandoe.co.uk> Message-Id: There are two categories of test: 1. Checks for correctly formed source code and the error reporting. 2. Checks for transformation and code-gen. The second set are run as 'torture' tests for the standard options set, including LTO. These are also intentionally run with no options provided (from the coroutines.exp script). gcc/testsuite/ChangeLog: 2019-11-17 Iain Sandoe * g++.dg/coroutines/co-yield-syntax-1.C: New test. * g++.dg/coroutines/co-yield-syntax-2.C: New test. * g++.dg/coroutines/co-yield-syntax-3.C: New test. * g++.dg/coroutines/coro-auto-fn.C: New test. * g++.dg/coroutines/coro-await-context-auto-fn.C: New test. * g++.dg/coroutines/coro-bad-return.C: New test. * g++.dg/coroutines/coro-builtins.C: New test. * g++.dg/coroutines/coro-constexpr-fn.C: New test. * g++.dg/coroutines/coro-context-ctor-dtor.C: New test. * g++.dg/coroutines/coro-context-main.C: New test. * g++.dg/coroutines/coro-context-vararg.C: New test. * g++.dg/coroutines/coro-missing-gro.C: New test. * g++.dg/coroutines/coro-missing-promise-yield.C: New test. * g++.dg/coroutines/coro-missing-ret-value.C: New test. * g++.dg/coroutines/coro-missing-ret-void.C: New test. * g++.dg/coroutines/coro-missing-ueh-1.C: New test. * g++.dg/coroutines/coro-missing-ueh-2.C: New test. * g++.dg/coroutines/coro-missing-ueh-3.C: New test. * g++.dg/coroutines/coro-missing-ueh.h: New test. * g++.dg/coroutines/coro-pre-proc.C: New test. * g++.dg/coroutines/coro.h: New test. * g++.dg/coroutines/coroutines.exp: New file. * g++.dg/coroutines/torture/co-await-0-triv.C: New test. * g++.dg/coroutines/torture/co-await-1-value.C: New test. * g++.dg/coroutines/torture/co-await-2-xform.C: New test. * g++.dg/coroutines/torture/co-await-3-rhs-op.C: New test. * g++.dg/coroutines/torture/co-await-4-control-flow.C: New test. * g++.dg/coroutines/torture/co-await-5-loop.C: New test. * g++.dg/coroutines/torture/co-await-6-ovl.C: New test. * g++.dg/coroutines/torture/co-await-7-tmpl.C: New test. * g++.dg/coroutines/torture/co-await-8-cascade.C: New test. * g++.dg/coroutines/torture/co-await-9-pair.C: New test. * g++.dg/coroutines/torture/co-ret-3.C: New test. * g++.dg/coroutines/torture/co-ret-4.C: New test. * g++.dg/coroutines/torture/co-ret-5.C: New test. * g++.dg/coroutines/torture/co-ret-6.C: New test. * g++.dg/coroutines/torture/co-ret-7.C: New test. * g++.dg/coroutines/torture/co-ret-8.C: New test. * g++.dg/coroutines/torture/co-ret-9.C: New test. * g++.dg/coroutines/torture/co-ret-void-is-ready.C: New test. * g++.dg/coroutines/torture/co-ret-void-is-suspend.C: New test. * g++.dg/coroutines/torture/co-yield-0-triv.C: New test. * g++.dg/coroutines/torture/co-yield-1-multi.C: New test. * g++.dg/coroutines/torture/co-yield-2-loop.C: New test. * g++.dg/coroutines/torture/co-yield-3-tmpl.C: New test. * g++.dg/coroutines/torture/co-yield-strings.C: New test. * g++.dg/coroutines/torture/coro-torture.exp: New file. * g++.dg/coroutines/torture/exceptions-test-0.C: New test. * g++.dg/coroutines/torture/func-params-0.C: New test. * g++.dg/coroutines/torture/func-params-1.C: New test. * g++.dg/coroutines/torture/func-params-2.C: New test. * g++.dg/coroutines/torture/func-params-3.C: New test. * g++.dg/coroutines/torture/func-params-4.C: New test. * g++.dg/coroutines/torture/func-params-5.C: New test. * g++.dg/coroutines/torture/gro_on_alloc_fail_0.C: New test. * g++.dg/coroutines/torture/local-var-0.C: New test. * g++.dg/coroutines/torture/local-var-1.C: New test. * g++.dg/coroutines/torture/local-var-2.C: New test. * g++.dg/coroutines/torture/local-var-3.C: New test. * g++.dg/coroutines/torture/local-var-4.C: New test. * g++.dg/coroutines/torture/mid-suspend-destruction-0.C: New test. --- .../g++.dg/coroutines/co-yield-syntax-1.C | 6 + .../g++.dg/coroutines/co-yield-syntax-2.C | 6 + .../g++.dg/coroutines/co-yield-syntax-3.C | 37 ++++ gcc/testsuite/g++.dg/coroutines/coro-auto-fn.C | 12 ++ .../g++.dg/coroutines/coro-await-context-auto-fn.C | 16 ++ gcc/testsuite/g++.dg/coroutines/coro-bad-return.C | 48 ++++++ gcc/testsuite/g++.dg/coroutines/coro-builtins.C | 17 ++ .../g++.dg/coroutines/coro-constexpr-fn.C | 12 ++ .../g++.dg/coroutines/coro-context-ctor-dtor.C | 8 + .../g++.dg/coroutines/coro-context-main.C | 7 + .../g++.dg/coroutines/coro-context-vararg.C | 13 ++ gcc/testsuite/g++.dg/coroutines/coro-missing-gro.C | 32 ++++ .../g++.dg/coroutines/coro-missing-promise-yield.C | 35 ++++ .../g++.dg/coroutines/coro-missing-ret-value.C | 34 ++++ .../g++.dg/coroutines/coro-missing-ret-void.C | 34 ++++ .../g++.dg/coroutines/coro-missing-ueh-1.C | 14 ++ .../g++.dg/coroutines/coro-missing-ueh-2.C | 16 ++ .../g++.dg/coroutines/coro-missing-ueh-3.C | 17 ++ gcc/testsuite/g++.dg/coroutines/coro-missing-ueh.h | 25 +++ gcc/testsuite/g++.dg/coroutines/coro-pre-proc.C | 9 + gcc/testsuite/g++.dg/coroutines/coro.h | 123 ++++++++++++++ gcc/testsuite/g++.dg/coroutines/coroutines.exp | 50 ++++++ .../g++.dg/coroutines/torture/co-await-0-triv.C | 144 ++++++++++++++++ .../g++.dg/coroutines/torture/co-await-1-value.C | 149 ++++++++++++++++ .../g++.dg/coroutines/torture/co-await-2-xform.C | 155 +++++++++++++++++ .../g++.dg/coroutines/torture/co-await-3-rhs-op.C | 155 +++++++++++++++++ .../coroutines/torture/co-await-4-control-flow.C | 148 ++++++++++++++++ .../g++.dg/coroutines/torture/co-await-5-loop.C | 147 ++++++++++++++++ .../g++.dg/coroutines/torture/co-await-6-ovl.C | 153 +++++++++++++++++ .../g++.dg/coroutines/torture/co-await-7-tmpl.C | 149 ++++++++++++++++ .../g++.dg/coroutines/torture/co-await-8-cascade.C | 157 +++++++++++++++++ .../g++.dg/coroutines/torture/co-await-9-pair.C | 155 +++++++++++++++++ gcc/testsuite/g++.dg/coroutines/torture/co-ret-3.C | 112 ++++++++++++ gcc/testsuite/g++.dg/coroutines/torture/co-ret-4.C | 129 ++++++++++++++ gcc/testsuite/g++.dg/coroutines/torture/co-ret-5.C | 122 +++++++++++++ gcc/testsuite/g++.dg/coroutines/torture/co-ret-6.C | 125 ++++++++++++++ gcc/testsuite/g++.dg/coroutines/torture/co-ret-7.C | 114 +++++++++++++ gcc/testsuite/g++.dg/coroutines/torture/co-ret-8.C | 126 ++++++++++++++ gcc/testsuite/g++.dg/coroutines/torture/co-ret-9.C | 118 +++++++++++++ .../coroutines/torture/co-ret-void-is-ready.C | 107 ++++++++++++ .../coroutines/torture/co-ret-void-is-suspend.C | 111 ++++++++++++ .../g++.dg/coroutines/torture/co-yield-0-triv.C | 146 ++++++++++++++++ .../g++.dg/coroutines/torture/co-yield-1-multi.C | 159 +++++++++++++++++ .../g++.dg/coroutines/torture/co-yield-2-loop.C | 153 +++++++++++++++++ .../g++.dg/coroutines/torture/co-yield-3-tmpl.C | 160 +++++++++++++++++ .../g++.dg/coroutines/torture/co-yield-strings.C | 182 ++++++++++++++++++++ .../g++.dg/coroutines/torture/coro-torture.exp | 19 +++ .../g++.dg/coroutines/torture/exceptions-test-0.C | 189 +++++++++++++++++++++ .../g++.dg/coroutines/torture/func-params-0.C | 126 ++++++++++++++ .../g++.dg/coroutines/torture/func-params-1.C | 130 ++++++++++++++ .../g++.dg/coroutines/torture/func-params-2.C | 134 +++++++++++++++ .../g++.dg/coroutines/torture/func-params-3.C | 133 +++++++++++++++ .../g++.dg/coroutines/torture/func-params-4.C | 141 +++++++++++++++ .../g++.dg/coroutines/torture/func-params-5.C | 141 +++++++++++++++ .../coroutines/torture/gro_on_alloc_fail_0.C | 137 +++++++++++++++ .../g++.dg/coroutines/torture/local-var-0.C | 123 ++++++++++++++ .../g++.dg/coroutines/torture/local-var-1.C | 123 ++++++++++++++ .../g++.dg/coroutines/torture/local-var-2.C | 135 +++++++++++++++ .../g++.dg/coroutines/torture/local-var-3.C | 153 +++++++++++++++++ .../g++.dg/coroutines/torture/local-var-4.C | 162 ++++++++++++++++++ .../coroutines/torture/mid-suspend-destruction-0.C | 127 ++++++++++++++ 61 files changed, 5920 insertions(+) create mode 100644 gcc/testsuite/g++.dg/coroutines/co-yield-syntax-1.C create mode 100644 gcc/testsuite/g++.dg/coroutines/co-yield-syntax-2.C create mode 100644 gcc/testsuite/g++.dg/coroutines/co-yield-syntax-3.C create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-auto-fn.C create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-await-context-auto-fn.C create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-bad-return.C create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-builtins.C create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-constexpr-fn.C create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-context-ctor-dtor.C create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-context-main.C create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-context-vararg.C create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-gro.C create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-promise-yield.C create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-ret-value.C create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-ret-void.C create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-1.C create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-2.C create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-3.C create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-ueh.h create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-pre-proc.C create mode 100644 gcc/testsuite/g++.dg/coroutines/coro.h create mode 100644 gcc/testsuite/g++.dg/coroutines/coroutines.exp create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-0-triv.C create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-1-value.C create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-2-xform.C create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-3-rhs-op.C create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-4-control-flow.C create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-5-loop.C create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-6-ovl.C create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-7-tmpl.C create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-8-cascade.C create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-9-pair.C create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-3.C create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-4.C create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-5.C create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-6.C create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-7.C create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-8.C create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-9.C create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-void-is-ready.C create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-void-is-suspend.C create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-0-triv.C create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-1-multi.C create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-2-loop.C create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-3-tmpl.C create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-strings.C create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/coro-torture.exp create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/exceptions-test-0.C create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/func-params-0.C create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/func-params-1.C create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/func-params-2.C create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/func-params-3.C create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/func-params-4.C create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/func-params-5.C create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/gro_on_alloc_fail_0.C create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/local-var-0.C create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/local-var-1.C create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/local-var-2.C create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/local-var-3.C create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/local-var-4.C create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/mid-suspend-destruction-0.C diff --git a/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-1.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-1.C new file mode 100644 index 0000000..30db0e9 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-1.C @@ -0,0 +1,6 @@ +// { dg-additional-options "-fsyntax-only -w" } + +#include "coro.h" + +auto f (int x = co_yield 5); // { dg-error {'co_yield' cannot be used outside a function} } + diff --git a/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-2.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-2.C new file mode 100644 index 0000000..71e119f --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-2.C @@ -0,0 +1,6 @@ +// { dg-additional-options "-fsyntax-only -w" } + +#include "coro.h" + +int a[] = { co_yield 21 }; // { dg-error {'co_yield' cannot be used outside a function} } + diff --git a/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-3.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-3.C new file mode 100644 index 0000000..20a5b56 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-3.C @@ -0,0 +1,37 @@ +// { dg-additional-options "-fsyntax-only -w" } +#include "coro.h" + +namespace coro = std::experimental; + +/* Diagose missing return_void() in the promise type. */ +struct DummyYield { + coro::coroutine_handle<> handle; + DummyYield () : handle (nullptr) {} + DummyYield (coro::coroutine_handle<> handle) : handle (handle) {} + struct dummy_yield { + coro::suspend_never initial_suspend() { return {}; } + coro::suspend_never final_suspend() { return {}; } + DummyYield get_return_object() { + return DummyYield (coro::coroutine_handle::from_promise (*this)); + } + void yield_value (int v) {} + void return_value (int v) {} + void unhandled_exception() { /*std::terminate();*/ }; + }; +}; + +template<> struct coro::coroutine_traits { + using promise_type = DummyYield::dummy_yield; +}; + +DummyYield +bar () +{ + co_yield; // { dg-error {expected primary-expression before} } + co_return 0; +} + +int main (int ac, char *av[]) { + DummyYield x = bar (); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/coro-auto-fn.C b/gcc/testsuite/g++.dg/coroutines/coro-auto-fn.C new file mode 100644 index 0000000..93a04dc --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/coro-auto-fn.C @@ -0,0 +1,12 @@ +// { dg-additional-options "-fsyntax-only -w" } + +#include "coro.h" + +auto bar () { + co_return 5; // { dg-error "cannot be used in a function with a deduced return type" } +} + +int main () { + bar (); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/coro-await-context-auto-fn.C b/gcc/testsuite/g++.dg/coroutines/coro-await-context-auto-fn.C new file mode 100644 index 0000000..7f4ed9a --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/coro-await-context-auto-fn.C @@ -0,0 +1,16 @@ +// { dg-additional-options "-fsyntax-only -w" } + +#include "coro.h" + +extern struct awaitable *aw (); + +auto bar () { + int x = 1 + co_await *aw(); // { dg-error "cannot be used in a function with a deduced return type" } + + return x; +} + +int main () { + bar (); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/coro-bad-return.C b/gcc/testsuite/g++.dg/coroutines/coro-bad-return.C new file mode 100644 index 0000000..e5b848d --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/coro-bad-return.C @@ -0,0 +1,48 @@ +// { dg-additional-options "-fsyntax-only -w" } +#if __clang__ +# include +# include +#else +# include "coro.h" +#endif +namespace coro = std::experimental; + +struct Coro { + struct promise_type; + using handle_type = coro::coroutine_handle; + handle_type handle; + Coro () : handle(0) {} + Coro (handle_type _handle) : handle(_handle) {} + Coro (Coro &&s) : handle(s.handle) { s.handle = nullptr; } + Coro &operator = (Coro &&s) { + handle = s.handle; + s.handle = nullptr; + return *this; + } + Coro (const Coro &) = delete; + ~Coro() { + if ( handle ) + handle.destroy(); + } + struct promise_type { + promise_type() {} + ~promise_type() {} + Coro get_return_object () { return Coro (handle_type::from_promise (*this)); } + auto initial_suspend () { return coro::suspend_always{}; } + auto final_suspend () { return coro::suspend_always{}; } + void return_void () { } + void unhandled_exception() { } + }; +}; + +extern int x; + +// Diagnose disallowed "return" in coroutine. +Coro +bar () // { dg-error "return statement not allowed" } +{ + if (x) + return Coro(); + else + co_return; +} diff --git a/gcc/testsuite/g++.dg/coroutines/coro-builtins.C b/gcc/testsuite/g++.dg/coroutines/coro-builtins.C new file mode 100644 index 0000000..d7c4883 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/coro-builtins.C @@ -0,0 +1,17 @@ +// { dg-additional-options "-fsyntax-only " } + +typedef __SIZE_TYPE__ size_t; + +int main () +{ + void *co_h; + void *promise; + const size_t co_align = 16; + + bool d = __builtin_coro_done (co_h); + __builtin_coro_resume (co_h); + __builtin_coro_destroy (co_h); + promise = __builtin_coro_promise (co_h, co_align, true); + + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/coro-constexpr-fn.C b/gcc/testsuite/g++.dg/coroutines/coro-constexpr-fn.C new file mode 100644 index 0000000..69b109f --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/coro-constexpr-fn.C @@ -0,0 +1,12 @@ +// { dg-additional-options "-fsyntax-only -w" } + +#include "coro.h" + +constexpr int bar () { + co_return 5; // { dg-error "cannot be used in a .constexpr. function" } + return 42; /* Suppress the "no return" error. */ +} + +int main () { + return bar (); +} diff --git a/gcc/testsuite/g++.dg/coroutines/coro-context-ctor-dtor.C b/gcc/testsuite/g++.dg/coroutines/coro-context-ctor-dtor.C new file mode 100644 index 0000000..9396432 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/coro-context-ctor-dtor.C @@ -0,0 +1,8 @@ +// { dg-additional-options "-fsyntax-only -w" } + +#include "coro.h" + +struct Foo { + Foo () { co_return; } // { dg-error "cannot be used in a constructor" } + ~Foo () { co_return 5; } // { dg-error "cannot be used in a destructor" } +}; diff --git a/gcc/testsuite/g++.dg/coroutines/coro-context-main.C b/gcc/testsuite/g++.dg/coroutines/coro-context-main.C new file mode 100644 index 0000000..40d7e4e --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/coro-context-main.C @@ -0,0 +1,7 @@ +// { dg-additional-options "-fsyntax-only -w" } + +#include "coro.h" + +int main (int ac, char *av[]) { + co_return 0; // { dg-error "cannot be used in the .main. function" } +} diff --git a/gcc/testsuite/g++.dg/coroutines/coro-context-vararg.C b/gcc/testsuite/g++.dg/coroutines/coro-context-vararg.C new file mode 100644 index 0000000..55a0295 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/coro-context-vararg.C @@ -0,0 +1,13 @@ +// { dg-additional-options "-fsyntax-only -w" } +#include "coro.h" + +int +bar (int x, ...) +{ + co_return 1; // { dg-error "cannot be used in a varargs function" } +} + +int main (int ac, char *av[]) { + bar (5, ac); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-gro.C b/gcc/testsuite/g++.dg/coroutines/coro-missing-gro.C new file mode 100644 index 0000000..8bedb77 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-gro.C @@ -0,0 +1,32 @@ +// { dg-additional-options "-fsyntax-only -w" } +#include "coro.h" + +namespace coro = std::experimental; + +/* Diagose missing return_void() in the promise type. */ +struct MissingGRO { + coro::coroutine_handle<> handle; + MissingGRO () : handle (nullptr) {} + MissingGRO (coro::coroutine_handle<> handle) : handle (handle) {} + struct missing_gro { + coro::suspend_never initial_suspend() { return {}; } + coro::suspend_never final_suspend() { return {}; } + void return_void () {} + void unhandled_exception() { /*std::terminate();*/ }; + }; +}; + +template<> struct coro::coroutine_traits { + using promise_type = MissingGRO::missing_gro; +}; + +MissingGRO +bar () // { dg-error {no member named 'get_return_object' in} } +{ + co_return; +} + +int main (int ac, char *av[]) { + MissingGRO x = bar (); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-promise-yield.C b/gcc/testsuite/g++.dg/coroutines/coro-missing-promise-yield.C new file mode 100644 index 0000000..95f567f --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-promise-yield.C @@ -0,0 +1,35 @@ +// { dg-additional-options "-fsyntax-only -w" } +#include "coro.h" + +namespace coro = std::experimental; + +struct MissingPromiseYield { + coro::coroutine_handle<> handle; + MissingPromiseYield () : handle (nullptr) {} + MissingPromiseYield (coro::coroutine_handle<> handle) : handle (handle) {} + struct missing_yield { + coro::suspend_never initial_suspend() { return {}; } + coro::suspend_never final_suspend() { return {}; } + MissingPromiseYield get_return_object() { + return MissingPromiseYield (coro::coroutine_handle::from_promise (*this)); + } + void return_value (int v) {} + void unhandled_exception() { /*std::terminate();*/ }; + }; +}; + +template<> struct coro::coroutine_traits { + using promise_type = MissingPromiseYield::missing_yield; +}; + +MissingPromiseYield +bar () +{ + co_yield 22; // { dg-error {no member named 'yield_value' in} } + co_return 0; +} + +int main (int ac, char *av[]) { + MissingPromiseYield x = bar (); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-ret-value.C b/gcc/testsuite/g++.dg/coroutines/coro-missing-ret-value.C new file mode 100644 index 0000000..fd81ce7 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-ret-value.C @@ -0,0 +1,34 @@ +// { dg-additional-options "-fsyntax-only -w" } +#include "coro.h" + +namespace coro = std::experimental; + +/* Diagose missing return_void() in the promise type. */ +struct MissingRetValue { + coro::coroutine_handle<> handle; + MissingRetValue () : handle (nullptr) {} + MissingRetValue (coro::coroutine_handle<> handle) : handle (handle) {} + struct missing_retvoid { + coro::suspend_never initial_suspend() { return {}; } + coro::suspend_never final_suspend() { return {}; } + MissingRetValue get_return_object() { + return MissingRetValue (coro::coroutine_handle::from_promise (*this)); + } + void unhandled_exception() { /*std::terminate();*/ }; + }; +}; + +template<> struct coro::coroutine_traits { + using promise_type = MissingRetValue::missing_retvoid; +}; + +MissingRetValue +bar () +{ + co_return 6174; // { dg-error {no member named 'return_value' in} } +} + +int main (int ac, char *av[]) { + MissingRetValue x = bar (); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-ret-void.C b/gcc/testsuite/g++.dg/coroutines/coro-missing-ret-void.C new file mode 100644 index 0000000..17a2180 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-ret-void.C @@ -0,0 +1,34 @@ +// { dg-additional-options "-fsyntax-only -w" } +#include "coro.h" + +namespace coro = std::experimental; + +/* Diagose missing return_void() in the promise type. */ +struct MissingRetVoid { + coro::coroutine_handle<> handle; + MissingRetVoid () : handle (nullptr) {} + MissingRetVoid (coro::coroutine_handle<> handle) : handle (handle) {} + struct missing_retvoid { + coro::suspend_never initial_suspend() { return {}; } + coro::suspend_never final_suspend() { return {}; } + MissingRetVoid get_return_object() { + return MissingRetVoid (coro::coroutine_handle::from_promise (*this)); + } + void unhandled_exception() { /*std::terminate();*/ }; + }; +}; + +template<> struct coro::coroutine_traits { + using promise_type = MissingRetVoid::missing_retvoid; +}; + +MissingRetVoid +bar () +{ + co_return; // { dg-error "no member named .return_void. in" } +} + +int main (int ac, char *av[]) { + MissingRetVoid x = bar (); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-1.C b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-1.C new file mode 100644 index 0000000..964b9f1 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-1.C @@ -0,0 +1,14 @@ +// { dg-additional-options "-fsyntax-only -fexceptions -w" } +#include "coro.h" +#include "coro-missing-ueh.h" + +MissingUEH +bar () // { dg-error {no member named 'unhandled_exception' in} } +{ + co_return; +} + +int main (int ac, char *av[]) { + MissingUEH x = bar (); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-2.C b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-2.C new file mode 100644 index 0000000..caf8fb9 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-2.C @@ -0,0 +1,16 @@ +// { dg-additional-options "-fsyntax-only -fno-exceptions " } +#include "coro.h" +#include "coro-missing-ueh.h" + +// The missing method is warned for when exceptions are off and pedantic +// is on (default in the testsuite). +MissingUEH +bar () // { dg-warning {no member named 'unhandled_exception' in} } +{ + co_return; +} + +int main (int ac, char *av[]) { + MissingUEH x = bar (); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-3.C b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-3.C new file mode 100644 index 0000000..f6ca196 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-3.C @@ -0,0 +1,17 @@ +// { dg-additional-options "-fsyntax-only -fno-exceptions -Wno-pedantic" } +#include "coro.h" +#include "coro-missing-ueh.h" + +/* We don't warn about the missing method, unless in pedantic mode, so + this compile should be clean. */ + +MissingUEH +bar () +{ + co_return; +} + +int main (int ac, char *av[]) { + MissingUEH x = bar (); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh.h b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh.h new file mode 100644 index 0000000..7a32354 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh.h @@ -0,0 +1,25 @@ +#ifndef __MissingUEH_H +#define __MissingUEH_H + +namespace coro = std::experimental; + +/* Diagose missing unhandled_exception() in the promise type. */ +struct MissingUEH { + coro::coroutine_handle<> handle; + MissingUEH () : handle (nullptr) {} + MissingUEH (coro::coroutine_handle<> handle) : handle (handle) {} + struct missing_ueh { + coro::suspend_never initial_suspend() { return {}; } + coro::suspend_never final_suspend() { return {}; } + MissingUEH get_return_object() { + return MissingUEH (coro::coroutine_handle::from_promise (*this)); + } + void return_void () {} + }; +}; + +template<> struct coro::coroutine_traits { + using promise_type = MissingUEH::missing_ueh; +}; + +#endif diff --git a/gcc/testsuite/g++.dg/coroutines/coro-pre-proc.C b/gcc/testsuite/g++.dg/coroutines/coro-pre-proc.C new file mode 100644 index 0000000..f22a5e0 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/coro-pre-proc.C @@ -0,0 +1,9 @@ +// Only need to compile this, with the default options from the .exp. + +#ifndef __cpp_coroutines +#error "coroutines should engaged." +#endif + +#if __cpp_coroutines != 201902L +#error "coroutine version out of sync." +#endif diff --git a/gcc/testsuite/g++.dg/coroutines/coro.h b/gcc/testsuite/g++.dg/coroutines/coro.h new file mode 100644 index 0000000..64df497 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/coro.h @@ -0,0 +1,123 @@ +#if __has_include() + +#include + +#else +#ifndef __CORO_H_n4835 +#define __CORO_H_n4835 + +// Fragments (with short-cuts) to mimic enough of the library header to +// make some progress. + +#if __cpp_coroutines + +namespace std { +namespace experimental { +inline namespace coroutines_n4835 { + +// 21.11.1 coroutine traits +template struct coroutine_traits { + using promise_type = typename _R::promise_type; +}; + +// 21.11.2 coroutine handle +template struct coroutine_handle; + +template <> struct coroutine_handle { + public: + // 21.11.2.1 construct/reset + constexpr coroutine_handle () noexcept + : __fr_ptr (0) {} + constexpr coroutine_handle (decltype(nullptr) __h) noexcept + : __fr_ptr (__h) {} + coroutine_handle &operator= (decltype(nullptr)) noexcept { + __fr_ptr = nullptr; + return *this; + } + + public: + // 21.11.2.2 export/import + constexpr void *address () const noexcept { return __fr_ptr; } + constexpr static coroutine_handle from_address (void *__a) noexcept { + coroutine_handle __self; + __self.__fr_ptr = __a; + return __self; + } + public: + // 21.11.2.3 observers + constexpr explicit operator bool () const noexcept { + return bool (__fr_ptr); + } + bool done () const noexcept { + return __builtin_coro_done (__fr_ptr); + } + // 21.11.2.4 resumption + void operator () () const { resume (); } + void resume () const { + __builtin_coro_resume (__fr_ptr); + } + void destroy () const { + __builtin_coro_destroy (__fr_ptr); + } + bool suspended_p () const { + return __builtin_coro_is_suspended (__fr_ptr); + } + protected: + void *__fr_ptr; + + private: + bool __is_suspended() const noexcept { + return __builtin_coro_is_suspended (__fr_ptr); + } +}; + +template +struct coroutine_handle : coroutine_handle<> { + // 21.11.2.1 construct/reset + using coroutine_handle<>::coroutine_handle; + static coroutine_handle from_promise(_Promise &p) { + coroutine_handle __self; + __self.__fr_ptr = + __builtin_coro_promise((char *)&p, __alignof(_Promise), true); + return __self; + } + coroutine_handle& operator=(decltype(nullptr)) noexcept { + coroutine_handle<>::operator=(nullptr); + return *this; + } + // 21.11.2.2 export/import + constexpr static coroutine_handle from_address(void* __a){ + coroutine_handle __self; + __self.__fr_ptr = __a; + return __self; + } + // 21.11.2.5 promise access + _Promise& promise() const { + void * __t = __builtin_coro_promise(this->__fr_ptr, + __alignof(_Promise), false); + return *static_cast<_Promise*>(__t); + } +}; + +// n4760 - 21.11.5 trivial awaitables + +struct suspend_always { + bool await_ready() { return false; } + void await_suspend(coroutine_handle<>) {} + void await_resume() {} +}; + +struct suspend_never { + bool await_ready() { return true; } + void await_suspend(coroutine_handle<>) {} + void await_resume() {} +}; + +}}} // namespace std::experimental::coroutines_n4775 + +#else +# error "coro.h requires support for coroutines, add -fcoroutines" +#endif +#endif // __CORO_H_n4835 + +#endif diff --git a/gcc/testsuite/g++.dg/coroutines/coroutines.exp b/gcc/testsuite/g++.dg/coroutines/coroutines.exp new file mode 100644 index 0000000..0696cc8 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/coroutines.exp @@ -0,0 +1,50 @@ +# Copyright (C) 2018-2019 Free Software Foundation, Inc. + +# Contributed by Iain Sandoe 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 +# . + +# Test C++ coroutines, requires c++17; doesn't, at present, seem much +# point in repeating these for other versions. + +# Load support procs. +load_lib g++-dg.exp + +# If a testcase doesn't have special options, use these. +global DEFAULT_CXXFLAGS +if ![info exists DEFAULT_CXXFLAGS] then { + set DEFAULT_CXXFLAGS " -pedantic-errors -Wno-long-long" +} + +set DEFAULT_COROFLAGS $DEFAULT_CXXFLAGS +lappend DEFAULT_COROFLAGS "-std=c++17" "-fcoroutines" + +dg-init + +# Run the tests. +# g++-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.C]] \ +# "" $DEFAULT_COROFLAGS + +foreach test [lsort [find $srcdir/$subdir {*.[CH]}]] { + if [runtest_file_p $runtests $test] { + set nshort [file tail [file dirname $test]]/[file tail $test] + verbose "Testing $nshort $DEFAULT_COROFLAGS" 1 + dg-test $test "" $DEFAULT_COROFLAGS + set testcase [string range $test [string length "$srcdir/"] end] + } +} + +# done. +dg-finish diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-0-triv.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-0-triv.C new file mode 100644 index 0000000..000d083 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-0-triv.C @@ -0,0 +1,144 @@ +// { dg-do run } +#if __clang__ +# include +#else +# include "../coro.h" +#endif + +namespace coro = std::experimental; + +/* just to avoid cluttering dump files. */ +extern "C" int puts (const char *); +extern "C" int printf (const char *, ...); +extern "C" void abort (void) __attribute__((__noreturn__)); + +#ifndef OUTPUT +# define PRINT(X) +# define PRINTF (void) +#else +# define PRINT(X) puts(X) +# define PRINTF printf +#endif + +struct coro1 { + struct promise_type; + using handle_type = coro::coroutine_handle; + handle_type handle; + coro1 () : handle(0) {} + coro1 (handle_type _handle) + : handle(_handle) { + puts("Created coro1 object from handle"); + } + coro1 (const coro1 &) = delete; // no copying + coro1 (coro1 &&s) : handle(s.handle) { + s.handle = nullptr; + puts("Moved coro1"); + } + coro1 &operator = (coro1 &&s) { + handle = s.handle; + s.handle = nullptr; + return *this; + } + ~coro1() { + puts("Destroyed coro1"); + if ( handle ) + handle.destroy(); + } + + struct suspend_never_prt { + ~suspend_never_prt() {} + bool await_ready() const noexcept { return true; } + void await_suspend(handle_type h) const noexcept { PRINT ("susp-never-susp");} + void await_resume() const noexcept {PRINT ("susp-never-resume");} + }; + + struct suspend_always_prt { + int x; + bool await_ready() const noexcept { return false; } + void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp");} + void await_resume() const noexcept {PRINT ("susp-always-resume");} + }; + + /* This returns an int. */ + struct suspend_always_intprt { + int x; + suspend_always_intprt() : x(5) {} + ~suspend_always_intprt() {} + bool await_ready() const noexcept { return false; } + void await_suspend(coro::coroutine_handle<>) const noexcept { puts ("susp-always-susp-int");} + int await_resume() const noexcept {puts ("susp-always-resume-int"); return x;} + }; + + struct promise_type { + int value; + promise_type() { PRINT ("Created Promise"); } + ~promise_type() { PRINT ("Destroyed Promise"); } + + coro1 get_return_object() { + PRINT ("get_return_object: from handle from promise"); + return coro1 (handle_type::from_promise (*this)); + } + + auto initial_suspend() { + PRINT ("get initial_suspend "); + return suspend_never_prt{}; + } + + auto final_suspend() { + PRINT ("get final_suspend"); + return suspend_always_prt{}; + } + + void return_value (int v) { + PRINTF ("return_value () %d\n",v); + value = v; + } + + int get_value () { return value; } + // Placeholder to satisfy parser, not doing exceptions yet. + void unhandled_exception() { /*exit(1);*/ } + }; +}; + +/* The simplest valued co_await we can do. */ +int gX = 1; + +coro1 +f () +{ + co_await coro1::suspend_always_prt{}; + co_return gX + 10; +} + +int main () +{ + PRINT ("main: create coro1"); + struct coro1 f_coro = f (); + PRINT ("main: got coro1 - checking gX"); + if (gX != 1) + { + PRINTF ("main: gX is wrong : %d, should be 1\n", gX); + abort (); + } + if (f_coro.handle.done()) + { + PRINT ("main: we should not be 'done' [1]"); + abort (); + } + PRINT ("main: resuming [1]"); + f_coro.handle.resume(); + /* we should now have returned with the co_return (15) */ + if (!f_coro.handle.done()) + { + PRINT ("main: we should be 'done' "); + abort (); + } + int y = f_coro.handle.promise().get_value(); + if (y != 11) + { + PRINTF ("main: y is wrong : %d, should be 11\n", y); + abort (); + } + puts ("main: done"); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-1-value.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-1-value.C new file mode 100644 index 0000000..cdc7b92 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-1-value.C @@ -0,0 +1,149 @@ +// { dg-do run } +#if __clang__ +# include +#else +# include "../coro.h" +#endif + +namespace coro = std::experimental; + +/* just to avoid cluttering dump files. */ +extern "C" int puts (const char *); +extern "C" int printf (const char *, ...); +extern "C" void abort (void) __attribute__((__noreturn__)); + +#ifndef OUTPUT +# define PRINT(X) +# define PRINTF (void) +#else +# define PRINT(X) puts(X) +# define PRINTF printf +#endif + +struct coro1 { + struct promise_type; + using handle_type = coro::coroutine_handle; + handle_type handle; + coro1 () : handle(0) {} + coro1 (handle_type _handle) + : handle(_handle) { + puts("Created coro1 object from handle"); + } + coro1 (const coro1 &) = delete; // no copying + coro1 (coro1 &&s) : handle(s.handle) { + s.handle = nullptr; + PRINT("Moved coro1"); + } + coro1 &operator = (coro1 &&s) { + handle = s.handle; + s.handle = nullptr; + return *this; + } + ~coro1() { + PRINT("Destroyed coro1"); + if ( handle ) + handle.destroy(); + } + + struct suspend_never_prt { + ~suspend_never_prt() {} + bool await_ready() const noexcept { return true; } + void await_suspend(handle_type h) const noexcept { PRINT ("susp-never-susp");} + void await_resume() const noexcept {PRINT ("susp-never-resume");} + }; + + struct suspend_always_prt { + int x; + bool await_ready() const noexcept { return false; } + void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp");} + void await_resume() const noexcept {PRINT ("susp-always-resume");} + }; + + /* This returns an int. */ + struct suspend_always_intprt { + int x; + suspend_always_intprt() : x(5) {} + ~suspend_always_intprt() {} + bool await_ready() const noexcept { return false; } + void await_suspend(coro::coroutine_handle<>) const noexcept { puts ("susp-always-susp-int");} + int await_resume() const noexcept {puts ("susp-always-resume-int"); return x;} + }; + + struct promise_type { + int value; + promise_type() { PRINT ("Created Promise"); } + ~promise_type() { PRINT ("Destroyed Promise"); } + + coro1 get_return_object() { + PRINT ("get_return_object: from handle from promise"); + return coro1 (handle_type::from_promise (*this)); + } + + auto initial_suspend() { + PRINT ("get initial_suspend "); + return suspend_never_prt{}; + } + + auto final_suspend() { + PRINT ("get final_suspend"); + return suspend_always_prt{}; + } + + void return_value (int v) { + PRINTF ("return_value () %d\n",v); + value = v; + } + + int get_value () { return value; } + // Placeholder to satisfy parser, not doing exceptions yet. + void unhandled_exception() { /*exit(1);*/ } + }; +}; + +/* The simplest valued co_await we can do. */ +int gX = 1; + +coro1 +f () +{ + gX = co_await coro1::suspend_always_intprt{}; + co_return gX + 10; +} + +int main () +{ + PRINT ("main: create coro1"); + struct coro1 f_coro = f (); + PRINT ("main: got coro1 - checking gX"); + if (gX != 1) + { + PRINTF ("main: gX is wrong : %d, should be 1\n", gX); + abort (); + } + if (f_coro.handle.done()) + { + PRINT ("main: we should not be 'done' [1]"); + abort (); + } + PRINT ("main: resuming [1]"); + f_coro.handle.resume(); + if (gX != 5) + { + PRINTF ("main: gX is wrong : %d, should be 5\n", gX); + abort (); + } + /* we should now have returned with the co_return (15) */ + if (!f_coro.handle.done()) + { + PRINT ("main: we should be 'done' "); + abort (); + } + int y = f_coro.handle.promise().get_value(); + if (y != 15) + { + PRINTF ("main: y is wrong : %d, should be 15\n", y); + abort (); + } + puts ("main: done"); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-2-xform.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-2-xform.C new file mode 100644 index 0000000..3b1bb71 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-2-xform.C @@ -0,0 +1,155 @@ +// { dg-do run } +#if __clang__ +# include +#else +# include "../coro.h" +#endif + +namespace coro = std::experimental; + +/* just to avoid cluttering dump files. */ +extern "C" int puts (const char *); +extern "C" int printf (const char *, ...); +extern "C" void abort (void) __attribute__((__noreturn__)); + +#ifndef OUTPUT +# define PRINT(X) +# define PRINTF (void) +#else +# define PRINT(X) puts(X) +# define PRINTF printf +#endif + +struct coro1 { + struct promise_type; + using handle_type = coro::coroutine_handle; + handle_type handle; + coro1 () : handle(0) {} + coro1 (handle_type _handle) + : handle(_handle) { + PRINT ("Created coro1 object from handle"); + } + coro1 (const coro1 &) = delete; // no copying + coro1 (coro1 &&s) : handle(s.handle) { + s.handle = nullptr; + PRINT ("Moved coro1"); + } + coro1 &operator = (coro1 &&s) { + handle = s.handle; + s.handle = nullptr; + return *this; + } + ~coro1() { + PRINT ("Destroyed coro1"); + if ( handle ) + handle.destroy(); + } + + struct suspend_never_prt { + ~suspend_never_prt() {} + bool await_ready() const noexcept { return true; } + void await_suspend(handle_type h) const noexcept { PRINT ("susp-never-susp");} + void await_resume() const noexcept {PRINT ("susp-never-resume");} + }; + + struct suspend_always_prt { + int x; + bool await_ready() const noexcept { return false; } + void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp");} + void await_resume() const noexcept {PRINT ("susp-always-resume");} + }; + + /* This returns an int. */ + struct suspend_always_intprt { + int x; + suspend_always_intprt() : x(5) { PRINT ("suspend_always_intprt def ctor"); } + suspend_always_intprt(int _x) : x(_x) { PRINTF ("suspend_always_intprt ctor with %d\n", x); } + ~suspend_always_intprt() {} + bool await_ready() const noexcept { return false; } + void await_suspend(coro::coroutine_handle<>) const noexcept { PRINT ("susp-always-susp-int");} + int await_resume() const noexcept { PRINT ("susp-always-resume-int"); return x;} + }; + + struct promise_type { + int value; + promise_type() { PRINT ("Created Promise"); } + ~promise_type() { PRINT ("Destroyed Promise"); } + + coro1 get_return_object() { + PRINT ("get_return_object: from handle from promise"); + return coro1 (handle_type::from_promise (*this)); + } + + auto initial_suspend() { + PRINT ("get initial_suspend "); + return suspend_never_prt{}; + } + + auto final_suspend() { + PRINT ("get final_suspend"); + return suspend_always_prt{}; + } + + void return_value (int v) { + PRINTF ("return_value () %d\n",v); + value = v; + } + + auto await_transform (int v) { + PRINTF ("await_transform an int () %d\n",v); + return suspend_always_intprt (v); + } + + int get_value () { return value; } + // Placeholder to satisfy parser, not doing exceptions yet. + void unhandled_exception() { /*exit(1);*/ } + }; +}; + +/* Valued with an await_transform. */ +int gX = 1; + +coro1 +f () +{ + gX = co_await 11; + co_return gX + 31; +} + +int main () +{ + PRINT ("main: create coro1"); + struct coro1 f_coro = f (); + PRINT ("main: got coro1 - checking gX"); + if (gX != 1) + { + PRINTF ("main: gX is wrong : %d, should be 1\n", gX); + abort (); + } + if (f_coro.handle.done()) + { + PRINT ("main: we should not be 'done' [1]"); + abort (); + } + PRINT ("main: resuming [1]"); + f_coro.handle.resume(); + if (gX != 11) + { + PRINTF ("main: gX is wrong : %d, should be 11\n", gX); + abort (); + } + /* we should now have returned with the co_return (15) */ + if (!f_coro.handle.done()) + { + PRINT ("main: we should be 'done' "); + abort (); + } + int y = f_coro.handle.promise().get_value(); + if (y != 42) + { + PRINTF ("main: y is wrong : %d, should be 42\n", y); + abort (); + } + puts ("main: done"); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-3-rhs-op.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-3-rhs-op.C new file mode 100644 index 0000000..1fb54f9 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-3-rhs-op.C @@ -0,0 +1,155 @@ +// { dg-do run } +#if __clang__ +# include +#else +# include "../coro.h" +#endif + +namespace coro = std::experimental; + +/* just to avoid cluttering dump files. */ +extern "C" int puts (const char *); +extern "C" int printf (const char *, ...); +extern "C" void abort (void) __attribute__((__noreturn__)); + +#ifndef OUTPUT +# define PRINT(X) +# define PRINTF (void) +#else +# define PRINT(X) puts(X) +# define PRINTF printf +#endif + +struct coro1 { + struct promise_type; + using handle_type = coro::coroutine_handle; + handle_type handle; + coro1 () : handle(0) {} + coro1 (handle_type _handle) + : handle(_handle) { + PRINT ("Created coro1 object from handle"); + } + coro1 (const coro1 &) = delete; // no copying + coro1 (coro1 &&s) : handle(s.handle) { + s.handle = nullptr; + PRINT ("Moved coro1"); + } + coro1 &operator = (coro1 &&s) { + handle = s.handle; + s.handle = nullptr; + return *this; + } + ~coro1() { + PRINT ("Destroyed coro1"); + if ( handle ) + handle.destroy(); + } + + struct suspend_never_prt { + ~suspend_never_prt() {} + bool await_ready() const noexcept { return true; } + void await_suspend(handle_type h) const noexcept { PRINT ("susp-never-susp");} + void await_resume() const noexcept {PRINT ("susp-never-resume");} + }; + + struct suspend_always_prt { + int x; + bool await_ready() const noexcept { return false; } + void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp");} + void await_resume() const noexcept {PRINT ("susp-always-resume");} + }; + + /* This returns an int. */ + struct suspend_always_intprt { + int x; + suspend_always_intprt() : x(5) { PRINT ("suspend_always_intprt def ctor"); } + suspend_always_intprt(int _x) : x(_x) { PRINTF ("suspend_always_intprt ctor with %d\n", x); } + ~suspend_always_intprt() {} + bool await_ready() const noexcept { return false; } + void await_suspend(coro::coroutine_handle<>) const noexcept { PRINT ("susp-always-susp-int");} + int await_resume() const noexcept { PRINT ("susp-always-resume-int"); return x;} + }; + + struct promise_type { + int value; + promise_type() { PRINT ("Created Promise"); } + ~promise_type() { PRINT ("Destroyed Promise"); } + + coro1 get_return_object() { + PRINT ("get_return_object: from handle from promise"); + return coro1 (handle_type::from_promise (*this)); + } + + auto initial_suspend() { + PRINT ("get initial_suspend "); + return suspend_never_prt{}; + } + + auto final_suspend() { + PRINT ("get final_suspend"); + return suspend_always_prt{}; + } + + void return_value (int v) { + PRINTF ("return_value () %d\n",v); + value = v; + } + + auto await_transform (int v) { + PRINTF ("await_transform an int () %d\n",v); + return suspend_always_intprt (v); + } + + int get_value () { return value; } + // Placeholder to satisfy parser, not doing exceptions yet. + void unhandled_exception() { /*exit(1);*/ } + }; +}; + +/* Valued with an await_transform. */ +int gX = 1; + +coro1 +f () +{ + gX = co_await 11 + 15; + co_return gX + 31; +} + +int main () +{ + PRINT ("main: create coro1"); + struct coro1 f_coro = f (); + PRINT ("main: got coro1 - checking gX"); + if (gX != 1) + { + PRINTF ("main: gX is wrong : %d, should be 1\n", gX); + abort (); + } + if (f_coro.handle.done()) + { + PRINT ("main: we should not be 'done' [1]"); + abort (); + } + PRINT ("main: resuming [1]"); + f_coro.handle.resume(); + if (gX != 26) + { + PRINTF ("main: gX is wrong : %d, should be 26\n", gX); + abort (); + } + /* we should now have returned with the co_return (15) */ + if (!f_coro.handle.done()) + { + PRINT ("main: we should be 'done' "); + abort (); + } + int y = f_coro.handle.promise().get_value(); + if (y != 57) + { + PRINTF ("main: y is wrong : %d, should be 57\n", y); + abort (); + } + puts ("main: done"); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-4-control-flow.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-4-control-flow.C new file mode 100644 index 0000000..594dfe5 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-4-control-flow.C @@ -0,0 +1,148 @@ +// { dg-do run } +#if __clang__ +# include +#else +# include "../coro.h" +#endif + +namespace coro = std::experimental; + +/* just to avoid cluttering dump files. */ +extern "C" int puts (const char *); +extern "C" int printf (const char *, ...); +extern "C" void abort (void) __attribute__((__noreturn__)); + +#ifndef OUTPUT +# define PRINT(X) +# define PRINTF (void) +#else +# define PRINT(X) puts(X) +# define PRINTF printf +#endif + +struct coro1 { + struct promise_type; + using handle_type = coro::coroutine_handle; + handle_type handle; + coro1 () : handle(0) {} + coro1 (handle_type _handle) + : handle(_handle) { + PRINT ("Created coro1 object from handle"); + } + coro1 (const coro1 &) = delete; // no copying + coro1 (coro1 &&s) : handle(s.handle) { + s.handle = nullptr; + PRINT ("Moved coro1"); + } + coro1 &operator = (coro1 &&s) { + handle = s.handle; + s.handle = nullptr; + return *this; + } + ~coro1() { + PRINT ("Destroyed coro1"); + if ( handle ) + handle.destroy(); + } + + struct suspend_never_prt { + ~suspend_never_prt() {} + bool await_ready() const noexcept { return true; } + void await_suspend(handle_type h) const noexcept { PRINT ("susp-never-susp");} + void await_resume() const noexcept {PRINT ("susp-never-resume");} + }; + + struct suspend_always_prt { + int x; + bool await_ready() const noexcept { return false; } + void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp");} + void await_resume() const noexcept {PRINT ("susp-always-resume");} + }; + + /* This returns an int. */ + struct suspend_always_intprt { + int x; + suspend_always_intprt() : x(5) { PRINT ("suspend_always_intprt def ctor"); } + suspend_always_intprt(int _x) : x(_x) { PRINTF ("suspend_always_intprt ctor with %d\n", x); } + ~suspend_always_intprt() {} + bool await_ready() const noexcept { return false; } + void await_suspend(coro::coroutine_handle<>) const noexcept { PRINT ("susp-always-susp-int");} + int await_resume() const noexcept { PRINT ("susp-always-resume-int"); return x;} + }; + + struct promise_type { + int value; + promise_type() { PRINT ("Created Promise"); } + ~promise_type() { PRINT ("Destroyed Promise"); } + + coro1 get_return_object() { + PRINT ("get_return_object: from handle from promise"); + return coro1 (handle_type::from_promise (*this)); + } + + auto initial_suspend() { + PRINT ("get initial_suspend "); + return suspend_never_prt{}; + } + + auto final_suspend() { + PRINT ("get final_suspend"); + return suspend_always_prt{}; + } + + void return_value (int v) { + PRINTF ("return_value () %d\n",v); + value = v; + } + + auto await_transform (int v) { + PRINTF ("await_transform an int () %d\n",v); + return suspend_always_intprt (v); + } + + int get_value () { return value; } + // Placeholder to satisfy parser, not doing exceptions yet. + void unhandled_exception() { /*exit(1);*/ } + }; +}; + +/* Valued with an await_transform. */ +int gX = 1; +int y = 30; + +coro1 +f () +{ + if (gX < 12) { + gX += y; + gX += co_await 11; + } else + gX += co_await 12; + + co_return gX; +} + +int main () +{ + PRINT ("main: create coro1"); + struct coro1 f_coro = f (); + PRINT ("main: got coro1 - checking gX"); + if (gX != 1) + { + PRINTF ("main: gX is wrong : %d, should be 1\n", gX); + // abort (); + } + PRINT ("main: gX OK -- looping"); + do { + //PRINTF ("main: gX : %d \n", gX); + f_coro.handle.resume(); + } while (!f_coro.handle.done()); + int y = f_coro.handle.promise().get_value(); + if (y != 42) + { + PRINTF ("main: y is wrong : %d, should be 42\n", y); + //abort (); + } + puts ("main: done"); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-5-loop.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-5-loop.C new file mode 100644 index 0000000..86223be --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-5-loop.C @@ -0,0 +1,147 @@ +// { dg-do run } +#if __clang__ +# include +#else +# include "../coro.h" +#endif + +namespace coro = std::experimental; + +/* just to avoid cluttering dump files. */ +extern "C" int puts (const char *); +extern "C" int printf (const char *, ...); +extern "C" void abort (void) __attribute__((__noreturn__)); + +#ifndef OUTPUT +# define PRINT(X) +# define PRINTF (void) +#else +# define PRINT(X) puts(X) +# define PRINTF printf +#endif + +struct coro1 { + struct promise_type; + using handle_type = coro::coroutine_handle; + handle_type handle; + coro1 () : handle(0) {} + coro1 (handle_type _handle) + : handle(_handle) { + PRINT ("Created coro1 object from handle"); + } + coro1 (const coro1 &) = delete; // no copying + coro1 (coro1 &&s) : handle(s.handle) { + s.handle = nullptr; + PRINT ("Moved coro1"); + } + coro1 &operator = (coro1 &&s) { + handle = s.handle; + s.handle = nullptr; + return *this; + } + ~coro1() { + PRINT ("Destroyed coro1"); + if ( handle ) + handle.destroy(); + } + + struct suspend_never_prt { + ~suspend_never_prt() {} + bool await_ready() const noexcept { return true; } + void await_suspend(handle_type h) const noexcept { PRINT ("susp-never-susp");} + void await_resume() const noexcept {PRINT ("susp-never-resume");} + }; + + struct suspend_always_prt { + int x; + bool await_ready() const noexcept { return false; } + void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp");} + void await_resume() const noexcept {PRINT ("susp-always-resume");} + }; + + /* This returns an int. */ + struct suspend_always_intprt { + int x; + suspend_always_intprt() : x(5) { PRINT ("suspend_always_intprt def ctor"); } + suspend_always_intprt(int _x) : x(_x) { PRINTF ("suspend_always_intprt ctor with %d\n", x); } + ~suspend_always_intprt() {} + bool await_ready() const noexcept { return false; } + void await_suspend(coro::coroutine_handle<>) const noexcept { PRINT ("susp-always-susp-int");} + int await_resume() const noexcept { PRINT ("susp-always-resume-int"); return x;} + }; + + struct promise_type { + int value; + promise_type() { PRINT ("Created Promise"); } + ~promise_type() { PRINT ("Destroyed Promise"); } + + coro1 get_return_object() { + PRINT ("get_return_object: from handle from promise"); + return coro1 (handle_type::from_promise (*this)); + } + + auto initial_suspend() { + PRINT ("get initial_suspend "); + return suspend_never_prt{}; + } + + auto final_suspend() { + PRINT ("get final_suspend"); + return suspend_always_prt{}; + } + + void return_value (int v) { + PRINTF ("return_value () %d\n",v); + value = v; + } + + auto await_transform (int v) { + PRINTF ("await_transform an int () %d\n",v); + return suspend_always_intprt (v); + } + + int get_value () { return value; } + // Placeholder to satisfy parser, not doing exceptions yet. + void unhandled_exception() { /*exit(1);*/ } + }; +}; + +/* Valued with an await_transform. */ +int gX = 1; + +coro1 +f () +{ + for (;;) + { + gX += co_await 11; + if (gX > 100) + break; + } + co_return gX; +} + +int main () +{ + PRINT ("main: create coro1"); + struct coro1 f_coro = f (); + PRINT ("main: got coro1 - checking gX"); + if (gX != 1) + { + PRINTF ("main: gX is wrong : %d, should be 1\n", gX); + // abort (); + } + PRINT ("main: gX OK -- looping"); + do { + //PRINTF ("main: gX : %d \n", gX); + f_coro.handle.resume(); + } while (!f_coro.handle.done()); + int y = f_coro.handle.promise().get_value(); + if (y != 42) + { + PRINTF ("main: y is wrong : %d, should be 42\n", y); + //abort (); + } + puts ("main: done"); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-6-ovl.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-6-ovl.C new file mode 100644 index 0000000..fc67246 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-6-ovl.C @@ -0,0 +1,153 @@ +// { dg-do run } +#if __clang__ +# include +#else +# include "../coro.h" +#endif + +namespace coro = std::experimental; + +/* just to avoid cluttering dump files. */ +extern "C" int puts (const char *); +extern "C" int printf (const char *, ...); +extern "C" void abort (void) __attribute__((__noreturn__)); + +#ifndef OUTPUT +# define PRINT(X) +# define PRINTF (void) +#else +# define PRINT(X) puts(X) +# define PRINTF printf +#endif + + +struct coro1 { + struct promise_type; + using handle_type = coro::coroutine_handle; + handle_type handle; + coro1 () : handle(0) {} + coro1 (handle_type _handle) + : handle(_handle) { + PRINT("Created coro1 object from handle"); + } + coro1 (const coro1 &) = delete; // no copying + coro1 (coro1 &&s) : handle(s.handle) { + s.handle = nullptr; + PRINT("Moved coro1"); + } + coro1 &operator = (coro1 &&s) { + handle = s.handle; + s.handle = nullptr; + return *this; + } + ~coro1() { + PRINT("Destroyed coro1"); + if ( handle ) + handle.destroy(); + } + + struct suspend_never_prt { + ~suspend_never_prt() {} + bool await_ready() const noexcept { return true; } + void await_suspend(handle_type h) const noexcept { PRINT ("susp-never-susp");} + void await_resume() const noexcept {PRINT ("susp-never-resume");} + }; + + struct suspend_always_prt { + int x; + bool await_ready() const noexcept { return false; } + void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp");} + void await_resume() const noexcept {PRINT ("susp-always-resume");} + }; + + /* This returns an int. */ + struct suspend_always_intprt { + int x; + suspend_always_intprt() : x(5) {} + ~suspend_always_intprt() {} + bool await_ready() const noexcept { return false; } + void await_suspend(coro::coroutine_handle<>) const noexcept { puts ("susp-always-susp-int");} + int await_resume() const noexcept {puts ("susp-always-resume-int"); return x;} + }; + + struct promise_type { + int value; + promise_type() { PRINT ("Created Promise"); } + ~promise_type() { PRINT ("Destroyed Promise"); } + + coro1 get_return_object() { + PRINT ("get_return_object: from handle from promise"); + return coro1 (handle_type::from_promise (*this)); + } + + auto initial_suspend() { + PRINT ("get initial_suspend "); + return suspend_never_prt{}; + } + + auto final_suspend() { + PRINT ("get final_suspend"); + return suspend_always_prt{}; + } + + void return_value (int v) { + PRINTF ("return_value () %d\n",v); + value = v; + } + + int get_value () { return value; } + // Placeholder to satisfy parser, not doing exceptions yet. + void unhandled_exception() { /*exit(1);*/ } + }; + + struct empty { + auto operator co_await() const noexcept { + return suspend_always_prt{}; + } + }; +}; + + +/* The simplest valued co_await we can do. */ +int gX = 1; +coro1::empty e{}; + +coro1 +f () +{ + co_await (e); /* overload */ + co_return gX + 10; +} + +int main () +{ + PRINT ("main: create coro1"); + struct coro1 f_coro = f (); + PRINT ("main: got coro1 - checking gX"); + if (gX != 1) + { + PRINTF ("main: gX is wrong : %d, should be 1\n", gX); + abort (); + } + if (f_coro.handle.done()) + { + PRINT ("main: we should not be 'done' [1]"); + abort (); + } + PRINT ("main: resuming [1]"); + f_coro.handle.resume(); + /* we should now have returned with the co_return (15) */ + if (!f_coro.handle.done()) + { + PRINT ("main: we should be 'done' "); + abort (); + } + int y = f_coro.handle.promise().get_value(); + if (y != 11) + { + PRINTF ("main: y is wrong : %d, should be 11\n", y); + abort (); + } + puts ("main: done"); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-7-tmpl.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-7-tmpl.C new file mode 100644 index 0000000..032e35f --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-7-tmpl.C @@ -0,0 +1,149 @@ +// { dg-do run } +#if __clang__ +# include +#else +# include "../coro.h" +#endif + +namespace coro = std::experimental; + +/* just to avoid cluttering dump files. */ +extern "C" int puts (const char *); +extern "C" int printf (const char *, ...); +extern "C" void abort (void) __attribute__((__noreturn__)); + +#ifndef OUTPUT +# define PRINT(X) +# define PRINTF (void) +#else +# define PRINT(X) puts(X) +# define PRINTF printf +#endif + +template +struct coro1 { + struct promise_type; + using handle_type = coro::coroutine_handle; + handle_type handle; + coro1 () : handle(0) {} + coro1 (handle_type _handle) + : handle(_handle) { + PRINT ("Created coro1 object from handle"); + } + coro1 (const coro1 &) = delete; // no copying + coro1 (coro1 &&s) : handle(s.handle) { + s.handle = nullptr; + PRINT ("Moved coro1"); + } + coro1 &operator = (coro1 &&s) { + handle = s.handle; + s.handle = nullptr; + return *this; + } + ~coro1() { + PRINT ("Destroyed coro1"); + if ( handle ) + handle.destroy(); + } + + struct suspend_never_prt { + ~suspend_never_prt() {} + bool await_ready() const noexcept { return true; } + void await_suspend(handle_type h) const noexcept { PRINT ("susp-never-susp");} + void await_resume() const noexcept {PRINT ("susp-never-resume");} + }; + + struct suspend_always_prt { + T x; + bool await_ready() const noexcept { return false; } + void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp");} + void await_resume() const noexcept {PRINT ("susp-always-resume");} + }; + + /* This returns an int. */ + struct suspend_always_intprt { + T x; + suspend_always_intprt() : x(5) { PRINT ("suspend_always_intprt def ctor"); } + suspend_always_intprt(T _x) : x(_x) + { PRINTF ("suspend_always_intprt ctor with %ld\n", (long)x); } + ~suspend_always_intprt() {} + bool await_ready() const noexcept { return false; } + void await_suspend(coro::coroutine_handle<>) const noexcept { PRINT ("susp-always-susp-int");} + int await_resume() const noexcept { PRINT ("susp-always-resume-int"); return x;} + }; + + struct promise_type { + T value; + promise_type() { PRINT ("Created Promise"); } + ~promise_type() { PRINT ("Destroyed Promise"); } + + coro1 get_return_object() { + PRINT ("get_return_object: from handle from promise"); + return coro1 (handle_type::from_promise (*this)); + } + + auto initial_suspend() { + PRINT ("get initial_suspend "); + return suspend_never_prt{}; + } + + auto final_suspend() { + PRINT ("get final_suspend"); + return suspend_always_prt{}; + } + + void return_value (int v) { + PRINTF ("return_value () %ld\n", (long) v); + value = v; + } + + auto await_transform (T v) { + PRINTF ("await_transform a T () %ld\n", (long)v); + return suspend_always_intprt (v); + } + + T get_value () { return value; } + // Placeholder to satisfy parser, not doing exceptions yet. + void unhandled_exception() { /*exit(1);*/ } + }; +}; + +/* Valued with an await_transform. */ +int gX = 2; + +template +coro1 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(); + + PRINT ("main: got coro1 - checking gX"); + if (gX != 2) + { + PRINTF ("main: gX is wrong : %d, should be 2\n", gX); + abort (); + } + PRINT ("main: gX OK -- looping"); + do { + f_coro.handle.resume(); + } while (!f_coro.handle.done()); + + int y = f_coro.handle.promise().get_value(); + + if (y != 42) + { + PRINTF ("main: y is wrong : %d, should be 42\n", y); + abort (); + } + puts ("main: done"); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-8-cascade.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-8-cascade.C new file mode 100644 index 0000000..f3504d0 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-8-cascade.C @@ -0,0 +1,157 @@ +// { dg-do run } +#if __clang__ +# include +#else +# include "../coro.h" +#endif + +namespace coro = std::experimental; + +/* just to avoid cluttering dump files. */ +extern "C" int puts (const char *); +extern "C" int printf (const char *, ...); +extern "C" void abort (void) __attribute__((__noreturn__)); + +#ifndef OUTPUT +# define PRINT(X) +# define PRINTF (void) +#else +# define PRINT(X) puts(X) +# define PRINTF printf +#endif + +struct coro1 { + struct promise_type; + using handle_type = coro::coroutine_handle; + handle_type handle; + coro1 () : handle(0) {} + coro1 (handle_type _handle) + : handle(_handle) { + PRINT ("Created coro1 object from handle"); + } + coro1 (const coro1 &) = delete; // no copying + coro1 (coro1 &&s) : handle(s.handle) { + s.handle = nullptr; + PRINT ("Moved coro1"); + } + coro1 &operator = (coro1 &&s) { + handle = s.handle; + s.handle = nullptr; + return *this; + } + ~coro1() { + PRINT ("Destroyed coro1"); + if ( handle ) + handle.destroy(); + } + + struct suspend_never_prt { + ~suspend_never_prt() {} + bool await_ready() const noexcept { return true; } + void await_suspend(handle_type h) const noexcept { PRINT ("susp-never-susp");} + void await_resume() const noexcept {PRINT ("susp-never-resume");} + }; + + struct suspend_always_prt { + int x; + bool await_ready() const noexcept { return false; } + void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp");} + void await_resume() const noexcept {PRINT ("susp-always-resume");} + }; + + /* This returns an int. */ + struct suspend_always_intprt { + int x; + suspend_always_intprt() : x(5) { PRINT ("suspend_always_intprt def ctor"); } + suspend_always_intprt(int _x) : x(_x) { PRINTF ("suspend_always_intprt ctor with %d\n", x); } + ~suspend_always_intprt() {} + bool await_ready() const noexcept { return false; } + void await_suspend(coro::coroutine_handle<>) const noexcept { PRINT ("susp-always-susp-int");} + int await_resume() const noexcept { PRINT ("susp-always-resume-int"); return x * x;} + }; + + struct promise_type { + int value; + promise_type() { PRINT ("Created Promise"); } + ~promise_type() { PRINT ("Destroyed Promise"); } + + coro1 get_return_object() { + PRINT ("get_return_object: from handle from promise"); + return coro1 (handle_type::from_promise (*this)); + } + + auto initial_suspend() { + PRINT ("get initial_suspend "); + return suspend_never_prt{}; + } + + auto final_suspend() { + PRINT ("get final_suspend"); + return suspend_always_prt{}; + } + + void return_value (int v) { + PRINTF ("return_value () %d\n",v); + value = v; + } + + auto await_transform (int v) { + PRINTF ("await_transform an int () %d\n",v); + return suspend_always_intprt (v); + } + + int get_value () { return value; } + // Placeholder to satisfy parser, not doing exceptions yet. + void unhandled_exception() { /*exit(1);*/ } + }; +}; + +/* Valued with an await_transform. */ +int gX = 1; +coro1 f () +{ + /* the await transform takes an int, the await_resume squares it. + so we get 11 ** 4, 14641. */ + gX = co_await co_await 11; + co_return gX + 31; +} + +int main () +{ + PRINT ("main: create coro1"); + struct coro1 f_coro = f (); + PRINT ("main: got coro1 - checking gX"); + if (gX != 1) + { + PRINTF ("main: gX is wrong : %d, should be 1\n", gX); + abort (); + } + if (f_coro.handle.done()) + { + PRINT ("main: we should not be 'done' [1]"); + abort (); + } + PRINT ("main: resuming [1] - nested"); + f_coro.handle.resume(); + PRINT ("main: resuming [2] - outer"); + f_coro.handle.resume(); + if (gX != 14641) + { + PRINTF ("main: gX is wrong : %d, should be 14641\n", gX); + abort (); + } + /* we should now have returned with the co_return (14672) */ + if (!f_coro.handle.done()) + { + PRINT ("main: we should be 'done' "); + abort (); + } + int y = f_coro.handle.promise().get_value(); + if (y != 14672) + { + PRINTF ("main: y is wrong : %d, should be 14672\n", y); + abort (); + } + puts ("main: done"); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-9-pair.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-9-pair.C new file mode 100644 index 0000000..8713adf --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-9-pair.C @@ -0,0 +1,155 @@ +// { dg-do run } +#if __clang__ +# include +#else +# include "../coro.h" +#endif + +namespace coro = std::experimental; + +/* just to avoid cluttering dump files. */ +extern "C" int puts (const char *); +extern "C" int printf (const char *, ...); +extern "C" void abort (void) __attribute__((__noreturn__)); + +#ifndef OUTPUT +# define PRINT(X) +# define PRINTF (void) +#else +# define PRINT(X) puts(X) +# define PRINTF printf +#endif + +struct coro1 { + struct promise_type; + using handle_type = coro::coroutine_handle; + handle_type handle; + coro1 () : handle(0) {} + coro1 (handle_type _handle) + : handle(_handle) { + PRINT ("Created coro1 object from handle"); + } + coro1 (const coro1 &) = delete; // no copying + coro1 (coro1 &&s) : handle(s.handle) { + s.handle = nullptr; + PRINT ("Moved coro1"); + } + coro1 &operator = (coro1 &&s) { + handle = s.handle; + s.handle = nullptr; + return *this; + } + ~coro1() { + PRINT ("Destroyed coro1"); + if ( handle ) + handle.destroy(); + } + + struct suspend_never_prt { + ~suspend_never_prt() {} + bool await_ready() const noexcept { return true; } + void await_suspend(handle_type h) const noexcept { PRINT ("susp-never-susp");} + void await_resume() const noexcept {PRINT ("susp-never-resume");} + }; + + struct suspend_always_prt { + int x; + bool await_ready() const noexcept { return false; } + void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp");} + void await_resume() const noexcept {PRINT ("susp-always-resume");} + }; + + /* This returns an int. */ + struct suspend_always_intprt { + int x; + suspend_always_intprt() : x(5) { PRINT ("suspend_always_intprt def ctor"); } + suspend_always_intprt(int _x) : x(_x) { PRINTF ("suspend_always_intprt ctor with %d\n", x); } + ~suspend_always_intprt() {} + bool await_ready() const noexcept { return false; } + void await_suspend(coro::coroutine_handle<>) const noexcept { PRINT ("susp-always-susp-int");} + int await_resume() const noexcept { PRINT ("susp-always-resume-int"); return x;} + }; + + struct promise_type { + int value; + promise_type() { PRINT ("Created Promise"); } + ~promise_type() { PRINT ("Destroyed Promise"); } + + coro1 get_return_object() { + PRINT ("get_return_object: from handle from promise"); + return coro1 (handle_type::from_promise (*this)); + } + + auto initial_suspend() { + PRINT ("get initial_suspend "); + return suspend_never_prt{}; + } + + auto final_suspend() { + PRINT ("get final_suspend"); + return suspend_always_prt{}; + } + + void return_value (int v) { + PRINTF ("return_value () %d\n",v); + value = v; + } + + auto await_transform (int v) { + PRINTF ("await_transform an int () %d\n",v); + return suspend_always_intprt (v); + } + + int get_value () { return value; } + // Placeholder to satisfy parser, not doing exceptions yet. + void unhandled_exception() { /*exit(1);*/ } + }; +}; + +/* Valued with an await_transform. */ +int gX = 1; +coro1 f () +{ + gX = co_await 11 + co_await 15; + co_return gX + 31; +} + +int main () +{ + PRINT ("main: create coro1"); + struct coro1 f_coro = f (); + PRINT ("main: got coro1 - checking gX"); + if (gX != 1) + { + PRINTF ("main: gX is wrong : %d, should be 1\n", gX); + abort (); + } + if (f_coro.handle.done()) + { + PRINT ("main: we should not be 'done' [1]"); + abort (); + } + PRINT ("main: resuming [1] one side of add"); + f_coro.handle.resume(); + PRINT ("main: resuming [1] other side of add"); + f_coro.handle.resume(); + if (gX != 26) + { + PRINTF ("main: gX is wrong : %d, should be 26\n", gX); + abort (); + } + /* we should now have returned with the co_return (57) */ + if (!f_coro.handle.done()) + { + PRINT ("main: we should be 'done' "); + abort (); + } + int y = f_coro.handle.promise().get_value(); + if (y != 57) + { + PRINTF ("main: y is wrong : %d, should be 57\n", y); + abort (); + } + puts ("main: done"); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-3.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-3.C new file mode 100644 index 0000000..f57c4e9 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-3.C @@ -0,0 +1,112 @@ +// { dg-do run } +#if __clang__ +# include +# include +#else +# include "../coro.h" +#endif + +namespace coro = std::experimental; + +// GRO differs from the eventual return type. + +/* just to avoid cluttering dump files. */ +extern "C" int puts (const char *); +extern "C" int printf (const char *, ...); +extern "C" void abort (void) __attribute__((__noreturn__)); + +#ifndef OUTPUT +# define PRINT(X) +#else +# define PRINT(X) puts(X) +#endif + +struct coro1 { + struct promise_type; + using handle_type = coro::coroutine_handle; + handle_type handle; + coro1 () : handle(0) {} + coro1 (handle_type _handle) + : handle(_handle) { + PRINT("Created coro1 object from handle"); + } + coro1 (const coro1 &) = delete; // no copying + coro1 (coro1 &&s) : handle(s.handle) { + s.handle = nullptr; + PRINT("coro1 mv ctor "); + } + coro1 &operator = (coro1 &&s) { + handle = s.handle; + s.handle = nullptr; + PRINT("coro1 op= "); + return *this; + } + ~coro1() { + PRINT("Destroyed coro1"); + if ( handle ) + handle.destroy(); + } + + struct suspend_never_prt { + bool await_ready() const noexcept { return true; } + void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp");} + void await_resume() const noexcept { PRINT ("susp-never-resume");} + ~suspend_never_prt() {}; + }; + + struct suspend_always_prt { + bool await_ready() const noexcept { return false; } + void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp");} + void await_resume() const noexcept { PRINT ("susp-always-resume");} + }; + + struct promise_type { + promise_type() { PRINT ("Created Promise"); } + ~promise_type() { PRINT ("Destroyed Promise"); } + + auto get_return_object () { + PRINT ("get_return_object: handle from promise"); + return handle_type::from_promise (*this); + } + auto initial_suspend () { + PRINT ("get initial_suspend (always)"); + return suspend_always_prt{}; + } + auto final_suspend () { + PRINT ("get final_suspend (always)"); + return suspend_always_prt{}; + } + void return_void () { + PRINT ("return_void ()"); + } + // Placeholder to satisfy parser, not doing exceptions yet. + void unhandled_exception() { /*exit(1);*/ } + }; + //int x; +}; + +struct coro1 +f () noexcept +{ + PRINT ("coro1: about to return"); + co_return; +} + +int main () +{ + PRINT ("main: create coro1"); + struct coro1 x = f (); + PRINT ("main: got coro1 - resuming"); + if (x.handle.done()) + abort(); + x.handle.resume(); + PRINT ("main: after resume"); + if (!x.handle.done()) + { + PRINT ("main: apparently not done..."); + abort (); + //x.handle.resume(); + } + PRINT ("main: returning"); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-4.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-4.C new file mode 100644 index 0000000..d85199e --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-4.C @@ -0,0 +1,129 @@ +// { dg-do run } +#if __clang__ +# include +# include +#else +# include "../coro.h" +#endif + +namespace coro = std::experimental; + +// GRO differs from eventual return type and has non-trivial dtor. + +/* just to avoid cluttering dump files. */ +extern "C" int puts (const char *); +extern "C" int printf (const char *, ...); +extern "C" void abort (void) __attribute__((__noreturn__)); + +#ifndef OUTPUT +# define PRINT(X) +#else +# define PRINT(X) puts(X) +#endif + +struct coro1 { + struct promise_type; + using handle_type = coro::coroutine_handle; + handle_type handle; + + struct nontriv { + handle_type handle; + nontriv () : handle(0) {PRINT("nontriv nul ctor");} + nontriv (handle_type _handle) + : handle(_handle) { + PRINT("Created nontriv object from handle"); + } + ~nontriv () { + PRINT("Destroyed nontriv"); + } + }; + + coro1 () : handle(0) {} + coro1 (handle_type _handle) + : handle(_handle) { + PRINT("Created coro1 object from handle"); + } + coro1 (nontriv _nt) + : handle(_nt.handle) { + PRINT("Created coro1 object from nontriv"); + } + coro1 (const coro1 &) = delete; // no copying + coro1 (coro1 &&s) : handle(s.handle) { + s.handle = nullptr; + PRINT("coro1 mv ctor "); + } + coro1 &operator = (coro1 &&s) { + handle = s.handle; + s.handle = nullptr; + PRINT("coro1 op= "); + return *this; + } + ~coro1() { + PRINT("Destroyed coro1"); + if ( handle ) + handle.destroy(); + } + + struct suspend_never_prt { + bool await_ready() const noexcept { return true; } + void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp");} + void await_resume() const noexcept { PRINT ("susp-never-resume");} + ~suspend_never_prt() {}; + }; + + struct suspend_always_prt { + bool await_ready() const noexcept { return false; } + void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp");} + void await_resume() const noexcept { PRINT ("susp-always-resume");} + }; + + struct promise_type { + promise_type() { PRINT ("Created Promise"); } + ~promise_type() { PRINT ("Destroyed Promise"); } + + auto get_return_object () { + PRINT ("get_return_object: handle from promise"); + return nontriv(handle_type::from_promise (*this)); + } + auto initial_suspend () { + PRINT ("get initial_suspend (always)"); + return suspend_always_prt{}; + } + auto final_suspend () { + PRINT ("get final_suspend (always)"); + return suspend_always_prt{}; + } + void return_void () { + PRINT ("return_void ()"); + } + // Placeholder to satisfy parser, not doing exceptions yet. + void unhandled_exception() { /*exit(1);*/ } + }; + //int x; +}; + +struct coro1 +f () noexcept +{ + PRINT ("coro1: about to return"); + co_return; +} + +int main () +{ + PRINT ("main: create coro1"); + struct coro1 x = f (); + PRINT ("main: got coro1 - resuming"); + if (x.handle.done()) + abort(); + x.handle.resume(); + PRINT ("main: after resume"); + if (!x.handle.done()) + { + PRINT ("main: apparently not done..."); + abort (); + //x.handle.resume(); + } + PRINT ("main: returning"); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-5.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-5.C new file mode 100644 index 0000000..0eb0055 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-5.C @@ -0,0 +1,122 @@ +// { dg-do run } +#if __clang__ +# include +# include +#else +# include "../coro.h" +#endif + +namespace coro = std::experimental; + +// Test returning an int. +// We will use the promise to contain this to avoid having to include +// additional C++ headers. + +/* just to avoid cluttering dump files. */ +extern "C" int puts (const char *); +extern "C" int printf (const char *, ...); +extern "C" void abort (void) __attribute__((__noreturn__)); + +#ifndef OUTPUT +# define PRINT(X) +# define PRINTF (void) +#else +# define PRINT(X) puts(X) +# define PRINTF printf +#endif + +struct coro1 { + struct promise_type; + using handle_type = coro::coroutine_handle; + handle_type handle; + coro1 () : handle(0) {} + coro1 (handle_type _handle) + : handle(_handle) { + PRINT("Created coro1 object from handle"); + } + coro1 (const coro1 &) = delete; // no copying + coro1 (coro1 &&s) : handle(s.handle) { + s.handle = nullptr; + PRINT("coro1 mv ctor "); + } + coro1 &operator = (coro1 &&s) { + handle = s.handle; + s.handle = nullptr; + PRINT("coro1 op= "); + return *this; + } + ~coro1() { + PRINT("Destroyed coro1"); + if ( handle ) + handle.destroy(); + } + + struct suspend_never_prt { + bool await_ready() const noexcept { return true; } + void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp"); } + void await_resume() const noexcept { PRINT ("susp-never-resume");} + }; + + /* NOTE: this has a DTOR to test that pathway. */ + struct suspend_always_prt { + bool await_ready() const noexcept { return false; } + void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp"); } + void await_resume() const noexcept { PRINT ("susp-always-resume"); } + ~suspend_always_prt() { PRINT ("susp-always-DTOR"); } + }; + + struct promise_type { + int value; + promise_type() { PRINT ("Created Promise"); } + ~promise_type() { PRINT ("Destroyed Promise"); } + + auto get_return_object () { + PRINT ("get_return_object: handle from promise"); + return handle_type::from_promise (*this); + } + auto initial_suspend () { + PRINT ("get initial_suspend (always)"); + return suspend_always_prt{}; + } + auto final_suspend () { + PRINT ("get final_suspend (always)"); + return suspend_always_prt{}; + } + void return_value (int v) { + PRINTF ("return_value () %d\n",v); + value = v; + } + int get_value (void) { return value; } + // Placeholder to satisfy parser, not doing exceptions yet. + void unhandled_exception() { /*exit(1);*/ } + }; +}; + +struct coro1 +f () noexcept +{ + PRINT ("coro1: about to return"); + co_return 42; +} + +int main () +{ + PRINT ("main: create coro1"); + struct coro1 x = f (); + PRINT ("main: got coro1 - resuming"); + if (x.handle.done()) + abort(); + x.handle.resume(); + PRINT ("main: after resume"); + int y = x.handle.promise().get_value(); + if ( y != 42 ) + abort (); + if (!x.handle.done()) + { + PRINT ("main: apparently not done..."); + abort (); + //x.handle.resume(); + } + PRINT ("main: returning"); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-6.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-6.C new file mode 100644 index 0000000..1aad1b1 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-6.C @@ -0,0 +1,125 @@ +// { dg-do run } +#if __clang__ +# include +# include +#else +# include "../coro.h" +#endif + +namespace coro = std::experimental; + +// Test returning a T. +// We will use the promise to contain this to avoid having to include +// additional C++ headers. + +/* just to avoid cluttering dump files. */ +extern "C" int puts (const char *); +extern "C" int printf (const char *, ...); +extern "C" void abort (void) __attribute__((__noreturn__)); + +#ifndef OUTPUT +# define PRINT(X) +# define PRINTF (void) +#else +# define PRINT(X) puts(X) +# define PRINTF printf +#endif + +struct suspend_never_prt { + bool await_ready() const noexcept { return true; } + void await_suspend(coro::coroutine_handle<>) const noexcept + { PRINT ("susp-never-susp"); } + void await_resume() const noexcept { PRINT ("susp-never-resume");} +}; + +/* NOTE: this has a DTOR to test that pathway. */ +struct suspend_always_prt { + bool await_ready() const noexcept { return false; } + void await_suspend(coro::coroutine_handle<>) const noexcept + { PRINT ("susp-always-susp"); } + void await_resume() const noexcept { PRINT ("susp-always-resume"); } + ~suspend_always_prt() { PRINT ("susp-always-DTOR"); } +}; + +template +struct coro1 { + struct promise_type; + using handle_type = coro::coroutine_handle; + handle_type handle; + coro1 () : handle(0) {} + coro1 (handle_type _handle) + : handle(_handle) { + PRINT("Created coro1 object from handle"); + } + coro1 (const coro1 &) = delete; // no copying + coro1 (coro1 &&s) : handle(s.handle) { + s.handle = nullptr; + PRINT("coro1 mv ctor "); + } + coro1 &operator = (coro1 &&s) { + handle = s.handle; + s.handle = nullptr; + PRINT("coro1 op= "); + return *this; + } + ~coro1() { + PRINT("Destroyed coro1"); + if ( handle ) + handle.destroy(); + } + + struct promise_type { + T value; + promise_type() { PRINT ("Created Promise"); } + ~promise_type() { PRINT ("Destroyed Promise"); } + + auto get_return_object () { + PRINT ("get_return_object: handle from promise"); + return handle_type::from_promise (*this); + } + + auto initial_suspend () const { + PRINT ("get initial_suspend (always)"); + return suspend_always_prt{}; + } + auto final_suspend () const { + PRINT ("get final_suspend (always)"); + return suspend_always_prt{}; + } + void return_value (T v) { + PRINTF ("return_value () %d\n",v); + value = v; + } + T get_value (void) { return value; } + // Placeholder to satisfy parser, not doing exceptions yet. + void unhandled_exception() { /*exit(1);*/ } + }; +}; + +coro1 +f () noexcept +{ + PRINT ("coro1: about to return"); + co_return (float) 42; +} + +int main () +{ + 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 != (float)42 ) + abort (); + if (!x.handle.done()) + { + PRINT ("main: apparently not done..."); + abort (); + } + PRINT ("main: returning"); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-7.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-7.C new file mode 100644 index 0000000..0af0922 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-7.C @@ -0,0 +1,114 @@ +// { dg-do run } +#if __clang__ +# include +# include +#else +# include "../coro.h" +#endif + +namespace coro = std::experimental; + +/* just to avoid cluttering dump files. */ +extern "C" int puts (const char *); +extern "C" int printf (const char *, ...); +extern "C" void abort (void) __attribute__((__noreturn__)); + +#ifndef OUTPUT +# define PRINT(X) +#else +# define PRINT(X) puts(X) +#endif + +struct coro1 { + struct promise_type; + using handle_type = coro::coroutine_handle; + handle_type handle; + coro1 () : handle(0) {} + coro1 (handle_type _handle) + : handle(_handle) { + PRINT("Created coro1 object from handle"); + } + coro1 (const coro1 &) = delete; // no copying + coro1 (coro1 &&s) : handle(s.handle) { + s.handle = nullptr; + PRINT("coro1 mv ctor "); + } + coro1 &operator = (coro1 &&s) { + handle = s.handle; + s.handle = nullptr; + PRINT("coro1 op= "); + return *this; + } + ~coro1() { + PRINT("Destroyed coro1"); + if ( handle ) + handle.destroy(); + } + + struct suspend_never_prt { + bool await_ready() const noexcept { return true; } + void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp");} + void await_resume() const noexcept { PRINT ("susp-never-resume");} + }; + + struct suspend_always_prt { + bool await_ready() const noexcept { return false; } + void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp");} + void await_resume() const noexcept { PRINT ("susp-always-resume");} + ~suspend_always_prt() { PRINT ("susp-always-dtor"); } + }; + + struct promise_type { + promise_type() { PRINT ("Created Promise"); } + ~promise_type() { PRINT ("Destroyed Promise"); } + + auto get_return_object () { + PRINT ("get_return_object: handle from promise"); + return handle_type::from_promise (*this); + } + auto initial_suspend () { + PRINT ("get initial_suspend (always)"); + return suspend_always_prt{}; + //return std::experimental::suspend_always{}; + } + auto final_suspend () { + PRINT ("get final_suspend (always)"); + return suspend_always_prt{}; + } + void return_void () { + PRINT ("return_void ()"); + } + // Placeholder to satisfy parser, not doing exceptions yet. + void unhandled_exception() { /*exit(1);*/ } + }; + //int x; +}; + +__attribute__((__noinline__)) +int foo (void) { PRINT ("called the int fn foo"); return 2; } + +struct coro1 +f () noexcept +{ + PRINT ("coro1: about to return"); + co_return (void)foo(); +} + +int main () +{ + PRINT ("main: create coro1"); + struct coro1 x = f (); + PRINT ("main: got coro1 - resuming"); + if (x.handle.done()) + abort(); + x.handle.resume(); + PRINT ("main: after resume"); + if (!x.handle.done()) + { + PRINT ("main: apparently not done..."); + abort (); + //x.handle.resume(); + } + PRINT ("main: returning"); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-8.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-8.C new file mode 100644 index 0000000..f79201c --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-8.C @@ -0,0 +1,126 @@ +// { dg-do run } +#if __clang__ +# include +# include +#else +# include "../coro.h" +#endif + +namespace coro = std::experimental; + +// Test templated co-return. + +/* just to avoid cluttering dump files. */ +extern "C" int puts (const char *); +extern "C" int printf (const char *, ...); +extern "C" void abort (void) __attribute__((__noreturn__)); + +#define BROKEN + +#ifndef OUTPUT +# define PRINT(X) +# define PRINTF (void) +#else +# define PRINT(X) puts(X) +# define PRINTF printf +#endif + +struct suspend_never_prt { + bool await_ready() const noexcept { return true; } + void await_suspend(coro::coroutine_handle<>) const noexcept + { PRINT ("susp-never-susp"); } + void await_resume() const noexcept { PRINT ("susp-never-resume");} +}; + +/* NOTE: this has a DTOR to test that pathway. */ +struct suspend_always_prt { + bool await_ready() const noexcept { return false; } + void await_suspend(coro::coroutine_handle<>) const noexcept + { PRINT ("susp-always-susp"); } + void await_resume() const noexcept { PRINT ("susp-always-resume"); } + ~suspend_always_prt() { PRINT ("susp-always-DTOR"); } +}; + +template +struct coro1 { + struct promise_type; + using handle_type = coro::coroutine_handle; + handle_type handle; + coro1 () : handle(0) {} + coro1 (handle_type _handle) + : handle(_handle) { + PRINT("Created coro1 object from handle"); + } + coro1 (const coro1 &) = delete; // no copying + coro1 (coro1 &&s) : handle(s.handle) { + s.handle = nullptr; + PRINT("coro1 mv ctor "); + } + coro1 &operator = (coro1 &&s) { + handle = s.handle; + s.handle = nullptr; + PRINT("coro1 op= "); + return *this; + } + ~coro1() { + PRINT("Destroyed coro1"); + if ( handle ) + handle.destroy(); + } + + struct promise_type { + T value; + promise_type() { PRINT ("Created Promise"); } + ~promise_type() { PRINT ("Destroyed Promise"); } + + auto get_return_object () { + PRINT ("get_return_object: handle from promise"); + return handle_type::from_promise (*this); + } + suspend_always_prt initial_suspend () const { + PRINT ("get initial_suspend (always)"); + return suspend_always_prt{}; + } + suspend_always_prt final_suspend () const { + PRINT ("get final_suspend (always)"); + return suspend_always_prt{}; + } + void return_value (T v) { + PRINTF ("return_value () %d\n",v); + value = v; + } + T get_value (void) { return value; } + // Placeholder to satisfy parser, not doing exceptions yet. + void unhandled_exception() { /*exit(1);*/ } + }; +}; + +template +coro1 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(); + PRINT ("main: got coro1 - resuming"); + if (x.handle.done()) + abort(); + x.handle.resume(); + PRINT ("main: after resume"); + int y = x.handle.promise().get_value(); + if ( y != 42 ) + abort (); + if (!x.handle.done()) + { + PRINT ("main: apparently not done..."); + abort (); + //x.handle.resume(); + } + PRINT ("main: returning"); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-9.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-9.C new file mode 100644 index 0000000..b2dd776 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-9.C @@ -0,0 +1,118 @@ +// { dg-do run } +#if __clang__ +# include +# include +#else +# include "../coro.h" +#endif + +namespace coro = std::experimental; + +/* boolean return from await_suspend (). */ + +/* just to avoid cluttering dump files. */ +extern "C" int puts (const char *); +extern "C" int printf (const char *, ...); +extern "C" void abort (void) __attribute__((__noreturn__)); + +#ifndef OUTPUT +# define PRINT(X) +#else +# define PRINT(X) puts(X) +#endif + +struct coro1 { + struct promise_type; + using handle_type = coro::coroutine_handle; + handle_type handle; + coro1 () : handle(0) {} + coro1 (handle_type _handle) + : handle(_handle) { + PRINT("Created coro1 object from handle"); + } + coro1 (const coro1 &) = delete; // no copying + coro1 (coro1 &&s) : handle(s.handle) { + s.handle = nullptr; + PRINT("coro1 mv ctor "); + } + coro1 &operator = (coro1 &&s) { + handle = s.handle; + s.handle = nullptr; + PRINT("coro1 op= "); + return *this; + } + ~coro1() { + PRINT("Destroyed coro1"); + if ( handle ) + handle.destroy(); + } + + struct suspend_never_prt { + bool await_ready() const noexcept { return true; } + bool await_suspend(handle_type) const noexcept { + PRINT ("susp-never-susp"); // never executed. + return true; // ... + } + void await_resume() const noexcept {PRINT ("susp-never-resume");} + ~suspend_never_prt() {}; + }; + + struct suspend_always_prt { + bool await_ready() const noexcept { return false; } + bool await_suspend(handle_type) const noexcept { + PRINT ("susp-always-susp, but we're going to continue.. "); + return false; // not going to suspend. + } + void await_resume() const noexcept { PRINT ("susp-always-resume");} + }; + + + struct promise_type { + promise_type() { PRINT ("Created Promise"); } + ~promise_type() { PRINT ("Destroyed Promise"); } + + coro1 get_return_object () { + PRINT ("get_return_object: from handle from promise"); + return coro1 (handle_type::from_promise (*this)); + } + auto initial_suspend () { + PRINT ("get initial_suspend (always, but really never) "); + return suspend_always_prt{}; + } + auto final_suspend () { + PRINT ("get final_suspend (always, but never) "); + return suspend_always_prt{}; + } + void return_void () { + PRINT ("return_void ()"); + } + // Placeholder to satisfy parser, not doing exceptions yet. + void unhandled_exception() { /*exit(1);*/ } + }; + //int x; +}; + +struct coro1 +f () noexcept +{ + PRINT ("coro1: about to return"); + co_return; +} + +int main () +{ + //__builtin_coro_promise ((void*)0, 16, true); + PRINT ("main: create coro1"); + struct coro1 x = f (); + auto p = x.handle.promise (); + auto aw = p.initial_suspend(); + auto f = aw.await_suspend(coro::coroutine_handle::from_address ((void *)&x)); + PRINT ("main: got coro1 - should be done"); + if (!x.handle.done()) + { + PRINT ("main: apparently was not done..."); + abort (); + } + PRINT ("main: returning"); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-void-is-ready.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-void-is-ready.C new file mode 100644 index 0000000..f5662d1 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-void-is-ready.C @@ -0,0 +1,107 @@ +// { dg-do run } +#if __clang__ +# include +# include +#else +# include "../coro.h" +#endif + +namespace coro = std::experimental; + +/* just to avoid cluttering dump files. */ +extern "C" int puts (const char *); +extern "C" int printf (const char *, ...); +extern "C" void abort (void) __attribute__((__noreturn__)); + +#ifndef OUTPUT +# define PRINT(X) +#else +# define PRINT(X) puts(X) +#endif + +struct coro1 { + struct promise_type; + using handle_type = coro::coroutine_handle; + handle_type handle; + coro1 () : handle(0) {} + coro1 (handle_type _handle) + : handle(_handle) { + PRINT("Created coro1 object from handle"); + } + coro1 (const coro1 &) = delete; // no copying + coro1 (coro1 &&s) : handle(s.handle) { + s.handle = nullptr; + PRINT("coro1 mv ctor "); + } + coro1 &operator = (coro1 &&s) { + handle = s.handle; + s.handle = nullptr; + PRINT("coro1 op= "); + return *this; + } + ~coro1() { + PRINT("Destroyed coro1"); + if ( handle ) + handle.destroy(); + } + + struct suspend_never_prt { + bool await_ready() const noexcept { return true; } + void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp");} + void await_resume() const noexcept {PRINT ("susp-never-resume");} + ~suspend_never_prt() {}; + }; + + struct suspend_always_prt { + bool await_ready() const noexcept { return false; } + void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp");} + void await_resume() const noexcept { PRINT ("susp-always-resume");} + }; + + + struct promise_type { + promise_type() { PRINT ("Created Promise"); } + ~promise_type() { PRINT ("Destroyed Promise"); } + + coro1 get_return_object () { + PRINT ("get_return_object: from handle from promise"); + return coro1 (handle_type::from_promise (*this)); + } + auto initial_suspend () { + PRINT ("get initial_suspend (never) "); + return suspend_never_prt{}; + } + auto final_suspend () { + PRINT ("get final_suspend (always) "); + return suspend_always_prt{}; + } + void return_void () { + PRINT ("return_void ()"); + } + // Placeholder to satisfy parser, not doing exceptions yet. + void unhandled_exception() { /*exit(1);*/ } + }; + //int x; +}; + +struct coro1 +f () noexcept +{ + PRINT ("coro1: about to return"); + co_return; +} + +int main () +{ + //__builtin_coro_promise ((void*)0, 16, true); + PRINT ("main: create coro1"); + struct coro1 x = f (); + PRINT ("main: got coro1 - should be done"); + if (!x.handle.done()) + { + PRINT ("main: apparently was not done..."); + abort (); + } + PRINT ("main: returning"); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-void-is-suspend.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-void-is-suspend.C new file mode 100644 index 0000000..bc2ab1e --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-void-is-suspend.C @@ -0,0 +1,111 @@ +// { dg-do run } +#if __clang__ +# include +# include +#else +# include "../coro.h" +#endif + +namespace coro = std::experimental; + +/* just to avoid cluttering dump files. */ +extern "C" int puts (const char *); +extern "C" int printf (const char *, ...); +extern "C" void abort (void) __attribute__((__noreturn__)); + +#ifndef OUTPUT +# define PRINT(X) +#else +# define PRINT(X) puts(X) +#endif + +struct coro1 { + struct promise_type; + using handle_type = coro::coroutine_handle; + handle_type handle; + coro1 () : handle(0) {} + coro1 (handle_type _handle) + : handle(_handle) { + PRINT("Created coro1 object from handle"); + } + coro1 (const coro1 &) = delete; // no copying + coro1 (coro1 &&s) : handle(s.handle) { + s.handle = nullptr; + PRINT("coro1 mv ctor "); + } + coro1 &operator = (coro1 &&s) { + handle = s.handle; + s.handle = nullptr; + PRINT("coro1 op= "); + return *this; + } + ~coro1() { + PRINT("Destroyed coro1"); + if ( handle ) + handle.destroy(); + } + + struct suspend_never_prt { + bool await_ready() const noexcept { return true; } + void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp");} + void await_resume() const noexcept { PRINT ("susp-never-resume");} + ~suspend_never_prt() {}; + }; + + struct suspend_always_prt { + bool await_ready() const noexcept { return false; } + void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp");} + void await_resume() const noexcept { PRINT ("susp-always-resume");} + }; + + + struct promise_type { + promise_type() { PRINT ("Created Promise"); } + ~promise_type() { PRINT ("Destroyed Promise"); } + + coro1 get_return_object () { + PRINT ("get_return_object: from handle from promise"); + return coro1 (handle_type::from_promise (*this)); + } + auto initial_suspend () { + PRINT ("get initial_suspend (always)"); + return suspend_always_prt{}; + } + auto final_suspend () { + PRINT ("get final_suspend (always)"); + return suspend_always_prt{}; + } + void return_void () { + PRINT ("return_void ()"); + } + // Placeholder to satisfy parser, not doing exceptions yet. + void unhandled_exception() { /*exit(1);*/ } + }; + //int x; +}; + +struct coro1 +f () noexcept +{ + PRINT ("coro1: about to return"); + co_return; +} + +int main () +{ + PRINT ("main: create coro1"); + struct coro1 x = f (); + PRINT ("main: got coro1 - resuming"); + if (x.handle.done()) + abort(); + x.handle.resume(); + PRINT ("main: after resume"); + if (!x.handle.done()) + { + PRINT ("main: apparently not done..."); + abort (); + //x.handle.resume(); + } + PRINT ("main: returning"); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-yield-0-triv.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-0-triv.C new file mode 100644 index 0000000..4d76a99 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-0-triv.C @@ -0,0 +1,146 @@ +// { dg-do run } +#if __clang__ +# include +# include +#else +# include "../coro.h" +#endif + +namespace coro = std::experimental; + +// Test returning an int. +// We will use the promise to contain this to avoid having to include +// additional C++ headers. + +/* just to avoid cluttering dump files. */ +extern "C" int puts (const char *); +extern "C" int printf (const char *, ...); +extern "C" void abort (void) __attribute__((__noreturn__)); + +#ifndef OUTPUT +# define PRINT(X) +# define PRINTF (void) +#else +# define PRINT(X) puts(X) +# define PRINTF printf +#endif + +struct coro1 { + struct promise_type; + using handle_type = coro::coroutine_handle; + handle_type handle; + coro1 () : handle(0) {} + coro1 (handle_type _handle) + : handle(_handle) { + PRINT("Created coro1 object from handle"); + } + coro1 (const coro1 &) = delete; // no copying + coro1 (coro1 &&s) : handle(s.handle) { + s.handle = nullptr; + PRINT("coro1 mv ctor "); + } + coro1 &operator = (coro1 &&s) { + handle = s.handle; + s.handle = nullptr; + PRINT("coro1 op= "); + return *this; + } + ~coro1() { + PRINT("Destroyed coro1"); + if ( handle ) + handle.destroy(); + } + + struct suspend_never_prt { + bool await_ready() const noexcept { return true; } + void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp"); } + void await_resume() const noexcept { PRINT ("susp-never-resume");} + }; + + /* NOTE: this has a DTOR to test that pathway. */ + struct suspend_always_prt { + bool await_ready() const noexcept { return false; } + void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp"); } + void await_resume() const noexcept { PRINT ("susp-always-resume"); } + ~suspend_always_prt() { PRINT ("susp-always-DTOR"); } + }; + + struct promise_type { + int value; + promise_type() { PRINT ("Created Promise"); } + ~promise_type() { PRINT ("Destroyed Promise"); } + + auto get_return_object () { + PRINT ("get_return_object: handle from promise"); + return handle_type::from_promise (*this); + } + auto initial_suspend () { + PRINT ("get initial_suspend (always)"); + return suspend_always_prt{}; + } + auto final_suspend () { + PRINT ("get final_suspend (always)"); + return suspend_always_prt{}; + } + void return_value (int v) { + PRINTF ("return_value () %d\n",v); + value = v; + } + auto yield_value (int v) { + PRINTF ("yield_value () %d and suspend always\n",v); + value = v; + return suspend_always_prt{}; + } + /* Some non-matching overloads. */ + auto yield_value (suspend_always_prt s, int x) { + return s; + } + auto yield_value (void) { + return 42; + } + int get_value (void) { return value; } + // Placeholder to satisfy parser, not doing exceptions yet. + void unhandled_exception() { /*exit(1);*/ } + }; +}; + +struct coro1 +f () noexcept +{ + PRINT ("f: about to yield 42"); + co_yield 42; + + PRINT ("f: about to return 6174"); + co_return 6174; +} + +int main () +{ + PRINT ("main: create coro1"); + struct coro1 x = f (); + PRINT ("main: got coro1 - resuming (1)"); + if (x.handle.done()) + abort(); + x.handle.resume(); + PRINT ("main: after resume (1)"); + int y = x.handle.promise().get_value(); + if ( y != 42 ) + abort (); + PRINT ("main: apparently got 42"); + PRINT ("main: got coro1 - resuming (2)"); + if (x.handle.done()) + abort(); + x.handle.resume(); + PRINT ("main: after resume (2)"); + y = x.handle.promise().get_value(); + if ( y != 6174 ) + abort (); + PRINT ("main: apparently got 6174"); + if (!x.handle.done()) + { + PRINT ("main: apparently not done..."); + abort (); + } + PRINT ("main: returning"); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-yield-1-multi.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-1-multi.C new file mode 100644 index 0000000..341af61 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-1-multi.C @@ -0,0 +1,159 @@ +// { dg-do run } +#if __clang__ +# include +# include +#else +# include "../coro.h" +#endif + +namespace coro = std::experimental; + +// Test returning an int. +// We will use the promise to contain this to avoid having to include +// additional C++ headers. + +/* just to avoid cluttering dump files. */ +extern "C" int puts (const char *); +extern "C" int printf (const char *, ...); +extern "C" void abort (void) __attribute__((__noreturn__)); + +#ifndef OUTPUT +# define PRINT(X) +# define PRINTF (void) +#else +# define PRINT(X) puts(X) +# define PRINTF printf +#endif + +struct coro1 { + struct promise_type; + using handle_type = coro::coroutine_handle; + handle_type handle; + coro1 () : handle(0) {} + coro1 (handle_type _handle) + : handle(_handle) { + PRINT("Created coro1 object from handle"); + } + coro1 (const coro1 &) = delete; // no copying + coro1 (coro1 &&s) : handle(s.handle) { + s.handle = nullptr; + PRINT("coro1 mv ctor "); + } + coro1 &operator = (coro1 &&s) { + handle = s.handle; + s.handle = nullptr; + PRINT("coro1 op= "); + return *this; + } + ~coro1() { + PRINT("Destroyed coro1"); + if ( handle ) + handle.destroy(); + } + + struct suspend_never_prt { + bool await_ready() const noexcept { return true; } + void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp"); } + void await_resume() const noexcept { PRINT ("susp-never-resume");} + }; + + /* NOTE: this has a DTOR to test that pathway. */ + struct suspend_always_prt { + bool await_ready() const noexcept { return false; } + void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp"); } + void await_resume() const noexcept { PRINT ("susp-always-resume"); } + ~suspend_always_prt() { PRINT ("susp-always-DTOR"); } + }; + + struct promise_type { + int value; + promise_type() { PRINT ("Created Promise"); } + ~promise_type() { PRINT ("Destroyed Promise"); } + + auto get_return_object () { + PRINT ("get_return_object: handle from promise"); + return handle_type::from_promise (*this); + } + auto initial_suspend () { + PRINT ("get initial_suspend (always)"); + return suspend_always_prt{}; + } + auto final_suspend () { + PRINT ("get final_suspend (always)"); + return suspend_always_prt{}; + } + void return_value (int v) { + PRINTF ("return_value () %d\n",v); + value = v; + } + auto yield_value (int v) { + PRINTF ("yield_value () %d and suspend always\n",v); + value = v; + return suspend_always_prt{}; + } + /* Some non-matching overloads. */ + auto yield_value (suspend_always_prt s, int x) { + return s; + } + auto yield_value (void) { + return 42; + } + int get_value (void) { return value; } + // Placeholder to satisfy parser, not doing exceptions yet. + void unhandled_exception() { /*exit(1);*/ } + }; +}; + +struct coro1 +f () noexcept +{ + PRINT ("f: about to yield 42"); + co_yield 42; + + PRINT ("f: about to yield 11"); + co_yield 11; + + PRINT ("f: about to return 6174"); + co_return 6174; +} + +int main () +{ + PRINT ("main: create coro1"); + struct coro1 x = f (); + PRINT ("main: got coro1 - resuming (1)"); + if (x.handle.done()) + abort(); + x.handle.resume(); + PRINT ("main: after resume (1)"); + int y = x.handle.promise().get_value(); + if ( y != 42 ) + abort (); + PRINT ("main: apparently got 42 - resuming (2)"); + if (x.handle.done()) + abort(); + x.handle.resume(); + PRINT ("main: after resume (2)"); + y = x.handle.promise().get_value(); + if ( y != 11 ) + abort (); + PRINT ("main: apparently got 11 - resuming (3)"); + if (x.handle.done()) + { + PRINT ("main: done?"); + abort(); + } + x.handle.resume(); + PRINT ("main: after resume (2) checking return"); + y = x.handle.promise().get_value(); + if ( y != 6174 ) + abort (); + PRINT ("main: apparently got 6174"); + if (!x.handle.done()) + { + PRINT ("main: apparently not done..."); + abort (); + } + PRINT ("main: returning"); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-yield-2-loop.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-2-loop.C new file mode 100644 index 0000000..53851d9 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-2-loop.C @@ -0,0 +1,153 @@ +// { dg-do run } +#if __clang__ +# include +# include +#else +# include "../coro.h" +#endif + +namespace coro = std::experimental; + +// Test returning an int. +// We will use the promise to contain this to avoid having to include +// additional C++ headers. + +/* just to avoid cluttering dump files. */ +extern "C" int puts (const char *); +extern "C" int printf (const char *, ...); +extern "C" void abort (void) __attribute__((__noreturn__)); + +#ifndef OUTPUT +# define PRINT(X) +# define PRINTF (void) +#else +# define PRINT(X) puts(X) +# define PRINTF printf +#endif + +struct coro1 { + struct promise_type; + using handle_type = coro::coroutine_handle; + handle_type handle; + coro1 () : handle(0) {} + coro1 (handle_type _handle) + : handle(_handle) { + PRINT("Created coro1 object from handle"); + } + coro1 (const coro1 &) = delete; // no copying + coro1 (coro1 &&s) : handle(s.handle) { + s.handle = nullptr; + PRINT("coro1 mv ctor "); + } + coro1 &operator = (coro1 &&s) { + handle = s.handle; + s.handle = nullptr; + PRINT("coro1 op= "); + return *this; + } + ~coro1() { + PRINT("Destroyed coro1"); + if ( handle ) + handle.destroy(); + } + + struct suspend_never_prt { + bool await_ready() const noexcept { return true; } + void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp"); } + void await_resume() const noexcept { PRINT ("susp-never-resume");} + }; + + /* NOTE: this has a DTOR to test that pathway. */ + struct suspend_always_prt { + bool await_ready() const noexcept { return false; } + void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp"); } + void await_resume() const noexcept { PRINT ("susp-always-resume"); } + ~suspend_always_prt() { PRINT ("susp-always-DTOR"); } + }; + + struct promise_type { + int value; + promise_type() { PRINT ("Created Promise"); } + ~promise_type() { PRINT ("Destroyed Promise"); } + + auto get_return_object () { + PRINT ("get_return_object: handle from promise"); + return handle_type::from_promise (*this); + } + auto initial_suspend () { + PRINT ("get initial_suspend (always)"); + return suspend_always_prt{}; + } + auto final_suspend () { + PRINT ("get final_suspend (always)"); + return suspend_always_prt{}; + } + void return_value (int v) { + PRINTF ("return_value () %d\n",v); + value = v; + } + auto yield_value (int v) { + PRINTF ("yield_value () %d and suspend always\n",v); + value = v; + return suspend_always_prt{}; + } + /* Some non-matching overloads. */ + auto yield_value (suspend_always_prt s, int x) { + return s; + } + auto yield_value (void) { + return 42;//suspend_always_prt{}; + } + int get_value (void) { return value; } + // Placeholder to satisfy parser, not doing exceptions yet. + void unhandled_exception() { /*exit(1);*/ } + }; +}; + +int gX ; + +struct coro1 +f () noexcept +{ + for (gX = 5; gX < 10 ; gX++) + { + PRINTF ("f: about to yield %d\n", gX); + co_yield gX; + } + + PRINT ("f: about to return 6174"); + co_return 6174; + + //co_return 0; +} + +int main () +{ + PRINT ("main: create coro1"); + struct coro1 f_coro = f (); + PRINT ("main: got coro1 - resuming (1)"); + if (f_coro.handle.done()) + abort(); + f_coro.handle.resume(); + PRINT ("main: after resume (1)"); + int y = f_coro.handle.promise().get_value(); + + PRINT ("main: gX OK -- looping"); + do { + //PRINTF ("main: gX : %d \n", gX); + f_coro.handle.resume(); + } while (!f_coro.handle.done()); + + y = f_coro.handle.promise().get_value(); + if ( y != 6174 ) + abort (); + PRINT ("main: apparently got 6174"); + if (!f_coro.handle.done()) + { + PRINT ("main: apparently not done..."); + abort (); + //x.handle.resume(); + } + PRINT ("main: returning"); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-yield-3-tmpl.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-3-tmpl.C new file mode 100644 index 0000000..b1659e8 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-3-tmpl.C @@ -0,0 +1,160 @@ +// { dg-do run } +#if __clang__ +# include +# include +#else +# include "../coro.h" +#endif + +namespace coro = std::experimental; + +// Test co_yield in templated code. + +/* just to avoid cluttering dump files. */ +extern "C" int puts (const char *); +extern "C" int printf (const char *, ...); +extern "C" void abort (void) __attribute__((__noreturn__)); + +#ifndef OUTPUT +# define PRINT(X) +# define PRINTF (void) +#else +# define PRINT(X) puts(X) +# define PRINTF printf +#endif + +template +struct looper { + + struct promise_type { + T value; + promise_type() { PRINT ("Created Promise"); } + ~promise_type() { PRINT ("Destroyed Promise"); } + + auto get_return_object () { + PRINT ("get_return_object: handle from promise"); + return handle_type::from_promise (*this); + } + + auto initial_suspend () { + PRINT ("get initial_suspend (always)"); + return suspend_always_prt{}; + } + + auto final_suspend () { + PRINT ("get final_suspend (always)"); + return suspend_always_prt{}; + } + + void return_value (T v) { + PRINTF ("return_value () %lf\n", (double)v); + value = v; + } + + auto yield_value (T v) { + PRINTF ("yield_value () %lf and suspend always\n", (double)v); + value = v; + return suspend_always_prt{}; + } + + T get_value (void) { return value; } + + // Placeholder to satisfy parser, not doing exceptions yet. + void unhandled_exception() { /*exit(1);*/ } + }; + + using handle_type = coro::coroutine_handle; + 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 +looper 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 (); + + if (f_coro.handle.done()) + { + PRINT ("main: said we were done, but we hadn't started!"); + abort(); + } + + PRINT ("main: OK -- looping"); + int y, test = 5; + do { + f_coro.handle.resume(); + if (f_coro.handle.done()) + break; + y = f_coro.handle.promise().get_value(); + if (y != test) + { + PRINTF ("main: failed for test %d, got %d\n", test, y); + abort(); + } + test++; + } while (test < 20); + + y = f_coro.handle.promise().get_value(); + if ( y != 6174 ) + abort (); + + PRINT ("main: apparently got 6174"); + if (!f_coro.handle.done()) + { + PRINT ("main: apparently not done..."); + abort (); + } + PRINT ("main: returning"); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-yield-strings.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-strings.C new file mode 100644 index 0000000..9c953ba --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-strings.C @@ -0,0 +1,182 @@ +// { dg-do run } + +#if __clang__ +# include +# include +#else +# include "../coro.h" +#endif + +#include +#include + +namespace coro = std::experimental; + +/* just to avoid cluttering dump files. */ +extern "C" int puts (const char *); +extern "C" int printf (const char *, ...); +extern "C" void abort (void) __attribute__((__noreturn__)); + +#ifndef OUTPUT +# define PRINT(X) +# define PRINTF (void) +#else +# define PRINT(X) puts(X) +# define PRINTF printf +#endif + +template +struct looper { + + struct promise_type { + T value; + promise_type() { PRINT ("Created Promise"); } + ~promise_type() { PRINT ("Destroyed Promise"); } + + auto get_return_object () { + PRINT ("get_return_object: handle from promise"); + return handle_type::from_promise (*this); + } + + auto initial_suspend () { + PRINT ("get initial_suspend (always)"); + return suspend_always_prt{}; + } + + auto final_suspend () { + PRINT ("get final_suspend (always)"); + return suspend_always_prt{}; + } + + void return_value (T v) { + PRINTF ("return_value () %s\n", v.c_str()); + value = v; + } + + auto yield_value (T v) { + PRINTF ("yield_value () %s and suspend always\n", v.c_str()); + value = v; + return suspend_always_prt{}; + } + + T get_value (void) { return value; } + + // Placeholder to satisfy parser, not doing exceptions yet. + void unhandled_exception() { /*exit(1);*/ } + }; + + using handle_type = coro::coroutine_handle; + 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 +looper with_ctorable_state (std::vector d) noexcept +{ + std::vector 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 input = {"first", "the", "quick", "reddish", "fox", "done" }; + auto f_coro = with_ctorable_state (input); + + PRINT ("main: got looper - resuming (1)"); + if (f_coro.handle.done()) + abort(); + + f_coro.handle.resume(); + std::string s = f_coro.handle.promise().get_value(); + if ( s != "first" ) + abort (); + + PRINTF ("main: got : %s\n", s.c_str()); + unsigned check = 1; + do { + f_coro.handle.resume(); + s = f_coro.handle.promise().get_value(); + if (s != input[check++]) + abort (); + PRINTF ("main: got : %s\n", s.c_str()); + } while (!f_coro.handle.done()); + + if ( s != "done" ) + abort (); + + PRINT ("main: should be done"); + if (!f_coro.handle.done()) + { + PRINT ("main: apparently not done..."); + abort (); + //x.handle.resume(); + } + + if (gX != 6174) + { + PRINT ("main: apparently we didn't run mycounter DTOR..."); + abort (); + } + PRINT ("main: returning"); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/torture/coro-torture.exp b/gcc/testsuite/g++.dg/coroutines/torture/coro-torture.exp new file mode 100644 index 0000000..d2463b2 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/coro-torture.exp @@ -0,0 +1,19 @@ +# This harness is for tests that should be run at all optimisation levels. + +load_lib g++-dg.exp +load_lib torture-options.exp + +global DG_TORTURE_OPTIONS LTO_TORTURE_OPTIONS + +dg-init +torture-init + +set DEFAULT_COROFLAGS $DEFAULT_CXXFLAGS +lappend DEFAULT_COROFLAGS "-std=c++17" "-fcoroutines" + +set-torture-options [concat $DG_TORTURE_OPTIONS $LTO_TORTURE_OPTIONS] + +gcc-dg-runtest [lsort [glob $srcdir/$subdir/*.C]] "" $DEFAULT_COROFLAGS + +torture-finish +dg-finish diff --git a/gcc/testsuite/g++.dg/coroutines/torture/exceptions-test-0.C b/gcc/testsuite/g++.dg/coroutines/torture/exceptions-test-0.C new file mode 100644 index 0000000..9601564 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/exceptions-test-0.C @@ -0,0 +1,189 @@ +// { dg-do run } +#if __clang__ +# include +# include +#else +# include "../coro.h" +#endif +#include + +namespace coro = std::experimental; + +// Test returning an int. +// We will use the promise to contain this to avoid having to include +// additional C++ headers. + +/* just to avoid cluttering dump files. */ +extern "C" int puts (const char *); +extern "C" int printf (const char *, ...); +extern "C" void abort (void) __attribute__((__noreturn__)); + +#ifndef OUTPUT +# define PRINT(X) +# define PRINTF (void) +#else +# define PRINT(X) puts(X) +# define PRINTF printf +#endif + +int gX = 0; + +struct coro1 { + struct promise_type; + using handle_type = coro::coroutine_handle; + handle_type handle; + coro1 () : handle(0) {} + coro1 (handle_type _handle) + : handle(_handle) { + PRINT("Created coro1 object from handle"); + } + coro1 (const coro1 &) = delete; // no copying + coro1 (coro1 &&s) : handle(s.handle) { + s.handle = nullptr; + PRINT("coro1 mv ctor "); + } + coro1 &operator = (coro1 &&s) { + handle = s.handle; + s.handle = nullptr; + PRINT("coro1 op= "); + return *this; + } + ~coro1() { + PRINT("Destroyed coro1"); + if ( handle ) + handle.destroy(); + } + + struct suspend_never_prt { + bool await_ready() const noexcept { return true; } + void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp"); } + void await_resume() const noexcept { PRINT ("susp-never-resume");} + }; + + /* NOTE: this has a DTOR to test that pathway. */ + struct suspend_always_prt { + bool await_ready() const noexcept { return false; } + void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp"); } + void await_resume() const noexcept { PRINT ("susp-always-resume"); } + ~suspend_always_prt() { PRINT ("susp-always-DTOR"); } + }; + + struct promise_type { + int value; + promise_type() { PRINT ("Created Promise"); } + ~promise_type() { PRINT ("Destroyed Promise"); } + + auto get_return_object () { + PRINT ("get_return_object: handle from promise"); + return handle_type::from_promise (*this); + } + auto initial_suspend () { + PRINT ("get initial_suspend (always)"); + return suspend_always_prt{}; + } + auto final_suspend () { + PRINT ("get final_suspend (always)"); + return suspend_always_prt{}; + } + void return_value (int v) { + PRINTF ("return_value () %d\n",v); + value = v; + } + auto yield_value (int v) { + PRINTF ("yield_value () %d and suspend always\n",v); + value = v; + return suspend_always_prt{}; + } + /* Some non-matching overloads. */ + auto yield_value (suspend_always_prt s, int x) { + return s; + } + auto yield_value (void) { + return 42;//suspend_always_prt{}; + } + int get_value (void) { return value; } + // Placeholder to satisfy parser, not doing exceptions yet. + void unhandled_exception() { + PRINT ("unhandled_exception: caught one!");/*exit(1);*/ + gX = -1; + // returning from here should end up in final_suspend. + //std::terminate (); + } + }; +}; + +// So we want to check that the internal behaviour of try/catch is +// working OK - and that if we have an unhandled exception it is caught +// by the wrapper that we add to the rewritten func. + +struct coro1 throw_and_catch () noexcept +{ + int caught = 0; + + try { + PRINT ("f: about to yield 42"); + co_yield 42; + + throw (20); + + PRINT ("f: about to yield 6174"); + co_return 6174; + + } catch (int x) { + PRINTF ("f: caught %d\n", x); + caught = x; + } + + PRINTF ("f: about to yield what we caught %d\n", caught); + co_yield caught; + + throw ("bah"); + + PRINT ("f: about to return 22"); + co_return 22; +} + +int main () +{ + PRINT ("main: create coro1"); + struct coro1 x = throw_and_catch (); + if (x.handle.done()) + abort(); + x.handle.resume(); + PRINT ("main: got coro, resuming.."); + int y = x.handle.promise().get_value(); + if ( y != 42 ) + abort (); + PRINT ("main: apparently got the expected 42"); + if (x.handle.done()) + abort(); + PRINT ("main: resuming..."); + x.handle.resume(); + + y = x.handle.promise().get_value(); + if ( y != 20 ) + abort (); + PRINT ("main: apparently got 20, which we expected"); + if (x.handle.done()) + abort(); + + PRINT ("main: resuming..."); + x.handle.resume(); + // This should cause the throw of "bah" which is unhandled. + // We should catch the unhandled exception and then fall through + // to the final suspend point... thus be "done". + if (!x.handle.done()) + { + PRINT ("main: apparently not done..."); + abort (); + } + // When we caught the unhandled exception we flagged it instead of + // std::terminate-ing. + if (gX != -1) + { + PRINT ("main: apparently failed to catch"); + abort (); + } + PRINT ("main: returning"); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-0.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-0.C new file mode 100644 index 0000000..308f7ef --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-0.C @@ -0,0 +1,126 @@ +// { dg-do run } +#if __clang__ +# include +# include +#else +# include "../coro.h" +#endif + +namespace coro = std::experimental; + +// Test promise construction from function args list. +// We will use the promise to contain this to avoid having to include +// additional C++ headers. + +/* just to avoid cluttering dump files. */ +extern "C" int puts (const char *); +extern "C" int printf (const char *, ...); +extern "C" void abort (void) __attribute__((__noreturn__)); + +#ifndef OUTPUT +# define PRINT(X) +# define PRINTF (void) +#else +# define PRINT(X) puts(X) +# define PRINTF printf +#endif + +struct coro1 { + struct promise_type; + using handle_type = coro::coroutine_handle; + handle_type handle; + coro1 () : handle(0) {} + coro1 (handle_type _handle) + : handle(_handle) { + PRINT("Created coro1 object from handle"); + } + coro1 (const coro1 &) = delete; // no copying + coro1 (coro1 &&s) : handle(s.handle) { + s.handle = nullptr; + PRINT("coro1 mv ctor "); + } + coro1 &operator = (coro1 &&s) { + handle = s.handle; + s.handle = nullptr; + PRINT("coro1 op= "); + return *this; + } + ~coro1() { + PRINT("Destroyed coro1"); + if ( handle ) + handle.destroy(); + } + + struct suspend_never_prt { + bool await_ready() const noexcept { return true; } + void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp"); } + void await_resume() const noexcept { PRINT ("susp-never-resume");} + }; + + /* NOTE: this has a DTOR to test that pathway. */ + struct suspend_always_prt { + bool await_ready() const noexcept { return false; } + void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp"); } + void await_resume() const noexcept { PRINT ("susp-always-resume"); } + ~suspend_always_prt() { PRINT ("susp-always-DTOR"); } + }; + + struct promise_type { + int value; + promise_type(int x) : value (x) { PRINTF ("Created Promise with %d\n", x); } + promise_type() { PRINT ("Created Promise"); } + ~promise_type() { PRINT ("Destroyed Promise"); } + + auto get_return_object () { + PRINT ("get_return_object: handle from promise"); + return handle_type::from_promise (*this); + } + auto initial_suspend () { + PRINT ("get initial_suspend (always)"); + return suspend_always_prt{}; + } + auto final_suspend () { + PRINT ("get final_suspend (always)"); + return suspend_always_prt{}; + } + void return_value (int v) { + PRINTF ("return_value () %d\n",v); + value = v; + } + int get_value (void) { return value; } + // Placeholder to satisfy parser, not doing exceptions yet. + void unhandled_exception() { /*exit(1);*/ } + }; +}; + +struct coro1 +f (int x) noexcept +{ + PRINT ("coro1: about to return"); + co_return 42; +} + +int main () +{ + PRINT ("main: create coro1"); + struct coro1 x = f (555); + int y = x.handle.promise().get_value(); + if ( y != 555 ) + abort (); + PRINTF ("main: after coro1 ctor %d - now resuming\n", y); + if (x.handle.done()) + abort(); + x.handle.resume(); + PRINT ("main: after resume"); + y = x.handle.promise().get_value(); + if ( y != 42 ) + abort (); + if (!x.handle.done()) + { + PRINT ("main: apparently not done..."); + abort (); + //x.handle.resume(); + } + PRINT ("main: returning"); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-1.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-1.C new file mode 100644 index 0000000..4408ae1 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-1.C @@ -0,0 +1,130 @@ +// { dg-do run } +#if __clang__ +# include +# include +#else +# include "../coro.h" +#endif + +namespace coro = std::experimental; + +// Test returning an int. +// We will use the promise to contain this to avoid having to include +// additional C++ headers. + +/* just to avoid cluttering dump files. */ +extern "C" int puts (const char *); +extern "C" int printf (const char *, ...); +extern "C" void abort (void) __attribute__((__noreturn__)); + +#ifndef OUTPUT +# define PRINT(X) +# define PRINTF (void) +#else +# define PRINT(X) puts(X) +# define PRINTF printf +#endif + +struct coro1 { + struct promise_type; + using handle_type = coro::coroutine_handle; + handle_type handle; + coro1 () : handle(0) {} + coro1 (handle_type _handle) + : handle(_handle) { + PRINT("Created coro1 object from handle"); + } + coro1 (const coro1 &) = delete; // no copying + coro1 (coro1 &&s) : handle(s.handle) { + s.handle = nullptr; + PRINT("coro1 mv ctor "); + } + coro1 &operator = (coro1 &&s) { + handle = s.handle; + s.handle = nullptr; + PRINT("coro1 op= "); + return *this; + } + ~coro1() { + PRINT("Destroyed coro1"); + if ( handle ) + handle.destroy(); + } + + struct suspend_never_prt { + bool await_ready() const noexcept { return true; } + void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp"); } + void await_resume() const noexcept { PRINT ("susp-never-resume");} + }; + + /* NOTE: this has a DTOR to test that pathway. */ + struct suspend_always_prt { + bool await_ready() const noexcept { return false; } + void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp"); } + void await_resume() const noexcept { PRINT ("susp-always-resume"); } + ~suspend_always_prt() { PRINT ("susp-always-DTOR"); } + }; + + struct promise_type { + int value; + promise_type() { PRINT ("Created Promise"); } + ~promise_type() { PRINT ("Destroyed Promise"); } + + auto get_return_object () { + PRINT ("get_return_object: handle from promise"); + return handle_type::from_promise (*this); + } + auto initial_suspend () { + PRINT ("get initial_suspend (always)"); + return suspend_always_prt{}; + } + auto final_suspend () { + PRINT ("get final_suspend (always)"); + return suspend_always_prt{}; + } + void return_value (int v) { + PRINTF ("return_value () %d\n",v); + value = v; + } + int get_value (void) { return value; } + // Placeholder to satisfy parser, not doing exceptions yet. + void unhandled_exception() { /*exit(1);*/ } + }; +}; + +struct coro1 +f (int x) noexcept +{ + if (x > 20) + { + PRINT ("coro1: about to return k"); + co_return 6174; + } + else + { + PRINT ("coro1: about to return the answer"); + co_return 42; + } +} + +int main () +{ + PRINT ("main: create coro1"); + struct coro1 x = f (32); + PRINT ("main: got coro1 - resuming"); + if (x.handle.done()) + abort(); + x.handle.resume(); + PRINT ("main: after resume"); + int y = x.handle.promise().get_value(); + if ( y != 6174 ) + abort (); + if (!x.handle.done()) + { + PRINT ("main: apparently not done..."); + abort (); + //x.handle.resume(); + } + PRINT ("main: returning"); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-2.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-2.C new file mode 100644 index 0000000..2d97e76 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-2.C @@ -0,0 +1,134 @@ +// { dg-do run } +#if __clang__ +# include +# include +#else +# include "../coro.h" +#endif + +namespace coro = std::experimental; + +// Test that we correctly re-write multiple uses of a function param +// in the body. + +/* just to avoid cluttering dump files. */ +extern "C" int puts (const char *); +extern "C" int printf (const char *, ...); +extern "C" void abort (void) __attribute__((__noreturn__)); + +#ifndef OUTPUT +# define PRINT(X) +# define PRINTF (void) +#else +# define PRINT(X) puts(X) +# define PRINTF printf +#endif + +struct coro1 { + struct promise_type; + using handle_type = coro::coroutine_handle; + handle_type handle; + coro1 () : handle(0) {} + coro1 (handle_type _handle) + : handle(_handle) { + PRINT("Created coro1 object from handle"); + } + coro1 (const coro1 &) = delete; // no copying + coro1 (coro1 &&s) : handle(s.handle) { + s.handle = nullptr; + PRINT("coro1 mv ctor "); + } + coro1 &operator = (coro1 &&s) { + handle = s.handle; + s.handle = nullptr; + PRINT("coro1 op= "); + return *this; + } + ~coro1() { + PRINT("Destroyed coro1"); + if ( handle ) + handle.destroy(); + } + + struct suspend_never_prt { + bool await_ready() const noexcept { return true; } + void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp"); } + void await_resume() const noexcept { PRINT ("susp-never-resume");} + }; + + /* NOTE: this has a DTOR to test that pathway. */ + struct suspend_always_prt { + bool await_ready() const noexcept { return false; } + void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp"); } + void await_resume() const noexcept { PRINT ("susp-always-resume"); } + ~suspend_always_prt() { PRINT ("susp-always-DTOR"); } + }; + + struct promise_type { + int value; + promise_type() { PRINT ("Created Promise"); } + ~promise_type() { PRINT ("Destroyed Promise"); } + + auto get_return_object () { + PRINT ("get_return_object: handle from promise"); + return handle_type::from_promise (*this); + } + auto initial_suspend () { + PRINT ("get initial_suspend (always)"); + return suspend_always_prt{}; + } + auto final_suspend () { + PRINT ("get final_suspend (always)"); + return suspend_always_prt{}; + } + void return_value (int v) { + PRINTF ("return_value () %d\n",v); + value = v; + } + int get_value (void) { return value; } + // Placeholder to satisfy parser, not doing exceptions yet. + void unhandled_exception() { /*exit(1);*/ } + }; +}; + +struct coro1 +f (int x) noexcept +{ + if (x > 30) + { + PRINT ("coro1: about to return k"); + co_return 6174; + } + else if (x > 20) + { + PRINT ("coro1: about to return the answer"); + co_return 42; + } + else + { + PRINT ("coro1: about to return 0"); + co_return 0; + } +} + +int main () +{ + PRINT ("main: create coro1"); + struct coro1 x = f (25); + PRINT ("main: got coro1 - resuming"); + if (x.handle.done()) + abort(); + x.handle.resume(); + PRINT ("main: after resume"); + int y = x.handle.promise().get_value(); + if ( y != 42 ) + abort (); + if (!x.handle.done()) + { + PRINT ("main: apparently not done..."); + abort (); + //x.handle.resume(); + } + PRINT ("main: returning"); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-3.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-3.C new file mode 100644 index 0000000..1242b13 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-3.C @@ -0,0 +1,133 @@ +// { dg-do run } +#if __clang__ +# include +# include +#else +# include "../coro.h" +#endif + +namespace coro = std::experimental; + +// Test that we can use a function param in a co_xxxx status. + +/* just to avoid cluttering dump files. */ +extern "C" int puts (const char *); +extern "C" int printf (const char *, ...); +extern "C" void abort (void) __attribute__((__noreturn__)); + +#ifndef OUTPUT +# define PRINT(X) +# define PRINTF (void) +#else +# define PRINT(X) puts(X) +# define PRINTF printf +#endif + +struct coro1 { + struct promise_type; + using handle_type = coro::coroutine_handle; + handle_type handle; + coro1 () : handle(0) {} + coro1 (handle_type _handle) + : handle(_handle) { + PRINT("Created coro1 object from handle"); + } + coro1 (const coro1 &) = delete; // no copying + coro1 (coro1 &&s) : handle(s.handle) { + s.handle = nullptr; + PRINT("coro1 mv ctor "); + } + coro1 &operator = (coro1 &&s) { + handle = s.handle; + s.handle = nullptr; + PRINT("coro1 op= "); + return *this; + } + ~coro1() { + PRINT("Destroyed coro1"); + if ( handle ) + handle.destroy(); + } + + struct suspend_never_prt { + bool await_ready() const noexcept { return true; } + void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp"); } + void await_resume() const noexcept { PRINT ("susp-never-resume");} + }; + + /* NOTE: this has a DTOR to test that pathway. */ + struct suspend_always_prt { + bool await_ready() const noexcept { return false; } + void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp"); } + void await_resume() const noexcept { PRINT ("susp-always-resume"); } + ~suspend_always_prt() { PRINT ("susp-always-DTOR"); } + }; + + struct promise_type { + int value; + promise_type() { PRINT ("Created Promise"); } + ~promise_type() { PRINT ("Destroyed Promise"); } + + auto get_return_object () { + PRINT ("get_return_object: handle from promise"); + return handle_type::from_promise (*this); + } + auto initial_suspend () { + PRINT ("get initial_suspend (always)"); + return suspend_always_prt{}; + } + auto final_suspend () { + PRINT ("get final_suspend (always)"); + return suspend_always_prt{}; + } + void return_value (int v) { + PRINTF ("return_value () %d\n",v); + value = v; + } + int get_value (void) { return value; } + // Placeholder to satisfy parser, not doing exceptions yet. + void unhandled_exception() { /*exit(1);*/ } + }; +}; + +struct coro1 +f (int x) noexcept +{ + if (x > 30) + { + PRINT ("coro1: about to return k"); + co_return 6174; + } + else if (x > 20) + { + PRINTF ("coro1: about to co-return %d", x); + co_return x; + } + else + { + PRINT ("coro1: about to return 0"); + co_return 0; + } +} + +int main () +{ + PRINT ("main: create coro1"); + struct coro1 x = f (25); + PRINT ("main: got coro1 - resuming"); + if (x.handle.done()) + abort(); + x.handle.resume(); + PRINT ("main: after resume"); + int y = x.handle.promise().get_value(); + if ( y != 25 ) + abort (); + if (!x.handle.done()) + { + PRINT ("main: apparently not done..."); + abort (); + //x.handle.resume(); + } + PRINT ("main: returning"); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-4.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-4.C new file mode 100644 index 0000000..729aae6 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-4.C @@ -0,0 +1,141 @@ +// { dg-do run } +#if __clang__ +# include +# include +#else +# include "../coro.h" +#endif + +namespace coro = std::experimental; + +// Test that we can manage a constructed param copy. + +/* just to avoid cluttering dump files. */ +extern "C" int puts (const char *); +extern "C" int printf (const char *, ...); +extern "C" void abort (void) __attribute__((__noreturn__)); + +#ifndef OUTPUT +# define PRINT(X) +# define PRINTF (void) +#else +# define PRINT(X) puts(X) +# define PRINTF printf +#endif + +struct coro1 { + struct promise_type; + using handle_type = coro::coroutine_handle; + handle_type handle; + coro1 () : handle(0) {} + coro1 (handle_type _handle) + : handle(_handle) { + PRINT("Created coro1 object from handle"); + } + coro1 (const coro1 &) = delete; // no copying + coro1 (coro1 &&s) : handle(s.handle) { + s.handle = nullptr; + PRINT("coro1 mv ctor "); + } + coro1 &operator = (coro1 &&s) { + handle = s.handle; + s.handle = nullptr; + PRINT("coro1 op= "); + return *this; + } + ~coro1() { + PRINT("Destroyed coro1"); + if ( handle ) + handle.destroy(); + } + + struct suspend_never_prt { + bool await_ready() const noexcept { return true; } + void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp"); } + void await_resume() const noexcept { PRINT ("susp-never-resume");} + }; + + /* NOTE: this has a DTOR to test that pathway. */ + struct suspend_always_prt { + bool await_ready() const noexcept { return false; } + void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp"); } + void await_resume() const noexcept { PRINT ("susp-always-resume"); } + ~suspend_always_prt() { PRINT ("susp-always-DTOR"); } + }; + + struct promise_type { + int value; + promise_type() { PRINT ("Created Promise"); } + ~promise_type() { PRINT ("Destroyed Promise"); } + + auto get_return_object () { + PRINT ("get_return_object: handle from promise"); + return handle_type::from_promise (*this); + } + auto initial_suspend () { + PRINT ("get initial_suspend (always)"); + return suspend_always_prt{}; + } + auto final_suspend () { + PRINT ("get final_suspend (always)"); + return suspend_always_prt{}; + } + void return_value (int v) { + PRINTF ("return_value () %d\n",v); + value = v; + } + int get_value (void) { return value; } + // Placeholder to satisfy parser, not doing exceptions yet. + void unhandled_exception() { /*exit(1);*/ } + }; +}; + +// Require a ctor. +struct nontriv { + int a, b, c; + nontriv (int _a, int _b, int _c) : a(_a), b(_b), c(_c) {} + virtual int getA () { return a; } +}; + +struct coro1 +f (nontriv t) noexcept +{ + if (t.a > 30) + { + PRINTF ("coro1: about to return %d", t.b); + co_return t.b; + } + else if (t.a > 20) + { + PRINTF ("coro1: about to co-return %d", t.c); + co_return t.c; + } + else + { + PRINT ("coro1: about to return 0"); + co_return 0; + } +} + +int main () +{ + PRINT ("main: create coro1"); + nontriv test (25, 6174, 42); + struct coro1 x = f (test); + PRINT ("main: got coro1 - resuming"); + if (x.handle.done()) + abort(); + x.handle.resume(); + PRINT ("main: after resume"); + int y = x.handle.promise().get_value(); + if ( y != 42 ) + abort (); + if (!x.handle.done()) + { + PRINT ("main: apparently not done..."); + abort (); + //x.handle.resume(); + } + PRINT ("main: returning"); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-5.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-5.C new file mode 100644 index 0000000..e0c3d2f --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-5.C @@ -0,0 +1,141 @@ +// { dg-do run } +#if __clang__ +# include +# include +#else +# include "../coro.h" +#endif + +namespace coro = std::experimental; + +// Test that we can manage a constructed param copy. + +/* just to avoid cluttering dump files. */ +extern "C" int puts (const char *); +extern "C" int printf (const char *, ...); +extern "C" void abort (void) __attribute__((__noreturn__)); + +#ifndef OUTPUT +# define PRINT(X) +# define PRINTF (void) +#else +# define PRINT(X) puts(X) +# define PRINTF printf +#endif + +struct coro1 { + struct promise_type; + using handle_type = coro::coroutine_handle; + handle_type handle; + coro1 () : handle(0) {} + coro1 (handle_type _handle) + : handle(_handle) { + PRINT("Created coro1 object from handle"); + } + coro1 (const coro1 &) = delete; // no copying + coro1 (coro1 &&s) : handle(s.handle) { + s.handle = nullptr; + PRINT("coro1 mv ctor "); + } + coro1 &operator = (coro1 &&s) { + handle = s.handle; + s.handle = nullptr; + PRINT("coro1 op= "); + return *this; + } + ~coro1() { + PRINT("Destroyed coro1"); + if ( handle ) + handle.destroy(); + } + + struct suspend_never_prt { + bool await_ready() const noexcept { return true; } + void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp"); } + void await_resume() const noexcept { PRINT ("susp-never-resume");} + }; + + /* NOTE: this has a DTOR to test that pathway. */ + struct suspend_always_prt { + bool await_ready() const noexcept { return false; } + void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp"); } + void await_resume() const noexcept { PRINT ("susp-always-resume"); } + ~suspend_always_prt() { PRINT ("susp-always-DTOR"); } + }; + + struct promise_type { + int value; + promise_type() { PRINT ("Created Promise"); } + ~promise_type() { PRINT ("Destroyed Promise"); } + + auto get_return_object () { + PRINT ("get_return_object: handle from promise"); + return handle_type::from_promise (*this); + } + auto initial_suspend () { + PRINT ("get initial_suspend (always)"); + return suspend_always_prt{}; + } + auto final_suspend () { + PRINT ("get final_suspend (always)"); + return suspend_always_prt{}; + } + void return_value (int v) { + PRINTF ("return_value () %d\n",v); + value = v; + } + int get_value (void) { return value; } + // Placeholder to satisfy parser, not doing exceptions yet. + void unhandled_exception() { /*exit(1);*/ } + }; +}; + +// Require a ctor. +struct nontriv { + int a, b, c; + nontriv (int _a, int _b, int _c) : a(_a), b(_b), c(_c) {} + virtual int getA () { return a; } +}; + +struct coro1 +f (nontriv &t) noexcept +{ + if (t.a > 30) + { + PRINTF ("coro1: about to return %d", t.b); + co_return t.b; + } + else if (t.a > 20) + { + PRINTF ("coro1: about to co-return %d", t.c); + co_return t.c; + } + else + { + PRINT ("coro1: about to return 0"); + co_return 0; + } +} + +int main () +{ + PRINT ("main: create coro1"); + nontriv test (25, 6174, 42); + struct coro1 x = f (test); + PRINT ("main: got coro1 - resuming"); + if (x.handle.done()) + abort(); + x.handle.resume(); + PRINT ("main: after resume"); + int y = x.handle.promise().get_value(); + if ( y != 42 ) + abort (); + if (!x.handle.done()) + { + PRINT ("main: apparently not done..."); + abort (); + //x.handle.resume(); + } + PRINT ("main: returning"); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/torture/gro_on_alloc_fail_0.C b/gcc/testsuite/g++.dg/coroutines/torture/gro_on_alloc_fail_0.C new file mode 100644 index 0000000..40a3620 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/gro_on_alloc_fail_0.C @@ -0,0 +1,137 @@ +// { dg-do run } +#if __clang__ +# include +# include +#else +# include "../coro.h" +#endif + +namespace coro = std::experimental; + +// check the code-gen for the failed alloc return. + +#if __has_include() +# include +#else + +// Required when get_return_object_on_allocation_failure() is defined by +// the promise. +// we need a no-throw new, and new etc. build the relevant pieces here to +// avoid needing the headers in the test. + +namespace std { + struct nothrow_t {}; + constexpr nothrow_t nothrow = {}; + typedef __SIZE_TYPE__ size_t; +} // end namespace std + +void* operator new(std::size_t, const std::nothrow_t&) noexcept; +void operator delete(void* __p, const std::nothrow_t&) noexcept; +#endif + +/* just to avoid cluttering dump files. */ +extern "C" int puts (const char *); +extern "C" int printf (const char *, ...); +extern "C" void abort (void) __attribute__((__noreturn__)); + +#ifndef OUTPUT +# define PRINT(X) +#else +# define PRINT(X) puts(X) +#endif + +struct coro1 { + struct promise_type; + using handle_type = coro::coroutine_handle; + handle_type handle; + coro1 () noexcept : handle(0) {} + coro1 (handle_type _handle) noexcept + : handle(_handle) { + PRINT("Created coro1 object from handle"); + } + coro1 (const coro1 &) = delete; // no copying + coro1 (coro1 &&s) noexcept : handle(s.handle) { + s.handle = nullptr; + PRINT("coro1 mv ctor "); + } + coro1 &operator = (coro1 &&s) noexcept { + handle = s.handle; + s.handle = nullptr; + PRINT("coro1 op= "); + return *this; + } + ~coro1() noexcept { + PRINT("Destroyed coro1"); + if ( handle ) + handle.destroy(); + } + + struct suspend_never_prt { + bool await_ready() const noexcept { return true; } + void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp");} + void await_resume() const noexcept { PRINT ("susp-never-resume");} + ~suspend_never_prt() {}; + }; + + struct suspend_always_prt { + bool await_ready() const noexcept { return false; } + void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp");} + void await_resume() const noexcept { PRINT ("susp-always-resume");} + }; + + struct promise_type { + promise_type() { PRINT ("Created Promise"); } + ~promise_type() { PRINT ("Destroyed Promise"); } + + auto get_return_object () { + PRINT ("get_return_object: handle from promise"); + return handle_type::from_promise (*this); + } + auto initial_suspend () { + PRINT ("get initial_suspend (always)"); + return suspend_always_prt{}; + } + auto final_suspend () { + PRINT ("get final_suspend (always)"); + return suspend_always_prt{}; + } + void return_void () { + PRINT ("return_void ()"); + } + // Placeholder to satisfy parser, not doing exceptions yet. + void unhandled_exception() { /*exit(1);*/ } + static coro1 get_return_object_on_allocation_failure () noexcept; + }; // promise +}; // coro1 + +coro1 coro1::promise_type:: +get_return_object_on_allocation_failure () noexcept { + PRINT ("alloc fail return"); + return coro1 (nullptr); +} + +struct coro1 +f () noexcept +{ + PRINT ("coro1: about to return"); + co_return; +} + +int main () +{ + PRINT ("main: create coro1"); + struct coro1 x = f (); + PRINT ("main: got coro1 - resuming"); + if (x.handle.done()) + abort(); + x.handle.resume(); + PRINT ("main: after resume"); + if (!x.handle.done()) + { + PRINT ("main: apparently not done..."); + abort (); + //x.handle.resume(); + } + PRINT ("main: returning"); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/torture/local-var-0.C b/gcc/testsuite/g++.dg/coroutines/torture/local-var-0.C new file mode 100644 index 0000000..f78787b --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/local-var-0.C @@ -0,0 +1,123 @@ +// { dg-do run } +#if __clang__ +# include +# include +#else +# include "../coro.h" +#endif + +namespace coro = std::experimental; + +// Test returning an int. +// We will use the promise to contain this to avoid having to include +// additional C++ headers. + +/* just to avoid cluttering dump files. */ +extern "C" int puts (const char *); +extern "C" int printf (const char *, ...); +extern "C" void abort (void) __attribute__((__noreturn__)); + +#ifndef OUTPUT +# define PRINT(X) +# define PRINTF (void) +#else +# define PRINT(X) puts(X) +# define PRINTF printf +#endif + +struct coro1 { + struct promise_type; + using handle_type = coro::coroutine_handle; + handle_type handle; + coro1 () : handle(0) {} + coro1 (handle_type _handle) + : handle(_handle) { + PRINT("Created coro1 object from handle"); + } + coro1 (const coro1 &) = delete; // no copying + coro1 (coro1 &&s) : handle(s.handle) { + s.handle = nullptr; + PRINT("coro1 mv ctor "); + } + coro1 &operator = (coro1 &&s) { + handle = s.handle; + s.handle = nullptr; + PRINT("coro1 op= "); + return *this; + } + ~coro1() { + PRINT("Destroyed coro1"); + if ( handle ) + handle.destroy(); + } + + struct suspend_never_prt { + bool await_ready() const noexcept { return true; } + void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp"); } + void await_resume() const noexcept { PRINT ("susp-never-resume");} + }; + + /* NOTE: this has a DTOR to test that pathway. */ + struct suspend_always_prt { + bool await_ready() const noexcept { return false; } + void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp"); } + void await_resume() const noexcept { PRINT ("susp-always-resume"); } + ~suspend_always_prt() { PRINT ("susp-always-DTOR"); } + }; + + struct promise_type { + int value; + promise_type() { PRINT ("Created Promise"); } + ~promise_type() { PRINT ("Destroyed Promise"); } + + auto get_return_object () { + PRINT ("get_return_object: handle from promise"); + return handle_type::from_promise (*this); + } + auto initial_suspend () { + PRINT ("get initial_suspend (always)"); + return suspend_always_prt{}; + } + auto final_suspend () { + PRINT ("get final_suspend (always)"); + return suspend_always_prt{}; + } + void return_value (int v) { + PRINTF ("return_value () %d\n",v); + value = v; + } + int get_value (void) { return value; } + // Placeholder to satisfy parser, not doing exceptions yet. + void unhandled_exception() { /*exit(1);*/ } + }; +}; + +struct coro1 +f () noexcept +{ + const int answer = 42; + PRINTF ("coro1: about to return %d\n", answer); + co_return answer; +} + +int main () +{ + PRINT ("main: create coro1"); + struct coro1 x = f (); + PRINT ("main: got coro1 - resuming"); + if (x.handle.done()) + abort(); + x.handle.resume(); + PRINT ("main: after resume"); + int y = x.handle.promise().get_value(); + if ( y != 42 ) + abort (); + if (!x.handle.done()) + { + PRINT ("main: apparently not done..."); + abort (); + //x.handle.resume(); + } + PRINT ("main: returning"); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/torture/local-var-1.C b/gcc/testsuite/g++.dg/coroutines/torture/local-var-1.C new file mode 100644 index 0000000..9dd7ab3 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/local-var-1.C @@ -0,0 +1,123 @@ +// { dg-do run } +#if __clang__ +# include +# include +#else +# include "../coro.h" +#endif + +namespace coro = std::experimental; + +// Test returning an int. +// We will use the promise to contain this to avoid having to include +// additional C++ headers. + +/* just to avoid cluttering dump files. */ +extern "C" int puts (const char *); +extern "C" int printf (const char *, ...); +extern "C" void abort (void) __attribute__((__noreturn__)); + +#ifndef OUTPUT +# define PRINT(X) +# define PRINTF (void) +#else +# define PRINT(X) puts(X) +# define PRINTF printf +#endif + +struct coro1 { + struct promise_type; + using handle_type = coro::coroutine_handle; + handle_type handle; + coro1 () : handle(0) {} + coro1 (handle_type _handle) + : handle(_handle) { + PRINT("Created coro1 object from handle"); + } + coro1 (const coro1 &) = delete; // no copying + coro1 (coro1 &&s) : handle(s.handle) { + s.handle = nullptr; + PRINT("coro1 mv ctor "); + } + coro1 &operator = (coro1 &&s) { + handle = s.handle; + s.handle = nullptr; + PRINT("coro1 op= "); + return *this; + } + ~coro1() { + PRINT("Destroyed coro1"); + if ( handle ) + handle.destroy(); + } + + struct suspend_never_prt { + bool await_ready() const noexcept { return true; } + void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp"); } + void await_resume() const noexcept { PRINT ("susp-never-resume");} + }; + + /* NOTE: this has a DTOR to test that pathway. */ + struct suspend_always_prt { + bool await_ready() const noexcept { return false; } + void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp"); } + void await_resume() const noexcept { PRINT ("susp-always-resume"); } + ~suspend_always_prt() { PRINT ("susp-always-DTOR"); } + }; + + struct promise_type { + int value; + promise_type() { PRINT ("Created Promise"); } + ~promise_type() { PRINT ("Destroyed Promise"); } + + auto get_return_object () { + PRINT ("get_return_object: handle from promise"); + return handle_type::from_promise (*this); + } + auto initial_suspend () { + PRINT ("get initial_suspend (always)"); + return suspend_always_prt{}; + } + auto final_suspend () { + PRINT ("get final_suspend (always)"); + return suspend_always_prt{}; + } + void return_value (int v) { + PRINTF ("return_value () %d\n",v); + value = v; + } + int get_value (void) { return value; } + // Placeholder to satisfy parser, not doing exceptions yet. + void unhandled_exception() { /*exit(1);*/ } + }; +}; + +struct coro1 +f (int x) noexcept +{ + int answer = x + 6132; + PRINTF ("coro1: about to return %d\n", answer); + co_return answer; +} + +int main () +{ + PRINT ("main: create coro1"); + struct coro1 x = f (42); + PRINT ("main: got coro1 - resuming"); + if (x.handle.done()) + abort(); + x.handle.resume(); + PRINT ("main: after resume"); + int y = x.handle.promise().get_value(); + if ( y != 6174 ) + abort (); + if (!x.handle.done()) + { + PRINT ("main: apparently not done..."); + abort (); + //x.handle.resume(); + } + PRINT ("main: returning"); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/torture/local-var-2.C b/gcc/testsuite/g++.dg/coroutines/torture/local-var-2.C new file mode 100644 index 0000000..886c913 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/local-var-2.C @@ -0,0 +1,135 @@ +// { dg-do run } +#if __clang__ +# include +# include +#else +# include "../coro.h" +#endif + +namespace coro = std::experimental; + +// Test returning an int. +// We will use the promise to contain this to avoid having to include +// additional C++ headers. + +/* just to avoid cluttering dump files. */ +extern "C" int puts (const char *); +extern "C" int printf (const char *, ...); +extern "C" void abort (void) __attribute__((__noreturn__)); + +#ifndef OUTPUT +# define PRINT(X) +# define PRINTF (void) +#else +# define PRINT(X) puts(X) +# define PRINTF printf +#endif + +struct coro1 { + struct promise_type; + using handle_type = coro::coroutine_handle; + handle_type handle; + coro1 () : handle(0) {} + coro1 (handle_type _handle) + : handle(_handle) { + PRINT("Created coro1 object from handle"); + } + coro1 (const coro1 &) = delete; // no copying + coro1 (coro1 &&s) : handle(s.handle) { + s.handle = nullptr; + PRINT("coro1 mv ctor "); + } + coro1 &operator = (coro1 &&s) { + handle = s.handle; + s.handle = nullptr; + PRINT("coro1 op= "); + return *this; + } + ~coro1() { + PRINT("Destroyed coro1"); + if ( handle ) + handle.destroy(); + } + + struct suspend_never_prt { + bool await_ready() const noexcept { return true; } + void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp"); } + void await_resume() const noexcept { PRINT ("susp-never-resume");} + }; + + /* NOTE: this has a DTOR to test that pathway. */ + struct suspend_always_prt { + bool await_ready() const noexcept { return false; } + void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp"); } + void await_resume() const noexcept { PRINT ("susp-always-resume"); } + ~suspend_always_prt() { PRINT ("susp-always-DTOR"); } + }; + + struct promise_type { + int value; + promise_type() { PRINT ("Created Promise"); } + ~promise_type() { PRINT ("Destroyed Promise"); } + + auto get_return_object () { + PRINT ("get_return_object: handle from promise"); + return handle_type::from_promise (*this); + } + auto initial_suspend () { + PRINT ("get initial_suspend (always)"); + return suspend_always_prt{}; + } + auto final_suspend () { + PRINT ("get final_suspend (always)"); + return suspend_always_prt{}; + } + void return_value (int v) { + PRINTF ("return_value () %d\n",v); + value = v; + } + int get_value (void) { return value; } + // Placeholder to satisfy parser, not doing exceptions yet. + void unhandled_exception() { /*exit(1);*/ } + }; +}; + +struct coro1 +f (int x) noexcept +{ + int y = x; + const int test = 20; + if (y > test) + { + int fred = y - 20; + PRINTF ("coro1: about to return %d\n", fred); + co_return fred; + } + else + { + PRINT ("coro1: about to return the answer\n"); + co_return y; + } + + co_return x; +} + +int main () +{ + PRINT ("main: create coro1"); + struct coro1 x = f (6194); + PRINT ("main: got coro1 - resuming"); + if (x.handle.done()) + abort(); + x.handle.resume(); + PRINT ("main: after resume"); + int y = x.handle.promise().get_value(); + if ( y != 6174 ) + abort (); + if (!x.handle.done()) + { + PRINT ("main: apparently not done..."); + abort (); + //x.handle.resume(); + } + PRINT ("main: returning"); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/torture/local-var-3.C b/gcc/testsuite/g++.dg/coroutines/torture/local-var-3.C new file mode 100644 index 0000000..53b7e71 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/local-var-3.C @@ -0,0 +1,153 @@ +// { dg-do run } +#if __clang__ +# include +# include +#else +# include "../coro.h" +#endif + +namespace coro = std::experimental; + +// Test modifying a local var and yielding several instances of it. + +/* just to avoid cluttering dump files. */ +extern "C" int puts (const char *); +extern "C" int printf (const char *, ...); +extern "C" void abort (void) __attribute__((__noreturn__)); + +#ifndef OUTPUT +# define PRINT(X) +# define PRINTF (void) +#else +# define PRINT(X) puts(X) +# define PRINTF printf +#endif + +struct coro1 { + struct promise_type; + using handle_type = coro::coroutine_handle; + handle_type handle; + coro1 () : handle(0) {} + coro1 (handle_type _handle) + : handle(_handle) { + PRINT("Created coro1 object from handle"); + } + coro1 (const coro1 &) = delete; // no copying + coro1 (coro1 &&s) : handle(s.handle) { + s.handle = nullptr; + PRINT("coro1 mv ctor "); + } + coro1 &operator = (coro1 &&s) { + handle = s.handle; + s.handle = nullptr; + PRINT("coro1 op= "); + return *this; + } + ~coro1() { + PRINT("Destroyed coro1"); + if ( handle ) + handle.destroy(); + } + + struct suspend_never_prt { + bool await_ready() const noexcept { return true; } + void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp"); } + void await_resume() const noexcept { PRINT ("susp-never-resume");} + }; + + /* NOTE: this has a DTOR to test that pathway. */ + struct suspend_always_prt { + bool await_ready() const noexcept { return false; } + void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp"); } + void await_resume() const noexcept { PRINT ("susp-always-resume"); } + ~suspend_always_prt() { PRINT ("susp-always-DTOR"); } + }; + + struct promise_type { + int value; + promise_type() { PRINT ("Created Promise"); } + ~promise_type() { PRINT ("Destroyed Promise"); } + + auto get_return_object () { + PRINT ("get_return_object: handle from promise"); + return handle_type::from_promise (*this); + } + auto initial_suspend () { + PRINT ("get initial_suspend (always)"); + return suspend_always_prt{}; + } + auto final_suspend () { + PRINT ("get final_suspend (always)"); + return suspend_always_prt{}; + } + void return_value (int v) { + PRINTF ("return_value () %d\n",v); + value = v; + } + auto yield_value (int v) { + PRINTF ("yield_value () %d and suspend always\n",v); + value = v; + return suspend_always_prt{}; + } + int get_value (void) { return value; } + // Placeholder to satisfy parser, not doing exceptions yet. + void unhandled_exception() { /*exit(1);*/ } + }; +}; + +struct coro1 +f (int start) noexcept +{ + int value = start; + PRINT ("f: about to yield start"); + co_yield start; + + value -= 31; + PRINT ("f: about to yield (value-31)"); + co_yield value; + + value += 6163; + PRINT ("f: about to return (value+6163)"); + co_return value; +} + +int main () +{ + PRINT ("main: create coro1"); + struct coro1 x = f (42); + PRINT ("main: got coro1 - resuming (1)"); + if (x.handle.done()) + abort(); + x.handle.resume(); + PRINT ("main: after resume (1)"); + int y = x.handle.promise().get_value(); + if ( y != 42 ) + abort (); + PRINT ("main: apparently got 42 - resuming (2)"); + if (x.handle.done()) + abort(); + x.handle.resume(); + PRINT ("main: after resume (2)"); + y = x.handle.promise().get_value(); + if ( y != 11 ) + abort (); + PRINT ("main: apparently got 11 - resuming (3)"); + if (x.handle.done()) + { + PRINT ("main: done?"); + abort(); + } + x.handle.resume(); + PRINT ("main: after resume (2) checking return"); + y = x.handle.promise().get_value(); + if ( y != 6174 ) + abort (); + PRINT ("main: apparently got 6174"); + if (!x.handle.done()) + { + PRINT ("main: apparently not done..."); + abort (); + } + PRINT ("main: returning"); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/torture/local-var-4.C b/gcc/testsuite/g++.dg/coroutines/torture/local-var-4.C new file mode 100644 index 0000000..5d4c0ff --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/local-var-4.C @@ -0,0 +1,162 @@ +// { dg-do run } +#if __clang__ +# include +# include +#else +# include "../coro.h" +#endif + +namespace coro = std::experimental; + +// Test modifying a local var and yielding several instances of it. + +/* just to avoid cluttering dump files. */ +extern "C" int puts (const char *); +extern "C" int printf (const char *, ...); +extern "C" void abort (void) __attribute__((__noreturn__)); + +#ifndef OUTPUT +# define PRINT(X) +# define PRINTF (void) +#else +# define PRINT(X) puts(X) +# define PRINTF printf +#endif + +struct coro1 { + struct promise_type; + using handle_type = coro::coroutine_handle; + handle_type handle; + coro1 () : handle(0) {} + coro1 (handle_type _handle) + : handle(_handle) { + PRINT("Created coro1 object from handle"); + } + coro1 (const coro1 &) = delete; // no copying + coro1 (coro1 &&s) : handle(s.handle) { + s.handle = nullptr; + PRINT("coro1 mv ctor "); + } + coro1 &operator = (coro1 &&s) { + handle = s.handle; + s.handle = nullptr; + PRINT("coro1 op= "); + return *this; + } + ~coro1() { + PRINT("Destroyed coro1"); + if ( handle ) + handle.destroy(); + } + + struct suspend_never_prt { + bool await_ready() const noexcept { return true; } + void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp"); } + void await_resume() const noexcept { PRINT ("susp-never-resume");} + }; + + /* NOTE: this has a DTOR to test that pathway. */ + struct suspend_always_prt { + bool await_ready() const noexcept { return false; } + void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp"); } + void await_resume() const noexcept { PRINT ("susp-always-resume"); } + ~suspend_always_prt() { PRINT ("susp-always-DTOR"); } + }; + + struct promise_type { + int value; + promise_type() { PRINT ("Created Promise"); } + ~promise_type() { PRINT ("Destroyed Promise"); } + + auto get_return_object () { + PRINT ("get_return_object: handle from promise"); + return handle_type::from_promise (*this); + } + auto initial_suspend () { + PRINT ("get initial_suspend (always)"); + return suspend_always_prt{}; + } + auto final_suspend () { + PRINT ("get final_suspend (always)"); + return suspend_always_prt{}; + } + void return_value (int v) { + PRINTF ("return_value () %d\n",v); + value = v; + } + auto yield_value (int v) { + PRINTF ("yield_value () %d and suspend always\n",v); + value = v; + return suspend_always_prt{}; + } + int get_value (void) { return value; } + // Placeholder to satisfy parser, not doing exceptions yet. + void unhandled_exception() { /*exit(1);*/ } + }; +}; + +struct coro1 +f (int start) noexcept +{ + int value = start; + { + int value = start + 5; + { + int value = start + 20; + } + { + int value = start + 1; + PRINT ("f: about to yield start"); + co_yield value; + } + } + + value -= 31; + PRINT ("f: about to yield (value-31)"); + co_yield value; + + value += 6163; + PRINT ("f: about to return (value+6163)"); + co_return value; +} + +int main () +{ + PRINT ("main: create coro1"); + struct coro1 x = f (42); + PRINT ("main: got coro1 - resuming (1)"); + if (x.handle.done()) + abort(); + x.handle.resume(); + PRINT ("main: after resume (1)"); + int y = x.handle.promise().get_value(); + if ( y != 43 ) + abort (); + PRINT ("main: apparently got 42 - resuming (2)"); + if (x.handle.done()) + abort(); + x.handle.resume(); + PRINT ("main: after resume (2)"); + y = x.handle.promise().get_value(); + if ( y != 11 ) + abort (); + PRINT ("main: apparently got 11 - resuming (3)"); + if (x.handle.done()) + { + PRINT ("main: done?"); + abort(); + } + x.handle.resume(); + PRINT ("main: after resume (2) checking return"); + y = x.handle.promise().get_value(); + if ( y != 6174 ) + abort (); + PRINT ("main: apparently got 6174"); + if (!x.handle.done()) + { + PRINT ("main: apparently not done..."); + abort (); + } + PRINT ("main: returning"); + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/torture/mid-suspend-destruction-0.C b/gcc/testsuite/g++.dg/coroutines/torture/mid-suspend-destruction-0.C new file mode 100644 index 0000000..6e68688 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/mid-suspend-destruction-0.C @@ -0,0 +1,127 @@ +// { dg-do run } +// { dg-output "main: returning\n" } +// { dg-output "Destroyed coro1\n" } +// { dg-output "Destroyed suspend_always_prt\n" } +// { dg-output "Destroyed Promise\n" } + +// Check that we still get the right DTORs run when we let a suspended coro +// go out of scope. + +#if __clang__ +# include +# include +#else +# include "../coro.h" +#endif + +namespace coro = std::experimental; + +// GRO differs from the eventual return type. + +/* just to avoid cluttering dump files. */ +extern "C" int puts (const char *); +extern "C" int printf (const char *, ...); +extern "C" void abort (void) __attribute__((__noreturn__)); + +#ifndef OUTPUT +# define PRINT(X) +#else +# define PRINT(X) puts(X) +#endif + +struct coro1 { + struct promise_type; + using handle_type = coro::coroutine_handle; + handle_type handle; + coro1 () : handle(0) {} + coro1 (handle_type _handle) + : handle(_handle) { + PRINT("Created coro1 object from handle"); + } + coro1 (const coro1 &) = delete; // no copying + coro1 (coro1 &&s) : handle(s.handle) { + s.handle = nullptr; + PRINT("coro1 mv ctor "); + } + coro1 &operator = (coro1 &&s) { + handle = s.handle; + s.handle = nullptr; + PRINT("coro1 op= "); + return *this; + } + ~coro1() { + printf ("Destroyed coro1\n"); + if ( handle ) + handle.destroy(); + } + + struct suspend_never_prt { + bool await_ready() const noexcept { return true; } + void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp");} + void await_resume() const noexcept { PRINT ("susp-never-resume");} + ~suspend_never_prt() {}; + }; + + struct suspend_always_prt { + bool await_ready() const noexcept { return false; } + void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp");} + void await_resume() const noexcept { PRINT ("susp-always-resume");} + ~suspend_always_prt() { printf ("Destroyed suspend_always_prt\n"); } + }; + + struct promise_type { + promise_type() { PRINT ("Created Promise"); } + ~promise_type() { printf ("Destroyed Promise\n"); } + + auto get_return_object () { + PRINT ("get_return_object: handle from promise"); + return handle_type::from_promise (*this); + } + auto initial_suspend () { + PRINT ("get initial_suspend (always)"); + return suspend_always_prt{}; + } + auto final_suspend () { + PRINT ("get final_suspend (always)"); + return suspend_always_prt{}; + } + void return_void () { + PRINT ("return_void ()"); + } + // Placeholder to satisfy parser, not doing exceptions yet. + void unhandled_exception() { /*exit(1);*/ } + }; +}; + +struct coro1 +f () noexcept +{ + PRINT ("coro1: about to return"); + co_return; +} + +int main () +{ + PRINT ("main: create coro1"); + struct coro1 x = f (); + PRINT ("main: got coro1 - resuming"); + if (x.handle.done()) + { + PRINT ("main: f() should be suspended, says it's done"); + abort(); + } + +#if __has_builtin (__builtin_coro_suspended) + if (! x.handle.suspended_p()) + { + PRINT ("main: f() should be suspended, but says it isn't"); + abort(); + } +#endif + + /* We are suspended... so let everything out of scope and therefore + destroy it. */ + + puts ("main: returning"); + return 0; +}