From patchwork Wed Jul 31 10:54:54 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: =?utf-8?q?Arsen_Arsenovi=C4=87?= X-Patchwork-Id: 1967055 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=aarsen.me header.i=@aarsen.me header.a=rsa-sha256 header.s=MBO0001 header.b=Q1S6ucE/; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=2620:52:3:1:0:246e:9693:128c; helo=server2.sourceware.org; envelope-from=gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=patchwork.ozlabs.org) Received: from server2.sourceware.org (server2.sourceware.org [IPv6:2620:52:3:1:0:246e:9693:128c]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4WYq3j5lFsz1yYq for ; Wed, 31 Jul 2024 21:04:16 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id A493F3858432 for ; Wed, 31 Jul 2024 11:04:14 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mout-p-103.mailbox.org (mout-p-103.mailbox.org [80.241.56.161]) by sourceware.org (Postfix) with ESMTPS id A55643858C56 for ; Wed, 31 Jul 2024 11:03:45 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org A55643858C56 Authentication-Results: sourceware.org; dmarc=pass (p=reject dis=none) header.from=aarsen.me Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=aarsen.me ARC-Filter: OpenARC Filter v1.0.0 sourceware.org A55643858C56 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=80.241.56.161 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1722423829; cv=none; b=ey5wEbQwNNWWltJkXk0JbOq2CreVzcJCS9TgDsNFZE433tZh+aoXfXjJ4001X30RiGS7nX2Uu89JX3HRkS/0CN4Gqll7VssdtTFeYwar8XeNzkCjwRYJh8uNg4O4ncsP/ZN6T5xr1N/e39QhlAr+7ajS69waXZ0A34+dkVFkNxw= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1722423829; c=relaxed/simple; bh=5rflvZJWT7h4l/xyW8kfFPeQGEPei9+T8JzZMwASf0Q=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=YVsSr/SWJYz0EKD8Xluu9EmCp3VWK3ZfIMPWcuyoVaMphBZTHnJPgMrK/wEO2DpGB7WLdwVIVzKxzUqNuCJ85TwUl5jLkaDqAnN+szOICzqp+3Mem4IXCdmVHrNO39qwOONP4lWPkVM5bxSdVIzh6LwZPKNjGLeS/yi6LSitzAQ= ARC-Authentication-Results: i=1; server2.sourceware.org Received: from smtp202.mailbox.org (smtp202.mailbox.org [10.196.197.202]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by mout-p-103.mailbox.org (Postfix) with ESMTPS id 4WYq323SV4z9sSl; Wed, 31 Jul 2024 13:03:42 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=aarsen.me; s=MBO0001; t=1722423822; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding; bh=hV5Ncv+GuSW57GnAXV5AjCRttqth4A6evxpVDKBUwgk=; b=Q1S6ucE/JaTCpVefmrZv/nnPY3ratMNNHB34Lh8I/AZuxTsMnxJ++iPmGQ0hcfT3XnBtWh sCbxH6VPdsHx7WaNFYo5JhwsfoSgJs9knzpo+Qfk1lJBj04zqs5bNaD0/J7joeTpHW2/h+ eEBTBhKCJKxCcImDk2XVdxqEHJM4K8Lxw+YhPUGBdXFN26CG9FeH03Mb5rNC/Q2JhnOD7K 8zd8mPlEB1olewVcRDqwJC4e7gZs9blzWLNkNHCfZcaF98G++Zpt3Y5b6WH/Nvhx8HJNuX GYa/HV2gxUeSZ49XrkzgngqcIo0zTYII5lsv4XsNv2UqvvmwMs3nTG6+kaVQtQ== From: =?utf-8?q?Arsen_Arsenovi=C4=87?= To: gcc-patches@gcc.gnu.org Cc: Jason Merrill , Iain Sandoe , =?utf-8?q?Arsen_Arsenovi=C4=87?= Subject: [PATCH] c++/coroutines: only defer expanding co_{await, return, yield} if dependent [PR112341] Date: Wed, 31 Jul 2024 12:54:54 +0200 Message-ID: <20240731110326.3615564-1-arsen@aarsen.me> MIME-Version: 1.0 X-Spam-Status: No, score=-10.8 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_INFOUSMEBIZ, KAM_SHORT, RCVD_IN_DNSWL_LOW, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org Tested on x86_64-pc-linux-gnu. OK for trunk? TIA, have a lovely day. ---------- >8 ---------- By doing so, we can get diagnostics in template decls when we know we can. For instance, in the following: awaitable g(); template task f() { co_await g(); co_yield 1; co_return "foo"; } ... the coroutine promise type in each statement is always std::coroutine_handle::promise_type, and all of the operands are not type-dependent, so we can always compute the resulting types (and expected types) of these expressions and statements. Also, when we do not know the type of the CO_AWAIT_EXPR or CO_YIELD_EXPR, we now return NULL_TREE as the type rather than unknown_type_node. This is more correct, since the type is not unknown, it just isn't determined yet. This also means we can remove the CO_AWAIT_EXPR and CO_YIELD_EXPR special-cases from type_dependent_expression_p. PR c++/112341 - error: insufficient contextual information to determine type on co_await result in function template gcc/cp/ChangeLog: PR c++/112341 * coroutines.cc (struct coroutine_info): Also cache the traits type. (ensure_coro_initialized): New function. Makes sure we have initialized the coroutine state successfully, or informs the caller should it fail to do so. Extracted from coro_promise_type_found_p. (coro_get_traits_class): New function. Gets the (cached) coroutine traits type for a given coroutine. Extracted from coro_promise_type_found_p and refactored to cache the result. (coro_promise_type_found_p): Use the two functions above. (build_template_co_await_expr): New function. Builds a CO_AWAIT_EXPR representing a CO_AWAIT_EXPR in a template declaration. (build_co_await): Use the above if processing_template_decl, and give it a propert type. (defer_expansion_p): New function. Returns true iff its argument is a type-dependent expression OR the current functions traits class is type dependent. (finish_co_await_expr): Defer expansion only in the case defer_expasnion_p returns true. (finish_co_yield_expr): Ditto. (finish_co_return_stmt): Ditto. * pt.cc (type_dependent_expression_p): Do not treat CO_AWAIT/CO_YIELD specially. gcc/testsuite/ChangeLog: PR c++/112341 * g++.dg/coroutines/pr112341-2.C: New test. * g++.dg/coroutines/pr112341-3.C: New test. * g++.dg/coroutines/torture/co-yield-03-tmpl-nondependent.C: New test. * g++.dg/coroutines/pr112341.C: New test. --- gcc/cp/coroutines.cc | 142 ++++++++++++++---- gcc/cp/pt.cc | 5 - gcc/testsuite/g++.dg/coroutines/pr112341-2.C | 25 +++ gcc/testsuite/g++.dg/coroutines/pr112341-3.C | 65 ++++++++ gcc/testsuite/g++.dg/coroutines/pr112341.C | 21 +++ .../torture/co-yield-03-tmpl-nondependent.C | 140 +++++++++++++++++ 6 files changed, 361 insertions(+), 37 deletions(-) create mode 100644 gcc/testsuite/g++.dg/coroutines/pr112341-2.C create mode 100644 gcc/testsuite/g++.dg/coroutines/pr112341-3.C create mode 100644 gcc/testsuite/g++.dg/coroutines/pr112341.C create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-03-tmpl-nondependent.C diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc index 127a1c06b56e..9494cb499454 100644 --- a/gcc/cp/coroutines.cc +++ b/gcc/cp/coroutines.cc @@ -85,6 +85,7 @@ struct GTY((for_user)) coroutine_info tree actor_decl; /* The synthesized actor function. */ tree destroy_decl; /* The synthesized destroy function. */ tree promise_type; /* The cached promise type for this function. */ + tree traits_type; /* The cached traits type for this function. */ tree handle_type; /* The cached coroutine handle for this function. */ tree self_h_proxy; /* A handle instance that is used as the proxy for the one that will eventually be allocated in the coroutine @@ -429,11 +430,12 @@ find_promise_type (tree traits_class) return promise_type; } +/* Perform initialization of the coroutine processor state, if not done + before. */ + static bool -coro_promise_type_found_p (tree fndecl, location_t loc) +ensure_coro_initialized (location_t loc) { - gcc_assert (fndecl != NULL_TREE); - if (!coro_initialized) { /* Trees we only need to create once. @@ -466,6 +468,33 @@ coro_promise_type_found_p (tree fndecl, location_t loc) coro_initialized = true; } + return true; +} + +/* Try to get the coroutine traits class. */ +static tree +coro_get_traits_class (tree fndecl, location_t loc) +{ + gcc_assert (fndecl != NULL_TREE); + + if (!ensure_coro_initialized (loc)) + /* We can't continue. */ + return error_mark_node; + + coroutine_info *coro_info = get_or_insert_coroutine_info (fndecl); + auto& traits_type = coro_info->traits_type; + if (!traits_type) + traits_type = instantiate_coro_traits (fndecl, loc); + return traits_type; +} + +static bool +coro_promise_type_found_p (tree fndecl, location_t loc) +{ + gcc_assert (fndecl != NULL_TREE); + + if (!ensure_coro_initialized (loc)) + return false; /* Save the coroutine data on the side to avoid the overhead on every function decl tree. */ @@ -480,7 +509,7 @@ coro_promise_type_found_p (tree fndecl, location_t loc) /* Get the coroutine traits template class instance for the function signature we have - coroutine_traits */ - tree templ_class = instantiate_coro_traits (fndecl, loc); + tree templ_class = coro_get_traits_class (fndecl, loc); /* Find the promise type for that. */ coro_info->promise_type = find_promise_type (templ_class); @@ -914,6 +943,19 @@ coro_diagnose_throwing_final_aw_expr (tree expr) return coro_diagnose_throwing_fn (fn); } +/* Build a co_await suitable for later expansion. */ + +tree +build_template_co_await_expr (location_t kw, tree expr, tree type, tree kind) +{ + tree aw_expr = build5_loc (kw, CO_AWAIT_EXPR, type, expr, + NULL_TREE, NULL_TREE, NULL_TREE, + kind); + TREE_SIDE_EFFECTS (aw_expr) = true; + return aw_expr; +} + + /* This performs [expr.await] bullet 3.3 and validates the interface obtained. It is also used to build the initial and final suspend points. @@ -922,11 +964,24 @@ coro_diagnose_throwing_final_aw_expr (tree expr) A, the original yield/await expr, is found at source location LOC. We will be constructing a CO_AWAIT_EXPR for a suspend point of one of - the four suspend_point_kind kinds. This is indicated by SUSPEND_KIND. */ + the four suspend_point_kind kinds. This is indicated by SUSPEND_KIND. + + In the case that we're processing a template declaration, we can't save + actual awaiter expressions as the frame type will differ between + instantiations, but we can generate them to type-check them and compute the + resulting expression type. In those cases, we will return a + template-appropriate CO_AWAIT_EXPR and throw away the rest of the results. + Such an expression requires the original co_await operand unaltered. Pass + it as ORIG_OPERAND. If it is the same as 'a', you can pass error_mark_node + (the default) to use 'a' as the value. */ static tree -build_co_await (location_t loc, tree a, suspend_point_kind suspend_kind) +build_co_await (location_t loc, tree a, suspend_point_kind suspend_kind, + tree orig_operand = error_mark_node) { + if (orig_operand == error_mark_node) + orig_operand = a; + /* Try and overload of operator co_await, .... */ tree o; if (MAYBE_CLASS_TYPE_P (TREE_TYPE (a))) @@ -1131,11 +1186,13 @@ build_co_await (location_t loc, tree a, suspend_point_kind suspend_kind) if (REFERENCE_REF_P (e_proxy)) e_proxy = TREE_OPERAND (e_proxy, 0); + tree awrs_type = TREE_TYPE (TREE_TYPE (awrs_func)); + tree suspend_kind_cst = build_int_cst (integer_type_node, + (int) suspend_kind); tree await_expr = build5_loc (loc, CO_AWAIT_EXPR, - TREE_TYPE (TREE_TYPE (awrs_func)), + awrs_type, a, e_proxy, o, awaiter_calls, - build_int_cst (integer_type_node, - (int) suspend_kind)); + suspend_kind_cst); TREE_SIDE_EFFECTS (await_expr) = true; if (te) { @@ -1144,7 +1201,26 @@ build_co_await (location_t loc, tree a, suspend_point_kind suspend_kind) await_expr = te; } SET_EXPR_LOCATION (await_expr, loc); - return convert_from_reference (await_expr); + + if (processing_template_decl) + /* FIXME: Can we reuse the results above rather than re-make at template + instantiating? The "obvious" solution of checking if we have a fully + built expression and recurring into the operands and 'e' call + expressions seems to cause PR103868 again. */ + return build_template_co_await_expr (loc, orig_operand, awrs_type, + suspend_kind_cst); + return convert_from_reference (await_expr); +} + +/* Returns true iff EXPR or the current functions traits class are dependent. + In those cases, we can't decide what the type of our expressions is, so we + defer expansion entirely. */ + +static bool +defer_expansion_p (tree expr, location_t loc) +{ + return type_dependent_expression_p (expr) + || dependent_type_p (coro_get_traits_class (current_function_decl, loc)); } tree @@ -1166,20 +1242,17 @@ finish_co_await_expr (location_t kw, tree expr) extraneous warnings during substitution. */ suppress_warning (current_function_decl, OPT_Wreturn_type); - /* Defer expansion when we are processing a template. - FIXME: If the coroutine function's type is not dependent, and the operand - is not dependent, we should determine the type of the co_await expression - using the DEPENDENT_EXPR wrapper machinery. That allows us to determine - the subexpression type, but leave its operand unchanged and then - instantiate it later. */ - if (processing_template_decl) - { - tree aw_expr = build5_loc (kw, CO_AWAIT_EXPR, unknown_type_node, expr, - NULL_TREE, NULL_TREE, NULL_TREE, - integer_zero_node); - TREE_SIDE_EFFECTS (aw_expr) = true; - return aw_expr; - } + /* Defer expansion when we are processing a template, unless the traits type + and expression would not be dependent. In the case that the types are + not dependent but we are processing a template declaration, we will do + most of the computation but throw away the results (except for the + await_resume type). Otherwise, if our co_await is type-dependent + (i.e. the promise type or operand type is dependent), we can't do much, + and just return early with a NULL_TREE type (indicating that we cannot + compute the type yet). */ + if (defer_expansion_p (expr, kw)) + return build_template_co_await_expr (kw, expr, NULL_TREE, + integer_zero_node); /* We must be able to look up the "await_transform" method in the scope of the promise type, and obtain its return type. */ @@ -1216,7 +1289,7 @@ finish_co_await_expr (location_t kw, tree expr) } /* Now we want to build co_await a. */ - return build_co_await (kw, a, CO_AWAIT_SUSPEND_POINT); + return build_co_await (kw, a, CO_AWAIT_SUSPEND_POINT, expr); } /* Take the EXPR given and attempt to build: @@ -1243,10 +1316,15 @@ finish_co_yield_expr (location_t kw, tree expr) extraneous warnings during substitution. */ suppress_warning (current_function_decl, OPT_Wreturn_type); - /* Defer expansion when we are processing a template; see FIXME in the - co_await code. */ - if (processing_template_decl) - return build2_loc (kw, CO_YIELD_EXPR, unknown_type_node, expr, NULL_TREE); + /* Defer expansion when we are processing a template; see note in + finish_co_await_expr. Also note that a yield is equivalent to + + co_await p.yield_value(EXPR) + + If either p or EXPR are type-dependent, then the whole expression is + certainly type-dependent, and we can't proceed. */ + if (defer_expansion_p (expr, kw)) + return build2_loc (kw, CO_YIELD_EXPR, NULL_TREE, expr, NULL_TREE); if (!coro_promise_type_found_p (current_function_decl, kw)) /* We must be able to look up the "yield_value" method in the scope of @@ -1327,9 +1405,9 @@ finish_co_return_stmt (location_t kw, tree expr) && check_for_bare_parameter_packs (expr)) return error_mark_node; - /* Defer expansion when we are processing a template; see FIXME in the - co_await code. */ - if (processing_template_decl) + /* Defer expansion when we must and are processing a template; see note in + finish_co_await_expr. */ + if (defer_expansion_p (expr, kw)) { /* co_return expressions are always void type, regardless of the expression type. */ diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index 39f7e8a4e688..77fa5907c3d3 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -28651,11 +28651,6 @@ type_dependent_expression_p (tree expression) if (TREE_CODE (expression) == SCOPE_REF) return false; - /* CO_AWAIT/YIELD_EXPR with unknown type is always dependent. */ - if (TREE_CODE (expression) == CO_AWAIT_EXPR - || TREE_CODE (expression) == CO_YIELD_EXPR) - return true; - if (BASELINK_P (expression)) { if (BASELINK_OPTYPE (expression) diff --git a/gcc/testsuite/g++.dg/coroutines/pr112341-2.C b/gcc/testsuite/g++.dg/coroutines/pr112341-2.C new file mode 100644 index 000000000000..54c7d851ae8d --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/pr112341-2.C @@ -0,0 +1,25 @@ +// https://gcc.gnu.org/PR112341 +#include +struct A { int j; }; +struct B { + bool await_ready(); + bool await_suspend(std::coroutine_handle<>); + A await_resume(); +}; +struct C { + struct promise_type { + std::suspend_always initial_suspend(); + std::suspend_always final_suspend() noexcept; + B yield_value(auto); + void unhandled_exception(); + C get_return_object(); + void return_value(A); + }; +}; +template +C f() { + // Make sure we verify types we can still. + (co_await B()).i; // { dg-error "'struct A' has no member named 'i'" } + (co_yield 123).i; // { dg-error "'struct A' has no member named 'i'" } + co_return 123; // { dg-error "cannot convert 'int' to 'A'" } +} diff --git a/gcc/testsuite/g++.dg/coroutines/pr112341-3.C b/gcc/testsuite/g++.dg/coroutines/pr112341-3.C new file mode 100644 index 000000000000..79beab88416a --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/pr112341-3.C @@ -0,0 +1,65 @@ +// https://gcc.gnu.org/PR112341 +#include +struct A { int j; }; +struct B { + bool await_ready(); + bool await_suspend(std::coroutine_handle<>); + A await_resume(); +}; +struct C { + struct promise_type { + std::suspend_always initial_suspend(); + std::suspend_always final_suspend() noexcept; + void unhandled_exception(); + C get_return_object(); + void return_void(); + std::suspend_never yield_value(int x); + }; +}; + +// Similar to the above, but with a template yield_value and return_value. +struct D { + struct promise_type { + std::suspend_always initial_suspend(); + std::suspend_always final_suspend() noexcept; + void unhandled_exception(); + D get_return_object(); + void return_value(auto); + std::suspend_never yield_value(auto); + }; +}; + +template +C f() { + co_return; +} + +template +C g() +{ + co_yield 123; +} + +template +D f1() { + co_return 123; +} + +template +D g1() +{ + co_yield 123; +} + +void +g() { + f(); + f(); + g(); + g(); + + f1(); + f1(); + g1(); + g1(); +} diff --git a/gcc/testsuite/g++.dg/coroutines/pr112341.C b/gcc/testsuite/g++.dg/coroutines/pr112341.C new file mode 100644 index 000000000000..ffb1fec87c02 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/pr112341.C @@ -0,0 +1,21 @@ +// https://gcc.gnu.org/PR112341 +#include +struct A { int j; }; +struct B { + bool await_ready(); + bool await_suspend(std::coroutine_handle<>); + A await_resume(); +}; +struct C { + struct promise_type { + std::suspend_always initial_suspend(); + std::suspend_always final_suspend() noexcept; + void unhandled_exception(); + C get_return_object(); + void return_void(); + B yield_value(auto) { return {}; } + }; +}; +C f1(auto) { (co_await B()).j; } +C f2(auto) { (co_yield 0).j; } +void g() { f1(0); f2(0); } diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-yield-03-tmpl-nondependent.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-03-tmpl-nondependent.C new file mode 100644 index 000000000000..8e91d9557d2f --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-03-tmpl-nondependent.C @@ -0,0 +1,140 @@ +// { dg-do run } + +// Test co_yield in templated code where the promise type is not dependent. + +#include "../coro.h" + +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 () noexcept { + PRINT ("get final_suspend (always)"); + return suspend_always_prt{}; + } + + void return_value (T v) { + PRINTF ("return_value () %lf\n", (double)v); + value = v; + } + + auto yield_value (T v) { + PRINTF ("yield_value () %lf and suspend always\n", (double)v); + value = v; + return suspend_always_prt{}; + } + + T get_value (void) { return value; } + + void unhandled_exception() { PRINT ("** unhandled exception"); } + }; + + using handle_type = coro::coroutine_handle; + 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 (int) 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; +}