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} } }
