https://gcc.gnu.org/g:ffd34b63c2c2c9f0f928d8da26df729eef50db20

commit r16-6372-gffd34b63c2c2c9f0f928d8da26df729eef50db20
Author: Nathaniel Shead <[email protected]>
Date:   Sun Dec 7 23:17:15 2025 +1100

    c++: Non-inline temploid friends should still be COMDAT [PR122819]
    
    Modules allow temploid friends to no longer be implicitly inline, as
    functions defined in a class body will not be implicitly inline if
    attached to a named module.
    
    This requires us to clean up linkage handling a little bit, mostly by
    replacing usages of 'DECL_TEMPLATE_INSTANTIATION' with
    'DECL_TEMPLOID_INSTANTIATION' when determining if an entity has vague
    linkage.
    
    This caused the friend88.C testcase to miscompile however, as 'foo' was
    incorrectly having 'DECL_FRIEND_PSEUDO_TEMPLATE_INSTANTIATION' getting
    set because it was keeping its tinfo.
    
    This is because 'non_templated_friend_p' was returning 'false', since
    the function didn't have a primary template.  But that's expected I
    think here, so fixed by also returning true for friend declarations
    pushed into namespace scope, which still allows dependent nested friends
    to be considered templated.
    
            PR c++/122819
    
    gcc/cp/ChangeLog:
    
            * decl.cc (start_preparsed_function): Use
            DECL_TEMPLOID_INSTANTIATION instead of
            DECL_TEMPLATE_INSTANTIATION to check vague linkage.
            * decl2.cc (vague_linkage_p): Likewise.
            (c_parse_final_cleanups): Simplify condition.
            * pt.cc (non_templated_friend_p): Namespace-scope friend
            function declarations without a primary template are still
            non-templated.
            * semantics.cc (expand_or_defer_fn_1): Also check for temploid
            friend functions.
    
    gcc/testsuite/ChangeLog:
    
            * g++.dg/modules/tpl-friend-22.C: New test.
            * g++.dg/template/friend88.C: New test.
    
    Signed-off-by: Nathaniel Shead <[email protected]>
    Reviewed-by: Jason Merrill <[email protected]>

Diff:
---
 gcc/cp/decl.cc                               |  6 +++---
 gcc/cp/decl2.cc                              |  5 ++---
 gcc/cp/pt.cc                                 |  2 +-
 gcc/cp/semantics.cc                          |  4 +++-
 gcc/testsuite/g++.dg/modules/tpl-friend-22.C | 24 +++++++++++++++++++++
 gcc/testsuite/g++.dg/template/friend88.C     | 32 ++++++++++++++++++++++++++++
 6 files changed, 65 insertions(+), 8 deletions(-)

diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 301b38219424..caac2a495985 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -19761,7 +19761,7 @@ start_preparsed_function (tree decl1, tree attrs, int 
flags)
        }
     }
 
-  bool honor_interface = (!DECL_TEMPLATE_INSTANTIATION (decl1)
+  bool honor_interface = (!DECL_TEMPLOID_INSTANTIATION (decl1)
                          /* Implicitly-defined methods (like the
                             destructor for a class in which no destructor
                             is explicitly declared) must not be defined
@@ -19792,7 +19792,7 @@ start_preparsed_function (tree decl1, tree attrs, int 
flags)
   else if (!finfo->interface_unknown && honor_interface)
     {
       if (DECL_DECLARED_INLINE_P (decl1)
-         || DECL_TEMPLATE_INSTANTIATION (decl1))
+         || DECL_TEMPLOID_INSTANTIATION (decl1))
        {
          DECL_EXTERNAL (decl1)
            = (finfo->interface_only
@@ -19834,7 +19834,7 @@ start_preparsed_function (tree decl1, tree attrs, int 
flags)
        DECL_EXTERNAL (decl1) = 0;
 
       if ((DECL_DECLARED_INLINE_P (decl1)
-          || DECL_TEMPLATE_INSTANTIATION (decl1))
+          || DECL_TEMPLOID_INSTANTIATION (decl1))
          && ! DECL_INTERFACE_KNOWN (decl1))
        DECL_DEFER_OUTPUT (decl1) = 1;
       else
diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc
index 93415203d394..cb022d0b671c 100644
--- a/gcc/cp/decl2.cc
+++ b/gcc/cp/decl2.cc
@@ -2510,7 +2510,7 @@ vague_linkage_p (tree decl)
       || (TREE_CODE (decl) == FUNCTION_DECL
          && DECL_DECLARED_INLINE_P (decl))
       || (DECL_LANG_SPECIFIC (decl)
-         && DECL_TEMPLATE_INSTANTIATION (decl))
+         && DECL_TEMPLOID_INSTANTIATION (decl))
       || (VAR_P (decl) && DECL_INLINE_VAR_P (decl)))
     return true;
   else if (DECL_FUNCTION_SCOPE_P (decl))
@@ -5850,8 +5850,7 @@ c_parse_final_cleanups (void)
          && !(header_module_p ()
               && (DECL_DEFAULTED_FN (decl) || decl_tls_wrapper_p (decl)))
          /* Don't complain if the template was defined.  */
-         && !((DECL_TEMPLATE_INSTANTIATION (decl)
-               || DECL_FRIEND_PSEUDO_TEMPLATE_INSTANTIATION (decl))
+         && !(DECL_TEMPLOID_INSTANTIATION (decl)
               && DECL_INITIAL (DECL_TEMPLATE_RESULT
                                (template_for_substitution (decl))))
          && warning_at (DECL_SOURCE_LOCATION (decl), 0,
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index af2c7a767ab8..242aeee91413 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -11657,7 +11657,7 @@ non_templated_friend_p (tree t)
         template <class T> friend A<T>::f(); */
       tree tmpl = TI_TEMPLATE (ti);
       tree primary = DECL_PRIMARY_TEMPLATE (tmpl);
-      return (primary && primary != tmpl);
+      return ((primary || DECL_NAMESPACE_SCOPE_P (t)) && primary != tmpl);
     }
   else
     return false;
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index e598632d85bc..4feff4518bf6 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -5537,7 +5537,9 @@ expand_or_defer_fn_1 (tree fn)
      of the compilation.  Until that point, we do not want the back
      end to output them -- but we do want it to see the bodies of
      these functions so that it can inline them as appropriate.  */
-  if (DECL_DECLARED_INLINE_P (fn) || DECL_IMPLICIT_INSTANTIATION (fn))
+  if (DECL_DECLARED_INLINE_P (fn)
+      || DECL_IMPLICIT_INSTANTIATION (fn)
+      || DECL_FRIEND_PSEUDO_TEMPLATE_INSTANTIATION (fn))
     {
       if (DECL_INTERFACE_KNOWN (fn))
        /* We've already made a decision as to how this function will
diff --git a/gcc/testsuite/g++.dg/modules/tpl-friend-22.C 
b/gcc/testsuite/g++.dg/modules/tpl-friend-22.C
new file mode 100644
index 000000000000..a77d1cbecad8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/tpl-friend-22.C
@@ -0,0 +1,24 @@
+// PR c++/122819
+// { dg-do compile { target *-*-*gnu* } }
+// { dg-additional-options "-fmodules" }
+
+export module M;
+
+template <typename T> struct S;
+void foo(S<float>);
+
+template <typename T> struct S {
+  friend void foo(S) {}
+};
+
+void foo(S<double>);
+
+void use() {
+  foo(S<int>{});
+  foo(S<float>{});
+  foo(S<double>{});
+}
+
+// { dg-final { scan-assembler "_ZW1M3fooS_1SIiE,comdat" } }
+// { dg-final { scan-assembler "_ZW1M3fooS_1SIfE,comdat" } }
+// { dg-final { scan-assembler "_ZW1M3fooS_1SIdE,comdat" } }
diff --git a/gcc/testsuite/g++.dg/template/friend88.C 
b/gcc/testsuite/g++.dg/template/friend88.C
new file mode 100644
index 000000000000..73d3d525900a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/friend88.C
@@ -0,0 +1,32 @@
+// PR c++/122819
+// { dg-do compile { target c++11 } }
+
+template <typename T> struct basic_streambuf;
+using streambuf = basic_streambuf<char>;
+
+struct S {
+  void foo();
+  template <typename T> void bar();
+};
+
+template <typename T> struct X {
+  void foo();
+};
+
+template <typename T> struct basic_streambuf {
+  void qux();
+
+  friend void foo();
+  friend void S::foo();
+  template <typename U> friend void S::bar();
+  template <typename U> friend void X<U>::foo();
+  template <typename U> friend void basic_streambuf<U>::qux();
+};
+extern template struct basic_streambuf<char>;
+
+void foo() {}
+void S::foo() {}
+
+// { dg-final { scan-assembler {_Z3foov:} } }
+// { dg-final { scan-assembler {_ZN1S3fooEv:} } }
+// { dg-final { scan-assembler-not {comdat} } }

Reply via email to