diff mbox series

[pushed] c++: friend implicit template instantiation [PR91618]

Message ID 20220409015024.2691168-1-jason@redhat.com
State New
Headers show
Series [pushed] c++: friend implicit template instantiation [PR91618] | expand

Commit Message

Jason Merrill April 9, 2022, 1:50 a.m. UTC
This rule that for a friend with a qualified name we try to find a
matching template was already in C++98, but it seems we never implemented
it, and nobody reported it until 2019.

This patch sets DECL_IMPLICIT_INSTANTIATION to signal to
check_explicit_specialization that we want to find a template, like
grokfndecl already did for explicit template args.  check_classfn also needs
to call it, as check_classfn is called after the call to
check_explicit_specialization in grokfndecl, whereas the call to
set_decl_namespace comes sooner.  This inconsistency is inelegant, but safer
at this point in the release cycle; I'll unify them in stage 1.

Tested x86_64-pc-linux-gnu, applying to trunk.

	PR c++/91618
	PR c++/96604

gcc/cp/ChangeLog:

	* name-lookup.cc (set_decl_namespace): Set
	DECL_IMPLICIT_INSTANTIATION if no non-template match.
	* pt.cc (check_explicit_specialization): Check it.
	* decl2.cc (check_classfn): Call it.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp0x/friend7.C: Remove xfail.
	* g++.dg/template/friend72.C: New test.
	* g++.dg/template/friend72a.C: New test.
	* g++.dg/template/friend73.C: New test.
---
 gcc/cp/decl2.cc                           | 23 ++++++++++++++++++++++-
 gcc/cp/name-lookup.cc                     | 15 +++++++++++++++
 gcc/cp/pt.cc                              |  4 +++-
 gcc/testsuite/g++.dg/cpp0x/friend7.C      |  2 +-
 gcc/testsuite/g++.dg/template/friend72.C  | 15 +++++++++++++++
 gcc/testsuite/g++.dg/template/friend72a.C | 14 ++++++++++++++
 gcc/testsuite/g++.dg/template/friend73.C  |  5 +++++
 7 files changed, 75 insertions(+), 3 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/template/friend72.C
 create mode 100644 gcc/testsuite/g++.dg/template/friend72a.C
 create mode 100644 gcc/testsuite/g++.dg/template/friend73.C


base-commit: 405eda0d34b69fb6ee12ba6ed0f69c2c2411d8ee
prerequisite-patch-id: 6ebd0a0d57278b346f0f14058aaf68ac9619a105
prerequisite-patch-id: 42120f687957746cc6c936531d7dd3a196586b6a
diff mbox series

Patch

diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc
index dc2c924c472..c780702572d 100644
--- a/gcc/cp/decl2.cc
+++ b/gcc/cp/decl2.cc
@@ -734,11 +734,15 @@  check_classfn (tree ctype, tree function, tree template_parms)
   tree pushed_scope = push_scope (ctype);
   tree matched = NULL_TREE;
   tree fns = get_class_binding (ctype, DECL_NAME (function));
-  
+  bool saw_template = false;
+
   for (ovl_iterator iter (fns); !matched && iter; ++iter)
     {
       tree fndecl = *iter;
 
+      if (TREE_CODE (fndecl) == TEMPLATE_DECL)
+	saw_template = true;
+
       /* A member template definition only matches a member template
 	 declaration.  */
       if (is_template != (TREE_CODE (fndecl) == TEMPLATE_DECL))
@@ -788,6 +792,23 @@  check_classfn (tree ctype, tree function, tree template_parms)
 	matched = fndecl;
     }
 
+  if (!matched && !is_template && saw_template
+      && !processing_template_decl && DECL_UNIQUE_FRIEND_P (function))
+    {
+      /* "[if no non-template match is found,] each remaining function template
+	 is replaced with the specialization chosen by deduction from the
+	 friend declaration or discarded if deduction fails."
+
+	 So ask check_explicit_specialization to find a matching template.  */
+      SET_DECL_IMPLICIT_INSTANTIATION (function);
+      tree spec = check_explicit_specialization (DECL_NAME (function),
+						 function, /* tcount */0,
+						 /* friend flag */4,
+						 /* attrlist */NULL_TREE);
+      if (spec != error_mark_node)
+	matched = spec;
+    }
+
   if (!matched)
     {
       if (!COMPLETE_TYPE_P (ctype))
diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc
index b7fc2781d53..118da0950af 100644
--- a/gcc/cp/name-lookup.cc
+++ b/gcc/cp/name-lookup.cc
@@ -5898,6 +5898,7 @@  set_decl_namespace (tree decl, tree scope, bool friendp)
 
   tree found = NULL_TREE;
   bool hidden_p = false;
+  bool saw_template = false;
 
   for (lkp_iterator iter (old); iter; ++iter)
     {
@@ -5922,6 +5923,20 @@  set_decl_namespace (tree decl, tree scope, bool friendp)
 	  found = ofn;
 	  hidden_p = iter.hidden_p ();
 	}
+      else if (TREE_CODE (decl) == FUNCTION_DECL
+	       && TREE_CODE (ofn) == TEMPLATE_DECL)
+	saw_template = true;
+    }
+
+  if (!found && friendp && saw_template)
+    {
+      /* "[if no non-template match is found,] each remaining function template
+	 is replaced with the specialization chosen by deduction from the
+	 friend declaration or discarded if deduction fails."
+
+	 So tell check_explicit_specialization to look for a match.  */
+      SET_DECL_IMPLICIT_INSTANTIATION (decl);
+      return;
     }
 
   if (found)
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 63794a40d5d..78519562953 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -2863,7 +2863,9 @@  check_explicit_specialization (tree declarator,
 	  specialization = 1;
 	  SET_DECL_TEMPLATE_SPECIALIZATION (decl);
 	}
-      else if (TREE_CODE (declarator) == TEMPLATE_ID_EXPR)
+      else if (TREE_CODE (declarator) == TEMPLATE_ID_EXPR
+	       || (DECL_LANG_SPECIFIC (decl)
+		   && DECL_IMPLICIT_INSTANTIATION (decl)))
 	{
 	  if (is_friend)
 	    /* This could be something like:
diff --git a/gcc/testsuite/g++.dg/cpp0x/friend7.C b/gcc/testsuite/g++.dg/cpp0x/friend7.C
index e1d5f449f5c..4d0961c0397 100644
--- a/gcc/testsuite/g++.dg/cpp0x/friend7.C
+++ b/gcc/testsuite/g++.dg/cpp0x/friend7.C
@@ -21,7 +21,7 @@  struct S {
   friend class __attribute__((deprecated)) N3;
   [[deprecated]] friend void foo<>(int, int); // { dg-warning "attribute ignored" }
   [[deprecated]] friend void ::foo(int, int); // { dg-warning "attribute ignored" }
-  // { dg-bogus "should have" "PR100339" { xfail *-*-* } .-1 }
+  // { dg-bogus "should have" "PR100339" }
 };
 
 template<typename T>
diff --git a/gcc/testsuite/g++.dg/template/friend72.C b/gcc/testsuite/g++.dg/template/friend72.C
new file mode 100644
index 00000000000..b499909076d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/friend72.C
@@ -0,0 +1,15 @@ 
+// PR c++/91618
+// { dg-do link }
+
+template <class T> int f(T t)
+{ return t.i; }
+
+class A {
+  friend int ::f(A);
+  int i;
+};
+
+int main()
+{
+  f(A()); // link error, trying to call non-template function
+}
diff --git a/gcc/testsuite/g++.dg/template/friend72a.C b/gcc/testsuite/g++.dg/template/friend72a.C
new file mode 100644
index 00000000000..ef6834aae2d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/friend72a.C
@@ -0,0 +1,14 @@ 
+// PR c++/91618
+
+template <class T> int f(T t, int)
+{ return t.i; }
+
+class A {
+  friend int ::f(A);		// { dg-error "" }
+  int i;
+};
+
+int main()
+{
+  f(A());			// { dg-error "no match" }
+}
diff --git a/gcc/testsuite/g++.dg/template/friend73.C b/gcc/testsuite/g++.dg/template/friend73.C
new file mode 100644
index 00000000000..d009ec7a990
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/friend73.C
@@ -0,0 +1,5 @@ 
+// PR c++/96604
+
+struct A { template<typename T> operator T(); };
+struct X {};
+struct B { friend A::operator X(); };