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

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(); };

base-commit: 405eda0d34b69fb6ee12ba6ed0f69c2c2411d8ee
prerequisite-patch-id: 6ebd0a0d57278b346f0f14058aaf68ac9619a105
prerequisite-patch-id: 42120f687957746cc6c936531d7dd3a196586b6a
-- 
2.27.0

Reply via email to