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. Tested x86_64-pc-linux-gnu. First patch applied to trunk and 8, others only to trunk.
commit cce1fe034f823205286212bc7ea6feb386377a93 Author: Jason Merrill <ja...@redhat.com> Date: Thu Jun 14 17:17:07 2018 -0400 PR c++/82882 - ICE with lambda in template default argument. * lambda.c (record_null_lambda_scope): New. * pt.c (tsubst_lambda_expr): Use it. * name-lookup.c (do_pushtag): Don't give a lambda DECL_CONTEXT of a function that isn't open. diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 9a50d666cad..3566668efef 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -7018,6 +7018,7 @@ extern tree finish_builtin_launder (location_t, tree, tsubst_flags_t); extern void start_lambda_scope (tree); extern void record_lambda_scope (tree); +extern void record_null_lambda_scope (tree); extern void finish_lambda_scope (void); extern tree start_lambda_function (tree fn, tree lambda_expr); extern void finish_lambda_function (tree body); diff --git a/gcc/cp/lambda.c b/gcc/cp/lambda.c index 231490fbe4e..3776d6bcea9 100644 --- a/gcc/cp/lambda.c +++ b/gcc/cp/lambda.c @@ -1380,6 +1380,24 @@ record_lambda_scope (tree lambda) LAMBDA_EXPR_DISCRIMINATOR (lambda) = lambda_count++; } +/* This lambda is an instantiation of a lambda in a template default argument + that got no LAMBDA_EXPR_EXTRA_SCOPE, so this shouldn't either. But we do + need to use and increment the global count to avoid collisions. */ + +void +record_null_lambda_scope (tree lambda) +{ + if (vec_safe_is_empty (lambda_scope_stack)) + record_lambda_scope (lambda); + else + { + tree_int *p = lambda_scope_stack->begin(); + LAMBDA_EXPR_EXTRA_SCOPE (lambda) = p->t; + LAMBDA_EXPR_DISCRIMINATOR (lambda) = p->i++; + } + gcc_assert (LAMBDA_EXPR_EXTRA_SCOPE (lambda) == NULL_TREE); +} + void finish_lambda_scope (void) { diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c index 8d1748a285a..7990029d70c 100644 --- a/gcc/cp/name-lookup.c +++ b/gcc/cp/name-lookup.c @@ -6542,6 +6542,14 @@ do_pushtag (tree name, tree type, tag_scope scope) { tree cs = current_scope (); + /* 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); + if (scope == ts_current || (cs && TREE_CODE (cs) == FUNCTION_DECL)) context = cs; diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index ba78d2e34a5..ed634ddeefb 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -17530,7 +17530,11 @@ tsubst_lambda_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl) LAMBDA_EXPR_MUTABLE_P (r) = LAMBDA_EXPR_MUTABLE_P (t); if (LAMBDA_EXPR_EXTRA_SCOPE (t) == NULL_TREE) - LAMBDA_EXPR_EXTRA_SCOPE (r) = NULL_TREE; + /* A lambda in a default argument outside a class gets no + LAMBDA_EXPR_EXTRA_SCOPE, as specified by the ABI. But + tsubst_default_argument calls start_lambda_scope, so we need to + specifically ignore it here, and use the global scope. */ + record_null_lambda_scope (r); else record_lambda_scope (r); diff --git a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-defarg8.C b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-defarg8.C new file mode 100644 index 00000000000..cf842ad9dd1 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-defarg8.C @@ -0,0 +1,24 @@ +// PR c++/82282 +// { dg-do compile { target c++14 } } + +template<typename = int> +void f(const char* a = + ([](int = []{ static int i; return 42; }()) { + static int i; + return ""; + }())); + +template<typename = int> +struct X { + void f(const char* a = + ([](int = [] { static int i; return 42; }()) { + enum { Size = 42 - 1 }; + return ""; + }())); +}; + +void g() +{ + f(); + X<int>().f(); +}
commit 7844e09eb7e7a786219101e0bcc1f47f62558449 Author: Jason Merrill <ja...@redhat.com> Date: Fri Jun 15 12:56:55 2018 -0400 * decl.c (start_enum): Do compare dependent underlying type. diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index 98dea9b766f..d7df01289db 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -14131,8 +14131,6 @@ start_enum (tree name, tree enumtype, tree underlying_type, enumtype = error_mark_node; } else if (underlying_type && ENUM_UNDERLYING_TYPE (enumtype) - && !dependent_type_p (underlying_type) - && !dependent_type_p (ENUM_UNDERLYING_TYPE (enumtype)) && !same_type_p (underlying_type, ENUM_UNDERLYING_TYPE (enumtype))) { @@ -14157,7 +14155,7 @@ start_enum (tree name, tree enumtype, tree underlying_type, /* enumtype may be an ENUMERAL_TYPE if this is a redefinition of an opaque enum, or an opaque enum of an already defined - enumeration (C++0x only). + enumeration (C++11). In any other case, it'll be NULL_TREE. */ if (!enumtype) { @@ -14166,14 +14164,9 @@ start_enum (tree name, tree enumtype, tree underlying_type, } prevtype = enumtype; - /* Do not push the decl more than once, unless we need to - compare underlying types at instantiation time */ + /* Do not push the decl more than once. */ if (!enumtype - || TREE_CODE (enumtype) != ENUMERAL_TYPE - || (underlying_type - && dependent_type_p (underlying_type)) - || (ENUM_UNDERLYING_TYPE (enumtype) - && dependent_type_p (ENUM_UNDERLYING_TYPE (enumtype)))) + || TREE_CODE (enumtype) != ENUMERAL_TYPE) { enumtype = cxx_make_type (ENUMERAL_TYPE); enumtype = pushtag (name, enumtype, /*tag_scope=*/ts_current); diff --git a/gcc/cp/name-lookup.h b/gcc/cp/name-lookup.h index ae225db3ba5..2bda0f11d1d 100644 --- a/gcc/cp/name-lookup.h +++ b/gcc/cp/name-lookup.h @@ -112,7 +112,7 @@ enum scope_kind { of an if or switch statement. */ sk_function_parms, /* The scope containing function parameters. */ sk_class, /* The scope containing the members of a class. */ - sk_scoped_enum, /* The scope containing the enumertors of a C++0x + sk_scoped_enum, /* The scope containing the enumerators of a C++11 scoped enumeration. */ sk_namespace, /* The scope containing the members of a namespace, including the global scope. */ diff --git a/gcc/testsuite/g++.dg/cpp0x/forw_enum10.C b/gcc/testsuite/g++.dg/cpp0x/forw_enum10.C index 3c4c3de6d66..e5d8e49537c 100644 --- a/gcc/testsuite/g++.dg/cpp0x/forw_enum10.C +++ b/gcc/testsuite/g++.dg/cpp0x/forw_enum10.C @@ -6,7 +6,7 @@ template<typename T> struct S1 enum E : T; // { dg-message "previous definition" } enum E : int; // { dg-error "different underlying type" } }; -template struct S1<short>; // { dg-message "required from here" } +template struct S1<short>; template<typename T> struct S2 { @@ -17,8 +17,8 @@ template struct S2<short>; template<typename T1, typename T2> struct S3 { - enum E : T1; - enum E : T2; + enum E : T1; // { dg-message "previous definition" } + enum E : T2; // { dg-error "different underlying type" } }; template struct S3<short,short>; @@ -27,4 +27,4 @@ template<typename T1, typename T2> struct S4 enum E : T1; // { dg-message "previous definition" } enum E : T2; // { dg-error "different underlying type" } }; -template struct S4<short,char>; // { dg-message "required from here" } +template struct S4<short,char>; diff --git a/gcc/testsuite/g++.dg/cpp0x/forw_enum7.C b/gcc/testsuite/g++.dg/cpp0x/forw_enum7.C index 3c67f547057..d43bbc1caf2 100644 --- a/gcc/testsuite/g++.dg/cpp0x/forw_enum7.C +++ b/gcc/testsuite/g++.dg/cpp0x/forw_enum7.C @@ -4,8 +4,6 @@ template<typename T> struct S1 { enum E1 : int; - enum E1 : T; - enum class E2 : int; enum class E2 : T; }; diff --git a/gcc/testsuite/g++.dg/cpp0x/forw_enum8.C b/gcc/testsuite/g++.dg/cpp0x/forw_enum8.C index b84a759c6de..3f06cd4bd67 100644 --- a/gcc/testsuite/g++.dg/cpp0x/forw_enum8.C +++ b/gcc/testsuite/g++.dg/cpp0x/forw_enum8.C @@ -1,22 +1,19 @@ // { dg-do compile { target c++11 } } -//This instatiation is ok template<typename T> struct S1 { - enum E : int; - enum E : T; + enum E : int; // { dg-message "previous definition" } + enum E : T; // { dg-error "different underlying type" } }; template struct S1<int>; //ok -//This error is diagnosed at instantiation time template<typename T> struct S2 { enum E : int; // { dg-message "previous definition" } enum E : T; // { dg-error "different underlying type" } }; -template struct S2<short>; // { dg-message "required from here" } +template struct S2<short>; -//This error is diagnosed at compilation time template<typename T> struct S3 { enum E : int; // { dg-message "previous definition" }
commit 190aed44d9e0fcbf89cedddcc2003b41a0899374 Author: Jason Merrill <ja...@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;}