Support lambda templates.
* parser.c (cp_parser_lambda_declarator_opt): Accept template parameter list with std=c++1y or std=gnu++1y. (cp_parser_lambda_body): Don't call 'expand_or_defer_fn' for lambda call operator template to avoid adding template result to symbol table. * lambda.c (lambda_function): Return template result if call operator is a template. (maybe_add_lambda_conv_op): Move declarations to point of use. Refactor operator call building in order to support conversion of a non-capturing lambda template to a function pointer with help from ... (prepare_op_call): ... this new function. * decl2.c (check_member_template): Don't reject lambda call operator template in local [lambda] class. * pt.c (instantiate_class_template_1): Don't instantiate lambda call operator template when instantiating lambda class. From-SVN: r202539
This commit is contained in:
parent
1c1880fcc6
commit
c9469d539a
@ -1,3 +1,20 @@
|
||||
2013-09-12 Adam Butcher <adam@jessamine.co.uk>
|
||||
|
||||
* parser.c (cp_parser_lambda_declarator_opt): Accept template parameter
|
||||
list with std=c++1y or std=gnu++1y.
|
||||
(cp_parser_lambda_body): Don't call 'expand_or_defer_fn' for lambda call
|
||||
operator template to avoid adding template result to symbol table.
|
||||
* lambda.c (lambda_function): Return template result if call operator is
|
||||
a template.
|
||||
(maybe_add_lambda_conv_op): Move declarations to point of use. Refactor
|
||||
operator call building in order to support conversion of a non-capturing
|
||||
lambda template to a function pointer with help from ...
|
||||
(prepare_op_call): ... this new function.
|
||||
* decl2.c (check_member_template): Don't reject lambda call operator
|
||||
template in local [lambda] class.
|
||||
* pt.c (instantiate_class_template_1): Don't instantiate lambda call
|
||||
operator template when instantiating lambda class.
|
||||
|
||||
2013-09-12 Adam Butcher <adam@jessamine.co.uk>
|
||||
|
||||
* pt.c (instantiate_decl): Save/restore cp_unevaluated_operand and
|
||||
|
@ -507,8 +507,9 @@ check_member_template (tree tmpl)
|
||||
|| (TREE_CODE (decl) == TYPE_DECL
|
||||
&& MAYBE_CLASS_TYPE_P (TREE_TYPE (decl))))
|
||||
{
|
||||
/* The parser rejects template declarations in local classes. */
|
||||
gcc_assert (!current_function_decl);
|
||||
/* The parser rejects template declarations in local classes
|
||||
(with the exception of generic lambdas). */
|
||||
gcc_assert (!current_function_decl || LAMBDA_FUNCTION_P (decl));
|
||||
/* The parser rejects any use of virtual in a function template. */
|
||||
gcc_assert (!(TREE_CODE (decl) == FUNCTION_DECL
|
||||
&& DECL_VIRTUAL_P (decl)));
|
||||
|
189
gcc/cp/lambda.c
189
gcc/cp/lambda.c
@ -196,7 +196,7 @@ lambda_function (tree lambda)
|
||||
/*protect=*/0, /*want_type=*/false,
|
||||
tf_warning_or_error);
|
||||
if (lambda)
|
||||
lambda = BASELINK_FUNCTIONS (lambda);
|
||||
lambda = STRIP_TEMPLATE (get_first_fn (lambda));
|
||||
return lambda;
|
||||
}
|
||||
|
||||
@ -741,6 +741,22 @@ nonlambda_method_basetype (void)
|
||||
return TYPE_METHOD_BASETYPE (TREE_TYPE (fn));
|
||||
}
|
||||
|
||||
/* Helper function for maybe_add_lambda_conv_op; build a CALL_EXPR with
|
||||
indicated FN and NARGS, but do not initialize the return type or any of the
|
||||
argument slots. */
|
||||
|
||||
static tree
|
||||
prepare_op_call (tree fn, int nargs)
|
||||
{
|
||||
tree t;
|
||||
|
||||
t = build_vl_exp (CALL_EXPR, nargs + 3);
|
||||
CALL_EXPR_FN (t) = fn;
|
||||
CALL_EXPR_STATIC_CHAIN (t) = NULL;
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
/* If the closure TYPE has a static op(), also add a conversion to function
|
||||
pointer. */
|
||||
|
||||
@ -749,9 +765,6 @@ maybe_add_lambda_conv_op (tree type)
|
||||
{
|
||||
bool nested = (current_function_decl != NULL_TREE);
|
||||
tree callop = lambda_function (type);
|
||||
tree rettype, name, fntype, fn, body, compound_stmt;
|
||||
tree thistype, stattype, statfn, convfn, call, arg;
|
||||
vec<tree, va_gc> *argvec;
|
||||
|
||||
if (LAMBDA_EXPR_CAPTURE_LIST (CLASSTYPE_LAMBDA_EXPR (type)) != NULL_TREE)
|
||||
return;
|
||||
@ -759,6 +772,10 @@ maybe_add_lambda_conv_op (tree type)
|
||||
if (processing_template_decl)
|
||||
return;
|
||||
|
||||
bool const generic_lambda_p
|
||||
= (DECL_TEMPLATE_INFO (callop)
|
||||
&& DECL_TEMPLATE_RESULT (DECL_TI_TEMPLATE (callop)) == callop);
|
||||
|
||||
if (DECL_INITIAL (callop) == NULL_TREE)
|
||||
{
|
||||
/* If the op() wasn't instantiated due to errors, give up. */
|
||||
@ -766,16 +783,127 @@ maybe_add_lambda_conv_op (tree type)
|
||||
return;
|
||||
}
|
||||
|
||||
stattype = build_function_type (TREE_TYPE (TREE_TYPE (callop)),
|
||||
FUNCTION_ARG_CHAIN (callop));
|
||||
/* Non-template conversion operators are defined directly with build_call_a
|
||||
and using DIRECT_ARGVEC for arguments (including 'this'). Templates are
|
||||
deferred and the CALL is built in-place. In the case of a deduced return
|
||||
call op, the decltype expression, DECLTYPE_CALL, used as a substitute for
|
||||
the return type is also built in-place. The arguments of DECLTYPE_CALL in
|
||||
the return expression may differ in flags from those in the body CALL. In
|
||||
particular, parameter pack expansions are marked PACK_EXPANSION_LOCAL_P in
|
||||
the body CALL, but not in DECLTYPE_CALL. */
|
||||
|
||||
vec<tree, va_gc> *direct_argvec;
|
||||
tree decltype_call = 0, call;
|
||||
tree fn_result = TREE_TYPE (TREE_TYPE (callop));
|
||||
|
||||
if (generic_lambda_p)
|
||||
{
|
||||
/* Prepare the dependent member call for the static member function
|
||||
'_FUN' and, potentially, prepare another call to be used in a decltype
|
||||
return expression for a deduced return call op to allow for simple
|
||||
implementation of the conversion operator. */
|
||||
|
||||
tree instance = build_nop (type, null_pointer_node);
|
||||
tree objfn = build_min (COMPONENT_REF, NULL_TREE,
|
||||
instance, DECL_NAME (callop), NULL_TREE);
|
||||
int nargs = list_length (DECL_ARGUMENTS (callop)) - 1;
|
||||
|
||||
call = prepare_op_call (objfn, nargs);
|
||||
if (type_uses_auto (fn_result))
|
||||
decltype_call = prepare_op_call (objfn, nargs);
|
||||
}
|
||||
else
|
||||
{
|
||||
direct_argvec = make_tree_vector ();
|
||||
direct_argvec->quick_push (build1 (NOP_EXPR,
|
||||
TREE_TYPE (DECL_ARGUMENTS (callop)),
|
||||
null_pointer_node));
|
||||
}
|
||||
|
||||
/* Copy CALLOP's argument list (as per 'copy_list') as FN_ARGS in order to
|
||||
declare the static member function "_FUN" below. For each arg append to
|
||||
DIRECT_ARGVEC (for the non-template case) or populate the pre-allocated
|
||||
call args (for the template case). If a parameter pack is found, expand
|
||||
it, flagging it as PACK_EXPANSION_LOCAL_P for the body call. */
|
||||
|
||||
tree fn_args = NULL_TREE;
|
||||
{
|
||||
int ix = 0;
|
||||
tree src = DECL_CHAIN (DECL_ARGUMENTS (callop));
|
||||
tree tgt;
|
||||
|
||||
while (src)
|
||||
{
|
||||
tree new_node = copy_node (src);
|
||||
|
||||
if (!fn_args)
|
||||
fn_args = tgt = new_node;
|
||||
else
|
||||
{
|
||||
TREE_CHAIN (tgt) = new_node;
|
||||
tgt = new_node;
|
||||
}
|
||||
|
||||
mark_exp_read (tgt);
|
||||
|
||||
if (generic_lambda_p)
|
||||
{
|
||||
if (FUNCTION_PARAMETER_PACK_P (tgt))
|
||||
{
|
||||
tree a = make_pack_expansion (tgt);
|
||||
if (decltype_call)
|
||||
CALL_EXPR_ARG (decltype_call, ix) = copy_node (a);
|
||||
PACK_EXPANSION_LOCAL_P (a) = true;
|
||||
CALL_EXPR_ARG (call, ix) = a;
|
||||
}
|
||||
else
|
||||
{
|
||||
tree a = convert_from_reference (tgt);
|
||||
CALL_EXPR_ARG (call, ix) = a;
|
||||
if (decltype_call)
|
||||
CALL_EXPR_ARG (decltype_call, ix) = copy_node (a);
|
||||
}
|
||||
++ix;
|
||||
}
|
||||
else
|
||||
vec_safe_push (direct_argvec, tgt);
|
||||
|
||||
src = TREE_CHAIN (src);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (generic_lambda_p)
|
||||
{
|
||||
if (decltype_call)
|
||||
{
|
||||
++processing_template_decl;
|
||||
fn_result = finish_decltype_type
|
||||
(decltype_call, /*id_expression_or_member_access_p=*/false,
|
||||
tf_warning_or_error);
|
||||
--processing_template_decl;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
call = build_call_a (callop,
|
||||
direct_argvec->length (),
|
||||
direct_argvec->address ());
|
||||
if (MAYBE_CLASS_TYPE_P (TREE_TYPE (call)))
|
||||
call = build_cplus_new (TREE_TYPE (call), call, tf_warning_or_error);
|
||||
}
|
||||
CALL_FROM_THUNK_P (call) = 1;
|
||||
|
||||
tree stattype = build_function_type (fn_result, FUNCTION_ARG_CHAIN (callop));
|
||||
|
||||
/* First build up the conversion op. */
|
||||
|
||||
rettype = build_pointer_type (stattype);
|
||||
name = mangle_conv_op_name_for_type (rettype);
|
||||
thistype = cp_build_qualified_type (type, TYPE_QUAL_CONST);
|
||||
fntype = build_method_type_directly (thistype, rettype, void_list_node);
|
||||
fn = convfn = build_lang_decl (FUNCTION_DECL, name, fntype);
|
||||
tree rettype = build_pointer_type (stattype);
|
||||
tree name = mangle_conv_op_name_for_type (rettype);
|
||||
tree thistype = cp_build_qualified_type (type, TYPE_QUAL_CONST);
|
||||
tree fntype = build_method_type_directly (thistype, rettype, void_list_node);
|
||||
tree convfn = build_lang_decl (FUNCTION_DECL, name, fntype);
|
||||
tree fn = convfn;
|
||||
DECL_SOURCE_LOCATION (fn) = DECL_SOURCE_LOCATION (callop);
|
||||
|
||||
if (TARGET_PTRMEMFUNC_VBIT_LOCATION == ptrmemfunc_vbit_in_pfn
|
||||
@ -794,6 +922,9 @@ maybe_add_lambda_conv_op (tree type)
|
||||
if (nested)
|
||||
DECL_INTERFACE_KNOWN (fn) = 1;
|
||||
|
||||
if (generic_lambda_p)
|
||||
fn = add_inherited_template_parms (fn, DECL_TI_TEMPLATE (callop));
|
||||
|
||||
add_method (type, fn, NULL_TREE);
|
||||
|
||||
/* Generic thunk code fails for varargs; we'll complain in mark_used if
|
||||
@ -807,7 +938,8 @@ maybe_add_lambda_conv_op (tree type)
|
||||
/* Now build up the thunk to be returned. */
|
||||
|
||||
name = get_identifier ("_FUN");
|
||||
fn = statfn = build_lang_decl (FUNCTION_DECL, name, stattype);
|
||||
tree statfn = build_lang_decl (FUNCTION_DECL, name, stattype);
|
||||
fn = statfn;
|
||||
DECL_SOURCE_LOCATION (fn) = DECL_SOURCE_LOCATION (callop);
|
||||
if (TARGET_PTRMEMFUNC_VBIT_LOCATION == ptrmemfunc_vbit_in_pfn
|
||||
&& DECL_ALIGN (fn) < 2 * BITS_PER_UNIT)
|
||||
@ -820,8 +952,8 @@ maybe_add_lambda_conv_op (tree type)
|
||||
DECL_NOT_REALLY_EXTERN (fn) = 1;
|
||||
DECL_DECLARED_INLINE_P (fn) = 1;
|
||||
DECL_STATIC_FUNCTION_P (fn) = 1;
|
||||
DECL_ARGUMENTS (fn) = copy_list (DECL_CHAIN (DECL_ARGUMENTS (callop)));
|
||||
for (arg = DECL_ARGUMENTS (fn); arg; arg = DECL_CHAIN (arg))
|
||||
DECL_ARGUMENTS (fn) = fn_args;
|
||||
for (tree arg = fn_args; arg; arg = DECL_CHAIN (arg))
|
||||
{
|
||||
/* Avoid duplicate -Wshadow warnings. */
|
||||
DECL_NAME (arg) = NULL_TREE;
|
||||
@ -830,6 +962,9 @@ maybe_add_lambda_conv_op (tree type)
|
||||
if (nested)
|
||||
DECL_INTERFACE_KNOWN (fn) = 1;
|
||||
|
||||
if (generic_lambda_p)
|
||||
fn = add_inherited_template_parms (fn, DECL_TI_TEMPLATE (callop));
|
||||
|
||||
add_method (type, fn, NULL_TREE);
|
||||
|
||||
if (nested)
|
||||
@ -850,29 +985,17 @@ maybe_add_lambda_conv_op (tree type)
|
||||
((symtab_node) cgraph_get_create_node (statfn),
|
||||
(symtab_node) cgraph_get_create_node (callop));
|
||||
}
|
||||
body = begin_function_body ();
|
||||
compound_stmt = begin_compound_stmt (0);
|
||||
|
||||
arg = build1 (NOP_EXPR, TREE_TYPE (DECL_ARGUMENTS (callop)),
|
||||
null_pointer_node);
|
||||
argvec = make_tree_vector ();
|
||||
argvec->quick_push (arg);
|
||||
for (arg = DECL_ARGUMENTS (statfn); arg; arg = DECL_CHAIN (arg))
|
||||
{
|
||||
mark_exp_read (arg);
|
||||
vec_safe_push (argvec, arg);
|
||||
}
|
||||
call = build_call_a (callop, argvec->length (), argvec->address ());
|
||||
CALL_FROM_THUNK_P (call) = 1;
|
||||
if (MAYBE_CLASS_TYPE_P (TREE_TYPE (call)))
|
||||
call = build_cplus_new (TREE_TYPE (call), call, tf_warning_or_error);
|
||||
tree body = begin_function_body ();
|
||||
tree compound_stmt = begin_compound_stmt (0);
|
||||
call = convert_from_reference (call);
|
||||
finish_return_stmt (call);
|
||||
|
||||
finish_compound_stmt (compound_stmt);
|
||||
finish_function_body (body);
|
||||
|
||||
expand_or_defer_fn (finish_function (2));
|
||||
fn = finish_function (/*inline*/2);
|
||||
if (!generic_lambda_p)
|
||||
expand_or_defer_fn (fn);
|
||||
|
||||
/* Generate the body of the conversion op. */
|
||||
|
||||
@ -888,7 +1011,9 @@ maybe_add_lambda_conv_op (tree type)
|
||||
finish_compound_stmt (compound_stmt);
|
||||
finish_function_body (body);
|
||||
|
||||
expand_or_defer_fn (finish_function (2));
|
||||
fn = finish_function (/*inline*/2);
|
||||
if (!generic_lambda_p)
|
||||
expand_or_defer_fn (fn);
|
||||
|
||||
if (nested)
|
||||
pop_function_context ();
|
||||
|
@ -8783,6 +8783,7 @@ cp_parser_lambda_introducer (cp_parser* parser, tree lambda_expr)
|
||||
/* Parse the (optional) middle of a lambda expression.
|
||||
|
||||
lambda-declarator:
|
||||
< template-parameter-list [opt] >
|
||||
( parameter-declaration-clause [opt] )
|
||||
attribute-specifier [opt]
|
||||
mutable [opt]
|
||||
@ -8802,9 +8803,30 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr)
|
||||
tree param_list = void_list_node;
|
||||
tree attributes = NULL_TREE;
|
||||
tree exception_spec = NULL_TREE;
|
||||
tree template_param_list = NULL_TREE;
|
||||
|
||||
/* The lambda-declarator is optional, but must begin with an opening
|
||||
parenthesis if present. */
|
||||
/* The template-parameter-list is optional, but must begin with
|
||||
an opening angle if present. */
|
||||
if (cp_lexer_next_token_is (parser->lexer, CPP_LESS))
|
||||
{
|
||||
if (cxx_dialect < cxx1y)
|
||||
pedwarn (parser->lexer->next_token->location, 0,
|
||||
"lambda templates are only available with "
|
||||
"-std=c++1y or -std=gnu++1y");
|
||||
|
||||
cp_lexer_consume_token (parser->lexer);
|
||||
|
||||
template_param_list = cp_parser_template_parameter_list (parser);
|
||||
|
||||
cp_parser_skip_to_end_of_template_parameter_list (parser);
|
||||
|
||||
/* We just processed one more parameter list. */
|
||||
++parser->num_template_parameter_lists;
|
||||
}
|
||||
|
||||
/* The parameter-declaration-clause is optional (unless
|
||||
template-parameter-list was given), but must begin with an
|
||||
opening parenthesis if present. */
|
||||
if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN))
|
||||
{
|
||||
cp_lexer_consume_token (parser->lexer);
|
||||
@ -8847,6 +8869,8 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr)
|
||||
trailing-return-type in case of decltype. */
|
||||
pop_bindings_and_leave_scope ();
|
||||
}
|
||||
else if (template_param_list != NULL_TREE) // generate diagnostic
|
||||
cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN);
|
||||
|
||||
/* Create the function call operator.
|
||||
|
||||
@ -8890,6 +8914,12 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr)
|
||||
DECL_ARTIFICIAL (fco) = 1;
|
||||
/* Give the object parameter a different name. */
|
||||
DECL_NAME (DECL_ARGUMENTS (fco)) = get_identifier ("__closure");
|
||||
if (template_param_list)
|
||||
{
|
||||
fco = finish_member_template_decl (fco);
|
||||
finish_template_decl (template_param_list);
|
||||
--parser->num_template_parameter_lists;
|
||||
}
|
||||
}
|
||||
|
||||
finish_member_declaration (fco);
|
||||
@ -9012,7 +9042,11 @@ cp_parser_lambda_body (cp_parser* parser, tree lambda_expr)
|
||||
finish_lambda_scope ();
|
||||
|
||||
/* Finish the function and generate code for it if necessary. */
|
||||
expand_or_defer_fn (finish_function (/*inline*/2));
|
||||
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);
|
||||
}
|
||||
|
||||
parser->local_variables_forbidden_p = local_variables_forbidden_p;
|
||||
|
@ -9103,7 +9103,9 @@ instantiate_class_template_1 (tree type)
|
||||
tree decl = lambda_function (type);
|
||||
if (decl)
|
||||
{
|
||||
instantiate_decl (decl, false, false);
|
||||
if (!DECL_TEMPLATE_INFO (decl)
|
||||
|| DECL_TEMPLATE_RESULT (DECL_TI_TEMPLATE (decl)) != decl)
|
||||
instantiate_decl (decl, false, false);
|
||||
|
||||
/* We need to instantiate the capture list from the template
|
||||
after we've instantiated the closure members, but before we
|
||||
|
Loading…
Reference in New Issue
Block a user