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; +}