Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look
OK for trunk/14?

-- >8 --

When instantiating a previously declared hidden template friend declared
at class template scope such as slot_allocated in the first testcase below,
tsubst_friend_function needs to go through all existing specializations
thereof and make them point to the new definition (both in TI_TEMPLATE
and the specializations table).

In this class template scope case however old_decl is not the most
general template, instead it's the partial instantiation of the
previous hidden template friend, and function instantiations are always
relative to the most general template so DECL_TEMPLATE_INSTANTIATIONS
is empty.  Instead we need to use the most general template here.
And when adjusting DECL_TI_ARGS to match, only the innermost template
arguments should be preserved; the outer ones should correspond to the
new definition.

Otherwise we fail a checking-only sanity check in instantiate_decl in
the first testcase, and in the second/third we end up with multiple
definitions of the template friend instantiation at link time.

gcc/cp/ChangeLog:

        * pt.cc (tsubst_friend_function): When adjusting existing
        specializations after defining a previously declared template
        friend, consider the most general template and correct
        DECL_TI_ARGS adjustment.

gcc/testsuite/ChangeLog:

        * g++.dg/template/friend80.C: New test.
        * g++.dg/template/friend81.C: New test.
        * g++.dg/template/friend81a.C: New test.
---
 gcc/cp/pt.cc                              | 13 +++++-----
 gcc/testsuite/g++.dg/template/friend80.C  | 25 +++++++++++++++++++
 gcc/testsuite/g++.dg/template/friend81.C  | 28 +++++++++++++++++++++
 gcc/testsuite/g++.dg/template/friend81a.C | 30 +++++++++++++++++++++++
 4 files changed, 90 insertions(+), 6 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/template/friend80.C
 create mode 100644 gcc/testsuite/g++.dg/template/friend81.C
 create mode 100644 gcc/testsuite/g++.dg/template/friend81a.C

diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 1f6553790a5..ab231aaaa0f 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -11602,6 +11602,7 @@ tsubst_friend_function (tree decl, tree args)
            ;
          else
            {
+             tree old_template = most_general_template (old_decl);
              tree new_template = TI_TEMPLATE (new_friend_template_info);
              tree new_args = TI_ARGS (new_friend_template_info);
 
@@ -11639,7 +11640,7 @@ tsubst_friend_function (tree decl, tree args)
                  /* Reassign any specializations already in the hash table
                     to the new more general template, and add the
                     additional template args.  */
-                 for (t = DECL_TEMPLATE_INSTANTIATIONS (old_decl);
+                 for (t = DECL_TEMPLATE_INSTANTIATIONS (old_template);
                       t != NULL_TREE;
                       t = TREE_CHAIN (t))
                    {
@@ -11652,15 +11653,15 @@ tsubst_friend_function (tree decl, tree args)
 
                      decl_specializations->remove_elt (&elt);
 
-                     DECL_TI_ARGS (spec)
-                       = add_outermost_template_args (new_args,
-                                                      DECL_TI_ARGS (spec));
+                     tree& spec_args = DECL_TI_ARGS (spec);
+                     spec_args = add_outermost_template_args
+                       (new_args, INNERMOST_TEMPLATE_ARGS (spec_args));
 
                      register_specialization
-                       (spec, new_template, DECL_TI_ARGS (spec), true, 0);
+                       (spec, new_template, spec_args, true, 0);
 
                    }
-                 DECL_TEMPLATE_INSTANTIATIONS (old_decl) = NULL_TREE;
+                 DECL_TEMPLATE_INSTANTIATIONS (old_template) = NULL_TREE;
                }
            }
 
diff --git a/gcc/testsuite/g++.dg/template/friend80.C 
b/gcc/testsuite/g++.dg/template/friend80.C
new file mode 100644
index 00000000000..5c417e12dd0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/friend80.C
@@ -0,0 +1,25 @@
+// PR c++/112288
+// { dg-do compile { target c++11 } }
+
+template<class T>
+struct slot {
+  template<class U>
+  friend constexpr bool slot_allocated(slot<T>, U);
+};
+
+template<class T>
+struct allocate_slot {
+  template<class U>
+  friend constexpr bool slot_allocated(slot<T>, U) { return true; }
+};
+
+template<class T, bool = slot_allocated(slot<T>{}, 42)>
+constexpr int next(int) { return 1; }
+
+template<class T>
+constexpr int next(...) { return (allocate_slot<T>{}, 0); }
+
+// slot_allocated<slot<int>, int>() not defined yet
+static_assert(next<int>(0) == 0, "");
+// now it's defined, need to make existing spec point to defn or else ICE
+static_assert(next<int>(0) == 1, "");
diff --git a/gcc/testsuite/g++.dg/template/friend81.C 
b/gcc/testsuite/g++.dg/template/friend81.C
new file mode 100644
index 00000000000..cefcca03ab4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/friend81.C
@@ -0,0 +1,28 @@
+// PR c++/112288
+// { dg-do link }
+
+template<class T> struct A;
+template<class T> struct B;
+
+A<int>* a;
+B<int>* b;
+
+template<class T>
+struct B {
+  template<class U>
+  friend int f(A<T>*, B*, U); // #1
+};
+
+template struct B<int>; // f declared
+int n = f(a, b, 0); // f<int> specialized
+
+template<class T>
+struct A {
+  template<class U>
+  friend int f(A*, B<T>*, U) { return 42; } // #2
+};
+
+template struct A<int>; // f defined, need to make existing f<int> point to 
defn
+int m = f(a, b, 0); // reuses existing specialization f<int>
+
+int main() { }
diff --git a/gcc/testsuite/g++.dg/template/friend81a.C 
b/gcc/testsuite/g++.dg/template/friend81a.C
new file mode 100644
index 00000000000..b4e4c9ec52a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/friend81a.C
@@ -0,0 +1,30 @@
+// PR c++/112288
+// { dg-do link }
+// A version of friend81.C where A and B take a different number of template
+// parameters.
+
+template<class T> struct A;
+template<class T, class = T> struct B;
+
+A<int>* a;
+B<int>* b;
+
+template<class T, class>
+struct B {
+  template<class U>
+  friend int f(A<T>*, B*, U); // #1
+};
+
+template struct B<int>; // f declared
+int n = f(a, b, 0); // f<int> specialized
+
+template<class T>
+struct A {
+  template<class U>
+  friend int f(A*, B<T>*, U) { return 42; } // #2
+};
+
+template struct A<int>; // f defined, need to make existing f<int> point to 
defn
+int m = f(a, b, 0); // reuses existing specialization f<int>
+
+int main() { }
-- 
2.45.2.746.g06e570c0df

Reply via email to