diff options
Diffstat (limited to 'gcc/cp/parser.c')
-rw-r--r-- | gcc/cp/parser.c | 951 |
1 files changed, 682 insertions, 269 deletions
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index c61e0b2..b6e738c 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -169,6 +169,7 @@ enum required_token { RT_TRY, /* try */ RT_CATCH, /* catch */ RT_THROW, /* throw */ + RT_AUTO, /* auto */ RT_LABEL, /* __label__ */ RT_AT_TRY, /* @try */ RT_AT_SYNCHRONIZED, /* @synchronized */ @@ -2196,6 +2197,8 @@ static tree cp_parser_type_specifier int *, bool *); static tree cp_parser_simple_type_specifier (cp_parser *, cp_decl_specifier_seq *, cp_parser_flags); +static tree cp_parser_placeholder_type_specifier + (cp_parser *, location_t, tree, bool); static tree cp_parser_type_name (cp_parser *, bool); static tree cp_parser_nonclass_name @@ -2369,6 +2372,8 @@ static tree cp_parser_type_parameter (cp_parser *, bool *); static tree cp_parser_template_id (cp_parser *, bool, bool, enum tag_types, bool); +static tree cp_parser_template_id_expr + (cp_parser *, bool, bool, bool); static tree cp_parser_template_name (cp_parser *, bool, bool, bool, enum tag_types, bool *); static tree cp_parser_template_argument_list @@ -2444,7 +2449,9 @@ static void cp_parser_label_declaration /* Concept Extensions */ -static tree cp_parser_requires_clause +static tree cp_parser_concept_definition + (cp_parser *); +static tree cp_parser_constraint_expression (cp_parser *); static tree cp_parser_requires_clause_opt (cp_parser *); @@ -2454,7 +2461,7 @@ static tree cp_parser_requirement_parameter_list (cp_parser *); static tree cp_parser_requirement_body (cp_parser *); -static tree cp_parser_requirement_list +static tree cp_parser_requirement_seq (cp_parser *); static tree cp_parser_requirement (cp_parser *); @@ -2687,11 +2694,6 @@ static bool cp_parser_init_statement_p static bool cp_parser_skip_to_closing_square_bracket (cp_parser *); -/* Concept-related syntactic transformations */ - -static tree cp_parser_maybe_concept_name (cp_parser *, tree); -static tree cp_parser_maybe_partial_concept_id (cp_parser *, tree, tree); - // -------------------------------------------------------------------------- // // Unevaluated Operand Guard // @@ -4874,6 +4876,8 @@ class token_pair m_open_loc); } + location_t open_location () const { return m_open_loc; } + private: location_t m_open_loc; }; @@ -4948,7 +4952,7 @@ cp_parser_statement_expr (cp_parser *parser) This returns the tree code corresponding to the matched operator as an int. When the current token matches a compound assignment - opertor, the resulting tree code is the negative value of the + operator, the resulting tree code is the negative value of the non-assignment operator. */ static int @@ -5904,11 +5908,10 @@ cp_parser_id_expression (cp_parser *parser, cp_parser_parse_tentatively (parser); /* Try a template-id. */ - id = cp_parser_template_id (parser, - /*template_keyword_p=*/false, - /*check_dependency_p=*/true, - none_type, - declarator_p); + id = cp_parser_template_id_expr (parser, + /*template_keyword_p=*/false, + /*check_dependency_p=*/true, + declarator_p); /* If that worked, we're done. */ if (cp_parser_parse_definitely (parser)) return id; @@ -5983,10 +5986,9 @@ cp_parser_unqualified_id (cp_parser* parser, template-id. */ cp_parser_parse_tentatively (parser); /* Try a template-id. */ - id = cp_parser_template_id (parser, template_keyword_p, - check_dependency_p, - none_type, - declarator_p); + id = cp_parser_template_id_expr (parser, template_keyword_p, + check_dependency_p, + declarator_p); /* If it worked, we're done. */ if (cp_parser_parse_definitely (parser)) return id; @@ -5995,10 +5997,9 @@ cp_parser_unqualified_id (cp_parser* parser, } case CPP_TEMPLATE_ID: - return cp_parser_template_id (parser, template_keyword_p, - check_dependency_p, - none_type, - declarator_p); + return cp_parser_template_id_expr (parser, template_keyword_p, + check_dependency_p, + declarator_p); case CPP_COMPL: { @@ -6239,10 +6240,9 @@ cp_parser_unqualified_id (cp_parser* parser, /* This could be a template-id, so we try that first. */ cp_parser_parse_tentatively (parser); /* Try a template-id. */ - id = cp_parser_template_id (parser, template_keyword_p, - /*check_dependency_p=*/true, - none_type, - declarator_p); + id = cp_parser_template_id_expr (parser, template_keyword_p, + /*check_dependency_p=*/true, + declarator_p); /* If that worked, we're done. */ if (cp_parser_parse_definitely (parser)) return id; @@ -9638,6 +9638,8 @@ cp_parser_binary_expression (cp_parser* parser, bool cast_p, current.lhs = error_mark_node; else { + current.lhs.maybe_add_location_wrapper (); + rhs.maybe_add_location_wrapper (); current.lhs = build_min (current.tree_type, TREE_CODE_CLASS (current.tree_type) @@ -14059,10 +14061,26 @@ cp_parser_decl_specifier_seq (cp_parser* parser, case RID_CONCEPT: ds = ds_concept; cp_lexer_consume_token (parser->lexer); + + /* Warn for concept as a decl-specifier. We'll rewrite these as + concept declarations later. */ + if (!flag_concepts_ts) + { + cp_token *next = cp_lexer_peek_token (parser->lexer); + if (next->keyword == RID_BOOL) + pedwarn (next->location, 0, "the %<bool%> keyword is not " + "allowed in a C++20 concept definition"); + else + pedwarn (token->location, 0, "C++20 concept definition syntax " + "is %<concept <name> = <expr>%> "); + } + /* In C++20 a concept definition is just 'concept name = expr;' - Support that syntax by pretending we've seen 'bool'. */ + Support that syntax as a TS extension by pretending we've seen + the 'bool' specifier. */ if (cp_lexer_next_token_is (parser->lexer, CPP_NAME) - && cp_lexer_nth_token_is (parser->lexer, 2, CPP_EQ)) + && cp_lexer_nth_token_is (parser->lexer, 2, CPP_EQ) + && !decl_specs->any_type_specifiers_p) { cp_parser_set_decl_spec_type (decl_specs, boolean_type_node, token, /*type_definition*/false); @@ -15800,15 +15818,15 @@ get_unqualified_id (cp_declarator *declarator) return NULL_TREE; } -/* Returns true if DECL represents a constrained-parameter. */ +/* Returns true if TYPE would declare a constrained constrained-parameter. */ static inline bool -is_constrained_parameter (tree decl) +is_constrained_parameter (tree type) { - return (decl - && TREE_CODE (decl) == TYPE_DECL - && CONSTRAINED_PARM_CONCEPT (decl) - && DECL_P (CONSTRAINED_PARM_CONCEPT (decl))); + return (type + && TREE_CODE (type) == TYPE_DECL + && CONSTRAINED_PARM_CONCEPT (type) + && DECL_P (CONSTRAINED_PARM_CONCEPT (type))); } /* Returns true if PARM declares a constrained-parameter. */ @@ -15894,8 +15912,8 @@ cp_parser_constrained_template_template_parm (cp_parser *parser, declarator. */ static tree -constrained_non_type_template_parm (bool *is_non_type, - cp_parameter_declarator *parm) +cp_parser_constrained_non_type_template_parm (bool *is_non_type, + cp_parameter_declarator *parm) { *is_non_type = true; cp_declarator *decl = parm->declarator; @@ -15912,20 +15930,13 @@ constrained_non_type_template_parm (bool *is_non_type, static tree finish_constrained_parameter (cp_parser *parser, cp_parameter_declarator *parmdecl, - bool *is_non_type, - bool *is_parameter_pack) + bool *is_non_type) { tree decl = parmdecl->decl_specifiers.type; tree id = get_unqualified_id (parmdecl->declarator); tree def = parmdecl->default_argument; tree proto = DECL_INITIAL (decl); - /* A template parameter constrained by a variadic concept shall also - be declared as a template parameter pack. */ - bool is_variadic = template_parameter_pack_p (proto); - if (is_variadic && !*is_parameter_pack) - cp_parser_error (parser, "variadic constraint introduced without %<...%>"); - /* Build the parameter. Return an error if the declarator was invalid. */ tree parm; if (TREE_CODE (proto) == TYPE_DECL) @@ -15934,7 +15945,7 @@ finish_constrained_parameter (cp_parser *parser, parm = cp_parser_constrained_template_template_parm (parser, proto, id, parmdecl); else - parm = constrained_non_type_template_parm (is_non_type, parmdecl); + parm = cp_parser_constrained_non_type_template_parm (is_non_type, parmdecl); if (parm == error_mark_node) return error_mark_node; @@ -15949,14 +15960,13 @@ finish_constrained_parameter (cp_parser *parser, /* Returns true if the parsed type actually represents the declaration of a type template-parameter. */ -static inline bool +static bool declares_constrained_type_template_parameter (tree type) { return (is_constrained_parameter (type) && TREE_CODE (TREE_TYPE (type)) == TEMPLATE_TYPE_PARM); } - /* Returns true if the parsed type actually represents the declaration of a template template-parameter. */ @@ -16132,12 +16142,11 @@ cp_parser_template_parameter (cp_parser* parser, bool *is_non_type, cp_lexer_consume_token (parser->lexer); } - // The parameter may have been constrained. + /* The parameter may have been constrained type parameter. */ if (is_constrained_parameter (parameter_declarator)) return finish_constrained_parameter (parser, parameter_declarator, - is_non_type, - is_parameter_pack); + is_non_type); // Now we're sure that the parameter is a non-type parameter. *is_non_type = true; @@ -16263,8 +16272,8 @@ cp_parser_type_parameter (cp_parser* parser, bool *is_parameter_pack) if (flag_concepts) { tree reqs = get_shorthand_constraints (current_template_parms); - if (tree r = cp_parser_requires_clause_opt (parser)) - reqs = conjoin_constraints (reqs, normalize_expression (r)); + if (tree dreqs = cp_parser_requires_clause_opt (parser)) + reqs = combine_constraint_expressions (reqs, dreqs); TEMPLATE_PARMS_CONSTRAINTS (current_template_parms) = reqs; } @@ -16370,6 +16379,7 @@ cp_parser_template_id (cp_parser *parser, /* If the next token corresponds to a template-id, there is no need to reparse it. */ cp_token *token = cp_lexer_peek_token (parser->lexer); + if (token->type == CPP_TEMPLATE_ID) { cp_lexer_consume_token (parser->lexer); @@ -16512,7 +16522,7 @@ cp_parser_template_id (cp_parser *parser, = make_location (token->location, token->location, parser->lexer); /* Check for concepts autos where they don't belong. We could - identify types in some cases of idnetifier TEMPL, looking ahead + identify types in some cases of identifier TEMPL, looking ahead for a CPP_SCOPE, but that would buy us nothing: we accept auto in types. We reject them in functions, but if what we have is an identifier, even with none_type we can't conclude it's NOT a @@ -16538,11 +16548,13 @@ cp_parser_template_id (cp_parser *parser, template_id = finish_template_type (templ, arguments, entering_scope); } - /* A template-like identifier may be a partial concept id. */ - else if (flag_concepts - && (template_id = (cp_parser_maybe_partial_concept_id - (parser, templ, arguments)))) - return template_id; + else if (concept_definition_p (templ)) + { + /* The caller will decide whether this is a concept check or type + constraint. */ + template_id = build2_loc (combined_loc, TEMPLATE_ID_EXPR, + boolean_type_node, templ, arguments); + } else if (variable_template_p (templ)) { template_id = lookup_template_variable (templ, arguments); @@ -16599,6 +16611,23 @@ cp_parser_template_id (cp_parser *parser, return template_id; } +/* Like cp_parser_template_id, called in non-type context. */ + +static tree +cp_parser_template_id_expr (cp_parser *parser, + bool template_keyword_p, + bool check_dependency_p, + bool is_declaration) +{ + tree x = cp_parser_template_id (parser, template_keyword_p, check_dependency_p, + none_type, is_declaration); + if (TREE_CODE (x) == TEMPLATE_ID_EXPR + && concept_check_p (x)) + /* We didn't check the arguments in cp_parser_template_id; do that now. */ + return build_concept_id (x); + return x; +} + /* Parse a template-name. template-name: @@ -17019,11 +17048,7 @@ cp_parser_template_argument (cp_parser* parser) /*check_dependency=*/true, /*ambiguous_decls=*/NULL, argument_start_token->location); - /* Handle a constrained-type-specifier for a non-type template - parameter. */ - if (tree decl = cp_parser_maybe_concept_name (parser, argument)) - argument = decl; - else if (TREE_CODE (argument) != TEMPLATE_DECL + if (TREE_CODE (argument) != TEMPLATE_DECL && TREE_CODE (argument) != UNBOUND_CLASS_TEMPLATE) cp_parser_error (parser, "expected template-name"); } @@ -17772,7 +17797,7 @@ cp_parser_simple_type_specifier (cp_parser* parser, else if (!flag_concepts) pedwarn (token->location, 0, "use of %<auto%> in parameter declaration " - "only available with %<-fconcepts%>"); + "only available with %<-fconcepts-ts%>"); } else type = make_auto (); @@ -17888,6 +17913,10 @@ cp_parser_simple_type_specifier (cp_parser* parser, if (flags & CP_PARSER_FLAGS_OPTIONAL) cp_parser_parse_tentatively (parser); + /* Remember current tentative parsing state -- if we know we need + a type, we can give better diagnostics here. */ + bool tent = cp_parser_parsing_tentatively (parser); + token = cp_lexer_peek_token (parser->lexer); /* Look for the optional `::' operator. */ @@ -17942,13 +17971,44 @@ cp_parser_simple_type_specifier (cp_parser* parser, type = NULL_TREE; } + if (!type && flag_concepts && decl_specs) + { + /* Try for a type-constraint with template arguments. We check + decl_specs here to avoid trying this for a functional cast. */ + + cp_parser_parse_tentatively (parser); + + type = cp_parser_template_id (parser, + /*template_keyword_p=*/false, + /*check_dependency_p=*/true, + none_type, + /*is_declaration=*/false); + if (type && concept_check_p (type)) + { + location_t loc = EXPR_LOCATION (type); + type = cp_parser_placeholder_type_specifier (parser, loc, + type, tent); + if (tent && type == error_mark_node) + /* Perhaps it's a concept-check expression. */ + cp_parser_simulate_error (parser); + } + else + cp_parser_simulate_error (parser); + + if (!cp_parser_parse_definitely (parser)) + type = NULL_TREE; + } + if (!type && cxx_dialect >= cxx17) { - /* Try class template argument deduction. */ + /* Try class template argument deduction or type-constraint without + template arguments. */ tree name = cp_parser_identifier (parser); if (name && TREE_CODE (name) == IDENTIFIER_NODE && parser->scope != error_mark_node) { + location_t loc + = cp_lexer_previous_token (parser->lexer)->location; tree tmpl = cp_parser_lookup_name (parser, name, none_type, /*is_template=*/false, @@ -17960,6 +18020,9 @@ cp_parser_simple_type_specifier (cp_parser* parser, && (DECL_CLASS_TEMPLATE_P (tmpl) || DECL_TEMPLATE_TEMPLATE_PARM_P (tmpl))) type = make_template_placeholder (tmpl); + else if (flag_concepts && tmpl && concept_definition_p (tmpl)) + type = cp_parser_placeholder_type_specifier (parser, loc, + tmpl, tent); else { type = error_mark_node; @@ -18031,6 +18094,140 @@ cp_parser_simple_type_specifier (cp_parser* parser, return type; } +/* Parse the remainder of a placholder-type-specifier. + + placeholder-type-specifier: + type-constraint_opt auto + type-constraint_opt decltype(auto) + + The raw form of the constraint is parsed in cp_parser_simple_type_specifier + and passed as TMPL. This function converts TMPL to an actual type-constraint, + parses the placeholder type, and performs some contextual syntactic analysis. + + LOC provides the location of the template name. + + TENTATIVE is true if the type-specifier parsing is tentative; in that case, + don't give an error if TMPL isn't a valid type-constraint, as the template-id + might actually be a concept-check, + + Note that the Concepts TS allows the auto or decltype(auto) to be + omitted in a constrained-type-specifier. */ + +tree +cp_parser_placeholder_type_specifier (cp_parser *parser, location_t loc, + tree tmpl, bool tentative) +{ + if (tmpl == error_mark_node) + return error_mark_node; + + tree orig_tmpl = tmpl; + + /* Get the arguments as written for subsequent analysis. */ + tree args = NULL_TREE; + if (TREE_CODE (tmpl) == TEMPLATE_ID_EXPR) + { + args = TREE_OPERAND (tmpl, 1); + tmpl = TREE_OPERAND (tmpl, 0); + } + if (args == NULL_TREE) + /* A concept-name with no arguments can't be an expression. */ + tentative = false; + + tsubst_flags_t complain = tentative ? tf_none : tf_warning_or_error; + + /* Get the concept and prototype parameter for the constraint. */ + tree_pair info = finish_type_constraints (tmpl, args, complain); + tree con = info.first; + tree proto = info.second; + if (con == error_mark_node) + return error_mark_node; + + /* As per the standard, require auto or decltype(auto), except in some + cases (template parameter lists, -fconcepts-ts enabled). */ + cp_token *placeholder = NULL, *open_paren = NULL, *close_paren = NULL; + if (cxx_dialect >= cxx2a) + { + if (cp_lexer_next_token_is_keyword (parser->lexer, RID_AUTO)) + placeholder = cp_lexer_consume_token (parser->lexer); + else if (cp_lexer_next_token_is_keyword (parser->lexer, RID_DECLTYPE)) + { + placeholder = cp_lexer_consume_token (parser->lexer); + open_paren = cp_parser_require (parser, CPP_OPEN_PAREN, + RT_OPEN_PAREN); + cp_parser_require_keyword (parser, RID_AUTO, RT_AUTO); + close_paren = cp_parser_require (parser, CPP_CLOSE_PAREN, + RT_CLOSE_PAREN, + open_paren->location); + } + } + + /* A type constraint constrains a contextually determined type or type + parameter pack. However, the the Concepts TS does allow concepts + to introduce non-type and template template parameters. */ + if (TREE_CODE (proto) != TYPE_DECL) + { + if (!flag_concepts_ts + || !processing_template_parmlist) + { + error_at (loc, "%qE does not constrain a type", DECL_NAME (con)); + inform (DECL_SOURCE_LOCATION (con), "concept defined here"); + return error_mark_node; + } + } + + /* In a template parameter list, a type-parameter can be introduced + by type-constraints alone. */ + if (processing_template_parmlist && !placeholder) + return build_constrained_parameter (con, proto, args); + + /* Diagnose issues placeholder issues. */ + if (!flag_concepts_ts + && !parser->in_result_type_constraint_p + && !placeholder) + { + tree id = build_nt (TEMPLATE_ID_EXPR, tmpl, args); + tree expr = DECL_P (orig_tmpl) ? DECL_NAME (con) : id; + error_at (input_location, + "expected %<auto%> or %<decltype(auto)%> after %qE", expr); + /* Fall through. This is an error of omission. */ + } + else if (parser->in_result_type_constraint_p && placeholder) + { + /* A trailing return type only allows type-constraints. */ + error_at (input_location, + "unexpected placeholder in constrained result type"); + } + + /* In a parameter-declaration-clause, a placeholder-type-specifier + results in an invented template parameter. */ + if (parser->auto_is_implicit_function_template_parm_p) + { + if (placeholder && token_is_decltype (placeholder)) + { + location_t loc = make_location (placeholder->location, + placeholder->location, + close_paren->location); + error_at (loc, "cannot declare a parameter with %<decltype(auto)%>"); + return error_mark_node; + } + tree parm = build_constrained_parameter (con, proto, args); + return synthesize_implicit_template_parm (parser, parm); + } + + /* Determine if the type should be deduced using template argument + deduction or decltype deduction. Note that the latter is always + used for type-constraints in trailing return types. */ + bool decltype_p = placeholder + ? placeholder->keyword == RID_DECLTYPE + : parser->in_result_type_constraint_p; + + /* Otherwise, this is the type of a variable or return type. */ + if (decltype_p) + return make_constrained_decltype_auto (con, args); + else + return make_constrained_auto (con, args); +} + /* Parse a type-name. type-name: @@ -18103,8 +18300,6 @@ cp_parser_type_name (cp_parser* parser, bool typename_keyword_p) && TREE_CODE (type_decl) == TYPE_DECL && TYPE_DECL_ALIAS_P (type_decl)) gcc_assert (DECL_TEMPLATE_INSTANTIATION (type_decl)); - else if (is_constrained_parameter (type_decl)) - /* Don't do anything. */ ; else cp_parser_simulate_error (parser); @@ -18116,105 +18311,6 @@ cp_parser_type_name (cp_parser* parser, bool typename_keyword_p) return type_decl; } -/* Check if DECL and ARGS can form a constrained-type-specifier. - If ARGS is non-null, we try to form a concept check of the - form DECL<?, ARGS> where ? is a wildcard that matches any - kind of template argument. If ARGS is NULL, then we try to - form a concept check of the form DECL<?>. */ - -static tree -cp_parser_maybe_constrained_type_specifier (cp_parser *parser, - tree decl, tree args) -{ - gcc_assert (args ? TREE_CODE (args) == TREE_VEC : true); - - /* If we a constrained-type-specifier cannot be deduced. */ - if (parser->prevent_constrained_type_specifiers) - return NULL_TREE; - - /* A constrained type specifier can only be found in an - overload set or as a reference to a template declaration. - - FIXME: This might be masking a bug. It's possible that - that the deduction below is causing template specializations - to be formed with the wildcard as an argument. */ - if (TREE_CODE (decl) != OVERLOAD && TREE_CODE (decl) != TEMPLATE_DECL) - return NULL_TREE; - - /* Try to build a call expression that evaluates the - concept. This can fail if the overload set refers - only to non-templates. */ - tree placeholder = build_nt (WILDCARD_DECL); - tree check = build_concept_check (decl, placeholder, args); - if (check == error_mark_node) - return NULL_TREE; - - /* Deduce the checked constraint and the prototype parameter. - - FIXME: In certain cases, failure to deduce should be a - diagnosable error. */ - tree conc; - tree proto; - if (!deduce_constrained_parameter (check, conc, proto)) - return NULL_TREE; - - /* In template parameter scope, this results in a constrained - parameter. Return a descriptor of that parm. */ - if (processing_template_parmlist) - return build_constrained_parameter (conc, proto, args); - - /* In a parameter-declaration-clause, constrained-type - specifiers result in invented template parameters. */ - if (parser->auto_is_implicit_function_template_parm_p) - { - tree x = build_constrained_parameter (conc, proto, args); - return synthesize_implicit_template_parm (parser, x); - } - else - { - /* Otherwise, we're in a context where the constrained - type name is deduced and the constraint applies - after deduction. */ - return make_constrained_auto (conc, args); - } - - return NULL_TREE; -} - -/* If DECL refers to a concept, return a TYPE_DECL representing - the result of using the constrained type specifier in the - current context. DECL refers to a concept if - - - it is an overload set containing a function concept taking a single - type argument, or - - - it is a variable concept taking a single type argument. */ - -static tree -cp_parser_maybe_concept_name (cp_parser* parser, tree decl) -{ - if (flag_concepts - && (TREE_CODE (decl) == OVERLOAD - || BASELINK_P (decl) - || variable_concept_p (decl))) - return cp_parser_maybe_constrained_type_specifier (parser, decl, NULL_TREE); - else - return NULL_TREE; -} - -/* Check if DECL and ARGS form a partial-concept-id. If so, - assign ID to the resulting constrained placeholder. - - Returns true if the partial-concept-id designates a placeholder - and false otherwise. Note that *id is set to NULL_TREE in - this case. */ - -static tree -cp_parser_maybe_partial_concept_id (cp_parser *parser, tree decl, tree args) -{ - return cp_parser_maybe_constrained_type_specifier (parser, decl, args); -} - /* Parse a non-class type-name, that is, either an enum-name, a typedef-name, or a concept-name. @@ -18245,10 +18341,6 @@ cp_parser_nonclass_name (cp_parser* parser) type_decl = strip_using_decl (type_decl); - /* If we found an overload set, then it may refer to a concept-name. */ - if (tree decl = cp_parser_maybe_concept_name (parser, type_decl)) - type_decl = decl; - if (TREE_CODE (type_decl) != TYPE_DECL && (objc_is_id (identifier) || objc_is_class_name (identifier))) { @@ -26886,30 +26978,279 @@ cp_parser_label_declaration (cp_parser* parser) } // -------------------------------------------------------------------------- // +// Concept definitions + +static tree +cp_parser_concept_definition (cp_parser *parser) +{ + gcc_assert (cp_lexer_next_token_is_keyword (parser->lexer, RID_CONCEPT)); + cp_lexer_consume_token (parser->lexer); + + cp_expr id = cp_parser_identifier (parser); + if (id == error_mark_node) + { + cp_parser_skip_to_end_of_statement (parser); + cp_parser_consume_semicolon_at_end_of_statement (parser); + return NULL_TREE; + } + + if (!cp_parser_require (parser, CPP_EQ, RT_EQ)) + { + cp_parser_skip_to_end_of_statement (parser); + cp_parser_consume_semicolon_at_end_of_statement (parser); + return error_mark_node; + } + + processing_constraint_expression_sentinel parsing_constraint; + tree init = cp_parser_constraint_expression (parser); + if (init == error_mark_node) + cp_parser_skip_to_end_of_statement (parser); + + /* Consume the trailing ';'. Diagnose the problem if it isn't there, + but continue as if it were. */ + cp_parser_consume_semicolon_at_end_of_statement (parser); + + return finish_concept_definition (id, init); +} + +// -------------------------------------------------------------------------- // // Requires Clause -// Parse a requires clause. -// -// requires-clause: -// 'requires' logical-or-expression -// -// The required logical-or-expression must be a constant expression. Note -// that we don't check that the expression is constepxr here. We defer until -// we analyze constraints and then, we only check atomic constraints. +/* Diagnose an expression that should appear in ()'s within a requires-clause + and suggest where to place those parentheses. */ + +static void +cp_parser_diagnose_ungrouped_constraint_plain (location_t loc) +{ + error_at (loc, "expression after %<requires%> must be enclosed " + "in parentheses"); +} + +static void +cp_parser_diagnose_ungrouped_constraint_rich (location_t loc) +{ + gcc_rich_location richloc (loc); + richloc.add_fixit_insert_before ("("); + richloc.add_fixit_insert_after (")"); + error_at (&richloc, "expression after %<requires%> must be enclosed " + "in parentheses"); +} + +/* Parse a primary expression within a constraint. */ + +static cp_expr +cp_parser_constraint_primary_expression (cp_parser *parser) +{ + cp_parser_parse_tentatively (parser); + cp_id_kind idk; + location_t loc = input_location; + cp_expr expr = cp_parser_primary_expression (parser, + /*address_p=*/false, + /*cast_p=*/false, + /*template_arg_p=*/false, + &idk); + expr.maybe_add_location_wrapper (); + if (expr != error_mark_node) + expr = finish_constraint_primary_expr (expr); + if (cp_parser_parse_definitely (parser)) + return expr; + + /* Retry the parse at a lower precedence. If that succeeds, diagnose the + error, but return the expression as if it were valid. */ + cp_parser_parse_tentatively (parser); + expr = cp_parser_simple_cast_expression (parser); + if (cp_parser_parse_definitely (parser)) + { + cp_parser_diagnose_ungrouped_constraint_rich (expr.get_location()); + return expr; + } + + /* Otherwise, something has gone wrong, but we can't generate a more + meaningful diagnostic or recover. */ + cp_parser_diagnose_ungrouped_constraint_plain (loc); + return error_mark_node; +} + +/* Examine the token following EXPR. If it is an operator in a non-logical + binary expression, diagnose that as an error. Returns ERROR_MARK_NODE. */ + +static cp_expr +cp_parser_check_non_logical_constraint (cp_parser *parser, cp_expr lhs) +{ + cp_token *token = cp_lexer_peek_token (parser->lexer); + switch (token->type) + { + default: + return lhs; + + /* Arithmetic operators. */ + case CPP_PLUS: + case CPP_MINUS: + case CPP_MULT: + case CPP_DIV: + case CPP_MOD: + /* Bitwise operators. */ + case CPP_AND: + case CPP_OR: + case CPP_XOR: + case CPP_RSHIFT: + case CPP_LSHIFT: + /* Relational operators. */ + /* FIXME: Handle '<=>'. */ + case CPP_EQ_EQ: + case CPP_NOT_EQ: + case CPP_LESS: + case CPP_GREATER: + case CPP_LESS_EQ: + case CPP_GREATER_EQ: + /* Conditional operator */ + case CPP_QUERY: + /* Pointer-to-member. */ + case CPP_DOT_STAR: + case CPP_DEREF_STAR: + /* Assignment operators. */ + case CPP_PLUS_EQ: + case CPP_MINUS_EQ: + case CPP_MULT_EQ: + case CPP_DIV_EQ: + case CPP_MOD_EQ: + case CPP_AND_EQ: + case CPP_OR_EQ: + case CPP_XOR_EQ: + case CPP_RSHIFT_EQ: + case CPP_LSHIFT_EQ: + break; + + case CPP_EQ: { + /* An equal sign may be part of the the definition of a function, + and not an assignment operator, when parsing the expression + for a trailing requires-clause. For example: + + template<typename T> + struct S { + S() requires C<T> = default; + } + + This is not an error. */ + if (cp_lexer_nth_token_is_keyword (parser->lexer, 2, RID_DELETE) + || cp_lexer_nth_token_is_keyword (parser->lexer, 2, RID_DEFAULT)) + return lhs; + + break; + } + } + + /* Try to parse the RHS as either the remainder of a conditional-expression + or a logical-or-expression so we can form a good diagnostic. */ + cp_parser_parse_tentatively (parser); + cp_expr rhs; + if (token->type == CPP_QUERY) + rhs = cp_parser_question_colon_clause (parser, lhs); + else + { + cp_lexer_consume_token (parser->lexer); + rhs = cp_parser_binary_expression (parser, false, false, false, + PREC_NOT_OPERATOR, NULL); + } + + /* If we couldn't parse the RHS, then emit the best diagnostic we can. */ + if (!cp_parser_parse_definitely (parser)) + { + cp_parser_diagnose_ungrouped_constraint_plain (token->location); + return error_mark_node; + } + + /* Otherwise, emit a fixit for the complete binary expression. */ + location_t loc = make_location (token->location, + lhs.get_start(), + rhs.get_finish()); + cp_parser_diagnose_ungrouped_constraint_rich (loc); + return error_mark_node; +} + +/* Parse a constraint-logical-and-expression. + + constraint-logical-and-expression: + primary-expression + constraint-logical-and-expression '&&' primary-expression */ + +static cp_expr +cp_parser_constraint_logical_and_expression (cp_parser *parser) +{ + cp_expr lhs = cp_parser_constraint_primary_expression (parser); + while (cp_lexer_next_token_is (parser->lexer, CPP_AND_AND)) + { + cp_token *op = cp_lexer_consume_token (parser->lexer); + tree rhs = cp_parser_constraint_primary_expression (parser); + lhs = finish_constraint_and_expr (op->location, lhs, rhs); + } + return cp_parser_check_non_logical_constraint (parser, lhs); +} + +/* Parse a constraint-logical-or-expression. + + constraint-logical-or-expression: + constraint-logical-and-expression + constraint-logical-or-expression '||' constraint-logical-and-expression */ + +static cp_expr +cp_parser_constraint_logical_or_expression (cp_parser *parser) +{ + cp_expr lhs = cp_parser_constraint_logical_and_expression (parser); + while (cp_lexer_next_token_is (parser->lexer, CPP_OR_OR)) + { + cp_token *op = cp_lexer_consume_token (parser->lexer); + cp_expr rhs = cp_parser_constraint_logical_and_expression (parser); + lhs = finish_constraint_or_expr (op->location, lhs, rhs); + } + return cp_parser_check_non_logical_constraint (parser, lhs); +} + +/* Parse the expression after a requires-clause. This has a different grammar + than that in the concepts TS. */ + static tree -cp_parser_requires_clause (cp_parser *parser) +cp_parser_requires_clause_expression (cp_parser *parser) { - // Parse the requires clause so that it is not automatically folded. + processing_constraint_expression_sentinel parsing_constraint; ++processing_template_decl; - tree expr = cp_parser_binary_expression (parser, false, false, - PREC_NOT_OPERATOR, NULL); + cp_expr expr = cp_parser_constraint_logical_or_expression (parser); if (check_for_bare_parameter_packs (expr)) expr = error_mark_node; --processing_template_decl; return expr; } -// Optionally parse a requires clause: +/* Parse a expression after a requires clause. + + constraint-expression: + logical-or-expression + + The required logical-or-expression must be a constant expression. Note + that we don't check that the expression is constepxr here. We defer until + we analyze constraints and then, we only check atomic constraints. */ + +static tree +cp_parser_constraint_expression (cp_parser *parser) +{ + processing_constraint_expression_sentinel parsing_constraint; + ++processing_template_decl; + cp_expr expr = cp_parser_binary_expression (parser, false, true, + PREC_NOT_OPERATOR, NULL); + if (check_for_bare_parameter_packs (expr)) + expr = error_mark_node; + --processing_template_decl; + expr.maybe_add_location_wrapper (); + return expr; +} + +/* Optionally parse a requires clause: + + requires-clause: + `requires` constraint-logical-or-expression. + [ConceptsTS] + `requires constraint-expression. */ + static tree cp_parser_requires_clause_opt (cp_parser *parser) { @@ -26920,17 +27261,21 @@ cp_parser_requires_clause_opt (cp_parser *parser) && tok->u.value == ridpointers[RID_REQUIRES]) { error_at (cp_lexer_peek_token (parser->lexer)->location, - "%<requires%> only available with %<-fconcepts%>"); + "%<requires%> only available with " + "%<-std=c++2a%> or %<-fconcepts%>"); /* Parse and discard the requires-clause. */ cp_lexer_consume_token (parser->lexer); - cp_parser_requires_clause (parser); + cp_parser_constraint_expression (parser); } return NULL_TREE; } cp_lexer_consume_token (parser->lexer); - return cp_parser_requires_clause (parser); -} + if (!flag_concepts_ts) + return cp_parser_requires_clause_expression (parser); + else + return cp_parser_constraint_expression (parser); +} /*--------------------------------------------------------------------------- Requires expressions @@ -26940,6 +27285,7 @@ cp_parser_requires_clause_opt (cp_parser *parser) requirement-expression: 'requires' requirement-parameter-list [opt] requirement-body */ + static tree cp_parser_requires_expression (cp_parser *parser) { @@ -26957,6 +27303,9 @@ cp_parser_requires_expression (cp_parser *parser) return error_mark_node; } + /* This is definitely a requires-expression. */ + cp_parser_commit_to_tentative_parse (parser); + tree parms, reqs; { /* Local parameters are delared as variables within the scope @@ -26997,13 +27346,15 @@ cp_parser_requires_expression (cp_parser *parser) /* This needs to happen after pop_bindings_and_leave_scope, as it reverses the parm chain. */ grokparms (parms, &parms); - return finish_requires_expr (parms, reqs); + loc = make_location (loc, loc, parser->lexer); + return finish_requires_expr (loc, parms, reqs); } /* Parse a parameterized requirement. requirement-parameter-list: '(' parameter-declaration-clause ')' */ + static tree cp_parser_requirement_parameter_list (cp_parser *parser) { @@ -27031,7 +27382,7 @@ cp_parser_requirement_body (cp_parser *parser) if (!braces.require_open (parser)) return error_mark_node; - tree reqs = cp_parser_requirement_list (parser); + tree reqs = cp_parser_requirement_seq (parser); if (!braces.require_close (parser)) return error_mark_node; @@ -27039,34 +27390,28 @@ cp_parser_requirement_body (cp_parser *parser) return reqs; } -/* Parse a list of requirements. +/* Parse a sequence of requirements. - requirement-list: + requirement-seq: requirement - requirement-list ';' requirement[opt] */ + requirement-seq requirement */ + static tree -cp_parser_requirement_list (cp_parser *parser) +cp_parser_requirement_seq (cp_parser *parser) { tree result = NULL_TREE; - while (true) + do { tree req = cp_parser_requirement (parser); - if (req == error_mark_node) - return error_mark_node; - - result = tree_cons (NULL_TREE, req, result); - - /* If we see a semi-colon, consume it. */ - if (cp_lexer_next_token_is (parser->lexer, CPP_SEMICOLON)) - cp_lexer_consume_token (parser->lexer); + if (req != error_mark_node) + result = tree_cons (NULL_TREE, req, result); + } while (cp_lexer_next_token_is_not (parser->lexer, CPP_CLOSE_BRACE)); - /* Stop processing at the end of the list. */ - if (cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_BRACE)) - break; - } + /* If there are no valid requirements, this is not a valid expression. */ + if (!result) + return error_mark_node; - /* Reverse the order of requirements so they are analyzed in - declaration order. */ + /* Reverse the order of requirements so they are analyzed in order. */ return nreverse (result); } @@ -27077,6 +27422,7 @@ cp_parser_requirement_list (cp_parser *parser) compound-requirement type-requirement nested-requirement */ + static tree cp_parser_requirement (cp_parser *parser) { @@ -27094,17 +27440,26 @@ cp_parser_requirement (cp_parser *parser) simple-requirement: expression ';' */ + static tree cp_parser_simple_requirement (cp_parser *parser) { - tree expr = cp_parser_expression (parser, NULL, false, false); + location_t start = cp_lexer_peek_token (parser->lexer)->location; + cp_expr expr = cp_parser_expression (parser, NULL, false, false); + if (expr == error_mark_node) + cp_parser_skip_to_end_of_statement (parser); + + cp_parser_consume_semicolon_at_end_of_statement (parser); + if (!expr || expr == error_mark_node) return error_mark_node; - if (!cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON)) - return error_mark_node; + /* Sometimes we don't get locations, so use the cached token location + as a reasonable approximation. */ + if (expr.get_location() == UNKNOWN_LOCATION) + expr.set_location (start); - return finish_simple_requirement (expr); + return finish_simple_requirement (expr.get_location (), expr); } /* Parse a type requirement @@ -27115,16 +27470,18 @@ cp_parser_simple_requirement (cp_parser *parser) required-type-name: type-name 'template' [opt] simple-template-id */ + static tree cp_parser_type_requirement (cp_parser *parser) { - cp_lexer_consume_token (parser->lexer); + cp_token *start_tok = cp_lexer_consume_token (parser->lexer); + location_t loc = cp_lexer_peek_token (parser->lexer)->location; // Save the scope before parsing name specifiers. tree saved_scope = parser->scope; tree saved_object_scope = parser->object_scope; tree saved_qualifying_scope = parser->qualifying_scope; - cp_parser_global_scope_opt (parser, /*current_scope_valid_p=*/true); + cp_parser_global_scope_opt (parser, /*current_scope_valid_p=*/false); cp_parser_nested_name_specifier_opt (parser, /*typename_keyword_p=*/true, /*check_dependency_p=*/false, @@ -27156,18 +27513,20 @@ cp_parser_type_requirement (cp_parser *parser) if (type == error_mark_node) cp_parser_skip_to_end_of_statement (parser); - if (!cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON)) - return error_mark_node; + cp_parser_consume_semicolon_at_end_of_statement (parser); + if (type == error_mark_node) return error_mark_node; - return finish_type_requirement (type); + loc = make_location (loc, start_tok->location, parser->lexer); + return finish_type_requirement (loc, type); } /* Parse a compound requirement compound-requirement: '{' expression '}' 'noexcept' [opt] trailing-return-type [opt] ';' */ + static tree cp_parser_compound_requirement (cp_parser *parser) { @@ -27176,12 +27535,26 @@ cp_parser_compound_requirement (cp_parser *parser) if (!braces.require_open (parser)) return error_mark_node; + cp_token *expr_token = cp_lexer_peek_token (parser->lexer); + tree expr = cp_parser_expression (parser, NULL, false, false); - if (!expr || expr == error_mark_node) - return error_mark_node; + if (expr == error_mark_node) + cp_parser_skip_to_closing_brace (parser); if (!braces.require_close (parser)) - return error_mark_node; + { + cp_parser_skip_to_end_of_statement (parser); + cp_parser_consume_semicolon_at_end_of_statement (parser); + return error_mark_node; + } + + /* If the expression was invalid, skip the remainder of the requirement. */ + if (!expr || expr == error_mark_node) + { + cp_parser_skip_to_end_of_statement (parser); + cp_parser_consume_semicolon_at_end_of_statement (parser); + return error_mark_node; + } /* Parse the optional noexcept. */ bool noexcept_p = false; @@ -27196,29 +27569,69 @@ cp_parser_compound_requirement (cp_parser *parser) if (cp_lexer_next_token_is (parser->lexer, CPP_DEREF)) { cp_lexer_consume_token (parser->lexer); + cp_token *tok = cp_lexer_peek_token (parser->lexer); + bool saved_result_type_constraint_p = parser->in_result_type_constraint_p; parser->in_result_type_constraint_p = true; + /* C++2a allows either a type-id or a type-constraint. Parsing + a type-id will subsume the parsing for a type-constraint but + allow for more syntactic forms (e.g., const C<T>*). */ type = cp_parser_trailing_type_id (parser); parser->in_result_type_constraint_p = saved_result_type_constraint_p; if (type == error_mark_node) return error_mark_node; + + location_t type_loc = make_location (tok->location, tok->location, + parser->lexer); + + /* Check that we haven't written something like 'const C<T>*'. */ + if (type_uses_auto (type)) + { + if (!is_auto (type)) + { + error_at (type_loc, + "result type is not a plain type-constraint"); + cp_parser_consume_semicolon_at_end_of_statement (parser); + return error_mark_node; + } + } + else if (!flag_concepts_ts) + /* P1452R2 removed the trailing-return-type option. */ + error_at (type_loc, + "return-type-requirement is not a type-constraint"); } - return finish_compound_requirement (expr, type, noexcept_p); + location_t loc = make_location (expr_token->location, + braces.open_location (), + parser->lexer); + + cp_parser_consume_semicolon_at_end_of_statement (parser); + + if (expr == error_mark_node || type == error_mark_node) + return error_mark_node; + + return finish_compound_requirement (loc, expr, type, noexcept_p); } /* Parse a nested requirement. This is the same as a requires clause. nested-requirement: requires-clause */ + static tree cp_parser_nested_requirement (cp_parser *parser) { - cp_lexer_consume_token (parser->lexer); - tree req = cp_parser_requires_clause (parser); + gcc_assert (cp_lexer_next_token_is_keyword (parser->lexer, RID_REQUIRES)); + cp_token *tok = cp_lexer_consume_token (parser->lexer); + location_t loc = cp_lexer_peek_token (parser->lexer)->location; + tree req = cp_parser_constraint_expression (parser); + if (req == error_mark_node) + cp_parser_skip_to_end_of_statement (parser); + loc = make_location (loc, tok->location, parser->lexer); + cp_parser_consume_semicolon_at_end_of_statement (parser); if (req == error_mark_node) return error_mark_node; - return finish_nested_requirement (req); + return finish_nested_requirement (loc, req); } /* Support Functions */ @@ -28145,6 +28558,11 @@ cp_parser_template_declaration_after_parameters (cp_parser* parser, else if (cxx_dialect >= cxx11 && cp_lexer_next_token_is_keyword (parser->lexer, RID_USING)) decl = cp_parser_alias_declaration (parser); + else if (cxx_dialect >= cxx2a /* Implies flag_concept. */ + && cp_lexer_next_token_is_keyword (parser->lexer, RID_CONCEPT) + && !cp_lexer_nth_token_is_keyword (parser->lexer, 2, RID_BOOL)) + /* Allow 'concept bool' to be handled as per the TS. */ + decl = cp_parser_concept_definition (parser); else { /* There are no access checks when parsing a template, as we do not @@ -28264,6 +28682,8 @@ cp_parser_template_introduction (cp_parser* parser, bool member_p) tree saved_object_scope = parser->object_scope; tree saved_qualifying_scope = parser->qualifying_scope; + cp_token *start_token = cp_lexer_peek_token (parser->lexer); + /* Look for the optional `::' operator. */ cp_parser_global_scope_opt (parser, /*current_scope_valid_p=*/false); @@ -28285,12 +28705,14 @@ cp_parser_template_introduction (cp_parser* parser, bool member_p) parser->object_scope = saved_object_scope; parser->qualifying_scope = saved_qualifying_scope; - if (concept_name == error_mark_node) + if (concept_name == error_mark_node + || (seen_error () && !concept_definition_p (tmpl_decl))) cp_parser_simulate_error (parser); /* Look for opening brace for introduction. */ matching_braces braces; braces.require_open (parser); + location_t open_loc = input_location; if (!cp_parser_parse_definitely (parser)) return false; @@ -28321,15 +28743,26 @@ cp_parser_template_introduction (cp_parser* parser, bool member_p) } /* Build and associate the constraint. */ - tree parms = finish_template_introduction (tmpl_decl, introduction_list); + location_t introduction_loc = make_location (open_loc, + start_token->location, + parser->lexer); + tree parms = finish_template_introduction (tmpl_decl, + introduction_list, + introduction_loc); if (parms && parms != error_mark_node) { + if (!flag_concepts_ts) + pedwarn (introduction_loc, 0, "template-introductions" + " are not part of C++20 concepts [-fconcepts-ts]"); + cp_parser_template_declaration_after_parameters (parser, parms, member_p); return true; } - error_at (token->location, "no matching concept for template-introduction"); + if (parms == NULL_TREE) + error_at (token->location, "no matching concept for template-introduction"); + return true; } @@ -28397,8 +28830,8 @@ cp_parser_explicit_template_declaration (cp_parser* parser, bool member_p) if (flag_concepts) { tree reqs = get_shorthand_constraints (current_template_parms); - if (tree r = cp_parser_requires_clause_opt (parser)) - reqs = conjoin_constraints (reqs, normalize_expression (r)); + if (tree treqs = cp_parser_requires_clause_opt (parser)) + reqs = combine_constraint_expressions (reqs, treqs); TEMPLATE_PARMS_CONSTRAINTS (current_template_parms) = reqs; } @@ -29635,6 +30068,9 @@ cp_parser_required_error (cp_parser *parser, case RT_THROW: gmsgid = G_("expected %<throw%>"); break; + case RT_AUTO: + gmsgid = G_("expected %<auto%>"); + break; case RT_LABEL: gmsgid = G_("expected %<__label__%>"); break; @@ -41910,36 +42346,15 @@ make_generic_type_name () static tree synthesize_implicit_template_parm (cp_parser *parser, tree constr) { - gcc_assert (current_binding_level->kind == sk_function_parms); - - /* Before committing to modifying any scope, if we're in an - implicit template scope, and we're trying to synthesize a - constrained parameter, try to find a previous parameter with - the same name. This is the same-type rule for abbreviated - function templates. - - NOTE: We can generate implicit parameters when tentatively - parsing a nested name specifier, only to reject that parse - later. However, matching the same template-id as part of a - direct-declarator should generate an identical template - parameter, so this rule will merge them. */ - if (parser->implicit_template_scope && constr) - { - tree t = parser->implicit_template_parms; - while (t) - { - if (equivalent_placeholder_constraints (TREE_TYPE (t), constr)) - { - tree d = TREE_VALUE (t); - if (TREE_CODE (d) == PARM_DECL) - /* Return the TEMPLATE_PARM_INDEX. */ - d = DECL_INITIAL (d); - return d; - } - t = TREE_CHAIN (t); - } + /* A requires-clause is not a function and cannot have placeholders. */ + if (current_binding_level->kind == sk_block) + { + error ("placeholder type not allowed in this context"); + return error_mark_node; } + gcc_assert (current_binding_level->kind == sk_function_parms); + /* We are either continuing a function template that already contains implicit template parameters, creating a new fully-implicit function template, or extending an existing explicit function template with implicit template @@ -42050,18 +42465,9 @@ synthesize_implicit_template_parm (cp_parser *parser, tree constr) tree synth_tmpl_parm; bool non_type = false; - if (proto == NULL_TREE || TREE_CODE (proto) == TYPE_DECL) - synth_tmpl_parm - = finish_template_type_parm (class_type_node, synth_id); - else if (TREE_CODE (proto) == TEMPLATE_DECL) - synth_tmpl_parm - = finish_constrained_template_template_parm (proto, synth_id); - else - { - synth_tmpl_parm = copy_decl (proto); - DECL_NAME (synth_tmpl_parm) = synth_id; - non_type = true; - } + /* Synthesize the type template parameter. */ + gcc_assert(!proto || TREE_CODE (proto) == TYPE_DECL); + synth_tmpl_parm = finish_template_type_parm (class_type_node, synth_id); /* Attach the constraint to the parm before processing. */ tree node = build_tree_list (NULL_TREE, synth_tmpl_parm); @@ -42073,6 +42479,13 @@ synthesize_implicit_template_parm (cp_parser *parser, tree constr) /*non_type=*/non_type, /*param_pack=*/false); + /* Mark the synthetic declaration "virtual". This is used when + comparing template-heads to determine if whether an abbreviated + function template is equivalent to an explicit template. + + Note that DECL_ARTIFICIAL is used elsewhere for template parameters. */ + DECL_VIRTUAL_P (TREE_VALUE (new_parm)) = true; + // Chain the new parameter to the list of implicit parameters. if (parser->implicit_template_parms) parser->implicit_template_parms @@ -42111,7 +42524,7 @@ synthesize_implicit_template_parm (cp_parser *parser, tree constr) if (tree req = TEMPLATE_PARM_CONSTRAINTS (tree_last (new_parm))) { tree reqs = TEMPLATE_PARMS_CONSTRAINTS (current_template_parms); - reqs = conjoin_constraints (reqs, req); + reqs = combine_constraint_expressions (reqs, req); TEMPLATE_PARMS_CONSTRAINTS (current_template_parms) = reqs; } |