On Fri, Jun 15, 2018 at 4:21 PM, Jason Merrill <ja...@redhat.com> wrote: > This testcase was broken by the lambda rewrite. This turned out to be > because the lambda in the template's default argument had namespace > scope, while the instantiation had function scope, because of > tsubst_default_argument calling push_access_scope and do_pushtag using > the resulting value of current_function_decl. > > The fix is to teach do_pushtag to do better. For GCC 8 I'm applying a > focused patch that just recognizes when we're trying to give a lambda > a function scope we aren't actually in. This re-broke bug 59949, so > to avoid that I needed to fix tsubst_lambda_expr to use and update the > discriminator for global lambda scope when the template lambda has no > extra scope. > > Looking at do_pushtag led me to wonder why we were relying on > current_* for the scope in the first place; since we're going to add > the tag to current_binding_level, we should get the scope from there > as well. Making that change revealed a few more issues: > > * With a lambda in a default argument, we would frequently be in the > binding_level for the calling function. Fixed by calling > push_to_top_level. > * Redeclarations of C++11 enums with specified underlying type were > getting pushed into an arbitrary binding level. Fixed by removing the > unnecessary permissiveness around such redeclarations with underlying > types that are not template-equivalent, so a redeclaration isn't > pushed anywhere. > * Lambdas in default member initializers were also getting pushed into > an arbitrary binding level, due to the code for allowing type > definition in a compound literal in a static data member initializer. > Fixed by no longer looking through the binding_level for a complete > class, but instead not pushing the tag anywhere if the class is > complete; a lambda type isn't found by name anyway.
One more: looking through one class binding level then stopped at its enclosing template parms level even if that's still inside another class binding level. Tested x86_64-pc-linux-gnu, applying to trunk.
commit fa24a18a557cc0f7a7cb42416b0150a4847916c6 Author: Jason Merrill <ja...@redhat.com> Date: Wed Jun 27 12:25:07 2018 -0400 Avoid crash on friend in nested class template. * name-lookup.c (do_pushtag): If we skip a class level, also skip its template level. diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c index a30c37428ad..e0500d83071 100644 --- a/gcc/cp/name-lookup.c +++ b/gcc/cp/name-lookup.c @@ -6509,20 +6509,30 @@ do_pushtag (tree name, tree type, tag_scope scope) tree decl; cp_binding_level *b = current_binding_level; - while (/* Cleanup scopes are not scopes from the point of view of - the language. */ - b->kind == sk_cleanup - /* Neither are function parameter scopes. */ - || b->kind == sk_function_parms - /* Neither are the scopes used to hold template parameters - for an explicit specialization. For an ordinary template - declaration, these scopes are not scopes from the point of - view of the language. */ - || (b->kind == sk_template_parms - && (b->explicit_spec_p || scope == ts_global)) - || (b->kind == sk_class - && scope != ts_current)) - b = b->level_chain; + while (true) + { + if (/* Cleanup scopes are not scopes from the point of view of + the language. */ + b->kind == sk_cleanup + /* Neither are function parameter scopes. */ + || b->kind == sk_function_parms + /* Neither are the scopes used to hold template parameters + for an explicit specialization. For an ordinary template + declaration, these scopes are not scopes from the point of + view of the language. */ + || (b->kind == sk_template_parms + && (b->explicit_spec_p || scope == ts_global))) + b = b->level_chain; + else if (b->kind == sk_class + && scope != ts_current) + { + b = b->level_chain; + if (b->kind == sk_template_parms) + b = b->level_chain; + } + else + break; + } gcc_assert (identifier_p (name)); diff --git a/gcc/testsuite/g++.dg/template/friend66.C b/gcc/testsuite/g++.dg/template/friend66.C new file mode 100644 index 00000000000..f8c95f844b3 --- /dev/null +++ b/gcc/testsuite/g++.dg/template/friend66.C @@ -0,0 +1,9 @@ +template <class T> +struct A +{ + template <class U> + struct B + { + friend struct C; + }; +}; diff --git a/gcc/testsuite/g++.old-deja/g++.law/visibility13.C b/gcc/testsuite/g++.old-deja/g++.law/visibility13.C index 025b0b1ef54..451ef1afaf8 100644 --- a/gcc/testsuite/g++.old-deja/g++.law/visibility13.C +++ b/gcc/testsuite/g++.old-deja/g++.law/visibility13.C @@ -15,9 +15,11 @@ using namespace std; const int ArraySize = 12; +template <class> class Array_RC; + template <class Type> -class Array { // { dg-error "" } .struct Array_RC redecl.* -friend class Array_RC; +class Array { + friend class Array_RC<Type>; public: Array(const Type *ar, int sz) { init(ar,sz); } virtual ~Array() { delete [] ia; } @@ -76,8 +78,8 @@ Array_RC<Type>::Array_RC(const Type *ar, int sz) : Array<Type>(ar, sz) {} template <class Type> Type &Array_RC<Type>::operator[](int ix) { - assert(ix >= 0 && ix < size);// { dg-error "" } member .size.* - return ia[ix];// { dg-error "" } member .ia.* + assert(ix >= 0 && ix < this->size); + return this->ia[ix]; } // ------------------- Test routine ----------------------