public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
From: Patrick Palka <ppalka@redhat.com>
To: Nathaniel Shead <nathanieloshead@gmail.com>
Cc: Jason Merrill <jason@redhat.com>,
	Patrick Palka <ppalka@redhat.com>,
	 gcc-patches@gcc.gnu.org, Nathan Sidwell <nathan@acm.org>
Subject: Re: [PATCH][14 backport] c++: Fix instantiation of imported temploid friends [PR114275]
Date: Thu, 23 May 2024 18:29:18 -0400 (EDT)	[thread overview]
Message-ID: <37c9f526-43b2-3fe9-2f20-a8a467c0bdeb@idea> (raw)
In-Reply-To: <66420006.170a0220.4a5f3.40ce@mx.google.com>

On Mon, 13 May 2024, Nathaniel Shead wrote:

> > > @@ -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
> > 
> 
> I'm looking to backport this patch to GCC 14 now that it's been on trunk
> some time.  Here's the patch I'm aiming to add (squashed with the
> changes from r15-220-gec2365e07537e8) after cherrypicking the
> prerequisite commit r15-58-g2faf040335f9b4; is this OK?
> 
> Or should I keep it as two separate commits to make the cherrypicking
> more obvious? Not entirely sure on the etiquette around this.

Since the first patch "only" causes sporadic testsuite failures (and
doesn't e.g. break bootstrap or anything serious like that), I reckon
it'd be fine to keep them as separate commits?  Not sure either.

> 
> Bootstrapped and regtested on x86_64-pc-linux-gnu on top of the
> releases/gcc-14 branch.
> 
> -- >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.
> 	(remove_defining_module): Declare.
> 	(lookup_imported_hidden_friend): Declare.
> 	* decl.cc (duplicate_decls): Also check if hidden decls can be
> 	redeclared in this module. Call remove_defining_module on
> 	to-be-freed newdecl.
> 	* 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 for non-discarded decls.
> 	(get_originating_module_decl): Follow the owning decl for an
> 	imported temploid friend.
> 	(propagate_defining_module): New.
> 	(remove_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>
> Reviewed-by: Jason Merrill <jason@redhat.com>
> Reviewed-by: Patrick Palka <ppalka@redhat.com>
> ---
>  gcc/cp/cp-tree.h                              |  3 +
>  gcc/cp/decl.cc                                | 41 ++++++----
>  gcc/cp/module.cc                              | 75 +++++++++++++++++++
>  gcc/cp/name-lookup.cc                         | 53 +++++++++++++
>  gcc/cp/pt.cc                                  | 32 +++++++-
>  .../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, 418 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 9975dc78456..0c14241fce7 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -7417,6 +7417,8 @@ 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 remove_defining_module (tree decl);
>  
>  extern void mangle_module (int m, bool include_partition);
>  extern void mangle_module_fini ();
> @@ -7650,6 +7652,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..8fd39957fdc 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.  */
> @@ -3321,6 +3326,10 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
>    if (flag_concepts)
>      remove_constraints (newdecl);
>  
> +  /* And similarly for any module tracking data.  */
> +  if (modules_p ())
> +    remove_defining_module (newdecl);
> +
>    ggc_free (newdecl);
>  
>    return olddecl;
> diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc
> index c2f077d6fd8..85c410aaa4c 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 GTY((cache)) decl_tree_cache_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
> @@ -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,12 @@ trees_in::decl_value ()
>  	}
>      }
>  
> +  if (TREE_CODE (inner) == FUNCTION_DECL
> +      || TREE_CODE (inner) == TYPE_DECL)
> +    if (tree owner = tree_node ())
> +      if (is_new)
> +	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;
> @@ -18941,6 +18968,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))
>  	{
> @@ -19249,6 +19282,46 @@ 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);
> +    }
> +}
> +
> +/* DECL is being freed, clear data we don't need anymore.  */
> +
> +void
> +remove_defining_module (tree decl)
> +{
> +  if (!modules_p ())
> +    return;
> +
> +  if (imported_temploid_friends)
> +    imported_temploid_friends->remove (decl);
> +}
> +
>  /* Create the flat name string.  It is simplest to have it handy.  */
>  
>  void
> @@ -20462,6 +20535,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
> +	= decl_tree_cache_map::create_ggc (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;
> +}
> +
>  \f
>  /* 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..ac7669a7ffb 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,19 @@ 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.  */
> +	  if (modules_p ())
> +	    {
> +	      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 +11798,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 { };
> -- 
> 2.43.2
> 
> 


  reply	other threads:[~2024-05-23 22:29 UTC|newest]

Thread overview: 20+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-03-25  9:17 [PATCH] c++/modules: " Nathaniel Shead
2024-04-15  4:49 ` [PATCH v2 1/2] c++: Standardise errors for module_may_redeclare Nathaniel Shead
2024-04-17 15:13   ` Patrick Palka
2024-04-19 16:18   ` Nathaniel Shead
2024-04-25 19:52     ` Jason Merrill
2024-04-15  4:53 ` [PATCH v2 2/2] c++/modules: Fix instantiation of imported temploid friends [PR114275] Nathaniel Shead
2024-04-17 18:02   ` Patrick Palka
2024-04-19  2:14     ` Nathaniel Shead
2024-04-19 16:29       ` [PATCH v3 2/2] c++: " Nathaniel Shead
2024-04-27  1:16         ` Jason Merrill
2024-04-29  9:34           ` [PATCH v4 " Nathaniel Shead
2024-04-29 22:37             ` Jason Merrill
2024-05-13 11:56               ` [PATCH][14 backport] " Nathaniel Shead
2024-05-23 22:29                 ` Patrick Palka [this message]
2024-05-23 22:41                 ` Jason Merrill
2024-05-24  8:06                   ` Nathaniel Shead
2024-05-24 13:54                     ` Jason Merrill
2024-05-24 14:21                       ` Nathaniel Shead
2024-05-24 14:40                       ` Iain Sandoe
2024-05-24 15:13                         ` Jason Merrill

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=37c9f526-43b2-3fe9-2f20-a8a467c0bdeb@idea \
    --to=ppalka@redhat.com \
    --cc=gcc-patches@gcc.gnu.org \
    --cc=jason@redhat.com \
    --cc=nathan@acm.org \
    --cc=nathanieloshead@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).