From 8d8f7c4b504df982a81d4ed95961a256d22f8367 Mon Sep 17 00:00:00 2001 From: Adam Butcher Date: Mon, 10 Aug 2009 23:45:06 +0100 Subject: [PATCH] Second version of typename inference from auto parameters in lambda call operator. Still quite hacky -- though better than previous. No longer loses qualifiers on replaced auto parameters so is functionally closer to what's really needed. - This is just a behavioural proof to find out how things work. - Need to shuffle some stuff into pt.c and do away with code dup. - Not sure how to free tree_vec's and tidy up the counts and sizes (looks like they're only intended to grow.) - Added `type_decays_to (non_reference (finish_decltype_type' as suggested by Jason. Currently doesn't remove cv-quals from non-class types though. Need to treat implicit return type differently for dependent types -- should defer and mark that it needs to be computed later. --- gcc/cp/parser.c | 225 +++++++++++++++++++++++++++++++++++++++++++++++++--- gcc/cp/pt.c | 8 ++ gcc/cp/semantics.c | 6 +- 3 files changed, 226 insertions(+), 13 deletions(-) diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index 36762ef..1461309 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -7032,7 +7032,7 @@ cp_parser_lambda_introducer (cp_parser* parser, /* Eat the leading `['. */ cp_parser_require (parser, CPP_OPEN_SQUARE, "%<[%>"); - /* Record default capture mode. "[&" "[=" "[&," "[=," */ + /* Record default capture mode. `[&' `[=' `[&,' `[=,' */ if (cp_lexer_next_token_is (parser->lexer, CPP_AND) && cp_lexer_peek_nth_token (parser->lexer, 2)->type != CPP_NAME) LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda_expr) = CPLD_REFERENCE; @@ -7157,6 +7157,189 @@ cp_parser_lambda_introducer (cp_parser* parser, } + +/* The struct auto_parm_handler_t defines an interface for customizing + the behaviour when a C++0x `auto' type is found as the primary type + specifier of a function parameter declaration. + If auto_parm_handler is set whilst parsing a function parameter + list, the function auto_parm_handler->hook will be called for each + parameter having `auto' as its primary type; in each case the + result of the hook will be used to replace `auto' as the primary + type. */ +typedef struct auto_parm_handler_t auto_parm_handler_t; +typedef tree (*auto_parm_hook_t) (auto_parm_handler_t*); +struct auto_parm_handler_t +{ + auto_parm_hook_t hook; +}; +/* Set to a structure that provides the above interface to be called + if a type containing `auto' is found during + cp_parser_parameter_declaration_list. */ +auto_parm_handler_t* auto_parm_handler = 0; + +/* Handler state for processing `auto' found in lambda function call + parameter list. Supports implicit polymorphic lambdas. */ +typedef struct lambda_auto_handler_t +{ + auto_parm_hook_t hook; + tree* template_param_list; + VEC (deferred_access_check,gc)* checks; + cp_parser* parser; + int i; +} +lambda_auto_handler_t; + +/* FIXME: Much of this would appear to fit better in pt.c. */ + +/* FIXME: It would also mean the copied function + build_template_parm_index and rudely extern'd + x_canonical_type_parameter would be unnecessary. */ + +tree lambda_parameter_make_auto_type_name + (lambda_auto_handler_t*); +tree lambda_auto_handler + (lambda_auto_handler_t*); + +tree +lambda_parameter_make_auto_type_name (lambda_auto_handler_t* handler) +{ + char buf[32]; + sprintf (buf, "__AutoT%d", ++handler->i); + return get_identifier (buf); +} + +/* Return a new TEMPLATE_PARM_INDEX with the indicated INDEX, LEVEL, + ORIG_LEVEL, DECL, and TYPE. + FIXME: Remove this copy from here; i.e. probably move rest into + pt.c. */ + +static tree +build_template_parm_index (int index, + int level, + int orig_level, + tree decl, + tree type) +{ + tree t = make_node (TEMPLATE_PARM_INDEX); + TEMPLATE_PARM_IDX (t) = index; + TEMPLATE_PARM_LEVEL (t) = level; + TEMPLATE_PARM_ORIG_LEVEL (t) = orig_level; + TEMPLATE_PARM_DECL (t) = decl; + TREE_TYPE (t) = type; + TREE_CONSTANT (t) = TREE_CONSTANT (decl); + TREE_READONLY (t) = TREE_READONLY (decl); + + return t; +} + +tree +lambda_auto_handler (lambda_auto_handler_t* handler) +{ + struct cp_binding_level* scope = current_binding_level; + location_t param_loc = cp_lexer_peek_token (handler->parser->lexer)->location; /* XXX: Any way to get current location? */ + + /* First auto parameter may need to start a template parameter list. */ + bool become_template = *handler->template_param_list == NULL_TREE; + + tree synth_id = lambda_parameter_make_auto_type_name (handler); + tree synth_tmpl_parm = finish_template_type_parm (class_type_node, synth_id); + synth_tmpl_parm = build_tree_list (NULL_TREE, synth_tmpl_parm); + + if (become_template) + { + /* do something rude and pretend that the template parameter + scope surrounds the function definition. XXX: can we + guarantee that the immediate-outer scope /is/ the fco? */ + current_binding_level = current_binding_level->level_chain; + + /*if (ENABLE_SCOPE_CHECKING) + --binding_depth;*/ + + push_deferring_access_checks (dk_deferred); + begin_template_parm_list (); + } + + synth_tmpl_parm = process_template_parm (0, + param_loc, + synth_tmpl_parm, + /*non_type=*/false, + /*param_pack=*/false); + + /* Re-index based on last existing parameter. */ + if (!become_template) + { + tree old = *handler->template_param_list; + size_t len = TREE_VEC_LENGTH (old); + size_t idx; + extern tree x_canonical_type_parameter (tree); + + tree p = TREE_VALUE (TREE_VEC_ELT (old, --len)); + if (TREE_CODE (p) == TYPE_DECL || TREE_CODE (p) == TEMPLATE_DECL) + idx = TEMPLATE_TYPE_IDX (TREE_TYPE (p)); + else + idx = TEMPLATE_PARM_IDX (DECL_INITIAL (p)); + + ++idx; + + TEMPLATE_TYPE_PARM_INDEX (TREE_TYPE (synth_id)) + = build_template_parm_index (idx, processing_template_decl, + processing_template_decl, + TYPE_NAME (TREE_TYPE (synth_id)), + TREE_TYPE (synth_id)); + TYPE_CANONICAL (TREE_TYPE (synth_id)) = x_canonical_type_parameter (TREE_TYPE (synth_id)); + } + + if (become_template) + { + /* Initial template parameter, create new list. */ + *handler->template_param_list = end_template_parm_list (synth_tmpl_parm); + ++handler->parser->num_template_parameter_lists; + push_deferring_access_checks (dk_no_check); + push_binding_level (scope); + } + else /* Add to existing template parameter list. */ + { + tree old = *handler->template_param_list; + tree new_vec; + size_t len; + + gcc_assert (TREE_CODE (old) == TREE_VEC); + + len = TREE_VEC_LENGTH (old); + + /* XXX: Maybe do doubling and store real length in handler + state, there doesn't seem to be any way to free these; or + rather no way to free them that tidies up the + tree_node_counts and sizes. Ideally just want a + realloc_tree_vec or some such thing that would do realloc and + housekeeping. Maybe there's some reason why this + would be a bad thing. (?) In fact it would be better + to keep building a tree list and only flatten into + a vector after parsing the parameter list. */ + new_vec = make_tree_vec (len+1); + { + size_t n; + for (n = 0; n != len; ++n) + TREE_VEC_ELT (new_vec, n) = TREE_VEC_ELT (old, n); + } + TREE_VEC_ELT (new_vec, len) = synth_tmpl_parm; + /* memcpy (new_vec, old, sizeof (struct tree_vec) + sizeof (tree) * (len-1)); + TREE_VEC_LENGTH (new_vec) = ++len; + */ + + /* XXX: free old one - how? maybe ggc_free? but how to tidy up + * the node counts and sizes? */ + ggc_free (old); + + *handler->template_param_list = new_vec; + + TREE_VALUE (current_template_parms) = new_vec; + } + + /* Return synthesized type as a substitute for `auto'. */ + return TREE_TYPE (synth_id); +} + /* Parse the (optional) middle of a lambda expression. lambda-parameter-declaration: @@ -7176,11 +7359,10 @@ cp_parser_lambda_parameter_declaration_opt (cp_parser* parser, tree param_list = NULL_TREE; tree exception_spec = NULL_TREE; tree template_param_list = NULL_TREE; + VEC (deferred_access_check,gc) *checks = 0; if (cp_lexer_next_token_is (parser->lexer, CPP_LESS)) { - VEC (deferred_access_check,gc) *checks; - cp_parser_require (parser, CPP_LESS, "%<<%>"); push_deferring_access_checks (dk_deferred); @@ -7208,10 +7390,23 @@ cp_parser_lambda_parameter_declaration_opt (cp_parser* parser, if (cp_lexer_next_token_is_not (parser->lexer, CPP_CLOSE_PAREN)) { bool is_error = false; + + /* Set up handler for auto being used in function parameter list. */ + lambda_auto_handler_t auto_handler; + auto_handler.hook = (auto_parm_hook_t) lambda_auto_handler; + auto_handler.template_param_list = &template_param_list; + auto_handler.checks = 0; + auto_handler.parser = parser; + auto_handler.i = 0; + auto_parm_handler = (auto_parm_handler_t*) &auto_handler; + param_list = cp_parser_parameter_declaration_list (parser, &is_error); /* TODO: better way to handle this error? */ if (is_error) param_list = NULL_TREE; + + /* TODO: copy auto_handler.checks out */ + auto_parm_handler = 0; } cp_parser_require (parser, CPP_CLOSE_PAREN, "%<)%>"); @@ -7307,6 +7502,7 @@ cp_parser_lambda_parameter_declaration_opt (cp_parser* parser, { tree saved_current_function_decl = current_function_decl; pop_deferring_access_checks (); + /* TODO: do checks */ /* Clear current function decl to allow check_member_template to pass. Currently it rejects templates inside functions. @@ -7316,16 +7512,14 @@ cp_parser_lambda_parameter_declaration_opt (cp_parser* parser, fco = finish_member_template_decl (fco); current_function_decl = saved_current_function_decl; - --parser->num_template_parameter_lists; + --parser->num_template_parameter_lists; pop_deferring_access_checks (); finish_template_decl (template_param_list); } finish_member_declaration (fco); LAMBDA_EXPR_FUNCTION (lambda_expr) = fco; - } - } /* Parse the body of a lambda expression, which is simply @@ -14656,11 +14850,20 @@ cp_parser_parameter_declaration_list (cp_parser* parser, bool *is_error) deprecated_state = DEPRECATED_SUPPRESS; if (parameter) - decl = grokdeclarator (parameter->declarator, - ¶meter->decl_specifiers, - PARM, - parameter->default_argument != NULL_TREE, - ¶meter->decl_specifiers.attributes); + { + /* If there is a custom `auto' handler and the primary type + of this parameter is `auto', then invoke the hook and + replace `auto' with the result. */ + if (auto_parm_handler && is_auto (parameter->decl_specifiers.type)) + { + parameter->decl_specifiers.type = auto_parm_handler->hook (auto_parm_handler); + } + decl = grokdeclarator (parameter->declarator, + ¶meter->decl_specifiers, + PARM, + parameter->default_argument != NULL_TREE, + ¶meter->decl_specifiers.attributes); + } deprecated_state = DEPRECATED_NORMAL; diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index ed45324..2d8380c 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -3150,6 +3150,13 @@ canonical_type_parameter (tree type) } } +/* FIXME: Cleanup this mess */ +tree x_canonical_type_parameter (tree type); +tree x_canonical_type_parameter (tree type) +{ + return canonical_type_parameter (type); +} + /* Return a TEMPLATE_PARM_INDEX, similar to INDEX, but whose TEMPLATE_PARM_LEVEL has been decreased by LEVELS. If such a TEMPLATE_PARM_INDEX already exists, it is returned; otherwise, a @@ -17480,6 +17487,7 @@ is_auto (const_tree type) tree type_uses_auto (tree type) { + /* XXX: Maybe to loop rather than recurse here? */ enum tree_code code; if (is_auto (type)) return type; diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c index 5f1aab0..482887c 100644 --- a/gcc/cp/semantics.c +++ b/gcc/cp/semantics.c @@ -5276,8 +5276,10 @@ deduce_lambda_return_type (tree lambda, tree expr) fco = DECL_TEMPLATE_RESULT (fco); if (type_dependent_expression_p (expr)) - return_type = finish_decltype_type - (expr, /*id_expression_or_member_access_p=*/false); + /* TODO: Should defer this until instantiation rather than using + decltype. */ + return_type = type_decays_to (non_reference (finish_decltype_type + (expr, /*id_expression_or_member_access_p=*/false))); else return_type = type_decays_to (unlowered_expr_type (expr)); -- 1.5.6.GIT