Here's attempt two at this patch, with a testcase for the linker issue
I'd inadvertantly caused with my previous attempt.
Bootstrapped and regtested on x86_64-pc-linux-gnu, OK for trunk?
-- >8 --
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: New test.
Signed-off-by: Nathaniel Shead <[email protected]>
---
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(-)
create mode 100644 gcc/testsuite/g++.dg/modules/tpl-friend-22.C
create mode 100644 gcc/testsuite/g++.dg/template/friend88.C
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 74c862ec1c7..31081b390fa 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -19750,7 +19750,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
@@ -19781,7 +19781,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
@@ -19823,7 +19823,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 8ec9740c8a9..01bf26b1e4f 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 341e5ab8808..b37de8723ce 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -11652,7 +11652,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 e420fd4ebaf..02a1e6aee0e 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 00000000000..a77d1cbecad
--- /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 00000000000..73d3d525900
--- /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} } }
--
2.51.0