From patchwork Wed Aug 21 19:09:58 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Iain Sandoe X-Patchwork-Id: 1975079 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20230601 header.b=XvEfKEp2; 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 4WpwsJ4Fb6z1yXf for ; Thu, 22 Aug 2024 05:10:44 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id E697838708FA for ; Wed, 21 Aug 2024 19:10:41 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-ej1-x633.google.com (mail-ej1-x633.google.com [IPv6:2a00:1450:4864:20::633]) by sourceware.org (Postfix) with ESMTPS id 586F63858C52 for ; Wed, 21 Aug 2024 19:10:10 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 586F63858C52 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gmail.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 586F63858C52 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2a00:1450:4864:20::633 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1724267413; cv=none; b=lQvQv4FBNQtzw7ot7zr+7yc7wfhTlusXj1QT/ToCo6L+i8a3g7vD9hzl2lz9Ru3hDCjHNh7lB+bISPUxOBzELPVXMKPEQoJMVGsDE4HOKmvTeV6n5dcEdPdE7GKl2bWLGKjRtid+UNQ+Uv5gYuafPFZ6BJcEZQ91EAENPTwADtU= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1724267413; c=relaxed/simple; bh=RvcAhM+DFyioO76i1jYMrGQwVKkXA7e1UqmYtkd7RZs=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=YnBSk2YAEVeoFDOAAiW7VmeX4m6qyP3nFQMmQzDDqbo3GHCbgh/lKvRWpQCvJlT4Ly0plcf4l4uQwlKMLJ597tWOeiowwkp7GL2GRe76UkAktQvl68RqMNmzaqmwzyQ6WkLH5pl/v0roecvgmir8zwJjGiMVf5s9HGQ458FN+Es= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-ej1-x633.google.com with SMTP id a640c23a62f3a-a7a9cf7d3f3so904220666b.1 for ; Wed, 21 Aug 2024 12:10:10 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1724267409; x=1724872209; darn=gcc.gnu.org; h=content-transfer-encoding:mime-version:reply-to:references :in-reply-to:message-id:date:subject:cc:to:from:from:to:cc:subject :date:message-id:reply-to; bh=hRSVw+nRk8DkP88cF9Go2Yn8arcN3D7TH5jC1zDTj60=; b=XvEfKEp2dCMJQuymMtgRzuY0tWX9WFkjSzCg/0vR8EAswmU4hByrLMeb+msUv8s5ZN lH2jkj98dBu/00Hun7VW4s7yqrqaOzsHHFUB6LVFAvPM0eZTS9nqjfeGB3JkVDgmbw2/ 8VGFWyD+bB13xqMFdN7cHXaqDOXP41jQm64fHCgYdWlvAIZYUFdt5tkPErvkYNm46/hM FYlwW4wJppAgHq5dVOSp8jylKlWmgRb0y9C7ccDfpay+gIIEVBfoRxtj687IGHMZRjF4 WkeiXcE/o99SVKLq0y07pNDe6oN0jMK0kaU78JWVuwL5JZ1OJ1zXZD/z+KOggPxtGBpI OnPg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1724267409; x=1724872209; h=content-transfer-encoding:mime-version:reply-to:references :in-reply-to:message-id:date:subject:cc:to:from:x-gm-message-state :from:to:cc:subject:date:message-id:reply-to; bh=hRSVw+nRk8DkP88cF9Go2Yn8arcN3D7TH5jC1zDTj60=; b=jSI9VC1Zcr+qAj+aiJH/ulKYmGIbLFGWNIcl2he40GjSMk7YFf9HgUbE8AxXWHKgik ZC9S+AnpyxdoG2Hsz5l6z/VFrLa5pM9PhMsztOanv8fgARzZqphyaVJISoox2E/0bVWG +CzHGsRUqMjgqBlWo54PnYWliYWuM1KCQ7tethdfjJ3cQTMqIV5g1cQMpDuPXp6Nkrfk /ETIOxsw7x+KccJrGdwAZvPqtV7ebcfNknUrEoLb2y+PciNs3gIu5ESTUbnxiZlaiKXE 92hombG5ISK0JtPFbWbAm/FNEI73jmsS8ckBlkrWSX/jQ3wa1rwW4Y8oFBQMTxdy1BB6 OHJw== X-Gm-Message-State: AOJu0YyY6RqEBgGXEom67bs4JR13oRuPa47zQrMBNoiDz91iz1Q7ulCq OShF0oBYa+wimG4cnhRs6t1+lcHVUFcx5QeU4Q5S9bnSVsxFNBjrPWfLA/Ut X-Google-Smtp-Source: AGHT+IE4Sd8UTuS002FLC+d7smY+/lVve4CIal0Phb7wE8xoNzS7YkfvO0EY23u/H6aQFHAqn3VclA== X-Received: by 2002:a17:906:c14f:b0:a77:b516:3023 with SMTP id a640c23a62f3a-a866f11d8cfmr196161766b.4.1724267408409; Wed, 21 Aug 2024 12:10:08 -0700 (PDT) Received: from localhost.localdomain (host81-138-1-83.in-addr.btopenworld.com. [81.138.1.83]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a83838c6c07sm941222566b.43.2024.08.21.12.10.07 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Wed, 21 Aug 2024 12:10:08 -0700 (PDT) From: Iain Sandoe X-Google-Original-From: Iain Sandoe To: gcc-patches@gcc.gnu.org Cc: jason@redhat.com Subject: [PATCH 1/9] c++, coroutines: Split the ramp build into a separate function. Date: Wed, 21 Aug 2024 20:09:58 +0100 Message-Id: <1863dcd621d308ebae10fb0adcd35b541b3fb4e8.1724267239.git.iain@sandoe.co.uk> X-Mailer: git-send-email 2.39.2 (Apple Git-143) In-Reply-To: References: MIME-Version: 1.0 X-Spam-Status: No, score=-8.3 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP, T_SCC_BODY_TEXT_LINE 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: , Reply-To: iain@sandoe.co.uk Errors-To: gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org This is primarily preparation to partition the functionality of the coroutine transform into analysis, ramp generation and then (later) synthesis of the coroutine body. The patch does fix one latent issue in the ordering of DTORs for frame parameter copies (to ensure that they are processed in reverse order to the copy creation). gcc/cp/ChangeLog: * coroutines.cc (build_actor_fn): Arrange to apply any required parameter copy DTORs in reverse order to their creation. (morph_fn_to_coro): Split the ramp function completion into a separate function. (build_ramp_function): New. Signed-off-by: Iain Sandoe --- gcc/cp/coroutines.cc | 360 +++++++++++++++++++++++-------------------- 1 file changed, 192 insertions(+), 168 deletions(-) diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc index 1f1ea5c2fe4..50362fc3556 100644 --- a/gcc/cp/coroutines.cc +++ b/gcc/cp/coroutines.cc @@ -2298,7 +2298,7 @@ static void build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody, tree orig, hash_map *local_var_uses, hash_map *suspend_points, - vec *param_dtor_list, + vec *param_dtor_list, tree resume_idx_var, unsigned body_count, tree frame_size) { verify_stmt_tree (fnbody); @@ -2513,19 +2513,15 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody, fnf2_x = build1 (CONVERT_EXPR, integer_type_node, fnf2_x); tree cmp = build2 (NE_EXPR, integer_type_node, fnf2_x, integer_zero_node); finish_if_stmt_cond (cmp, need_free_if); - if (param_dtor_list != NULL) + while (!param_dtor_list->is_empty ()) { - int i; - tree pid; - FOR_EACH_VEC_ELT (*param_dtor_list, i, pid) - { - tree m - = lookup_member (coro_frame_type, pid, 1, 0, tf_warning_or_error); - tree a = build_class_member_access_expr (actor_frame, m, NULL_TREE, - false, tf_warning_or_error); - if (tree dtor = cxx_maybe_build_cleanup (a, tf_warning_or_error)) - add_stmt (dtor); - } + tree pid = param_dtor_list->pop (); + tree m = lookup_member (coro_frame_type, pid, 1, 0, tf_warning_or_error); + gcc_checking_assert (m); + tree a = build_class_member_access_expr (actor_frame, m, NULL_TREE, + false, tf_warning_or_error); + if (tree dtor = cxx_maybe_build_cleanup (a, tf_warning_or_error)) + add_stmt (dtor); } /* Build the frame DTOR. */ @@ -4553,147 +4549,28 @@ split_coroutine_body_from_ramp (tree fndecl) return body; } -/* Here we: - a) Check that the function and promise type are valid for a - coroutine. - b) Carry out the initial morph to create the skeleton of the - coroutine ramp function and the rewritten body. - - Assumptions. +/* Build the ramp function. + Here we take the original function definition which has now had its body + removed, and use it as the declaration of the ramp which both replaces the + user's written function at call sites, and is responsible for starting + the coroutine it defined. + returns NULL_TREE on error or an expression for the frame size. - 1. We only hit this code once all dependencies are resolved. - 2. The function body will be either a bind expr or a statement list - 3. That cfun and current_function_decl are valid for the case we're - expanding. - 4. 'input_location' will be of the final brace for the function. - - We do something like this: - declare a dummy coro frame. - struct _R_frame { - using handle_type = coro::coroutine_handle; - void (*_Coro_resume_fn)(_R_frame *); - void (*_Coro_destroy_fn)(_R_frame *); - coro1::promise_type _Coro_promise; - bool _Coro_frame_needs_free; free the coro frame mem if set. - bool _Coro_i_a_r_c; [dcl.fct.def.coroutine] / 5.3 - short _Coro_resume_index; - handle_type _Coro_self_handle; - parameter copies (were required). - local variables saved (including awaitables) - (maybe) trailing space. - }; */ + We should arrive here with the state of the compiler as if we had just + executed start_preparsed_function(). */ -bool -morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer) +static tree +build_ramp_function (tree orig, location_t fn_start, tree coro_frame_ptr, + tree coro_frame_type, + hash_map *param_uses, + tree act_des_fn_ptr, tree actor, tree destroy, + vec *param_dtor_list) { - gcc_checking_assert (orig && TREE_CODE (orig) == FUNCTION_DECL); - - *resumer = error_mark_node; - *destroyer = error_mark_node; - if (!coro_function_valid_p (orig)) - { - /* For early errors, we do not want a diagnostic about the missing - ramp return value, since the user cannot fix this - a 'return' is - not allowed in a coroutine. */ - suppress_warning (orig, OPT_Wreturn_type); - /* Discard the body, we can't process it further. */ - pop_stmt_list (DECL_SAVED_TREE (orig)); - DECL_SAVED_TREE (orig) = push_stmt_list (); - /* Match the expected nesting when an eh block is in use. */ - if (use_eh_spec_block (orig)) - current_eh_spec_block = begin_eh_spec_block (); - return false; - } - - /* We don't have the locus of the opening brace - it's filled in later (and - there doesn't really seem to be any easy way to get at it). - The closing brace is assumed to be input_location. */ - location_t fn_start = DECL_SOURCE_LOCATION (orig); - - /* FIXME: This has the hidden side-effect of preparing the current function - to be the ramp. */ - tree fnbody = split_coroutine_body_from_ramp (orig); - if (!fnbody) - return false; - - /* If the original function has a return value with a non-trivial DTOR - and the body contains a var with a DTOR that might throw, the decl is - marked "throwing_cleanup". - We do not [in the ramp, which is synthesised here], use any body var - types with DTORs that might throw. - The original body is transformed into the actor function which only - contains void returns, and is also wrapped in a try-catch block. - So (a) the 'throwing_cleanup' is not correct for the ramp and (b) we do - not need to transfer it to the actor which only contains void returns. */ - cp_function_chain->throwing_cleanup = false; - - /* Create the coro frame type, as far as it can be known at this stage. - 1. Types we already know. */ - - tree fn_return_type = TREE_TYPE (TREE_TYPE (orig)); tree promise_type = get_coroutine_promise_type (orig); - - /* 2. Types we need to define or look up. */ - - tree fr_name = get_fn_local_identifier (orig, "Frame"); - tree coro_frame_type = xref_tag (record_type, fr_name); - DECL_CONTEXT (TYPE_NAME (coro_frame_type)) = DECL_CONTEXT (orig); - tree coro_frame_ptr = build_pointer_type (coro_frame_type); - tree act_des_fn_type - = build_function_type_list (void_type_node, coro_frame_ptr, NULL_TREE); - tree act_des_fn_ptr = build_pointer_type (act_des_fn_type); - - /* Declare the actor and destroyer function. */ - tree actor = coro_build_actor_or_destroy_function (orig, act_des_fn_type, - coro_frame_ptr, true); - tree destroy = coro_build_actor_or_destroy_function (orig, act_des_fn_type, - coro_frame_ptr, false); - - /* Construct the wrapped function body; we will analyze this to determine - the requirements for the coroutine frame. */ - - tree resume_idx_var = NULL_TREE; - tree fs_label = NULL_TREE; - hash_map *param_uses = analyze_fn_parms (orig); - - fnbody = coro_rewrite_function_body (fn_start, fnbody, orig, param_uses, - act_des_fn_ptr, - resume_idx_var, fs_label); - /* Build our dummy coro frame layout. */ - coro_frame_type = begin_class_definition (coro_frame_type); - - - /* We need to know, and inspect, each suspend point in the function - in several places. It's convenient to place this map out of line - since it's used from tree walk callbacks. */ - - hash_map suspend_points; - /* Now insert the data for any body await points, at this time we also need - to promote any temporaries that are captured by reference (to regular - vars) they will get added to the coro frame along with other locals. */ - susp_frame_data body_aw_points (fs_label, &suspend_points); - cp_walk_tree (&fnbody, await_statement_walker, &body_aw_points, NULL); - - /* 4. Now make space for local vars, this is conservative again, and we - would expect to delete unused entries later. Compose the frame entries - list. */ - - /* The fields for the coro frame. */ - tree field_list = NULL_TREE; - hash_map local_var_uses; - local_vars_frame_data local_vars_data (&field_list, &local_var_uses); - cp_walk_tree (&fnbody, register_local_var_uses, &local_vars_data, NULL); - - /* Tie off the struct for now, so that we can build offsets to the - known entries. */ - TYPE_FIELDS (coro_frame_type) = field_list; - TYPE_BINFO (coro_frame_type) = make_tree_binfo (0); - BINFO_OFFSET (TYPE_BINFO (coro_frame_type)) = size_zero_node; - BINFO_TYPE (TYPE_BINFO (coro_frame_type)) = coro_frame_type; - - coro_frame_type = finish_struct (coro_frame_type, NULL_TREE); + tree fn_return_type = TREE_TYPE (TREE_TYPE (orig)); /* Ramp: */ + tree stmt = begin_function_body (); /* Now build the ramp function pieces. */ tree ramp_bind = build3 (BIND_EXPR, void_type_node, NULL, NULL, NULL); add_stmt (ramp_bind); @@ -5018,8 +4895,6 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer) parameter copies ends immediately after the lifetime of the coroutine promise object ends. */ - vec *param_dtor_list = NULL; - if (DECL_ARGUMENTS (orig)) { promise_args = make_tree_vector (); @@ -5064,9 +4939,7 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer) finish_expr_stmt (r); if (!parm.trivial_dtor) { - if (param_dtor_list == NULL) - param_dtor_list = make_tree_vector (); - vec_safe_push (param_dtor_list, parm.field_id); + param_dtor_list->safe_push (parm.field_id); /* Cleanup this frame copy on exception. */ parm.fr_copy_dtor = cxx_maybe_build_cleanup (fld_idx, tf_warning_or_error); @@ -5146,7 +5019,7 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer) BIND_EXPR_BODY (ramp_bind) = pop_stmt_list (ramp_body); /* Suppress warnings about the missing return value. */ suppress_warning (orig, OPT_Wreturn_type); - return false; + return NULL_TREE; } tree gro_context_body = push_stmt_list (); @@ -5318,7 +5191,9 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer) add_stmt (promise_d_if); } - /* Clean up any frame copies of parms with non-trivial dtors. */ + /* Clean up any frame copies of parms with non-trivial dtors. + Do this in reverse order from their creation. */ + vec worklist = vNULL; if (DECL_ARGUMENTS (orig)) for (tree arg = DECL_ARGUMENTS (orig); arg != NULL; arg = DECL_CHAIN (arg)) @@ -5326,18 +5201,23 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer) param_info *parm_i = param_uses->get (arg); if (parm_i->trivial_dtor) continue; - if (parm_i->fr_copy_dtor && parm_i->fr_copy_dtor != error_mark_node) - { - tree dtor_if = begin_if_stmt (); - finish_if_stmt_cond (parm_i->guard_var, dtor_if); - finish_expr_stmt (parm_i->fr_copy_dtor); - finish_then_clause (dtor_if); - tree parm_d_if_scope = IF_SCOPE (dtor_if); - IF_SCOPE (dtor_if) = NULL; - dtor_if = do_poplevel (parm_d_if_scope); - add_stmt (dtor_if); - } + worklist.safe_push (parm_i); } + while (!worklist.is_empty()) + { + param_info *parm_i = worklist.pop (); + if (parm_i->fr_copy_dtor && parm_i->fr_copy_dtor != error_mark_node) + { + tree dtor_if = begin_if_stmt (); + finish_if_stmt_cond (parm_i->guard_var, dtor_if); + finish_expr_stmt (parm_i->fr_copy_dtor); + finish_then_clause (dtor_if); + tree parm_d_if_scope = IF_SCOPE (dtor_if); + IF_SCOPE (dtor_if) = NULL; + dtor_if = do_poplevel (parm_d_if_scope); + add_stmt (dtor_if); + } + } /* We always expect to delete the frame. */ tree del_coro_fr = coro_get_frame_dtor (coro_fp, orig, frame_size, @@ -5351,7 +5231,151 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer) } BIND_EXPR_BODY (ramp_bind) = pop_stmt_list (ramp_body); - TREE_SIDE_EFFECTS (ramp_bind) = true; + finish_function_body (stmt); + return frame_size; +} + +/* Here we: + a) Check that the function and promise type are valid for a + coroutine. + b) Carry out the initial morph to create the skeleton of the + coroutine ramp function and the rewritten body. + + Assumptions. + + 1. We only hit this code once all dependencies are resolved. + 2. The function body will be either a bind expr or a statement list + 3. That cfun and current_function_decl are valid for the case we're + expanding. + 4. 'input_location' will be of the final brace for the function. + + We do something like this: + declare a dummy coro frame. + struct _R_frame { + using handle_type = coro::coroutine_handle; + void (*_Coro_resume_fn)(_R_frame *); + void (*_Coro_destroy_fn)(_R_frame *); + coro1::promise_type _Coro_promise; + bool _Coro_frame_needs_free; free the coro frame mem if set. + bool _Coro_i_a_r_c; [dcl.fct.def.coroutine] / 5.3 + short _Coro_resume_index; + handle_type _Coro_self_handle; + parameter copies (were required). + local variables saved (including awaitables) + (maybe) trailing space. + }; */ + +bool +morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer) +{ + gcc_checking_assert (orig && TREE_CODE (orig) == FUNCTION_DECL); + + *resumer = error_mark_node; + *destroyer = error_mark_node; + if (!coro_function_valid_p (orig)) + { + /* For early errors, we do not want a diagnostic about the missing + ramp return value, since the user cannot fix this - a 'return' is + not allowed in a coroutine. */ + suppress_warning (orig, OPT_Wreturn_type); + /* Discard the body, we can't process it further. */ + pop_stmt_list (DECL_SAVED_TREE (orig)); + DECL_SAVED_TREE (orig) = push_stmt_list (); + /* Match the expected nesting when an eh block is in use. */ + if (use_eh_spec_block (orig)) + current_eh_spec_block = begin_eh_spec_block (); + return false; + } + + /* We don't have the locus of the opening brace - it's filled in later (and + there doesn't really seem to be any easy way to get at it). + The closing brace is assumed to be input_location. */ + location_t fn_start = DECL_SOURCE_LOCATION (orig); + + /* FIXME: This has the hidden side-effect of preparing the current function + to be the ramp. */ + tree fnbody = split_coroutine_body_from_ramp (orig); + if (!fnbody) + return false; + + /* If the original function has a return value with a non-trivial DTOR + and the body contains a var with a DTOR that might throw, the decl is + marked "throwing_cleanup". + We do not [in the ramp, which is synthesised here], use any body var + types with DTORs that might throw. + The original body is transformed into the actor function which only + contains void returns, and is also wrapped in a try-catch block. + So (a) the 'throwing_cleanup' is not correct for the ramp and (b) we do + not need to transfer it to the actor which only contains void returns. */ + cp_function_chain->throwing_cleanup = false; + + /* Create the coro frame type, as far as it can be known at this stage. + 1. Types we already know. */ + + /* 2. Types we need to define or look up. */ + + tree fr_name = get_fn_local_identifier (orig, "Frame"); + tree coro_frame_type = xref_tag (record_type, fr_name); + DECL_CONTEXT (TYPE_NAME (coro_frame_type)) = DECL_CONTEXT (orig); //current_scope (); + tree coro_frame_ptr = build_pointer_type (coro_frame_type); + tree act_des_fn_type + = build_function_type_list (void_type_node, coro_frame_ptr, NULL_TREE); + tree act_des_fn_ptr = build_pointer_type (act_des_fn_type); + + /* Construct the wrapped function body; we will analyze this to determine + the requirements for the coroutine frame. */ + + tree resume_idx_var = NULL_TREE; + tree fs_label = NULL_TREE; + hash_map *param_uses = analyze_fn_parms (orig); + + fnbody = coro_rewrite_function_body (fn_start, fnbody, orig, param_uses, + act_des_fn_ptr, + resume_idx_var, fs_label); + + /* We need to know, and inspect, each suspend point in the function + in several places. It's convenient to place this map out of line + since it's used from tree walk callbacks. */ + + hash_map suspend_points; + /* Now insert the data for any body await points, at this time we also need + to promote any temporaries that are captured by reference (to regular + vars) they will get added to the coro frame along with other locals. */ + susp_frame_data body_aw_points (fs_label, &suspend_points); + cp_walk_tree (&fnbody, await_statement_walker, &body_aw_points, NULL); + + /* 4. Now make space for local vars, this is conservative again, and we + would expect to delete unused entries later. Compose the frame entries + list. */ + + /* The fields for the coro frame. */ + tree field_list = NULL_TREE; + hash_map local_var_uses; + local_vars_frame_data local_vars_data (&field_list, &local_var_uses); + cp_walk_tree (&fnbody, register_local_var_uses, &local_vars_data, NULL); + + /* Tie off the struct for now, so that we can build offsets to the + known entries. */ + coro_frame_type = begin_class_definition (coro_frame_type); + TYPE_FIELDS (coro_frame_type) = field_list; + TYPE_BINFO (coro_frame_type) = make_tree_binfo (0); + BINFO_OFFSET (TYPE_BINFO (coro_frame_type)) = size_zero_node; + BINFO_TYPE (TYPE_BINFO (coro_frame_type)) = coro_frame_type; + coro_frame_type = finish_struct (coro_frame_type, NULL_TREE); + + /* Declare the actor and destroyer function. */ + tree actor = coro_build_actor_or_destroy_function (orig, act_des_fn_type, + coro_frame_ptr, true); + tree destroy = coro_build_actor_or_destroy_function (orig, act_des_fn_type, + coro_frame_ptr, false); + + vec param_dtor_list = vNULL; + tree frame_size + = build_ramp_function (orig, fn_start, coro_frame_ptr, coro_frame_type, + param_uses, act_des_fn_ptr, actor, destroy, + ¶m_dtor_list); + if (!frame_size) + return false; /* Start to build the final functions. @@ -5362,7 +5386,7 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer) /* Build the actor... */ build_actor_fn (fn_start, coro_frame_type, actor, fnbody, orig, - &local_var_uses, &suspend_points, param_dtor_list, + &local_var_uses, &suspend_points, ¶m_dtor_list, resume_idx_var, body_aw_points.await_number, frame_size); /* Destroyer ... */ From patchwork Wed Aug 21 19:09:59 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Iain Sandoe X-Patchwork-Id: 1975085 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20230601 header.b=fytF5R7U; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=8.43.85.97; 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 [8.43.85.97]) (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 4Wpwtg1jKkz1yXf for ; Thu, 22 Aug 2024 05:11:55 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 6E95038708FD for ; Wed, 21 Aug 2024 19:11:53 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-lf1-x12c.google.com (mail-lf1-x12c.google.com [IPv6:2a00:1450:4864:20::12c]) by sourceware.org (Postfix) with ESMTPS id 53B9C3858C32 for ; Wed, 21 Aug 2024 19:10:12 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 53B9C3858C32 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gmail.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 53B9C3858C32 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2a00:1450:4864:20::12c ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1724267420; cv=none; b=v1NvKoziWVZJ5xjel2W3YYHSMZGSUP5zbp5AlulGSiF/NrLwBRh39o4uPnLA/qPY8hX2gByR3kaA3LovB97kSWS/irK4NdddIAiVE3+urer3LC7e8XtyytTxg9b1JVXsKr+EhRDGw2i9iVyn2l3CA5PfeBhkuZdH/kiJfTOWNCg= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1724267420; c=relaxed/simple; bh=hZQ5vwQsD0EXbYr851qo4sn85WqfkvGhJN8Kd9b/6UU=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=Soj2Ezog/vreJ3YrqELRT1FpU5Uuo1E5XZeI47xstCUbNC2nNSBIRq2exuvmU7CVCnNTGjp933AN/NLjfSfa+ShU2VAPN3azhBIEiW1Isq6DBYe6e9sDaWi1/xdEZlduTx1J+YKlG2mlGiAkD2IVT8CEhAiNOwbls9XvM4l462Y= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-lf1-x12c.google.com with SMTP id 2adb3069b0e04-533462b9428so62023e87.3 for ; Wed, 21 Aug 2024 12:10:12 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1724267411; x=1724872211; darn=gcc.gnu.org; h=content-transfer-encoding:mime-version:reply-to:references :in-reply-to:message-id:date:subject:cc:to:from:from:to:cc:subject :date:message-id:reply-to; bh=K7PJnuLzHWf82McreWWx8s9KUgU6wbAq31plnsnqgUY=; b=fytF5R7U2z+wNedfVNvW5XNfd3DRaAly8xvvltQRQrB7H6L+jJdUYCKU1+Eq42Rra/ xTN0DjMtawiaJLT4KYyVr+JQ02889w7UvkXkHUw8XP4+p2Nswo2IEsmcTyCVowLvQMXj ueysNWZkCkp+vnNwUrmzAQJT3PD+nbnUsdbnAedFNx2C+NjdQMGXfuVE4eHpwno8yG1b iJIFvzNF+mQNcsFwoIP3VALoLeyzUpogZ7HkufxIE+fyfZOeuSUEzMmIcecDU5FNZb/g GQJvy5bvzAVGb8G80OPFSa7EFavTtZQ4a5HpOpNjOyhimDcaU+tCmOGn3s2qbd8Qtaid r1OQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1724267411; x=1724872211; h=content-transfer-encoding:mime-version:reply-to:references :in-reply-to:message-id:date:subject:cc:to:from:x-gm-message-state :from:to:cc:subject:date:message-id:reply-to; bh=K7PJnuLzHWf82McreWWx8s9KUgU6wbAq31plnsnqgUY=; b=LwxpVg0n+dCUlqdU/d76SRiNpJwQTpMBL/9WjISeVoKONjXDUE+PR3XGPDb4YSc8WQ IJBjjEjl1ltgIyCedyiKAGAcQZTen04st9rRTMB04xSvp4lj3hRpqmAqrq4X0AYluRoZ unt2piuJ9pYLxSVSK/hJOZC19/wKuNGr//Fr6Q0358h5wc/P81r/QJNf1aZ0FAo5qr8c VrHo53lbMwx5+O7pgo2G+yULg81IRKAFV0T5s4cQxJdSfGYHP1UwLiRYBERvt4t3iBYi UtqxW+k2WrgUKo1OamoA8L+cpA080+FEuYhBI9oH0aCcpJcv7v5tQwol39n4Eug9gzgU a9gw== X-Gm-Message-State: AOJu0Yz5W5OSeD0Ry9pwP/ox5icGvPrIgt8MPejxjxQhBqCstMy+SU0Q r/vg8h9+uW2lBE4VbquphcsJYGFV6fRKD4OpT9/EukNzCqvyDkkAtnTiElqJ X-Google-Smtp-Source: AGHT+IH+YZknI8EW6cWJvuXgFtpqhWz62cZM4auo2jf0CqEU5DcrKt0y4Sc9FDo94ifi8c1FtJ209A== X-Received: by 2002:a05:6512:308c:b0:52e:933f:f1fa with SMTP id 2adb3069b0e04-53348601d3fmr2612215e87.61.1724267409328; Wed, 21 Aug 2024 12:10:09 -0700 (PDT) Received: from localhost.localdomain (host81-138-1-83.in-addr.btopenworld.com. [81.138.1.83]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a83838c6c07sm941222566b.43.2024.08.21.12.10.08 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Wed, 21 Aug 2024 12:10:08 -0700 (PDT) From: Iain Sandoe X-Google-Original-From: Iain Sandoe To: gcc-patches@gcc.gnu.org Cc: jason@redhat.com Subject: [PATCH 2/9] c++, coroutines: Separate the analysis, ramp and outlined function synthesis. Date: Wed, 21 Aug 2024 20:09:59 +0100 Message-Id: <39570143d9e0a5cb3e5313c8bebf307f18174312.1724267239.git.iain@sandoe.co.uk> X-Mailer: git-send-email 2.39.2 (Apple Git-143) In-Reply-To: References: MIME-Version: 1.0 X-Spam-Status: No, score=-8.3 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP, T_SCC_BODY_TEXT_LINE 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: , Reply-To: iain@sandoe.co.uk Errors-To: gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org This change is preparation for fixes to the ramp and codegen to follow. The primary motivation is that we have thee activities; analysis, ramp synthesis and outlined coroutine body synthesis. These are currently carried out in sequence in the 'morph_fn_to_coro' code, which means that we are nesting the synthesis of the outlined coroutine body inside the finish_function call for the original function (which becomes the ramp). The revised code splits the three interests so that the analysis can be used independently by the ramp and body synthesis. This avoids some issues seen with global state that start/finish function use and allows us to use more of the high-level APIs in fixing bugs. The resultant implementation is more self-contained, and has less impact on finish_function. gcc/cp/ChangeLog: * coroutines.cc (struct suspend_point_info, struct param_info, struct local_var_info, struct susp_frame_data, struct local_vars_frame_data): Move to coroutines.h. (build_actor_fn): Use start/finish function APIs. (build_destroy_fn): Likewise. (coro_build_actor_or_destroy_function): No longer mark the actor / destroyer as DECL_COROUTINE_P. (coro_rewrite_function_body): Use class members. (cp_coroutine_transform::wrap_original_function_body): Likewise. (build_ramp_function): Replace by... (cp_coroutine_transform::complete_ramp_function): ...this. (cp_coroutine_transform::cp_coroutine_transform): New. (cp_coroutine_transform::~cp_coroutine_transform): New (morph_fn_to_coro): Replace by... (cp_coroutine_transform::apply_transforms): ...this. (cp_coroutine_transform::finish_transforms): New. * cp-tree.h (morph_fn_to_coro): Remove. * decl.cc (emit_coro_helper): Remove. (finish_function): Revise handling of coroutine transforms. * coroutines.h: New file. Signed-off-by: Iain Sandoe Co-authored-by: Arsen Arsenović --- gcc/cp/coroutines.cc | 635 +++++++++++++++++++------------------------ gcc/cp/coroutines.h | 132 +++++++++ gcc/cp/cp-tree.h | 1 - gcc/cp/decl.cc | 80 +++--- 4 files changed, 439 insertions(+), 409 deletions(-) create mode 100644 gcc/cp/coroutines.h diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc index 50362fc3556..4968852c9ca 100644 --- a/gcc/cp/coroutines.cc +++ b/gcc/cp/coroutines.cc @@ -32,6 +32,7 @@ along with GCC; see the file COPYING3. If not see #include "tree.h" #include "gcc-rich-location.h" #include "hash-map.h" +#include "coroutines.h" static bool coro_promise_type_found_p (tree, location_t); @@ -2049,16 +2050,6 @@ await_statement_expander (tree *stmt, int *do_subtree, void *d) return res; } -/* Suspend point hash_map. */ - -struct suspend_point_info -{ - /* coro frame field type. */ - tree awaitable_type; - /* coro frame field name. */ - tree await_field_id; -}; - struct await_xform_data { tree actor_fn; /* Decl for context. */ @@ -2136,37 +2127,6 @@ transform_await_wrapper (tree *stmt, int *do_subtree, void *d) return NULL_TREE; } -/* This caches information that we determine about function params, - their uses and copies in the coroutine frame. */ - -struct param_info -{ - tree field_id; /* The name of the copy in the coroutine frame. */ - tree copy_var; /* The local var proxy for the frame copy. */ - vec *body_uses; /* Worklist of uses, void if there are none. */ - tree frame_type; /* The type used to represent this parm in the frame. */ - tree orig_type; /* The original type of the parm (not as passed). */ - tree guard_var; /* If we need a DTOR on exception, this bool guards it. */ - tree fr_copy_dtor; /* If we need a DTOR on exception, this is it. */ - bool by_ref; /* Was passed by reference. */ - bool pt_ref; /* Was a pointer to object. */ - bool rv_ref; /* Was an rvalue ref. */ - bool trivial_dtor; /* The frame type has a trivial DTOR. */ - bool this_ptr; /* Is 'this' */ - bool lambda_cobj; /* Lambda capture object */ -}; - -struct local_var_info -{ - tree field_id; - tree field_idx; - tree frame_type; - bool is_lambda_capture; - bool is_static; - bool has_value_expr_p; - location_t def_loc; -}; - /* For figuring out what local variable usage we have. */ struct local_vars_transform { @@ -2299,7 +2259,8 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody, tree orig, hash_map *local_var_uses, hash_map *suspend_points, vec *param_dtor_list, - tree resume_idx_var, unsigned body_count, tree frame_size) + tree resume_idx_var, unsigned body_count, tree frame_size, + bool inline_p) { verify_stmt_tree (fnbody); /* Some things we inherit from the original function. */ @@ -2309,12 +2270,10 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody, /* One param, the coro frame pointer. */ tree actor_fp = DECL_ARGUMENTS (actor); - /* We have a definition here. */ - TREE_STATIC (actor) = 1; - - tree actor_outer = push_stmt_list (); - current_stmt_tree ()->stmts_are_full_exprs_p = 1; - tree stmt = begin_compound_stmt (BCS_FN_BODY); + bool spf = start_preparsed_function (actor, NULL_TREE, SF_PRE_PARSED); + gcc_checking_assert (spf); + gcc_checking_assert (cfun && current_function_decl && TREE_STATIC (actor)); + tree stmt = begin_function_body (); tree actor_bind = build3 (BIND_EXPR, void_type_node, NULL, NULL, NULL); tree top_block = make_node (BLOCK); @@ -2588,9 +2547,9 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody, BIND_EXPR_BODY (actor_bind) = pop_stmt_list (actor_body); TREE_SIDE_EFFECTS (actor_bind) = true; - finish_compound_stmt (stmt); - DECL_SAVED_TREE (actor) = pop_stmt_list (actor_outer); - verify_stmt_tree (DECL_SAVED_TREE (actor)); + cfun->coroutine_component = 1; + finish_function_body (stmt); + finish_function (inline_p); } /* The prototype 'destroy' function : @@ -2599,44 +2558,46 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody, static void build_destroy_fn (location_t loc, tree coro_frame_type, tree destroy, - tree actor) + tree actor, bool inline_p) { /* One param, the coro frame pointer. */ tree destr_fp = DECL_ARGUMENTS (destroy); + gcc_checking_assert (POINTER_TYPE_P (TREE_TYPE (destr_fp)) + && same_type_p (coro_frame_type, + TREE_TYPE (TREE_TYPE (destr_fp)))); - /* We have a definition here. */ - TREE_STATIC (destroy) = 1; - - tree destr_outer = push_stmt_list (); - current_stmt_tree ()->stmts_are_full_exprs_p = 1; - tree dstr_stmt = begin_compound_stmt (BCS_FN_BODY); + bool spf = start_preparsed_function (destroy, NULL_TREE, SF_PRE_PARSED); + gcc_checking_assert (spf); + tree dstr_stmt = begin_function_body (); - tree destr_frame = build1 (INDIRECT_REF, coro_frame_type, destr_fp); + tree destr_frame + = cp_build_indirect_ref (loc, destr_fp, RO_UNARY_STAR, + tf_warning_or_error); tree rat_field = lookup_member (coro_frame_type, coro_resume_index_id, 1, 0, tf_warning_or_error); - tree rat = build3 (COMPONENT_REF, short_unsigned_type_node, - destr_frame, rat_field, NULL_TREE); + tree rat + = build_class_member_access_expr (destr_frame, rat_field, NULL_TREE, + /*reference*/false, tf_warning_or_error); /* _resume_at |= 1 */ - tree dstr_idx = build2 (BIT_IOR_EXPR, short_unsigned_type_node, rat, - build_int_cst (short_unsigned_type_node, 1)); - tree r = build2 (MODIFY_EXPR, short_unsigned_type_node, rat, dstr_idx); - r = coro_build_cvt_void_expr_stmt (r, loc); - add_stmt (r); + tree dstr_idx + = build2_loc (loc, BIT_IOR_EXPR, short_unsigned_type_node, rat, + build_int_cst (short_unsigned_type_node, 1)); + tree r = cp_build_modify_expr (loc, rat, NOP_EXPR, dstr_idx, + tf_warning_or_error); + finish_expr_stmt (r); /* So .. call the actor .. */ - r = build_call_expr_loc (loc, actor, 1, destr_fp); - r = coro_build_cvt_void_expr_stmt (r, loc); - add_stmt (r); + finish_expr_stmt (build_call_expr_loc (loc, actor, 1, destr_fp)); /* done. */ - r = build_stmt (loc, RETURN_EXPR, NULL); - r = maybe_cleanup_point_expr_void (r); - add_stmt (r); + finish_return_stmt (NULL_TREE); - finish_compound_stmt (dstr_stmt); - DECL_SAVED_TREE (destroy) = pop_stmt_list (destr_outer); + gcc_checking_assert (cfun && current_function_decl); + cfun->coroutine_component = 1; + finish_function_body (dstr_stmt); + finish_function (inline_p); } /* Helper that returns an identifier for an appended extension to the @@ -2728,33 +2689,6 @@ register_await_info (tree await_expr, tree aw_type, tree aw_nam, return true; } -/* This data set is used when analyzing statements for await expressions. */ - -struct susp_frame_data -{ - /* Function-wide. */ - tree fs_label; /* The destination for co_returns. */ - hash_map *suspend_points; /* Not owned. */ - vec *block_stack; /* Track block scopes. */ - vec *bind_stack; /* Track current bind expr. */ - unsigned await_number = 0; /* Which await in the function. */ - unsigned cond_number = 0; /* Which replaced condition in the fn. */ - - /* Temporary values for one statement or expression being analyzed. */ - hash_set *truth_aoif_to_expand = nullptr; /* The set of TRUTH exprs to expand. */ - unsigned saw_awaits = 0; /* Count of awaits in this statement */ - bool captures_temporary = false; /* This expr captures temps by ref. */ - bool needs_truth_if_exp = false; /* We must expand a truth_if expression. */ - bool has_awaiter_init = false; /* We must handle initializing an awaiter. */ - - susp_frame_data (tree _final_susp, hash_map *_spt) - : fs_label (_final_susp), suspend_points (_spt) - { - block_stack = make_tree_vector (); - bind_stack = make_tree_vector (); - } -}; - /* If this is an await expression, then count it (both uniquely within the function and locally within a single statement). */ @@ -4052,22 +3986,6 @@ coro_make_frame_entry (tree *field_list, const char *name, tree fld_type, return id; } -/* For recording local variable usage. */ - -struct local_vars_frame_data -{ - tree *field_list; - hash_map *local_var_uses; - unsigned int nest_depth = 0; - unsigned int bind_indx = 0; - location_t loc = UNKNOWN_LOCATION; - bool saw_capture = false; - bool local_var_seen = false; - - local_vars_frame_data (tree *_fl, hash_map *_lvu) - : field_list (_fl), local_var_uses (_lvu) {} -}; - /* A tree-walk callback that processes one bind expression noting local variables, and making a coroutine frame slot available for those that need it, so that they can be 'promoted' across suspension points. */ @@ -4216,9 +4134,6 @@ coro_build_actor_or_destroy_function (tree orig, tree fn_type, DECL_IGNORED_P (resdecl) = 1; DECL_RESULT (fn) = resdecl; - /* This is a coroutine component. */ - DECL_COROUTINE_P (fn) = 1; - /* Set up a means to find out if a decl is one of the helpers and, if so, which one. */ if (coroutine_info *info = get_coroutine_info (orig)) @@ -4235,21 +4150,24 @@ coro_build_actor_or_destroy_function (tree orig, tree fn_type, /* Re-write the body as per [dcl.fct.def.coroutine] / 5. */ -static tree -coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig, - hash_map *param_uses, - tree resume_fn_ptr_type, - tree& resume_idx_var, tree& fs_label) +void +cp_coroutine_transform::wrap_original_function_body () { + /* Avoid the code here attaching a location that makes the debugger jump. */ + location_t save_input_loc = input_location; + location_t loc = UNKNOWN_LOCATION; + input_location = loc; + /* This will be our new outer scope. */ - tree update_body = build3 (BIND_EXPR, void_type_node, NULL, NULL, NULL); + tree update_body + = build3_loc (loc, BIND_EXPR, void_type_node, NULL, NULL, NULL); tree top_block = make_node (BLOCK); BIND_EXPR_BLOCK (update_body) = top_block; BIND_EXPR_BODY (update_body) = push_stmt_list (); /* If the function has a top level bind expression, then connect that after first making sure we give it a new block. */ - tree first = expr_first (fnbody); + tree first = expr_first (coroutine_body); if (first && TREE_CODE (first) == BIND_EXPR) { tree block = BIND_EXPR_BLOCK (first); @@ -4273,14 +4191,14 @@ coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig, { /* We are missing a top level BIND_EXPR. We need one to ensure that we don't shuffle around the coroutine frame and corrupt it. */ - tree bind_wrap = build3_loc (fn_start, BIND_EXPR, void_type_node, + tree bind_wrap = build3_loc (loc, BIND_EXPR, void_type_node, NULL, NULL, NULL); - BIND_EXPR_BODY (bind_wrap) = fnbody; + BIND_EXPR_BODY (bind_wrap) = coroutine_body; /* Ensure we have a block to connect up the scopes. */ tree new_blk = make_node (BLOCK); BIND_EXPR_BLOCK (bind_wrap) = new_blk; BLOCK_SUBBLOCKS (top_block) = new_blk; - fnbody = bind_wrap; + coroutine_body = bind_wrap; } /* Wrap the function body in a try {} catch (...) {} block, if exceptions @@ -4293,12 +4211,12 @@ coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig, coroutine is equivalent to a co_return with no operand; otherwise flowing off the end of a coroutine results in undefined behavior. */ tree return_void - = get_coroutine_return_void_expr (current_function_decl, fn_start, false); + = get_coroutine_return_void_expr (orig_fn_decl, loc, false); /* The pointer to the resume function. */ tree resume_fn_ptr - = coro_build_artificial_var (fn_start, coro_resume_fn_id, - resume_fn_ptr_type, orig, NULL_TREE); + = coro_build_artificial_var (loc, coro_resume_fn_id, + act_des_fn_ptr_type, orig_fn_decl, NULL_TREE); DECL_CHAIN (resume_fn_ptr) = var_list; var_list = resume_fn_ptr; add_decl_expr (resume_fn_ptr); @@ -4306,20 +4224,21 @@ coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig, /* We will need to be able to set the resume function pointer to nullptr to signal that the coroutine is 'done'. */ tree zero_resume - = build1 (CONVERT_EXPR, resume_fn_ptr_type, nullptr_node); + = build1 (CONVERT_EXPR, act_des_fn_ptr_type, nullptr_node); /* The pointer to the destroy function. */ - tree var = coro_build_artificial_var (fn_start, coro_destroy_fn_id, - resume_fn_ptr_type, orig, NULL_TREE); + tree var + = coro_build_artificial_var (loc, coro_destroy_fn_id, + act_des_fn_ptr_type, orig_fn_decl, NULL_TREE); DECL_CHAIN (var) = var_list; var_list = var; add_decl_expr (var); /* The promise was created on demand when parsing we now link it into our scope. */ - tree promise = get_coroutine_promise_proxy (orig); - DECL_CONTEXT (promise) = orig; - DECL_SOURCE_LOCATION (promise) = fn_start; + tree promise = get_coroutine_promise_proxy (orig_fn_decl); + DECL_CONTEXT (promise) = orig_fn_decl; + DECL_SOURCE_LOCATION (promise) = loc; DECL_CHAIN (promise) = var_list; var_list = promise; add_decl_expr (promise); @@ -4327,9 +4246,9 @@ coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig, /* We need a handle to this coroutine, which is passed to every await_suspend(). This was created on demand when parsing we now link it into our scope. */ - var = get_coroutine_self_handle_proxy (orig); - DECL_CONTEXT (var) = orig; - DECL_SOURCE_LOCATION (var) = fn_start; + var = get_coroutine_self_handle_proxy (orig_fn_decl); + DECL_CONTEXT (var) = orig_fn_decl; + DECL_SOURCE_LOCATION (var) = loc; DECL_CHAIN (var) = var_list; var_list = var; add_decl_expr (var); @@ -4341,9 +4260,9 @@ coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig, with them in the debugger. */ if (param_uses) { - gcc_checking_assert (DECL_ARGUMENTS (orig)); + gcc_checking_assert (DECL_ARGUMENTS (orig_fn_decl)); /* Add a local var for each parm. */ - for (tree arg = DECL_ARGUMENTS (orig); arg != NULL; + for (tree arg = DECL_ARGUMENTS (orig_fn_decl); arg != NULL; arg = DECL_CHAIN (arg)) { param_info *parm_i = param_uses->get (arg); @@ -4351,12 +4270,12 @@ coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig, parm_i->copy_var = build_lang_decl (VAR_DECL, parm_i->field_id, TREE_TYPE (arg)); DECL_SOURCE_LOCATION (parm_i->copy_var) = DECL_SOURCE_LOCATION (arg); - DECL_CONTEXT (parm_i->copy_var) = orig; + DECL_CONTEXT (parm_i->copy_var) = orig_fn_decl; DECL_ARTIFICIAL (parm_i->copy_var) = true; DECL_CHAIN (parm_i->copy_var) = var_list; var_list = parm_i->copy_var; add_decl_expr (parm_i->copy_var); - } + } /* Now replace all uses of the parms in the function body with the proxy vars. We want to this to apply to every instance of param's use, so @@ -4364,14 +4283,15 @@ coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig, arrange to visit each containing expression only once. */ hash_set visited; param_frame_data param_data = {NULL, param_uses, - &visited, fn_start, false}; - cp_walk_tree (&fnbody, rewrite_param_uses, ¶m_data, NULL); + &visited, loc, false}; + cp_walk_tree (&coroutine_body, rewrite_param_uses, ¶m_data, NULL); } /* We create a resume index, this is initialized in the ramp. */ resume_idx_var - = coro_build_artificial_var (fn_start, coro_resume_index_id, - short_unsigned_type_node, orig, NULL_TREE); + = coro_build_artificial_var (loc, coro_resume_index_id, + short_unsigned_type_node, orig_fn_decl, + NULL_TREE); DECL_CHAIN (resume_idx_var) = var_list; var_list = resume_idx_var; add_decl_expr (resume_idx_var); @@ -4379,7 +4299,7 @@ coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig, /* If the coroutine has a frame that needs to be freed, this will be set by the ramp. */ var = coro_build_artificial_var (fn_start, coro_frame_needs_free_id, - boolean_type_node, orig, NULL_TREE); + boolean_type_node, orig_fn_decl, NULL_TREE); DECL_CHAIN (var) = var_list; var_list = var; add_decl_expr (var); @@ -4388,20 +4308,20 @@ coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig, { /* Build promise.unhandled_exception(); */ tree ueh - = coro_build_promise_expression (current_function_decl, promise, + = coro_build_promise_expression (orig_fn_decl, promise, coro_unhandled_exception_identifier, fn_start, NULL, /*musthave=*/true); /* Create and initialize the initial-await-resume-called variable per [dcl.fct.def.coroutine] / 5.3. */ tree i_a_r_c - = coro_build_artificial_var (fn_start, coro_frame_i_a_r_c_id, - boolean_type_node, orig, + = coro_build_artificial_var (loc, coro_frame_i_a_r_c_id, + boolean_type_node, orig_fn_decl, boolean_false_node); DECL_CHAIN (i_a_r_c) = var_list; var_list = i_a_r_c; add_decl_expr (i_a_r_c); /* Start the try-catch. */ - tree tcb = build_stmt (fn_start, TRY_BLOCK, NULL_TREE, NULL_TREE); + tree tcb = build_stmt (loc, TRY_BLOCK, NULL_TREE, NULL_TREE); add_stmt (tcb); TRY_STMTS (tcb) = push_stmt_list (); if (initial_await != error_mark_node) @@ -4425,7 +4345,7 @@ coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig, /* Add the initial await to the start of the user-authored function. */ finish_expr_stmt (initial_await); /* Append the original function body. */ - add_stmt (fnbody); + add_stmt (coroutine_body); if (return_void) add_stmt (return_void); TRY_STMTS (tcb) = pop_stmt_list (TRY_STMTS (tcb)); @@ -4436,11 +4356,11 @@ coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig, /* Get the initial await resume called value. */ tree not_iarc_if = begin_if_stmt (); - tree not_iarc = build1_loc (fn_start, TRUTH_NOT_EXPR, + tree not_iarc = build1_loc (loc, TRUTH_NOT_EXPR, boolean_type_node, i_a_r_c); 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, tf_warning_or_error); + tree rethrow = build_throw (loc, NULL_TREE, tf_warning_or_error); suppress_warning (rethrow); finish_expr_stmt (rethrow); finish_then_clause (not_iarc_if); @@ -4453,8 +4373,8 @@ coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig, If the unhandled exception method returns, then we continue to the final await expression (which duplicates the clearing of the field). */ - tree r = build2 (MODIFY_EXPR, resume_fn_ptr_type, resume_fn_ptr, - zero_resume); + tree r = build2_loc (loc, MODIFY_EXPR, act_des_fn_ptr_type, resume_fn_ptr, + zero_resume); finish_expr_stmt (r); tree short_zero = build_int_cst (short_unsigned_type_node, 0); r = build2 (MODIFY_EXPR, short_unsigned_type_node, resume_idx_var, @@ -4471,39 +4391,41 @@ coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig, /* We still try to look for the promise method and warn if it's not present. */ tree ueh_meth - = lookup_promise_method (orig, coro_unhandled_exception_identifier, + = lookup_promise_method (orig_fn_decl, + coro_unhandled_exception_identifier, fn_start, /*musthave=*/false); if (!ueh_meth || ueh_meth == error_mark_node) warning_at (fn_start, 0, "no member named %qE in %qT", coro_unhandled_exception_identifier, - get_coroutine_promise_type (orig)); + get_coroutine_promise_type (orig_fn_decl)); } /* Else we don't check and don't care if the method is missing.. just add the initial suspend, function and return. */ finish_expr_stmt (initial_await); /* Append the original function body. */ - add_stmt (fnbody); + add_stmt (coroutine_body); if (return_void) add_stmt (return_void); } /* co_return branches to the final_suspend label, so declare that now. */ fs_label - = create_named_label_with_ctx (fn_start, "final.suspend", NULL_TREE); - add_stmt (build_stmt (fn_start, LABEL_EXPR, fs_label)); + = create_named_label_with_ctx (loc, "final.suspend", NULL_TREE); + add_stmt (build_stmt (loc, LABEL_EXPR, fs_label)); /* Before entering the final suspend point, we signal that this point has been reached by setting the resume function pointer to zero (this is what the 'done()' builtin tests) as per the current ABI. */ - zero_resume = build2 (MODIFY_EXPR, resume_fn_ptr_type, resume_fn_ptr, - zero_resume); + zero_resume = build2_loc (loc, MODIFY_EXPR, act_des_fn_ptr_type, + resume_fn_ptr, zero_resume); finish_expr_stmt (zero_resume); finish_expr_stmt (build_init_or_final_await (fn_start, true)); BIND_EXPR_BODY (update_body) = pop_stmt_list (BIND_EXPR_BODY (update_body)); BIND_EXPR_VARS (update_body) = nreverse (var_list); BLOCK_VARS (top_block) = BIND_EXPR_VARS (update_body); - return update_body; + input_location = save_input_loc; + coroutine_body = update_body; } /* Extract the body of the function we are going to outline, leaving @@ -4559,15 +4481,17 @@ split_coroutine_body_from_ramp (tree fndecl) We should arrive here with the state of the compiler as if we had just executed start_preparsed_function(). */ -static tree -build_ramp_function (tree orig, location_t fn_start, tree coro_frame_ptr, - tree coro_frame_type, - hash_map *param_uses, - tree act_des_fn_ptr, tree actor, tree destroy, - vec *param_dtor_list) +void +cp_coroutine_transform::complete_ramp_function () { - tree promise_type = get_coroutine_promise_type (orig); - tree fn_return_type = TREE_TYPE (TREE_TYPE (orig)); + gcc_checking_assert (current_binding_level + && current_binding_level->kind == sk_function_parms); + + /* Avoid the code here attaching a location that makes the debugger jump. */ + location_t loc = fn_start; + + tree promise_type = get_coroutine_promise_type (orig_fn_decl); + tree fn_return_type = TREE_TYPE (TREE_TYPE (orig_fn_decl)); /* Ramp: */ tree stmt = begin_function_body (); @@ -4576,15 +4500,16 @@ build_ramp_function (tree orig, location_t fn_start, tree coro_frame_ptr, add_stmt (ramp_bind); tree ramp_body = push_stmt_list (); - tree zeroinit = build1_loc (fn_start, CONVERT_EXPR, - coro_frame_ptr, nullptr_node); - tree coro_fp = coro_build_artificial_var (fn_start, "_Coro_frameptr", - coro_frame_ptr, orig, zeroinit); + tree zeroinit = build1_loc (loc, CONVERT_EXPR, + frame_ptr_type, nullptr_node); + tree coro_fp = coro_build_artificial_var (loc, "_Coro_frameptr", + frame_ptr_type, orig_fn_decl, + zeroinit); tree varlist = coro_fp; /* To signal that we need to cleanup copied function args. */ - if (flag_exceptions && DECL_ARGUMENTS (orig)) - for (tree arg = DECL_ARGUMENTS (orig); arg != NULL; + if (flag_exceptions && DECL_ARGUMENTS (orig_fn_decl)) + for (tree arg = DECL_ARGUMENTS (orig_fn_decl); arg != NULL; arg = DECL_CHAIN (arg)) { param_info *parm_i = param_uses->get (arg); @@ -4597,16 +4522,16 @@ build_ramp_function (tree orig, location_t fn_start, tree coro_frame_ptr, /* Signal that we need to clean up the promise object on exception. */ tree coro_promise_live - = coro_build_artificial_var (fn_start, "_Coro_promise_live", - boolean_type_node, orig, boolean_false_node); + = coro_build_artificial_var (loc, "_Coro_promise_live", + boolean_type_node, orig_fn_decl, boolean_false_node); DECL_CHAIN (coro_promise_live) = varlist; varlist = coro_promise_live; /* When the get-return-object is in the RETURN slot, we need to arrange for cleanup on exception. */ tree coro_gro_live - = coro_build_artificial_var (fn_start, "_Coro_gro_live", - boolean_type_node, orig, boolean_false_node); + = coro_build_artificial_var (loc, "_Coro_gro_live", + boolean_type_node, orig_fn_decl, boolean_false_node); DECL_CHAIN (coro_gro_live) = varlist; varlist = coro_gro_live; @@ -4628,8 +4553,8 @@ build_ramp_function (tree orig, location_t fn_start, tree coro_frame_ptr, directly apparently). This avoids a "used uninitialized" warning. */ add_decl_expr (coro_fp); - if (flag_exceptions && DECL_ARGUMENTS (orig)) - for (tree arg = DECL_ARGUMENTS (orig); arg != NULL; + if (flag_exceptions && DECL_ARGUMENTS (orig_fn_decl)) + for (tree arg = DECL_ARGUMENTS (orig_fn_decl); arg != NULL; arg = DECL_CHAIN (arg)) { param_info *parm_i = param_uses->get (arg); @@ -4644,9 +4569,9 @@ build_ramp_function (tree orig, location_t fn_start, tree coro_frame_ptr, to adjust the allocation in response to optimizations. We provide the current conservative estimate of the frame size (as per the current) computed layout. */ - tree frame_size = TYPE_SIZE_UNIT (coro_frame_type); + frame_size = TYPE_SIZE_UNIT (frame_type); tree resizeable - = build_call_expr_internal_loc (fn_start, IFN_CO_FRAME, size_type_node, 2, + = build_call_expr_internal_loc (loc, IFN_CO_FRAME, size_type_node, 2, frame_size, coro_fp); /* [dcl.fct.def.coroutine] / 10 (part1) @@ -4655,11 +4580,11 @@ build_ramp_function (tree orig, location_t fn_start, tree coro_frame_ptr, /* We don't require this, so coro_build_promise_expression can return NULL, but, if the lookup succeeds, then the function must be usable. */ - tree dummy_promise = build_dummy_object (get_coroutine_promise_type (orig)); + tree dummy_promise = build_dummy_object (get_coroutine_promise_type (orig_fn_decl)); tree grooaf - = coro_build_promise_expression (orig, dummy_promise, + = coro_build_promise_expression (orig_fn_decl, dummy_promise, coro_gro_on_allocation_fail_identifier, - fn_start, NULL, /*musthave=*/false); + loc, NULL, /*musthave=*/false); /* however, should that fail, returning an error, the later stages can't handle the erroneous expression, so we reset the call as if it was @@ -4677,7 +4602,7 @@ build_ramp_function (tree orig, location_t fn_start, tree coro_frame_ptr, if (TYPE_HAS_NEW_OPERATOR (promise_type)) { - tree fns = lookup_promise_method (orig, nwname, fn_start, + tree fns = lookup_promise_method (orig_fn_decl, nwname, fn_start, /*musthave=*/true); /* [dcl.fct.def.coroutine] / 9 (part 2) If the lookup finds an allocation function in the scope of the promise @@ -4688,7 +4613,7 @@ build_ramp_function (tree orig, location_t fn_start, tree coro_frame_ptr, vec *args = make_tree_vector (); vec_safe_push (args, resizeable); /* Space needed. */ - for (tree arg = DECL_ARGUMENTS (orig); arg != NULL; + for (tree arg = DECL_ARGUMENTS (orig_fn_decl); arg != NULL; arg = DECL_CHAIN (arg)) { param_info *parm_i = param_uses->get (arg); @@ -4730,12 +4655,19 @@ build_ramp_function (tree orig, location_t fn_start, tree coro_frame_ptr, if (new_fn == error_mark_node) { error_at (fn_start, "%qE is provided by %qT but is not usable with" - " the function signature %qD", nwname, promise_type, orig); + " the function signature %qD", nwname, promise_type, + orig_fn_decl); new_fn = error_mark_node; + valid_coroutine = false; + return; } else if (grooaf && !TYPE_NOTHROW_P (TREE_TYPE (func))) - error_at (fn_start, "%qE is provided by %qT but %qE is not marked" + { + error_at (fn_start, "%qE is provided by %qT but %qE is not marked" " % or %", grooaf, promise_type, nwname); + valid_coroutine = false; + return; + } else if (!grooaf && TYPE_NOTHROW_P (TREE_TYPE (func))) warning_at (fn_start, 0, "%qE is marked % or % but" " no usable %" @@ -4781,7 +4713,7 @@ build_ramp_function (tree orig, location_t fn_start, tree coro_frame_ptr, /*size_check=*/NULL, /*fn=*/NULL, tf_warning_or_error); resizeable = build_call_expr_internal_loc - (fn_start, IFN_CO_FRAME, size_type_node, 2, frame_size, coro_fp); + (loc, IFN_CO_FRAME, size_type_node, 2, frame_size, coro_fp); /* If the operator call fails for some reason, then don't try to amend it. */ if (new_fn != error_mark_node) @@ -4790,10 +4722,9 @@ build_ramp_function (tree orig, location_t fn_start, tree coro_frame_ptr, release_tree_vector (args); } - tree allocated = build1 (CONVERT_EXPR, coro_frame_ptr, new_fn); + tree allocated = build1 (CONVERT_EXPR, frame_ptr_type, new_fn); tree r = cp_build_init_expr (coro_fp, allocated); - r = coro_build_cvt_void_expr_stmt (r, fn_start); - add_stmt (r); + finish_expr_stmt (r); /* If the user provided a method to return an object on alloc fail, then check the returned pointer and call the func if it's null. @@ -4809,7 +4740,7 @@ build_ramp_function (tree orig, location_t fn_start, tree coro_frame_ptr, gcc_checking_assert (same_type_p (fn_return_type, TREE_TYPE (grooaf))); tree if_stmt = begin_if_stmt (); - tree cond = build1 (CONVERT_EXPR, coro_frame_ptr, nullptr_node); + tree cond = build1 (CONVERT_EXPR, frame_ptr_type, nullptr_node); cond = build2 (EQ_EXPR, boolean_type_node, coro_fp, cond); finish_if_stmt_cond (cond, if_stmt); if (VOID_TYPE_P (fn_return_type)) @@ -4838,43 +4769,42 @@ build_ramp_function (tree orig, location_t fn_start, tree coro_frame_ptr, tree ramp_cleanup = NULL_TREE; if (flag_exceptions) { - ramp_cleanup = build_stmt (fn_start, TRY_BLOCK, NULL, NULL); + ramp_cleanup = build_stmt (loc, TRY_BLOCK, NULL, NULL); add_stmt (ramp_cleanup); TRY_STMTS (ramp_cleanup) = push_stmt_list (); } /* deref the frame pointer, to use in member access code. */ - tree deref_fp = build_x_arrow (fn_start, coro_fp, tf_warning_or_error); + tree deref_fp = build_x_arrow (loc, coro_fp, tf_warning_or_error); /* For now, once allocation has succeeded we always assume that this needs destruction, there's no impl. for frame allocation elision. */ - tree fnf_m = lookup_member (coro_frame_type, coro_frame_needs_free_id, + tree fnf_m = lookup_member (frame_type, coro_frame_needs_free_id, 1, 0,tf_warning_or_error); tree fnf_x = build_class_member_access_expr (deref_fp, fnf_m, NULL_TREE, false, tf_warning_or_error); r = cp_build_init_expr (fnf_x, boolean_true_node); - r = coro_build_cvt_void_expr_stmt (r, fn_start); - add_stmt (r); + finish_expr_stmt (r); /* Put the resumer and destroyer functions in. */ - tree actor_addr = build1 (ADDR_EXPR, act_des_fn_ptr, actor); + tree actor_addr = build1 (ADDR_EXPR, act_des_fn_ptr_type, resumer); tree resume_m - = lookup_member (coro_frame_type, coro_resume_fn_id, + = lookup_member (frame_type, coro_resume_fn_id, /*protect=*/1, /*want_type=*/0, tf_warning_or_error); tree resume_x = build_class_member_access_expr (deref_fp, resume_m, NULL_TREE, false, tf_warning_or_error); - r = cp_build_init_expr (fn_start, resume_x, actor_addr); + r = cp_build_init_expr (loc, resume_x, actor_addr); finish_expr_stmt (r); - tree destroy_addr = build1 (ADDR_EXPR, act_des_fn_ptr, destroy); + tree destroy_addr = build1 (ADDR_EXPR, act_des_fn_ptr_type, destroyer); tree destroy_m - = lookup_member (coro_frame_type, coro_destroy_fn_id, + = lookup_member (frame_type, coro_destroy_fn_id, /*protect=*/1, /*want_type=*/0, tf_warning_or_error); tree destroy_x = build_class_member_access_expr (deref_fp, destroy_m, NULL_TREE, false, tf_warning_or_error); - r = cp_build_init_expr (fn_start, destroy_x, destroy_addr); + r = cp_build_init_expr (loc, destroy_x, destroy_addr); finish_expr_stmt (r); /* [dcl.fct.def.coroutine] /13 @@ -4895,16 +4825,16 @@ build_ramp_function (tree orig, location_t fn_start, tree coro_frame_ptr, parameter copies ends immediately after the lifetime of the coroutine promise object ends. */ - if (DECL_ARGUMENTS (orig)) + if (DECL_ARGUMENTS (orig_fn_decl)) { promise_args = make_tree_vector (); - for (tree arg = DECL_ARGUMENTS (orig); arg != NULL; + for (tree arg = DECL_ARGUMENTS (orig_fn_decl); arg != NULL; arg = DECL_CHAIN (arg)) { bool existed; param_info &parm = param_uses->get_or_insert (arg, &existed); - tree fld_ref = lookup_member (coro_frame_type, parm.field_id, + tree fld_ref = lookup_member (frame_type, parm.field_id, /*protect=*/1, /*want_type=*/0, tf_warning_or_error); tree fld_idx @@ -4928,26 +4858,26 @@ build_ramp_function (tree orig, location_t fn_start, tree coro_frame_ptr, if (parm.rv_ref || parm.pt_ref) /* Initialise the frame reference field directly. */ - r = cp_build_modify_expr (fn_start, TREE_OPERAND (fld_idx, 0), + r = cp_build_modify_expr (loc, TREE_OPERAND (fld_idx, 0), INIT_EXPR, arg, tf_warning_or_error); else { r = forward_parm (arg); - r = cp_build_modify_expr (fn_start, fld_idx, INIT_EXPR, r, + r = cp_build_modify_expr (loc, fld_idx, INIT_EXPR, r, tf_warning_or_error); } finish_expr_stmt (r); if (!parm.trivial_dtor) { - param_dtor_list->safe_push (parm.field_id); + param_dtor_list.safe_push (parm.field_id); /* Cleanup this frame copy on exception. */ parm.fr_copy_dtor = cxx_maybe_build_cleanup (fld_idx, tf_warning_or_error); if (flag_exceptions) { /* This var is now live. */ - r = build_modify_expr (fn_start, parm.guard_var, - boolean_type_node, INIT_EXPR, fn_start, + r = build_modify_expr (loc, parm.guard_var, + boolean_type_node, INIT_EXPR, loc, boolean_true_node, boolean_type_node); finish_expr_stmt (r); } @@ -4957,7 +4887,7 @@ build_ramp_function (tree orig, location_t fn_start, tree coro_frame_ptr, /* Set up the promise. */ tree promise_m - = lookup_member (coro_frame_type, coro_promise_id, + = lookup_member (frame_type, coro_promise_id, /*protect=*/1, /*want_type=*/0, tf_warning_or_error); tree p = build_class_member_access_expr (deref_fp, promise_m, NULL_TREE, @@ -4974,7 +4904,7 @@ build_ramp_function (tree orig, location_t fn_start, tree coro_frame_ptr, original function (if it has params), failing that find a constructor with no parameter list. */ - if (DECL_ARGUMENTS (orig)) + if (DECL_ARGUMENTS (orig_fn_decl)) { r = build_special_member_call (p, complete_ctor_identifier, &promise_args, promise_type, @@ -4989,11 +4919,10 @@ build_ramp_function (tree orig, location_t fn_start, tree coro_frame_ptr, promise_type, LOOKUP_NORMAL, tf_warning_or_error); - r = coro_build_cvt_void_expr_stmt (r, fn_start); finish_expr_stmt (r); - r = build_modify_expr (fn_start, coro_promise_live, boolean_type_node, - INIT_EXPR, fn_start, boolean_true_node, + r = build_modify_expr (loc, coro_promise_live, boolean_type_node, + INIT_EXPR, loc, boolean_true_node, boolean_type_node); finish_expr_stmt (r); @@ -5010,7 +4939,7 @@ build_ramp_function (tree orig, location_t fn_start, tree coro_frame_ptr, add_stmt (gro_context_bind); tree get_ro - = coro_build_promise_expression (orig, p, + = coro_build_promise_expression (orig_fn_decl, p, coro_get_return_object_identifier, fn_start, NULL, /*musthave=*/true); /* Without a return object we haven't got much clue what's going on. */ @@ -5018,8 +4947,9 @@ build_ramp_function (tree orig, location_t fn_start, tree coro_frame_ptr, { BIND_EXPR_BODY (ramp_bind) = pop_stmt_list (ramp_body); /* Suppress warnings about the missing return value. */ - suppress_warning (orig, OPT_Wreturn_type); - return NULL_TREE; + suppress_warning (orig_fn_decl, OPT_Wreturn_type); + valid_coroutine = false; + return; } tree gro_context_body = push_stmt_list (); @@ -5044,27 +4974,27 @@ build_ramp_function (tree orig, location_t fn_start, tree coro_frame_ptr, if (type_build_ctor_call (gro_type)) { vec *arg = make_tree_vector_single (get_ro); - r = build_special_member_call (DECL_RESULT (orig), + r = build_special_member_call (DECL_RESULT (orig_fn_decl), complete_ctor_identifier, &arg, gro_type, LOOKUP_NORMAL, tf_warning_or_error); release_tree_vector (arg); } else - r = cp_build_init_expr (fn_start, DECL_RESULT (orig), get_ro); + r = cp_build_init_expr (loc, DECL_RESULT (orig_fn_decl), get_ro); if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (gro_type)) /* If some part of the initalization code (prior to the await_resume of the initial suspend expression), then we need to clean up the return value. */ - gro_ret_dtor = cxx_maybe_build_cleanup (DECL_RESULT (orig), + gro_ret_dtor = cxx_maybe_build_cleanup (DECL_RESULT (orig_fn_decl), tf_warning_or_error); } else { /* ... or ... Construct an object that will be used as the single param to the CTOR for the return object. */ - gro = coro_build_artificial_var (fn_start, "_Coro_gro", gro_type, orig, + gro = coro_build_artificial_var (loc, "_Coro_gro", gro_type, orig_fn_decl, NULL_TREE); add_decl_expr (gro); gro_bind_vars = gro; @@ -5084,27 +5014,25 @@ build_ramp_function (tree orig, location_t fn_start, tree coro_frame_ptr, cleanup. */ if (gro_ret_dtor) { - r = build_modify_expr (fn_start, coro_gro_live, boolean_type_node, - INIT_EXPR, fn_start, boolean_true_node, + r = build_modify_expr (loc, coro_gro_live, boolean_type_node, + INIT_EXPR, loc, boolean_true_node, boolean_type_node); finish_expr_stmt (r); } /* Initialize the resume_idx_var to 0, meaning "not started". */ tree resume_idx_m - = lookup_member (coro_frame_type, coro_resume_index_id, + = lookup_member (frame_type, coro_resume_index_id, /*protect=*/1, /*want_type=*/0, tf_warning_or_error); tree resume_idx = build_class_member_access_expr (deref_fp, resume_idx_m, NULL_TREE, false, tf_warning_or_error); r = build_int_cst (short_unsigned_type_node, 0); - r = cp_build_init_expr (fn_start, resume_idx, r); - r = coro_build_cvt_void_expr_stmt (r, fn_start); - add_stmt (r); + r = cp_build_init_expr (loc, resume_idx, r); + finish_expr_stmt (r); /* So .. call the actor .. */ - r = build_call_expr_loc (fn_start, actor, 1, coro_fp); - r = maybe_cleanup_point_expr_void (r); - add_stmt (r); + r = build_call_expr_loc (fn_start, resumer, 1, coro_fp); + finish_expr_stmt (r); /* Switch to using 'input_location' as the loc, since we're now more logically doing things related to the end of the function. */ @@ -5120,7 +5048,7 @@ build_ramp_function (tree orig, location_t fn_start, tree coro_frame_ptr, for an object of the return type. */ if (same_type_p (gro_type, fn_return_type)) - r = gro_is_void_p ? NULL_TREE : DECL_RESULT (orig); + r = gro_is_void_p ? NULL_TREE : DECL_RESULT (orig_fn_decl); else if (!gro_is_void_p) /* check_return_expr will automatically return gro as an rvalue via treat_lvalue_as_rvalue_p. */ @@ -5141,6 +5069,8 @@ build_ramp_function (tree orig, location_t fn_start, tree coro_frame_ptr, error_at (input_location, "cannot initialize a return object of type" " %qT with an rvalue of type %", fn_return_type); r = error_mark_node; + valid_coroutine = false; + return; } finish_return_stmt (r); @@ -5194,8 +5124,8 @@ build_ramp_function (tree orig, location_t fn_start, tree coro_frame_ptr, /* Clean up any frame copies of parms with non-trivial dtors. Do this in reverse order from their creation. */ vec worklist = vNULL; - if (DECL_ARGUMENTS (orig)) - for (tree arg = DECL_ARGUMENTS (orig); arg != NULL; + if (DECL_ARGUMENTS (orig_fn_decl)) + for (tree arg = DECL_ARGUMENTS (orig_fn_decl); arg != NULL; arg = DECL_CHAIN (arg)) { param_info *parm_i = param_uses->get (arg); @@ -5220,10 +5150,13 @@ build_ramp_function (tree orig, location_t fn_start, tree coro_frame_ptr, } /* We always expect to delete the frame. */ - tree del_coro_fr = coro_get_frame_dtor (coro_fp, orig, frame_size, - promise_type, fn_start); - finish_expr_stmt (del_coro_fr); - tree rethrow = build_throw (fn_start, NULL_TREE, tf_warning_or_error); + tree del_coro_fr = coro_get_frame_dtor (coro_fp, orig_fn_decl, frame_size, + promise_type, loc); + if (!del_coro_fr || del_coro_fr == error_mark_node) + valid_coroutine = false; /* Do not add this. */ + else + finish_expr_stmt (del_coro_fr); + tree rethrow = build_throw (loc, NULL_TREE, tf_warning_or_error); suppress_warning (rethrow); finish_expr_stmt (rethrow); finish_handler (handler); @@ -5232,7 +5165,53 @@ build_ramp_function (tree orig, location_t fn_start, tree coro_frame_ptr, BIND_EXPR_BODY (ramp_bind) = pop_stmt_list (ramp_body); finish_function_body (stmt); - return frame_size; +} + +/* ------- Encapsulate analysis of the couroutine -------- */ + + +cp_coroutine_transform::cp_coroutine_transform (tree _orig_fn, bool _inl) + : orig_fn_decl (_orig_fn), inline_p (_inl) + { + /* We don't expect to be called with missing decl or e_m_n. */ + gcc_checking_assert (orig_fn_decl + && TREE_CODE (orig_fn_decl) == FUNCTION_DECL); + if (!coro_function_valid_p (orig_fn_decl)) + { + /* For early errors, we do not want a diagnostic about the missing + ramp return value, since the user cannot fix this - a 'return' is + not allowed in a coroutine. */ + suppress_warning (orig_fn_decl, OPT_Wreturn_type); + /* Discard the body, we can't process it further... */ + pop_stmt_list (DECL_SAVED_TREE (orig_fn_decl)); + /* ... and make an empty fn. */ + DECL_SAVED_TREE (orig_fn_decl) = push_stmt_list (); + /* Match the expected nesting when an eh block is in use. */ + if (use_eh_spec_block (orig_fn_decl)) + current_eh_spec_block = begin_eh_spec_block (); + valid_coroutine = false; + } + + /* We don't have the locus of the opening brace - it's filled in later (and + there doesn't really seem to be any easy way to get at it). + The closing brace is assumed to be input_location. */ + fn_start = DECL_SOURCE_LOCATION (orig_fn_decl); + + /* Build types we need. */ + tree fr_name = get_fn_local_identifier (orig_fn_decl, "Frame"); + frame_type = xref_tag (record_type, fr_name); + DECL_CONTEXT (TYPE_NAME (frame_type)) = DECL_CONTEXT (orig_fn_decl); + frame_ptr_type = build_pointer_type (frame_type); + act_des_fn_type + = build_function_type_list (void_type_node, frame_ptr_type, NULL_TREE); + act_des_fn_ptr_type = build_pointer_type (act_des_fn_type); + valid_coroutine = true; + } + +cp_coroutine_transform::~cp_coroutine_transform () +{ + if (param_uses) + delete param_uses; } /* Here we: @@ -5265,38 +5244,20 @@ build_ramp_function (tree orig, location_t fn_start, tree coro_frame_ptr, (maybe) trailing space. }; */ -bool -morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer) +void +cp_coroutine_transform::apply_transforms () { - gcc_checking_assert (orig && TREE_CODE (orig) == FUNCTION_DECL); - *resumer = error_mark_node; - *destroyer = error_mark_node; - if (!coro_function_valid_p (orig)) + coroutine_body + = split_coroutine_body_from_ramp (orig_fn_decl); + if (!coroutine_body) { - /* For early errors, we do not want a diagnostic about the missing - ramp return value, since the user cannot fix this - a 'return' is - not allowed in a coroutine. */ - suppress_warning (orig, OPT_Wreturn_type); - /* Discard the body, we can't process it further. */ - pop_stmt_list (DECL_SAVED_TREE (orig)); - DECL_SAVED_TREE (orig) = push_stmt_list (); - /* Match the expected nesting when an eh block is in use. */ - if (use_eh_spec_block (orig)) - current_eh_spec_block = begin_eh_spec_block (); - return false; + valid_coroutine = false; + return; } - - /* We don't have the locus of the opening brace - it's filled in later (and - there doesn't really seem to be any easy way to get at it). - The closing brace is assumed to be input_location. */ - location_t fn_start = DECL_SOURCE_LOCATION (orig); - - /* FIXME: This has the hidden side-effect of preparing the current function - to be the ramp. */ - tree fnbody = split_coroutine_body_from_ramp (orig); - if (!fnbody) - return false; + /* Keep the original function block tree to one side and reset. */ + body_blocks = current_binding_level->blocks; + current_binding_level->blocks = NULL_TREE; /* If the original function has a return value with a non-trivial DTOR and the body contains a var with a DTOR that might throw, the decl is @@ -5309,100 +5270,56 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer) not need to transfer it to the actor which only contains void returns. */ cp_function_chain->throwing_cleanup = false; - /* Create the coro frame type, as far as it can be known at this stage. - 1. Types we already know. */ - - /* 2. Types we need to define or look up. */ - - tree fr_name = get_fn_local_identifier (orig, "Frame"); - tree coro_frame_type = xref_tag (record_type, fr_name); - DECL_CONTEXT (TYPE_NAME (coro_frame_type)) = DECL_CONTEXT (orig); //current_scope (); - tree coro_frame_ptr = build_pointer_type (coro_frame_type); - tree act_des_fn_type - = build_function_type_list (void_type_node, coro_frame_ptr, NULL_TREE); - tree act_des_fn_ptr = build_pointer_type (act_des_fn_type); - - /* Construct the wrapped function body; we will analyze this to determine - the requirements for the coroutine frame. */ - - tree resume_idx_var = NULL_TREE; - tree fs_label = NULL_TREE; - hash_map *param_uses = analyze_fn_parms (orig); + /* Collect information on the original function params and their use in the + function body. */ + param_uses = analyze_fn_parms (orig_fn_decl); - fnbody = coro_rewrite_function_body (fn_start, fnbody, orig, param_uses, - act_des_fn_ptr, - resume_idx_var, fs_label); + /* Declare the actor and destroyer functions, the following code needs to + see these. */ + resumer + = coro_build_actor_or_destroy_function (orig_fn_decl, act_des_fn_type, + frame_ptr_type, true); + destroyer + = coro_build_actor_or_destroy_function (orig_fn_decl, act_des_fn_type, + frame_ptr_type, false); - /* We need to know, and inspect, each suspend point in the function - in several places. It's convenient to place this map out of line - since it's used from tree walk callbacks. */ + /* Transform the function body as per [dcl.fct.def.coroutine] / 5. */ + wrap_original_function_body (); - hash_map suspend_points; - /* Now insert the data for any body await points, at this time we also need - to promote any temporaries that are captured by reference (to regular - vars) they will get added to the coro frame along with other locals. */ + /* Analyze the body await expressions. */ susp_frame_data body_aw_points (fs_label, &suspend_points); - cp_walk_tree (&fnbody, await_statement_walker, &body_aw_points, NULL); - - /* 4. Now make space for local vars, this is conservative again, and we - would expect to delete unused entries later. Compose the frame entries - list. */ + cp_walk_tree (&coroutine_body, await_statement_walker, &body_aw_points, NULL); + await_count = body_aw_points.await_number; - /* The fields for the coro frame. */ + /* Determine the fields for the coro frame. */ tree field_list = NULL_TREE; - hash_map local_var_uses; local_vars_frame_data local_vars_data (&field_list, &local_var_uses); - cp_walk_tree (&fnbody, register_local_var_uses, &local_vars_data, NULL); - - /* Tie off the struct for now, so that we can build offsets to the - known entries. */ - coro_frame_type = begin_class_definition (coro_frame_type); - TYPE_FIELDS (coro_frame_type) = field_list; - TYPE_BINFO (coro_frame_type) = make_tree_binfo (0); - BINFO_OFFSET (TYPE_BINFO (coro_frame_type)) = size_zero_node; - BINFO_TYPE (TYPE_BINFO (coro_frame_type)) = coro_frame_type; - coro_frame_type = finish_struct (coro_frame_type, NULL_TREE); - - /* Declare the actor and destroyer function. */ - tree actor = coro_build_actor_or_destroy_function (orig, act_des_fn_type, - coro_frame_ptr, true); - tree destroy = coro_build_actor_or_destroy_function (orig, act_des_fn_type, - coro_frame_ptr, false); - - vec param_dtor_list = vNULL; - tree frame_size - = build_ramp_function (orig, fn_start, coro_frame_ptr, coro_frame_type, - param_uses, act_des_fn_ptr, actor, destroy, - ¶m_dtor_list); - if (!frame_size) - return false; + cp_walk_tree (&coroutine_body, register_local_var_uses, &local_vars_data, NULL); - /* Start to build the final functions. + /* Conservative computation of the coroutine frame content. */ + frame_type = begin_class_definition (frame_type); + TYPE_FIELDS (frame_type) = field_list; + TYPE_BINFO (frame_type) = make_tree_binfo (0); + BINFO_OFFSET (TYPE_BINFO (frame_type)) = size_zero_node; + BINFO_TYPE (TYPE_BINFO (frame_type)) = frame_type; + frame_type = finish_struct (frame_type, NULL_TREE); - We push_deferring_access_checks to avoid these routines being seen as - nested by the middle end; we are doing the outlining here. */ + complete_ramp_function (); +} - push_deferring_access_checks (dk_no_check); +void +cp_coroutine_transform::finish_transforms () +{ + if (!valid_coroutine) + return; - /* Build the actor... */ - build_actor_fn (fn_start, coro_frame_type, actor, fnbody, orig, + current_function_decl = resumer; + build_actor_fn (fn_start, frame_type, resumer, coroutine_body, orig_fn_decl, &local_var_uses, &suspend_points, ¶m_dtor_list, - resume_idx_var, body_aw_points.await_number, frame_size); + resume_idx_var, await_count, frame_size, inline_p); - /* Destroyer ... */ - build_destroy_fn (fn_start, coro_frame_type, destroy, actor); - - pop_deferring_access_checks (); - - /* Link our new functions into the list. */ - TREE_CHAIN (destroy) = TREE_CHAIN (orig); - TREE_CHAIN (actor) = destroy; - TREE_CHAIN (orig) = actor; - - *resumer = actor; - *destroyer = destroy; - - return true; + current_function_decl = destroyer; + build_destroy_fn (fn_start, frame_type, destroyer, resumer, inline_p); } #include "gt-cp-coroutines.h" diff --git a/gcc/cp/coroutines.h b/gcc/cp/coroutines.h new file mode 100644 index 00000000000..121f20ed4f0 --- /dev/null +++ b/gcc/cp/coroutines.h @@ -0,0 +1,132 @@ + +/* This caches information that we determine about function params, + their uses and copies in the coroutine frame. */ + +struct param_info +{ + tree field_id; /* The name of the copy in the coroutine frame. */ + tree copy_var; /* The local var proxy for the frame copy. */ + vec *body_uses; /* Worklist of uses, void if there are none. */ + tree frame_type; /* The type used to represent this parm in the frame. */ + tree orig_type; /* The original type of the parm (not as passed). */ + tree guard_var; /* If we need a DTOR on exception, this bool guards it. */ + tree fr_copy_dtor; /* If we need a DTOR on exception, this is it. */ + bool by_ref; /* Was passed by reference. */ + bool pt_ref; /* Was a pointer to object. */ + bool rv_ref; /* Was an rvalue ref. */ + bool trivial_dtor; /* The frame type has a trivial DTOR. */ + bool this_ptr; /* Is 'this' */ + bool lambda_cobj; /* Lambda capture object */ +}; + +/* Suspend point hash_map. */ + +struct suspend_point_info +{ + /* coro frame field type. */ + tree awaitable_type; + /* coro frame field name. */ + tree await_field_id; +}; + +/* This data set is used when analyzing statements for await expressions. */ + +struct susp_frame_data +{ + /* Function-wide. */ + tree fs_label; /* The destination for co_returns. */ + hash_map *suspend_points; /* Not owned. */ + vec *block_stack; /* Track block scopes. */ + vec *bind_stack; /* Track current bind expr. */ + unsigned await_number = 0; /* Which await in the function. */ + unsigned cond_number = 0; /* Which replaced condition in the fn. */ + + /* Temporary values for one statement or expression being analyzed. */ + /* The set of TRUTH exprs to expand. */ + hash_set *truth_aoif_to_expand = nullptr; + /* Count of awaits in this statement */ + unsigned saw_awaits = 0; + /* This expr captures temps by ref. */ + bool captures_temporary = false; + /* We must expand a truth_if expression. */ + bool needs_truth_if_exp = false; + /* We must handle initializing an awaiter. */ + bool has_awaiter_init = false; + + susp_frame_data (tree _final_susp, hash_map *_spt) + : fs_label (_final_susp), suspend_points (_spt) + { + block_stack = make_tree_vector (); + bind_stack = make_tree_vector (); + } +}; + +struct local_var_info +{ + tree field_id; + tree field_idx; + tree frame_type; + bool is_lambda_capture; + bool is_static; + bool has_value_expr_p; + location_t def_loc; +}; + +/* For recording local variable usage. */ + +struct local_vars_frame_data +{ + tree *field_list; + hash_map *local_var_uses; + unsigned int nest_depth = 0; + unsigned int bind_indx = 0; + location_t loc = UNKNOWN_LOCATION; + bool saw_capture = false; + bool local_var_seen = false; + + local_vars_frame_data (tree *_fl, hash_map *_lvu) + : field_list (_fl), local_var_uses (_lvu) {} +}; + +class cp_coroutine_transform { +public: + cp_coroutine_transform (tree, bool); + ~cp_coroutine_transform (); + + bool cp_valid_coroutine () const { return valid_coroutine; } + void apply_transforms (); + void finish_transforms (); + tree get_resumer () { return resumer; } + tree get_destroyer () { return destroyer; } + +private: + tree orig_fn_decl; /* The original function decl. */ + tree orig_fn_body = NULL_TREE; /* The original function body. */ + location_t fn_start = UNKNOWN_LOCATION; + tree resumer = error_mark_node; + tree destroyer = error_mark_node; + tree coroutine_body = NULL_TREE; + tree body_blocks = NULL_TREE; + + /* Types for this coroutine. */ + tree frame_type; + tree frame_ptr_type; + tree act_des_fn_type; + tree act_des_fn_ptr_type; + + /* Cached information about the transformed function. */ + tree resume_idx_var = NULL_TREE; + tree fs_label = NULL_TREE; + hash_map *param_uses = nullptr; + hash_map suspend_points; + hash_map local_var_uses; + vec param_dtor_list = vNULL; + tree frame_size = NULL_TREE; + unsigned int await_count = 0; + + bool inline_p = false; + bool valid_coroutine = false; + + void wrap_original_function_body (); + void complete_ramp_function (); +}; diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index a9ce44bb214..cf4d6df90d8 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -8769,7 +8769,6 @@ extern tree finish_co_await_expr (location_t, tree); extern tree finish_co_yield_expr (location_t, tree); extern tree coro_validate_builtin_call (tree, tsubst_flags_t = tf_warning_or_error); -extern bool morph_fn_to_coro (tree, tree *, tree *); extern tree coro_get_actor_function (tree); extern tree coro_get_destroy_function (tree); extern tree coro_get_ramp_function (tree); diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc index 7ab73f1031d..a6e83f87a78 100644 --- a/gcc/cp/decl.cc +++ b/gcc/cp/decl.cc @@ -60,6 +60,7 @@ along with GCC; see the file COPYING3. If not see #include "omp-offload.h" /* For offload_vars. */ #include "opts.h" #include "langhooks-def.h" /* For lhd_simulate_record_decl */ +#include "coroutines.h" /* Possible cases of bad specifiers type used by bad_specifiers. */ enum bad_spec_place { @@ -18533,36 +18534,6 @@ add_return_star_this_fixit (gcc_rich_location *richloc, tree fndecl) indent); } -/* This function carries out the subset of finish_function operations needed - to emit the compiler-generated outlined helper functions used by the - coroutines implementation. */ - -static void -emit_coro_helper (tree helper) -{ - /* This is a partial set of the operations done by finish_function() - plus emitting the result. */ - set_cfun (NULL); - current_function_decl = helper; - begin_scope (sk_function_parms, NULL); - store_parm_decls (DECL_ARGUMENTS (helper)); - announce_function (helper); - allocate_struct_function (helper, false); - cfun->language = ggc_cleared_alloc (); - poplevel (1, 0, 1); - maybe_save_constexpr_fundef (helper); - /* We must start each function with a clear fold cache. */ - clear_fold_cache (); - cp_fold_function (helper); - DECL_CONTEXT (DECL_RESULT (helper)) = helper; - BLOCK_SUPERCONTEXT (DECL_INITIAL (helper)) = helper; - /* This function has coroutine IFNs that we should handle in middle - end lowering. */ - cfun->coroutine_component = true; - cp_genericize (helper); - expand_or_defer_fn (helper); -} - /* Finish up a function declaration and compile that function all the way to assembler language output. The free the storage for the function definition. INLINE_P is TRUE if we just @@ -18582,10 +18553,6 @@ finish_function (bool inline_p) if (fndecl == NULL_TREE || fndecl == error_mark_node) return error_mark_node; - bool coro_p = (flag_coroutines - && !processing_template_decl - && DECL_COROUTINE_P (fndecl)); - bool coro_emit_helpers = false; bool do_contracts = (DECL_HAS_CONTRACTS_P (fndecl) && !processing_template_decl); @@ -18609,24 +18576,29 @@ finish_function (bool inline_p) error_mark_node. */ gcc_assert (DECL_INITIAL (fndecl) == error_mark_node); - if (coro_p) + cp_coroutine_transform *coroutine = nullptr; + if (flag_coroutines + && !processing_template_decl + && DECL_COROUTINE_P (fndecl) + && !DECL_RAMP_FN (fndecl)) { - /* Only try to emit the coroutine outlined helper functions if the - transforms succeeded. Otherwise, treat errors in the same way as - a regular function. */ - coro_emit_helpers = morph_fn_to_coro (fndecl, &resumer, &destroyer); + gcc_checking_assert (!DECL_CLONED_FUNCTION_P (fndecl) + && !DECL_DEFAULTED_FN (fndecl)); + coroutine = new cp_coroutine_transform (fndecl, inline_p); + if (coroutine && coroutine->cp_valid_coroutine()) + coroutine->apply_transforms (); /* We should handle coroutine IFNs in middle end lowering. */ cfun->coroutine_component = true; /* Do not try to process the ramp's EH unless outlining succeeded. */ - if (coro_emit_helpers && use_eh_spec_block (fndecl)) + if (coroutine->cp_valid_coroutine() && use_eh_spec_block (fndecl)) finish_eh_spec_block (TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fndecl)), current_eh_spec_block); /* If outlining succeeded, then add contracts handling if needed. */ - if (coro_emit_helpers && do_contracts) + if (coroutine->cp_valid_coroutine() && do_contracts) maybe_apply_function_contracts (fndecl); } else @@ -18867,15 +18839,8 @@ finish_function (bool inline_p) opt_for_fn (fndecl, flag_semantic_interposition))) TREE_NOTHROW (fndecl) = 1; - /* Emit the resumer and destroyer functions now, providing that we have - not encountered some fatal error. */ - if (coro_emit_helpers) - { - emit_coro_helper (resumer); - emit_coro_helper (destroyer); - } - cleanup: + /* We're leaving the context of this function, so zap cfun. It's still in DECL_STRUCT_FUNCTION, and we'll restore it in tree_rest_of_compilation. */ set_cfun (NULL); @@ -18897,6 +18862,23 @@ finish_function (bool inline_p) /* Clean up. */ invoke_plugin_callbacks (PLUGIN_FINISH_PARSE_FUNCTION, fndecl); + /* Build outlined functions for coroutines and contracts. */ + + if (coroutine) + { + /* Emit the resumer and destroyer functions now, providing that we have + not encountered some fatal error. */ + if (coroutine->cp_valid_coroutine()) + { + coroutine->finish_transforms (); + resumer = coroutine->get_resumer (); + destroyer = coroutine->get_destroyer (); + expand_or_defer_fn (resumer); + expand_or_defer_fn (destroyer); + } + delete coroutine; + } + /* If we have used outlined contracts checking functions, build and emit them here. */ finish_function_contracts (fndecl); From patchwork Wed Aug 21 19:10:00 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Iain Sandoe X-Patchwork-Id: 1975087 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20230601 header.b=Ift3+VMO; 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 4Wpwtk3QXhz1yfb for ; Thu, 22 Aug 2024 05:11:58 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 54D7A387093C for ; Wed, 21 Aug 2024 19:11:56 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-ej1-x633.google.com (mail-ej1-x633.google.com [IPv6:2a00:1450:4864:20::633]) by sourceware.org (Postfix) with ESMTPS id 895BE3858417 for ; Wed, 21 Aug 2024 19:10:12 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 895BE3858417 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gmail.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 895BE3858417 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2a00:1450:4864:20::633 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1724267420; cv=none; b=akjIIHJrcPe6hpWpJM7XJqdBIvFKJsHdSopjrvaD6MoAP9prvDndeG/kT9oLsjOCdTnLfD/DjQjhVcDe9EBGiJtgUETpusLgFOYryxQdWOdx/63ftsRxr5CkK0Q1oXz/PkcNTeyJRabHzJlKDwmbHq1AMgyUuztE03VZyJXkzJU= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1724267420; c=relaxed/simple; bh=9wJf0lIXKKM9rBCQn5X3bu+Kt9RSCTBfozCee6DNYPU=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=VCwGRk7NDf8g4VjcqK6cEmUadS6Lxt/W8JEecee3VFK/0lp4yuD4WpSJz6vb418sdvz/H7KaLWZgghQzBJ3AYizFyid/0UMvVKjrhBUmYW/e9DOEMAJVHx+lhYmEsyB9S3JQQ6mshXCYB98cU2TchCVIV15yCLu8IvWrxUUlTpg= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-ej1-x633.google.com with SMTP id a640c23a62f3a-a868d7f92feso11805366b.2 for ; Wed, 21 Aug 2024 12:10:12 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1724267411; x=1724872211; darn=gcc.gnu.org; h=content-transfer-encoding:mime-version:reply-to:references :in-reply-to:message-id:date:subject:cc:to:from:from:to:cc:subject :date:message-id:reply-to; bh=aO7UfRRd90hKsFQitYww93wHUqAOMQre029jFJdVqzU=; b=Ift3+VMO7Riw1Ku1t3jlK2CQSgoWfD7PC93MTDV6x3uqt8Ohqo0j5uqgrZyX6J8OLX MjpX3xUHe8cfle/UJMUR4jM3cTv599mn0/uq5GClzA+fgQFJEPeYET/cpoBSLcVDdNQg d97IkZnEbaG6GNMX3PgQDQJXf8lAP6Vl7FXzN5QdXc5KiI0YMD3+5lWbux9chr+2WHqh cWjAMbt/Zks3YyZQeLn6GHxFgZcRPcM6YrsKVBa4eFLfGHGsv6IiM2Ka5dtYmCAezwsO AP8Kx7Pwu9m8Bf7eB2KiT717D0CnXrFVOOCDVkhIi2mFGxi2jWmedBwzELhvRZ85HArX 4KNg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1724267411; x=1724872211; h=content-transfer-encoding:mime-version:reply-to:references :in-reply-to:message-id:date:subject:cc:to:from:x-gm-message-state :from:to:cc:subject:date:message-id:reply-to; bh=aO7UfRRd90hKsFQitYww93wHUqAOMQre029jFJdVqzU=; b=u/UfvTJgPbah+YK7So0vN/xRw3WY343OOvP8x6K43SA3otacbOqouisgfquLVNOPr1 bI+c63vty398r3UQTQV5Jk5ggF5RxnQVdj2WT5AQNx/CodH+HYBr8lyXlJZn1Ne/r52D cKPR7/OrDee3QUYcgTt3QTErbJ5uW/C3KBzy9FlT7RYRL3uBxwq+Gssbv3b4r92PY1g+ wdiXTrav1znUb/df+JE77UPhsEos42WLDw5cXUWmroj77Y9ZXPD2IPVwCsHJ7DmlhJQZ OvmLfbFC/AGPZG8kM/NZ9PvoINMhCjq02eFmA/Pndl/K51PzwRqgaFqPhmA6h3vnazIE twfA== X-Gm-Message-State: AOJu0YzyuaA1rLRvrpUd1dAFzmVTl9hmbGndeMDI8yNjU9ysXItLZS/p paWNI7/YKA3MWmePhybgFjqk/tYrDvZGmMB06q/kxZSkJXJhgUPLb1zwZ+no X-Google-Smtp-Source: AGHT+IGgPnrKcVXUrFoSBDBjub97pH21A3mtWhPeS43D4fr9k34M2n0u30u21lt56RWZxT0DVHelcA== X-Received: by 2002:a17:907:d583:b0:a7d:e857:1490 with SMTP id a640c23a62f3a-a866f3782a6mr275819466b.37.1724267410437; Wed, 21 Aug 2024 12:10:10 -0700 (PDT) Received: from localhost.localdomain (host81-138-1-83.in-addr.btopenworld.com. [81.138.1.83]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a83838c6c07sm941222566b.43.2024.08.21.12.10.09 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Wed, 21 Aug 2024 12:10:09 -0700 (PDT) From: Iain Sandoe X-Google-Original-From: Iain Sandoe To: gcc-patches@gcc.gnu.org Cc: jason@redhat.com Subject: [PATCH 3/9] c++, coroutines: Separate allocator work from the ramp body build. Date: Wed, 21 Aug 2024 20:10:00 +0100 Message-Id: <48703f2dc2abade847e22c8dc304a88970366972.1724267239.git.iain@sandoe.co.uk> X-Mailer: git-send-email 2.39.2 (Apple Git-143) In-Reply-To: References: MIME-Version: 1.0 X-Spam-Status: No, score=-8.3 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP, T_SCC_BODY_TEXT_LINE 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: , Reply-To: iain@sandoe.co.uk Errors-To: gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org This splits out the building of the allocation and deallocation expressions and runs them early in the ramp build, so that we can exit if they are not usable, before we start building the ramp body. Likewise move checks for other required resources to the begining of the ramp builder. This is preparation for work needed to update the allocation/destruction in cases where we have excess alignment of the promise or other saved frame state. gcc/cp/ChangeLog: * coroutines.cc (coro_get_frame_dtor): Rename... (build_coroutine_frame_delete_expr):... to this. (build_actor_fn): Use revised frame delete function. (build_coroutine_frame_alloc_expr): New. (cp_coroutine_transform::complete_ramp_function): Rename... (cp_coroutine_transform::build_ramp_function): ... to this. Reorder code to carry out checks for prerequisites before the codegen. Split out the allocation/delete code. (cp_coroutine_transform::apply_transforms): Use revised name. * coroutines.h: Rename function. gcc/testsuite/ChangeLog: * g++.dg/coroutines/coro-bad-gro-00-class-gro-scalar-return.C: Use revised diagnostics. * g++.dg/coroutines/coro-bad-gro-01-void-gro-non-class-coro.C: Likewise. * g++.dg/coroutines/coro-bad-grooaf-00-static.C: Likewise. * g++.dg/coroutines/ramp-return-b.C: Likewise. Signed-off-by: Iain Sandoe --- gcc/cp/coroutines.cc | 484 ++++++++++-------- gcc/cp/coroutines.h | 2 +- .../coro-bad-gro-00-class-gro-scalar-return.C | 4 +- .../coro-bad-gro-01-void-gro-non-class-coro.C | 4 +- .../coroutines/coro-bad-grooaf-00-static.C | 6 +- .../g++.dg/coroutines/ramp-return-b.C | 8 +- 6 files changed, 289 insertions(+), 219 deletions(-) diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc index 4968852c9ca..f9f0bf40ed7 100644 --- a/gcc/cp/coroutines.cc +++ b/gcc/cp/coroutines.cc @@ -2202,55 +2202,7 @@ transform_local_var_uses (tree *stmt, int *do_subtree, void *d) argument. */ static tree -coro_get_frame_dtor (tree coro_fp, tree orig, tree frame_size, - tree promise_type, location_t loc) -{ - tree del_coro_fr = NULL_TREE; - tree frame_arg = build1 (CONVERT_EXPR, ptr_type_node, coro_fp); - tree delname = ovl_op_identifier (false, DELETE_EXPR); - tree fns = lookup_promise_method (orig, delname, loc, - /*musthave=*/false); - if (fns && BASELINK_P (fns)) - { - /* Look for sized version first, since this takes precedence. */ - vec *args = make_tree_vector (); - vec_safe_push (args, frame_arg); - vec_safe_push (args, frame_size); - tree dummy_promise = build_dummy_object (promise_type); - - /* It's OK to fail for this one... */ - del_coro_fr = build_new_method_call (dummy_promise, fns, &args, - NULL_TREE, LOOKUP_NORMAL, NULL, - tf_none); - - if (!del_coro_fr || del_coro_fr == error_mark_node) - { - release_tree_vector (args); - args = make_tree_vector_single (frame_arg); - del_coro_fr = build_new_method_call (dummy_promise, fns, &args, - NULL_TREE, LOOKUP_NORMAL, NULL, - tf_none); - } - - /* But one of them must succeed, or the program is ill-formed. */ - if (!del_coro_fr || del_coro_fr == error_mark_node) - { - error_at (loc, "%qE is provided by %qT but is not usable with" - " the function signature %qD", delname, promise_type, orig); - del_coro_fr = error_mark_node; - } - } - else - { - del_coro_fr = build_op_delete_call (DELETE_EXPR, frame_arg, frame_size, - /*global_p=*/true, /*placement=*/NULL, - /*alloc_fn=*/NULL, - tf_warning_or_error); - if (!del_coro_fr || del_coro_fr == error_mark_node) - del_coro_fr = error_mark_node; - } - return del_coro_fr; -} +build_coroutine_frame_delete_expr (tree, tree, tree, tree, location_t); /* The actor transform. */ @@ -2484,8 +2436,9 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody, } /* Build the frame DTOR. */ - tree del_coro_fr = coro_get_frame_dtor (actor_fp, orig, frame_size, - promise_type, loc); + tree del_coro_fr + = build_coroutine_frame_delete_expr (actor_fp, orig, frame_size, + promise_type, loc); finish_expr_stmt (del_coro_fr); finish_then_clause (need_free_if); tree scope = IF_SCOPE (need_free_if); @@ -4471,134 +4424,24 @@ split_coroutine_body_from_ramp (tree fndecl) return body; } -/* Build the ramp function. - Here we take the original function definition which has now had its body - removed, and use it as the declaration of the ramp which both replaces the - user's written function at call sites, and is responsible for starting - the coroutine it defined. - returns NULL_TREE on error or an expression for the frame size. +/* Built the expression to allocate the coroutine frame according to the + rules of [dcl.fct.def.coroutine] / 9. */ - We should arrive here with the state of the compiler as if we had just - executed start_preparsed_function(). */ - -void -cp_coroutine_transform::complete_ramp_function () +static tree +build_coroutine_frame_alloc_expr (tree promise_type, tree orig_fn_decl, + location_t fn_start, tree grooaf, + hash_map *param_uses, + tree frame_size) { - gcc_checking_assert (current_binding_level - && current_binding_level->kind == sk_function_parms); - - /* Avoid the code here attaching a location that makes the debugger jump. */ - location_t loc = fn_start; - - tree promise_type = get_coroutine_promise_type (orig_fn_decl); - tree fn_return_type = TREE_TYPE (TREE_TYPE (orig_fn_decl)); - - /* Ramp: */ - tree stmt = begin_function_body (); - /* Now build the ramp function pieces. */ - tree ramp_bind = build3 (BIND_EXPR, void_type_node, NULL, NULL, NULL); - add_stmt (ramp_bind); - tree ramp_body = push_stmt_list (); - - tree zeroinit = build1_loc (loc, CONVERT_EXPR, - frame_ptr_type, nullptr_node); - tree coro_fp = coro_build_artificial_var (loc, "_Coro_frameptr", - frame_ptr_type, orig_fn_decl, - zeroinit); - tree varlist = coro_fp; - - /* To signal that we need to cleanup copied function args. */ - if (flag_exceptions && DECL_ARGUMENTS (orig_fn_decl)) - for (tree arg = DECL_ARGUMENTS (orig_fn_decl); arg != NULL; - arg = DECL_CHAIN (arg)) - { - param_info *parm_i = param_uses->get (arg); - gcc_checking_assert (parm_i); - if (parm_i->trivial_dtor) - continue; - DECL_CHAIN (parm_i->guard_var) = varlist; - varlist = parm_i->guard_var; - } - - /* Signal that we need to clean up the promise object on exception. */ - tree coro_promise_live - = coro_build_artificial_var (loc, "_Coro_promise_live", - boolean_type_node, orig_fn_decl, boolean_false_node); - DECL_CHAIN (coro_promise_live) = varlist; - varlist = coro_promise_live; - - /* When the get-return-object is in the RETURN slot, we need to arrange for - cleanup on exception. */ - tree coro_gro_live - = coro_build_artificial_var (loc, "_Coro_gro_live", - boolean_type_node, orig_fn_decl, boolean_false_node); - - DECL_CHAIN (coro_gro_live) = varlist; - varlist = coro_gro_live; - - /* Collected the scope vars we need ... only one for now. */ - BIND_EXPR_VARS (ramp_bind) = nreverse (varlist); - - /* We're now going to create a new top level scope block for the ramp - function. */ - tree top_block = make_node (BLOCK); - - BIND_EXPR_BLOCK (ramp_bind) = top_block; - BLOCK_VARS (top_block) = BIND_EXPR_VARS (ramp_bind); - BLOCK_SUBBLOCKS (top_block) = NULL_TREE; - current_binding_level->blocks = top_block; - - /* The decl_expr for the coro frame pointer, initialize to zero so that we - can pass it to the IFN_CO_FRAME (since there's no way to pass a type, - directly apparently). This avoids a "used uninitialized" warning. */ - - add_decl_expr (coro_fp); - if (flag_exceptions && DECL_ARGUMENTS (orig_fn_decl)) - for (tree arg = DECL_ARGUMENTS (orig_fn_decl); arg != NULL; - arg = DECL_CHAIN (arg)) - { - param_info *parm_i = param_uses->get (arg); - if (parm_i->trivial_dtor) - continue; - add_decl_expr (parm_i->guard_var); - } - add_decl_expr (coro_promise_live); - add_decl_expr (coro_gro_live); - - /* The CO_FRAME internal function is a mechanism to allow the middle end - to adjust the allocation in response to optimizations. We provide the - current conservative estimate of the frame size (as per the current) - computed layout. */ - frame_size = TYPE_SIZE_UNIT (frame_type); - tree resizeable - = build_call_expr_internal_loc (loc, IFN_CO_FRAME, size_type_node, 2, - frame_size, coro_fp); - - /* [dcl.fct.def.coroutine] / 10 (part1) - The unqualified-id get_return_object_on_allocation_failure is looked up - in the scope of the promise type by class member access lookup. */ - - /* We don't require this, so coro_build_promise_expression can return NULL, - but, if the lookup succeeds, then the function must be usable. */ - tree dummy_promise = build_dummy_object (get_coroutine_promise_type (orig_fn_decl)); - tree grooaf - = coro_build_promise_expression (orig_fn_decl, dummy_promise, - coro_gro_on_allocation_fail_identifier, - loc, NULL, /*musthave=*/false); - - /* however, should that fail, returning an error, the later stages can't - handle the erroneous expression, so we reset the call as if it was - absent. */ - if (grooaf == error_mark_node) - grooaf = NULL_TREE; - /* Allocate the frame, this has several possibilities: [dcl.fct.def.coroutine] / 9 (part 1) The allocation function’s name is looked up in the scope of the promise - type. It's not a failure for it to be absent see part 4, below. */ + type. It is not a failure for it to be absent see part 4, below. */ tree nwname = ovl_op_identifier (false, NEW_EXPR); - tree new_fn = NULL_TREE; + tree new_fn_call = NULL_TREE; + tree dummy_promise + = build_dummy_object (get_coroutine_promise_type (orig_fn_decl)); if (TYPE_HAS_NEW_OPERATOR (promise_type)) { @@ -4611,7 +4454,7 @@ cp_coroutine_transform::complete_ramp_function () requested, and has type std::size_t. The lvalues p1...pn are the succeeding arguments.. */ vec *args = make_tree_vector (); - vec_safe_push (args, resizeable); /* Space needed. */ + vec_safe_push (args, frame_size); /* Space needed. */ for (tree arg = DECL_ARGUMENTS (orig_fn_decl); arg != NULL; arg = DECL_CHAIN (arg)) @@ -4633,18 +4476,18 @@ cp_coroutine_transform::complete_ramp_function () /* Note the function selected; we test to see if it's NOTHROW. */ tree func; /* Failure is not an error for this attempt. */ - new_fn = build_new_method_call (dummy_promise, fns, &args, NULL, + new_fn_call = build_new_method_call (dummy_promise, fns, &args, NULL, LOOKUP_NORMAL, &func, tf_none); release_tree_vector (args); - if (new_fn == error_mark_node) + if (new_fn_call == error_mark_node) { /* [dcl.fct.def.coroutine] / 9 (part 3) If no viable function is found, overload resolution is performed again on a function call created by passing just the amount of space required as an argument of type std::size_t. */ - args = make_tree_vector_single (resizeable); /* Space needed. */ - new_fn = build_new_method_call (dummy_promise, fns, &args, + args = make_tree_vector_single (frame_size); /* Space needed. */ + new_fn_call = build_new_method_call (dummy_promise, fns, &args, NULL_TREE, LOOKUP_NORMAL, &func, tf_none); release_tree_vector (args); @@ -4652,21 +4495,18 @@ cp_coroutine_transform::complete_ramp_function () /* However, if the promise provides an operator new, then one of these two options must be available. */ - if (new_fn == error_mark_node) + if (new_fn_call == error_mark_node) { error_at (fn_start, "%qE is provided by %qT but is not usable with" " the function signature %qD", nwname, promise_type, orig_fn_decl); - new_fn = error_mark_node; - valid_coroutine = false; - return; + return error_mark_node; } else if (grooaf && !TYPE_NOTHROW_P (TREE_TYPE (func))) { error_at (fn_start, "%qE is provided by %qT but %qE is not marked" " % or %", grooaf, promise_type, nwname); - valid_coroutine = false; - return; + return error_mark_node; } else if (!grooaf && TYPE_NOTHROW_P (TREE_TYPE (func))) warning_at (fn_start, 0, "%qE is marked % or % but" @@ -4698,31 +4538,264 @@ cp_coroutine_transform::complete_ramp_function () LOOK_want::NORMAL, /*complain=*/true); if (!std_nt || std_nt == error_mark_node) - error_at (fn_start, "%qE is provided by %qT but % " - "cannot be found", grooaf, promise_type); + { + /* Something is seriously wrong, punt. */ + error_at (fn_start, "%qE is provided by %qT but %" + " cannot be found", grooaf, promise_type); + return error_mark_node; + } vec_safe_push (args, std_nt); } /* If we get to this point, we must succeed in looking up the global operator new for the params provided. Extract a simplified version - of the machinery from build_operator_new_call. This can update the - frame size. */ + of the machinery from build_operator_new_call. + NOTE: This can update the frame size so we need to account for that + when building the IFN_CO_FRAME later. */ tree cookie = NULL; - new_fn = build_operator_new_call (nwname, &args, &frame_size, &cookie, - /*align_arg=*/NULL, - /*size_check=*/NULL, /*fn=*/NULL, - tf_warning_or_error); - resizeable = build_call_expr_internal_loc - (loc, IFN_CO_FRAME, size_type_node, 2, frame_size, coro_fp); - /* If the operator call fails for some reason, then don't try to - amend it. */ - if (new_fn != error_mark_node) - CALL_EXPR_ARG (new_fn, 0) = resizeable; - + new_fn_call = build_operator_new_call (nwname, &args, &frame_size, + &cookie, /*align_arg=*/NULL, + /*size_check=*/NULL, /*fn=*/NULL, + tf_warning_or_error); release_tree_vector (args); } + return new_fn_call; +} - tree allocated = build1 (CONVERT_EXPR, frame_ptr_type, new_fn); +static tree +build_coroutine_frame_delete_expr (tree coro_fp, tree orig, tree frame_size, + tree promise_type, location_t loc) +{ + tree del_coro_fr = NULL_TREE; + tree frame_arg = build1 (CONVERT_EXPR, ptr_type_node, coro_fp); + tree delname = ovl_op_identifier (false, DELETE_EXPR); + tree fns = lookup_promise_method (orig, delname, loc, + /*musthave=*/false); + if (fns && BASELINK_P (fns)) + { + /* Look for sized version first, since this takes precedence. */ + vec *args = make_tree_vector (); + vec_safe_push (args, frame_arg); + vec_safe_push (args, frame_size); + tree dummy_promise = build_dummy_object (promise_type); + + /* It's OK to fail for this one... */ + del_coro_fr = build_new_method_call (dummy_promise, fns, &args, + NULL_TREE, LOOKUP_NORMAL, NULL, + tf_none); + + if (!del_coro_fr || del_coro_fr == error_mark_node) + { + release_tree_vector (args); + args = make_tree_vector_single (frame_arg); + del_coro_fr = build_new_method_call (dummy_promise, fns, &args, + NULL_TREE, LOOKUP_NORMAL, NULL, + tf_none); + } + + /* But one of them must succeed, or the program is ill-formed. */ + if (!del_coro_fr || del_coro_fr == error_mark_node) + { + error_at (loc, "%qE is provided by %qT but is not usable with" + " the function signature %qD", delname, promise_type, orig); + del_coro_fr = error_mark_node; + } + } + else + { + del_coro_fr = build_op_delete_call (DELETE_EXPR, frame_arg, frame_size, + /*global_p=*/true, /*placement=*/NULL, + /*alloc_fn=*/NULL, + tf_warning_or_error); + if (!del_coro_fr || del_coro_fr == error_mark_node) + del_coro_fr = error_mark_node; + } + return del_coro_fr; +} + +/* Build the ramp function. + Here we take the original function definition which has now had its body + removed, and use it as the declaration of the ramp which both replaces the + user's written function at call sites, and is responsible for starting + the coroutine it defined. + returns NULL_TREE on error or an expression for the frame size. + + We should arrive here with the state of the compiler as if we had just + executed start_preparsed_function(). */ + +void +cp_coroutine_transform::build_ramp_function () +{ + gcc_checking_assert (current_binding_level + && current_binding_level->kind == sk_function_parms); + + /* This is completely synthetic code, if we find an issue then we have not + much chance to point at the most useful place in the user's code. In + lieu of this use the function start - so at least the diagnostic relates + to something that the user can inspect. */ + + location_t save_input_loc = input_location; + location_t loc = fn_start; + input_location = loc; + + tree promise_type = get_coroutine_promise_type (orig_fn_decl); + tree fn_return_type = TREE_TYPE (TREE_TYPE (orig_fn_decl)); + + /* [dcl.fct.def.coroutine] / 10 (part1) + The unqualified-id get_return_object_on_allocation_failure is looked up + in the scope of the promise type by class member access lookup. */ + + /* We don't require this, but, if the lookup succeeds, then the function + must be usable, punt if it is not. */ + tree grooaf_meth + = lookup_promise_method (orig_fn_decl, + coro_gro_on_allocation_fail_identifier, loc, + /*musthave*/ false); + tree grooaf = NULL_TREE; + tree dummy_promise + = build_dummy_object (get_coroutine_promise_type (orig_fn_decl)); + if (grooaf_meth && grooaf_meth != error_mark_node) + { + grooaf + = coro_build_promise_expression (orig_fn_decl, dummy_promise, + coro_gro_on_allocation_fail_identifier, + fn_start, NULL, /*musthave=*/false); + + /* That should succeed. */ + if (!grooaf || grooaf == error_mark_node) + { + error_at (fn_start, "%qE is provided by %qT but is not usable with" + " the function %qD", coro_gro_on_allocation_fail_identifier, + promise_type, orig_fn_decl); + valid_coroutine = false; + input_location = save_input_loc; + return; + } + } + + /* Check early for usable allocator/deallocator, without which we cannot + build a useful ramp; early exit if they are not available or usable. */ + + frame_size = TYPE_SIZE_UNIT (frame_type); + + /* Make a var to represent the frame pointer early. */ + tree zeroinit = build1_loc (loc, CONVERT_EXPR, + frame_ptr_type, nullptr_node); + tree coro_fp = coro_build_artificial_var (loc, "_Coro_frameptr", + frame_ptr_type, orig_fn_decl, + zeroinit); + + tree new_fn_call + = build_coroutine_frame_alloc_expr (promise_type, orig_fn_decl, fn_start, + grooaf, param_uses, frame_size); + + /* We must have a useable allocator to proceed. */ + if (!new_fn_call || new_fn_call == error_mark_node) + { + valid_coroutine = false; + input_location = save_input_loc; + return; + } + + /* Likewise, we need the DTOR to delete the frame. */ + tree delete_frame_call + = build_coroutine_frame_delete_expr (coro_fp, orig_fn_decl, frame_size, + promise_type, fn_start); + if (!delete_frame_call || delete_frame_call == error_mark_node) + { + valid_coroutine = false; + input_location = save_input_loc; + return; + } + + /* At least verify we can lookup the get return object method. */ + tree get_ro_meth + = lookup_promise_method (orig_fn_decl, + coro_get_return_object_identifier, loc, + /*musthave*/ true); + if (!get_ro_meth || get_ro_meth == error_mark_node) + { + valid_coroutine = false; + input_location = save_input_loc; + return; + } + + /* So now construct the Ramp: */ + tree stmt = begin_function_body (); + /* Now build the ramp function pieces. */ + tree ramp_bind = build3 (BIND_EXPR, void_type_node, NULL, NULL, NULL); + add_stmt (ramp_bind); + tree ramp_body = push_stmt_list (); + tree varlist = coro_fp; + + /* To signal that we need to cleanup copied function args. */ + if (flag_exceptions && DECL_ARGUMENTS (orig_fn_decl)) + for (tree arg = DECL_ARGUMENTS (orig_fn_decl); arg != NULL; + arg = DECL_CHAIN (arg)) + { + param_info *parm_i = param_uses->get (arg); + gcc_checking_assert (parm_i); + if (parm_i->trivial_dtor) + continue; + DECL_CHAIN (parm_i->guard_var) = varlist; + varlist = parm_i->guard_var; + } + + /* Signal that we need to clean up the promise object on exception. */ + tree coro_promise_live + = coro_build_artificial_var (loc, "_Coro_promise_live", boolean_type_node, + orig_fn_decl, boolean_false_node); + DECL_CHAIN (coro_promise_live) = varlist; + varlist = coro_promise_live; + + /* When the get-return-object is in the RETURN slot, we need to arrange for + cleanup on exception. */ + tree coro_gro_live + = coro_build_artificial_var (loc, "_Coro_gro_live", boolean_type_node, + orig_fn_decl, boolean_false_node); + + DECL_CHAIN (coro_gro_live) = varlist; + varlist = coro_gro_live; + + /* Collected the scope vars we need ... only one for now. */ + BIND_EXPR_VARS (ramp_bind) = nreverse (varlist); + + /* We're now going to create a new top level scope block for the ramp + function. */ + tree top_block = make_node (BLOCK); + + BIND_EXPR_BLOCK (ramp_bind) = top_block; + BLOCK_VARS (top_block) = BIND_EXPR_VARS (ramp_bind); + BLOCK_SUBBLOCKS (top_block) = NULL_TREE; + current_binding_level->blocks = top_block; + + /* The decl_expr for the coro frame pointer, initialize to zero so that we + can pass it to the IFN_CO_FRAME (since there's no way to pass a type, + directly apparently). This avoids a "used uninitialized" warning. */ + + add_decl_expr (coro_fp); + if (flag_exceptions && DECL_ARGUMENTS (orig_fn_decl)) + for (tree arg = DECL_ARGUMENTS (orig_fn_decl); arg != NULL; + arg = DECL_CHAIN (arg)) + { + param_info *parm_i = param_uses->get (arg); + if (parm_i->trivial_dtor) + continue; + add_decl_expr (parm_i->guard_var); + } + add_decl_expr (coro_promise_live); + add_decl_expr (coro_gro_live); + + /* Build the frame. */ + + /* The CO_FRAME internal function is a mechanism to allow the middle end + to adjust the allocation in response to optimizations. We provide the + current conservative estimate of the frame size (as per the current) + computed layout. */ + tree resizeable = build_call_expr_internal_loc + (loc, IFN_CO_FRAME, size_type_node, 2, frame_size, coro_fp); + CALL_EXPR_ARG (new_fn_call, 0) = resizeable; + tree allocated = build1 (CONVERT_EXPR, frame_ptr_type, new_fn_call); tree r = cp_build_init_expr (coro_fp, allocated); finish_expr_stmt (r); @@ -4949,6 +5022,7 @@ cp_coroutine_transform::complete_ramp_function () /* Suppress warnings about the missing return value. */ suppress_warning (orig_fn_decl, OPT_Wreturn_type); valid_coroutine = false; + input_location = save_input_loc; return; } @@ -5066,10 +5140,11 @@ cp_coroutine_transform::complete_ramp_function () else { /* We can't initialize a non-class return value from void. */ - error_at (input_location, "cannot initialize a return object of type" + error_at (fn_start, "cannot initialize a return object of type" " %qT with an rvalue of type %", fn_return_type); r = error_mark_node; valid_coroutine = false; + input_location = save_input_loc; return; } @@ -5150,12 +5225,7 @@ cp_coroutine_transform::complete_ramp_function () } /* We always expect to delete the frame. */ - tree del_coro_fr = coro_get_frame_dtor (coro_fp, orig_fn_decl, frame_size, - promise_type, loc); - if (!del_coro_fr || del_coro_fr == error_mark_node) - valid_coroutine = false; /* Do not add this. */ - else - finish_expr_stmt (del_coro_fr); + finish_expr_stmt (delete_frame_call); tree rethrow = build_throw (loc, NULL_TREE, tf_warning_or_error); suppress_warning (rethrow); finish_expr_stmt (rethrow); @@ -5304,7 +5374,7 @@ cp_coroutine_transform::apply_transforms () BINFO_TYPE (TYPE_BINFO (frame_type)) = frame_type; frame_type = finish_struct (frame_type, NULL_TREE); - complete_ramp_function (); + build_ramp_function (); } void diff --git a/gcc/cp/coroutines.h b/gcc/cp/coroutines.h index 121f20ed4f0..9fb50ce8b30 100644 --- a/gcc/cp/coroutines.h +++ b/gcc/cp/coroutines.h @@ -128,5 +128,5 @@ private: bool valid_coroutine = false; void wrap_original_function_body (); - void complete_ramp_function (); + void build_ramp_function (); }; diff --git a/gcc/testsuite/g++.dg/coroutines/coro-bad-gro-00-class-gro-scalar-return.C b/gcc/testsuite/g++.dg/coroutines/coro-bad-gro-00-class-gro-scalar-return.C index 0512f03f4d0..605a2135878 100644 --- a/gcc/testsuite/g++.dg/coroutines/coro-bad-gro-00-class-gro-scalar-return.C +++ b/gcc/testsuite/g++.dg/coroutines/coro-bad-gro-00-class-gro-scalar-return.C @@ -33,11 +33,11 @@ struct std::coroutine_traits { }; int -my_coro (std::coroutine_handle<>& h) +my_coro (std::coroutine_handle<>& h) // { dg-error {cannot convert 'Thing' to 'int' in return} } { PRINT ("coro1: about to return"); co_return; -} // { dg-error {cannot convert 'Thing' to 'int' in return} } +} int main () { diff --git a/gcc/testsuite/g++.dg/coroutines/coro-bad-gro-01-void-gro-non-class-coro.C b/gcc/testsuite/g++.dg/coroutines/coro-bad-gro-01-void-gro-non-class-coro.C index 2671ce7ca28..85aadad93b2 100644 --- a/gcc/testsuite/g++.dg/coroutines/coro-bad-gro-01-void-gro-non-class-coro.C +++ b/gcc/testsuite/g++.dg/coroutines/coro-bad-gro-01-void-gro-non-class-coro.C @@ -27,11 +27,11 @@ struct std::coroutine_traits { }; int -my_coro (std::coroutine_handle<>& h) +my_coro (std::coroutine_handle<>& h) // { dg-error {cannot initialize a return object of type 'int' with an rvalue of type 'void'} } { PRINT ("coro1: about to return"); co_return; -} // { dg-error {cannot initialize a return object of type 'int' with an rvalue of type 'void'} } +} int main () { diff --git a/gcc/testsuite/g++.dg/coroutines/coro-bad-grooaf-00-static.C b/gcc/testsuite/g++.dg/coroutines/coro-bad-grooaf-00-static.C index e7d04346d57..6a41b4e85a2 100644 --- a/gcc/testsuite/g++.dg/coroutines/coro-bad-grooaf-00-static.C +++ b/gcc/testsuite/g++.dg/coroutines/coro-bad-grooaf-00-static.C @@ -7,9 +7,9 @@ int used_grooaf = 0; struct coro1 -f () noexcept +f () noexcept // { dg-error {cannot call member function 'coro1 coro1::promise_type::get_return_object_on_allocation_failure\(\)' without object} } +// { dg-error {'get_return_object_on_allocation_failure' is provided by.*} "" { target *-*-* } .-1 } { PRINT ("coro1: about to return"); co_return; -} // { dg-error {cannot call member function 'coro1 coro1::promise_type::get_return_object_on_allocation_failure\(\)' without object} } - +} diff --git a/gcc/testsuite/g++.dg/coroutines/ramp-return-b.C b/gcc/testsuite/g++.dg/coroutines/ramp-return-b.C index d0e5d1f3c7f..dc21e974900 100644 --- a/gcc/testsuite/g++.dg/coroutines/ramp-return-b.C +++ b/gcc/testsuite/g++.dg/coroutines/ramp-return-b.C @@ -10,13 +10,13 @@ foo () } task -bar () +bar () // { dg-error {use of deleted function 'task::task\(const task&\) \[with T = int\]'} } { co_return 0; -} // { dg-error {use of deleted function 'task::task\(const task&\) \[with T = int\]'} } +} task> -baz () +baz () // { dg-error {use of deleted function 'task::task\(const task&\) \[with T = std::vector\]'} } { co_return std::vector(); -} // { dg-error {use of deleted function 'task::task\(const task&\) \[with T = std::vector\]'} } +} From patchwork Wed Aug 21 19:10:01 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Iain Sandoe X-Patchwork-Id: 1975084 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20230601 header.b=AfK/cLqZ; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=8.43.85.97; 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 [8.43.85.97]) (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 4Wpwtf1WLbz1yXf for ; Thu, 22 Aug 2024 05:11:54 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 6804D38708E4 for ; Wed, 21 Aug 2024 19:11:52 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-ej1-x62b.google.com (mail-ej1-x62b.google.com [IPv6:2a00:1450:4864:20::62b]) by sourceware.org (Postfix) with ESMTPS id DCE9C3857000 for ; Wed, 21 Aug 2024 19:10:12 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org DCE9C3857000 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gmail.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org DCE9C3857000 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2a00:1450:4864:20::62b ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1724267414; cv=none; b=G9/bMfpkncfzhuEGBJRowpUZAcZ+zdmCs+FuVbMCJLJGFsRbZAtzy7jTKnr7bWj0v8NlcrwkRQsHWeD94Osb6SxrMgbplw3tTb8rvNZqmWxZpCIu6pKiWg/I/PjR6PtonEvVOqDQAl1m8pVT6MjtevDfu807lhW9CcT93K/SoIo= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1724267414; c=relaxed/simple; bh=YdF/eQ8zi+5Pmr+FnddgXZHN7lhxNUEkWwKEMRQpMvQ=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=vw4nTltmOwIPNHHzjkU74ISJDLbfMgxIBBl5fp+ioqu5QZVpv/GMT5pPi8VVVyJ7CDa/ZfnB1tis/0xS/olHCCxYamma4iFr7jr0bzl8/EOi+23G76xh0pDipUN9qu2NapEyI87DqzsYYmb00JTDsIM0p+IqiUoWZezJCfuxjHk= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-ej1-x62b.google.com with SMTP id a640c23a62f3a-a83597ce5beso1422966b.1 for ; Wed, 21 Aug 2024 12:10:12 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1724267411; x=1724872211; darn=gcc.gnu.org; h=content-transfer-encoding:mime-version:reply-to:references :in-reply-to:message-id:date:subject:cc:to:from:from:to:cc:subject :date:message-id:reply-to; bh=Zy/4CuG1c1nrRLvnkgQ7F1yRTvWwkXnB6Xc+68mOsh4=; b=AfK/cLqZA5o9n0TiL/RVAGBB2O/HIPyyXNf2jtKtaLOx+in0HncJcTMLdcfL/ivK/F WV+QThQmDp7l668biMhVaRUnLDvB0exS42hoY30gJaPjHZ0A5djivaPpcRxRHK60wyMJ kncSjd2xOMW/RDbrg04ZNOxf1C+rNPotxzVFFwQBiLtLwi5ARxvKNDWg6/Z0p8+dhDl3 0wK30uMuGwe7NtHlVDsGSrB4QiNugviHTGABlYx0j0RDPWJzTeYjCVTdj3DHxhsSRhZq 2138Wh8KVdTc+vOmx+wdnI9ZZtmJJdx0smsK8yUetD0k0Cbqsc66dUB1x2A2R9LCKD75 sfpA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1724267411; x=1724872211; h=content-transfer-encoding:mime-version:reply-to:references :in-reply-to:message-id:date:subject:cc:to:from:x-gm-message-state :from:to:cc:subject:date:message-id:reply-to; bh=Zy/4CuG1c1nrRLvnkgQ7F1yRTvWwkXnB6Xc+68mOsh4=; b=wN3PGwCcyA1DB81XOepk6kiprFyfPVVcJC1es4X3Y1m+G1vzMbD99VfGoZV3hdXDCC bGCQqjSQqJ5NHsHGHSh8QJ9sItHnbhCe9HhCVYV4HYqH57TrwKGBn9w+JEnfMtUeRsQm CT3jaeY64ziGN2pQxVEwOe6Gl3I/vpwoi9LDOFZOUneQ/fVEfv1ozeNmp+y8eYDu+5cu WK1zDbk5Xy8vu14n1b2ZeDDVAyca+qgL/Q+XSGSJlmWNUcDTtIi4xAuFJGZQTVtS3RP+ //N01ZhAstQld8mcoaGNPUbkx0N/h1LWz7Vna+HEAmCP/0pviRVs0rImmYcMhbOXyAQ/ Q1lg== X-Gm-Message-State: AOJu0YwjEB6/dJ+INNHQNJIKIibGOqisocwgsdQCUnHrt3pf0j+h43Tb 9erqCi9vizaM2JG8GCU0vDPUtTWIo4CjIwXdowTH396yxOGp61PF79SK3b8g X-Google-Smtp-Source: AGHT+IFF+ToVbkOssbWOtckcoiuAxz+UivMV0+QcWFGzRv42bH3pEYn8m/1Wapri7nnY4uULPCNQlQ== X-Received: by 2002:a17:907:9451:b0:a7a:b26d:fb5 with SMTP id a640c23a62f3a-a868a7b4800mr46452366b.19.1724267411114; Wed, 21 Aug 2024 12:10:11 -0700 (PDT) Received: from localhost.localdomain (host81-138-1-83.in-addr.btopenworld.com. [81.138.1.83]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a83838c6c07sm941222566b.43.2024.08.21.12.10.10 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Wed, 21 Aug 2024 12:10:10 -0700 (PDT) From: Iain Sandoe X-Google-Original-From: Iain Sandoe To: gcc-patches@gcc.gnu.org Cc: jason@redhat.com Subject: [PATCH 4/9] c++, coroutines: Fix handling of early exceptions [PR113773]. Date: Wed, 21 Aug 2024 20:10:01 +0100 Message-Id: <1d01b29247e0ab540787a5790dc9990994575b06.1724267239.git.iain@sandoe.co.uk> X-Mailer: git-send-email 2.39.2 (Apple Git-143) In-Reply-To: References: MIME-Version: 1.0 X-Spam-Status: No, score=-8.3 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP, T_SCC_BODY_TEXT_LINE 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: , Reply-To: iain@sandoe.co.uk Errors-To: gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org The responsibility for destroying part of the frame content (promise, arg copies and the frame itself) transitions from the ramp to the body of the coroutine once we reach the await_resume () for the initial suspend. We added the variable that flags the transition, but failed to act on it. This corrects that so that the ramp only tries to run DTORs for objects when an exception occurs before the initial suspend await resume has started. PR c++/113773 gcc/cp/ChangeLog: * coroutines.cc (cp_coroutine_transform::build_ramp_function): Only cleanup the frame state on exceptions that occur before the initial await resume has begun. gcc/testsuite/ChangeLog: * g++.dg/coroutines/torture/pr113773.C: New test. Signed-off-by: Iain Sandoe --- gcc/cp/coroutines.cc | 23 +++++++ .../g++.dg/coroutines/torture/pr113773.C | 66 +++++++++++++++++++ 2 files changed, 89 insertions(+) create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/pr113773.C diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc index f9f0bf40ed7..2faf198c206 100644 --- a/gcc/cp/coroutines.cc +++ b/gcc/cp/coroutines.cc @@ -5183,6 +5183,17 @@ cp_coroutine_transform::build_ramp_function () add_stmt (gro_d_if); } + /* Before initial resume is called, the responsibility for cleanup on + exception falls to the ramp. After that, the coroutine body code + should do the cleanup. */ + tree iarc_m = lookup_member (frame_type, coro_frame_i_a_r_c_id, + 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); + tree not_iarc + = build1_loc (loc, TRUTH_NOT_EXPR, boolean_type_node, iarc_x); + tree cleanup_if = begin_if_stmt (); + finish_if_stmt_cond (not_iarc, cleanup_if); /* If the promise is live, then run its dtor if that's available. */ if (promise_dtor && promise_dtor != error_mark_node) { @@ -5225,7 +5236,19 @@ cp_coroutine_transform::build_ramp_function () } /* We always expect to delete the frame. */ + tree fnf_if = begin_if_stmt (); + finish_if_stmt_cond (fnf_x, fnf_if); finish_expr_stmt (delete_frame_call); + finish_then_clause (fnf_if); + tree fnf_if_scope = IF_SCOPE (fnf_if); + IF_SCOPE (fnf_if) = NULL; + fnf_if = do_poplevel (fnf_if_scope); + add_stmt (fnf_if); + finish_then_clause (cleanup_if); + tree cleanup_if_scope = IF_SCOPE (cleanup_if); + IF_SCOPE (cleanup_if) = NULL; + cleanup_if = do_poplevel (cleanup_if_scope); + add_stmt (cleanup_if); tree rethrow = build_throw (loc, NULL_TREE, tf_warning_or_error); suppress_warning (rethrow); finish_expr_stmt (rethrow); diff --git a/gcc/testsuite/g++.dg/coroutines/torture/pr113773.C b/gcc/testsuite/g++.dg/coroutines/torture/pr113773.C new file mode 100644 index 00000000000..b048b0d63f4 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/pr113773.C @@ -0,0 +1,66 @@ +// { dg-do run } +#include +#ifdef OUTPUT +#include +#endif + +struct result { + operator int() { + throw 42; + } +}; + +static int p_dtor_count = 0; +class promise { +public: + result get_return_object() { return {}; } + std::suspend_never initial_suspend() { +#ifdef OUTPUT + std::cout << "initial suspend" << std::endl; +#endif + return {}; + } + void unhandled_exception() { +#ifdef OUTPUT + std::cout << "unhandled exception" << std::endl; +#endif + } + std::suspend_never final_suspend() noexcept { +#ifdef OUTPUT + std::cout << "final suspend" << std::endl; +#endif + return {}; + } + void return_void() {} + ~promise() { + p_dtor_count++; +#ifdef OUTPUT + std::cout << "~promise()" << std::endl; +#endif + } +}; + +template +struct std::coroutine_traits { + using promise_type = promise; +}; + +int f() { + co_return; +} + +int main() { + try { + f(); + } + catch (int i) { + if (i != 42) + __builtin_abort (); +#ifdef OUTPUT + std::cout << "caught 42" << std::endl; +#endif + } + if (p_dtor_count != 1) + __builtin_abort (); + return 0; +} From patchwork Wed Aug 21 19:10:02 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Iain Sandoe X-Patchwork-Id: 1975082 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20230601 header.b=da23q3ZO; 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 4WpwsS5rZrz1yfb for ; Thu, 22 Aug 2024 05:10:52 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id A6630387093F for ; Wed, 21 Aug 2024 19:10:50 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-ej1-x62e.google.com (mail-ej1-x62e.google.com [IPv6:2a00:1450:4864:20::62e]) by sourceware.org (Postfix) with ESMTPS id C368E385DC1B for ; Wed, 21 Aug 2024 19:10:13 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org C368E385DC1B Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gmail.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org C368E385DC1B Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2a00:1450:4864:20::62e ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1724267419; cv=none; b=UrGPa26ooyuU2hf5vpQ927OwtXBmgL8yq1JgRXPqEmILiZxDg+In5srJHF8/upOL9EAgIHgSw9oWxAE6ec5bsC5K+xNibK0xwE1yvTr+WfwJu6fPyvSv03kO1aLCWQr3qes5jrIWKjnYGguYaiWQ7qsnlJlI+SGLJAf+LUD8ilQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1724267419; c=relaxed/simple; bh=31TIxw9UAeArEkMc8zL8Zuz946MqJyOTzLiWtxutIBw=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=Grro2+pzrJE3Q1/+IlvzaHgTBS9B9LbOint/SKcya4ooMblUhflK9y9JcEPFTgtf2s6AksORyXKj7o79FDahj9nESbYtndXcY6RSd11Hlbix4HyBgeLa+FWh3atEoSCa2mezVqfNnXBqVzJcITnGe3+OQDtxwuW2xuDZI/dRZug= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-ej1-x62e.google.com with SMTP id a640c23a62f3a-a868b8bb0feso22362866b.0 for ; Wed, 21 Aug 2024 12:10:13 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1724267412; x=1724872212; darn=gcc.gnu.org; h=content-transfer-encoding:mime-version:reply-to:references :in-reply-to:message-id:date:subject:cc:to:from:from:to:cc:subject :date:message-id:reply-to; bh=eDL//FPuQCWq/gWM5od4TZaPbDWWztH3zw/3nyGHzrQ=; b=da23q3ZOf/cfQ2KuKH0idgUM1uoB5srNZMe0Onsd8lmUVQqT5NOO4bBw+WBbIJk4hg 3Ma7gWOLs2XK0FxvdR46tuC2vm6vOcCD6bsnpg3RK7IA1m//C1H5WCbseIremWDJtnr3 FVWptHbqhe12pj6zINohS298ZRmRnxIeMc6ZfoT27oGxhwjdAsb0Yk5luoYZXbTcKMVk TR5vX5Ujc9K1YY/xTBndxPwA+joqKRFuu9gRRk8gg/d6l+/6irp+OdcgPTO3gp/vqGce Gc9IqWAbdSneYFUXZ6jG9m4d8AyYtAJFjZRiVaHWahVuPvqQtdLp3M9D6XhFpwcwybj+ q7Bg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1724267412; x=1724872212; h=content-transfer-encoding:mime-version:reply-to:references :in-reply-to:message-id:date:subject:cc:to:from:x-gm-message-state :from:to:cc:subject:date:message-id:reply-to; bh=eDL//FPuQCWq/gWM5od4TZaPbDWWztH3zw/3nyGHzrQ=; b=XxtFv1gN01w6FxtUCFpMSypdGW3DkqMcBvG3HXO8rEs9n83M7QDifpwCzUbEYKXRb4 0T6q0NCIlXeRqvnIEXCzy9oiYW7rc2qJvmtnNNsakma/cNttglxF18nAckEYKCKtNLKS qSqc5yXtKv0lH4fHvaynQqIyE8UfBER2ghxYUbNIKLCXSmbPExehwQd3j7W/87JgX5dP jOMDeR9Dhb8Hy2bW5cT9+ABYHPiNVrravrPpvEVoJd0rfqFjk4cBqdrKASGCajIHeYl0 PP1QaID7Y4RvzVLC6jwPAUhYg/qzNjRP2DOwYcveZ8yJBTB40YPw2VcrLiaIs0a0SEKD OiZw== X-Gm-Message-State: AOJu0Yy90dqenhwPfaCYO1BlDdS5XtO1lol/odPE2auDLHQ+Nz0FJ17S uBzJPzLpsPMathxXQ2U0aiaK0+DpHTQlc22YmucPH1AnAoV+5ZVAYOslYEsH X-Google-Smtp-Source: AGHT+IGx0fXNAKk9BZPekyXpLFaPu2OhWOCHA0qofCP2KejUwIPov58Rp3Pj0omRQA4YkmcjXnQjPQ== X-Received: by 2002:a17:907:3ea2:b0:a86:88f7:679c with SMTP id a640c23a62f3a-a8688f768ccmr75530666b.41.1724267411827; Wed, 21 Aug 2024 12:10:11 -0700 (PDT) Received: from localhost.localdomain (host81-138-1-83.in-addr.btopenworld.com. [81.138.1.83]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a83838c6c07sm941222566b.43.2024.08.21.12.10.11 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Wed, 21 Aug 2024 12:10:11 -0700 (PDT) From: Iain Sandoe X-Google-Original-From: Iain Sandoe To: gcc-patches@gcc.gnu.org Cc: jason@redhat.com Subject: [PATCH 5/9] c++, coroutines: Only allow void get_return_object if the ramp is void [PR100476]. Date: Wed, 21 Aug 2024 20:10:02 +0100 Message-Id: X-Mailer: git-send-email 2.39.2 (Apple Git-143) In-Reply-To: References: MIME-Version: 1.0 X-Spam-Status: No, score=-8.3 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP, T_SCC_BODY_TEXT_LINE 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: , Reply-To: iain@sandoe.co.uk Errors-To: gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org Require that the value returned by get_return_object is convertible to the ramp return. This means that the only time we allow a void get_return_object, is when the ramp is also a void function. We diagnose this early to allow us to exit the ramp build if the return values are incompatible. PR c++/100476 gcc/cp/ChangeLog: * coroutines.cc (cp_coroutine_transform::build_ramp_function): Remove special handling of void get_return_object expressions. gcc/testsuite/ChangeLog: * g++.dg/coroutines/coro-bad-gro-01-void-gro-non-class-coro.C: Adjust expected diagnostic. * g++.dg/coroutines/pr102489.C: Avoid void get_return_object. * g++.dg/coroutines/pr103868.C: Likewise. * g++.dg/coroutines/pr94879-folly-1.C: Likewise. * g++.dg/coroutines/pr94883-folly-2.C: Likewise. * g++.dg/coroutines/pr96749-2.C: Likewise. Signed-off-by: Iain Sandoe --- gcc/cp/coroutines.cc | 48 +++++++++---------- .../coro-bad-gro-01-void-gro-non-class-coro.C | 2 +- gcc/testsuite/g++.dg/coroutines/pr102489.C | 2 +- gcc/testsuite/g++.dg/coroutines/pr103868.C | 2 +- .../g++.dg/coroutines/pr94879-folly-1.C | 3 +- .../g++.dg/coroutines/pr94883-folly-2.C | 39 +++++++-------- gcc/testsuite/g++.dg/coroutines/pr96749-2.C | 2 +- 7 files changed, 48 insertions(+), 50 deletions(-) diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc index 2faf198c206..d152ad20dca 100644 --- a/gcc/cp/coroutines.cc +++ b/gcc/cp/coroutines.cc @@ -4640,6 +4640,7 @@ cp_coroutine_transform::build_ramp_function () tree promise_type = get_coroutine_promise_type (orig_fn_decl); tree fn_return_type = TREE_TYPE (TREE_TYPE (orig_fn_decl)); + bool void_ramp_p = VOID_TYPE_P (fn_return_type); /* [dcl.fct.def.coroutine] / 10 (part1) The unqualified-id get_return_object_on_allocation_failure is looked up @@ -4720,6 +4721,19 @@ cp_coroutine_transform::build_ramp_function () return; } + /* Check for a bad get return object type. */ + tree gro_return_type = FUNC_OR_METHOD_TYPE_P (TREE_TYPE (get_ro_meth)) + ? TREE_TYPE (TREE_TYPE (get_ro_meth)) + : TREE_TYPE (get_ro_meth); + if (VOID_TYPE_P (gro_return_type) && !void_ramp_p) + { + error_at (fn_start, "no viable conversion from % provided by" + " % to return type %qT", fn_return_type); + valid_coroutine = false; + input_location = save_input_loc; + return; + } + /* So now construct the Ramp: */ tree stmt = begin_function_body (); /* Now build the ramp function pieces. */ @@ -4816,7 +4830,7 @@ cp_coroutine_transform::build_ramp_function () tree cond = build1 (CONVERT_EXPR, frame_ptr_type, nullptr_node); cond = build2 (EQ_EXPR, boolean_type_node, coro_fp, cond); finish_if_stmt_cond (cond, if_stmt); - if (VOID_TYPE_P (fn_return_type)) + if (void_ramp_p) { /* Execute the get-return-object-on-alloc-fail call... */ finish_expr_stmt (grooaf); @@ -5028,7 +5042,6 @@ cp_coroutine_transform::build_ramp_function () tree gro_context_body = push_stmt_list (); tree gro_type = TREE_TYPE (get_ro); - bool gro_is_void_p = VOID_TYPE_P (gro_type); tree gro = NULL_TREE; tree gro_bind_vars = NULL_TREE; @@ -5037,8 +5050,11 @@ cp_coroutine_transform::build_ramp_function () tree gro_cleanup_stmt = NULL_TREE; /* We have to sequence the call to get_return_object before initial suspend. */ - if (gro_is_void_p) - r = get_ro; + if (void_ramp_p) + { + gcc_checking_assert (VOID_TYPE_P (gro_type)); + r = get_ro; + } else if (same_type_p (gro_type, fn_return_type)) { /* [dcl.fct.def.coroutine] / 7 @@ -5122,31 +5138,11 @@ cp_coroutine_transform::build_ramp_function () for an object of the return type. */ if (same_type_p (gro_type, fn_return_type)) - r = gro_is_void_p ? NULL_TREE : DECL_RESULT (orig_fn_decl); - else if (!gro_is_void_p) + r = void_ramp_p ? NULL_TREE : DECL_RESULT (orig_fn_decl); + else /* check_return_expr will automatically return gro as an rvalue via treat_lvalue_as_rvalue_p. */ r = gro; - else if (CLASS_TYPE_P (fn_return_type)) - { - /* For class type return objects, we can attempt to construct, - even if the gro is void. ??? Citation ??? c++/100476 */ - r = build_special_member_call (NULL_TREE, - complete_ctor_identifier, NULL, - fn_return_type, LOOKUP_NORMAL, - tf_warning_or_error); - r = build_cplus_new (fn_return_type, r, tf_warning_or_error); - } - else - { - /* We can't initialize a non-class return value from void. */ - error_at (fn_start, "cannot initialize a return object of type" - " %qT with an rvalue of type %", fn_return_type); - r = error_mark_node; - valid_coroutine = false; - input_location = save_input_loc; - return; - } finish_return_stmt (r); diff --git a/gcc/testsuite/g++.dg/coroutines/coro-bad-gro-01-void-gro-non-class-coro.C b/gcc/testsuite/g++.dg/coroutines/coro-bad-gro-01-void-gro-non-class-coro.C index 85aadad93b2..a7e3f3d1ac7 100644 --- a/gcc/testsuite/g++.dg/coroutines/coro-bad-gro-01-void-gro-non-class-coro.C +++ b/gcc/testsuite/g++.dg/coroutines/coro-bad-gro-01-void-gro-non-class-coro.C @@ -27,7 +27,7 @@ struct std::coroutine_traits { }; int -my_coro (std::coroutine_handle<>& h) // { dg-error {cannot initialize a return object of type 'int' with an rvalue of type 'void'} } +my_coro (std::coroutine_handle<>& h) // { dg-error {no viable conversion from 'void' provided by 'get_return_object' to return type 'int'} } { PRINT ("coro1: about to return"); co_return; diff --git a/gcc/testsuite/g++.dg/coroutines/pr102489.C b/gcc/testsuite/g++.dg/coroutines/pr102489.C index 0ef06daa211..15b85f4375b 100644 --- a/gcc/testsuite/g++.dg/coroutines/pr102489.C +++ b/gcc/testsuite/g++.dg/coroutines/pr102489.C @@ -9,7 +9,7 @@ struct footask { std::suspend_never initial_suspend(); std::suspend_never final_suspend() noexcept; void unhandled_exception(); - void get_return_object(); + footask get_return_object(); }; std::suspend_always foo; footask taskfun() { co_await foo; } diff --git a/gcc/testsuite/g++.dg/coroutines/pr103868.C b/gcc/testsuite/g++.dg/coroutines/pr103868.C index fd05769db3d..0ce40b699ce 100644 --- a/gcc/testsuite/g++.dg/coroutines/pr103868.C +++ b/gcc/testsuite/g++.dg/coroutines/pr103868.C @@ -116,7 +116,7 @@ struct awaitable_frame_base { template auto await_transform(T a) { return a; } }; template <> struct awaitable_frame : awaitable_frame_base { - void get_return_object(); + awaitable get_return_object(); }; } // namespace detail } // namespace asio diff --git a/gcc/testsuite/g++.dg/coroutines/pr94879-folly-1.C b/gcc/testsuite/g++.dg/coroutines/pr94879-folly-1.C index 6e091526fe7..2d886fd387d 100644 --- a/gcc/testsuite/g++.dg/coroutines/pr94879-folly-1.C +++ b/gcc/testsuite/g++.dg/coroutines/pr94879-folly-1.C @@ -39,9 +39,10 @@ public: std::g initial_suspend(); l final_suspend() noexcept; }; +class n; class m : public j { public: - void get_return_object(); + n get_return_object(); void unhandled_exception(); }; class n { diff --git a/gcc/testsuite/g++.dg/coroutines/pr94883-folly-2.C b/gcc/testsuite/g++.dg/coroutines/pr94883-folly-2.C index 98c5a7e3eee..f12897e8690 100644 --- a/gcc/testsuite/g++.dg/coroutines/pr94883-folly-2.C +++ b/gcc/testsuite/g++.dg/coroutines/pr94883-folly-2.C @@ -16,25 +16,7 @@ struct b { void await_resume(); }; } // namespace std - -template auto ab(int ac, d ad) -> decltype(ad.e(ac)); -int f; -class h { - class j { - public: - bool await_ready() noexcept; - void await_suspend(std::coroutine_handle<>) noexcept; - void await_resume() noexcept; - }; - -public: - void get_return_object(); - std::b initial_suspend(); - j final_suspend() noexcept; - void unhandled_exception(); - template - auto await_transform (g c) { return ab(f, c); } -}; +class h; template class k { public: using promise_type = h; @@ -64,6 +46,25 @@ my_coro (k am, ai) { ; } +template auto ab(int ac, d ad) -> decltype(ad.e(ac)); +int f; +class h { + class j { + public: + bool await_ready() noexcept; + void await_suspend(std::coroutine_handle<>) noexcept; + void await_resume() noexcept; + }; + +public: + k get_return_object(); + std::b initial_suspend(); + j final_suspend() noexcept; + void unhandled_exception(); + template + auto await_transform (g c) { return ab(f, c); } +}; + void foo () { k a; my_coro (a, [] {}); diff --git a/gcc/testsuite/g++.dg/coroutines/pr96749-2.C b/gcc/testsuite/g++.dg/coroutines/pr96749-2.C index 43052b57dd9..3d764527291 100644 --- a/gcc/testsuite/g++.dg/coroutines/pr96749-2.C +++ b/gcc/testsuite/g++.dg/coroutines/pr96749-2.C @@ -27,7 +27,7 @@ struct Task { struct promise_type { auto initial_suspend() { return std::suspend_always{}; } auto final_suspend() noexcept { return std::suspend_always{}; } - void get_return_object() {} + Task get_return_object() ; void unhandled_exception() {} }; }; From patchwork Wed Aug 21 19:10:03 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Iain Sandoe X-Patchwork-Id: 1975081 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20230601 header.b=kjCCl4n/; 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 4WpwsS5qkqz1yXf for ; Thu, 22 Aug 2024 05:10:52 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id A645E387093A for ; Wed, 21 Aug 2024 19:10:50 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-ed1-x529.google.com (mail-ed1-x529.google.com [IPv6:2a00:1450:4864:20::529]) by sourceware.org (Postfix) with ESMTPS id 99DBF385DDED for ; Wed, 21 Aug 2024 19:10:14 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 99DBF385DDED Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gmail.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 99DBF385DDED Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2a00:1450:4864:20::529 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1724267419; cv=none; b=oWIBMK59H2J5a1YeFQ9KJgtPgM8K6Jy3rqlHzcEapUhg2ZjL0btzxVoymdtSUcSO7lrR57Xuu33VtzQCM7YTjbB9x1T5+tOSlJsrTOR+c/tPFRzQ98t8ZY8gRkrNcdzySvJI+lwp1lPzamyyEA46n2bHkZVzSjG9cYr7vCqSvLY= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1724267419; c=relaxed/simple; bh=9GQ+JVmcUgp7C8gcdQBAD0uo5YsScWG1IQl7EfqqSpY=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=qtGwiriC72uCQFkyE0pUtsbvukaKV750O2o50f09VNx6HE3hnX02QnFC/HV7VxgNHI/KQOGqL78SNyKtEptKbs9T5YBBAzN/mJ4YW17d7ZT9NMvxsKcWEfvz7rKvgRCvTdEfwJOTOeKtdHWmfHuD6Y6+rKQ8YhryZ1YVGJYdrts= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-ed1-x529.google.com with SMTP id 4fb4d7f45d1cf-5bf068aebe5so83863a12.0 for ; Wed, 21 Aug 2024 12:10:14 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1724267413; x=1724872213; darn=gcc.gnu.org; h=content-transfer-encoding:mime-version:reply-to:references :in-reply-to:message-id:date:subject:cc:to:from:from:to:cc:subject :date:message-id:reply-to; bh=s5aWoSTF2p5+mXu1gxQtaCUGT48PZyn1KWDdYqN4yKU=; b=kjCCl4n/Z9+gev49PRjwfpfrIgw+QsIq3gdbSHMInktsrp0BQ41XAt0kdvL0L1uJBy HhuLvO6NF5a8geIZGgPPQiWP9Zuxyl65QjT+E9QHda0dgPRQYjqla7Yf8P0QV8ZbHr6r ooBBRe/1wDGHwXwLXZ3n3+zSRz46lWorRI+DVmC4BHr+w73aINm1dWsoSnKBHs/IvIfL qpZ3S3ON/j0qgEbuMDJpkhF/CGzCqiOLPsBdcAR+szjWE6mJh9rg9HdT0C4G3p0PmmT2 58HFDFO/vuRVdcx2g4b0Wh6R2HODconigBnO5UqE0LOynoNVDOHiK1bav6jawzLDjWMY xthA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1724267413; x=1724872213; h=content-transfer-encoding:mime-version:reply-to:references :in-reply-to:message-id:date:subject:cc:to:from:x-gm-message-state :from:to:cc:subject:date:message-id:reply-to; bh=s5aWoSTF2p5+mXu1gxQtaCUGT48PZyn1KWDdYqN4yKU=; b=HxU7+eC0CoKDSr0OrESjZ1V3/pJt1wTHZJqV1p+1AmH8hmX1i5R4i+kAQ5ELdzf7w7 2WRDz2hLxK+Pb0ERu83iQgDcSrOOPSkThia8XSuzMxutOcadHElBRFmgfxMae63t6ayM fTK8tEwsl39r++pHmxuiuVm6nDkczW48KesB848IiZbOiDZta5PaZmTi0clXPA+gTFfB G9AwYmcDRFmj61H1anEkA6Dq4Oad3zcpuZJqX1pn/yRXVfw1us6LcQWTUc7nRJfXslwD 1NKLFqONRHpwkJ+P/bICRAUkIcfT2LRHdRDNpLQUlp19F8y3dJuzOIhYKiQLNDU4lYgX JN+w== X-Gm-Message-State: AOJu0YzRIwc8oycvi2JbjDDZPX8CFx+q7XAxR4sxvrX3SueubvTzv7ay NLLW4OoqCE9TxZ/uJq95YbEsUdQ6OTiEgPF65M7g742R1jpyfXUNGxHnLssE X-Google-Smtp-Source: AGHT+IGG+ytB1bBsAz2QGmBpy4PVDsloLQ3JraM3t3GfMJm34vUX4KHLhToW+wBxqoZINY6ObVHHpQ== X-Received: by 2002:a17:907:d859:b0:a71:ddb8:9394 with SMTP id a640c23a62f3a-a866f417993mr229932266b.40.1724267412563; Wed, 21 Aug 2024 12:10:12 -0700 (PDT) Received: from localhost.localdomain (host81-138-1-83.in-addr.btopenworld.com. [81.138.1.83]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a83838c6c07sm941222566b.43.2024.08.21.12.10.11 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Wed, 21 Aug 2024 12:10:12 -0700 (PDT) From: Iain Sandoe X-Google-Original-From: Iain Sandoe To: gcc-patches@gcc.gnu.org Cc: jason@redhat.com Subject: [PATCH 6/9] c++, coroutines: Allow convertible get_return_on_allocation_fail [PR109682]. Date: Wed, 21 Aug 2024 20:10:03 +0100 Message-Id: <1c6f7df8ccf8f2b26dbdddfb3f2b516efcddc53c.1724267239.git.iain@sandoe.co.uk> X-Mailer: git-send-email 2.39.2 (Apple Git-143) In-Reply-To: References: MIME-Version: 1.0 X-Spam-Status: No, score=-8.3 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP, T_SCC_BODY_TEXT_LINE 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: , Reply-To: iain@sandoe.co.uk Errors-To: gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org We have been requiring the get_return_on_allocation_fail() call to have the same type as the ramp. This is not intended by the standard, so relax that to allow anything convertible to the ramp return. PR c++/109682 gcc/cp/ChangeLog: * coroutines.cc (cp_coroutine_transform::build_ramp_function): Allow for cases where get_return_on_allocation_fail has a type convertible to the ramp return type. gcc/testsuite/ChangeLog: * g++.dg/coroutines/pr109682.C: New test. Signed-off-by: Iain Sandoe --- gcc/cp/coroutines.cc | 19 +++++---------- gcc/testsuite/g++.dg/coroutines/pr109682.C | 28 ++++++++++++++++++++++ 2 files changed, 34 insertions(+), 13 deletions(-) create mode 100644 gcc/testsuite/g++.dg/coroutines/pr109682.C diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc index d152ad20dca..64a08b49132 100644 --- a/gcc/cp/coroutines.cc +++ b/gcc/cp/coroutines.cc @@ -4824,25 +4824,18 @@ cp_coroutine_transform::build_ramp_function () control to the caller of the coroutine and the return value is obtained by a call to T::get_return_object_on_allocation_failure(), where T is the promise type. */ - - gcc_checking_assert (same_type_p (fn_return_type, TREE_TYPE (grooaf))); tree if_stmt = begin_if_stmt (); tree cond = build1 (CONVERT_EXPR, frame_ptr_type, nullptr_node); cond = build2 (EQ_EXPR, boolean_type_node, coro_fp, cond); finish_if_stmt_cond (cond, if_stmt); + r = NULL_TREE; if (void_ramp_p) - { - /* Execute the get-return-object-on-alloc-fail call... */ - finish_expr_stmt (grooaf); - /* ... but discard the result, since we return void. */ - finish_return_stmt (NULL_TREE); - } + /* Execute the get-return-object-on-alloc-fail call... */ + finish_expr_stmt (grooaf); else - { - /* Get the fallback return object. */ - r = build_cplus_new (fn_return_type, grooaf, tf_warning_or_error); - finish_return_stmt (r); - } + /* Get the fallback return object. */ + r = grooaf; + finish_return_stmt (r); finish_then_clause (if_stmt); finish_if_stmt (if_stmt); } diff --git a/gcc/testsuite/g++.dg/coroutines/pr109682.C b/gcc/testsuite/g++.dg/coroutines/pr109682.C new file mode 100644 index 00000000000..24aab921ab2 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/pr109682.C @@ -0,0 +1,28 @@ + +#include +#include + +struct test +{ + test () {} + test (int) {} + + struct promise_type { + test get_return_object () { return {}; } + // vvv + static int get_return_object_on_allocation_failure () { return {}; } + std::suspend_never initial_suspend () noexcept { return {}; } + std::suspend_never final_suspend () noexcept { return {}; } + void return_void () {} + void unhandled_exception () {} + }; +}; + +test +f () { co_return; } + +int +main () +{ + f (); +} From patchwork Wed Aug 21 19:10:04 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Iain Sandoe X-Patchwork-Id: 1975083 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20230601 header.b=Q0Ngl7zw; 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 4Wpwsv6VVzz1yXf for ; Thu, 22 Aug 2024 05:11:15 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id BB87E3870937 for ; Wed, 21 Aug 2024 19:11:13 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-ej1-x62a.google.com (mail-ej1-x62a.google.com [IPv6:2a00:1450:4864:20::62a]) by sourceware.org (Postfix) with ESMTPS id 7B52938708BE for ; Wed, 21 Aug 2024 19:10:15 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 7B52938708BE Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gmail.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 7B52938708BE Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2a00:1450:4864:20::62a ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1724267420; cv=none; b=H19x76OTBqP7QLEm8caZXzY/2L4vCwjEP1xfcV2/+jb9oJTmReeTepCQmiUKb08XDlEp13/JeByfHDtG16HhDxcCUUhGMHb/dxBmLscc30vfSpdyi8pqKO8fSnX4qqbZm7vOlNKHVLddU9fdgrHN8nQIapNhUbfE8E1rxu2AR80= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1724267420; c=relaxed/simple; bh=aah40xxidwC6IEsW5R4dzOnrAnamFcb33/Ilc3zLZTg=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=O+sxTiWDCXBy6Y0uwy0eS3S2KUU0EpJVkhd7i4Z+5M8NN1fG/QSTn/b50Yll8qcF5qJc27Mfzq1wwve82Gs9OZRPs6LzWR3b7SRZ7PRyVCqCqR6vDP0fHyhlzgfGxdbSf3H4Z9/kCmxW+WGyhdbfQkF7OMvUoT7JrrVhnHNH7v8= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-ej1-x62a.google.com with SMTP id a640c23a62f3a-a83562f9be9so652212166b.0 for ; Wed, 21 Aug 2024 12:10:15 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1724267414; x=1724872214; darn=gcc.gnu.org; h=content-transfer-encoding:mime-version:reply-to:references :in-reply-to:message-id:date:subject:cc:to:from:from:to:cc:subject :date:message-id:reply-to; bh=hDce4vnWe+t5U3cgqGQDxFMhAQINg/yeJ59TwuFvOfo=; b=Q0Ngl7zw/v8+hiUg53r+nqproh749Mb/V2k/hAsM8+UBlhFzTjmzTdGiB49GnY+iSU gnq3Zp3QiHs1+h1/qWJEFUoBb6x/+nsYpojBmQ5ICInv9LCFHe27Ekw0gWuL7NrM0hGk Bct9too7hVE+mOKSGkw+1ydNp4Inan0+8cPJ9Me8lm0mL3VORXThRjwXb8iLQiJ1mEcE G41f96/PU3PTx3xNUPm3XI4Nswh99ishTOtD2rMOlCHHENhT1zDz8sE6sPMnHzY1BGCI TLFjsJu/SRsXSaK7KSFnCDAr0vfjHD6FnvpZq0XQHXAEmvcTsf/lTgA4X7KC3xAANqAE 2Wuw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1724267414; x=1724872214; h=content-transfer-encoding:mime-version:reply-to:references :in-reply-to:message-id:date:subject:cc:to:from:x-gm-message-state :from:to:cc:subject:date:message-id:reply-to; bh=hDce4vnWe+t5U3cgqGQDxFMhAQINg/yeJ59TwuFvOfo=; b=j5OptGGNTv5S7LuycdroLszife6YajWoBQh4iJIgM4/WyGezBexT+yRsqAxw9FTF30 kGMLf8dJ4piXYcix1XcbcQgMgWrS8jTEg9hYAYcFPhCf7wezFsG8g5ig35gpnNorkIRG aKpuuNIY5kwl9z9DWaxNAO51y/W9E97ff0C8/xVGUy85P39VA2SHqcve3vdR+bsAvDY6 IhEqfFOqgXtqpQapCGajh5t2O2gy3M6XH5U8tr9qiPtOeJ59as3tHshi+rlDmsFXkc8T ROhAFQNI1R5uZ+3Prml0xA6MOFWXw8FLlMc3OwWFY4QTHjYdt+GpkVjXMP3k68LLh6xB 8mxw== X-Gm-Message-State: AOJu0YwYGJvHHragItav9vMEb/6yCtlD5Fhp6+o0gBMhNB+CFnnHwZE1 vkxeNIibE407S9HVJCMdL/nXahkVWSKUuXyt0B07V1pojjz/Po2YqnAQQnlg X-Google-Smtp-Source: AGHT+IHW1BZqYWOsPWFQaqQJUT073W3lgV+Y6+24Z4x4RZX41pNvI7woYHytK6nH9xySlgD7Sskc0w== X-Received: by 2002:a17:907:7e9e:b0:a83:a85b:ed40 with SMTP id a640c23a62f3a-a866f11db06mr228911066b.6.1724267413370; Wed, 21 Aug 2024 12:10:13 -0700 (PDT) Received: from localhost.localdomain (host81-138-1-83.in-addr.btopenworld.com. [81.138.1.83]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a83838c6c07sm941222566b.43.2024.08.21.12.10.12 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Wed, 21 Aug 2024 12:10:13 -0700 (PDT) From: Iain Sandoe X-Google-Original-From: Iain Sandoe To: gcc-patches@gcc.gnu.org Cc: jason@redhat.com Subject: [PATCH 7/9] c++, coroutines: Fix ordering of return object conversions [PR115908]. Date: Wed, 21 Aug 2024 20:10:04 +0100 Message-Id: X-Mailer: git-send-email 2.39.2 (Apple Git-143) In-Reply-To: References: MIME-Version: 1.0 X-Spam-Status: No, score=-8.3 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP, T_SCC_BODY_TEXT_LINE 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: , Reply-To: iain@sandoe.co.uk Errors-To: gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org [dcl.fct.def.coroutine]/7 says: The expression promise.get_return_object() is used to initialize the returned reference or prvalue result object of a call to a coroutine. The call to get_return_object is sequenced before the call to initial_suspend and is invoked at most once. The issue is about when any conversions are carried out if the type of the g_r_o call is not the same as the ramp return. Currently, we have been doing this by materialising the g_r_o return value and passing that to finish_return_expr() which handles the necessary conversions and checks. As the PR shows, this does not work as expected. In the revised version we carry out the work of the conversions when intialising the return slot (with the same facilities that are used by finish_return_expr()). We do this before the call that initiates the coroutine body, satisfying the requirements for one call before initial suspend. The return expression becomes a trivial 'return '. This simplifes the ramp logic considerably, since we no longer need to keep track of the temporarily-materialised g_r_o value. PR c++/115908 gcc/cp/ChangeLog: * coroutines.cc (cp_coroutine_transform::build_ramp_function): Rework the return value initialisation to initialise the return slot always from get_return_object, even if that implies carrying out conversions to do so. gcc/testsuite/ChangeLog: * g++.dg/coroutines/pr115908.C: New test. Signed-off-by: Iain Sandoe --- gcc/cp/coroutines.cc | 153 +++++++-------------- gcc/testsuite/g++.dg/coroutines/pr115908.C | 75 ++++++++++ 2 files changed, 121 insertions(+), 107 deletions(-) create mode 100644 gcc/testsuite/g++.dg/coroutines/pr115908.C diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc index 64a08b49132..8738ce15533 100644 --- a/gcc/cp/coroutines.cc +++ b/gcc/cp/coroutines.cc @@ -4641,6 +4641,8 @@ cp_coroutine_transform::build_ramp_function () tree promise_type = get_coroutine_promise_type (orig_fn_decl); tree fn_return_type = TREE_TYPE (TREE_TYPE (orig_fn_decl)); bool void_ramp_p = VOID_TYPE_P (fn_return_type); + /* We know there was no return statement, that is intentional. */ + suppress_warning (orig_fn_decl, OPT_Wreturn_type); /* [dcl.fct.def.coroutine] / 10 (part1) The unqualified-id get_return_object_on_allocation_failure is looked up @@ -5009,162 +5011,99 @@ cp_coroutine_transform::build_ramp_function () promise_dtor = cxx_maybe_build_cleanup (p, tf_warning_or_error); } - /* Set up a new bind context for the GRO. */ - tree gro_context_bind = build3 (BIND_EXPR, void_type_node, NULL, NULL, NULL); - /* Make and connect the scope blocks. */ - tree gro_block = make_node (BLOCK); - BLOCK_SUPERCONTEXT (gro_block) = top_block; - BLOCK_SUBBLOCKS (top_block) = gro_block; - BIND_EXPR_BLOCK (gro_context_bind) = gro_block; - add_stmt (gro_context_bind); - tree get_ro = coro_build_promise_expression (orig_fn_decl, p, coro_get_return_object_identifier, fn_start, NULL, /*musthave=*/true); - /* Without a return object we haven't got much clue what's going on. */ + /* Without a return object we haven't got much clue what is going on. */ if (get_ro == error_mark_node) { - BIND_EXPR_BODY (ramp_bind) = pop_stmt_list (ramp_body); /* Suppress warnings about the missing return value. */ - suppress_warning (orig_fn_decl, OPT_Wreturn_type); valid_coroutine = false; input_location = save_input_loc; return; } - tree gro_context_body = push_stmt_list (); - tree gro_type = TREE_TYPE (get_ro); + /* Initialize the resume_idx_var to 0, meaning "not started". */ + tree resume_idx_m + = lookup_member (frame_type, coro_resume_index_id, + /*protect=*/1, /*want_type=*/0, tf_warning_or_error); + tree resume_idx + = build_class_member_access_expr (deref_fp, resume_idx_m, NULL_TREE, false, + tf_warning_or_error); + r = build_int_cst (short_unsigned_type_node, 0); + r = cp_build_init_expr (loc, resume_idx, r); + finish_expr_stmt (r); - tree gro = NULL_TREE; - tree gro_bind_vars = NULL_TREE; /* Used for return objects in the RESULT slot. */ - tree gro_ret_dtor = NULL_TREE; - tree gro_cleanup_stmt = NULL_TREE; - /* We have to sequence the call to get_return_object before initial - suspend. */ + tree ret_val_dtor = NULL_TREE; + + /* [dcl.fct.def.coroutine] / 7 + The expression promise.get_return_object() is used to initialize the + glvalue result or prvalue result object of a call to a coroutine. */ + if (void_ramp_p) { - gcc_checking_assert (VOID_TYPE_P (gro_type)); + gcc_checking_assert (VOID_TYPE_P (TREE_TYPE (get_ro))); + /* We still want to call the method, even if the result is unused. */ r = get_ro; } - else if (same_type_p (gro_type, fn_return_type)) + else { - /* [dcl.fct.def.coroutine] / 7 - The expression promise.get_return_object() is used to initialize the - glvalue result or... (see below) - Construct the return result directly. */ - if (type_build_ctor_call (gro_type)) + bool no_warning; + bool dangling; + r = check_return_expr (get_ro, &no_warning, &dangling); + gcc_checking_assert (!dangling); + /* Check for bad things. */ + if (!r || r == error_mark_node) { - vec *arg = make_tree_vector_single (get_ro); - r = build_special_member_call (DECL_RESULT (orig_fn_decl), - complete_ctor_identifier, - &arg, gro_type, LOOKUP_NORMAL, - tf_warning_or_error); - release_tree_vector (arg); + valid_coroutine = false; + input_location = save_input_loc; + return; } - else - r = cp_build_init_expr (loc, DECL_RESULT (orig_fn_decl), get_ro); - - if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (gro_type)) - /* If some part of the initalization code (prior to the await_resume - of the initial suspend expression), then we need to clean up the - return value. */ - gro_ret_dtor = cxx_maybe_build_cleanup (DECL_RESULT (orig_fn_decl), - tf_warning_or_error); - } - else - { - /* ... or ... Construct an object that will be used as the single - param to the CTOR for the return object. */ - gro = coro_build_artificial_var (loc, "_Coro_gro", gro_type, orig_fn_decl, - NULL_TREE); - add_decl_expr (gro); - gro_bind_vars = gro; - r = cp_build_modify_expr (input_location, gro, INIT_EXPR, get_ro, - tf_warning_or_error); - /* The constructed object might require a cleanup. */ - if (tree cleanup = cxx_maybe_build_cleanup (gro, tf_warning_or_error)) - gro_cleanup_stmt = build_stmt (input_location, CLEANUP_STMT, NULL, - cleanup, gro); } + finish_expr_stmt (r); - if (gro_cleanup_stmt && gro_cleanup_stmt != error_mark_node) - CLEANUP_BODY (gro_cleanup_stmt) = push_stmt_list (); + if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (fn_return_type)) + /* If some part of the initalization code (prior to the await_resume + of the initial suspend expression), then we need to clean up the + return value. */ + ret_val_dtor = cxx_maybe_build_cleanup (DECL_RESULT (orig_fn_decl), + tf_warning_or_error); /* If we have a live g.r.o in the return slot, then signal this for exception cleanup. */ - if (gro_ret_dtor) + if (ret_val_dtor) { r = build_modify_expr (loc, coro_gro_live, boolean_type_node, INIT_EXPR, loc, boolean_true_node, boolean_type_node); finish_expr_stmt (r); } - /* Initialize the resume_idx_var to 0, meaning "not started". */ - tree resume_idx_m - = lookup_member (frame_type, coro_resume_index_id, - /*protect=*/1, /*want_type=*/0, tf_warning_or_error); - tree resume_idx - = build_class_member_access_expr (deref_fp, resume_idx_m, NULL_TREE, false, - tf_warning_or_error); - r = build_int_cst (short_unsigned_type_node, 0); - r = cp_build_init_expr (loc, resume_idx, r); - finish_expr_stmt (r); - /* So .. call the actor .. */ + /* Start the coroutine body. */ r = build_call_expr_loc (fn_start, resumer, 1, coro_fp); finish_expr_stmt (r); - /* Switch to using 'input_location' as the loc, since we're now more - logically doing things related to the end of the function. */ - - /* The ramp is done, we just need the return value. - [dcl.fct.def.coroutine] / 7 - The expression promise.get_return_object() is used to initialize the - glvalue result or prvalue result object of a call to a coroutine. - - If the 'get return object' is non-void, then we built it before the - promise was constructed. We now supply a reference to that var, - either as the return value (if it's the same type) or to the CTOR - for an object of the return type. */ - - if (same_type_p (gro_type, fn_return_type)) - r = void_ramp_p ? NULL_TREE : DECL_RESULT (orig_fn_decl); - else - /* check_return_expr will automatically return gro as an rvalue via - treat_lvalue_as_rvalue_p. */ - r = gro; + /* The ramp is done, we just need the return statement, which we build from + the return object we constructed before we called the actor. */ + r = void_ramp_p ? NULL_TREE : DECL_RESULT (orig_fn_decl); finish_return_stmt (r); - if (gro_cleanup_stmt) - { - CLEANUP_BODY (gro_cleanup_stmt) - = pop_stmt_list (CLEANUP_BODY (gro_cleanup_stmt)); - add_stmt (gro_cleanup_stmt); - } - - /* Finish up the ramp function. */ - BIND_EXPR_VARS (gro_context_bind) = gro_bind_vars; - BIND_EXPR_BODY (gro_context_bind) = pop_stmt_list (gro_context_body); - TREE_SIDE_EFFECTS (gro_context_bind) = true; - if (flag_exceptions) { TRY_HANDLERS (ramp_cleanup) = push_stmt_list (); tree handler = begin_handler (); finish_handler_parms (NULL_TREE, handler); /* catch (...) */ - /* If we have a live G.R.O in the return slot, then run its DTOR. - When the return object is constructed from a separate g.r.o, this is - already handled by its regular cleanup. */ - if (gro_ret_dtor && gro_ret_dtor != error_mark_node) + /* If we have a live G.R.O in the return slot, then run its DTOR. */ + if (ret_val_dtor && ret_val_dtor != error_mark_node) { tree gro_d_if = begin_if_stmt (); finish_if_stmt_cond (coro_gro_live, gro_d_if); - finish_expr_stmt (gro_ret_dtor); + finish_expr_stmt (ret_val_dtor); finish_then_clause (gro_d_if); tree gro_d_if_scope = IF_SCOPE (gro_d_if); IF_SCOPE (gro_d_if) = NULL; diff --git a/gcc/testsuite/g++.dg/coroutines/pr115908.C b/gcc/testsuite/g++.dg/coroutines/pr115908.C new file mode 100644 index 00000000000..ac27d916de2 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/pr115908.C @@ -0,0 +1,75 @@ +#include + +#ifdef OUTPUT +#include +#endif + +struct Promise; + +bool promise_live = false; + +struct Handle : std::coroutine_handle { + Handle(Promise &p) : std::coroutine_handle(Handle::from_promise(p)) { + if (!promise_live) + __builtin_abort (); +#ifdef OUTPUT + std::cout << "Handle(Promise &)\n"; +#endif + } + Handle(Promise &&p) : std::coroutine_handle(Handle::from_promise(p)) { + if (!promise_live) + __builtin_abort (); +#ifdef OUTPUT + std::cout << "Handle(Promise &&)\n"; +#endif + } + + using promise_type = Promise; +}; + +struct Promise { + Promise() { +#ifdef OUTPUT + std::cout << "Promise()\n"; +#endif + promise_live = true; + } + ~Promise() { +#ifdef OUTPUT + std::cout << "~Promise()\n"; +#endif + if (!promise_live) + __builtin_abort (); + promise_live = false; + } + Promise& get_return_object() noexcept { +#ifdef OUTPUT + std::cout << "get_return_object()\n"; +#endif + if (!promise_live) + __builtin_abort (); + return *this; + } + std::suspend_never initial_suspend() const noexcept { return {}; } + std::suspend_never final_suspend() const noexcept { return {}; } + void return_void() const noexcept { + if (!promise_live) + __builtin_abort (); +#ifdef OUTPUT + std::cout << "return_void()\n"; +#endif + } + void unhandled_exception() const noexcept {} +}; + +Handle Coro() { + co_return; +} + +int main() { + Coro(); + + if (promise_live) + __builtin_abort (); + return 0; +} From patchwork Wed Aug 21 19:10:05 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Iain Sandoe X-Patchwork-Id: 1975086 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20230601 header.b=PVRFDT38; 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 4Wpwtk16Pcz1yXf for ; Thu, 22 Aug 2024 05:11:58 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 04C173870927 for ; Wed, 21 Aug 2024 19:11:56 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-ej1-x634.google.com (mail-ej1-x634.google.com [IPv6:2a00:1450:4864:20::634]) by sourceware.org (Postfix) with ESMTPS id E94F2385E836 for ; Wed, 21 Aug 2024 19:10:15 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org E94F2385E836 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gmail.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org E94F2385E836 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2a00:1450:4864:20::634 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1724267421; cv=none; b=avZMxgXqjavlt6pDhab2TzhwFets3l4pTUMMwuLU9tfKpXthTdYpNeIqOU9qjUGuopHsZJ2GqY6+a4AI3WzY31tSlTfiLQI+QVTApDh3v8+gz+dKSRjrj3eSY0IRJ6mXfheX97o7diwJ3FTWsF669lVQHFo96Yb48m4FIH53+Y8= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1724267421; c=relaxed/simple; bh=7NrprE8AewVVGxuxBlSyiedEYsHjeQ1NPUY0jQgTC+M=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=BXBvl2RxoKU/ftx1y4Jm1m/kf3JYAFo4UZF5lNl75AJRkuh4NLjWwk5vImQUZHIqYKOPgdeyvAzl1xXOjnUUUbYgn9LDuxyps8y8ptY43BBpI5X7N4GWiv8HuT36XN123QiBTo8SaDDeQ3NHSLDYtuJmgCYB++kJGVSf6CZF14Y= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-ej1-x634.google.com with SMTP id a640c23a62f3a-a864574429aso292289166b.0 for ; Wed, 21 Aug 2024 12:10:15 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1724267414; x=1724872214; darn=gcc.gnu.org; h=content-transfer-encoding:mime-version:reply-to:references :in-reply-to:message-id:date:subject:cc:to:from:from:to:cc:subject :date:message-id:reply-to; bh=9HJZOqYF5LlsyAgYkilCsGmVcQ0Ds1lbmJuZ4XFtzks=; b=PVRFDT383CM16xLnuOJ1ARE3Vv8GUQwBn1YNsAdSJzxQso51rxdfnlYFsQbKEFzboF CG3lsSsuYx2CaSzEMebUD8Od6SvJ56gIRyU67fyGXQIZKQ4YVweJX4y0FSW1h6sjbqcW SdBxBB6buteyYXJ0JOlImP9flC5nMS1SJDsDRmwZHZYWRnY3Zz/x5PYiTWVzZ2t+js8e koXLFLqbXGHInFjWRDjY+yBJeRgqKkq1j9z6L8HIARM5P5CS1N0em+UjJ7n47HeYPnvg A7ADc0ERZqpNNISsHnNvR2NCC8q/KIy3Uk6Ux0EVXNK9cUGNgochaSsu/0+3JB5x6oHi A7uw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1724267414; x=1724872214; h=content-transfer-encoding:mime-version:reply-to:references :in-reply-to:message-id:date:subject:cc:to:from:x-gm-message-state :from:to:cc:subject:date:message-id:reply-to; bh=9HJZOqYF5LlsyAgYkilCsGmVcQ0Ds1lbmJuZ4XFtzks=; b=igAfCzU9aO7Z4VYAAfZ1fg8zf8BTsykGYNsVRuqzRIC8Gq/M+FAJcVihSD5o88HZIq fdMcniAgTwa2RvCLSUY79ulsijC1tmYCq0DXv8zApUabQrzW44a4P6z4bxrvbmCR96Jf 1BEp5r2n+7TglMgrolEfBghaeYl10KR8u/mcTU4CALCOKdvG0bdH6esT/YzRfua1toX4 TlwxxlK9x/esFGcRFHER6PXC6aQd4tk32N24b8uf2p6GvMBMEZuuptG3iJUCVzrdH4Sq FPs5Aspigg++YFRUMqgssIYkvFPtemqy/dMmYx5IgF7NTQyPdauVLke9r6H58yUynJyF /Fnw== X-Gm-Message-State: AOJu0YwW+8g91Wml0vQypGsRRI/XuR9iJPKcCv2HA2m9dXRVTObTUa7B 1IuNVYf355QMNv1kFihZNdG+494kYbg5jWFhRf5XciR6K7Yucatecil5PkUf X-Google-Smtp-Source: AGHT+IGQ0Yg0VafV6DZ0UOIy/L1Qm9M91P+6lwFZ1JZ6FKhRXiZ3ZDXVN3cma9yiFlltpp+5RnA2nQ== X-Received: by 2002:a17:907:9807:b0:a86:85eb:bde9 with SMTP id a640c23a62f3a-a8685ebcf75mr97349266b.50.1724267414091; Wed, 21 Aug 2024 12:10:14 -0700 (PDT) Received: from localhost.localdomain (host81-138-1-83.in-addr.btopenworld.com. [81.138.1.83]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a83838c6c07sm941222566b.43.2024.08.21.12.10.13 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Wed, 21 Aug 2024 12:10:13 -0700 (PDT) From: Iain Sandoe X-Google-Original-From: Iain Sandoe To: gcc-patches@gcc.gnu.org Cc: jason@redhat.com Subject: [PATCH 8/9] c++, coroutines: Rework handling of throwing_cleanups [PR102051]. Date: Wed, 21 Aug 2024 20:10:05 +0100 Message-Id: <2014547e103bd589380b26667c54c026c2f9c929.1724267239.git.iain@sandoe.co.uk> X-Mailer: git-send-email 2.39.2 (Apple Git-143) In-Reply-To: References: MIME-Version: 1.0 X-Spam-Status: No, score=-8.4 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP, T_SCC_BODY_TEXT_LINE 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: , Reply-To: iain@sandoe.co.uk Errors-To: gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org In the fix for PR95822 (r11-7402) we set throwing_cleanup false in the top level of the coroutine transform code. However, as the current PR shows, that is not sufficient. Any use of cxx_maybe_build_cleanup() can reset the flag, which causes the check_return_expr () logic to try to add a guard variable and set it. For the coroutine code, we need to handle the cleanups separately, since the responsibility for them changes after the first resume point, which we handle in the ramp exception processing. Fix this by forcing the "throwing_cleanup" flag false right before the processing of the return expression. PR c++/102051 gcc/cp/ChangeLog: * coroutines.cc (cp_coroutine_transform::build_ramp_function): Handle "throwing_cleanup" here instead of ... (cp_coroutine_transform::apply_transforms): ... here. gcc/testsuite/ChangeLog: * g++.dg/coroutines/pr102051.C: New test. Signed-off-by: Iain Sandoe --- gcc/cp/coroutines.cc | 21 +++++++++------------ gcc/testsuite/g++.dg/coroutines/pr102051.C | 16 ++++++++++++++++ 2 files changed, 25 insertions(+), 12 deletions(-) create mode 100644 gcc/testsuite/g++.dg/coroutines/pr102051.C diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc index 8738ce15533..1039d2f8515 100644 --- a/gcc/cp/coroutines.cc +++ b/gcc/cp/coroutines.cc @@ -5042,6 +5042,10 @@ cp_coroutine_transform::build_ramp_function () The expression promise.get_return_object() is used to initialize the glvalue result or prvalue result object of a call to a coroutine. */ + /* We must manage the cleanups ourselves, because the responsibility for + them changes after the initial suspend. However, any use of + cxx_maybe_build_cleanup () can set the throwing_cleanup flag. */ + cp_function_chain->throwing_cleanup = false; if (void_ramp_p) { gcc_checking_assert (VOID_TYPE_P (TREE_TYPE (get_ro))); @@ -5050,6 +5054,7 @@ cp_coroutine_transform::build_ramp_function () } else { + /* The initial section of finish_return_expr (). */ bool no_warning; bool dangling; r = check_return_expr (get_ro, &no_warning, &dangling); @@ -5090,7 +5095,10 @@ cp_coroutine_transform::build_ramp_function () the return object we constructed before we called the actor. */ r = void_ramp_p ? NULL_TREE : DECL_RESULT (orig_fn_decl); - finish_return_stmt (r); + /* The reminder of finish_return_expr (). */ + r = build_stmt (loc, RETURN_EXPR, r); + r = maybe_cleanup_point_expr_void (r); + r = add_stmt (r); if (flag_exceptions) { @@ -5280,17 +5288,6 @@ cp_coroutine_transform::apply_transforms () body_blocks = current_binding_level->blocks; current_binding_level->blocks = NULL_TREE; - /* If the original function has a return value with a non-trivial DTOR - and the body contains a var with a DTOR that might throw, the decl is - marked "throwing_cleanup". - We do not [in the ramp, which is synthesised here], use any body var - types with DTORs that might throw. - The original body is transformed into the actor function which only - contains void returns, and is also wrapped in a try-catch block. - So (a) the 'throwing_cleanup' is not correct for the ramp and (b) we do - not need to transfer it to the actor which only contains void returns. */ - cp_function_chain->throwing_cleanup = false; - /* Collect information on the original function params and their use in the function body. */ param_uses = analyze_fn_parms (orig_fn_decl); diff --git a/gcc/testsuite/g++.dg/coroutines/pr102051.C b/gcc/testsuite/g++.dg/coroutines/pr102051.C new file mode 100644 index 00000000000..bba98b691cc --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/pr102051.C @@ -0,0 +1,16 @@ +#include + +struct Foo { + ~Foo() noexcept(false); // true succeeds + struct promise_type { + Foo get_return_object() { return {}; } + std::suspend_never initial_suspend() { return {}; } + void return_void() {} + void unhandled_exception() {} + std::suspend_always final_suspend() noexcept { return {}; } + }; +}; + +Foo bar() { + co_return; +} From patchwork Wed Aug 21 19:10:06 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Iain Sandoe X-Patchwork-Id: 1975088 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20230601 header.b=SdQf2B0b; 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 4Wpwvp0rg1z1yXf for ; Thu, 22 Aug 2024 05:12:54 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id EFF0A38708BF for ; Wed, 21 Aug 2024 19:12:51 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-ed1-x529.google.com (mail-ed1-x529.google.com [IPv6:2a00:1450:4864:20::529]) by sourceware.org (Postfix) with ESMTPS id 92DAB386F478 for ; Wed, 21 Aug 2024 19:10:16 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 92DAB386F478 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gmail.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 92DAB386F478 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2a00:1450:4864:20::529 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1724267422; cv=none; b=iwF3KslaKjw8u+kKsKRKxH2apLTflngCtnoaewda7IRuVa0A/T28dLYnrL7IIUZ1sznvCHg18V+AazeihELLBgr17JgrYpzX5+n1Kbcoi7cw6RRNicAL8u17ROdlYuxaflcU19HK7oLb5Ryl+pLBEqlFMn24mBGACAKf13vaU6w= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1724267422; c=relaxed/simple; bh=eLaCbjkMHYSrsSmFSfyM6UZfcySHel7rQajqug3dPdg=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=oWoNrksf9jM9L0eO0SqVHCEKbtYDUJlkpP2hnUWwDb3ampSBP7RNDYGFDFqnwBsiowrt/1zusYcIWhC3mUAOQXCmAvLal6omAmwtvnBJGg3i6YJYYI5ZXogyNl8MQRklaRQV8OpGqmpRfPoG8FwOv18PXz70yLJYdFeXz1NBiIc= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-ed1-x529.google.com with SMTP id 4fb4d7f45d1cf-5bede548f7cso52849a12.2 for ; Wed, 21 Aug 2024 12:10:16 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1724267415; x=1724872215; darn=gcc.gnu.org; h=content-transfer-encoding:mime-version:reply-to:references :in-reply-to:message-id:date:subject:cc:to:from:from:to:cc:subject :date:message-id:reply-to; bh=bDS/UgRgYKC6VhdrT1Y8CZLLt3k4SB/xG53DOVUreYo=; b=SdQf2B0bLF6cun3v/E+w7NvG6r0QYJyr4LrzJA9qF9yDH+cQwRLLbPm/+faETeX1i5 GoXD5Tfi3E9w3PB6CYHpx+zJ34Ue3FLeybJBfeIg8kD5w9xMhGincTrgQ4AiNkqSJltK hDclbwCKy7mw96XHEzxzueEFkHJAI6NZ3hRCQzc4cwkXx1TGHFtuhBbDxF/MEJ0Iwd6S xMk08TArPBJGlDHWdkuy53jvrXr+juI9yB+RZeN+sUYir8TcJaHAXSNJiNeouiiCousV 4jk1wSOrtpnzMR8IOCcUVWbpMU57t/SwGS2SyAnXPOjsXhjIeyJ8Jsh52GjC1fIlqzKX 6s/w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1724267415; x=1724872215; h=content-transfer-encoding:mime-version:reply-to:references :in-reply-to:message-id:date:subject:cc:to:from:x-gm-message-state :from:to:cc:subject:date:message-id:reply-to; bh=bDS/UgRgYKC6VhdrT1Y8CZLLt3k4SB/xG53DOVUreYo=; b=lyfC5zxzE9Z+VtA13U2Rvdd/wFwplMSMKEYS3wPNbnGWHVxdNMJzZ8lHVSnX+I2hgY IlE2Qeyb9z1djysAyyNCk47AKJ7g1UuP9CiG5YKYjumlGIEosHDkZHMe4BksXv9sI8z6 IGnrVMgVDr0k+52+9pKYxfA+wEsbuYATTyMtZW1Td2bwMenuZX1ghrnBRpdBx1dGFaM4 ovgZamLl30TdhVv8Qinn9MOgW0ZtBS2/GtSexCBygZ1nVGedvwzXdGtP0/4A96kekd2+ iHg321KQFqL0KWKA9rKew/VwRRSTLOqYHlu5anm9+/444o/HZJBWTUbFLRithTi12RxE Cqqg== X-Gm-Message-State: AOJu0YwLWBhwC2WF+nLSEuSYzDNuWyte2PRzHSrCGVpkI+SIbbxKlslG v30nB/Vafi0fVygWd/xD7lxIciNy13KSvpQfhZnT66uj1VaU1MtSPrCIdUbw X-Google-Smtp-Source: AGHT+IG2oHrePE/hMMR2ImXjICk5eyTlxXhBpCykahKM5BkLj+moC9TmirtksbjDqDoMOKL9qFtEvQ== X-Received: by 2002:a17:906:7309:b0:a86:6fd8:53e3 with SMTP id a640c23a62f3a-a866fd8558dmr248646466b.66.1724267414769; Wed, 21 Aug 2024 12:10:14 -0700 (PDT) Received: from localhost.localdomain (host81-138-1-83.in-addr.btopenworld.com. [81.138.1.83]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a83838c6c07sm941222566b.43.2024.08.21.12.10.14 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Wed, 21 Aug 2024 12:10:14 -0700 (PDT) From: Iain Sandoe X-Google-Original-From: Iain Sandoe To: gcc-patches@gcc.gnu.org Cc: jason@redhat.com Subject: [PATCH 9/9] c++, coroutines: Look through initial_await target exprs [PR110635]. Date: Wed, 21 Aug 2024 20:10:06 +0100 Message-Id: <83199df1749de7a7a63a3fdf219ae0d9db59896d.1724267239.git.iain@sandoe.co.uk> X-Mailer: git-send-email 2.39.2 (Apple Git-143) In-Reply-To: References: MIME-Version: 1.0 X-Spam-Status: No, score=-8.4 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP, T_SCC_BODY_TEXT_LINE 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: , Reply-To: iain@sandoe.co.uk Errors-To: gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org In the case that the initial awaiter returns an object, the initial await can be a target expression and we need to look at its initializer to cast the await_resume() to void and to wrap in a compoun expression that sets the initial_await_resume_called flag. PR c++/110635 gcc/cp/ChangeLog: * coroutines.cc (cp_coroutine_transform::wrap_original_function_body): Look through initial await target expressions to find the actual co_await_expr that we need to update. gcc/testsuite/ChangeLog: * g++.dg/coroutines/pr110635.C: New test. Signed-off-by: Iain Sandoe --- gcc/cp/coroutines.cc | 10 ++- gcc/testsuite/g++.dg/coroutines/pr110635.C | 72 ++++++++++++++++++++++ 2 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 gcc/testsuite/g++.dg/coroutines/pr110635.C diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc index 1039d2f8515..fcd6d7f0a7d 100644 --- a/gcc/cp/coroutines.cc +++ b/gcc/cp/coroutines.cc @@ -4287,7 +4287,15 @@ cp_coroutine_transform::wrap_original_function_body () a reference type, look past the indirection. */ if (INDIRECT_REF_P (initial_await)) initial_await = TREE_OPERAND (initial_await, 0); - tree vec = TREE_OPERAND (initial_await, 3); + /* In the case that the initial_await returns a target expression + we might need to look through that to update the await expr. */ + tree iaw = initial_await; + if (TREE_CODE (iaw) == TARGET_EXPR) + { + iaw = TARGET_EXPR_INITIAL (iaw); + gcc_checking_assert (TREE_CODE (iaw) == CO_AWAIT_EXPR); + } + tree vec = TREE_OPERAND (iaw, 3); tree aw_r = TREE_VEC_ELT (vec, 2); aw_r = convert_to_void (aw_r, ICV_STATEMENT, tf_warning_or_error); tree update = build2 (MODIFY_EXPR, boolean_type_node, i_a_r_c, diff --git a/gcc/testsuite/g++.dg/coroutines/pr110635.C b/gcc/testsuite/g++.dg/coroutines/pr110635.C new file mode 100644 index 00000000000..ea4e0e853eb --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/pr110635.C @@ -0,0 +1,72 @@ + +#define CASE 0 +#include +#include + +struct Coroutine { + + struct promise_type; + + using handler_type = std::coroutine_handle; + + struct initial_suspend_awaiter { + + bool await_ready() noexcept { + std::cout << "await_ready" << std::endl; + return false; + } + + void await_suspend(handler_type h) noexcept { + std::cout << "await_suspend" << std::endl; + } + +#if CASE == 0 + struct await_resume_return_object { + await_resume_return_object() noexcept { + std::cout << "await_resume_return_object" << std::endl; + } + + ~await_resume_return_object() noexcept { + std::cout << "~await_resume_return_object" << std::endl; + } + }; +#elif CASE == 1 + using await_resume_return_object = struct{}; +#elif CASE == 2 + using await_resume_return_object = int; +#else + using await_resume_return_object = void; +#endif + await_resume_return_object await_resume() noexcept { + std::cout << "await_resume" << std::endl; +#if CASE == 0 || CASE == 1 || CASE == 2 + return {}; +#endif + } + + initial_suspend_awaiter() noexcept { + std::cout << "initial_suspend_awaiter" << std::endl; + } + + ~initial_suspend_awaiter() noexcept { + std::cout << "~initial_suspend_awaiter" << std::endl; + } + }; + + struct promise_type { + void return_void() noexcept {} + void unhandled_exception() noexcept { std::terminate();} + initial_suspend_awaiter initial_suspend() noexcept { return {}; } + std::suspend_never final_suspend() noexcept { return {}; } + Coroutine get_return_object() { + return Coroutine{handler_type::from_promise(*this)}; + } + }; + + handler_type handler; +}; + +int main() { + auto coro = []()->Coroutine { co_return; }(); + coro.handler.resume(); +}