From patchwork Thu Sep 12 18:57:49 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Adam Butcher X-Patchwork-Id: 274588 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 41A4A2C03A8 for ; Fri, 13 Sep 2013 04:58:14 +1000 (EST) DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:from :to:cc:subject:date:message-id; q=dns; s=default; b=gd5JW2fLWFCY AZqisLFWjDjuaxD3ViBahHllDlaQ1uvKRt0LZO6zdjzlcO7UAhw4v0L88FMENeNp pi9hZetg9MrDBCvyXMbO86d1jKEIV+eyUqvfKi5/dO1UTqtVYJT4cCfjt9rj3OJP WkY35Ue0btVWRoGYfHsts5qrdn8+QXM= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:from :to:cc:subject:date:message-id; s=default; bh=ZFvfOEofbhQW0BHiZo z7RNup1BI=; b=xrW+ev2+XPUKvdYJA2Un/YNUo7wQJO96OZMm6gAMLnSwlEKleW AVZc0113n++7hbwduL92eO/PiV+MtuLWViAfRmuKRb78GSRWjf0i/hXiDbWQ9p7+ j7GJj/TJSqC7Fs0cfJY8yOnYVgYkOOtBNjlLf3eofdzRrOpTKHREDs4TM= Received: (qmail 26291 invoked by alias); 12 Sep 2013 18:58:08 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 26281 invoked by uid 89); 12 Sep 2013 18:58:07 -0000 Received: from mail-wg0-f49.google.com (HELO mail-wg0-f49.google.com) (74.125.82.49) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES128-SHA encrypted) ESMTPS; Thu, 12 Sep 2013 18:58:07 +0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-2.8 required=5.0 tests=ALL_TRUSTED, AWL, BAYES_00, FREEMAIL_FROM autolearn=ham version=3.3.2 X-HELO: mail-wg0-f49.google.com Received: by mail-wg0-f49.google.com with SMTP id l18so220506wgh.16 for ; Thu, 12 Sep 2013 11:58:03 -0700 (PDT) X-Received: by 10.194.24.168 with SMTP id v8mr7677821wjf.28.1379012283201; Thu, 12 Sep 2013 11:58:03 -0700 (PDT) Received: from sphere.lan (munkyhouse.force9.co.uk. [84.92.244.81]) by mx.google.com with ESMTPSA id iz19sm6559385wic.9.1969.12.31.16.00.00 (version=TLSv1.1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Thu, 12 Sep 2013 11:58:02 -0700 (PDT) From: Adam Butcher To: gcc-patches@gcc.gnu.org Cc: Jason Merrill , Gabriel Dos Reis , Andrew Sutton , adam@jessamine.co.uk Subject: [PATCH, committed] Support lambda templates. Date: Thu, 12 Sep 2013 19:57:49 +0100 Message-Id: <1379012276-2415-1-git-send-email-adam@jessamine.co.uk> From: abutcher * 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. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@202539 138bc75d-0d04-0410-961f-82ee72b054a4 --- gcc/cp/ChangeLog | 17 +++++ gcc/cp/decl2.c | 5 +- gcc/cp/lambda.c | 189 +++++++++++++++++++++++++++++++++++++++++++++---------- gcc/cp/parser.c | 40 +++++++++++- gcc/cp/pt.c | 4 +- 5 files changed, 217 insertions(+), 38 deletions(-) diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index cbad022..66869d2 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,5 +1,22 @@ 2013-09-12 Adam Butcher + * 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 + * pt.c (instantiate_decl): Save/restore cp_unevaluated_operand and c_inhibit_evaluation_warnings. Reset if instantiating within a function-local template. diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c index d5d2912..ac9dbd7 100644 --- a/gcc/cp/decl2.c +++ b/gcc/cp/decl2.c @@ -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))); diff --git a/gcc/cp/lambda.c b/gcc/cp/lambda.c index a53e692..2d20333 100644 --- a/gcc/cp/lambda.c +++ b/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 *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 *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 (); diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index e00e56c..9db5464 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -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; diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 3ae679a..93e6258 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -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