Here we got into infinite recursion trying to determine if A<int> is the same as A<T, typename A<T>::type> because we would keep trying to resolve the typename, which would want to compare the types again to see if A<T> is a currently open class, and so on.
This patch avoids this recursion by checking whether the typename scope has TYPE_FIELDS set before we bother to see if it's a currently open class; if TYPE_FIELDS isn't set then this particular dependent template-id has not been defined as a partial specialization anywhere, so there's no point in checking to see if it matches one of the currently open classes. Tested x86_64-pc-linux-gnu, applying to trunk.
commit 56912a98d98144373a621a87455dcb5721f346a8 Author: Jason Merrill <ja...@redhat.com> Date: Fri Jul 22 14:42:53 2016 -0400 PR c++/71515 - typename in partial specialization * pt.c (resolve_typename_type): Try to avoid calling currently_open_class. diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 65fa982..a61f1c8 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -23678,29 +23678,26 @@ resolve_typename_type (tree type, bool only_current_p) } /* If we don't know what SCOPE refers to, then we cannot resolve the TYPENAME_TYPE. */ - if (TREE_CODE (scope) == TYPENAME_TYPE) - return type; - /* If the SCOPE is a template type parameter, we have no way of - resolving the name. */ - if (TREE_CODE (scope) == TEMPLATE_TYPE_PARM) - return type; - /* If the SCOPE is not the current instantiation, there's no reason - to look inside it. */ - if (only_current_p && !currently_open_class (scope)) + if (!CLASS_TYPE_P (scope)) return type; /* If this is a typedef, we don't want to look inside (c++/11987). */ if (typedef_variant_p (type)) return type; /* If SCOPE isn't the template itself, it will not have a valid TYPE_FIELDS list. */ - if (CLASS_TYPE_P (scope) - && same_type_p (scope, CLASSTYPE_PRIMARY_TEMPLATE_TYPE (scope))) + if (same_type_p (scope, CLASSTYPE_PRIMARY_TEMPLATE_TYPE (scope))) /* scope is either the template itself or a compatible instantiation like X<T>, so look up the name in the original template. */ scope = CLASSTYPE_PRIMARY_TEMPLATE_TYPE (scope); - else - /* scope is a partial instantiation, so we can't do the lookup or we - will lose the template arguments. */ + /* We shouldn't have built a TYPENAME_TYPE with a non-dependent scope. */ + gcc_checking_assert (uses_template_parms (scope)); + /* If scope has no fields, it can't be a current instantiation. Check this + before currently_open_class to avoid infinite recursion (71515). */ + if (!TYPE_FIELDS (scope)) + return type; + /* If the SCOPE is not the current instantiation, there's no reason + to look inside it. */ + if (only_current_p && !currently_open_class (scope)) return type; /* Enter the SCOPE so that name lookup will be resolved as if we were in the class definition. In particular, SCOPE will no diff --git a/gcc/testsuite/g++.dg/template/typename22.C b/gcc/testsuite/g++.dg/template/typename22.C new file mode 100644 index 0000000..b5dc1b5 --- /dev/null +++ b/gcc/testsuite/g++.dg/template/typename22.C @@ -0,0 +1,8 @@ +// PR c++/71515 + +template < typename, typename = int > struct A; + +template < typename T > struct A < T, typename A < T >::type > +{ + A < int > *a; +};