From patchwork Fri Jan 9 04:39:12 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nathaniel Shead X-Patchwork-Id: 127689 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from vm01.sourceware.org (localhost [127.0.0.1]) by sourceware.org (Postfix) with ESMTP id 8DB0B4BA2E1D for ; Fri, 9 Jan 2026 04:40:04 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 8DB0B4BA2E1D Authentication-Results: sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20230601 header.b=Ea5OEPlu X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-pj1-f53.google.com (mail-pj1-f53.google.com [209.85.216.53]) by sourceware.org (Postfix) with ESMTPS id DE9AF4BA2E07 for ; Fri, 9 Jan 2026 04:39:18 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org DE9AF4BA2E07 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 DE9AF4BA2E07 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=209.85.216.53 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1767933559; cv=none; b=x7gTLHtRgvGBuRV4ha/g9EIZwcCdAArAXTXsnncTGz4kJgHIo/Xen96mdbSvI9I6HzPrpB/ATxBsD3Hn19NQRcJEZdxRlD060QzTo6eYUiaZj7z0Q2hVuiYDK0wVRpIEsZ7qogMav6JCIdKcoV8JqnOSSOB7Nrcqg13SWEzjl7w= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1767933559; c=relaxed/simple; bh=h+w/XAyjY3veY/ReZQ71EqHheEhvhjyDeMD/BPOqb0A=; h=DKIM-Signature:Date:From:To:Subject:Message-ID:MIME-Version; b=d8h9HPf1zNSIrtG39Bd5/rAspVmN1z/COk1Dg5Olav0FCKwgMlJQig7DiUoOsjJ4oGGkWRRbMwWoj93HiC6pX7iw7XKylfVlzkdMBZd0rk49VgU2I6T622ku9tTQ2YNXcB/qqswXsh1Td7IuddXbmLtnHYzLwnV0BXWVGJry13Y= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-pj1-f53.google.com with SMTP id 98e67ed59e1d1-34eeffdb197so597178a91.1 for ; Thu, 08 Jan 2026 20:39:18 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1767933558; x=1768538358; darn=gcc.gnu.org; h=content-disposition:mime-version:message-id:subject:cc:to:from:date :from:to:cc:subject:date:message-id:reply-to; bh=Cv7CrmyPeBSh8INzpfNd/pjQvEwlRG0SfrqaefRrzrY=; b=Ea5OEPlu4zja8Z+IhVX26AHT1mFzsyBqv2B7DcJ5Fqktvxfnhd4mLvdTXnvQo2gXSn 6DsFvYowv5HfKrNJp4xh7a4RibN/ilGVr+u+WyZHuNBh++9OZwLbBZxwqTz7IJu/sdS/ t01AFjYYcyoTKoF3Pu/5+76NhPVO0t20yJVd+fL+piXOeVvSplBLshWoHn+T2h8ZJLbb coyrwRaPP6yVYVTVhOryozmTrp3zskSUFECIgWzWmDKRNOJ41fO3JaHbZw3WZCrbyvQj 6ZYxuqpcoisqmTSvu5pNofQ1kW24lM1eEYe9s98/qYy0uV+901jCFbTjvtA8LZ7Yw2YS fm/g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1767933558; x=1768538358; h=content-disposition:mime-version:message-id:subject:cc:to:from:date :x-gm-gg:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=Cv7CrmyPeBSh8INzpfNd/pjQvEwlRG0SfrqaefRrzrY=; b=t7DD+Cehk9Xzx1Rhnq7cfxWsKI8EfdzkcIPme7iKbeZ6Ai8ceRv8zVtpun1GGx7twr rJxbb//Ie4UM4MvQGFBTNhwTossvBo3/yzvqA7my7aVGhldMhOcs5U4IrzXG0LfRmJPm rFjxxu7joKLSp2NiRAJCGqCzabxhSKAsHBPdhXJMR2ShqepkBYkuWVmjDkkLvLuYMq6R Jmw1NCX0o/bciKqbUKFYm1Ws5Bck2sUDqVerKcDyA4p2x584B1A7+WcoUpz8FxJaYVUE JZoP1GI0sU6VGkOKpexI9VYjnCYcwmm5ltWOn3bZTcQ5ceKElLZD8XvqDOtnw0YQsMKW X0NQ== X-Gm-Message-State: AOJu0Yyq/uiJajb0sm/+YIuIMax+NGN7LrN42X7BrMUQkNCFPAUvQF2s s7FylxS2f2ueHfap46xvnitNlD8Q7OZXi/dMdumiLTUjZi/wWp+vxL4C/poCMQ== X-Gm-Gg: AY/fxX7jsW8B1QcrULymF1W6xpcILBcwcFreI9rPDtyM57ZC0Fo8/7EicpODettVgLJ y442MUcxNhgLkd2qH9ON+ssXfux3VG2V+6eVbmsQ1w8SILF2sbSQ3Z7PC2EnJcQ1AtaWa4fTCqt mJ0XshuYxNKWGEh1oFVc4qjzGKXstXXiECThXhYlyH7cYpP30fKehKNwXCdXgILrutXjMUmqXpG zxCgvPHeOhtmOD44QWlWy+/hvykcAKHacjMSHZrtN0sD2wvvJbhSquK7KT6RWy7LVlDkf1WMJ2k Vt3r8bV9tam29zGY+Jaep8WC1SEsDiul2Yn2T/ZF3LXw5DAPpRhdpbWTFZOHkPIWyPrcebup/Fv eUb1nSoEuuVq62U6+wjYUj2wDWkLqAy+xPDTwWzEOaw5we5CpY7h3j7gLuK1OXyLZP4ZD3z6bYE WqdyVP4LiQyloGQIAOiKQiRraq2IqtTJrnPQAVNVpDC+o= X-Google-Smtp-Source: AGHT+IHDS0SPLSz7fagwXZRRCKj5gwd1zS5b3Tyw+uG5T3ZtQGH09fMi+g1Wqf8MxA8JBE7HgBn3OA== X-Received: by 2002:a17:903:1ca:b0:2a0:d662:7283 with SMTP id d9443c01a7336-2a3ee436196mr60410385ad.3.1767933557485; Thu, 08 Jan 2026 20:39:17 -0800 (PST) Received: from Thaum.localdomain ([163.53.146.3]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2a3e3c48dc1sm90988185ad.40.2026.01.08.20.39.15 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 08 Jan 2026 20:39:17 -0800 (PST) Date: Fri, 9 Jan 2026 15:39:12 +1100 From: Nathaniel Shead To: gcc-patches@gcc.gnu.org Cc: Jason Merrill , Iain Sandoe Subject: [PATCH] c++: modules and coroutines Message-ID: MIME-Version: 1.0 Content-Disposition: inline X-Spam-Status: No, score=-11.8 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, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED, SPF_HELO_NONE, SPF_PASS, TXREP, URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gcc-patches-bounces~patchwork=sourceware.org@gcc.gnu.org Bootstrapped and regtested on x86_64-pc-linux-gnu, OK for trunk? -- >8 -- While working on another issue I found that currently modules do not work with coroutines at all. This patch fixes a number of issues in both the coroutines logic and modules logic to ensure that they play well together. To summarize: - The coroutine proxy objects did not have a DECL_CONTEXT set (required for modules to merge declarations). - The coroutine transformation functions were always considered non-inline, even for an inline ramp function, which meant that modules didn't realise it needed to stream a definition. - In an importing TU we had lost the connection between the ramp functions and the transform functions, as they were kept in a pair of global maps. - Modules streaming couldn't discriminate between the actor or destroy functions when merging. - Modules streaming wasn't setting the cfun->coroutine_component flag, needed to activate the middle-end coroutine lowering pass. This patch also separates the coroutine_info_table initialization from the ensure_coro_initialized function. If the first time we see a coroutine is from a module import, we need to register the transformation functions now but calling ensure_coro_initialized would lookup e.g. std::coroutine_traits, which may only be visible from this module that we're currently reading, causing a recursive load. Separating the concerns allows this to work correctly. gcc/cp/ChangeLog: * coroutines.cc (create_coroutine_info_table): New function. (get_or_insert_coroutine_info): Mark static. (ensure_coro_initialized): Likewise; use create_coroutine_info_table. (coro_promise_type_found_p): Set DECL_CONTEXT of proxies. (coro_set_ramp_function): New function. (coro_set_transform_functions): New function. (coro_build_actor_or_destroy_function): Use coro_set_ramp_function; copy linkage from original function. * cp-tree.h (coro_set_transform_functions): Declare. (coro_set_ramp_function): Declare. * decl2.cc (mark_used): Mark transform functions as used if we use the ramp function. * module.cc (struct merge_key): New field coro_disc. (struct post_process_data): New field coroutine_component. (get_coroutine_discriminator): New function. (trees_out::key_mergeable): Write coroutine discriminator. (check_mergeable_decl): Adjust comment, check for matching coroutine discriminator. (trees_in::key_mergeable): Read coroutine discriminator. (trees_out::write_function_def): Write coroutine_component and ramp/actor/destroy functions for coroutines. (trees_in::read_function_def): Read them. (module_state::read_cluster): Set cfun->coroutine_component. gcc/testsuite/ChangeLog: * g++.dg/modules/coro-1_a.C: New test. * g++.dg/modules/coro-1_b.C: New test. Signed-off-by: Nathaniel Shead --- gcc/cp/coroutines.cc | 64 ++++++++++++++++---- gcc/cp/cp-tree.h | 4 ++ gcc/cp/decl2.cc | 13 +++++ gcc/cp/module.cc | 78 ++++++++++++++++++++++--- gcc/testsuite/g++.dg/modules/coro-1_a.C | 28 +++++++++ gcc/testsuite/g++.dg/modules/coro-1_b.C | 19 ++++++ 6 files changed, 189 insertions(+), 17 deletions(-) create mode 100644 gcc/testsuite/g++.dg/modules/coro-1_a.C create mode 100644 gcc/testsuite/g++.dg/modules/coro-1_b.C diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc index f0485a95073..930d453dde2 100644 --- a/gcc/cp/coroutines.cc +++ b/gcc/cp/coroutines.cc @@ -353,10 +353,20 @@ coroutine_info_hasher::equal (coroutine_info *lhs, const compare_type& rhs) return lhs->function_decl == rhs; } +/* Initialize the coroutine info table, to hold state per coroutine decl, + if not already created. */ + +static void +create_coroutine_info_table () +{ + if (!coroutine_info_table) + coroutine_info_table = hash_table::create_ggc (11); +} + /* Get the existing coroutine_info for FN_DECL, or insert a new one if the entry does not yet exist. */ -coroutine_info * +static coroutine_info * get_or_insert_coroutine_info (tree fn_decl) { gcc_checking_assert (coroutine_info_table != NULL); @@ -375,7 +385,7 @@ get_or_insert_coroutine_info (tree fn_decl) /* Get the existing coroutine_info for FN_DECL, fail if it doesn't exist. */ -coroutine_info * +static coroutine_info * get_coroutine_info (tree fn_decl) { if (coroutine_info_table == NULL) @@ -757,11 +767,7 @@ ensure_coro_initialized (location_t loc) if (!void_coro_handle_address) return false; - /* A table to hold the state, per coroutine decl. */ - gcc_checking_assert (coroutine_info_table == NULL); - coroutine_info_table = - hash_table::create_ggc (11); - + create_coroutine_info_table (); if (coroutine_info_table == NULL) return false; @@ -873,11 +879,13 @@ coro_promise_type_found_p (tree fndecl, location_t loc) coro_info->self_h_proxy = build_lang_decl (VAR_DECL, coro_self_handle_id, coro_info->handle_type); + DECL_CONTEXT (coro_info->self_h_proxy) = fndecl; /* Build a proxy for the promise so that we can perform lookups. */ coro_info->promise_proxy = build_lang_decl (VAR_DECL, coro_promise_id, coro_info->promise_type); + DECL_CONTEXT (coro_info->promise_proxy) = fndecl; /* Note where we first saw a coroutine keyword. */ coro_info->first_coro_keyword = loc; @@ -902,6 +910,17 @@ coro_get_ramp_function (tree decl) return NULL_TREE; } +/* Given a DECL, an actor or destroyer, build a link from that to the ramp + function. Used by modules streaming. */ + +void +coro_set_ramp_function (tree decl, tree ramp) +{ + if (!to_ramp) + to_ramp = hash_map::create_ggc (10); + to_ramp->put (decl, ramp); +} + /* Given the DECL for a ramp function (the user's original declaration) return the actor function if it has been defined. */ @@ -926,6 +945,27 @@ coro_get_destroy_function (tree decl) return NULL_TREE; } +/* For a given ramp function DECL, set the actor and destroy functions. + This is only used by modules streaming. */ + +void +coro_set_transform_functions (tree decl, tree actor, tree destroy) +{ + /* Only relevant with modules. */ + gcc_assert (modules_p ()); + + /* This should only be called for newly streamed declarations. */ + gcc_assert (!get_coroutine_info (decl)); + + /* This might be the first use of coroutine info in the TU, so + create the coroutine info table if needed. */ + create_coroutine_info_table (); + + coroutine_info *coroutine = get_or_insert_coroutine_info (decl); + coroutine->actor_decl = actor; + coroutine->destroy_decl = destroy; +} + /* Given a CO_AWAIT_EXPR AWAIT_EXPR, return its resume call. */ tree @@ -4393,15 +4433,19 @@ coro_build_actor_or_destroy_function (tree orig, tree fn_type, = build_lang_decl (FUNCTION_DECL, copy_node (DECL_NAME (orig)), fn_type); /* Allow for locating the ramp (original) function from this one. */ - if (!to_ramp) - to_ramp = hash_map::create_ggc (10); - to_ramp->put (fn, orig); + coro_set_ramp_function (fn, orig); DECL_CONTEXT (fn) = DECL_CONTEXT (orig); DECL_SOURCE_LOCATION (fn) = loc; DECL_ARTIFICIAL (fn) = true; DECL_INITIAL (fn) = error_mark_node; + /* Copy linkage from the original function. */ + TREE_PUBLIC (fn) = TREE_PUBLIC (orig); + DECL_DECLARED_INLINE_P (fn) = DECL_DECLARED_INLINE_P (orig); + DECL_NOT_REALLY_EXTERN (fn) = DECL_NOT_REALLY_EXTERN (orig); + DECL_INTERFACE_KNOWN (fn) = DECL_INTERFACE_KNOWN (orig); + tree id = get_identifier ("frame_ptr"); tree fp = build_lang_decl (PARM_DECL, id, coro_frame_ptr); DECL_ARTIFICIAL (fp) = true; diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index b8470fc256c..1d40b387d6e 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -9144,6 +9144,10 @@ extern tree coro_get_ramp_function (tree); extern tree co_await_get_resume_call (tree await_expr); +/* Only for use by modules. */ +extern void coro_set_transform_functions (tree, tree, tree); +extern void coro_set_ramp_function (tree, tree); + /* Inline bodies. */ inline tree diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc index e807eab1b8a..d50864b8f75 100644 --- a/gcc/cp/decl2.cc +++ b/gcc/cp/decl2.cc @@ -6480,6 +6480,19 @@ mark_used (tree decl, tsubst_flags_t complain /* = tf_warning_or_error */) return false; } + /* For coroutines, we need to mark the transform functions as used, + if they exist yet. */ + if (flag_coroutines + && TREE_CODE (decl) == FUNCTION_DECL + && DECL_COROUTINE_P (decl) + && DECL_RAMP_P (decl)) + { + if (tree actor = DECL_ACTOR_FN (decl)) + mark_used (actor); + if (tree destroy = DECL_DESTROY_FN (decl)) + mark_used (destroy); + } + /* If DECL has a deduced return type, we need to instantiate it now to find out its type. For OpenMP user defined reductions, we need them instantiated for reduction clauses which inline them by hand directly. diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc index af0730ba974..67fb1e4a22d 100644 --- a/gcc/cp/module.cc +++ b/gcc/cp/module.cc @@ -2969,6 +2969,7 @@ static char const *const merge_kind_name[MK_hwm] = /* Mergeable entity location data. */ struct merge_key { cp_ref_qualifier ref_q : 2; + unsigned coro_disc : 2; /* Discriminator for coroutine transforms. */ unsigned index; tree ret; /* Return type, if appropriate. */ @@ -2977,7 +2978,7 @@ struct merge_key { tree constraints; /* Constraints. */ merge_key () - :ref_q (REF_QUAL_NONE), index (0), + :ref_q (REF_QUAL_NONE), coro_disc (0), index (0), ret (NULL_TREE), args (NULL_TREE), constraints (NULL_TREE) { @@ -2999,6 +3000,7 @@ struct post_process_data { bool returns_null; bool returns_abnormally; bool infinite_loop; + bool coroutine_component; }; /* Tree stream reader. Note that reading a stream doesn't mark the @@ -11696,6 +11698,24 @@ trees_in::decl_container () return container; } +/* Gets a 2-bit discriminator to distinguish coroutine actor or destroy + functions from a normal function. */ + +static int +get_coroutine_discriminator (tree inner) +{ + if (tree ramp = DECL_RAMP_FN (inner)) + { + if (DECL_ACTOR_FN (ramp) == inner) + return 1; + else if (DECL_DESTROY_FN (ramp) == inner) + return 2; + else + gcc_unreachable (); + } + return 0; +} + /* Write out key information about a mergeable DEP. Does not write the contents of DEP itself. The context has already been written. The container has already been streamed. */ @@ -11787,6 +11807,7 @@ trees_out::key_mergeable (int tag, merge_kind mk, tree decl, tree inner, tree fn_type = TREE_TYPE (inner); key.ref_q = type_memfn_rqual (fn_type); + key.coro_disc = get_coroutine_discriminator (inner); key.args = TYPE_ARG_TYPES (fn_type); if (tree reqs = get_constraints (inner)) @@ -11923,7 +11944,12 @@ trees_out::key_mergeable (int tag, merge_kind mk, tree decl, tree inner, tree_node (name); if (streaming_p ()) { - unsigned code = (key.ref_q << 0) | (key.index << 2); + /* Check we have enough bits for the index. */ + gcc_checking_assert (key.index < (1u << (sizeof (unsigned) * 8 - 4))); + + unsigned code = ((key.ref_q << 0) + | (key.coro_disc << 2) + | (key.index << 4)); u (code); } @@ -11947,8 +11973,8 @@ trees_out::key_mergeable (int tag, merge_kind mk, tree decl, tree inner, } } -/* DECL is a new declaration that may be duplicated in OVL. Use RET & - ARGS to find its clone, or NULL. If DECL's DECL_NAME is NULL, this +/* DECL is a new declaration that may be duplicated in OVL. Use KEY + to find its clone, or NULL. If DECL's DECL_NAME is NULL, this has been found by a proxy. It will be an enum type located by its first member. @@ -12008,6 +12034,9 @@ check_mergeable_decl (merge_kind mk, tree decl, tree ovl, merge_key const &key) && (!DECL_IS_UNDECLARED_BUILTIN (m_inner) || !DECL_EXTERN_C_P (m_inner) || DECL_EXTERN_C_P (d_inner)) + /* Reject if we're not the same kind of coroutine function. */ + && (!flag_coroutines + || key.coro_disc == get_coroutine_discriminator (m_inner)) /* Reject if one is a different member of a guarded/pre/post fn set. */ && (!flag_contracts @@ -12125,7 +12154,8 @@ trees_in::key_mergeable (int tag, merge_kind mk, tree decl, tree inner, merge_key key; unsigned code = u (); key.ref_q = cp_ref_qualifier ((code >> 0) & 3); - key.index = code >> 2; + key.coro_disc = (code >> 2) & 3; + key.index = code >> 4; if (mk == MK_enum) key.ret = tree_node (); @@ -13031,6 +13061,8 @@ trees_out::write_function_def (tree decl) flags |= 8 * f->language->returns_null; flags |= 16 * f->language->returns_abnormally; flags |= 32 * f->language->infinite_loop; + /* Set for coroutines. */ + flags |= 64 * f->coroutine_component; } u (flags); @@ -13041,6 +13073,17 @@ trees_out::write_function_def (tree decl) state->write_location (*this, f->function_start_locus); state->write_location (*this, f->function_end_locus); } + + if (f && f->coroutine_component) + { + tree ramp = DECL_RAMP_FN (decl); + tree_node (ramp); + if (!ramp) + { + tree_node (DECL_ACTOR_FN (decl)); + tree_node (DECL_DESTROY_FN (decl)); + } + } } void @@ -13056,13 +13099,13 @@ trees_in::read_function_def (tree decl, tree maybe_template) tree initial = tree_node (); tree saved = tree_node (); tree context = tree_node (); - constexpr_fundef cexpr; post_process_data pdata {}; pdata.decl = maybe_template; tree maybe_dup = odr_duplicate (maybe_template, DECL_SAVED_TREE (decl)); bool installing = maybe_dup && !DECL_SAVED_TREE (decl); + constexpr_fundef cexpr; if (u ()) { cexpr.parms = chained_decls (); @@ -13074,7 +13117,6 @@ trees_in::read_function_def (tree decl, tree maybe_template) cexpr.decl = NULL_TREE; unsigned flags = u (); - if (flags & 2) { pdata.start_locus = state->read_location (*this); @@ -13083,6 +13125,22 @@ trees_in::read_function_def (tree decl, tree maybe_template) pdata.returns_null = flags & 8; pdata.returns_abnormally = flags & 16; pdata.infinite_loop = flags & 32; + pdata.coroutine_component = flags & 64; + } + + tree coro_actor = NULL_TREE; + tree coro_destroy = NULL_TREE; + tree coro_ramp = NULL_TREE; + if (pdata.coroutine_component) + { + coro_ramp = tree_node (); + if (!coro_ramp) + { + coro_actor = tree_node (); + coro_destroy = tree_node (); + if ((coro_actor == NULL_TREE) != (coro_destroy == NULL_TREE)) + set_overrun (); + } } if (get_overrun ()) @@ -13100,6 +13158,11 @@ trees_in::read_function_def (tree decl, tree maybe_template) if (cexpr.decl) register_constexpr_fundef (cexpr); + if (coro_ramp) + coro_set_ramp_function (decl, coro_ramp); + else if (coro_actor && coro_destroy) + coro_set_transform_functions (decl, coro_actor, coro_destroy); + if (DECL_LOCAL_DECL_P (decl)) /* Block-scope OMP UDRs aren't real functions, and don't need a function structure to be allocated or to be expanded. */ @@ -17556,6 +17619,7 @@ module_state::read_cluster (unsigned snum) cfun->language->returns_null = pdata.returns_null; cfun->language->returns_abnormally = pdata.returns_abnormally; cfun->language->infinite_loop = pdata.infinite_loop; + cfun->coroutine_component = pdata.coroutine_component; /* Make sure we emit explicit instantiations. FIXME do we want to do this in expand_or_defer_fn instead? */ diff --git a/gcc/testsuite/g++.dg/modules/coro-1_a.C b/gcc/testsuite/g++.dg/modules/coro-1_a.C new file mode 100644 index 00000000000..ec2c8300a80 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/coro-1_a.C @@ -0,0 +1,28 @@ +// { dg-do compile { target c++20 } } +// { dg-additional-options "-fmodules" } +// { dg-module-cmi M } + +module; +#include +export module M; + +struct simple_promise; +struct simple_coroutine : std::coroutine_handle { + using promise_type = ::simple_promise; +}; +struct simple_promise { + simple_coroutine get_return_object() { return { simple_coroutine::from_promise(*this) }; } + std::suspend_always initial_suspend() noexcept { return {}; } + std::suspend_always final_suspend() noexcept { return {}; } + void return_void() {} + void unhandled_exception() {} +}; +export simple_coroutine coroutine() { + co_return; +} +export inline simple_coroutine inline_coroutine() { + co_return; +} +export template simple_coroutine template_coroutine() { + co_return; +} diff --git a/gcc/testsuite/g++.dg/modules/coro-1_b.C b/gcc/testsuite/g++.dg/modules/coro-1_b.C new file mode 100644 index 00000000000..e1384a7d639 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/coro-1_b.C @@ -0,0 +1,19 @@ +// { dg-module-do run { target c++20 } } +// { dg-additional-options "-fmodules" } + +#include +import M; + +int main() { + auto a = coroutine(); + a.resume(); + a.destroy(); + + auto b = inline_coroutine(); + b.resume(); + b.destroy(); + + auto c = template_coroutine(); + c.resume(); + c.destroy(); +}