Here lookup_template_class is tsubsting a TEMPLATE_DECL and then
crashing when it gets a TEMPLATE_DECL back, when a TEMPLATE_DECL is
what it wants to end up with anyway.  This is silly.

The testcase is also silly, since members have access to their
enclosing class and so the friend declaration has no effect.  Less
silly variants of the testcase still don't work.  But fixing this ICE
doesn't need to wait for the more general fix for multi-level template
friends.

Tested x86_64-pc-linux-gnu, applying to trunk.
commit 990c17ca879abf64c248d4f68fdb20937d515dbe
Author: Jason Merrill <ja...@redhat.com>
Date:   Fri Jul 22 15:37:49 2016 -0400

        PR c++/71738 - nested template friend
    
        * pt.c (lookup_template_class_1): Handle getting template from tsubst.

diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 3ee53d1..a44bead 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -8601,7 +8601,9 @@ lookup_template_class_1 (tree d1, tree arglist, tree 
in_decl, tree context,
             for parameters in the TYPE_DECL of the alias template
             done earlier.  So be careful while getting the template
             of FOUND.  */
-         found = TREE_CODE (found) == TYPE_DECL
+         found = TREE_CODE (found) == TEMPLATE_DECL
+           ? found
+           : TREE_CODE (found) == TYPE_DECL
            ? TYPE_TI_TEMPLATE (TREE_TYPE (found))
            : CLASSTYPE_TI_TEMPLATE (found);
        }
diff --git a/gcc/testsuite/g++.dg/template/friend63.C 
b/gcc/testsuite/g++.dg/template/friend63.C
new file mode 100644
index 0000000..f3a292c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/friend63.C
@@ -0,0 +1,29 @@
+// PR c++/71738
+
+template < class > struct S
+{
+  template < class > struct A
+  { 
+    template < class > struct B
+    {
+      template <class Z>
+      void operator=(Z) { S::i = 0; }
+    };
+  };
+
+  // Note that this friend declaration is useless, since nested classes are
+  // already friends of their enclosing class.
+  template < class X >
+  template < class Y >
+  template < class Z >
+  friend void A < X >::B < Y >::operator= (Z);
+
+private:
+  static int i;
+};
+
+int main()
+{
+  S<int>::A<int>::B<int> b;
+  b = 0;
+}

Reply via email to