Message ID | 662f69c6.170a0220.d3cac.81f9@mx.google.com |
---|---|
State | New |
Headers | show |
Series | None | expand |
On 4/29/24 02:34, Nathaniel Shead wrote: > On Fri, Apr 26, 2024 at 09:16:40PM -0400, Jason Merrill wrote: >> On 4/19/24 09:29, Nathaniel Shead wrote: >>> On Fri, Apr 19, 2024 at 12:14:06PM +1000, Nathaniel Shead wrote: >>>> On Wed, Apr 17, 2024 at 02:02:21PM -0400, Patrick Palka wrote: >>>>> On Mon, 15 Apr 2024, Nathaniel Shead wrote: >>>>> >>>>>> 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. >>>>> >>>>> IIUC this value is going to be almost always null which is encoded as a >>>>> single 0 byte, which should be fast to stream. But I wonder how much >>>>> larger <bits/stdc++.h> gets? Can we get away with streaming this value >>>>> only for TEMPLATE_DECLs? >>>> >>>> Yes, it should either just be a 0 byte or an additional backref >>>> somewhere, which will likely also be small. On my system it increases >>>> the size by 0.26%, from 31186800 bytes to 31268672. >>>> >>>> But I've just found that this patch has a bug anyway, in that it doesn't >>>> correctly dedup if the friend types are instantiated in two separate >>>> modules that are then both imported. I'll see what I need to do to fix >>>> this which may influence what we need to stream here. >>>> >>> >>> Here's an updated version of the patch that fixes this. Also changed to >>> only stream when 'inner' is either TYPE_DECL or FUNCTION_DECL, which >>> cuts the size of <bits/stdc++.h> down a bit to 31246992 (0.19% growth). >>> >>> Another alternative would be to add another boolean flag at the top of >>> 'decl_value' and branch on that; that would make use of the bitpacking >>> logic and probably cut down on the size further. (I haven't measured >>> this yet though.) >>> >>> Bootstrapped and regtested (so far just dg.exp and modules.exp) on >>> x86_64-pc-linux-gnu, OK for trunk if full regtest succeeds? >>> >>> -- >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 is only an issue for DECL_UNINSTANTIATED_TEMPLATE_FRIEND_P, right? > > It's true for any friend instantiations, including e.g. friend > functions, not just template class friends. > >> >> Hmm, CWG2588 should probably touch [module.unit]/7.1 as well as >> [basic.link]. >> >>> 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. >> >> Tricksy. >> >>> 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; don't consider imported >>> temploid friends as attached to this module. >>> (trees_in::decl_value): Read it. >>> (depset::hash::add_specializations): Don't treat instantiations >>> of a friend type as a specialisation. >>> (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. >>> >>> diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc >>> index aa66da4829d..56752cf6872 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 friend declarations just use exportingness of the >>> + old declaration; see CWG2588. */ >> >> I'm not sure what this comment is trying to say, wouldn't hiding be true for >> a hidden friend? >> > > Right, it wasn't clear; the comment was meant to be explaining why we > don't do the following hunk for hidden friends, because for a hidden > friend we only care about the exportingness of OLDDECL (and NEWDECL > doesn't need to change). I've updated the comment to be clearer. > > I took a closer look at the code as well and it doesn't seem to exactly > match the rules given by [module.interface] anyway, but I think that > should be fixed as a separate patch. (In particular, this allows > redeclaring any GM entity as exported even if the initial declaration > was not, whereas [module.interface] p2 only allows this for names > declared within header units.) > >>> diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc >>> index 36e035544f4..bd3ab686543 100644 >>> --- a/gcc/cp/module.cc >>> +++ b/gcc/cp/module.cc >>> @@ -2727,6 +2727,11 @@ vec<tree, va_heap, vl_embed> *post_load_decls; >>> typedef hash_map<tree, auto_vec<tree>> 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<tree, tree> *imported_temploid_friends; >> >> So IIUC the way to express the attachment to an imported module is by >> association with an actually imported decl. >> >> And the problem is that there's no current way to get from the result of >> tsubst_friend_* to the original temploid friend; as tsubst_class_template >> says, "The new TMPL is not an instantiation of anything, so we forget its >> origins." And this map provides that lookup? >> >> Rather than "their instantiating template" and "the template that >> instantiated them" I'd just say "the temploid", maybe "the temploid they are >> instantiated from". >> >> I'd also change "owned" to "attached". >> > > Yup, right. I'll change the comment. > >>> @@ -13370,10 +13393,17 @@ depset::hash::add_specializations (bool decl_p) >>> int use_tpl = 0; >>> bool is_friend = false; >>> - if (decl_p && DECL_UNINSTANTIATED_TEMPLATE_FRIEND_P (entry->tmpl)) >>> - /* A friend of a template. This is keyed to the >>> - instantiation. */ >>> - is_friend = true; >>> + if (DECL_UNINSTANTIATED_TEMPLATE_FRIEND_P (entry->tmpl)) >>> + { >>> + if (decl_p) >>> + /* A friend of a template. This is keyed to the >>> + instantiation. */ >>> + is_friend = true; >>> + else >>> + /* An instantiated friend struct. Don't count this as >>> + a specialization, it'll be picked up later. */ >> >> You mean, in propagate_defining_module? >> >> Would it make sense to call propagate_defining_module here, rather than in >> tsubst_friend_class? >> > > No sorry, in 'add_namespace_entities'. The issue here is that even > though this isn't counted as a specialisation anymore, it's still kept > in 'type_specializations' as-if it was a dependent specialization of > the instantiating type. As such, e.g. for > > template <typename T> > struct A { > template <typename> friend struct B; > }; > > Instantiating A<int> in one module and A<double> in a different module > will emit two different 'specialization' merge keys with two sets of > template parameters, the outermost set being 'int' for the former and > 'double' for the latter, preventing deduplication. > > An alternative here I've tried would be to instead also remove the new > entry from 'type_specializations' in 'tsubst_friend_class' (given that > it shouldn't be used anywhere else either); this might be the neater > approach so I've done it below, but happy either way. > > Either way we need to do 'propagate_defining_module' earlier, because > this function only happens after parsing is complete but we need to > ensure that 'get_originating_module_decl' gives the correct values > before then. > >>> @@ -18930,6 +18960,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)) >>> { >> >> I note that just below this is >> >>> decl = TI_TEMPLATE (ti); >>> if (TREE_CODE (decl) != TEMPLATE_DECL) >>> { >>> /* A friend template specialization. */ >>> gcc_checking_assert (OVL_P (decl)); >>> return global_namespace; >>> } >> >> which seems to want to say "global module" for a friend template >> specialization, rather than the correct module. Is this code obsolete with >> this patch? >> > > It doesn't look like it; removing this regresses e.g. tpl-friend-6_a.C, > and seems to be handling friend specializations of existing > declarations, whereas the imported_temploid_friends logic is for > handling names first declared as friends. > >>> 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); >> >> Maybe checking_assert CP_DECL_CONTEXT (friend_tmpl) == current_namespace? >> > > Initially I hadn't because I thought that the context could possibly be > a class type. But if that was the case then I suppose that the original > lookup_name must succeed, so this assert should hold. > >>> + 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; >>> +} >> >> Maybe factor much of this out into a get_namespace_binding_in_module >> function? > > I've factored and clarified the slot-finding parts into a new > 'get_mergeable_namespace_binding' to correspond with > 'add_mergeable_namespace_entity'. Most of the rest I think relies on > implementation details specific to handling friend classes, I've added > some more comments too to expand on that. > >>> 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 >>> @@ -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); >> >> Isn't this redundant with the duplicate_decls change? >> > > No, because in this code path 'duplicate_decls' never actually gets > called: we just return the existing declaration directly. And this > check is needed for tpl-friend-13_[ce].C. Maybe we should be, but there > isn't really a corresponding 'olddecl' to compare with that I can see, > since friend_tmpl is just the template we're instantiating from rather > than a duplicate. > > I've updated the comment to hopefully make it a bit clearer what this > check is actually doing. > > Here's an updated version of the patch. 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 class template, that it > doesn't exist at all: it could be a non-exported entity that we've > nevertheless streamed in from an imported module. We need to ensure > that when instantiating template friend classes that we return the same > TEMPLATE_DECL that we got from our imports, otherwise we will get later > issues with 'duplicate_decls' (rightfully) complaining that they're > different when trying to merge. > > This doesn't appear necessary for function templates 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 decls can be > redeclared in this module. > * module.cc (imported_temploid_friends): New. > (init_modules): Initialize it. > (trees_out::decl_value): Write it; don't consider imported > temploid friends as attached to a module. > (trees_in::decl_value): Read it. > (get_originating_module_decl): Follow the owning decl for an > imported temploid friend. > (propagate_defining_module): New. > * name-lookup.cc (get_mergeable_namespace_binding): New. > (lookup_imported_hidden_friend): New. > * pt.cc (tsubst_friend_function): Propagate defining module for > new friend functions. > (tsubst_friend_class): Lookup imported hidden friends. Check > for valid module attachment of existing names. Propagate > defining module for new 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-10_d.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-13_f.C: New test. > * g++.dg/modules/tpl-friend-13_g.C: New test. > * g++.dg/modules/tpl-friend-14_a.C: New test. > * g++.dg/modules/tpl-friend-14_b.C: New test. > * g++.dg/modules/tpl-friend-14_c.C: New test. > * g++.dg/modules/tpl-friend-14_d.C: New test. > * g++.dg/modules/tpl-friend-9.C: New test. > > Signed-off-by: Nathaniel Shead <nathanieloshead@gmail.com> > --- > gcc/cp/cp-tree.h | 2 + > gcc/cp/decl.cc | 37 ++++++----- > gcc/cp/module.cc | 62 +++++++++++++++++++ > gcc/cp/name-lookup.cc | 53 ++++++++++++++++ > gcc/cp/pt.cc | 29 ++++++++- > .../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-10_d.C | 8 +++ > .../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 | 13 ++++ > .../g++.dg/modules/tpl-friend-13_b.C | 11 ++++ > .../g++.dg/modules/tpl-friend-13_c.C | 13 ++++ > .../g++.dg/modules/tpl-friend-13_d.C | 7 +++ > .../g++.dg/modules/tpl-friend-13_e.C | 18 ++++++ > .../g++.dg/modules/tpl-friend-13_f.C | 7 +++ > .../g++.dg/modules/tpl-friend-13_g.C | 11 ++++ > .../g++.dg/modules/tpl-friend-14_a.C | 8 +++ > .../g++.dg/modules/tpl-friend-14_b.C | 8 +++ > .../g++.dg/modules/tpl-friend-14_c.C | 7 +++ > .../g++.dg/modules/tpl-friend-14_d.C | 9 +++ > gcc/testsuite/g++.dg/modules/tpl-friend-9.C | 13 ++++ > 29 files changed, 397 insertions(+), 17 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-10_d.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-13_f.C > create mode 100644 gcc/testsuite/g++.dg/modules/tpl-friend-13_g.C > create mode 100644 gcc/testsuite/g++.dg/modules/tpl-friend-14_a.C > create mode 100644 gcc/testsuite/g++.dg/modules/tpl-friend-14_b.C > create mode 100644 gcc/testsuite/g++.dg/modules/tpl-friend-14_c.C > create mode 100644 gcc/testsuite/g++.dg/modules/tpl-friend-14_d.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 91268ff631d..d88e0698652 100644 > --- a/gcc/cp/decl.cc > +++ b/gcc/cp/decl.cc > @@ -2276,30 +2276,35 @@ 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)) > + /* The old declaration should match the exportingness of the new > + declaration. But hidden friend declarations just keep the > + 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 (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 0e2e60f85ae..c5141e5245b 100644 > --- a/gcc/cp/module.cc > +++ b/gcc/cp/module.cc > @@ -2727,6 +2727,12 @@ vec<tree, va_heap, vl_embed> *post_load_decls; > typedef hash_map<tree, auto_vec<tree>> keyed_map_t; > static keyed_map_t *keyed_table; > > +/* Instantiations of temploid friends imported from another module > + need to be attached to the same module as the temploid. This maps > + these decls to the temploid they are instantiated them, as there is > + no other easy way to get this information. */ > +static hash_map<tree, tree> *imported_temploid_friends; > + > /********************************************************************/ > /* Tree streaming. The tree streaming is very specific to the tree > structures themselves. A tag indicates the kind of tree being > @@ -7820,6 +7826,12 @@ trees_out::decl_value (tree decl, depset *dep) > && DECL_MODULE_ATTACH_P (not_tmpl)) > is_attached = true; > > + /* But don't consider imported temploid friends as attached, > + since importers will need to merge this decl even if it was > + attached to a different module. */ > + if (imported_temploid_friends->get (decl)) > + is_attached = false; > + > bits.b (is_attached); > } > bits.b (dep && dep->has_defn ()); > @@ -7997,6 +8009,15 @@ trees_out::decl_value (tree decl, depset *dep) > } > } > > + if (TREE_CODE (inner) == FUNCTION_DECL > + || TREE_CODE (inner) == TYPE_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) > { > @@ -8303,6 +8324,11 @@ trees_in::decl_value () > } > } > > + if (TREE_CODE (inner) == FUNCTION_DECL > + || TREE_CODE (inner) == TYPE_DECL) > + if (tree owner = tree_node ()) > + imported_temploid_friends->put (decl, owner); > + > /* Regular typedefs will have a NULL TREE_TYPE at this point. */ > unsigned tdef_flags = 0; > bool is_typedef = false; > @@ -18939,6 +18965,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)) > { > @@ -19247,6 +19279,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 > @@ -20460,6 +20520,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<tree,tree> (EXPERIMENT (1, 400)); > } > > #if CHECKING_P > diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc > index 7af7f00e34c..4dffc0e9acc 100644 > --- a/gcc/cp/name-lookup.cc > +++ b/gcc/cp/name-lookup.cc > @@ -4131,6 +4131,22 @@ mergeable_namespace_slots (tree ns, tree name, bool is_attached, tree *vec) > return vslot; > } > > +/* Retrieve the bindings for an existing mergeable entity in namespace > + NS slot NAME. Returns NULL if no such bindings exists. */ > + > +static tree > +get_mergeable_namespace_binding (tree ns, tree name, bool is_attached) > +{ > + tree *mslot = find_namespace_slot (ns, name, false); > + if (!mslot || !*mslot || TREE_CODE (*mslot) != BINDING_VECTOR) > + return NULL_TREE; > + > + tree *vslot = get_fixed_binding_slot > + (mslot, name, is_attached ? BINDING_SLOT_PARTITION : BINDING_SLOT_GLOBAL, > + false); > + return vslot ? *vslot : NULL_TREE; > +} > + > /* DECL is a new mergeable namespace-scope decl. Add it to the > mergeable entities on GSLOT. */ > > @@ -4453,6 +4469,43 @@ push_local_binding (tree id, tree decl, bool is_using) > add_decl_to_level (b, decl); > } > > +/* Lookup the FRIEND_TMPL within all merged module imports. Used to dedup > + instantiations of temploid hidden friends from imported modules. */ > + > +tree > +lookup_imported_hidden_friend (tree friend_tmpl) > +{ > + /* For a class-scope friend class it should have been found by regular > + name lookup. Otherwise we're looking in the current namespace. */ > + gcc_checking_assert (CP_DECL_CONTEXT (friend_tmpl) == current_namespace); > + > + tree inner = DECL_TEMPLATE_RESULT (friend_tmpl); > + if (!DECL_LANG_SPECIFIC (inner) > + || !DECL_MODULE_IMPORT_P (inner)) > + return NULL_TREE; > + > + /* Imported temploid friends are not considered as attached to this > + module for merging purposes. */ > + tree bind = get_mergeable_namespace_binding (current_namespace, > + DECL_NAME (inner), false); > + if (!bind) > + 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); > + > + /* There should be at most one class template from the module we're > + looking for, return it. */ > + for (ovl_iterator iter (bind); iter; ++iter) > + if (DECL_CLASS_TEMPLATE_P (*iter) > + && 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..1c3eef60c06 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,12 @@ 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 the existing declaration's module attachment is > + compatible with the attachment of the friend template. */ > + module_may_redeclare (tmpl, friend_tmpl); > + > if (TMPL_PARMS_DEPTH (DECL_TEMPLATE_PARMS (friend_tmpl)) > > TMPL_ARGS_DEPTH (args)) > { > @@ -11751,9 +11767,16 @@ tsubst_friend_class (tree friend_tmpl, tree args) > if (tmpl != error_mark_node) > { > /* The new TMPL is not an instantiation of anything, so we > - forget its origins. We don't reset CLASSTYPE_TI_TEMPLATE > + forget its origins. It is also not a specialization of > + anything. We don't reset CLASSTYPE_TI_TEMPLATE > for the new type because that is supposed to be the > corresponding template decl, i.e., TMPL. */ > + spec_entry elt; > + elt.tmpl = friend_tmpl; > + elt.args = CLASSTYPE_TI_ARGS (TREE_TYPE (tmpl)); > + elt.spec = TREE_TYPE (tmpl); > + type_specializations->remove_elt (&elt); For GCC 14.2 let's guard this with if (modules_p ()); for GCC 15 it can be unconditional. OK. Jason
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 91268ff631d..d88e0698652 100644 --- a/gcc/cp/decl.cc +++ b/gcc/cp/decl.cc @@ -2276,30 +2276,35 @@ 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)) + /* The old declaration should match the exportingness of the new + declaration. But hidden friend declarations just keep the + 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 (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 0e2e60f85ae..c5141e5245b 100644 --- a/gcc/cp/module.cc +++ b/gcc/cp/module.cc @@ -2727,6 +2727,12 @@ vec<tree, va_heap, vl_embed> *post_load_decls; typedef hash_map<tree, auto_vec<tree>> keyed_map_t; static keyed_map_t *keyed_table; +/* Instantiations of temploid friends imported from another module + need to be attached to the same module as the temploid. This maps + these decls to the temploid they are instantiated them, as there is + no other easy way to get this information. */ +static hash_map<tree, tree> *imported_temploid_friends; + /********************************************************************/ /* Tree streaming. The tree streaming is very specific to the tree structures themselves. A tag indicates the kind of tree being @@ -7820,6 +7826,12 @@ trees_out::decl_value (tree decl, depset *dep) && DECL_MODULE_ATTACH_P (not_tmpl)) is_attached = true; + /* But don't consider imported temploid friends as attached, + since importers will need to merge this decl even if it was + attached to a different module. */ + if (imported_temploid_friends->get (decl)) + is_attached = false; + bits.b (is_attached); } bits.b (dep && dep->has_defn ()); @@ -7997,6 +8009,15 @@ trees_out::decl_value (tree decl, depset *dep) } } + if (TREE_CODE (inner) == FUNCTION_DECL + || TREE_CODE (inner) == TYPE_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) { @@ -8303,6 +8324,11 @@ trees_in::decl_value () } } + if (TREE_CODE (inner) == FUNCTION_DECL + || TREE_CODE (inner) == TYPE_DECL) + if (tree owner = tree_node ()) + imported_temploid_friends->put (decl, owner); + /* Regular typedefs will have a NULL TREE_TYPE at this point. */ unsigned tdef_flags = 0; bool is_typedef = false; @@ -18939,6 +18965,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)) { @@ -19247,6 +19279,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 @@ -20460,6 +20520,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<tree,tree> (EXPERIMENT (1, 400)); } #if CHECKING_P diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc index 7af7f00e34c..4dffc0e9acc 100644 --- a/gcc/cp/name-lookup.cc +++ b/gcc/cp/name-lookup.cc @@ -4131,6 +4131,22 @@ mergeable_namespace_slots (tree ns, tree name, bool is_attached, tree *vec) return vslot; } +/* Retrieve the bindings for an existing mergeable entity in namespace + NS slot NAME. Returns NULL if no such bindings exists. */ + +static tree +get_mergeable_namespace_binding (tree ns, tree name, bool is_attached) +{ + tree *mslot = find_namespace_slot (ns, name, false); + if (!mslot || !*mslot || TREE_CODE (*mslot) != BINDING_VECTOR) + return NULL_TREE; + + tree *vslot = get_fixed_binding_slot + (mslot, name, is_attached ? BINDING_SLOT_PARTITION : BINDING_SLOT_GLOBAL, + false); + return vslot ? *vslot : NULL_TREE; +} + /* DECL is a new mergeable namespace-scope decl. Add it to the mergeable entities on GSLOT. */ @@ -4453,6 +4469,43 @@ push_local_binding (tree id, tree decl, bool is_using) add_decl_to_level (b, decl); } +/* Lookup the FRIEND_TMPL within all merged module imports. Used to dedup + instantiations of temploid hidden friends from imported modules. */ + +tree +lookup_imported_hidden_friend (tree friend_tmpl) +{ + /* For a class-scope friend class it should have been found by regular + name lookup. Otherwise we're looking in the current namespace. */ + gcc_checking_assert (CP_DECL_CONTEXT (friend_tmpl) == current_namespace); + + tree inner = DECL_TEMPLATE_RESULT (friend_tmpl); + if (!DECL_LANG_SPECIFIC (inner) + || !DECL_MODULE_IMPORT_P (inner)) + return NULL_TREE; + + /* Imported temploid friends are not considered as attached to this + module for merging purposes. */ + tree bind = get_mergeable_namespace_binding (current_namespace, + DECL_NAME (inner), false); + if (!bind) + 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); + + /* There should be at most one class template from the module we're + looking for, return it. */ + for (ovl_iterator iter (bind); iter; ++iter) + if (DECL_CLASS_TEMPLATE_P (*iter) + && 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..1c3eef60c06 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,12 @@ 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 the existing declaration's module attachment is + compatible with the attachment of the friend template. */ + module_may_redeclare (tmpl, friend_tmpl); + if (TMPL_PARMS_DEPTH (DECL_TEMPLATE_PARMS (friend_tmpl)) > TMPL_ARGS_DEPTH (args)) { @@ -11751,9 +11767,16 @@ tsubst_friend_class (tree friend_tmpl, tree args) if (tmpl != error_mark_node) { /* The new TMPL is not an instantiation of anything, so we - forget its origins. We don't reset CLASSTYPE_TI_TEMPLATE + forget its origins. It is also not a specialization of + anything. We don't reset CLASSTYPE_TI_TEMPLATE for the new type because that is supposed to be the corresponding template decl, i.e., TMPL. */ + spec_entry elt; + elt.tmpl = friend_tmpl; + elt.args = CLASSTYPE_TI_ARGS (TREE_TYPE (tmpl)); + elt.spec = TREE_TYPE (tmpl); + type_specializations->remove_elt (&elt); + DECL_USE_TEMPLATE (tmpl) = 0; DECL_TEMPLATE_INFO (tmpl) = NULL_TREE; CLASSTYPE_USE_TEMPLATE (TREE_TYPE (tmpl)) = 0; @@ -11772,6 +11795,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<class> struct _Sp_atomic; +template<class> struct shared_ptr { + template<class> friend struct _Sp_atomic; + using atomic_type = _Sp_atomic<int>; +}; +export module test_support; +export +template<class T> struct A { + shared_ptr<T> 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<int> 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..90bcd18a45e --- /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:part } + +export module user:part; +import test_support; +export A<int> b; diff --git a/gcc/testsuite/g++.dg/modules/tpl-friend-10_d.C b/gcc/testsuite/g++.dg/modules/tpl-friend-10_d.C new file mode 100644 index 00000000000..861d19c9eaa --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/tpl-friend-10_d.C @@ -0,0 +1,8 @@ +// PR c++/105320 +// { dg-additional-options "-fmodules-ts" } +// { dg-module-cmi user } + +export module user; +export import :part; +import test_support; +A<double> c; 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 <typename... _Elements> struct T; + +template <typename H> struct T<H> { + template <typename...> friend struct T; +}; + +export module M; +export template <typename=void> void fun() { T<int> 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 <typename T> struct A { + template <typename U> 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 <typename U> struct B { + int foo(A<U> 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 <typename T> struct B; +export template <typename T, typename U> int bar(B<T> 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<int>{}, A<int>{}); +} 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<double> 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..8c972776d60 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/tpl-friend-13_a.C @@ -0,0 +1,13 @@ +// { dg-additional-options "-fmodules-ts" } +// { dg-module-cmi M } + +export module M; +export template <typename> struct A { + friend struct S; + template <typename> friend struct T; +}; + +export template <typename> struct B { + friend void f(); + template <typename> 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..0e27e97b113 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/tpl-friend-13_b.C @@ -0,0 +1,11 @@ +// { dg-additional-options "-fmodules-ts" } + +import M; + +A<int> a; +struct S {}; // { dg-error "conflicts with import" } +template <typename> struct T {}; // { dg-error "conflicts with import" } + +B<int> c; +void f() {} // { dg-error "conflicts with import" } +template <typename> 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..3464aa26bf8 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/tpl-friend-13_c.C @@ -0,0 +1,13 @@ +// { dg-additional-options "-fmodules-ts" } + +import M; + +struct S {}; // { dg-error "conflicts with import" } +template <typename> struct T {}; // { dg-message "previously declared" } +A<int> a; // { dg-message "required from here" } + +void f() {} // { dg-message "previously declared" } +template <typename> void g() {} // { dg-message "previously declared" } +B<int> b; // { 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..5b935474ab2 --- /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 X } + +export module X; +export import M; +A<int> ax; +B<int> bx; 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..afbd0a39c23 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/tpl-friend-13_e.C @@ -0,0 +1,18 @@ +// { dg-additional-options "-fmodules-ts" } + +// 'import X' does not correctly notice that S has already been declared. +struct S {}; // { dg-message "previously declared" "" { xfail *-*-* } } +template <typename> struct T {}; // { dg-message "previously declared" } +void f() {} // { dg-message "previously declared" } +template <typename T> void g() {} // { dg-message "previously declared" } + +import X; +A<double> a2; // { dg-message "required from here" } +B<double> b2; // { dg-message "required from here" } + +// specifically, S and T are defined in M, not X, despite the instantiation being in X +// { dg-error "conflicting declaration \[^\n\r\]* S@M" "" { xfail *-*-* } 0 } +// { dg-error "conflicting declaration \[^\n\r\]* T@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-13_f.C b/gcc/testsuite/g++.dg/modules/tpl-friend-13_f.C new file mode 100644 index 00000000000..287f95c7bdc --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/tpl-friend-13_f.C @@ -0,0 +1,7 @@ +// { dg-additional-options "-fmodules-ts" } +// { dg-module-cmi Y } + +export module Y; +export import M; +A<double> ay; +B<double> by; diff --git a/gcc/testsuite/g++.dg/modules/tpl-friend-13_g.C b/gcc/testsuite/g++.dg/modules/tpl-friend-13_g.C new file mode 100644 index 00000000000..b7da60f2322 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/tpl-friend-13_g.C @@ -0,0 +1,11 @@ +// { dg-additional-options "-fmodules-ts" } + +import X; +import Y; + +// This should happily refer to the same S and T +// as already instantiated in both X and Y +A<long> az; + +// And same for f and g +B<long> bz; diff --git a/gcc/testsuite/g++.dg/modules/tpl-friend-14_a.C b/gcc/testsuite/g++.dg/modules/tpl-friend-14_a.C new file mode 100644 index 00000000000..6912512ecf7 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/tpl-friend-14_a.C @@ -0,0 +1,8 @@ +// { dg-additional-options "-fmodules-ts" } +// { dg-module-cmi M } + +export module M; + +export extern "C++" template <typename> struct A { + template <typename> friend struct B; +}; diff --git a/gcc/testsuite/g++.dg/modules/tpl-friend-14_b.C b/gcc/testsuite/g++.dg/modules/tpl-friend-14_b.C new file mode 100644 index 00000000000..5f8aa7fd62e --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/tpl-friend-14_b.C @@ -0,0 +1,8 @@ +// { dg-additional-options "-fmodules-ts" } +// { dg-module-cmi X } + +export module X; +export import M; + +A<int> x; +export extern "C++" template <typename T> struct B { using type = T; }; diff --git a/gcc/testsuite/g++.dg/modules/tpl-friend-14_c.C b/gcc/testsuite/g++.dg/modules/tpl-friend-14_c.C new file mode 100644 index 00000000000..8d89298878a --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/tpl-friend-14_c.C @@ -0,0 +1,7 @@ +// { dg-additional-options "-fmodules-ts" } +// { dg-module-cmi Y } + +export module Y; +export import M; + +A<double> x; diff --git a/gcc/testsuite/g++.dg/modules/tpl-friend-14_d.C b/gcc/testsuite/g++.dg/modules/tpl-friend-14_d.C new file mode 100644 index 00000000000..7a842586b62 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/tpl-friend-14_d.C @@ -0,0 +1,9 @@ +// { dg-additional-options "-fmodules-ts" } + +import X; +import Y; + +int main() { + A<long> a; + B<int>::type r = 10; +} 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<class> struct A { + template<class> friend struct B; + friend void C(); +}; +A<int> a; +void C() {} +template<class> struct B { };