On Wed, Aug 07, 2024 at 04:18:47PM -0400, Jason Merrill wrote:
> On 8/5/24 9:16 AM, Nathaniel Shead wrote:
> > Bootstrapped and regtested (so far just modules.exp) on
> > x86_64-pc-linux-gnu, OK for trunk if full regtest passes?
> 
> OK.
> 
> > -- >8 --
> > 
> > Currently name lookup generally seems to assume that all entities
> > declared within a named module (partition) are attached to said module,
> > which is not true for GM entities (e.g. via extern "C++"), and causes
> > issues with deduplication.
> > 
> > This patch fixes the issue by ensuring that module attachment of a
> > declaration is consistently used to handling merging.  Handling this
> > exposes some issues with deduplicating temploid friends; to resolve this
> > we always create the BINDING_SLOT_PARTITION slot so that we have
> > somewhere to place attached names (from any module).
> > 
> >     PR c++/114950
> > 
> > gcc/cp/ChangeLog:
> > 
> >     * module.cc (trees_out::decl_value): Stream bit indicating
> >     imported temploid friends early.
> >     (trees_in::decl_value): Use this bit with key_mergeable.
> >     (trees_in::key_mergeable): Allow merging attached declarations
> >     if they're imported temploid friends.
> >     (module_state::read_cluster): Check for GM entities that may
> >     require merging even when importing from partitions.
> >     * name-lookup.cc (enum binding_slots): Adjust comment.
> >     (get_fixed_binding_slot): Always create partition slot.
> >     (name_lookup::search_namespace_only): Support binding vectors
> >     with both partition and GM entities to dedup.
> >     (walk_module_binding): Likewise.
> >     (name_lookup::adl_namespace_fns): Likewise.
> >     (set_module_binding): Likewise.
> >     (check_module_override): Use attachment of the decl when
> >     checking overrides rather than named_module_p.
> >     (lookup_imported_hidden_friend): Use partition slot for finding
> >     mergeable template bindings.
> >     * name-lookup.h (set_module_binding): Split mod_glob_flag
> >     parameter into separate global_p and partition_p params.
> > 
> > gcc/testsuite/ChangeLog:
> > 
> >     * g++.dg/modules/tpl-friend-13_e.C: Adjust error message.
> >     * g++.dg/modules/ambig-2_a.C: New test.
> >     * g++.dg/modules/ambig-2_b.C: New test.
> >     * g++.dg/modules/part-9_a.C: New test.
> >     * g++.dg/modules/part-9_b.C: New test.
> >     * g++.dg/modules/part-9_c.C: New test.
> >     * g++.dg/modules/tpl-friend-15.h: New test.
> >     * g++.dg/modules/tpl-friend-15_a.C: New test.
> >     * g++.dg/modules/tpl-friend-15_b.C: New test.
> >     * g++.dg/modules/tpl-friend-15_c.C: New test.
> > 
> > Signed-off-by: Nathaniel Shead <nathanielosh...@gmail.com>
> > ---
> >   gcc/cp/module.cc                              | 55 ++++++++++------
> >   gcc/cp/name-lookup.cc                         | 65 ++++++++++---------
> >   gcc/cp/name-lookup.h                          |  2 +-
> >   gcc/testsuite/g++.dg/modules/ambig-2_a.C      |  7 ++
> >   gcc/testsuite/g++.dg/modules/ambig-2_b.C      | 10 +++
> >   gcc/testsuite/g++.dg/modules/part-9_a.C       | 10 +++
> >   gcc/testsuite/g++.dg/modules/part-9_b.C       | 10 +++
> >   gcc/testsuite/g++.dg/modules/part-9_c.C       |  8 +++
> >   .../g++.dg/modules/tpl-friend-13_e.C          |  4 +-
> >   gcc/testsuite/g++.dg/modules/tpl-friend-15.h  | 11 ++++
> >   .../g++.dg/modules/tpl-friend-15_a.C          |  8 +++
> >   .../g++.dg/modules/tpl-friend-15_b.C          |  8 +++
> >   .../g++.dg/modules/tpl-friend-15_c.C          |  7 ++
> >   13 files changed, 150 insertions(+), 55 deletions(-)
> >   create mode 100644 gcc/testsuite/g++.dg/modules/ambig-2_a.C
> >   create mode 100644 gcc/testsuite/g++.dg/modules/ambig-2_b.C
> >   create mode 100644 gcc/testsuite/g++.dg/modules/part-9_a.C
> >   create mode 100644 gcc/testsuite/g++.dg/modules/part-9_b.C
> >   create mode 100644 gcc/testsuite/g++.dg/modules/part-9_c.C
> >   create mode 100644 gcc/testsuite/g++.dg/modules/tpl-friend-15.h
> >   create mode 100644 gcc/testsuite/g++.dg/modules/tpl-friend-15_a.C
> >   create mode 100644 gcc/testsuite/g++.dg/modules/tpl-friend-15_b.C
> >   create mode 100644 gcc/testsuite/g++.dg/modules/tpl-friend-15_c.C
> > 
> > diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc
> > index d1607a06757..e6b569ebca5 100644
> > --- a/gcc/cp/module.cc
> > +++ b/gcc/cp/module.cc
> > @@ -2955,7 +2955,8 @@ private:
> >   public:
> >     tree decl_container ();
> >     tree key_mergeable (int tag, merge_kind, tree decl, tree inner, tree 
> > type,
> > -                 tree container, bool is_attached);
> > +                 tree container, bool is_attached,
> > +                 bool is_imported_temploid_friend);
> >     unsigned binfo_mergeable (tree *);
> >   private:
> > @@ -7803,6 +7804,7 @@ trees_out::decl_value (tree decl, depset *dep)
> >                    || !TYPE_PTRMEMFUNC_P (TREE_TYPE (decl)));
> >     merge_kind mk = get_merge_kind (decl, dep);
> > +  bool is_imported_temploid_friend = imported_temploid_friends->get (decl);
> >     if (CHECKING_P)
> >       {
> > @@ -7838,13 +7840,11 @@ 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);
> > +
> > +         /* Also tell the importer whether this is an imported temploid
> > +            friend, which has implications for merging.  */
> > +         bits.b (is_imported_temploid_friend);
> >         }
> >       bits.b (dep && dep->has_defn ());
> >     }
> > @@ -8021,13 +8021,12 @@ trees_out::decl_value (tree decl, depset *dep)
> >     }
> >       }
> > -  if (TREE_CODE (inner) == FUNCTION_DECL
> > -      || TREE_CODE (inner) == TYPE_DECL)
> > +  if (is_imported_temploid_friend)
> >       {
> >         /* 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);
> > +      tree_node (*slot);
> >       }
> >     bool is_typedef = false;
> > @@ -8106,6 +8105,7 @@ trees_in::decl_value ()
> >   {
> >     int tag = 0;
> >     bool is_attached = false;
> > +  bool is_imported_temploid_friend = false;
> >     bool has_defn = false;
> >     unsigned mk_u = u ();
> >     if (mk_u >= MK_hwm || !merge_kind_name[mk_u])
> > @@ -8126,7 +8126,10 @@ trees_in::decl_value ()
> >     {
> >       bits_in bits = stream_bits ();
> >       if (!(mk & MK_template_mask) && !state->is_header ())
> > -       is_attached = bits.b ();
> > +       {
> > +         is_attached = bits.b ();
> > +         is_imported_temploid_friend = bits.b ();
> > +       }
> >       has_defn = bits.b ();
> >     }
> > @@ -8231,7 +8234,7 @@ trees_in::decl_value ()
> >       parm_tag = fn_parms_init (inner);
> >     tree existing = key_mergeable (tag, mk, decl, inner, type, container,
> > -                            is_attached);
> > +                            is_attached, is_imported_temploid_friend);
> >     tree existing_inner = existing;
> >     if (existing)
> >       {
> > @@ -8336,8 +8339,7 @@ trees_in::decl_value ()
> >     }
> >       }
> > -  if (TREE_CODE (inner) == FUNCTION_DECL
> > -      || TREE_CODE (inner) == TYPE_DECL)
> > +  if (is_imported_temploid_friend)
> >       if (tree owner = tree_node ())
> >         if (is_new)
> >     imported_temploid_friends->put (decl, owner);
> > @@ -11174,7 +11176,8 @@ check_mergeable_decl (merge_kind mk, tree decl, 
> > tree ovl, merge_key const &key)
> >   tree
> >   trees_in::key_mergeable (int tag, merge_kind mk, tree decl, tree inner,
> > -                    tree type, tree container, bool is_attached)
> > +                    tree type, tree container, bool is_attached,
> > +                    bool is_imported_temploid_friend)
> >   {
> >     const char *kind = "new";
> >     tree existing = NULL_TREE;
> > @@ -11316,6 +11319,7 @@ trees_in::key_mergeable (int tag, merge_kind mk, 
> > tree decl, tree inner,
> >       case NAMESPACE_DECL:
> >         if (is_attached
> > +           && !is_imported_temploid_friend
> 
> How can a namespace be an imported temploid friend?
> 

Cut off by context, but this is

        switch (TREE_CODE (container))
          {
          default:
            gcc_unreachable ();

          case NAMESPACE_DECL:
            if (is_attached
                && !is_imported_temploid_friend
                && !(state->is_module () || state->is_partition ()))
              kind = "unique";

i.e. the NAMESPACE_DECL is referring to the container that the decl is
attached to for merging purposes.

> >             && !(state->is_module () || state->is_partition ()))
> >           kind = "unique";
> >         else
> > @@ -11347,7 +11351,9 @@ trees_in::key_mergeable (int tag, merge_kind mk, 
> > tree decl, tree inner,
> >         break;
> >       case TYPE_DECL:
> > -       if (is_attached && !(state->is_module () || state->is_partition ())
> > +       if (is_attached
> > +           && !is_imported_temploid_friend

This is the one that may perhaps be unnecessary (on thinking over this
again I would expect any class-scope friends to not be redeclared
outside of their named module, even for imported templates?), so I'll
actually re-test this patch without this hunk.

> > +           && !(state->is_module () || state->is_partition ())
> >             /* Implicit member functions can come from
> >                anywhere.  */
> >             && !(DECL_ARTIFICIAL (decl)
> > @@ -15291,6 +15297,7 @@ module_state::read_cluster (unsigned snum)
> >         tree visible = NULL_TREE;
> >         tree type = NULL_TREE;
> >         bool dedup = false;
> > +       bool global_p = false;
> >         /* We rely on the bindings being in the reverse order of
> >            the resulting overload set.  */
> > @@ -15308,6 +15315,16 @@ module_state::read_cluster (unsigned snum)
> >             if (sec.get_overrun ())
> >               break;
> > +           if (!global_p)
> > +             {
> > +               /* Check if the decl could require GM merging.  */
> > +               tree orig = get_originating_module_decl (decl);
> > +               tree inner = STRIP_TEMPLATE (orig);
> > +               if (!DECL_LANG_SPECIFIC (inner)
> > +                   || !DECL_MODULE_ATTACH_P (inner))
> > +                 global_p = true;
> > +             }
> > +
> >             if (decls && TREE_CODE (decl) == TYPE_DECL)
> >               {
> >                 /* Stat hack.  */
> > @@ -15394,10 +15411,8 @@ module_state::read_cluster (unsigned snum)
> >           break; /* Bail.  */
> >         dump () && dump ("Binding of %P", ns, name);
> > -       if (!set_module_binding (ns, name, mod,
> > -                                is_header () ? -1
> > -                                : is_module () || is_partition () ? 1
> > -                                : 0,
> > +       if (!set_module_binding (ns, name, mod, global_p,
> > +                                is_module () || is_partition (),
> >                                  decls, type, visible))
> >           sec.set_overrun ();
> >       }
> > diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc
> > index 8823ab71c60..872f1af0b2e 100644
> > --- a/gcc/cp/name-lookup.cc
> > +++ b/gcc/cp/name-lookup.cc
> > @@ -51,8 +51,8 @@ enum binding_slots
> >   {
> >    BINDING_SLOT_CURRENT,    /* Slot for current TU.  */
> >    BINDING_SLOT_GLOBAL,     /* Slot for merged global module. */
> > - BINDING_SLOT_PARTITION, /* Slot for merged partition entities
> > -                       (optional).  */
> > + BINDING_SLOT_PARTITION, /* Slot for merged partition entities or
> > +                       imported friends.  */
> >    /* Number of always-allocated slots.  */
> >    BINDING_SLOTS_FIXED = BINDING_SLOT_GLOBAL + 1
> > @@ -248,9 +248,10 @@ get_fixed_binding_slot (tree *slot, tree name, 
> > unsigned ix, int create)
> >         if (!create)
> >     return NULL;
> > -      /* The partition slot is only needed when we're a named
> > -    module.  */
> > -      bool partition_slot = named_module_p ();
> > +      /* The partition slot is always needed, in case we have imported
> > +    temploid friends with attachment different from the module we
> > +    imported them from.  */
> > +      bool partition_slot = true;
> >         unsigned want = ((BINDING_SLOTS_FIXED + partition_slot + (create < 
> > 0)
> >                     + BINDING_VECTOR_SLOTS_PER_CLUSTER - 1)
> >                    / BINDING_VECTOR_SLOTS_PER_CLUSTER);
> > @@ -937,7 +938,6 @@ name_lookup::search_namespace_only (tree scope)
> >                stat_hack, then everything was exported.  */
> >             tree type = NULL_TREE;
> > -
> >             /* If STAT_HACK_P is false, everything is visible, and
> >                there's no duplication possibilities.  */
> >             if (STAT_HACK_P (bind))
> > @@ -947,9 +947,9 @@ name_lookup::search_namespace_only (tree scope)
> >                     /* Do we need to engage deduplication?  */
> >                     int dup = 0;
> >                     if (MODULE_BINDING_GLOBAL_P (bind))
> > -                     dup = 1;
> > -                   else if (MODULE_BINDING_PARTITION_P (bind))
> > -                     dup = 2;
> > +                     dup |= 1;
> > +                   if (MODULE_BINDING_PARTITION_P (bind))
> > +                     dup |= 2;
> >                     if (unsigned hit = dup_detect & dup)
> >                       {
> >                         if ((hit & 1 && BINDING_VECTOR_GLOBAL_DUPS_P (val))
> > @@ -1275,9 +1275,9 @@ name_lookup::adl_namespace_fns (tree scope, bitmap 
> > imports)
> >                     /* Do we need to engage deduplication?  */
> >                     int dup = 0;
> >                     if (MODULE_BINDING_GLOBAL_P (bind))
> > -                     dup = 1;
> > -                   else if (MODULE_BINDING_PARTITION_P (bind))
> > -                     dup = 2;
> > +                     dup |= 1;
> > +                   if (MODULE_BINDING_PARTITION_P (bind))
> > +                     dup |= 2;
> >                     if (unsigned hit = dup_detect & dup)
> >                       if ((hit & 1 && BINDING_VECTOR_GLOBAL_DUPS_P (val))
> >                           || (hit & 2
> > @@ -3758,6 +3758,9 @@ check_module_override (tree decl, tree mvec, bool 
> > hiding,
> >     binding_cluster *cluster = BINDING_VECTOR_CLUSTER_BASE (mvec);
> >     unsigned ix = BINDING_VECTOR_NUM_CLUSTERS (mvec);
> > +  tree nontmpl = STRIP_TEMPLATE (decl);
> > +  bool attached = DECL_LANG_SPECIFIC (nontmpl) && DECL_MODULE_ATTACH_P 
> > (nontmpl);
> > +
> >     if (BINDING_VECTOR_SLOTS_PER_CLUSTER == BINDING_SLOTS_FIXED)
> >       {
> >         cluster++;
> > @@ -3819,7 +3822,7 @@ check_module_override (tree decl, tree mvec, bool 
> > hiding,
> >       {
> >         /* Look in the appropriate mergeable decl slot.  */
> >         tree mergeable = NULL_TREE;
> > -      if (named_module_p ())
> > +      if (attached)
> >     mergeable = BINDING_VECTOR_CLUSTER (mvec, BINDING_SLOT_PARTITION
> >                                        / BINDING_VECTOR_SLOTS_PER_CLUSTER)
> >       .slots[BINDING_SLOT_PARTITION % BINDING_VECTOR_SLOTS_PER_CLUSTER];
> > @@ -3839,15 +3842,13 @@ check_module_override (tree decl, tree mvec, bool 
> > hiding,
> >    matched:
> >     if (match != error_mark_node)
> >       {
> > -      if (named_module_p ())
> > +      if (attached)
> >     BINDING_VECTOR_PARTITION_DUPS_P (mvec) = true;
> >         else
> >     BINDING_VECTOR_GLOBAL_DUPS_P (mvec) = true;
> >       }
> >     return match;
> > -
> > -
> >   }
> >   /* Record DECL as belonging to the current lexical scope.  Check for
> > @@ -4300,7 +4301,9 @@ walk_module_binding (tree binding, bitmap partitions,
> >       cluster++;
> >     }
> > -      bool maybe_dups = BINDING_VECTOR_PARTITION_DUPS_P (binding);
> > +      /* There could be duplicate module or GMF entries.  */
> > +      bool maybe_dups = (BINDING_VECTOR_PARTITION_DUPS_P (binding)
> > +                    || BINDING_VECTOR_GLOBAL_DUPS_P (binding));
> >         for (; ix--; cluster++)
> >     for (unsigned jx = 0; jx != BINDING_VECTOR_SLOTS_PER_CLUSTER; jx++)
> > @@ -4394,14 +4397,14 @@ import_module_binding  (tree ns, tree name, 
> > unsigned mod, unsigned snum)
> >   }
> >   /* An import of MODULE is binding NS::NAME.  There should be no
> > -   existing binding for >= MODULE.  MOD_GLOB indicates whether MODULE
> > -   is a header_unit (-1) or part of the current module (+1).  VALUE
> > -   and TYPE are the value and type bindings. VISIBLE are the value
> > -   bindings being exported.  */
> > +   existing binding for >= MODULE.  GLOBAL_P indicates whether the
> > +   bindings include global module entities.  PARTITION_P is true if
> > +   it is part of the current module. VALUE and TYPE are the value
> > +   and type bindings. VISIBLE are the value bindings being exported.  */
> >   bool
> > -set_module_binding (tree ns, tree name, unsigned mod, int mod_glob,
> > -               tree value, tree type, tree visible)
> > +set_module_binding (tree ns, tree name, unsigned mod, bool global_p,
> > +               bool partition_p, tree value, tree type, tree visible)
> >   {
> >     if (!value)
> >       /* Bogus BMIs could give rise to nothing to bind.  */
> > @@ -4419,19 +4422,19 @@ set_module_binding (tree ns, tree name, unsigned 
> > mod, int mod_glob,
> >       return false;
> >     tree bind = value;
> > -  if (type || visible != bind || mod_glob)
> > +  if (type || visible != bind || partition_p || global_p)
> >       {
> >         bind = stat_hack (bind, type);
> >         STAT_VISIBLE (bind) = visible;
> > -      if ((mod_glob > 0 && TREE_PUBLIC (ns))
> > +      if ((partition_p && TREE_PUBLIC (ns))
> >       || (type && DECL_MODULE_EXPORT_P (type)))
> >     STAT_TYPE_VISIBLE_P (bind) = true;
> >       }
> > -  /* Note if this is this-module or global binding.  */
> > -  if (mod_glob > 0)
> > +  /* Note if this is this-module and/or global binding.  */
> > +  if (partition_p)
> >       MODULE_BINDING_PARTITION_P (bind) = true;
> > -  else if (mod_glob < 0)
> > +  if (global_p)
> >       MODULE_BINDING_GLOBAL_P (bind) = true;
> >     *mslot = bind;
> > @@ -4540,10 +4543,8 @@ lookup_imported_hidden_friend (tree friend_tmpl)
> >         || !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);
> > +  tree bind = get_mergeable_namespace_binding
> > +    (current_namespace, DECL_NAME (inner), DECL_MODULE_ATTACH_P (inner));
> >     if (!bind)
> >       return NULL_TREE;
> > diff --git a/gcc/cp/name-lookup.h b/gcc/cp/name-lookup.h
> > index 5cf6ae6374a..7c4193444dd 100644
> > --- a/gcc/cp/name-lookup.h
> > +++ b/gcc/cp/name-lookup.h
> > @@ -484,7 +484,7 @@ extern tree lookup_class_binding (tree ctx, tree name);
> >   extern bool import_module_binding (tree ctx, tree name, unsigned mod,
> >                                unsigned snum);
> >   extern bool set_module_binding (tree ctx, tree name, unsigned mod,
> > -                           int mod_glob_flag,
> > +                           bool global_p, bool partition_p,
> >                             tree value, tree type, tree visible);
> >   extern void add_module_namespace_decl (tree ns, tree decl);
> > diff --git a/gcc/testsuite/g++.dg/modules/ambig-2_a.C 
> > b/gcc/testsuite/g++.dg/modules/ambig-2_a.C
> > new file mode 100644
> > index 00000000000..d5dcc93584a
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/modules/ambig-2_a.C
> > @@ -0,0 +1,7 @@
> > +// { dg-additional-options "-fmodules-ts" }
> > +// { dg-module-cmi A }
> > +
> > +export module A;
> > +
> > +extern "C++" int foo ();
> > +extern "C++" char bar ();
> > diff --git a/gcc/testsuite/g++.dg/modules/ambig-2_b.C 
> > b/gcc/testsuite/g++.dg/modules/ambig-2_b.C
> > new file mode 100644
> > index 00000000000..b94416aabbf
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/modules/ambig-2_b.C
> > @@ -0,0 +1,10 @@
> > +// { dg-additional-options "-fmodules-ts" }
> > +// { dg-module-cmi !B }
> > +
> > +export module B;
> > +import A;
> > +
> > +extern "C++" int foo ();
> > +extern "C++" int bar ();  // { dg-error "ambiguating new declaration" }
> > +
> > +// { dg-prune-output "not writing module" }
> > diff --git a/gcc/testsuite/g++.dg/modules/part-9_a.C 
> > b/gcc/testsuite/g++.dg/modules/part-9_a.C
> > new file mode 100644
> > index 00000000000..dc033d301ee
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/modules/part-9_a.C
> > @@ -0,0 +1,10 @@
> > +// PR c++/114950
> > +// { dg-additional-options "-fmodules-ts" }
> > +// { dg-module-cmi M:a }
> > +
> > +module M:a;
> > +
> > +struct A {};
> > +extern "C++" struct B {};
> > +void f(int) {}
> > +extern "C++" void f(double) {}
> > diff --git a/gcc/testsuite/g++.dg/modules/part-9_b.C 
> > b/gcc/testsuite/g++.dg/modules/part-9_b.C
> > new file mode 100644
> > index 00000000000..7339da22d97
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/modules/part-9_b.C
> > @@ -0,0 +1,10 @@
> > +// PR c++/114950
> > +// { dg-additional-options "-fmodules-ts" }
> > +// { dg-module-cmi M:b }
> > +
> > +module M:b;
> > +
> > +struct A {};
> > +extern "C++" struct B {};
> > +void f(int) {}
> > +extern "C++" void f(double) {}
> > diff --git a/gcc/testsuite/g++.dg/modules/part-9_c.C 
> > b/gcc/testsuite/g++.dg/modules/part-9_c.C
> > new file mode 100644
> > index 00000000000..26ac6777b7a
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/modules/part-9_c.C
> > @@ -0,0 +1,8 @@
> > +// PR c++/114950
> > +// { dg-additional-options "-fmodules-ts" }
> > +// { dg-module-cmi M }
> > +// Handle merging definitions of extern "C++" decls across partitions
> > +
> > +export module M;
> > +import :a;
> > +import :b;
> > diff --git a/gcc/testsuite/g++.dg/modules/tpl-friend-13_e.C 
> > b/gcc/testsuite/g++.dg/modules/tpl-friend-13_e.C
> > index afbd0a39c23..b32fd98b756 100644
> > --- a/gcc/testsuite/g++.dg/modules/tpl-friend-13_e.C
> > +++ b/gcc/testsuite/g++.dg/modules/tpl-friend-13_e.C
> > @@ -1,8 +1,8 @@
> >   // { 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" }
> > +struct S {};  // { dg-message "previous declaration" "" { xfail *-*-* } }
> > +template <typename> struct T {};  // { dg-message "previous declaration" }
> >   void f() {}  // { dg-message "previously declared" }
> >   template <typename T> void g() {}  // { dg-message "previously declared" }
> > diff --git a/gcc/testsuite/g++.dg/modules/tpl-friend-15.h 
> > b/gcc/testsuite/g++.dg/modules/tpl-friend-15.h
> > new file mode 100644
> > index 00000000000..e4d3fff4445
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/modules/tpl-friend-15.h
> > @@ -0,0 +1,11 @@
> > +// PR c++/114950
> > +
> > +template <typename T>
> > +struct A {
> > +  friend void x();
> > +};
> > +template <typename T>
> > +struct B {
> > +  virtual void f() { A<T> r; }
> > +};
> > +template struct B<int>;
> > diff --git a/gcc/testsuite/g++.dg/modules/tpl-friend-15_a.C 
> > b/gcc/testsuite/g++.dg/modules/tpl-friend-15_a.C
> > new file mode 100644
> > index 00000000000..04c800875f4
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/modules/tpl-friend-15_a.C
> > @@ -0,0 +1,8 @@
> > +// PR c++/114950
> > +// { dg-additional-options "-fmodules-ts" }
> > +// { dg-module-cmi M:a }
> > +
> > +module M:a;
> > +extern "C++" {
> > +  #include "tpl-friend-15.h"
> > +}
> > diff --git a/gcc/testsuite/g++.dg/modules/tpl-friend-15_b.C 
> > b/gcc/testsuite/g++.dg/modules/tpl-friend-15_b.C
> > new file mode 100644
> > index 00000000000..781882f97bc
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/modules/tpl-friend-15_b.C
> > @@ -0,0 +1,8 @@
> > +// PR c++/114950
> > +// { dg-additional-options "-fmodules-ts" }
> > +// { dg-module-cmi M:b }
> > +
> > +module M:b;
> > +extern "C++" {
> > +  #include "tpl-friend-15.h"
> > +}
> > diff --git a/gcc/testsuite/g++.dg/modules/tpl-friend-15_c.C 
> > b/gcc/testsuite/g++.dg/modules/tpl-friend-15_c.C
> > new file mode 100644
> > index 00000000000..ced7e87d993
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/modules/tpl-friend-15_c.C
> > @@ -0,0 +1,7 @@
> > +// PR c++/114950
> > +// { dg-additional-options "-fmodules-ts" }
> > +// { dg-module-cmi M }
> > +
> > +export module M;
> > +import :a;
> > +import :b;
> 

Reply via email to