https://gcc.gnu.org/g:5fbcd8a57a0f2524259df87db5c6c82b757bf2e6

commit r16-5213-g5fbcd8a57a0f2524259df87db5c6c82b757bf2e6
Author: Nathaniel Shead <[email protected]>
Date:   Tue Nov 11 17:13:46 2025 +1100

    c++/modules: Maintain attachment of temploid friends after duplicate_decls 
[PR122551]
    
    The ICE in the PR is because we're attempting to create a binding for an
    imported declaration.  This is problematic because if there are
    duplicates we'll stream via a tt_entity, but won't enable deduplication
    on the relevant binding vectors which can cause issues.
    
    The root cause seems to stem from us forgetting that we've produced a
    declaration for this entity within our own module, and so the active
    declaration is not purely from an imported entity.  We also didn't
    properly track that this entity has unusual module attachment and
    despite being declared here without being an instantiation actually is
    attached to a different module than the current one (which may have
    caused other problems down the line).  This patch fixes both of these
    issues.
    
            PR c++/122551
    
    gcc/cp/ChangeLog:
    
            * cp-tree.h (transfer_defining_module): Declare.
            * decl.cc (duplicate_decls): Call it for all decls.
            Remove now unnecessary equivalent logic for templates.
            * module.cc (mangle_module): Add assertion.
            (transfer_defining_module): New function.
    
    gcc/testsuite/ChangeLog:
    
            * g++.dg/modules/tpl-friend-20_a.C: New test.
            * g++.dg/modules/tpl-friend-20_b.C: New test.
            * g++.dg/modules/tpl-friend-20_c.C: New test.
    
    Signed-off-by: Nathaniel Shead <[email protected]>
    Reviewed-by: Jason Merrill <[email protected]>

Diff:
---
 gcc/cp/cp-tree.h                               |  1 +
 gcc/cp/decl.cc                                 | 15 ++--------
 gcc/cp/module.cc                               | 38 ++++++++++++++++++++++++++
 gcc/testsuite/g++.dg/modules/tpl-friend-20_a.C | 16 +++++++++++
 gcc/testsuite/g++.dg/modules/tpl-friend-20_b.C | 10 +++++++
 gcc/testsuite/g++.dg/modules/tpl-friend-20_c.C | 11 ++++++++
 6 files changed, 79 insertions(+), 12 deletions(-)

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 8b7e78a23779..5e8d1c9644c5 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7734,6 +7734,7 @@ extern void set_defining_module (tree);
 extern void set_defining_module_for_partial_spec (tree);
 extern void maybe_key_decl (tree ctx, tree decl);
 extern void propagate_defining_module (tree decl, tree orig);
+extern void transfer_defining_module (tree olddecl, tree newdecl);
 extern void remove_defining_module (tree decl);
 
 extern void mangle_module (int m, bool include_partition);
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 751ba40fc7f0..5f990ea56b2c 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -2595,6 +2595,9 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, 
bool was_hidden)
   else
     DECL_ATTRIBUTES (olddecl) = DECL_ATTRIBUTES (newdecl);
 
+  /* Transfer purviewness and importingness to the old decl.  */
+  transfer_defining_module (olddecl, newdecl);
+
   if (TREE_CODE (newdecl) == TEMPLATE_DECL)
     {
       tree old_result = DECL_TEMPLATE_RESULT (olddecl);
@@ -2669,18 +2672,6 @@ duplicate_decls (tree newdecl, tree olddecl, bool 
hiding, bool was_hidden)
            }
        }
 
-      /* Propagate purviewness and importingness as with
-        set_instantiating_module, unless newdecl is a friend injection.  */
-      if (modules_p () && DECL_LANG_SPECIFIC (new_result)
-         && !(TREE_CODE (new_result) == FUNCTION_DECL
-              && DECL_UNIQUE_FRIEND_P (new_result)))
-       {
-         if (DECL_MODULE_PURVIEW_P (new_result))
-           DECL_MODULE_PURVIEW_P (old_result) = true;
-         if (!DECL_MODULE_IMPORT_P (new_result))
-           DECL_MODULE_IMPORT_P (old_result) = false;
-       }
-
       /* If the new declaration is a definition, update the file and
         line information on the declaration, and also make
         the old declaration the same definition.  */
diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc
index 352d42af2ed5..ccabd640757d 100644
--- a/gcc/cp/module.cc
+++ b/gcc/cp/module.cc
@@ -16298,6 +16298,9 @@ mangle_module (int mod, bool include_partition)
     /* Set when importing the primary module interface.  */
     imp = imp->parent;
 
+  /* Ensure this is actually a module unit.  */
+  gcc_checking_assert (imp);
+
   imp->mangle (include_partition);
 }
 
@@ -21784,6 +21787,41 @@ propagate_defining_module (tree decl, tree orig)
     }
 }
 
+/* NEWDECL matched with OLDDECL, transfer defining module information
+   onto OLDDECL.  We've already validated attachment matches.  */
+
+void
+transfer_defining_module (tree olddecl, tree newdecl)
+{
+  if (!modules_p ())
+    return;
+
+  tree old_inner = STRIP_TEMPLATE (olddecl);
+  tree new_inner = STRIP_TEMPLATE (newdecl);
+
+  if (DECL_LANG_SPECIFIC (new_inner))
+    {
+      gcc_checking_assert (DECL_LANG_SPECIFIC (old_inner));
+      if (DECL_MODULE_PURVIEW_P (new_inner))
+       DECL_MODULE_PURVIEW_P (old_inner) = true;
+      if (!DECL_MODULE_IMPORT_P (new_inner))
+       DECL_MODULE_IMPORT_P (old_inner) = false;
+    }
+
+  if (tree *orig = imported_temploid_friends->get (newdecl))
+    {
+      tree &slot = imported_temploid_friends->get_or_insert (olddecl);
+      if (!slot)
+       slot = *orig;
+      else if (slot != *orig)
+       /* This can happen when multiple classes declare the same
+          friend function (e.g. g++.dg/modules/tpl-friend-4);
+          make sure we at least attach to the same module.  */
+       gcc_checking_assert (get_originating_module (slot)
+                            == get_originating_module (*orig));
+    }
+}
+
 /* DECL is being freed, clear data we don't need anymore.  */
 
 void
diff --git a/gcc/testsuite/g++.dg/modules/tpl-friend-20_a.C 
b/gcc/testsuite/g++.dg/modules/tpl-friend-20_a.C
new file mode 100644
index 000000000000..0918ec4cf5a2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/tpl-friend-20_a.C
@@ -0,0 +1,16 @@
+// PR c++/122551
+// { dg-additional-options "-fmodules" }
+// { dg-module-cmi M }
+
+export module M;
+export template <typename T> struct S {
+  operator int() const { return 123; }
+  template <typename U> friend void f(U);
+  template <typename U> friend void g(U) {}
+  friend void h(int);
+  friend void i(int) {}
+};
+template <typename U> void f(U) {}
+template <typename U> void g(U);
+void h(int) {}
+void i(int);
diff --git a/gcc/testsuite/g++.dg/modules/tpl-friend-20_b.C 
b/gcc/testsuite/g++.dg/modules/tpl-friend-20_b.C
new file mode 100644
index 000000000000..b8e203355c56
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/tpl-friend-20_b.C
@@ -0,0 +1,10 @@
+// PR c++/122551
+// { dg-additional-options "-fmodules" }
+// { dg-module-cmi X }
+
+export module X;
+import M;
+
+export S<int> test() {
+  return {};
+}
diff --git a/gcc/testsuite/g++.dg/modules/tpl-friend-20_c.C 
b/gcc/testsuite/g++.dg/modules/tpl-friend-20_c.C
new file mode 100644
index 000000000000..0414cc768026
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/tpl-friend-20_c.C
@@ -0,0 +1,11 @@
+// PR c++/122551
+// { dg-additional-options "-fmodules" }
+
+import X;
+
+int main() {
+  f(test());
+  g(test());
+  h(test());
+  i(test());
+}

Reply via email to