From f44a8dd56f5bfbd0596c39693e268ef880c06221 Mon Sep 17 00:00:00 2001 From: Jason Merrill Date: Tue, 29 Aug 2017 16:37:15 -0400 Subject: [PATCH] Reimplement handling of lambdas in templates. * cp-tree.h (LAMBDA_FUNCTION_P): Check DECL_DECLARES_FUNCTION_P. * decl.c (start_preparsed_function): Call start_lambda_scope. (finish_function): Call finish_lambda_scope. * init.c (get_nsdmi): Call start/finish_lambda_scope. * lambda.c (start_lambda_scope): Only ignore VAR_DECL in a function. * parser.c (cp_parser_function_definition_after_declarator): Don't call start/finish_lambda_scope. * pt.c (retrieve_specialization): Ignore lambda functions in templates. (find_parameter_packs_r): Ignore capture proxies. Look into lambdas. (check_for_bare_parameter_packs): Allow bare packs in lambdas. (tsubst_default_argument): Call start/finish_lambda_scope. (tsubst_function_decl): Handle lambda functions differently. (tsubst_template_decl): Likewise. (tsubst_expr) [DECL_EXPR]: Skip closure declarations and capture proxies. (tsubst_lambda_expr): Create a new closure rather than instantiate the one from the template. (tsubst_copy_and_build): Don't register a specialization of a pack. (regenerate_decl_from_template): Call start/finish_lambda_scope. (instantiate_decl): Remove special lambda function handling. * semantics.c (process_outer_var_ref): Remove special generic lambda handling. Don't implicitly capture in a lambda in a template. Look for an existing proxy. * class.c (current_nonlambda_class_type): Use decl_type_context. From-SVN: r251433 --- gcc/cp/ChangeLog | 30 + gcc/cp/class.c | 25 +- gcc/cp/cp-tree.h | 10 +- gcc/cp/decl.c | 12 +- gcc/cp/init.c | 6 +- gcc/cp/lambda.c | 83 ++ gcc/cp/parser.c | 82 +- gcc/cp/pt.c | 1107 +++++++++++++--------- gcc/cp/semantics.c | 95 +- gcc/testsuite/g++.dg/cpp1z/fold-lambda.C | 14 + gcc/testsuite/g++.dg/warn/Wshadow-6.C | 2 +- 11 files changed, 847 insertions(+), 619 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp1z/fold-lambda.C diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index ef7f4292d4d..387a27591c4 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,33 @@ +2017-08-23 Jason Merrill + + Reimplement handling of lambdas in templates. + * cp-tree.h (LAMBDA_FUNCTION_P): Check DECL_DECLARES_FUNCTION_P. + * decl.c (start_preparsed_function): Call start_lambda_scope. + (finish_function): Call finish_lambda_scope. + * init.c (get_nsdmi): Call start/finish_lambda_scope. + * lambda.c (start_lambda_scope): Only ignore VAR_DECL in a function. + * parser.c (cp_parser_function_definition_after_declarator): Don't + call start/finish_lambda_scope. + * pt.c (retrieve_specialization): Ignore lambda functions in + templates. + (find_parameter_packs_r): Ignore capture proxies. Look into + lambdas. + (check_for_bare_parameter_packs): Allow bare packs in lambdas. + (tsubst_default_argument): Call start/finish_lambda_scope. + (tsubst_function_decl): Handle lambda functions differently. + (tsubst_template_decl): Likewise. + (tsubst_expr) [DECL_EXPR]: Skip closure declarations and capture + proxies. + (tsubst_lambda_expr): Create a new closure rather than instantiate + the one from the template. + (tsubst_copy_and_build): Don't register a specialization of a pack. + (regenerate_decl_from_template): Call start/finish_lambda_scope. + (instantiate_decl): Remove special lambda function handling. + * semantics.c (process_outer_var_ref): Remove special generic lambda + handling. Don't implicitly capture in a lambda in a template. Look + for an existing proxy. + * class.c (current_nonlambda_class_type): Use decl_type_context. + 2017-08-29 Jason Merrill * cp-tree.h (LAMBDA_EXPR_CLOSURE): Use TREE_TYPE. diff --git a/gcc/cp/class.c b/gcc/cp/class.c index 28cf7dcdb5f..a5f1007b993 100644 --- a/gcc/cp/class.c +++ b/gcc/cp/class.c @@ -7709,27 +7709,10 @@ outermost_open_class (void) tree current_nonlambda_class_type (void) { - int i; - - /* We start looking from 1 because entry 0 is from global scope, - and has no type. */ - for (i = current_class_depth; i > 0; --i) - { - tree c; - if (i == current_class_depth) - c = current_class_type; - else - { - if (current_class_stack[i].hidden) - break; - c = current_class_stack[i].type; - } - if (!c) - continue; - if (!LAMBDA_TYPE_P (c)) - return c; - } - return NULL_TREE; + tree type = current_class_type; + while (type && LAMBDA_TYPE_P (type)) + type = decl_type_context (TYPE_NAME (type)); + return type; } /* When entering a class scope, all enclosing class scopes' names with diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index ad97be4c1ce..41c48ece8a4 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -1216,8 +1216,9 @@ struct GTY (()) tree_trait_expr { (CLASS_TYPE_P (NODE) && CLASSTYPE_LAMBDA_EXPR (NODE)) /* Test if FUNCTION_DECL is a lambda function. */ -#define LAMBDA_FUNCTION_P(FNDECL) \ - (DECL_OVERLOADED_OPERATOR_P (FNDECL) == CALL_EXPR \ +#define LAMBDA_FUNCTION_P(FNDECL) \ + (DECL_DECLARES_FUNCTION_P (FNDECL) \ + && DECL_OVERLOADED_OPERATOR_P (FNDECL) == CALL_EXPR \ && LAMBDA_TYPE_P (CP_DECL_CONTEXT (FNDECL))) enum cp_lambda_default_capture_mode_type { @@ -6828,6 +6829,11 @@ extern bool is_lambda_ignored_entity (tree); extern bool lambda_static_thunk_p (tree); 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 finish_lambda_scope (void); +extern tree start_lambda_function (tree fn, tree lambda_expr); +extern void finish_lambda_function (tree body); /* in tree.c */ extern int cp_tree_operand_length (const_tree); diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index 23829b0f18e..d6b80c604c8 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -15097,6 +15097,8 @@ start_preparsed_function (tree decl1, tree attrs, int flags) && !implicit_default_ctor_p (decl1)) cp_ubsan_maybe_initialize_vtbl_ptrs (current_class_ptr); + start_lambda_scope (decl1); + return true; } @@ -15462,6 +15464,8 @@ finish_function (int flags) if (fndecl == NULL_TREE) return error_mark_node; + finish_lambda_scope (); + if (c_dialect_objc ()) objc_finish_function (); @@ -15565,11 +15569,11 @@ finish_function (int flags) /* Lambda closure members are implicitly constexpr if possible. */ if (cxx_dialect >= cxx1z - && LAMBDA_TYPE_P (CP_DECL_CONTEXT (fndecl)) - && (processing_template_decl + && LAMBDA_TYPE_P (CP_DECL_CONTEXT (fndecl))) + DECL_DECLARED_CONSTEXPR_P (fndecl) + = ((processing_template_decl || is_valid_constexpr_fn (fndecl, /*complain*/false)) - && potential_constant_expression (DECL_SAVED_TREE (fndecl))) - DECL_DECLARED_CONSTEXPR_P (fndecl) = true; + && potential_constant_expression (DECL_SAVED_TREE (fndecl))); /* Save constexpr function body before it gets munged by the NRV transformation. */ diff --git a/gcc/cp/init.c b/gcc/cp/init.c index 56a5df87cb3..b01d662fef2 100644 --- a/gcc/cp/init.c +++ b/gcc/cp/init.c @@ -574,13 +574,17 @@ get_nsdmi (tree member, bool in_ctor, tsubst_flags_t complain) inject_this_parameter (DECL_CONTEXT (member), TYPE_UNQUALIFIED); + start_lambda_scope (member); + /* Do deferred instantiation of the NSDMI. */ init = (tsubst_copy_and_build (init, DECL_TI_ARGS (member), complain, member, /*function_p=*/false, /*integral_constant_expression_p=*/false)); init = digest_nsdmi_init (member, init, complain); - + + finish_lambda_scope (); + DECL_INSTANTIATING_NSDMI_P (member) = 0; if (init != error_mark_node) diff --git a/gcc/cp/lambda.c b/gcc/cp/lambda.c index 55d3415676c..4747a727173 100644 --- a/gcc/cp/lambda.c +++ b/gcc/cp/lambda.c @@ -1253,4 +1253,87 @@ is_lambda_ignored_entity (tree val) return false; } +/* Lambdas that appear in variable initializer or default argument scope + get that in their mangling, so we need to record it. We might as well + use the count for function and namespace scopes as well. */ +static GTY(()) tree lambda_scope; +static GTY(()) int lambda_count; +struct GTY(()) tree_int +{ + tree t; + int i; +}; +static GTY(()) vec *lambda_scope_stack; + +void +start_lambda_scope (tree decl) +{ + tree_int ti; + gcc_assert (decl); + /* Once we're inside a function, we ignore variable scope and just push + the function again so that popping works properly. */ + if (current_function_decl && TREE_CODE (decl) == VAR_DECL) + decl = current_function_decl; + ti.t = lambda_scope; + ti.i = lambda_count; + vec_safe_push (lambda_scope_stack, ti); + if (lambda_scope != decl) + { + /* Don't reset the count if we're still in the same function. */ + lambda_scope = decl; + lambda_count = 0; + } +} + +void +record_lambda_scope (tree lambda) +{ + LAMBDA_EXPR_EXTRA_SCOPE (lambda) = lambda_scope; + LAMBDA_EXPR_DISCRIMINATOR (lambda) = lambda_count++; +} + +void +finish_lambda_scope (void) +{ + tree_int *p = &lambda_scope_stack->last (); + if (lambda_scope != p->t) + { + lambda_scope = p->t; + lambda_count = p->i; + } + lambda_scope_stack->pop (); +} + +tree +start_lambda_function (tree fco, tree lambda_expr) +{ + /* Let the front end know that we are going to be defining this + function. */ + start_preparsed_function (fco, + NULL_TREE, + SF_PRE_PARSED | SF_INCLASS_INLINE); + + tree body = begin_function_body (); + + /* Push the proxies for any explicit captures. */ + for (tree cap = LAMBDA_EXPR_CAPTURE_LIST (lambda_expr); cap; + cap = TREE_CHAIN (cap)) + build_capture_proxy (TREE_PURPOSE (cap)); + + return body; +} + +void +finish_lambda_function (tree body) +{ + finish_function_body (body); + + /* Finish the function and generate code for it if necessary. */ + tree fn = finish_function (/*inline*/2); + + /* Only expand if the call op is not a template. */ + if (!DECL_TEMPLATE_INFO (fn)) + expand_or_defer_fn (fn); +} + #include "gt-cp-lambda.h" diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index 9f62b4310e7..d0d71fa1dae 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -9982,57 +9982,6 @@ cp_parser_trait_expr (cp_parser* parser, enum rid keyword) } } -/* Lambdas that appear in variable initializer or default argument scope - get that in their mangling, so we need to record it. We might as well - use the count for function and namespace scopes as well. */ -static GTY(()) tree lambda_scope; -static GTY(()) int lambda_count; -struct GTY(()) tree_int -{ - tree t; - int i; -}; -static GTY(()) vec *lambda_scope_stack; - -static void -start_lambda_scope (tree decl) -{ - tree_int ti; - gcc_assert (decl); - /* Once we're inside a function, we ignore other scopes and just push - the function again so that popping works properly. */ - if (current_function_decl && TREE_CODE (decl) != FUNCTION_DECL) - decl = current_function_decl; - ti.t = lambda_scope; - ti.i = lambda_count; - vec_safe_push (lambda_scope_stack, ti); - if (lambda_scope != decl) - { - /* Don't reset the count if we're still in the same function. */ - lambda_scope = decl; - lambda_count = 0; - } -} - -static void -record_lambda_scope (tree lambda) -{ - LAMBDA_EXPR_EXTRA_SCOPE (lambda) = lambda_scope; - LAMBDA_EXPR_DISCRIMINATOR (lambda) = lambda_count++; -} - -static void -finish_lambda_scope (void) -{ - tree_int *p = &lambda_scope_stack->last (); - if (lambda_scope != p->t) - { - lambda_scope = p->t; - lambda_count = p->i; - } - lambda_scope_stack->pop (); -} - /* Parse a lambda expression. lambda-expression: @@ -10605,29 +10554,14 @@ cp_parser_lambda_body (cp_parser* parser, tree lambda_expr) + ctor_initializer_opt_and_function_body */ { tree fco = lambda_function (lambda_expr); - tree body; + tree body = start_lambda_function (fco, lambda_expr); bool done = false; tree compound_stmt; - tree cap; - - /* Let the front end know that we are going to be defining this - function. */ - start_preparsed_function (fco, - NULL_TREE, - SF_PRE_PARSED | SF_INCLASS_INLINE); - - start_lambda_scope (fco); - body = begin_function_body (); matching_braces braces; if (!braces.require_open (parser)) goto out; - /* Push the proxies for any explicit captures. */ - for (cap = LAMBDA_EXPR_CAPTURE_LIST (lambda_expr); cap; - cap = TREE_CHAIN (cap)) - build_capture_proxy (TREE_PURPOSE (cap)); - compound_stmt = begin_compound_stmt (0); /* 5.1.1.4 of the standard says: @@ -10691,15 +10625,7 @@ cp_parser_lambda_body (cp_parser* parser, tree lambda_expr) finish_compound_stmt (compound_stmt); out: - finish_function_body (body); - finish_lambda_scope (); - - /* Finish the function and generate code for it if necessary. */ - tree fn = finish_function (/*inline*/2); - - /* Only expand if the call op is not a template. */ - if (!DECL_TEMPLATE_INFO (fco)) - expand_or_defer_fn (fn); + finish_lambda_function (body); } restore_omp_privatization_clauses (omp_privatization_save); @@ -26577,8 +26503,6 @@ cp_parser_function_definition_after_declarator (cp_parser* parser, = parser->num_template_parameter_lists; parser->num_template_parameter_lists = 0; - start_lambda_scope (current_function_decl); - /* If the next token is `try', `__transaction_atomic', or `__transaction_relaxed`, then we are looking at either function-try-block or function-transaction-block. Note that all of these include the @@ -26596,8 +26520,6 @@ cp_parser_function_definition_after_declarator (cp_parser* parser, ctor_initializer_p = cp_parser_ctor_initializer_opt_and_function_body (parser, /*in_function_try_block=*/false); - finish_lambda_scope (); - /* Finish the function. */ fn = finish_function ((ctor_initializer_p ? 1 : 0) | (inline_p ? 2 : 0)); diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index e064a11c05b..141b4d7564a 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -220,6 +220,7 @@ static bool complex_alias_template_p (const_tree tmpl); static tree tsubst_attributes (tree, tree, tsubst_flags_t, tree); static tree canonicalize_expr_argument (tree, tsubst_flags_t); static tree make_argument_pack (tree); +static void register_parameter_specializations (tree, tree); /* Make the current scope suitable for access checking when we are processing T. T can be FUNCTION_DECL for instantiated function @@ -1190,6 +1191,19 @@ retrieve_specialization (tree tmpl, tree args, hashval_t hash) if (flag_checking) verify_unstripped_args (args); + /* Lambda functions in templates aren't instantiated normally, but through + tsubst_lambda_expr. */ + if (LAMBDA_FUNCTION_P (tmpl)) + { + bool generic = PRIMARY_TEMPLATE_P (tmpl); + if (TMPL_PARMS_DEPTH (DECL_TEMPLATE_PARMS (tmpl)) > generic) + return NULL_TREE; + + /* But generic lambda functions are instantiated normally, once their + containing context is fully instantiated. */ + gcc_assert (generic); + } + if (optimize_specialization_lookup_p (tmpl)) { /* The template arguments actually apply to the containing @@ -3615,6 +3629,12 @@ find_parameter_packs_r (tree *tp, int *walk_subtrees, void* data) case PARM_DECL: return NULL_TREE; + case DECL_EXPR: + /* Ignore the declaration of a capture proxy for a parameter pack. */ + if (is_capture_proxy (DECL_EXPR_DECL (t))) + *walk_subtrees = 0; + return NULL_TREE; + case RECORD_TYPE: if (TYPE_PTRMEMFUNC_P (t)) return NULL_TREE; @@ -3662,6 +3682,15 @@ find_parameter_packs_r (tree *tp, int *walk_subtrees, void* data) *walk_subtrees = 0; return NULL_TREE; + case LAMBDA_EXPR: + { + tree fn = lambda_function (t); + cp_walk_tree (&DECL_SAVED_TREE (fn), &find_parameter_packs_r, ppd, + ppd->visited); + *walk_subtrees = 0; + return NULL_TREE; + } + case DECLTYPE_TYPE: { /* When traversing a DECLTYPE_TYPE_EXPR, we need to set @@ -3849,6 +3878,10 @@ check_for_bare_parameter_packs (tree t) if (!processing_template_decl || !t || t == error_mark_node) return false; + /* A lambda might use a parameter pack from the containing context. */ + if (current_function_decl && LAMBDA_FUNCTION_P (current_function_decl)) + return false; + if (TREE_CODE (t) == TYPE_DECL) t = TREE_TYPE (t); @@ -12056,6 +12089,8 @@ tsubst_default_argument (tree fn, int parmnum, tree type, tree arg, 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 @@ -12069,6 +12104,8 @@ tsubst_default_argument (tree fn, int parmnum, tree type, tree arg, --function_depth; pop_deferring_access_checks(); + finish_lambda_scope (); + /* Restore the "this" pointer. */ if (cfun) { @@ -12125,415 +12162,466 @@ tsubst_default_arguments (tree fn, tsubst_flags_t complain) complain); } -/* Substitute the ARGS into the T, which is a _DECL. Return the - result of the substitution. Issue error and warning messages under - control of COMPLAIN. */ +/* Subroutine of tsubst_decl for the case when T is a FUNCTION_DECL. */ static tree -tsubst_decl (tree t, tree args, tsubst_flags_t complain) +tsubst_function_decl (tree t, tree args, tsubst_flags_t complain, + tree lambda_fntype) { -#define RETURN(EXP) do { r = (EXP); goto out; } while(0) - location_t saved_loc; - tree r = NULL_TREE; - tree in_decl = t; + tree gen_tmpl, argvec; hashval_t hash = 0; + tree in_decl = t; - /* Set the filename and linenumber to improve error-reporting. */ - saved_loc = input_location; - input_location = DECL_SOURCE_LOCATION (t); + /* Nobody should be tsubst'ing into non-template functions. */ + gcc_assert (DECL_TEMPLATE_INFO (t) != NULL_TREE); - switch (TREE_CODE (t)) + if (TREE_CODE (DECL_TI_TEMPLATE (t)) == TEMPLATE_DECL) { - case TEMPLATE_DECL: - { - /* We can get here when processing a member function template, - member class template, or template template parameter. */ - tree decl = DECL_TEMPLATE_RESULT (t); - tree spec; - tree tmpl_args; - tree full_args; + /* If T is not dependent, just return it. */ + if (!uses_template_parms (DECL_TI_ARGS (t))) + return t; - if (DECL_TEMPLATE_TEMPLATE_PARM_P (t)) - { - /* Template template parameter is treated here. */ - tree new_type = tsubst (TREE_TYPE (t), args, complain, in_decl); - if (new_type == error_mark_node) - r = error_mark_node; - /* If we get a real template back, return it. This can happen in - the context of most_specialized_partial_spec. */ - else if (TREE_CODE (new_type) == TEMPLATE_DECL) - r = new_type; - else - /* The new TEMPLATE_DECL was built in - reduce_template_parm_level. */ - r = TEMPLATE_TEMPLATE_PARM_TEMPLATE_DECL (new_type); - break; - } + /* Calculate the most general template of which R is a + specialization, and the complete set of arguments used to + specialize R. */ + gen_tmpl = most_general_template (DECL_TI_TEMPLATE (t)); + argvec = tsubst_template_args (DECL_TI_ARGS + (DECL_TEMPLATE_RESULT + (DECL_TI_TEMPLATE (t))), + args, complain, in_decl); + if (argvec == error_mark_node) + return error_mark_node; - /* We might already have an instance of this template. - The ARGS are for the surrounding class type, so the - full args contain the tsubst'd args for the context, - plus the innermost args from the template decl. */ - tmpl_args = DECL_CLASS_TEMPLATE_P (t) - ? CLASSTYPE_TI_ARGS (TREE_TYPE (t)) - : DECL_TI_ARGS (DECL_TEMPLATE_RESULT (t)); - /* Because this is a template, the arguments will still be - dependent, even after substitution. If - PROCESSING_TEMPLATE_DECL is not set, the dependency - predicates will short-circuit. */ - ++processing_template_decl; - full_args = tsubst_template_args (tmpl_args, args, - complain, in_decl); - --processing_template_decl; - if (full_args == error_mark_node) - RETURN (error_mark_node); + /* Check to see if we already have this specialization. */ + if (!lambda_fntype) + { + hash = hash_tmpl_and_args (gen_tmpl, argvec); + if (tree spec = retrieve_specialization (gen_tmpl, argvec, hash)) + return spec; + } - /* If this is a default template template argument, - tsubst might not have changed anything. */ - if (full_args == tmpl_args) - RETURN (t); + /* We can see more levels of arguments than parameters if + there was a specialization of a member template, like + this: - hash = hash_tmpl_and_args (t, full_args); - spec = retrieve_specialization (t, full_args, hash); - if (spec != NULL_TREE) - { - r = spec; - break; - } + template struct S { template void f(); } + template <> template void S::f(U); - /* Make a new template decl. It will be similar to the - original, but will record the current template arguments. - We also create a new function declaration, which is just - like the old one, but points to this new template, rather - than the old one. */ - r = copy_decl (t); - gcc_assert (DECL_LANG_SPECIFIC (r) != 0); - DECL_CHAIN (r) = NULL_TREE; + Here, we'll be substituting into the specialization, + because that's where we can find the code we actually + want to generate, but we'll have enough arguments for + the most general template. - // Build new template info linking to the original template decl. - DECL_TEMPLATE_INFO (r) = build_template_info (t, args); + We also deal with the peculiar case: - if (TREE_CODE (decl) == TYPE_DECL - && !TYPE_DECL_ALIAS_P (decl)) - { - tree new_type; - ++processing_template_decl; - new_type = tsubst (TREE_TYPE (t), args, complain, in_decl); - --processing_template_decl; - if (new_type == error_mark_node) - RETURN (error_mark_node); + template struct S { + template friend void f(); + }; + template void f() {} + template S; + template void f(); - TREE_TYPE (r) = new_type; - /* For a partial specialization, we need to keep pointing to - the primary template. */ - if (!DECL_TEMPLATE_SPECIALIZATION (t)) - CLASSTYPE_TI_TEMPLATE (new_type) = r; - DECL_TEMPLATE_RESULT (r) = TYPE_MAIN_DECL (new_type); - DECL_TI_ARGS (r) = CLASSTYPE_TI_ARGS (new_type); - DECL_CONTEXT (r) = TYPE_CONTEXT (new_type); - } - else - { - tree new_decl; - ++processing_template_decl; - new_decl = tsubst (decl, args, complain, in_decl); - --processing_template_decl; - if (new_decl == error_mark_node) - RETURN (error_mark_node); + Here, the ARGS for the instantiation of will be {int, + double}. But, we only need as many ARGS as there are + levels of template parameters in CODE_PATTERN. We are + careful not to get fooled into reducing the ARGS in + situations like: - DECL_TEMPLATE_RESULT (r) = new_decl; - DECL_TI_TEMPLATE (new_decl) = r; - TREE_TYPE (r) = TREE_TYPE (new_decl); - DECL_TI_ARGS (r) = DECL_TI_ARGS (new_decl); - DECL_CONTEXT (r) = DECL_CONTEXT (new_decl); - } + template struct S { template void f(U); } + template template <> void S::f(int) {} - SET_DECL_IMPLICIT_INSTANTIATION (r); - DECL_TEMPLATE_INSTANTIATIONS (r) = NULL_TREE; - DECL_TEMPLATE_SPECIALIZATIONS (r) = NULL_TREE; - - /* The template parameters for this new template are all the - template parameters for the old template, except the - outermost level of parameters. */ - DECL_TEMPLATE_PARMS (r) - = tsubst_template_parms (DECL_TEMPLATE_PARMS (t), args, - complain); - - if (PRIMARY_TEMPLATE_P (t)) - DECL_PRIMARY_TEMPLATE (r) = r; - - if (TREE_CODE (decl) != TYPE_DECL && !VAR_P (decl)) - /* Record this non-type partial instantiation. */ - register_specialization (r, t, - DECL_TI_ARGS (DECL_TEMPLATE_RESULT (r)), - false, hash); - } - break; + which we can spot because the pattern will be a + specialization in this case. */ + int args_depth = TMPL_ARGS_DEPTH (args); + int parms_depth = + TMPL_PARMS_DEPTH (DECL_TEMPLATE_PARMS (DECL_TI_TEMPLATE (t))); - case FUNCTION_DECL: + if (args_depth > parms_depth && !DECL_TEMPLATE_SPECIALIZATION (t)) + args = get_innermost_template_args (args, parms_depth); + } + else + { + /* This special case arises when we have something like this: + + template struct S { + friend void f(int, double); + }; + + Here, the DECL_TI_TEMPLATE for the friend declaration + will be an IDENTIFIER_NODE. We are being called from + tsubst_friend_function, and we want only to create a + new decl (R) with appropriate types so that we can call + determine_specialization. */ + gen_tmpl = NULL_TREE; + argvec = NULL_TREE; + } + + tree closure = (lambda_fntype ? TYPE_METHOD_BASETYPE (lambda_fntype) + : NULL_TREE); + tree ctx = closure ? closure : DECL_CONTEXT (t); + bool member = ctx && TYPE_P (ctx); + + if (member && !closure) + ctx = tsubst_aggr_type (ctx, args, + complain, t, /*entering_scope=*/1); + + tree type = (lambda_fntype ? lambda_fntype + : tsubst (TREE_TYPE (t), args, + complain | tf_fndecl_type, in_decl)); + if (type == error_mark_node) + return error_mark_node; + + /* If we hit excessive deduction depth, the type is bogus even if + it isn't error_mark_node, so don't build a decl. */ + if (excessive_deduction_depth) + return error_mark_node; + + /* We do NOT check for matching decls pushed separately at this + point, as they may not represent instantiations of this + template, and in any case are considered separate under the + discrete model. */ + tree r = copy_decl (t); + DECL_USE_TEMPLATE (r) = 0; + TREE_TYPE (r) = type; + /* Clear out the mangled name and RTL for the instantiation. */ + SET_DECL_ASSEMBLER_NAME (r, NULL_TREE); + SET_DECL_RTL (r, NULL); + /* Leave DECL_INITIAL set on deleted instantiations. */ + if (!DECL_DELETED_FN (r)) + DECL_INITIAL (r) = NULL_TREE; + DECL_CONTEXT (r) = ctx; + + /* OpenMP UDRs have the only argument a reference to the declared + type. We want to diagnose if the declared type is a reference, + which is invalid, but as references to references are usually + quietly merged, diagnose it here. */ + if (DECL_OMP_DECLARE_REDUCTION_P (t)) + { + tree argtype + = TREE_TYPE (TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (t)))); + argtype = tsubst (argtype, args, complain, in_decl); + if (TREE_CODE (argtype) == REFERENCE_TYPE) + error_at (DECL_SOURCE_LOCATION (t), + "reference type %qT in " + "%<#pragma omp declare reduction%>", argtype); + if (strchr (IDENTIFIER_POINTER (DECL_NAME (t)), '~') == NULL) + DECL_NAME (r) = omp_reduction_id (ERROR_MARK, DECL_NAME (t), + argtype); + } + + if (member && DECL_CONV_FN_P (r)) + /* Type-conversion operator. Reconstruct the name, in + case it's the name of one of the template's parameters. */ + DECL_NAME (r) = make_conv_op_name (TREE_TYPE (type)); + + tree parms = DECL_ARGUMENTS (t); + if (closure) + parms = DECL_CHAIN (parms); + parms = tsubst (parms, args, complain, t); + for (tree parm = parms; parm; parm = DECL_CHAIN (parm)) + DECL_CONTEXT (parm) = r; + if (closure) + { + tree tparm = build_this_parm (r, closure, type_memfn_quals (type)); + DECL_CHAIN (tparm) = parms; + parms = tparm; + } + DECL_ARGUMENTS (r) = parms; + DECL_RESULT (r) = NULL_TREE; + + TREE_STATIC (r) = 0; + TREE_PUBLIC (r) = TREE_PUBLIC (t); + DECL_EXTERNAL (r) = 1; + /* If this is an instantiation of a function with internal + linkage, we already know what object file linkage will be + assigned to the instantiation. */ + DECL_INTERFACE_KNOWN (r) = !TREE_PUBLIC (r); + DECL_DEFER_OUTPUT (r) = 0; + DECL_CHAIN (r) = NULL_TREE; + DECL_PENDING_INLINE_INFO (r) = 0; + DECL_PENDING_INLINE_P (r) = 0; + DECL_SAVED_TREE (r) = NULL_TREE; + DECL_STRUCT_FUNCTION (r) = NULL; + TREE_USED (r) = 0; + /* We'll re-clone as appropriate in instantiate_template. */ + DECL_CLONED_FUNCTION (r) = NULL_TREE; + + /* If we aren't complaining now, return on error before we register + the specialization so that we'll complain eventually. */ + if ((complain & tf_error) == 0 + && IDENTIFIER_ANY_OP_P (DECL_NAME (r)) + && !grok_op_properties (r, /*complain=*/false)) + return error_mark_node; + + /* When instantiating a constrained member, substitute + into the constraints to create a new constraint. */ + if (tree ci = get_constraints (t)) + if (member) { - tree gen_tmpl, argvec; + ci = tsubst_constraint_info (ci, argvec, complain, NULL_TREE); + set_constraints (r, ci); + } - /* Nobody should be tsubst'ing into non-template functions. */ - gcc_assert (DECL_TEMPLATE_INFO (t) != NULL_TREE); + /* Set up the DECL_TEMPLATE_INFO for R. There's no need to do + this in the special friend case mentioned above where + GEN_TMPL is NULL. */ + if (gen_tmpl && !closure) + { + DECL_TEMPLATE_INFO (r) + = build_template_info (gen_tmpl, argvec); + SET_DECL_IMPLICIT_INSTANTIATION (r); + + tree new_r + = register_specialization (r, gen_tmpl, argvec, false, hash); + if (new_r != r) + /* We instantiated this while substituting into + the type earlier (template/friend54.C). */ + return new_r; + + /* We're not supposed to instantiate default arguments + until they are called, for a template. But, for a + declaration like: + + template void f () + { extern void g(int i = T()); } + + we should do the substitution when the template is + instantiated. We handle the member function case in + instantiate_class_template since the default arguments + might refer to other members of the class. */ + if (!member + && !PRIMARY_TEMPLATE_P (gen_tmpl) + && !uses_template_parms (argvec)) + tsubst_default_arguments (r, complain); + } + else + DECL_TEMPLATE_INFO (r) = NULL_TREE; - if (TREE_CODE (DECL_TI_TEMPLATE (t)) == TEMPLATE_DECL) - { - /* If T is not dependent, just return it. */ - if (!uses_template_parms (DECL_TI_ARGS (t))) - RETURN (t); - - /* Calculate the most general template of which R is a - specialization, and the complete set of arguments used to - specialize R. */ - gen_tmpl = most_general_template (DECL_TI_TEMPLATE (t)); - argvec = tsubst_template_args (DECL_TI_ARGS - (DECL_TEMPLATE_RESULT - (DECL_TI_TEMPLATE (t))), - args, complain, in_decl); - if (argvec == error_mark_node) - RETURN (error_mark_node); + /* Copy the list of befriending classes. */ + for (tree *friends = &DECL_BEFRIENDING_CLASSES (r); + *friends; + friends = &TREE_CHAIN (*friends)) + { + *friends = copy_node (*friends); + TREE_VALUE (*friends) + = tsubst (TREE_VALUE (*friends), args, complain, in_decl); + } - /* Check to see if we already have this specialization. */ - hash = hash_tmpl_and_args (gen_tmpl, argvec); - if (tree spec = retrieve_specialization (gen_tmpl, argvec, hash)) - { - r = spec; - break; - } + if (DECL_CONSTRUCTOR_P (r) || DECL_DESTRUCTOR_P (r)) + { + maybe_retrofit_in_chrg (r); + if (DECL_CONSTRUCTOR_P (r) && !grok_ctor_properties (ctx, r)) + return error_mark_node; + /* If this is an instantiation of a member template, clone it. + If it isn't, that'll be handled by + clone_constructors_and_destructors. */ + if (PRIMARY_TEMPLATE_P (gen_tmpl)) + clone_function_decl (r, /*update_methods=*/false); + } + else if ((complain & tf_error) != 0 + && IDENTIFIER_ANY_OP_P (DECL_NAME (r)) + && !grok_op_properties (r, /*complain=*/true)) + return error_mark_node; - /* We can see more levels of arguments than parameters if - there was a specialization of a member template, like - this: + if (DECL_FRIEND_P (t) && DECL_FRIEND_CONTEXT (t)) + SET_DECL_FRIEND_CONTEXT (r, + tsubst (DECL_FRIEND_CONTEXT (t), + args, complain, in_decl)); - template struct S { template void f(); } - template <> template void S::f(U); + /* Possibly limit visibility based on template args. */ + DECL_VISIBILITY (r) = VISIBILITY_DEFAULT; + if (DECL_VISIBILITY_SPECIFIED (t)) + { + DECL_VISIBILITY_SPECIFIED (r) = 0; + DECL_ATTRIBUTES (r) + = remove_attribute ("visibility", DECL_ATTRIBUTES (r)); + } + determine_visibility (r); + if (DECL_DEFAULTED_OUTSIDE_CLASS_P (r) + && !processing_template_decl) + defaulted_late_check (r); - Here, we'll be substituting into the specialization, - because that's where we can find the code we actually - want to generate, but we'll have enough arguments for - the most general template. + apply_late_template_attributes (&r, DECL_ATTRIBUTES (r), 0, + args, complain, in_decl); + return r; +} - We also deal with the peculiar case: +/* Subroutine of tsubst_decl for the case when T is a TEMPLATE_DECL. */ - template struct S { - template friend void f(); - }; - template void f() {} - template S; - template void f(); +static tree +tsubst_template_decl (tree t, tree args, tsubst_flags_t complain, + tree lambda_fntype) +{ + /* We can get here when processing a member function template, + member class template, or template template parameter. */ + tree decl = DECL_TEMPLATE_RESULT (t); + tree in_decl = t; + tree spec; + tree tmpl_args; + tree full_args; + tree r; + hashval_t hash = 0; - Here, the ARGS for the instantiation of will be {int, - double}. But, we only need as many ARGS as there are - levels of template parameters in CODE_PATTERN. We are - careful not to get fooled into reducing the ARGS in - situations like: + if (DECL_TEMPLATE_TEMPLATE_PARM_P (t)) + { + /* Template template parameter is treated here. */ + tree new_type = tsubst (TREE_TYPE (t), args, complain, in_decl); + if (new_type == error_mark_node) + r = error_mark_node; + /* If we get a real template back, return it. This can happen in + the context of most_specialized_partial_spec. */ + else if (TREE_CODE (new_type) == TEMPLATE_DECL) + r = new_type; + else + /* The new TEMPLATE_DECL was built in + reduce_template_parm_level. */ + r = TEMPLATE_TEMPLATE_PARM_TEMPLATE_DECL (new_type); + return r; + } - template struct S { template void f(U); } - template template <> void S::f(int) {} + if (!lambda_fntype) + { + /* We might already have an instance of this template. + The ARGS are for the surrounding class type, so the + full args contain the tsubst'd args for the context, + plus the innermost args from the template decl. */ + tmpl_args = DECL_CLASS_TEMPLATE_P (t) + ? CLASSTYPE_TI_ARGS (TREE_TYPE (t)) + : DECL_TI_ARGS (DECL_TEMPLATE_RESULT (t)); + /* Because this is a template, the arguments will still be + dependent, even after substitution. If + PROCESSING_TEMPLATE_DECL is not set, the dependency + predicates will short-circuit. */ + ++processing_template_decl; + full_args = tsubst_template_args (tmpl_args, args, + complain, in_decl); + --processing_template_decl; + if (full_args == error_mark_node) + return error_mark_node; - which we can spot because the pattern will be a - specialization in this case. */ - int args_depth = TMPL_ARGS_DEPTH (args); - int parms_depth = - TMPL_PARMS_DEPTH (DECL_TEMPLATE_PARMS (DECL_TI_TEMPLATE (t))); + /* If this is a default template template argument, + tsubst might not have changed anything. */ + if (full_args == tmpl_args) + return t; - if (args_depth > parms_depth && !DECL_TEMPLATE_SPECIALIZATION (t)) - args = get_innermost_template_args (args, parms_depth); - } - else - { - /* This special case arises when we have something like this: - - template struct S { - friend void f(int, double); - }; - - Here, the DECL_TI_TEMPLATE for the friend declaration - will be an IDENTIFIER_NODE. We are being called from - tsubst_friend_function, and we want only to create a - new decl (R) with appropriate types so that we can call - determine_specialization. */ - gen_tmpl = NULL_TREE; - argvec = NULL_TREE; - } + hash = hash_tmpl_and_args (t, full_args); + spec = retrieve_specialization (t, full_args, hash); + if (spec != NULL_TREE) + return spec; + } - tree ctx = DECL_CONTEXT (t); - bool member = ctx && TYPE_P (ctx); + /* Make a new template decl. It will be similar to the + original, but will record the current template arguments. + We also create a new function declaration, which is just + like the old one, but points to this new template, rather + than the old one. */ + r = copy_decl (t); + gcc_assert (DECL_LANG_SPECIFIC (r) != 0); + DECL_CHAIN (r) = NULL_TREE; - if (member) - ctx = tsubst_aggr_type (ctx, args, - complain, t, /*entering_scope=*/1); + // Build new template info linking to the original template decl. + if (!lambda_fntype) + { + DECL_TEMPLATE_INFO (r) = build_template_info (t, args); + SET_DECL_IMPLICIT_INSTANTIATION (r); + } + else + DECL_TEMPLATE_INFO (r) = NULL_TREE; - tree type = tsubst (TREE_TYPE (t), args, - complain | tf_fndecl_type, in_decl); - if (type == error_mark_node) - RETURN (error_mark_node); + /* The template parameters for this new template are all the + template parameters for the old template, except the + outermost level of parameters. */ + DECL_TEMPLATE_PARMS (r) + = tsubst_template_parms (DECL_TEMPLATE_PARMS (t), args, + complain); - /* If we hit excessive deduction depth, the type is bogus even if - it isn't error_mark_node, so don't build a decl. */ - if (excessive_deduction_depth) - RETURN (error_mark_node); + if (TREE_CODE (decl) == TYPE_DECL + && !TYPE_DECL_ALIAS_P (decl)) + { + tree new_type; + ++processing_template_decl; + new_type = tsubst (TREE_TYPE (t), args, complain, in_decl); + --processing_template_decl; + if (new_type == error_mark_node) + return error_mark_node; - /* We do NOT check for matching decls pushed separately at this - point, as they may not represent instantiations of this - template, and in any case are considered separate under the - discrete model. */ - r = copy_decl (t); - DECL_USE_TEMPLATE (r) = 0; - TREE_TYPE (r) = type; - /* Clear out the mangled name and RTL for the instantiation. */ - SET_DECL_ASSEMBLER_NAME (r, NULL_TREE); - SET_DECL_RTL (r, NULL); - /* Leave DECL_INITIAL set on deleted instantiations. */ - if (!DECL_DELETED_FN (r)) - DECL_INITIAL (r) = NULL_TREE; - DECL_CONTEXT (r) = ctx; + TREE_TYPE (r) = new_type; + /* For a partial specialization, we need to keep pointing to + the primary template. */ + if (!DECL_TEMPLATE_SPECIALIZATION (t)) + CLASSTYPE_TI_TEMPLATE (new_type) = r; + DECL_TEMPLATE_RESULT (r) = TYPE_MAIN_DECL (new_type); + DECL_TI_ARGS (r) = CLASSTYPE_TI_ARGS (new_type); + DECL_CONTEXT (r) = TYPE_CONTEXT (new_type); + } + else + { + tree new_decl; + ++processing_template_decl; + if (TREE_CODE (decl) == FUNCTION_DECL) + new_decl = tsubst_function_decl (decl, args, complain, lambda_fntype); + else + new_decl = tsubst (decl, args, complain, in_decl); + --processing_template_decl; + if (new_decl == error_mark_node) + return error_mark_node; - /* OpenMP UDRs have the only argument a reference to the declared - type. We want to diagnose if the declared type is a reference, - which is invalid, but as references to references are usually - quietly merged, diagnose it here. */ - if (DECL_OMP_DECLARE_REDUCTION_P (t)) - { - tree argtype - = TREE_TYPE (TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (t)))); - argtype = tsubst (argtype, args, complain, in_decl); - if (TREE_CODE (argtype) == REFERENCE_TYPE) - error_at (DECL_SOURCE_LOCATION (t), - "reference type %qT in " - "%<#pragma omp declare reduction%>", argtype); - if (strchr (IDENTIFIER_POINTER (DECL_NAME (t)), '~') == NULL) - DECL_NAME (r) = omp_reduction_id (ERROR_MARK, DECL_NAME (t), - argtype); - } + DECL_TEMPLATE_RESULT (r) = new_decl; + TREE_TYPE (r) = TREE_TYPE (new_decl); + DECL_CONTEXT (r) = DECL_CONTEXT (new_decl); + if (lambda_fntype) + { + tree args = template_parms_to_args (DECL_TEMPLATE_PARMS (r)); + DECL_TEMPLATE_INFO (new_decl) = build_template_info (r, args); + } + else + { + DECL_TI_TEMPLATE (new_decl) = r; + DECL_TI_ARGS (r) = DECL_TI_ARGS (new_decl); + } + } - if (member && DECL_CONV_FN_P (r)) - /* Type-conversion operator. Reconstruct the name, in - case it's the name of one of the template's parameters. */ - DECL_NAME (r) = make_conv_op_name (TREE_TYPE (type)); - - DECL_ARGUMENTS (r) = tsubst (DECL_ARGUMENTS (t), args, - complain, t); - for (tree parm = DECL_ARGUMENTS (r); parm; parm = DECL_CHAIN (parm)) - DECL_CONTEXT (parm) = r; - DECL_RESULT (r) = NULL_TREE; - - TREE_STATIC (r) = 0; - TREE_PUBLIC (r) = TREE_PUBLIC (t); - DECL_EXTERNAL (r) = 1; - /* If this is an instantiation of a function with internal - linkage, we already know what object file linkage will be - assigned to the instantiation. */ - DECL_INTERFACE_KNOWN (r) = !TREE_PUBLIC (r); - DECL_DEFER_OUTPUT (r) = 0; - DECL_CHAIN (r) = NULL_TREE; - DECL_PENDING_INLINE_INFO (r) = 0; - DECL_PENDING_INLINE_P (r) = 0; - DECL_SAVED_TREE (r) = NULL_TREE; - DECL_STRUCT_FUNCTION (r) = NULL; - TREE_USED (r) = 0; - /* We'll re-clone as appropriate in instantiate_template. */ - DECL_CLONED_FUNCTION (r) = NULL_TREE; - - /* If we aren't complaining now, return on error before we register - the specialization so that we'll complain eventually. */ - if ((complain & tf_error) == 0 - && IDENTIFIER_ANY_OP_P (DECL_NAME (r)) - && !grok_op_properties (r, /*complain=*/false)) - RETURN (error_mark_node); + DECL_TEMPLATE_INSTANTIATIONS (r) = NULL_TREE; + DECL_TEMPLATE_SPECIALIZATIONS (r) = NULL_TREE; - /* When instantiating a constrained member, substitute - into the constraints to create a new constraint. */ - if (tree ci = get_constraints (t)) - if (member) - { - ci = tsubst_constraint_info (ci, argvec, complain, NULL_TREE); - set_constraints (r, ci); - } + if (PRIMARY_TEMPLATE_P (t)) + DECL_PRIMARY_TEMPLATE (r) = r; - /* Set up the DECL_TEMPLATE_INFO for R. There's no need to do - this in the special friend case mentioned above where - GEN_TMPL is NULL. */ - if (gen_tmpl) - { - DECL_TEMPLATE_INFO (r) - = build_template_info (gen_tmpl, argvec); - SET_DECL_IMPLICIT_INSTANTIATION (r); + if (TREE_CODE (decl) != TYPE_DECL && !VAR_P (decl) + && !lambda_fntype) + /* Record this non-type partial instantiation. */ + register_specialization (r, t, + DECL_TI_ARGS (DECL_TEMPLATE_RESULT (r)), + false, hash); - tree new_r - = register_specialization (r, gen_tmpl, argvec, false, hash); - if (new_r != r) - /* We instantiated this while substituting into - the type earlier (template/friend54.C). */ - RETURN (new_r); - - /* We're not supposed to instantiate default arguments - until they are called, for a template. But, for a - declaration like: - - template void f () - { extern void g(int i = T()); } - - we should do the substitution when the template is - instantiated. We handle the member function case in - instantiate_class_template since the default arguments - might refer to other members of the class. */ - if (!member - && !PRIMARY_TEMPLATE_P (gen_tmpl) - && !uses_template_parms (argvec)) - tsubst_default_arguments (r, complain); - } - else - DECL_TEMPLATE_INFO (r) = NULL_TREE; + return r; +} - /* Copy the list of befriending classes. */ - for (tree *friends = &DECL_BEFRIENDING_CLASSES (r); - *friends; - friends = &TREE_CHAIN (*friends)) - { - *friends = copy_node (*friends); - TREE_VALUE (*friends) - = tsubst (TREE_VALUE (*friends), args, complain, in_decl); - } +/* Substitute the ARGS into the T, which is a _DECL. Return the + result of the substitution. Issue error and warning messages under + control of COMPLAIN. */ - if (DECL_CONSTRUCTOR_P (r) || DECL_DESTRUCTOR_P (r)) - { - maybe_retrofit_in_chrg (r); - if (DECL_CONSTRUCTOR_P (r) && !grok_ctor_properties (ctx, r)) - RETURN (error_mark_node); - /* If this is an instantiation of a member template, clone it. - If it isn't, that'll be handled by - clone_constructors_and_destructors. */ - if (PRIMARY_TEMPLATE_P (gen_tmpl)) - clone_function_decl (r, /*update_methods=*/false); - } - else if ((complain & tf_error) != 0 - && IDENTIFIER_ANY_OP_P (DECL_NAME (r)) - && !grok_op_properties (r, /*complain=*/true)) - RETURN (error_mark_node); +static tree +tsubst_decl (tree t, tree args, tsubst_flags_t complain) +{ +#define RETURN(EXP) do { r = (EXP); goto out; } while(0) + location_t saved_loc; + tree r = NULL_TREE; + tree in_decl = t; + hashval_t hash = 0; - if (DECL_FRIEND_P (t) && DECL_FRIEND_CONTEXT (t)) - SET_DECL_FRIEND_CONTEXT (r, - tsubst (DECL_FRIEND_CONTEXT (t), - args, complain, in_decl)); + /* Set the filename and linenumber to improve error-reporting. */ + saved_loc = input_location; + input_location = DECL_SOURCE_LOCATION (t); - /* Possibly limit visibility based on template args. */ - DECL_VISIBILITY (r) = VISIBILITY_DEFAULT; - if (DECL_VISIBILITY_SPECIFIED (t)) - { - DECL_VISIBILITY_SPECIFIED (r) = 0; - DECL_ATTRIBUTES (r) - = remove_attribute ("visibility", DECL_ATTRIBUTES (r)); - } - determine_visibility (r); - if (DECL_DEFAULTED_OUTSIDE_CLASS_P (r) - && !processing_template_decl) - defaulted_late_check (r); + switch (TREE_CODE (t)) + { + case TEMPLATE_DECL: + r = tsubst_template_decl (t, args, complain, /*lambda*/NULL_TREE); + break; - apply_late_template_attributes (&r, DECL_ATTRIBUTES (r), 0, - args, complain, in_decl); - } + case FUNCTION_DECL: + r = tsubst_function_decl (t, args, complain, /*lambda*/NULL_TREE); break; case PARM_DECL: @@ -15862,6 +15950,18 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl, instantiate the elements directly as needed. */ break; } + else if (is_capture_proxy (decl) + && !DECL_TEMPLATE_INSTANTIATION (current_function_decl)) + { + /* We're in tsubst_lambda_expr, we've already inserted new capture + proxies, and uses will find them with lookup_name. */ + break; + } + else if (DECL_IMPLICIT_TYPEDEF_P (decl) + && LAMBDA_TYPE_P (TREE_TYPE (decl))) + /* Don't copy the old closure; we'll create a new one in + tsubst_lambda_expr. */ + break; else { init = DECL_INITIAL (decl); @@ -16659,6 +16759,149 @@ tsubst_non_call_postfix_expression (tree t, tree args, return t; } +/* T is a LAMBDA_EXPR. Generate a new LAMBDA_EXPR for the current + instantiation context. Instantiating a pack expansion containing a lambda + might result in multiple lambdas all based on the same lambda in the + template. */ + +tree +tsubst_lambda_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl) +{ + tree oldfn = lambda_function (t); + in_decl = oldfn; + + tree r = build_lambda_expr (); + + LAMBDA_EXPR_LOCATION (r) + = LAMBDA_EXPR_LOCATION (t); + LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (r) + = LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (t); + 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; + else + record_lambda_scope (r); + + gcc_assert (LAMBDA_EXPR_THIS_CAPTURE (t) == NULL_TREE + && LAMBDA_EXPR_PENDING_PROXIES (t) == NULL); + + for (tree cap = LAMBDA_EXPR_CAPTURE_LIST (t); cap; + cap = TREE_CHAIN (cap)) + { + tree field = TREE_PURPOSE (cap); + if (PACK_EXPANSION_P (field)) + field = PACK_EXPANSION_PATTERN (field); + field = tsubst_decl (field, args, complain); + + if (field == error_mark_node) + return error_mark_node; + + tree init = TREE_VALUE (cap); + if (PACK_EXPANSION_P (init)) + init = tsubst_pack_expansion (init, args, complain, in_decl); + else + init = tsubst_copy_and_build (init, args, complain, in_decl, + /*fn*/false, /*constexpr*/false); + + if (TREE_CODE (field) == TREE_VEC) + { + int len = TREE_VEC_LENGTH (field); + gcc_assert (TREE_CODE (init) == TREE_VEC + && TREE_VEC_LENGTH (init) == len); + for (int i = 0; i < len; ++i) + LAMBDA_EXPR_CAPTURE_LIST (r) + = tree_cons (TREE_VEC_ELT (field, i), + TREE_VEC_ELT (init, i), + LAMBDA_EXPR_CAPTURE_LIST (r)); + } + else + { + LAMBDA_EXPR_CAPTURE_LIST (r) + = tree_cons (field, init, LAMBDA_EXPR_CAPTURE_LIST (r)); + + if (id_equal (DECL_NAME (field), "__this")) + LAMBDA_EXPR_THIS_CAPTURE (r) = field; + } + } + + tree type = begin_lambda_type (r); + + /* Do this again now that LAMBDA_EXPR_EXTRA_SCOPE is set. */ + determine_visibility (TYPE_NAME (type)); + + register_capture_members (LAMBDA_EXPR_CAPTURE_LIST (r)); + + tree oldtmpl = (generic_lambda_fn_p (oldfn) + ? DECL_TI_TEMPLATE (oldfn) + : NULL_TREE); + + tree fntype = static_fn_type (oldfn); + if (oldtmpl) + ++processing_template_decl; + fntype = tsubst (fntype, args, complain, in_decl); + if (oldtmpl) + --processing_template_decl; + + if (fntype == error_mark_node) + r = error_mark_node; + else + { + /* Fix the type of 'this'. */ + fntype = build_memfn_type (fntype, type, + type_memfn_quals (fntype), + type_memfn_rqual (fntype)); + tree fn, tmpl; + if (oldtmpl) + { + tmpl = tsubst_template_decl (oldtmpl, args, complain, fntype); + fn = DECL_TEMPLATE_RESULT (tmpl); + finish_member_declaration (tmpl); + } + else + { + tmpl = NULL_TREE; + fn = tsubst_function_decl (oldfn, args, complain, fntype); + finish_member_declaration (fn); + } + + /* Let finish_function set this. */ + DECL_DECLARED_CONSTEXPR_P (fn) = false; + + bool nested = cfun; + if (nested) + push_function_context (); + + tree body = start_lambda_function (fn, r); + + local_specialization_stack s (lss_copy); + + register_parameter_specializations (oldfn, fn); + + tsubst_expr (DECL_SAVED_TREE (oldfn), args, complain, r, + /*constexpr*/false); + + finish_lambda_function (body); + + if (nested) + pop_function_context (); + + /* The capture list was built up in reverse order; fix that now. */ + LAMBDA_EXPR_CAPTURE_LIST (r) + = nreverse (LAMBDA_EXPR_CAPTURE_LIST (r)); + + LAMBDA_EXPR_THIS_CAPTURE (r) = NULL_TREE; + + maybe_add_lambda_conv_op (type); + } + + finish_struct (type, /*attr*/NULL_TREE); + + insert_pending_capture_proxies (); + + return r; +} + /* Like tsubst but deals with expressions and performs semantic analysis. FUNCTION_P is true if T is the "F" in "F (ARGS)". */ @@ -17861,7 +18104,7 @@ tsubst_copy_and_build (tree t, else if (outer_automatic_var_p (r)) { r = process_outer_var_ref (r, complain); - if (is_capture_proxy (r)) + if (is_capture_proxy (r) && !DECL_PACK_P (t)) register_local_specialization (r, t); } @@ -17929,59 +18172,7 @@ tsubst_copy_and_build (tree t, case LAMBDA_EXPR: { - tree r = build_lambda_expr (); - - tree type = tsubst (LAMBDA_EXPR_CLOSURE (t), args, complain, NULL_TREE); - LAMBDA_EXPR_CLOSURE (r) = type; - CLASSTYPE_LAMBDA_EXPR (type) = r; - - LAMBDA_EXPR_LOCATION (r) - = LAMBDA_EXPR_LOCATION (t); - LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (r) - = LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (t); - LAMBDA_EXPR_MUTABLE_P (r) = LAMBDA_EXPR_MUTABLE_P (t); - LAMBDA_EXPR_DISCRIMINATOR (r) - = (LAMBDA_EXPR_DISCRIMINATOR (t)); - tree scope = LAMBDA_EXPR_EXTRA_SCOPE (t); - if (!scope) - /* No substitution needed. */; - else if (VAR_OR_FUNCTION_DECL_P (scope)) - /* For a function or variable scope, we want to use tsubst so that we - don't complain about referring to an auto before deduction. */ - scope = tsubst (scope, args, complain, in_decl); - else if (TREE_CODE (scope) == PARM_DECL) - { - /* Look up the parameter we want directly, as tsubst_copy - doesn't do what we need. */ - tree fn = tsubst (DECL_CONTEXT (scope), args, complain, in_decl); - tree parm = FUNCTION_FIRST_USER_PARM (fn); - while (DECL_PARM_INDEX (parm) != DECL_PARM_INDEX (scope)) - parm = DECL_CHAIN (parm); - scope = parm; - /* FIXME Work around the parm not having DECL_CONTEXT set. */ - if (DECL_CONTEXT (scope) == NULL_TREE) - DECL_CONTEXT (scope) = fn; - } - else if (TREE_CODE (scope) == FIELD_DECL) - /* For a field, use tsubst_copy so that we look up the existing field - rather than build a new one. */ - scope = RECUR (scope); - else - gcc_unreachable (); - LAMBDA_EXPR_EXTRA_SCOPE (r) = scope; - - gcc_assert (LAMBDA_EXPR_THIS_CAPTURE (t) == NULL_TREE - && LAMBDA_EXPR_PENDING_PROXIES (t) == NULL); - - /* Do this again now that LAMBDA_EXPR_EXTRA_SCOPE is set. */ - determine_visibility (TYPE_NAME (type)); - /* Now that we know visibility, instantiate the type so we have a - declaration of the op() for later calls to lambda_function. */ - complete_type (type); - - LAMBDA_EXPR_THIS_CAPTURE (r) = NULL_TREE; - - insert_pending_capture_proxies (); + tree r = tsubst_lambda_expr (t, args, complain, in_decl); RETURN (build_lambda_object (r)); } @@ -22434,10 +22625,12 @@ regenerate_decl_from_template (tree decl, tree tmpl, tree args) } else if (VAR_P (decl)) { + start_lambda_scope (decl); DECL_INITIAL (decl) = tsubst_expr (DECL_INITIAL (code_pattern), args, tf_error, DECL_TI_TEMPLATE (decl), /*integral_constant_expression_p=*/false); + finish_lambda_scope (); if (VAR_HAD_UNKNOWN_BOUND (decl)) TREE_TYPE (decl) = tsubst (TREE_TYPE (code_pattern), args, tf_error, DECL_TI_TEMPLATE (decl)); @@ -22605,6 +22798,38 @@ maybe_instantiate_noexcept (tree fn, tsubst_flags_t complain) return true; } +/* We're starting to process the function INST, an instantiation of PATTERN; + add their parameters to local_specializations. */ + +static void +register_parameter_specializations (tree pattern, tree inst) +{ + tree tmpl_parm = DECL_ARGUMENTS (pattern); + tree spec_parm = DECL_ARGUMENTS (inst); + if (DECL_NONSTATIC_MEMBER_FUNCTION_P (inst)) + { + register_local_specialization (spec_parm, tmpl_parm); + spec_parm = skip_artificial_parms_for (inst, spec_parm); + tmpl_parm = skip_artificial_parms_for (pattern, tmpl_parm); + } + for (; tmpl_parm; tmpl_parm = DECL_CHAIN (tmpl_parm)) + { + if (!DECL_PACK_P (tmpl_parm)) + { + register_local_specialization (spec_parm, tmpl_parm); + spec_parm = DECL_CHAIN (spec_parm); + } + else + { + /* Register the (value) argument pack as a specialization of + TMPL_PARM, then move on. */ + tree argpack = extract_fnparm_pack (tmpl_parm, &spec_parm); + register_local_specialization (argpack, tmpl_parm); + } + } + gcc_assert (!spec_parm); +} + /* Produce the definition of D, a _DECL generated from a template. If DEFER_OK is true, then we don't have to actually do the instantiation now; we just have to do it sometime. Normally it is @@ -22939,10 +23164,7 @@ instantiate_decl (tree d, bool defer_ok, bool expl_inst_class_mem_p) else if (TREE_CODE (d) == FUNCTION_DECL) { hash_map *saved_local_specializations; - tree tmpl_parm; - tree spec_parm; tree block = NULL_TREE; - tree lambda_ctx = NULL_TREE; /* Save away the current list, in case we are instantiating one template from within the body of another. */ @@ -22956,23 +23178,7 @@ instantiate_decl (tree d, bool defer_ok, bool expl_inst_class_mem_p) && TREE_CODE (DECL_CONTEXT (code_pattern)) == FUNCTION_DECL) block = push_stmt_list (); else - { - if (push_to_top && LAMBDA_FUNCTION_P (d)) - { - /* When instantiating a lambda's templated function - operator, we need to push the non-lambda class scope - of the lambda itself so that the nested function - stack is sufficiently correct to deal with this - capture. */ - lambda_ctx = DECL_CONTEXT (d); - do - lambda_ctx = decl_type_context (TYPE_NAME (lambda_ctx)); - while (lambda_ctx && LAMBDA_TYPE_P (lambda_ctx)); - if (lambda_ctx) - push_nested_class (lambda_ctx); - } - start_preparsed_function (d, NULL_TREE, SF_PRE_PARSED); - } + start_preparsed_function (d, NULL_TREE, SF_PRE_PARSED); /* Some typedefs referenced from within the template code need to be access checked at template instantiation time, i.e now. These @@ -22982,30 +23188,7 @@ instantiate_decl (tree d, bool defer_ok, bool expl_inst_class_mem_p) args); /* Create substitution entries for the parameters. */ - tmpl_parm = DECL_ARGUMENTS (code_pattern); - spec_parm = DECL_ARGUMENTS (d); - if (DECL_NONSTATIC_MEMBER_FUNCTION_P (d)) - { - register_local_specialization (spec_parm, tmpl_parm); - spec_parm = skip_artificial_parms_for (d, spec_parm); - tmpl_parm = skip_artificial_parms_for (code_pattern, tmpl_parm); - } - for (; tmpl_parm; tmpl_parm = DECL_CHAIN (tmpl_parm)) - { - if (!DECL_PACK_P (tmpl_parm)) - { - register_local_specialization (spec_parm, tmpl_parm); - spec_parm = DECL_CHAIN (spec_parm); - } - else - { - /* Register the (value) argument pack as a specialization of - TMPL_PARM, then move on. */ - tree argpack = extract_fnparm_pack (tmpl_parm, &spec_parm); - register_local_specialization (argpack, tmpl_parm); - } - } - gcc_assert (!spec_parm); + register_parameter_specializations (code_pattern, d); /* Substitute into the body of the function. */ if (DECL_OMP_DECLARE_REDUCTION_P (code_pattern)) @@ -23040,8 +23223,6 @@ instantiate_decl (tree d, bool defer_ok, bool expl_inst_class_mem_p) d = finish_function (0); expand_or_defer_fn (d); } - if (lambda_ctx) - pop_nested_class (); if (DECL_OMP_DECLARE_REDUCTION_P (code_pattern)) cp_check_omp_declare_reduction (d); diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c index fe118cd97af..8f2822107d6 100644 --- a/gcc/cp/semantics.c +++ b/gcc/cp/semantics.c @@ -3301,40 +3301,56 @@ process_outer_var_ref (tree decl, tsubst_flags_t complain) if (!mark_used (decl, complain)) return error_mark_node; - bool saw_generic_lambda = false; if (parsing_nsdmi ()) containing_function = NULL_TREE; - else - /* If we are in a lambda function, we can move out until we hit - 1. the context, - 2. a non-lambda function, or - 3. a non-default capturing lambda function. */ - while (context != containing_function - /* containing_function can be null with invalid generic lambdas. */ - && containing_function - && LAMBDA_FUNCTION_P (containing_function)) - { - tree closure = DECL_CONTEXT (containing_function); - lambda_expr = CLASSTYPE_LAMBDA_EXPR (closure); - if (generic_lambda_fn_p (containing_function)) - saw_generic_lambda = true; + if (containing_function && DECL_TEMPLATE_INFO (context) + && LAMBDA_FUNCTION_P (containing_function)) + { + /* Check whether we've already built a proxy; + insert_pending_capture_proxies doesn't update + local_specializations. */ + tree d = lookup_name (DECL_NAME (decl)); + if (d && is_capture_proxy (d) + && DECL_CONTEXT (d) == containing_function) + return d; + } - if (TYPE_CLASS_SCOPE_P (closure)) - /* A lambda in an NSDMI (c++/64496). */ - break; + /* If we are in a lambda function, we can move out until we hit + 1. the context, + 2. a non-lambda function, or + 3. a non-default capturing lambda function. */ + while (context != containing_function + /* containing_function can be null with invalid generic lambdas. */ + && containing_function + && LAMBDA_FUNCTION_P (containing_function)) + { + tree closure = DECL_CONTEXT (containing_function); + lambda_expr = CLASSTYPE_LAMBDA_EXPR (closure); - if (LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda_expr) - == CPLD_NONE) - break; + if (TYPE_CLASS_SCOPE_P (closure)) + /* A lambda in an NSDMI (c++/64496). */ + break; - lambda_stack = tree_cons (NULL_TREE, - lambda_expr, - lambda_stack); + if (LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda_expr) + == CPLD_NONE) + break; - containing_function - = decl_function_context (containing_function); - } + lambda_stack = tree_cons (NULL_TREE, + lambda_expr, + lambda_stack); + + containing_function + = decl_function_context (containing_function); + } + + /* In a lambda within a template, wait until instantiation + time to implicitly capture. */ + if (context == containing_function + && DECL_TEMPLATE_INFO (containing_function) + && any_dependent_template_arguments_p (DECL_TI_ARGS + (containing_function))) + return decl; /* Core issue 696: "[At the July 2009 meeting] the CWG expressed support for an approach in which a reference to a local @@ -3343,26 +3359,11 @@ process_outer_var_ref (tree decl, tsubst_flags_t complain) the complexity of the problem" FIXME update for final resolution of core issue 696. */ - if (decl_maybe_constant_var_p (decl)) - { - if (processing_template_decl && !saw_generic_lambda) - /* In a non-generic lambda within a template, wait until instantiation - time to decide whether to capture. For a generic lambda, we can't - wait until we instantiate the op() because the closure class is - already defined at that point. FIXME to get the semantics exactly - right we need to partially-instantiate the lambda body so the only - dependencies left are on the generic parameters themselves. This - probably means moving away from our current model of lambdas in - templates (instantiating the closure type) to one based on creating - the closure type when instantiating the lambda context. That is - probably also the way to handle lambdas within pack expansions. */ - return decl; - else if (decl_constant_var_p (decl)) - { - tree t = maybe_constant_value (convert_from_reference (decl)); - if (TREE_CONSTANT (t)) - return t; - } + if (decl_constant_var_p (decl)) + { + tree t = maybe_constant_value (convert_from_reference (decl)); + if (TREE_CONSTANT (t)) + return t; } if (lambda_expr && VAR_P (decl) diff --git a/gcc/testsuite/g++.dg/cpp1z/fold-lambda.C b/gcc/testsuite/g++.dg/cpp1z/fold-lambda.C new file mode 100644 index 00000000000..5eaed4a0156 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/fold-lambda.C @@ -0,0 +1,14 @@ +// { dg-do run } +// { dg-options -std=c++17 } + +template +auto f() { + int i = 42; + return ([i]{ return T(i); }() + ...); +} + +int main() +{ + if (f() != 84) + __builtin_abort(); +} diff --git a/gcc/testsuite/g++.dg/warn/Wshadow-6.C b/gcc/testsuite/g++.dg/warn/Wshadow-6.C index 9c2e8b89460..1d8d21b9b6f 100644 --- a/gcc/testsuite/g++.dg/warn/Wshadow-6.C +++ b/gcc/testsuite/g++.dg/warn/Wshadow-6.C @@ -43,7 +43,7 @@ template void f4(int i) { [=]{ int j = i; // { dg-message "shadowed declaration" } - int i; // { dg-warning "shadows a lambda capture" } + int i; // { dg-warning "shadows a " } i = 1; }; } -- 2.30.2