Message ID | CADzB+2nb=LjRMp=EfD1LxGWAJnkYNu3FGnX3-Dp1BJCdKBwpQA@mail.gmail.com |
---|---|
State | New |
Headers | show |
Series | C++ PATCHes for c++/82882, ICE with lambda in template default argument | expand |
On Fri, Jun 15, 2018 at 4:21 PM, Jason Merrill <jason@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 <jason@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 ----------------------
commit 190aed44d9e0fcbf89cedddcc2003b41a0899374 Author: Jason Merrill <jason@redhat.com> Date: Thu Jun 14 21:56:33 2018 -0400 * pt.c (tsubst_default_argument): Use push_to/pop_from_top_level. * name-lookup.c (do_pushtag): Don't look through complete types, but don't add to them either. Get context from current_binding_level. diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c index 7990029d70c..ec001016d3e 100644 --- a/gcc/cp/name-lookup.c +++ b/gcc/cp/name-lookup.c @@ -6521,12 +6521,7 @@ do_pushtag (tree name, tree type, tag_scope scope) || (b->kind == sk_template_parms && (b->explicit_spec_p || scope == ts_global)) || (b->kind == sk_class - && (scope != ts_current - /* We may be defining a new type in the initializer - of a static member variable. We allow this when - not pedantic, and it is particularly useful for - type punning via an anonymous union. */ - || COMPLETE_TYPE_P (b->this_entity)))) + && scope != ts_current)) b = b->level_chain; gcc_assert (identifier_p (name)); @@ -6540,15 +6535,18 @@ do_pushtag (tree name, tree type, tag_scope scope) if (! context) { - tree cs = current_scope (); + cp_binding_level *cb = b; + while (cb->kind != sk_namespace + && cb->kind != sk_class + && (cb->kind != sk_function_parms + || !cb->this_entity)) + cb = cb->level_chain; + tree cs = cb->this_entity; - /* Avoid setting the lambda context to a current_function_decl that - we aren't actually inside, e.g. one set by push_access_scope - during tsubst_default_argument. */ - if (cs && TREE_CODE (cs) == FUNCTION_DECL - && LAMBDA_TYPE_P (type) - && !at_function_scope_p ()) - cs = DECL_CONTEXT (cs); + gcc_checking_assert (TREE_CODE (cs) == FUNCTION_DECL + ? cs == current_function_decl + : TYPE_P (cs) ? cs == current_class_type + : cs == current_namespace); if (scope == ts_current || (cs && TREE_CODE (cs) == FUNCTION_DECL)) @@ -6587,11 +6585,11 @@ do_pushtag (tree name, tree type, tag_scope scope) if (b->kind == sk_class) { - if (!TYPE_BEING_DEFINED (current_class_type) - && !LAMBDA_TYPE_P (type)) - return error_mark_node; - - if (!PROCESSING_REAL_TEMPLATE_DECL_P ()) + if (!TYPE_BEING_DEFINED (current_class_type)) + /* Don't push anywhere if the class is complete; a lambda in an + NSDMI is not a member of the class. */ + ; + else if (!PROCESSING_REAL_TEMPLATE_DECL_P ()) /* Put this TYPE_DECL on the TYPE_FIELDS list for the class. But if it's a member template class, we want the TEMPLATE_DECL, not the TYPE_DECL, so this is done diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index ed634ddeefb..4ee84b201e9 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -12675,8 +12675,6 @@ tree tsubst_default_argument (tree fn, int parmnum, tree type, tree arg, tsubst_flags_t complain) { - tree saved_class_ptr = NULL_TREE; - tree saved_class_ref = NULL_TREE; int errs = errorcount + sorrycount; /* This can happen in invalid code. */ @@ -12709,19 +12707,10 @@ tsubst_default_argument (tree fn, int parmnum, tree type, tree arg, we must be careful to do name lookup in the scope of S<T>, rather than in the current class. */ + push_to_top_level (); push_access_scope (fn); - /* The "this" pointer is not valid in a default argument. */ - if (cfun) - { - saved_class_ptr = current_class_ptr; - cp_function_chain->x_current_class_ptr = NULL_TREE; - saved_class_ref = current_class_ref; - cp_function_chain->x_current_class_ref = NULL_TREE; - } - start_lambda_scope (parm); - push_deferring_access_checks(dk_no_deferred); /* The default argument expression may cause implicitly defined member functions to be synthesized, which will result in garbage collection. We must treat this situation as if we were within @@ -12732,17 +12721,9 @@ tsubst_default_argument (tree fn, int parmnum, tree type, tree arg, complain, NULL_TREE, /*integral_constant_expression_p=*/false); --function_depth; - pop_deferring_access_checks(); finish_lambda_scope (); - /* Restore the "this" pointer. */ - if (cfun) - { - cp_function_chain->x_current_class_ptr = saved_class_ptr; - cp_function_chain->x_current_class_ref = saved_class_ref; - } - if (errorcount+sorrycount > errs && (complain & tf_warning_or_error)) inform (input_location, @@ -12752,6 +12733,7 @@ tsubst_default_argument (tree fn, int parmnum, tree type, tree arg, arg = check_default_argument (type, arg, complain); pop_access_scope (fn); + pop_from_top_level (); if (arg != error_mark_node && !cp_unevaluated_operand) { diff --git a/gcc/testsuite/g++.dg/template/crash108.C b/gcc/testsuite/g++.dg/template/crash108.C index 9bcabc6009b..3ffadc14c25 100644 --- a/gcc/testsuite/g++.dg/template/crash108.C +++ b/gcc/testsuite/g++.dg/template/crash108.C @@ -1,5 +1,5 @@ // PR c++/50861 -template<class T> struct A {A(int b=k(0));}; // { dg-error "parameter|argument" } -void f(int k){A<int> a;} // // { dg-message "declared" } -// { dg-message "note" "note" { target *-*-* } 3 } +template<class T> struct A {A(int b=k(0));}; // { dg-error "not declared" } + // { dg-error "that depend on a template parameter" "" { target *-*-* } .-1 } +void f(int k){A<int> a;}