c++: redeclared hidden friend take 2 [PR105852]
authorJason Merrill <jason@redhat.com>
Tue, 7 Jun 2022 01:49:06 +0000 (21:49 -0400)
committerJason Merrill <jason@redhat.com>
Wed, 8 Jun 2022 21:29:54 +0000 (17:29 -0400)
My previous patch for 105761 avoided copying DECL_TEMPLATE_INFO from a
friend to a later definition, but in this testcase we have first a
non-friend declaration and then a definition, and we need to avoid copying
in that case as well.  But we do still want to set new_template_info to
avoid GC trouble.

With this change, the modules dump correctly identifies ::foo as a
non-template function in tpl-friend-2_a.C.

Along the way I noticed that the duplicate_decls handling of
DECL_UNIQUE_FRIEND_P was backwards for templates, where we don't clobber
DECL_LANG_SPECIFIC (olddecl) with DECL_LANG_SPECIFIC (newdecl) like we do
for non-templates.

PR c++/105852
PR c++/105761

gcc/cp/ChangeLog:

* decl.cc (duplicate_decls): Avoid copying template info
from non-templated friend even if newdecl isn't a definition.
Correct handling of DECL_UNIQUE_FRIEND_P on templates.
* pt.cc (non_templated_friend_p): New.
* cp-tree.h (non_templated_friend_p): Declare it.

gcc/testsuite/ChangeLog:

* g++.dg/modules/tpl-friend-2_a.C: Adjust expected dump.
* g++.dg/template/friend74.C: New test.

gcc/cp/cp-tree.h
gcc/cp/decl.cc
gcc/cp/pt.cc
gcc/testsuite/g++.dg/modules/tpl-friend-2_a.C
gcc/testsuite/g++.dg/template/friend74.C [new file with mode: 0644]

index b540c23..97610e3 100644 (file)
@@ -7414,6 +7414,7 @@ extern bool push_tinst_level_loc                (tree, location_t);
 extern bool push_tinst_level_loc                (tree, tree, location_t);
 extern void pop_tinst_level                     (void);
 extern struct tinst_level *outermost_tinst_level(void);
+extern bool non_templated_friend_p             (tree);
 extern void init_template_processing           (void);
 extern void print_template_statistics          (void);
 bool template_template_parameter_p             (const_tree);
index f53136c..66d62af 100644 (file)
@@ -2295,8 +2295,8 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
              merge_default_template_args (new_parms, old_parms,
                                           /*class_p=*/false);
            }
-         if (!DECL_UNIQUE_FRIEND_P (old_result))
-           DECL_UNIQUE_FRIEND_P (new_result) = false;
+         if (!DECL_UNIQUE_FRIEND_P (new_result))
+           DECL_UNIQUE_FRIEND_P (old_result) = false;
 
          check_default_args (newdecl);
 
@@ -2655,13 +2655,7 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
       if (LANG_DECL_HAS_MIN (newdecl))
        {
          DECL_ACCESS (newdecl) = DECL_ACCESS (olddecl);
-         if (new_defines_function
-             && DECL_TEMPLATE_INFO (olddecl)
-             && DECL_UNIQUE_FRIEND_P (DECL_TEMPLATE_RESULT
-                                      (DECL_TI_TEMPLATE (olddecl))))
-           /* Don't copy template info from a non-template friend declaration
-              in a class template (PR105761).  */;
-         else if (DECL_TEMPLATE_INFO (newdecl))
+         if (DECL_TEMPLATE_INFO (newdecl))
            {
              new_template_info = DECL_TEMPLATE_INFO (newdecl);
              if (DECL_TEMPLATE_INSTANTIATION (olddecl)
@@ -2669,8 +2663,10 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
                /* Remember the presence of explicit specialization args.  */
                TINFO_USED_TEMPLATE_ID (DECL_TEMPLATE_INFO (olddecl))
                  = TINFO_USED_TEMPLATE_ID (new_template_info);
-             DECL_TEMPLATE_INFO (newdecl) = DECL_TEMPLATE_INFO (olddecl);
            }
+
+         if (non_templated_friend_p (olddecl))
+           /* Don't copy tinfo from a non-templated friend (PR105761).  */;
          else
            DECL_TEMPLATE_INFO (newdecl) = DECL_TEMPLATE_INFO (olddecl);
        }
index e036f01..214ad84 100644 (file)
@@ -11172,6 +11172,33 @@ outermost_tinst_level (void)
   return level;
 }
 
+/* True iff T is a friend function declaration that is not itself a template
+   and is not defined in a class template.  */
+
+bool
+non_templated_friend_p (tree t)
+{
+  if (t && TREE_CODE (t) == FUNCTION_DECL
+      && DECL_UNIQUE_FRIEND_P (t))
+    {
+      tree ti = DECL_TEMPLATE_INFO (t);
+      if (!ti)
+       return true;
+      /* DECL_FRIEND_CONTEXT is set for a friend defined in class.  */
+      if (DECL_FRIEND_CONTEXT (t))
+       return false;
+      /* Non-templated friends in a class template are still represented with a
+        TEMPLATE_DECL; check that its primary template is the befriending
+        class.  Note that DECL_PRIMARY_TEMPLATE is null for
+        template <class T> friend A<T>::f(); */
+      tree tmpl = TI_TEMPLATE (ti);
+      tree primary = DECL_PRIMARY_TEMPLATE (tmpl);
+      return (primary && primary != tmpl);
+    }
+  else
+    return false;
+}
+
 /* DECL is a friend FUNCTION_DECL or TEMPLATE_DECL.  ARGS is the
    vector of template arguments, as for tsubst.
 
@@ -14096,7 +14123,7 @@ tsubst_function_decl (tree t, tree args, tsubst_flags_t complain,
       /* This special case arises when we have something like this:
 
         template <class T> struct S {
-        friend void f<int>(int, double);
+          friend void f<int>(int, double);
         };
 
         Here, the DECL_TI_TEMPLATE for the friend declaration
index 3acacf8..c12857f 100644 (file)
@@ -16,5 +16,5 @@ template class TPL<int>;  // instantiate
 void foo (int, void *);
 
 // { dg-final { scan-lang-dump {Cluster members:\n  \[0\]=decl definition '::template TPL'\n(  \[.\]=[^\n]*'\n)*  \[.\]=decl declaration '::template foo'\n(  \[.\]=[^\n]*'\n)*  \[.\]=binding '::TPL'} module } }
-// { dg-final { scan-lang-dump {Cluster members:\n  \[0\]=decl declaration '::foo<int>'\n  \[.\]=binding '::foo'} module } }
+// { dg-final { scan-lang-dump {Cluster members:\n  \[0\]=decl declaration '::foo'\n  \[.\]=binding '::foo'} module } }
 // { dg-final { scan-lang-dump {Cluster members:\n  \[0\]=specialization definition '::TPL<int>'} module } }
diff --git a/gcc/testsuite/g++.dg/template/friend74.C b/gcc/testsuite/g++.dg/template/friend74.C
new file mode 100644 (file)
index 0000000..5170833
--- /dev/null
@@ -0,0 +1,8 @@
+// PR c++/105852
+// { dg-additional-options -w }
+
+template <class> struct Local { friend Local False(int *); };
+Local<int> loc;
+Local<int> False(int *);
+void New() { False; }
+Local<int> False(int *) { return Local<int>(); }