From patchwork Fri Mar 20 14:54:07 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Iain Sandoe X-Patchwork-Id: 1258973 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=8.43.85.97; helo=sourceware.org; envelope-from=gcc-patches-bounces@gcc.gnu.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=sandoe-acoustics.co.uk Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=bt1383820pop.onmicrosoft.com header.i=@bt1383820pop.onmicrosoft.com header.a=rsa-sha256 header.s=selector2-bt1383820pop-onmicrosoft-com header.b=iXlpWPZK; dkim-atps=neutral Received: from sourceware.org (server2.sourceware.org [8.43.85.97]) (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 ozlabs.org (Postfix) with ESMTPS id 48kRcv44YHz9sRf for ; Sat, 21 Mar 2020 01:54:21 +1100 (AEDT) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id AB558387703A; Fri, 20 Mar 2020 14:54:16 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from EUR04-VI1-obe.outbound.protection.outlook.com (mail-eopbgr80087.outbound.protection.outlook.com [40.107.8.87]) by sourceware.org (Postfix) with ESMTPS id B72CB385F024 for ; Fri, 20 Mar 2020 14:54:11 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org B72CB385F024 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=sandoe-acoustics.co.uk Authentication-Results: sourceware.org; spf=none smtp.mailfrom=developer@sandoe-acoustics.co.uk ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=H80V/a3eEQndcR/XkeUFYDdkaXpVWQhdw3PGAroohR7brtfZQskTfwxolQFkLuA1CjjfIS93fSVE/oWNRV7EKtC3hgYxZ8AyBwEikFBFlVZYm1HoNpPfaCHWWFKBSum6tiBR4flY6sLkwuKpjs5P+h+qg/fn/fB9RWd4aCeONalv/UtZp1kXmwvyCpzL2yTPlpRJjHKuNWilyBfq2rh+jKO7i0T27fuCVWXyDafHd16QpH+VFfre7uZfnx6cwlDRvKVaRL9uvRbUIS+TABOaogUeb+PvpjCL0Cpgewh11uyeIntsge69Hob2OfmOq0yBl0DWiwYxb0X8XMvJSgyVIA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=kvRG/9xfCLFMbauT6IvgrZYpSZyGFdgDQ4Jz4Pg0Mf4=; b=Ns2UDWIk830QwY3Ys8zqd8Vv9iKbTTZJKs4dtoGhAwAVs4U8A9LkzxlzcQtJjvZYC92k3C/HXiJPt0GioMEI4qeOYAfeRyS5WleWE+AJ0M1aaGKmCF4mXjIg1FdQIvyvpaMg6ZozZ6rlqZHLBYKL8ME+P1uZcb+A3z+9AwghPpDlrdhG4ZoPCqjRbor92pAh2U9660m1UIQFZ+02UNp7DVFQJKWKnY8MKGFLgrSmiYlTrSpsMbDV6t0Ct/oo6PNtPuBInEeJSmU2hBB2465/elxyvA98Xr9UyxXPaq25vpMwbxO39bzRpZs5fnY4wsciOIc63JKOyA3Gr+fS/J+nrg== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=sandoe-acoustics.co.uk; dmarc=pass action=none header.from=sandoe-acoustics.co.uk; dkim=pass header.d=sandoe-acoustics.co.uk; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bt1383820pop.onmicrosoft.com; s=selector2-bt1383820pop-onmicrosoft-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=kvRG/9xfCLFMbauT6IvgrZYpSZyGFdgDQ4Jz4Pg0Mf4=; b=iXlpWPZKeaPmqFP/R8AGCc7XgTXsMuZ6srDC17SoIzBFsqyLQOt7hGOqjF6MHIye1oILShzFB6hZ+E3NGL9KaQN01oyrAGLMdW5o6NU2dOHsWoAm3xZT0pCRtJQEQr2IMwo9C3kjxitQiCt6kYI6t3XqE/178ov6q8pAU4JF/vY= Authentication-Results: spf=none (sender IP is ) smtp.mailfrom=developer@sandoe-acoustics.co.uk; Received: from AM0PR02MB5603.eurprd02.prod.outlook.com (10.255.31.18) by AM0PR02MB4257.eurprd02.prod.outlook.com (20.177.108.205) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.2835.18; Fri, 20 Mar 2020 14:54:09 +0000 Received: from AM0PR02MB5603.eurprd02.prod.outlook.com ([fe80::9d77:31a:d638:bbf1]) by AM0PR02MB5603.eurprd02.prod.outlook.com ([fe80::9d77:31a:d638:bbf1%4]) with mapi id 15.20.2814.025; Fri, 20 Mar 2020 14:54:09 +0000 From: Iain Sandoe Subject: [PATCH] coroutines: Implement n4849 changes to exception handling. Message-Id: <05C4C6DA-FBE2-4AB1-9037-AA742BE15FB6@sandoe-acoustics.co.uk> Date: Fri, 20 Mar 2020 14:54:07 +0000 To: gcc-patches@gcc.gnu.org X-Mailer: Apple Mail (2.3273) X-ClientProxiedBy: LNXP265CA0071.GBRP265.PROD.OUTLOOK.COM (2603:10a6:600:5d::35) To AM0PR02MB5603.eurprd02.prod.outlook.com (2603:10a6:208:163::18) MIME-Version: 1.0 X-MS-Exchange-MessageSentRepresentingType: 1 Received: from [192.168.1.212] (81.138.1.83) by LNXP265CA0071.GBRP265.PROD.OUTLOOK.COM (2603:10a6:600:5d::35) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.2835.15 via Frontend Transport; Fri, 20 Mar 2020 14:54:08 +0000 X-Mailer: Apple Mail (2.3273) X-Originating-IP: [81.138.1.83] X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-Correlation-Id: 0f87d093-01f0-43d7-eae3-08d7ccde8b21 X-MS-TrafficTypeDiagnostic: AM0PR02MB4257: X-Microsoft-Antispam-PRVS: X-MS-Oob-TLC-OOBClassifiers: OLM:389; X-Forefront-PRVS: 03484C0ABF X-Forefront-Antispam-Report: SFV:NSPM; SFS:(10009020)(366004)(396003)(376002)(39830400003)(136003)(346002)(199004)(66476007)(66556008)(16576012)(5660300002)(36756003)(26005)(186003)(8936002)(508600001)(16526019)(6916009)(6486002)(66946007)(8676002)(52116002)(316002)(956004)(2616005)(86362001)(4326008)(81156014)(81166006)(33656002)(2906002)(30864003); DIR:OUT; SFP:1101; SCL:1; SRVR:AM0PR02MB4257; H:AM0PR02MB5603.eurprd02.prod.outlook.com; FPR:; SPF:None; LANG:en; PTR:InfoNoRecords; A:1; Received-SPF: None (protection.outlook.com: sandoe-acoustics.co.uk does not designate permitted sender hosts) X-MS-Exchange-SenderADCheck: 1 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: JEqV56BQlflxFFuuzLzYs+3DywpQ4s3HN2e9BDfxdHvdz0Qua5tOgt3Kiin63c1RXEECaV8ZIdEgzXU0l/aktxsJAreAC1x1YGcpyoj7/AbAceGqQITaxlMFNb4EREp5FmxOWuiHeJn42PsJqoT+gjV5ZW//5KAETmHbNvHzio36RI2b5bjQCkgm5D6tyV6dWOnfr9zITUZ7lXhCt4H3RG/hmDghaRFUShH4DIQrvI+Y9YgGAk+1VHvHz7MN+7kCeyeaAfX7BzIVhuPFEOsjLr0Ox0fwOm54kveQ9PEGXHP6VUExnl+BB5Y2i484uNEL1Rx7aBLaMzM+i+Oo2pS9orO9IbiR5EziIE/aaKE+czv9wQO+eCkaiHv6MWa5V0CyGc1Uq+DpfM6LntI5Llj5SxP4h+kldG6CVN9/YSSJOEPdmkDqR0nBKEyw6NhLj2QP X-MS-Exchange-AntiSpam-MessageData: ww/QAqjUbESLi3SoNgExzlQ2S0FBLaXWD68OsYBVwCGAeYevzuQU5jrJ6WxAAv3Fs2mJZRm+JpBVwCqGSnHD8jMyNwGhrp7vZuKbuZFMThwEgHERkbLmamGwCDcvvQYOngsvau+xGJep54fB6JqBew== X-OriginatorOrg: sandoe-acoustics.co.uk X-MS-Exchange-CrossTenant-Network-Message-Id: 0f87d093-01f0-43d7-eae3-08d7ccde8b21 X-MS-Exchange-CrossTenant-OriginalArrivalTime: 20 Mar 2020 14:54:08.8754 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: eddbedf4-c37d-4645-97d5-2380a349ca88 X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: tmfQ6TNpYs70OnBkdo8JBvZrUZQl2AJFjF7dDCjBDJt61fS8P4iGMepDZ2k2Vii5iP3LDI2LnxuWfVmLWqXndtrk6r5abUJOX8oBMKcQ3r/hDTUx7mBjfoJ5ILYvNu2y X-MS-Exchange-Transport-CrossTenantHeadersStamped: AM0PR02MB4257 X-Spam-Status: No, score=-22.9 required=5.0 tests=BAYES_00, DKIM_INVALID, DKIM_SIGNED, FORGED_SPF_HELO, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, KAM_COUK, KAM_DMARC_STATUS, MSGID_FROM_MTA_HEADER, RCVD_IN_DNSWL_NONE, SPF_HELO_PASS, SPF_NONE autolearn=ham autolearn_force=no version=3.4.2 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Nathan Sidwell Errors-To: gcc-patches-bounces@gcc.gnu.org Sender: "Gcc-patches" Hi, This is the first of two remaining changes needed to bring the GCC implementation into line with the standard draft at n4849. The standard now calls up a revised mechanism to handle exceptions where exceptions thrown by the await_resume () method of the initial suspend expression are considered in the same manner as exceptions thrown by the user's function body. This implements [dcl.fct.def.coroutine] / 5.3. tested on x86_64-{linux, darwin} powerpc-linux, OK for master? thanks Iain gcc/cp/ChangeLog: 2020-03-20 Iain Sandoe * coroutines.cc (co_await_expander): If we are expanding the initial await expression, set a boolean flag to show that we have now reached the initial await_resume() method call. (expand_co_awaits): Handle the 'initial await resume called' flag. (build_actor_fn): Insert the initial await expression into the start of the user's function-body. Handle the 'initial await resume called' flag. (morph_fn_to_coro): Initialise the 'initial await resume called' flag. Modify the unhandled exception catch clause to recognise exceptions that occur before the initial await_resume() and re- throw them. gcc/testsuite/ChangeLog: 2020-03-20 Iain Sandoe * g++.dg/coroutines/torture/exceptions-test-01-n4849-a.C: New test. diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc index a943ba01de6..bcf3514bbcf 100644 --- a/gcc/cp/coroutines.cc +++ b/gcc/cp/coroutines.cc @@ -1348,6 +1348,7 @@ struct coro_aw_data tree actor_fn; /* Decl for context. */ tree coro_fp; /* Frame pointer var. */ tree resume_idx; /* This is the index var in the frame. */ + tree iarc_idx; /* This is the initial await resume called index. */ tree self_h; /* This is a handle to the current coro (frame var). */ tree cleanup; /* This is where to go once we complete local destroy. */ tree cororet; /* This is where to go if we suspend. */ @@ -1445,6 +1446,8 @@ co_await_expander (tree *stmt, int * /*do_subtree*/, void *d) tree awaiter_calls = TREE_OPERAND (saved_co_await, 3); tree source = TREE_OPERAND (saved_co_await, 4); + bool is_initial = + (source && TREE_INT_CST_LOW (source) == (int) INITIAL_SUSPEND_POINT); bool is_final = (source && TREE_INT_CST_LOW (source) == (int) FINAL_SUSPEND_POINT); bool needs_dtor = TYPE_HAS_NONTRIVIAL_DESTRUCTOR (TREE_TYPE (var)); @@ -1586,6 +1589,16 @@ co_await_expander (tree *stmt, int * /*do_subtree*/, void *d) resume_label = build_stmt (loc, LABEL_EXPR, resume_label); append_to_statement_list (resume_label, &stmt_list); + if (is_initial) + { + /* Note that we are about to execute the await_resume() for the initial + await expression. */ + r = build2_loc (loc, MODIFY_EXPR, boolean_type_node, data->iarc_idx, + boolean_true_node); + r = coro_build_cvt_void_expr_stmt (r, loc); + append_to_statement_list (r, &stmt_list); + } + /* This will produce the value (if one is provided) from the co_await expression. */ tree resume_call = TREE_VEC_ELT (awaiter_calls, 2); /* await_resume(). */ @@ -1634,10 +1647,10 @@ co_await_expander (tree *stmt, int * /*do_subtree*/, void *d) static tree expand_co_awaits (tree fn, tree *fnbody, tree coro_fp, tree resume_idx, - tree cleanup, tree cororet, tree self_h) + tree iarc_idx, tree cleanup, tree cororet, tree self_h) { coro_aw_data data - = {fn, coro_fp, resume_idx, self_h, cleanup, cororet, 2}; + = {fn, coro_fp, resume_idx, iarc_idx, self_h, cleanup, cororet, 2}; cp_walk_tree (fnbody, co_await_expander, &data, NULL); return *fnbody; } @@ -2158,8 +2171,7 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody, /* Get a reference to the initial suspend var in the frame. */ transform_await_expr (initial_await, &xform); - r = coro_build_expr_stmt (initial_await, loc); - add_stmt (r); + tree initial_await_stmt = coro_build_expr_stmt (initial_await, loc); /* co_return branches to the final_suspend label, so declare that now. */ tree fs_label = create_named_label_with_ctx (loc, "final.suspend", actor); @@ -2167,6 +2179,34 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody, /* Expand co_returns in the saved function body */ fnbody = expand_co_returns (&fnbody, promise_proxy, ap, fs_label); + /* n4849 adds specific behaviour to treat exceptions thrown by the + await_resume () of the initial suspend expression. In order to + implement this, we need to treat the initial_suspend expression + as if it were part of the user's authored function body. This + only applies if exceptions are enabled. */ + if (flag_exceptions) + { + tree outer = fnbody; + if (TREE_CODE (outer) == BIND_EXPR) + outer = BIND_EXPR_BODY (outer); + gcc_checking_assert (TREE_CODE (outer) == TRY_BLOCK); + tree sl = TRY_STMTS (outer); + if (TREE_CODE (sl) == STATEMENT_LIST) + { + tree_stmt_iterator si = tsi_start (sl); + tsi_link_before (&si, initial_await_stmt, TSI_NEW_STMT); + } + else + { + tree new_try = NULL_TREE; + append_to_statement_list (initial_await_stmt, &new_try); + append_to_statement_list (sl, &new_try); + TRY_STMTS (outer) = new_try; + } + } + else + add_stmt (initial_await_stmt); + /* Transform the await expressions in the function body. Only do each await tree once! */ hash_set pset; @@ -2336,10 +2376,18 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody, = build_class_member_access_expr (actor_frame, res_idx_m, NULL_TREE, false, tf_warning_or_error); + /* We need the initial await resume called index to work with. */ + tree iarc_idx_m + = lookup_member (coro_frame_type, get_identifier ("__i_a_r_c"), + /*protect=*/1, /*want_type=*/0, tf_warning_or_error); + tree iarc_idx + = build_class_member_access_expr (actor_frame, iarc_idx_m, NULL_TREE, + false, tf_warning_or_error); + /* We've now rewritten the tree and added the initial and final co_awaits. Now pass over the tree and expand the co_awaits. */ actor_body = expand_co_awaits (actor, &actor_body, actor_fp, res_idx, - del_promise_label, ret_label, ash); + iarc_idx, del_promise_label, ret_label, ash); actor_body = pop_stmt_list (actor_body); BIND_EXPR_BODY (actor_bind) = actor_body; @@ -3040,6 +3088,7 @@ act_des_fn (tree orig, tree fn_type, tree coro_frame_ptr, const char* name) void (*__destroy)(_R_frame *); coro1::promise_type __p; bool frame_needs_free; free the coro frame mem if set. + bool i_a_r_c; [dcl.fct.def.coroutine] / 5.3 short __resume_at; handle_type self_handle; (maybe) parameter copies. @@ -3161,6 +3210,8 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer) = coro_make_frame_entry (&field_list, "__p", promise_type, fn_start); tree fnf_name = coro_make_frame_entry (&field_list, "__frame_needs_free", boolean_type_node, fn_start); + tree iarc_name = coro_make_frame_entry (&field_list, "__i_a_r_c", + boolean_type_node, fn_start); tree resume_idx_name = coro_make_frame_entry (&field_list, "__resume_at", short_unsigned_type_node, fn_start); @@ -3725,6 +3776,16 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer) r = coro_build_cvt_void_expr_stmt (r, fn_start); add_stmt (r); + /* Initialize 'initial-await-resume-called' as per + [dcl.fct.def.coroutine] / 5.3 */ + tree iarc_m + = lookup_member (coro_frame_type, iarc_name, 1, 0, tf_warning_or_error); + tree iarc_x = build_class_member_access_expr (deref_fp, iarc_m, NULL_TREE, + false, tf_warning_or_error); + r = build2 (INIT_EXPR, boolean_type_node, iarc_x, boolean_false_node); + r = coro_build_cvt_void_expr_stmt (r, fn_start); + add_stmt (r); + /* So .. call the actor .. */ r = build_call_expr_loc (fn_start, actor, 1, coro_fp); r = maybe_cleanup_point_expr_void (r); @@ -3845,6 +3906,25 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer) /* Mimic what the parser does for the catch. */ tree handler = begin_handler (); finish_handler_parms (NULL_TREE, handler); /* catch (...) */ + + /* Get the initial await resume called value. */ + tree iarc = build_class_member_access_expr (actor_frame, iarc_m, + NULL_TREE, false, + tf_warning_or_error); + tree not_iarc_if = begin_if_stmt (); + tree not_iarc = build1_loc (fn_start, TRUTH_NOT_EXPR, + boolean_type_node, iarc); + finish_if_stmt_cond (not_iarc, not_iarc_if); + /* If the initial await resume called value is false, rethrow... */ + tree rethrow = build_throw (fn_start, NULL_TREE); + TREE_NO_WARNING (rethrow) = true; + finish_expr_stmt (rethrow); + finish_then_clause (not_iarc_if); + tree iarc_scope = IF_SCOPE (not_iarc_if); + IF_SCOPE (not_iarc_if) = NULL; + not_iarc_if = do_poplevel (iarc_scope); + add_stmt (not_iarc_if); + /* ... else call the promise unhandled exception method. */ ueh = maybe_cleanup_point_expr_void (ueh); add_stmt (ueh); finish_handler (handler); diff --git a/gcc/testsuite/g++.dg/coroutines/torture/exceptions-test-01-n4849-a.C b/gcc/testsuite/g++.dg/coroutines/torture/exceptions-test-01-n4849-a.C new file mode 100644 index 00000000000..e96b4ed1a8b --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/exceptions-test-01-n4849-a.C @@ -0,0 +1,213 @@ +// { dg-do run } + +// Test exceptions in the initial await expression, per n4849. + +#include "../coro.h" +#include + +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");} + }; + + 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"); } + }; + + /* Constructing this with: + * a value of '1' will cause the initial suspend await_suspend() + call to throw. + * a value of '2' will cause the await resume to throw. */ + struct suspend_always_susp_throws_prt { + int thrower; + suspend_always_susp_throws_prt (int _t) : thrower(_t) {} + bool await_ready() const noexcept { return false; } + + void await_suspend(handle_type) const + { PRINT ("suspend_always_susp_throws_prt:await_suspend"); + if (thrower == 1) + throw (42); + } + void await_resume() const + { PRINT ("suspend_always_susp_throws_prt:await_resume"); + if (thrower == 2) + throw (6174); + } + ~suspend_always_susp_throws_prt() { PRINT ("suspend_always_susp_throws_prt-DTOR"); } + }; + + struct promise_type { + int throw_control = 0; + int value; + promise_type(int k) : throw_control(k), value(-373) + { PRINTF ("Created Promise with %d\n", k);} + + ~promise_type() { PRINT ("Destroyed Promise"); } + + auto get_return_object () { + PRINT ("get_return_object: handle from promise"); + return handle_type::from_promise (*this); + } + // This provides the tests for what catches exceptions thrown at + // different points in the initial await expression. + auto initial_suspend () { + PRINT ("get initial_suspend (always)"); + return suspend_always_susp_throws_prt(throw_control); + } + 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; } + + void unhandled_exception() { + PRINT ("unhandled_exception: caught one!"); + gX = -11; + // returning from here should end up in final_suspend. + } + }; +}; + +// This doesn't have to do much - we only need to exercise the initial +// await expression. + +struct coro1 +n4849_ia_thrower (int k) +{ + int caught = 0; + PRINT ("f: about to return 22"); + co_return 22; +} + +int main () +{ + { + /* Case 0 - nothing should throw. */ + struct coro1 x0; + try { + x0 = n4849_ia_thrower (0); + } catch (...) { + PRINT ("main: case 0 ctor threw?"); + abort (); + } + /* Resume the initial suspend expression. */ + PRINT ("main: got coro, resuming.."); + x0.handle.resume(); + int y = x0.handle.promise().get_value(); + if ( y != 22 ) + { + PRINT ("main: case 0 got the wrong answer."); + abort (); + } + if (!x0.handle.done()) + { + PRINT ("main: case 0 not done."); + abort (); + } + if (gX != 0) + { + PRINT ("main: case 0 body threw?"); + abort (); + } + } + + { + /* Case 1 - initial suspend should throw and thus be caught by the + ramp's caller. */ + struct coro1 x1; + try { + x1 = n4849_ia_thrower (1); + } catch (int message) { + PRINTF ("main: caught an int %d\n", message); + if (message != 42) + { + PRINT ("main: unexpected value?"); + abort (); + } + } catch (...) { + PRINT ("main: case 1 ctor threw something else?"); + abort (); + } + if (gX != 0) + { + PRINT ("main: case 0 body threw (how?)"); + abort (); + } + } + + { + /* Case 2 - the await_resume from the initial await expression throws + this should be caught by the regular function body wrapper. */ + struct coro1 x2; + try { + x2 = n4849_ia_thrower (2); + } catch (...) { + PRINT ("main: case 2 ctor threw?"); + abort (); + } + // We now resume - and expect the await_resume to throw which should + // be caught by unhandled_exception(). + PRINT ("main: got coro, resuming.."); + x2.handle.resume(); + int y = x2.handle.promise().get_value(); + if ( y != -373 ) + { + PRINT ("main: case 2 got the wrong answer."); + abort (); + } + if (!x2.handle.done()) + { + PRINT ("main: case 2 not done."); + abort (); + } + if (gX != -11) + { + PRINT ("main: n4849_is_thrower await_resume exception not caught"); + abort (); + } + } + PRINT ("main: returning"); + return 0; +}