resolve_typename_type may peek into template types that might still be specialized. In some cases, e.g. g++.dg/template/friend48.C or g++.dg/template/decl2.C, that is exactly the right thing to do. In others, like the newly-added testcase g++.dg/template/pr84789.C, it isn't, and if the qualifying scope happens to resolve to a non-template type, we resolve to that and then fail the assert that checks we still have a template-dependent scope.
It appears to me that, in cases in which the assert would fail, we are missing the typename keyword, and we ought to report an error; if we just return the incoming type unchanged, that's exactly what we get. So, I'm turning such failed asserts into early returns, so that the parser can recover and report an error. Regstrapped on i686- and x86_64-linux-gnu. Ok to install? for gcc/cp/ChangeLog PR c++/84789 * pt.c (resolve_typename_type): Keep the type template-dependent. for gcc/testsuite/ChangeLog PR c++/84789 * g++.dg/template/pr84789.C: New. --- gcc/cp/pt.c | 19 +++++++++++++++++-- gcc/testsuite/g++.dg/template/pr84789.C | 13 +++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 gcc/testsuite/g++.dg/template/pr84789.C diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 89024c10fe2b..067221fa78ea 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -25195,8 +25195,23 @@ resolve_typename_type (tree type, bool only_current_p) /* 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); - /* We shouldn't have built a TYPENAME_TYPE with a non-dependent scope. */ - gcc_checking_assert (uses_template_parms (scope)); + /* We shouldn't have built a TYPENAME_TYPE with a non-dependent + scope. However, it might be a dependent scope that's being + resolved to a non-dependent scope just because we're looking up + scopes we shouldn't, e.g. + + template <typename> class B { typedef int I; }; + template <typename T> class C : B<T> { B<T>::I i; }; + + We need 'typename' before C<T>::i's type, because we can't enter + the scope of B<T> for an unbound template parameter T to tell I + identifies a type (that's why we need typename), but the parser + attempts to do so, presumably so that it can produce better error + messages. However, we'd skip necessary errors if we resolved a + template-dependent type to a template-independent one, so don't + do that. */ + if (!uses_template_parms (scope)) + return type; /* 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)) diff --git a/gcc/testsuite/g++.dg/template/pr84789.C b/gcc/testsuite/g++.dg/template/pr84789.C new file mode 100644 index 000000000000..bc1567f3fe77 --- /dev/null +++ b/gcc/testsuite/g++.dg/template/pr84789.C @@ -0,0 +1,13 @@ +// { dg-do compile } + +struct A +{ + typedef int I; +}; + +template<typename> struct B : A {}; + +template<typename T> struct C : B<T> +{ + B<T>::A::I::I i; // { dg-error "typename" } +}; -- Alexandre Oliva, freedom fighter http://FSFLA.org/~lxoliva/ You must be the change you wish to see in the world. -- Gandhi Be Free! -- http://FSFLA.org/ FSF Latin America board member Free Software Evangelist|Red Hat Brasil GNU Toolchain Engineer