]> gcc.gnu.org Git - gcc.git/commitdiff
c++: friend implicit template instantiation [PR91618]
authorJason Merrill <jason@redhat.com>
Thu, 7 Apr 2022 03:46:53 +0000 (23:46 -0400)
committerJason Merrill <jason@redhat.com>
Sat, 9 Apr 2022 01:50:46 +0000 (21:50 -0400)
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.

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
gcc/cp/name-lookup.cc
gcc/cp/pt.cc
gcc/testsuite/g++.dg/cpp0x/friend7.C
gcc/testsuite/g++.dg/template/friend72.C [new file with mode: 0644]
gcc/testsuite/g++.dg/template/friend72a.C [new file with mode: 0644]
gcc/testsuite/g++.dg/template/friend73.C [new file with mode: 0644]

index dc2c924c4728b9ec91b0877270510550079f7663..c780702572d5bcdb4ccff0f4d6a9482c97ffa19c 100644 (file)
@@ -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))
index b7fc2781d53af387c0ad91102fffb5cad394525f..118da0950af4d1ea4a21549dbb81aca1d94c91b5 100644 (file)
@@ -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)
index 63794a40d5d2478b70c4f184be7a3fcd597c47f1..78519562953b757ba71e7e28ea52081e52f11fe2 100644 (file)
@@ -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:
index e1d5f449f5c9d7b70ab4449d3f7f46fc10859eb3..4d0961c0397ed0bef834f4fc540059465e162d58 100644 (file)
@@ -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 (file)
index 0000000..b499909
--- /dev/null
@@ -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 (file)
index 0000000..ef6834a
--- /dev/null
@@ -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 (file)
index 0000000..d009ec7
--- /dev/null
@@ -0,0 +1,5 @@
+// PR c++/96604
+
+struct A { template<typename T> operator T(); };
+struct X {};
+struct B { friend A::operator X(); };
This page took 0.131409 seconds and 5 git commands to generate.