From patchwork Mon Mar 25 09:17:30 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nathaniel Shead X-Patchwork-Id: 87614 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 88C023858417 for ; Mon, 25 Mar 2024 09:18:16 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-pj1-x1032.google.com (mail-pj1-x1032.google.com [IPv6:2607:f8b0:4864:20::1032]) by sourceware.org (Postfix) with ESMTPS id 3E1F23858403 for ; Mon, 25 Mar 2024 09:17:38 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 3E1F23858403 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 3E1F23858403 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2607:f8b0:4864:20::1032 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1711358262; cv=none; b=mLJ4qM10pS84MW+TvpXwl6ddwflO6ZuC2J9ZU3Zln4VAXhleHxmZXkhSyS85NyGAe2MVIRI60VMJJPGtvBvcHeoTL9YSY42r8IKDv9heMtw7oLqddMthZe2Li6K5U2NuLxXZW5yAQGJOkVOFgs9RSjlB1q5skfI8NY+FTxlov64= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1711358262; c=relaxed/simple; bh=UdPAxlACv94AThTjOqjvNNa68TuiBOMhGoHV7uOsSzQ=; h=DKIM-Signature:Message-ID:Date:From:To:Subject:MIME-Version; b=xIwknsQWU2Raask/gh3WqxBrXyfkAix68zGf3gCpSeFPdpXEMzieoPMhdq3WUaw9/47SQqGI/jwZjooN7DIgxZjhJHA+hH/S2wtiNHQoqQawaTft0v416vIsiDg0HIfJHu9WCMPoWRDW87ii98xTZoxV+JdweeMQl5oj7gWEfEM= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-pj1-x1032.google.com with SMTP id 98e67ed59e1d1-29f1686ff12so2756998a91.1 for ; Mon, 25 Mar 2024 02:17:38 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1711358257; x=1711963057; darn=gcc.gnu.org; h=content-disposition:mime-version:subject:cc:to:from:date:message-id :from:to:cc:subject:date:message-id:reply-to; bh=/RYVHGhPqytooXWa1fgZJ0SYvZokQCugIvE12sjJLXY=; b=c7m6dLnjMmT157L19iR4ltxFyx9WOXQMb2NMrireYD1bORFPf392EpiLNRSptZ+R7V N3BpeL9ONy51rhc6og8NnRVzshu3E/4+FHBwE9wqzC2uhsETYWkPsjja0Om+7u6FzLHF kEKH7ZJ28tQf5O/MfGS3+85hzlrM2xBGA5J5kgOuvwCR6ogPEc0momeneCecmP2h1OjO ThpE0XcdSuLKSc3i8Lr7cmQGvUbOwLCMiugpPszZi73SyLK4jz+vKWyo8kqE0pFDAdHv fHv5JA9bByuFAwJCkLxpP2Vn/7R+NYWDer3I4UhMKTC6extJ7hR0vgxOSz4hIwda2CHZ pF6Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1711358257; x=1711963057; h=content-disposition:mime-version:subject:cc:to:from:date:message-id :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=/RYVHGhPqytooXWa1fgZJ0SYvZokQCugIvE12sjJLXY=; b=ZNpe7munJv0+xkevy9h3RUmRwtjLqmDYqbztTzaaNhRVatw8yj6ZO3+jCmBU09wYhh KEkytBuUARTX/R2maW2GXvcZW4bIisP2JippbWq3qHjWDYOVBzReharuHclo/ATbXlWO HR1nJzpVOBsWkLVh53OBLb/Pj8qNzcHWcjJkAM5bRi6m4qLN963B54uFhJUh2M2hJdZI 4cYf+adAojxV5JdGw2YzPylrS66kUURVk6Rvj2GJNQX9XH96FXpVpmivWW1JwYiK6mQP muEVLVCt165XOlE2YxSiQdC1he+m0Zd5huaFBHM+PNG37L3tUB5bttSm3t4XG+Aijd3B lCwQ== X-Gm-Message-State: AOJu0Yz9sBc6kj60KXu1tlJx/DMSePyqrAf2qewncuIB9YJaqf/t+G6j XpvCUxm0Fk7WwC19+Pfh9PI4mEo1jTm88Q+Pik5lwXXk7TciLdyLW/rF0L7d X-Google-Smtp-Source: AGHT+IHqwyvPPeXIEui5Ms7getiTRpNozFVPMcL7l3jdvNv+ucqs2oFHEi21Q6TzuCNJyWbw4fYT3w== X-Received: by 2002:a17:90a:b118:b0:29b:ce5b:596f with SMTP id z24-20020a17090ab11800b0029bce5b596fmr3916383pjq.41.1711358256853; Mon, 25 Mar 2024 02:17:36 -0700 (PDT) Received: from Thaum. (193-119-89-230.tpgi.com.au. [193.119.89.230]) by smtp.gmail.com with ESMTPSA id eu16-20020a17090af95000b0029fbfb620cdsm6077388pjb.28.2024.03.25.02.17.34 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 25 Mar 2024 02:17:36 -0700 (PDT) Message-ID: <66014130.170a0220.d7c40.0e9b@mx.google.com> X-Google-Original-Message-ID: Date: Mon, 25 Mar 2024 20:17:30 +1100 From: Nathaniel Shead To: gcc-patches@gcc.gnu.org Cc: Jason Merrill , Nathan Sidwell , Patrick Palka Subject: [PATCH] c++/modules: Fix instantiation of imported temploid friends [PR114275] MIME-Version: 1.0 Content-Disposition: inline X-Spam-Status: No, score=-11.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, URIBL_BLACK autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gcc-patches-bounces+patchwork=sourceware.org@gcc.gnu.org Bootstrapped and regtested on x86_64-pc-linux-gnu, OK for trunk? I'm not 100% sure I've covered all places where this needs to be handled but so far this passes all the testcases I have. -- >8 -- This patch fixes a number of issues with the handling of temploid friend declarations. The primary issue is that instantiations of friend declarations should attach the declaration to the same module as the befriending class, by [module.unit] p7.1 and [temp.friend] p2; this could be a different module from the current TU, and so needs special handling. This patch only implements this for class declarations so far, as function declarations don't seem to be causing any issues with this currently, but probably should revisit for GCC 15. The other main issue here is that we can't assume that just because name lookup didn't find a definition for a hidden template class, it doesn't mean that it doesn't exist: it could be a non-exported entity that we've nevertheless streamed in from an imported module. We need to ensure that when instantiating friend classes that we return the same TYPE_DECL that we got from our imports, otherwise we will get later issues with 'duplicate_decls' (rightfully) complaining that they're different. PR c++/105320 PR c++/114275 gcc/cp/ChangeLog: * cp-tree.h (module_may_redeclare_friend_class): New. (propagate_defining_module): New. (lookup_imported_hidden_friend): New. * module.cc (imported_temploid_friends): New. (trees_out::decl_value): Write imported_temploid_friends. (trees_in::decl_value): Read it. (get_originating_module_decl): Handle instantiated friend classes being attached to a different module. (module_may_redeclare_friend_class): New. (propagate_defining_module): New. (init_modules): Initialize imported_temploid_friends. * name-lookup.cc (lookup_imported_hidden_friend): New. * pt.cc (tsubst_friend_class): Lookup imported hidden friends. Error when redeclaring the type in the wrong module. Propagate the defining module to the instantiated type. gcc/testsuite/ChangeLog: * g++.dg/modules/tpl-friend-10_a.C: New test. * g++.dg/modules/tpl-friend-10_b.C: New test. * g++.dg/modules/tpl-friend-10_c.C: New test. * g++.dg/modules/tpl-friend-11_a.C: New test. * g++.dg/modules/tpl-friend-11_b.C: New test. * g++.dg/modules/tpl-friend-12_a.C: New test. * g++.dg/modules/tpl-friend-12_b.C: New test. * g++.dg/modules/tpl-friend-12_c.C: New test. * g++.dg/modules/tpl-friend-12_d.C: New test. * g++.dg/modules/tpl-friend-12_e.C: New test. * g++.dg/modules/tpl-friend-12_f.C: New test. * g++.dg/modules/tpl-friend-13_a.C: New test. * g++.dg/modules/tpl-friend-13_b.C: New test. * g++.dg/modules/tpl-friend-13_c.C: New test. * g++.dg/modules/tpl-friend-13_d.C: New test. * g++.dg/modules/tpl-friend-13_e.C: New test. * g++.dg/modules/tpl-friend-9.C: New test. Signed-off-by: Nathaniel Shead --- gcc/cp/cp-tree.h | 3 + gcc/cp/module.cc | 96 +++++++++++++++++++ gcc/cp/name-lookup.cc | 42 ++++++++ gcc/cp/pt.cc | 20 ++++ gcc/testsuite/g++.dg/modules/tpl-friend-10.h | 0 .../g++.dg/modules/tpl-friend-10_a.C | 15 +++ .../g++.dg/modules/tpl-friend-10_b.C | 5 + .../g++.dg/modules/tpl-friend-10_c.C | 7 ++ .../g++.dg/modules/tpl-friend-11_a.C | 14 +++ .../g++.dg/modules/tpl-friend-11_b.C | 5 + .../g++.dg/modules/tpl-friend-12_a.C | 10 ++ .../g++.dg/modules/tpl-friend-12_b.C | 9 ++ .../g++.dg/modules/tpl-friend-12_c.C | 10 ++ .../g++.dg/modules/tpl-friend-12_d.C | 8 ++ .../g++.dg/modules/tpl-friend-12_e.C | 7 ++ .../g++.dg/modules/tpl-friend-12_f.C | 8 ++ .../g++.dg/modules/tpl-friend-13_a.C | 7 ++ .../g++.dg/modules/tpl-friend-13_b.C | 5 + .../g++.dg/modules/tpl-friend-13_c.C | 7 ++ .../g++.dg/modules/tpl-friend-13_d.C | 6 ++ .../g++.dg/modules/tpl-friend-13_e.C | 8 ++ gcc/testsuite/g++.dg/modules/tpl-friend-9.C | 11 +++ 22 files changed, 303 insertions(+) create mode 100644 gcc/testsuite/g++.dg/modules/tpl-friend-10.h create mode 100644 gcc/testsuite/g++.dg/modules/tpl-friend-10_a.C create mode 100644 gcc/testsuite/g++.dg/modules/tpl-friend-10_b.C create mode 100644 gcc/testsuite/g++.dg/modules/tpl-friend-10_c.C create mode 100644 gcc/testsuite/g++.dg/modules/tpl-friend-11_a.C create mode 100644 gcc/testsuite/g++.dg/modules/tpl-friend-11_b.C create mode 100644 gcc/testsuite/g++.dg/modules/tpl-friend-12_a.C create mode 100644 gcc/testsuite/g++.dg/modules/tpl-friend-12_b.C create mode 100644 gcc/testsuite/g++.dg/modules/tpl-friend-12_c.C create mode 100644 gcc/testsuite/g++.dg/modules/tpl-friend-12_d.C create mode 100644 gcc/testsuite/g++.dg/modules/tpl-friend-12_e.C create mode 100644 gcc/testsuite/g++.dg/modules/tpl-friend-12_f.C create mode 100644 gcc/testsuite/g++.dg/modules/tpl-friend-13_a.C create mode 100644 gcc/testsuite/g++.dg/modules/tpl-friend-13_b.C create mode 100644 gcc/testsuite/g++.dg/modules/tpl-friend-13_c.C create mode 100644 gcc/testsuite/g++.dg/modules/tpl-friend-13_d.C create mode 100644 gcc/testsuite/g++.dg/modules/tpl-friend-13_e.C create mode 100644 gcc/testsuite/g++.dg/modules/tpl-friend-9.C diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 52d53589e51..b782385c574 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -7397,6 +7397,7 @@ inline bool module_exporting_p () extern module_state *get_module (tree name, module_state *parent = NULL, bool partition = false); extern bool module_may_redeclare (tree decl); +extern bool module_may_redeclare_friend_class (tree decl, tree friend_tmpl); extern bool module_global_init_needed (); extern bool module_determine_import_inits (); @@ -7412,6 +7413,7 @@ extern unsigned get_importing_module (tree, bool = false) ATTRIBUTE_PURE; extern void set_instantiating_module (tree); extern void set_defining_module (tree); extern void maybe_key_decl (tree ctx, tree decl); +extern void propagate_defining_module (tree decl, tree tmpl); extern void mangle_module (int m, bool include_partition); extern void mangle_module_fini (); @@ -7644,6 +7646,7 @@ extern bool template_guide_p (const_tree); extern bool builtin_guide_p (const_tree); extern void store_explicit_specifier (tree, tree); extern tree lookup_explicit_specifier (tree); +extern tree lookup_imported_hidden_friend (tree); extern void walk_specializations (bool, void (*)(bool, spec_entry *, void *), diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc index 8aab9ea0bae..cd3acca5e52 100644 --- a/gcc/cp/module.cc +++ b/gcc/cp/module.cc @@ -2693,6 +2693,11 @@ vec *post_load_decls; typedef hash_map> keyed_map_t; static keyed_map_t *keyed_table; +/* Instantiations of temploid friends imported from another module + need to be owned by the same module as their instantiating template. + This maps these to the template that instantiated them. */ +static hash_map *imported_temploid_friends; + /********************************************************************/ /* Tree streaming. The tree streaming is very specific to the tree structures themselves. A tag indicates the kind of tree being @@ -7884,6 +7889,14 @@ trees_out::decl_value (tree decl, depset *dep) } } + if (TREE_CODE (decl) == TEMPLATE_DECL) + { + /* Write imported temploid friends so that importers can reconstruct + this information on stream-in. */ + tree* slot = imported_temploid_friends->get (decl); + tree_node (slot ? *slot : NULL_TREE); + } + bool is_typedef = false; if (!type && TREE_CODE (inner) == TYPE_DECL) { @@ -8190,6 +8203,13 @@ trees_in::decl_value () } } + if (TREE_CODE (decl) == TEMPLATE_DECL) + if (tree owner = tree_node ()) + { + bool exists = imported_temploid_friends->put (decl, owner); + gcc_assert (exists == !is_new); + } + /* Regular typedefs will have a NULL TREE_TYPE at this point. */ unsigned tdef_flags = 0; bool is_typedef = false; @@ -18717,6 +18737,12 @@ get_originating_module_decl (tree decl) && DECL_UNINSTANTIATED_TEMPLATE_FRIEND_P (decl)) decl = TYPE_NAME (DECL_CHAIN (decl)); + /* An imported temploid friend is attached to the same module the + befriending class was. */ + if (TREE_CODE (decl) == TEMPLATE_DECL) + if (tree *slot = imported_temploid_friends->get (decl)) + decl = *slot; + int use; if (tree ti = node_template_info (decl, use)) { @@ -18851,6 +18877,46 @@ module_may_redeclare (tree decl) return me && get_primary (them) == get_primary (me); } +/* Like module_may_redeclare, but compares the attachment and module + with FRIEND_TMPL rather than the current scope. */ + +bool +module_may_redeclare_friend_class (tree tmpl, tree friend_tmpl) +{ + tree inner = DECL_TEMPLATE_RESULT (tmpl); + tree friend_inner = DECL_TEMPLATE_RESULT (friend_tmpl); + + bool tmpl_attached_p = (DECL_LANG_SPECIFIC (inner) + && DECL_MODULE_ATTACH_P (inner)); + bool friend_attached_p = (DECL_LANG_SPECIFIC (friend_inner) + && DECL_MODULE_ATTACH_P (friend_inner)); + + if (!friend_attached_p) + /* We should be attached to the GMF if the befriending class is. */ + return !tmpl_attached_p; + + if (!tmpl_attached_p) + /* The befriending class is attached to a named module, so should we. */ + return false; + + module_state *me = (*modules)[0]; + module_state *them = me; + + if (DECL_LANG_SPECIFIC (inner) + && DECL_MODULE_IMPORT_P (inner)) + if (tree* slot = imported_temploid_friends->get (tmpl)) + me = import_entity_module (import_entity_index (*slot)); + + if (DECL_LANG_SPECIFIC (friend_inner) + && DECL_MODULE_IMPORT_P (friend_inner)) + them = import_entity_module (import_entity_index (friend_tmpl)); + + if (me == them) + return true; + + return me && get_primary (me) == get_primary (them); +} + /* DECL is being created by this TU. Record it came from here. We record module purview, so we can see if partial or explicit specialization needs to be written out, even though its purviewness @@ -18985,6 +19051,34 @@ maybe_key_decl (tree ctx, tree decl) vec.safe_push (decl); } +/* DECL is an instantiated friend that should be attached to the same + module that TMPL is. */ + +void +propagate_defining_module (tree decl, tree tmpl) +{ + if (!modules_p ()) + return; + + tree not_tmpl = DECL_TEMPLATE_RESULT (tmpl); + if (DECL_LANG_SPECIFIC (not_tmpl) && DECL_MODULE_ATTACH_P (not_tmpl)) + { + tree inner = DECL_TEMPLATE_RESULT (decl); + retrofit_lang_decl (inner); + DECL_MODULE_ATTACH_P (inner) = true; + } + + if (DECL_LANG_SPECIFIC (not_tmpl) && DECL_MODULE_IMPORT_P (not_tmpl)) + { + bool exists = imported_temploid_friends->put (decl, tmpl); + + /* We should only be called if lookup for an imported decl + failed in tsubst_friend_class, in which case there shouldn't + already be an entry in the map. */ + gcc_assert (!exists); + } +} + /* Create the flat name string. It is simplest to have it handy. */ void @@ -20200,6 +20294,8 @@ init_modules (cpp_reader *reader) vec_safe_reserve (entity_ary, EXPERIMENT (1, 400)); } + imported_temploid_friends = new hash_map (EXPERIMENT (1, 400)); + #if CHECKING_P note_defs = note_defs_table_t::create_ggc (1000); #endif diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc index dce4caf8981..4e79ee84aa2 100644 --- a/gcc/cp/name-lookup.cc +++ b/gcc/cp/name-lookup.cc @@ -4453,6 +4453,48 @@ push_local_binding (tree id, tree decl, bool is_using) add_decl_to_level (b, decl); } +/* Lookup the FRIEND_TMPL within all module imports. Used to dedup + instantiations of temploid hidden friends from imported modules. */ + +tree +lookup_imported_hidden_friend (tree friend_tmpl) +{ + tree inner = DECL_TEMPLATE_RESULT (friend_tmpl); + if (!DECL_LANG_SPECIFIC (inner) + || !DECL_MODULE_IMPORT_P (inner)) + return NULL_TREE; + + tree name = DECL_NAME (inner); + tree *slot = find_namespace_slot (current_namespace, name); + if (!slot || !*slot || TREE_CODE (*slot) != BINDING_VECTOR) + return NULL_TREE; + + /* Look in the appropriate slot, as with check_module_override. */ + binding_slot mslot; + if (named_module_p ()) + mslot = BINDING_VECTOR_CLUSTER (*slot, BINDING_SLOT_PARTITION + / BINDING_VECTOR_SLOTS_PER_CLUSTER) + .slots[BINDING_SLOT_PARTITION % BINDING_VECTOR_SLOTS_PER_CLUSTER]; + else + mslot = BINDING_VECTOR_CLUSTER (*slot, 0).slots[BINDING_SLOT_GLOBAL]; + gcc_assert (!mslot.is_lazy ()); + + tree ovl = mslot; + if (!ovl) + return NULL_TREE; + + /* We're only interested in declarations coming from the same module + of the friend class we're attempting to instantiate. */ + int m = get_originating_module (friend_tmpl); + gcc_assert (m != 0); + + for (ovl_iterator iter (ovl); iter; ++iter) + if (get_originating_module (*iter) == m) + return *iter; + + return NULL_TREE; +} + /* true means unconditionally make a BLOCK for the next level pushed. */ diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index 8cf0d5b7a8d..1e0d3efe5d2 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -11693,6 +11693,12 @@ tsubst_friend_class (tree friend_tmpl, tree args) tmpl = lookup_name (DECL_NAME (friend_tmpl), LOOK_where::CLASS_NAMESPACE, LOOK_want::NORMAL | LOOK_want::HIDDEN_FRIEND); + if (!tmpl) + /* If we didn't find by name lookup, the type may still exist but as a + 'hidden' import; we should check for this too to avoid accidentally + instantiating a duplicate. */ + tmpl = lookup_imported_hidden_friend (friend_tmpl); + if (tmpl && DECL_CLASS_TEMPLATE_P (tmpl)) { /* The friend template has already been declared. Just @@ -11701,6 +11707,16 @@ tsubst_friend_class (tree friend_tmpl, tree args) of course. We only need the innermost template parameters because that is all that redeclare_class_template will look at. */ + + if (modules_p () + && !module_may_redeclare_friend_class (tmpl, friend_tmpl)) + { + auto_diagnostic_group d; + error_at (DECL_SOURCE_LOCATION (friend_tmpl), + "cannot declare %qD in different module", tmpl); + inform (DECL_SOURCE_LOCATION (tmpl), "previously declared here"); + } + if (TMPL_PARMS_DEPTH (DECL_TEMPLATE_PARMS (friend_tmpl)) > TMPL_ARGS_DEPTH (args)) { @@ -11750,6 +11766,10 @@ tsubst_friend_class (tree friend_tmpl, tree args) args, tf_warning_or_error); } + /* We need to propagate the attachment of the original template to the + newly instantiated template type. */ + propagate_defining_module (tmpl, friend_tmpl); + /* Inject this template into the enclosing namspace scope. */ tmpl = pushdecl_namespace_level (tmpl, /*hiding=*/true); } diff --git a/gcc/testsuite/g++.dg/modules/tpl-friend-10.h b/gcc/testsuite/g++.dg/modules/tpl-friend-10.h new file mode 100644 index 00000000000..e69de29bb2d diff --git a/gcc/testsuite/g++.dg/modules/tpl-friend-10_a.C b/gcc/testsuite/g++.dg/modules/tpl-friend-10_a.C new file mode 100644 index 00000000000..7547326e554 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/tpl-friend-10_a.C @@ -0,0 +1,15 @@ +// PR c++/105320 +// { dg-additional-options "-fmodules-ts -Wno-global-module" } +// { dg-module-cmi test_support } + +module; +template struct _Sp_atomic; +template struct shared_ptr { + template friend struct _Sp_atomic; + using atomic_type = _Sp_atomic; +}; +export module test_support; +export +template struct A { + shared_ptr data; +}; diff --git a/gcc/testsuite/g++.dg/modules/tpl-friend-10_b.C b/gcc/testsuite/g++.dg/modules/tpl-friend-10_b.C new file mode 100644 index 00000000000..6b88ee4258b --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/tpl-friend-10_b.C @@ -0,0 +1,5 @@ +// PR c++/105320 +// { dg-additional-options "-fmodules-ts" } + +import test_support; +A a; diff --git a/gcc/testsuite/g++.dg/modules/tpl-friend-10_c.C b/gcc/testsuite/g++.dg/modules/tpl-friend-10_c.C new file mode 100644 index 00000000000..789bdeb64d5 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/tpl-friend-10_c.C @@ -0,0 +1,7 @@ +// PR c++/105320 +// { dg-additional-options "-fmodules-ts" } +// { dg-module-cmi user } + +export module user; +import test_support; +A b; diff --git a/gcc/testsuite/g++.dg/modules/tpl-friend-11_a.C b/gcc/testsuite/g++.dg/modules/tpl-friend-11_a.C new file mode 100644 index 00000000000..f29eebd1a7f --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/tpl-friend-11_a.C @@ -0,0 +1,14 @@ +// PR c++/114275 +// { dg-additional-options "-fmodules-ts -Wno-global-module" } +// { dg-module-cmi M } + +module; + +template struct T; + +template struct T { + template friend struct T; +}; + +export module M; +export template void fun() { T t; } diff --git a/gcc/testsuite/g++.dg/modules/tpl-friend-11_b.C b/gcc/testsuite/g++.dg/modules/tpl-friend-11_b.C new file mode 100644 index 00000000000..5bf79998139 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/tpl-friend-11_b.C @@ -0,0 +1,5 @@ +// PR c++/114275 +// { dg-additional-options "-fmodules-ts" } + +import M; +int main() { fun(); } diff --git a/gcc/testsuite/g++.dg/modules/tpl-friend-12_a.C b/gcc/testsuite/g++.dg/modules/tpl-friend-12_a.C new file mode 100644 index 00000000000..216dbf62c71 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/tpl-friend-12_a.C @@ -0,0 +1,10 @@ +// { dg-additional-options "-fmodules-ts" } +// { dg-module-cmi M:A } + +module M:A; + +template struct A { + template friend struct B; +private: + int x = 42; +}; diff --git a/gcc/testsuite/g++.dg/modules/tpl-friend-12_b.C b/gcc/testsuite/g++.dg/modules/tpl-friend-12_b.C new file mode 100644 index 00000000000..26e1c38b518 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/tpl-friend-12_b.C @@ -0,0 +1,9 @@ +// { dg-additional-options "-fmodules-ts" } +// { dg-module-cmi M:B } + +export module M:B; +import :A; + +export template struct B { + int foo(A a) { return a.x; } +}; diff --git a/gcc/testsuite/g++.dg/modules/tpl-friend-12_c.C b/gcc/testsuite/g++.dg/modules/tpl-friend-12_c.C new file mode 100644 index 00000000000..e44c2819cfd --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/tpl-friend-12_c.C @@ -0,0 +1,10 @@ +// { dg-additional-options "-fmodules-ts" } +// { dg-module-cmi M:C } + +export module M:C; +import :A; + +template struct B; +export template int bar(B t, U u) { + return t.foo(u); +} diff --git a/gcc/testsuite/g++.dg/modules/tpl-friend-12_d.C b/gcc/testsuite/g++.dg/modules/tpl-friend-12_d.C new file mode 100644 index 00000000000..9a575ad5046 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/tpl-friend-12_d.C @@ -0,0 +1,8 @@ +// { dg-additional-options "-fmodules-ts" } +// { dg-module-cmi M } + +export module M; +export import :B; +export import :C; + +export int go_in_module(); diff --git a/gcc/testsuite/g++.dg/modules/tpl-friend-12_e.C b/gcc/testsuite/g++.dg/modules/tpl-friend-12_e.C new file mode 100644 index 00000000000..329d1e8b263 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/tpl-friend-12_e.C @@ -0,0 +1,7 @@ +// { dg-additional-options "-fmodules-ts" } + +module M; + +int go_in_module() { + return bar(B{}, A{}); +} diff --git a/gcc/testsuite/g++.dg/modules/tpl-friend-12_f.C b/gcc/testsuite/g++.dg/modules/tpl-friend-12_f.C new file mode 100644 index 00000000000..c9855663fbd --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/tpl-friend-12_f.C @@ -0,0 +1,8 @@ +// { dg-additional-options "-fmodules-ts" } + +import M; + +int main() { + B b{}; + go_in_module(); +} diff --git a/gcc/testsuite/g++.dg/modules/tpl-friend-13_a.C b/gcc/testsuite/g++.dg/modules/tpl-friend-13_a.C new file mode 100644 index 00000000000..dda5c544564 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/tpl-friend-13_a.C @@ -0,0 +1,7 @@ +// { dg-additional-options "-fmodules-ts" } +// { dg-module-cmi M } + +export module M; +export template struct A { + template friend struct B; +}; diff --git a/gcc/testsuite/g++.dg/modules/tpl-friend-13_b.C b/gcc/testsuite/g++.dg/modules/tpl-friend-13_b.C new file mode 100644 index 00000000000..1fa5cc7b57b --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/tpl-friend-13_b.C @@ -0,0 +1,5 @@ +// { dg-additional-options "-fmodules-ts" } + +import M; +A a; +template struct B {}; // { dg-error "different module" } diff --git a/gcc/testsuite/g++.dg/modules/tpl-friend-13_c.C b/gcc/testsuite/g++.dg/modules/tpl-friend-13_c.C new file mode 100644 index 00000000000..1f293fe0785 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/tpl-friend-13_c.C @@ -0,0 +1,7 @@ +// { dg-additional-options "-fmodules-ts" } + +import M; +template struct B {}; // { dg-message "previously declared here" } +A a; // { dg-message "required from here" } + +// { dg-error "different module" "" { target *-*-* } 0 } diff --git a/gcc/testsuite/g++.dg/modules/tpl-friend-13_d.C b/gcc/testsuite/g++.dg/modules/tpl-friend-13_d.C new file mode 100644 index 00000000000..34eb3249186 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/tpl-friend-13_d.C @@ -0,0 +1,6 @@ +// { dg-additional-options "-fmodules-ts" } +// { dg-module-cmi O } + +export module O; +export import M; +A a; diff --git a/gcc/testsuite/g++.dg/modules/tpl-friend-13_e.C b/gcc/testsuite/g++.dg/modules/tpl-friend-13_e.C new file mode 100644 index 00000000000..6d50bc487b3 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/tpl-friend-13_e.C @@ -0,0 +1,8 @@ +// { dg-additional-options "-fmodules-ts" } + +template struct B {}; // { dg-message "previous declaration" } +import O; +A b; // { dg-message "required from here" } + +// specifically, B is defined in M, not O, despite the instantiation being in O +// { dg-error "conflicting declaration \[^\n\r\]* B@M" "" { target *-*-* } 0 } diff --git a/gcc/testsuite/g++.dg/modules/tpl-friend-9.C b/gcc/testsuite/g++.dg/modules/tpl-friend-9.C new file mode 100644 index 00000000000..487b44327b0 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/tpl-friend-9.C @@ -0,0 +1,11 @@ +// PR c++/114275 +// { dg-additional-options "-fmodules-ts" } +// { dg-module-cmi M } + +export module M; + +template struct A { + template friend struct B; +}; +A a; +template struct B { }; From patchwork Mon Apr 15 04:53:34 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nathaniel Shead X-Patchwork-Id: 88473 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id A1F743858D37 for ; Mon, 15 Apr 2024 04:54:10 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-pj1-x1034.google.com (mail-pj1-x1034.google.com [IPv6:2607:f8b0:4864:20::1034]) by sourceware.org (Postfix) with ESMTPS id 5BD9A3858D34 for ; Mon, 15 Apr 2024 04:53:41 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 5BD9A3858D34 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 5BD9A3858D34 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2607:f8b0:4864:20::1034 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1713156825; cv=none; b=iaPXSHJDG23Jo5rN9tMc4T9dblL6LMipP2J1WpPoQl7O696UxhsXaI8ZNBfjFkyPOjwQyUPvmJIi4oW8m1w+QwFEFT6aOgjvZHZhYt/vXIdtcltzjvODynZmwqUgVl9W8w4XE6pU3NHBpGiIx9ZwRnhJAecDLH1DaOhdf/Fp/fA= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1713156825; c=relaxed/simple; bh=z8wzHmQmUI0/vk5kpHW5bDvjC5jvXJmNz3x6B35v7eQ=; h=DKIM-Signature:Message-ID:Date:From:To:Subject:MIME-Version; b=cZ83Q6TMVFhLK71hA/gjJUyPwJjvPbKfqshQRevXNXvtzGVehvRfdHZh4Fccdevx3UkQc8er+TXZvdUW4vwOcjHhMmhFp/gkJFjDHFNBVuWf5fKLqe5NbU4RPF1SSf8RIzznc+U+xjI9FCOpOWN9hfveQjOFP7YsVzMxkt3br24= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-pj1-x1034.google.com with SMTP id 98e67ed59e1d1-2a28b11db68so1219829a91.2 for ; Sun, 14 Apr 2024 21:53:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1713156820; x=1713761620; darn=gcc.gnu.org; h=in-reply-to:content-disposition:mime-version:references:subject:cc :to:from:date:message-id:from:to:cc:subject:date:message-id:reply-to; bh=vn9JcC1h70pzgUwXgCRwt+52m8aUyA5zDvXA9vEVPFg=; b=a1bs8XJV+/NHRk5/bCbbAHvHONSt87CF2c69j4nfnNn56LcLFGXdUjmAcn5FfqTJsG UyMuDwN1/hXd+/w8FqtGKz41NUkBAxKC5x66XW5uB9aJneGNYG59d2DQK9DmgvLaBXV9 3E5LHYhT3yOb9krkEjOKicfLPKtPAzJmtwRZ4sPDql0ez4tGRtfSSbUFKQBtutmjPz8E lsNCrhTEeloXG6XQ1dgsXf2BcBDfAc573pafY4rP0iUPvTDVGIv8FXL4bwJkWOKrJpmV clL1owvyBC9IGSZvhP2tV+/+2BMHlT6HZGoF7C3OOPm9PCM32SWbf4UIPn5RKJDq5A+j xkgg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1713156820; x=1713761620; h=in-reply-to:content-disposition:mime-version:references:subject:cc :to:from:date:message-id:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=vn9JcC1h70pzgUwXgCRwt+52m8aUyA5zDvXA9vEVPFg=; b=lXgacTySyZpf8K/sPfVfjwG0nUFpkzHmCikxlB+8dVS74Dv/DuQKY4urz2OTust2uu u196ZIwhehbdjEhnwqFTHre/4BS8NO5k48NGRZQVeBPjLe6VObwj49F1vbjDXXQcEqO7 mtnNSwarvHgmHw0XB7gzQV/snjwFMDNhosTW9ODvcm5PKpoN9BQh6u9fCWddjEEuBBaN KKzj4kctgyf8huvHVqKa3sUJ2X14EmYq0dwD4aZtCRO6C9lIpQtTol9Zwxw4TzsfIqrF U5wuHwzwFjZkx1BEhVvbMVceoun9kE1W1vSMSUjMHrumvma1LtyJSZWFj00w2k1wUbuv xtiQ== X-Gm-Message-State: AOJu0YzvIYJszRn/Lly2gC3Q9Y0uW7oNqnVD59A2NrBFo5FDDWDZTCeg vHyj5i+5PnW37aBbBiJMvoWiPidBm7jlgJ9xxL2UFNdlqk6mZuiji/vcDg== X-Google-Smtp-Source: AGHT+IE+DRtTHZ0aMXyZGWwUZIL0AvDdeQWQSoC4oCUjmiIGXAwfPpWCDlEoCptiq+ONiXhFMNoT+w== X-Received: by 2002:a17:90a:fe86:b0:2a7:7457:3e9e with SMTP id co6-20020a17090afe8600b002a774573e9emr3058604pjb.32.1713156819975; Sun, 14 Apr 2024 21:53:39 -0700 (PDT) Received: from Thaum. (121-44-11-123.tpgi.com.au. [121.44.11.123]) by smtp.gmail.com with ESMTPSA id t17-20020a17090aae1100b002a575fcdbfesm6396193pjq.26.2024.04.14.21.53.37 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 14 Apr 2024 21:53:39 -0700 (PDT) Message-ID: <661cb2d3.170a0220.c9922.173d@mx.google.com> X-Google-Original-Message-ID: Date: Mon, 15 Apr 2024 14:53:34 +1000 From: Nathaniel Shead To: gcc-patches@gcc.gnu.org Cc: Jason Merrill , Nathan Sidwell , Patrick Palka Subject: [PATCH v2 2/2] c++/modules: Fix instantiation of imported temploid friends [PR114275] References: <66014130.170a0220.d7c40.0e9b@mx.google.com> MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: <66014130.170a0220.d7c40.0e9b@mx.google.com> X-Spam-Status: No, score=-11.1 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, URIBL_BLACK autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gcc-patches-bounces+patchwork=sourceware.org@gcc.gnu.org I'm not a huge fan of always streaming 'imported_temploid_friends' for all decls, but I don't think it adds much performance cost over adding a new flag to categorise decls that might be marked as such. Bootstrapped and regtested on x86_64-pc-linux-gnu, OK for trunk? -- >8 -- This patch fixes a number of issues with the handling of temploid friend declarations. The primary issue is that instantiations of friend declarations should attach the declaration to the same module as the befriending class, by [module.unit] p7.1 and [temp.friend] p2; this could be a different module from the current TU, and so needs special handling. The other main issue here is that we can't assume that just because name lookup didn't find a definition for a hidden template class, it doesn't mean that it doesn't exist: it could be a non-exported entity that we've nevertheless streamed in from an imported module. We need to ensure that when instantiating friend classes that we return the same TYPE_DECL that we got from our imports, otherwise we will get later issues with 'duplicate_decls' (rightfully) complaining that they're different. This doesn't appear necessary for functions due to the existing name lookup handling already finding these hidden declarations. PR c++/105320 PR c++/114275 gcc/cp/ChangeLog: * cp-tree.h (propagate_defining_module): Declare. (lookup_imported_hidden_friend): Declare. * decl.cc (duplicate_decls): Also check if hidden declarations can be redeclared in this module. * module.cc (imported_temploid_friends): New map. (init_modules): Initialize it. (trees_out::decl_value): Write it. (trees_in::decl_value): Read it. (get_originating_module_decl): Follow the owning decl for an imported temploid friend. (propagate_defining_module): New function. * name-lookup.cc (lookup_imported_hidden_friend): New function. * pt.cc (tsubst_friend_function): Propagate defining module for new friend functions. (tsubst_friend_class): Lookup imported hidden friends. Check for valid redeclaration. Propagate defining module for new friend classes. gcc/testsuite/ChangeLog: * g++.dg/modules/tpl-friend-10_a.C: New test. * g++.dg/modules/tpl-friend-10_b.C: New test. * g++.dg/modules/tpl-friend-10_c.C: New test. * g++.dg/modules/tpl-friend-11_a.C: New test. * g++.dg/modules/tpl-friend-11_b.C: New test. * g++.dg/modules/tpl-friend-12_a.C: New test. * g++.dg/modules/tpl-friend-12_b.C: New test. * g++.dg/modules/tpl-friend-12_c.C: New test. * g++.dg/modules/tpl-friend-12_d.C: New test. * g++.dg/modules/tpl-friend-12_e.C: New test. * g++.dg/modules/tpl-friend-12_f.C: New test. * g++.dg/modules/tpl-friend-13_a.C: New test. * g++.dg/modules/tpl-friend-13_b.C: New test. * g++.dg/modules/tpl-friend-13_c.C: New test. * g++.dg/modules/tpl-friend-13_d.C: New test. * g++.dg/modules/tpl-friend-13_e.C: New test. * g++.dg/modules/tpl-friend-9.C: New test. Signed-off-by: Nathaniel Shead --- gcc/cp/cp-tree.h | 2 + gcc/cp/decl.cc | 36 +++++++------ gcc/cp/module.cc | 52 +++++++++++++++++++ gcc/cp/name-lookup.cc | 42 +++++++++++++++ gcc/cp/pt.cc | 19 +++++++ .../g++.dg/modules/tpl-friend-10_a.C | 15 ++++++ .../g++.dg/modules/tpl-friend-10_b.C | 5 ++ .../g++.dg/modules/tpl-friend-10_c.C | 7 +++ .../g++.dg/modules/tpl-friend-11_a.C | 14 +++++ .../g++.dg/modules/tpl-friend-11_b.C | 5 ++ .../g++.dg/modules/tpl-friend-12_a.C | 10 ++++ .../g++.dg/modules/tpl-friend-12_b.C | 9 ++++ .../g++.dg/modules/tpl-friend-12_c.C | 10 ++++ .../g++.dg/modules/tpl-friend-12_d.C | 8 +++ .../g++.dg/modules/tpl-friend-12_e.C | 7 +++ .../g++.dg/modules/tpl-friend-12_f.C | 8 +++ .../g++.dg/modules/tpl-friend-13_a.C | 12 +++++ .../g++.dg/modules/tpl-friend-13_b.C | 9 ++++ .../g++.dg/modules/tpl-friend-13_c.C | 11 ++++ .../g++.dg/modules/tpl-friend-13_d.C | 7 +++ .../g++.dg/modules/tpl-friend-13_e.C | 14 +++++ gcc/testsuite/g++.dg/modules/tpl-friend-9.C | 13 +++++ 22 files changed, 299 insertions(+), 16 deletions(-) create mode 100644 gcc/testsuite/g++.dg/modules/tpl-friend-10_a.C create mode 100644 gcc/testsuite/g++.dg/modules/tpl-friend-10_b.C create mode 100644 gcc/testsuite/g++.dg/modules/tpl-friend-10_c.C create mode 100644 gcc/testsuite/g++.dg/modules/tpl-friend-11_a.C create mode 100644 gcc/testsuite/g++.dg/modules/tpl-friend-11_b.C create mode 100644 gcc/testsuite/g++.dg/modules/tpl-friend-12_a.C create mode 100644 gcc/testsuite/g++.dg/modules/tpl-friend-12_b.C create mode 100644 gcc/testsuite/g++.dg/modules/tpl-friend-12_c.C create mode 100644 gcc/testsuite/g++.dg/modules/tpl-friend-12_d.C create mode 100644 gcc/testsuite/g++.dg/modules/tpl-friend-12_e.C create mode 100644 gcc/testsuite/g++.dg/modules/tpl-friend-12_f.C create mode 100644 gcc/testsuite/g++.dg/modules/tpl-friend-13_a.C create mode 100644 gcc/testsuite/g++.dg/modules/tpl-friend-13_b.C create mode 100644 gcc/testsuite/g++.dg/modules/tpl-friend-13_c.C create mode 100644 gcc/testsuite/g++.dg/modules/tpl-friend-13_d.C create mode 100644 gcc/testsuite/g++.dg/modules/tpl-friend-13_e.C create mode 100644 gcc/testsuite/g++.dg/modules/tpl-friend-9.C diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index faa7a0052a5..67cc7d7bcec 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -7417,6 +7417,7 @@ extern unsigned get_importing_module (tree, bool = false) ATTRIBUTE_PURE; extern void set_instantiating_module (tree); extern void set_defining_module (tree); extern void maybe_key_decl (tree ctx, tree decl); +extern void propagate_defining_module (tree decl, tree orig); extern void mangle_module (int m, bool include_partition); extern void mangle_module_fini (); @@ -7649,6 +7650,7 @@ extern bool template_guide_p (const_tree); extern bool builtin_guide_p (const_tree); extern void store_explicit_specifier (tree, tree); extern tree lookup_explicit_specifier (tree); +extern tree lookup_imported_hidden_friend (tree); extern void walk_specializations (bool, void (*)(bool, spec_entry *, void *), diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc index aa66da4829d..ba8689efe21 100644 --- a/gcc/cp/decl.cc +++ b/gcc/cp/decl.cc @@ -2276,30 +2276,34 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden) if (modules_p () && TREE_CODE (CP_DECL_CONTEXT (olddecl)) == NAMESPACE_DECL - && TREE_CODE (olddecl) != NAMESPACE_DECL - && !hiding) + && TREE_CODE (olddecl) != NAMESPACE_DECL) { if (!module_may_redeclare (olddecl, newdecl)) return error_mark_node; - tree not_tmpl = STRIP_TEMPLATE (olddecl); - if (DECL_LANG_SPECIFIC (not_tmpl) - && DECL_MODULE_ATTACH_P (not_tmpl) - /* Typedefs are not entities and so are OK to be redeclared - as exported: see [module.interface]/p6. */ - && TREE_CODE (olddecl) != TYPE_DECL) + if (!hiding) { - if (DECL_MODULE_EXPORT_P (STRIP_TEMPLATE (newdecl)) - && !DECL_MODULE_EXPORT_P (not_tmpl)) + /* Hidden friends declarations just use exportingness of the + old declaration; see CWG2588. */ + tree not_tmpl = STRIP_TEMPLATE (olddecl); + if (DECL_LANG_SPECIFIC (not_tmpl) + && DECL_MODULE_ATTACH_P (not_tmpl) + /* Typedefs are not entities and so are OK to be redeclared + as exported: see [module.interface]/p6. */ + && TREE_CODE (olddecl) != TYPE_DECL) { - auto_diagnostic_group d; - error ("conflicting exporting for declaration %qD", newdecl); - inform (olddecl_loc, - "previously declared here without exporting"); + if (DECL_MODULE_EXPORT_P (STRIP_TEMPLATE (newdecl)) + && !DECL_MODULE_EXPORT_P (not_tmpl)) + { + auto_diagnostic_group d; + error ("conflicting exporting for declaration %qD", newdecl); + inform (olddecl_loc, + "previously declared here without exporting"); + } } + else if (DECL_MODULE_EXPORT_P (newdecl)) + DECL_MODULE_EXPORT_P (not_tmpl) = true; } - else if (DECL_MODULE_EXPORT_P (newdecl)) - DECL_MODULE_EXPORT_P (not_tmpl) = true; } /* We have committed to returning OLDDECL at this point. */ diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc index e2d2910ae48..1a064e4ea79 100644 --- a/gcc/cp/module.cc +++ b/gcc/cp/module.cc @@ -2727,6 +2727,11 @@ vec *post_load_decls; typedef hash_map> keyed_map_t; static keyed_map_t *keyed_table; +/* Instantiations of temploid friends imported from another module + need to be owned by the same module as their instantiating template. + This maps these to the template that instantiated them. */ +static hash_map *imported_temploid_friends; + /********************************************************************/ /* Tree streaming. The tree streaming is very specific to the tree structures themselves. A tag indicates the kind of tree being @@ -7997,6 +8002,11 @@ trees_out::decl_value (tree decl, depset *dep) } } + /* Write imported temploid friends so that importers can reconstruct + this information on stream-in. */ + tree* slot = imported_temploid_friends->get (decl); + tree_node (slot ? *slot : NULL_TREE); + bool is_typedef = false; if (!type && TREE_CODE (inner) == TYPE_DECL) { @@ -8303,6 +8313,12 @@ trees_in::decl_value () } } + if (tree owner = tree_node ()) + { + bool exists = imported_temploid_friends->put (decl, owner); + gcc_assert (exists == !is_new); + } + /* Regular typedefs will have a NULL TREE_TYPE at this point. */ unsigned tdef_flags = 0; bool is_typedef = false; @@ -18930,6 +18946,12 @@ get_originating_module_decl (tree decl) && DECL_UNINSTANTIATED_TEMPLATE_FRIEND_P (decl)) decl = TYPE_NAME (DECL_CHAIN (decl)); + /* An imported temploid friend is attached to the same module the + befriending class was. */ + if (imported_temploid_friends) + if (tree *slot = imported_temploid_friends->get (decl)) + decl = *slot; + int use; if (tree ti = node_template_info (decl, use)) { @@ -19238,6 +19260,34 @@ maybe_key_decl (tree ctx, tree decl) vec.safe_push (decl); } +/* DECL is an instantiated friend that should be attached to the same + module that ORIG is. */ + +void +propagate_defining_module (tree decl, tree orig) +{ + if (!modules_p ()) + return; + + tree not_tmpl = STRIP_TEMPLATE (orig); + if (DECL_LANG_SPECIFIC (not_tmpl) && DECL_MODULE_ATTACH_P (not_tmpl)) + { + tree inner = STRIP_TEMPLATE (decl); + retrofit_lang_decl (inner); + DECL_MODULE_ATTACH_P (inner) = true; + } + + if (DECL_LANG_SPECIFIC (not_tmpl) && DECL_MODULE_IMPORT_P (not_tmpl)) + { + bool exists = imported_temploid_friends->put (decl, orig); + + /* We should only be called if lookup for an existing decl + failed, in which case there shouldn't already be an entry + in the map. */ + gcc_assert (!exists); + } +} + /* Create the flat name string. It is simplest to have it handy. */ void @@ -20451,6 +20501,8 @@ init_modules (cpp_reader *reader) pending_table = new pending_map_t (EXPERIMENT (1, 400)); entity_map = new entity_map_t (EXPERIMENT (1, 400)); vec_safe_reserve (entity_ary, EXPERIMENT (1, 400)); + imported_temploid_friends + = new hash_map (EXPERIMENT (1, 400)); } #if CHECKING_P diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc index 7af7f00e34c..dd6e7b6eaea 100644 --- a/gcc/cp/name-lookup.cc +++ b/gcc/cp/name-lookup.cc @@ -4453,6 +4453,48 @@ push_local_binding (tree id, tree decl, bool is_using) add_decl_to_level (b, decl); } +/* Lookup the FRIEND_TMPL within all module imports. Used to dedup + instantiations of temploid hidden friends from imported modules. */ + +tree +lookup_imported_hidden_friend (tree friend_tmpl) +{ + tree inner = DECL_TEMPLATE_RESULT (friend_tmpl); + if (!DECL_LANG_SPECIFIC (inner) + || !DECL_MODULE_IMPORT_P (inner)) + return NULL_TREE; + + tree name = DECL_NAME (inner); + tree *slot = find_namespace_slot (current_namespace, name); + if (!slot || !*slot || TREE_CODE (*slot) != BINDING_VECTOR) + return NULL_TREE; + + /* Look in the appropriate slot, as with check_module_override. */ + binding_slot mslot; + if (named_module_p ()) + mslot = BINDING_VECTOR_CLUSTER (*slot, BINDING_SLOT_PARTITION + / BINDING_VECTOR_SLOTS_PER_CLUSTER) + .slots[BINDING_SLOT_PARTITION % BINDING_VECTOR_SLOTS_PER_CLUSTER]; + else + mslot = BINDING_VECTOR_CLUSTER (*slot, 0).slots[BINDING_SLOT_GLOBAL]; + gcc_assert (!mslot.is_lazy ()); + + tree ovl = mslot; + if (!ovl) + return NULL_TREE; + + /* We're only interested in declarations coming from the same module + of the friend class we're attempting to instantiate. */ + int m = get_originating_module (friend_tmpl); + gcc_assert (m != 0); + + for (ovl_iterator iter (ovl); iter; ++iter) + if (get_originating_module (*iter) == m) + return *iter; + + return NULL_TREE; +} + /* true means unconditionally make a BLOCK for the next level pushed. */ diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index 3b2106dd3f6..e7e7f2fbc3b 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -11512,6 +11512,10 @@ tsubst_friend_function (tree decl, tree args) new_friend_result_template_info = DECL_TEMPLATE_INFO (not_tmpl); } + /* We need to propagate module attachment for the new friend from the + owner of this template. */ + propagate_defining_module (new_friend, decl); + /* Inside pushdecl_namespace_level, we will push into the current namespace. However, the friend function should go into the namespace of the template. */ @@ -11715,6 +11719,12 @@ tsubst_friend_class (tree friend_tmpl, tree args) tmpl = lookup_name (DECL_NAME (friend_tmpl), LOOK_where::CLASS_NAMESPACE, LOOK_want::NORMAL | LOOK_want::HIDDEN_FRIEND); + if (!tmpl) + /* If we didn't find by name lookup, the type may still exist but as a + 'hidden' import; we should check for this too to avoid accidentally + instantiating a duplicate. */ + tmpl = lookup_imported_hidden_friend (friend_tmpl); + if (tmpl && DECL_CLASS_TEMPLATE_P (tmpl)) { /* The friend template has already been declared. Just @@ -11723,6 +11733,11 @@ tsubst_friend_class (tree friend_tmpl, tree args) of course. We only need the innermost template parameters because that is all that redeclare_class_template will look at. */ + + if (modules_p ()) + /* Check that we can redeclare TMPL in the current context. */ + module_may_redeclare (tmpl, friend_tmpl); + if (TMPL_PARMS_DEPTH (DECL_TEMPLATE_PARMS (friend_tmpl)) > TMPL_ARGS_DEPTH (args)) { @@ -11772,6 +11787,10 @@ tsubst_friend_class (tree friend_tmpl, tree args) args, tf_warning_or_error); } + /* We need to propagate the attachment of the original template to the + newly instantiated template type. */ + propagate_defining_module (tmpl, friend_tmpl); + /* Inject this template into the enclosing namspace scope. */ tmpl = pushdecl_namespace_level (tmpl, /*hiding=*/true); } diff --git a/gcc/testsuite/g++.dg/modules/tpl-friend-10_a.C b/gcc/testsuite/g++.dg/modules/tpl-friend-10_a.C new file mode 100644 index 00000000000..7547326e554 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/tpl-friend-10_a.C @@ -0,0 +1,15 @@ +// PR c++/105320 +// { dg-additional-options "-fmodules-ts -Wno-global-module" } +// { dg-module-cmi test_support } + +module; +template struct _Sp_atomic; +template struct shared_ptr { + template friend struct _Sp_atomic; + using atomic_type = _Sp_atomic; +}; +export module test_support; +export +template struct A { + shared_ptr data; +}; diff --git a/gcc/testsuite/g++.dg/modules/tpl-friend-10_b.C b/gcc/testsuite/g++.dg/modules/tpl-friend-10_b.C new file mode 100644 index 00000000000..6b88ee4258b --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/tpl-friend-10_b.C @@ -0,0 +1,5 @@ +// PR c++/105320 +// { dg-additional-options "-fmodules-ts" } + +import test_support; +A a; diff --git a/gcc/testsuite/g++.dg/modules/tpl-friend-10_c.C b/gcc/testsuite/g++.dg/modules/tpl-friend-10_c.C new file mode 100644 index 00000000000..789bdeb64d5 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/tpl-friend-10_c.C @@ -0,0 +1,7 @@ +// PR c++/105320 +// { dg-additional-options "-fmodules-ts" } +// { dg-module-cmi user } + +export module user; +import test_support; +A b; diff --git a/gcc/testsuite/g++.dg/modules/tpl-friend-11_a.C b/gcc/testsuite/g++.dg/modules/tpl-friend-11_a.C new file mode 100644 index 00000000000..f29eebd1a7f --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/tpl-friend-11_a.C @@ -0,0 +1,14 @@ +// PR c++/114275 +// { dg-additional-options "-fmodules-ts -Wno-global-module" } +// { dg-module-cmi M } + +module; + +template struct T; + +template struct T { + template friend struct T; +}; + +export module M; +export template void fun() { T t; } diff --git a/gcc/testsuite/g++.dg/modules/tpl-friend-11_b.C b/gcc/testsuite/g++.dg/modules/tpl-friend-11_b.C new file mode 100644 index 00000000000..5bf79998139 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/tpl-friend-11_b.C @@ -0,0 +1,5 @@ +// PR c++/114275 +// { dg-additional-options "-fmodules-ts" } + +import M; +int main() { fun(); } diff --git a/gcc/testsuite/g++.dg/modules/tpl-friend-12_a.C b/gcc/testsuite/g++.dg/modules/tpl-friend-12_a.C new file mode 100644 index 00000000000..216dbf62c71 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/tpl-friend-12_a.C @@ -0,0 +1,10 @@ +// { dg-additional-options "-fmodules-ts" } +// { dg-module-cmi M:A } + +module M:A; + +template struct A { + template friend struct B; +private: + int x = 42; +}; diff --git a/gcc/testsuite/g++.dg/modules/tpl-friend-12_b.C b/gcc/testsuite/g++.dg/modules/tpl-friend-12_b.C new file mode 100644 index 00000000000..26e1c38b518 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/tpl-friend-12_b.C @@ -0,0 +1,9 @@ +// { dg-additional-options "-fmodules-ts" } +// { dg-module-cmi M:B } + +export module M:B; +import :A; + +export template struct B { + int foo(A a) { return a.x; } +}; diff --git a/gcc/testsuite/g++.dg/modules/tpl-friend-12_c.C b/gcc/testsuite/g++.dg/modules/tpl-friend-12_c.C new file mode 100644 index 00000000000..e44c2819cfd --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/tpl-friend-12_c.C @@ -0,0 +1,10 @@ +// { dg-additional-options "-fmodules-ts" } +// { dg-module-cmi M:C } + +export module M:C; +import :A; + +template struct B; +export template int bar(B t, U u) { + return t.foo(u); +} diff --git a/gcc/testsuite/g++.dg/modules/tpl-friend-12_d.C b/gcc/testsuite/g++.dg/modules/tpl-friend-12_d.C new file mode 100644 index 00000000000..9a575ad5046 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/tpl-friend-12_d.C @@ -0,0 +1,8 @@ +// { dg-additional-options "-fmodules-ts" } +// { dg-module-cmi M } + +export module M; +export import :B; +export import :C; + +export int go_in_module(); diff --git a/gcc/testsuite/g++.dg/modules/tpl-friend-12_e.C b/gcc/testsuite/g++.dg/modules/tpl-friend-12_e.C new file mode 100644 index 00000000000..329d1e8b263 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/tpl-friend-12_e.C @@ -0,0 +1,7 @@ +// { dg-additional-options "-fmodules-ts" } + +module M; + +int go_in_module() { + return bar(B{}, A{}); +} diff --git a/gcc/testsuite/g++.dg/modules/tpl-friend-12_f.C b/gcc/testsuite/g++.dg/modules/tpl-friend-12_f.C new file mode 100644 index 00000000000..c9855663fbd --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/tpl-friend-12_f.C @@ -0,0 +1,8 @@ +// { dg-additional-options "-fmodules-ts" } + +import M; + +int main() { + B b{}; + go_in_module(); +} diff --git a/gcc/testsuite/g++.dg/modules/tpl-friend-13_a.C b/gcc/testsuite/g++.dg/modules/tpl-friend-13_a.C new file mode 100644 index 00000000000..a044485248f --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/tpl-friend-13_a.C @@ -0,0 +1,12 @@ +// { dg-additional-options "-fmodules-ts" } +// { dg-module-cmi M } + +export module M; +export template struct A { + template friend struct B; +}; + +export template struct C { + friend void f(); + template friend void g(); +}; diff --git a/gcc/testsuite/g++.dg/modules/tpl-friend-13_b.C b/gcc/testsuite/g++.dg/modules/tpl-friend-13_b.C new file mode 100644 index 00000000000..72dc8611e39 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/tpl-friend-13_b.C @@ -0,0 +1,9 @@ +// { dg-additional-options "-fmodules-ts" } + +import M; +A a; +template struct B {}; // { dg-error "conflicts with import" } + +C c; +void f() {} // { dg-error "conflicts with import" } +template void g() {} // { dg-error "conflicts with import" } diff --git a/gcc/testsuite/g++.dg/modules/tpl-friend-13_c.C b/gcc/testsuite/g++.dg/modules/tpl-friend-13_c.C new file mode 100644 index 00000000000..e1d2860bfe6 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/tpl-friend-13_c.C @@ -0,0 +1,11 @@ +// { dg-additional-options "-fmodules-ts" } + +import M; +template struct B {}; // { dg-message "previously declared" } +A a; // { dg-message "required from here" } + +void f() {} // { dg-message "previously declared" } +template void g(); // { dg-message "previously declared" } +C c; // { dg-message "required from here" } + +// { dg-error "conflicting declaration" "" { target *-*-* } 0 } diff --git a/gcc/testsuite/g++.dg/modules/tpl-friend-13_d.C b/gcc/testsuite/g++.dg/modules/tpl-friend-13_d.C new file mode 100644 index 00000000000..44b0f441db2 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/tpl-friend-13_d.C @@ -0,0 +1,7 @@ +// { dg-additional-options "-fmodules-ts" } +// { dg-module-cmi O } + +export module O; +export import M; +A a; +C c; diff --git a/gcc/testsuite/g++.dg/modules/tpl-friend-13_e.C b/gcc/testsuite/g++.dg/modules/tpl-friend-13_e.C new file mode 100644 index 00000000000..cec899e426b --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/tpl-friend-13_e.C @@ -0,0 +1,14 @@ +// { dg-additional-options "-fmodules-ts" } + +template struct B {}; // { dg-message "previous declaration" } +void f() {} +template void g() {} +import O; +A b; // { dg-message "required from here" } +C d; // { dg-message "required from here" } + +// specifically, B is defined in M, not O, despite the instantiation being in O +// { dg-error "conflicting declaration \[^\n\r\]* B@M" "" { target *-*-* } 0 } +// and similarly for f and g +// { dg-error "conflicting declaration \[^\n\r\]* f@M" "" { target *-*-* } 0 } +// { dg-error "conflicting declaration \[^\n\r\]* g@M" "" { target *-*-* } 0 } diff --git a/gcc/testsuite/g++.dg/modules/tpl-friend-9.C b/gcc/testsuite/g++.dg/modules/tpl-friend-9.C new file mode 100644 index 00000000000..c7216f0f8c1 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/tpl-friend-9.C @@ -0,0 +1,13 @@ +// PR c++/114275 +// { dg-additional-options "-fmodules-ts" } +// { dg-module-cmi M } + +export module M; + +template struct A { + template friend struct B; + friend void C(); +}; +A a; +void C() {} +template struct B { };