This patch implements C++20 P0634R3, Down with typename! <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0634r3.html> which makes 'typename' optional in several contexts specified in [temp.res].
The gist of the patch is in cp_parser_simple_type_specifier, where, if the context makes typename optional and the id is qualified, we pretend we've seen the typename keyword. There's quite a lot of churn because we need to be careful where we want to make typename optional, and e.g. a flag in cp_parser would be too global. I'm not sure about some of the bits in typename5.C, not quite sure if the code is valid, but I didn't have time to investigate deeply and it seems pretty obscure anyway. There are preexisting cases when g++ and clang++ disagree. The resolve_typename_type hunk was to make typename9.C work with -fconcepts. Bootstrapped/regtested on x86_64-linux. 2018-11-12 Marek Polacek <pola...@redhat.com> Implement P0634R3, Down with typename! * parser.c (CP_PARSER_FLAGS_TYPENAME_OPTIONAL): New enumerator. (cp_parser_type_name): Remove declaration. (cp_parser_postfix_expression): Pass TYPENAME_OPTIONAL_P to cp_parser_type_id. (cp_parser_new_type_id): Pass TYPENAME_OPTIONAL_P to cp_parser_type_specifier_seq. (cp_parser_lambda_declarator_opt): Pass TYPENAME_OPTIONAL_P to cp_parser_parameter_declaration_clause. (cp_parser_condition): Adjust call to cp_parser_declarator. (cp_parser_simple_declaration): Adjust call to cp_parser_init_declarator. (cp_parser_conversion_type_id): Adjust call to cp_parser_type_specifier_seq. (cp_parser_default_type_template_argument): Pass TYPENAME_OPTIONAL_P to cp_parser_type_id. (cp_parser_template_parameter): Pass TYPENAME_OPTIONAL_P to cp_parser_parameter_declaration. (cp_parser_explicit_instantiation): Adjust call to cp_parser_declarator. (cp_parser_simple_type_specifier): Adjust call to cp_parser_type_name. (cp_parser_type_name): Remove unused function. (cp_parser_enum_specifier): Adjust call to cp_parser_type_specifier_seq. (cp_parser_alias_declaration): Pass TYPENAME_OPTIONAL_P to cp_parser_type_id. (cp_parser_init_declarator): New parameter. (cp_parser_declarator): New parameter. Use it. (cp_parser_direct_declarator): Likewise. (cp_parser_type_id_1): Likewise. (cp_parser_type_id): Likewise. (cp_parser_template_type_arg): Adjust call to cp_parser_type_id_1. (cp_parser_trailing_type_id): Pass TYPENAME_OPTIONAL_P to cp_parser_type_id_1. (cp_parser_type_specifier_seq): New parameter. Set flags to CP_PARSER_FLAGS_TYPENAME_OPTIONAL. (cp_parser_parameter_declaration_clause): New parameter. Use it. (cp_parser_parameter_declaration_list): Likewise. (cp_parser_parameter_declaration): Likewise. (cp_parser_member_declaration): Set flags to CP_PARSER_FLAGS_TYPENAME_OPTIONAL. (cp_parser_exception_declaration): Adjust calls to cp_parser_type_specifier_seq and cp_parser_declarator. (cp_parser_requirement_parameter_list): Adjust call to cp_parser_parameter_declaration_clause. (cp_parser_constructor_declarator_p): Resolve the TYPENAME_TYPE. (cp_parser_single_declaration): Set flags to CP_PARSER_FLAGS_TYPENAME_OPTIONAL. Pass TYPENAME_OPTIONAL_P to cp_parser_init_declarator. (cp_parser_cache_defarg): Adjust call to cp_parser_declarator. (cp_parser_objc_method_tail_params_opt): Adjust call to cp_parser_parameter_declaration. (cp_parser_objc_class_ivars): Adjust call to cp_parser_declarator. (cp_parser_objc_try_catch_finally_statement): Adjust call to cp_parser_parameter_declaration. (cp_parser_objc_struct_declaration): Adjust call to cp_parser_declarator. (cp_parser_omp_for_loop_init): Adjust calls to cp_parser_type_specifier_seq and cp_parser_declarator. * g++.dg/cpp0x/alias-decl-43.C: Adjust dg-error. * g++.dg/cpp0x/decltype67.C: Only expect error in c++17_down. * g++.dg/cpp1z/typename1.C: New test. * g++.dg/cpp2a/typename1.C: New test. * g++.dg/cpp2a/typename10.C: New test. * g++.dg/cpp2a/typename11.C: New test. * g++.dg/cpp2a/typename2.C: New test. * g++.dg/cpp2a/typename3.C: New test. * g++.dg/cpp2a/typename4.C: New test. * g++.dg/cpp2a/typename5.C: New test. * g++.dg/cpp2a/typename6.C: New test. * g++.dg/cpp2a/typename7.C: New test. * g++.dg/cpp2a/typename8.C: New test. * g++.dg/cpp2a/typename9.C: New test. * g++.dg/diagnostic/missing-typename.C: Only run the test in c++17_down. * g++.dg/other/crash-9.C: Add template disambiguator. * g++.dg/other/nontype-1.C: Only expect error in c++17_down. * g++.dg/parse/crash13.C: Likewise. * g++.dg/parse/error36.C: Likewise. * g++.dg/parse/typedef2.C: Likewise. * g++.dg/parse/typename11.C: Likewise. * g++.dg/template/crash48.C: Adjust dg-error. * g++.dg/template/dependent-name5.C: Only expect error in c++17_down. Add dg-error. * g++.dg/template/error29.C: Only expect error in c++17_down. * g++.dg/template/nested5.C: Add template disambiguator. * g++.dg/template/pr84789.C: Only expect error in c++17_down. * g++.dg/template/static30.C: Add dg-error. * g++.dg/template/typedef6.C: Adjust dg-error. * g++.dg/template/typename3.C: Only expect error in c++17_down. diff --git gcc/cp/parser.c gcc/cp/parser.c index deaca5cc974..4cc16b3e825 100644 --- gcc/cp/parser.c +++ gcc/cp/parser.c @@ -1790,7 +1790,9 @@ enum constexpr. */ CP_PARSER_FLAGS_ONLY_TYPE_OR_CONSTEXPR = 0x8, /* When parsing a decl-specifier-seq, only allow mutable or constexpr. */ - CP_PARSER_FLAGS_ONLY_MUTABLE_OR_CONSTEXPR = 0x10 + CP_PARSER_FLAGS_ONLY_MUTABLE_OR_CONSTEXPR = 0x10, + /* When parsing a decl-specifier-seq, allow missing typename. */ + CP_PARSER_FLAGS_TYPENAME_OPTIONAL = 0x20 }; /* This type is used for parameters and variables which hold @@ -2153,8 +2155,6 @@ static tree cp_parser_simple_type_specifier (cp_parser *, cp_decl_specifier_seq *, cp_parser_flags); static tree cp_parser_type_name (cp_parser *, bool); -static tree cp_parser_type_name - (cp_parser *); static tree cp_parser_nonclass_name (cp_parser* parser); static tree cp_parser_elaborated_type_specifier @@ -2196,11 +2196,11 @@ static tree cp_parser_decomposition_declaration static tree cp_parser_init_declarator (cp_parser *, cp_decl_specifier_seq *, vec<deferred_access_check, va_gc> *, - bool, bool, int, bool *, tree *, location_t *, tree *); + bool, bool, int, bool *, tree *, location_t *, tree *, bool); static cp_declarator *cp_parser_declarator - (cp_parser *, cp_parser_declarator_kind, int *, bool *, bool, bool); + (cp_parser *, cp_parser_declarator_kind, int *, bool *, bool, bool, bool); static cp_declarator *cp_parser_direct_declarator - (cp_parser *, cp_parser_declarator_kind, int *, bool, bool); + (cp_parser *, cp_parser_declarator_kind, int *, bool, bool, bool); static enum tree_code cp_parser_ptr_operator (cp_parser *, tree *, cp_cv_quals *, tree *); static cp_cv_quals cp_parser_cv_qualifier_seq_opt @@ -2216,20 +2216,20 @@ static tree cp_parser_late_return_type_opt static tree cp_parser_declarator_id (cp_parser *, bool); static tree cp_parser_type_id - (cp_parser *, location_t * = NULL); + (cp_parser *, location_t * = NULL, bool = false); static tree cp_parser_template_type_arg (cp_parser *); static tree cp_parser_trailing_type_id (cp_parser *); static tree cp_parser_type_id_1 - (cp_parser *, bool, bool, location_t *); + (cp_parser *, bool, bool, bool, location_t *); static void cp_parser_type_specifier_seq - (cp_parser *, bool, bool, cp_decl_specifier_seq *); + (cp_parser *, bool, bool, bool, cp_decl_specifier_seq *); static tree cp_parser_parameter_declaration_clause - (cp_parser *); + (cp_parser *, bool); static tree cp_parser_parameter_declaration_list - (cp_parser *); + (cp_parser *, bool); static cp_parameter_declarator *cp_parser_parameter_declaration - (cp_parser *, bool, bool *); + (cp_parser *, bool, bool *, bool); static tree cp_parser_default_argument (cp_parser *, bool); static void cp_parser_function_body @@ -6775,6 +6775,7 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p, cp_expr expression; const char *saved_message; bool saved_in_type_id_in_expr_p; + const bool typename_optional_p = (cxx_dialect >= cxx2a); /* All of these can be handled in the same way from the point of view of parsing. Begin by consuming the token @@ -6791,7 +6792,7 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p, /* Parse the type to which we are casting. */ saved_in_type_id_in_expr_p = parser->in_type_id_in_expr_p; parser->in_type_id_in_expr_p = true; - type = cp_parser_type_id (parser); + type = cp_parser_type_id (parser, NULL, typename_optional_p); parser->in_type_id_in_expr_p = saved_in_type_id_in_expr_p; /* Look for the closing `>'. */ cp_parser_require (parser, CPP_GREATER, RT_GREATER); @@ -8585,6 +8586,7 @@ cp_parser_new_type_id (cp_parser* parser, tree *nelts) cp_declarator *declarator; cp_declarator *outer_declarator; const char *saved_message; + const bool typename_optional_p = (cxx_dialect >= cxx2a); /* The type-specifier sequence must not contain type definitions. (It cannot contain declarations of new types either, but if they @@ -8596,6 +8598,7 @@ cp_parser_new_type_id (cp_parser* parser, tree *nelts) /* Parse the type-specifier-seq. */ cp_parser_type_specifier_seq (parser, /*is_declaration=*/false, /*is_trailing_return=*/false, + typename_optional_p, &type_specifier_seq); /* Restore the old message. */ parser->type_definition_forbidden_message = saved_message; @@ -10586,13 +10589,15 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr) opening parenthesis if present. */ if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN)) { + const bool typename_optional_p = (cxx_dialect >= cxx2a); matching_parens parens; parens.consume_open (parser); begin_scope (sk_function_parms, /*entity=*/NULL_TREE); /* Parse parameters. */ - param_list = cp_parser_parameter_declaration_clause (parser); + param_list + = cp_parser_parameter_declaration_clause (parser, typename_optional_p); /* Default arguments shall not be specified in the parameter-declaration-clause of a lambda-declarator. */ @@ -11752,7 +11757,8 @@ cp_parser_condition (cp_parser* parser) /*ctor_dtor_or_conv_p=*/NULL, /*parenthesized_p=*/NULL, /*member_p=*/false, - /*friend_p=*/false); + /*friend_p=*/false, + /*typename_optional_p=*/false); /* Parse the attributes. */ attributes = cp_parser_attributes_opt (parser); /* Parse the asm-specification. */ @@ -13231,7 +13237,8 @@ cp_parser_simple_declaration (cp_parser* parser, &function_definition_p, maybe_range_for_decl, &init_loc, - &auto_result); + &auto_result, + /*typename_optional_p=*/false); /* If an error occurred while parsing tentatively, exit quickly. (That usually happens when in the body of a function; each statement is treated as a declaration-statement until proven @@ -14488,6 +14495,7 @@ cp_parser_conversion_type_id (cp_parser* parser) /* Parse the type-specifiers. */ cp_parser_type_specifier_seq (parser, /*is_declaration=*/false, /*is_trailing_return=*/false, + /*typename_optional_p=*/false, &type_specifiers); parser->type_definition_forbidden_message = saved_message; @@ -15563,8 +15571,10 @@ cp_parser_default_type_template_argument (cp_parser *parser) cp_token *token = cp_lexer_peek_token (parser->lexer); /* Parse the default-argument. */ + const bool typename_optional_p = (cxx_dialect >= cxx2a); push_deferring_access_checks (dk_no_deferred); - tree default_argument = cp_parser_type_id (parser); + tree default_argument = cp_parser_type_id (parser, NULL, + typename_optional_p); pop_deferring_access_checks (); if (flag_concepts && type_uses_auto (default_argument)) @@ -15690,9 +15700,11 @@ cp_parser_template_parameter (cp_parser* parser, bool *is_non_type, template-parameter, the first non-nested `>' is taken as the end of the template parameter-list rather than a greater-than operator. */ + const bool typename_optional_p = (cxx_dialect >= cxx2a); parameter_declarator = cp_parser_parameter_declaration (parser, /*template_parm_p=*/true, - /*parenthesized_p=*/NULL); + /*parenthesized_p=*/NULL, + typename_optional_p); if (!parameter_declarator) return error_mark_node; @@ -16841,7 +16853,8 @@ cp_parser_explicit_instantiation (cp_parser* parser) /*ctor_dtor_or_conv_p=*/NULL, /*parenthesized_p=*/NULL, /*member_p=*/false, - /*friend_p=*/false); + /*friend_p=*/false, + /*typename_optional_p=*/false); if (declares_class_or_enum & 2) cp_parser_check_for_definition_in_return_type (declarator, decl_specifiers.type, @@ -17480,7 +17493,10 @@ cp_parser_simple_type_specifier (cp_parser* parser, } /* Otherwise, look for a type-name. */ else - type = cp_parser_type_name (parser); + { + bool typename_p = (flags & CP_PARSER_FLAGS_TYPENAME_OPTIONAL); + type = cp_parser_type_name (parser, (qualified_p && typename_p)); + } /* Keep track of all name-lookups performed in class scopes. */ if (type && !global_p @@ -17605,13 +17621,6 @@ cp_parser_simple_type_specifier (cp_parser* parser, Returns a TYPE_DECL for the type. */ -static tree -cp_parser_type_name (cp_parser* parser) -{ - return cp_parser_type_name (parser, /*typename_keyword_p=*/false); -} - -/* See above. */ static tree cp_parser_type_name (cp_parser* parser, bool typename_keyword_p) { @@ -18393,6 +18402,7 @@ cp_parser_enum_specifier (cp_parser* parser) /* Parse the type-specifier-seq. */ cp_parser_type_specifier_seq (parser, /*is_declaration=*/false, /*is_trailing_return=*/false, + /*typename_optional_p=*/false, &type_specifiers); /* At this point this is surely not elaborated type specifier. */ @@ -19180,6 +19190,7 @@ cp_parser_alias_declaration (cp_parser* parser) cp_decl_specifier_seq decl_specs; bool member_p; const char *saved_message = NULL; + const bool typename_optional_p = (cxx_dialect >= cxx2a); /* Look for the `using' keyword. */ cp_token *using_token @@ -19227,7 +19238,7 @@ cp_parser_alias_declaration (cp_parser* parser) G_("types may not be defined in alias template declarations"); } - type = cp_parser_type_id (parser, &type_location); + type = cp_parser_type_id (parser, &type_location, typename_optional_p); /* Restore the error message if need be. */ if (parser->num_template_parameter_lists) @@ -19635,7 +19646,8 @@ strip_declarator_types (tree type, cp_declarator *declarator) If INIT_LOC is not NULL, and *INIT_LOC is equal to UNKNOWN_LOCATION, and there is an initializer, the pointed location_t is set to the location of the '=' or `(', or '{' in C++11 token introducing the - initializer. */ + initializer. TYPENAME_OPTIONAL_P is true if the typename keyword is optional + in this context. */ static tree cp_parser_init_declarator (cp_parser* parser, @@ -19647,7 +19659,8 @@ cp_parser_init_declarator (cp_parser* parser, bool* function_definition_p, tree* maybe_range_for_decl, location_t* init_loc, - tree* auto_result) + tree* auto_result, + bool typename_optional_p) { cp_token *token = NULL, *asm_spec_start_token = NULL, *attributes_start_token = NULL; @@ -19697,7 +19710,7 @@ cp_parser_init_declarator (cp_parser* parser, = cp_parser_declarator (parser, CP_PARSER_DECLARATOR_NAMED, &ctor_dtor_or_conv_p, /*parenthesized_p=*/NULL, - member_p, friend_p); + member_p, friend_p, typename_optional_p); /* Gather up the deferred checks. */ stop_deferring_access_checks (); @@ -20113,14 +20126,17 @@ cp_parser_init_declarator (cp_parser* parser, MEMBER_P is true iff this declarator is a member-declarator. - FRIEND_P is true iff this declarator is a friend. */ + FRIEND_P is true iff this declarator is a friend. + + TYPENAME_OPTIONAL_P is true if the typename keyword is optional in this + context. */ static cp_declarator * cp_parser_declarator (cp_parser* parser, cp_parser_declarator_kind dcl_kind, int* ctor_dtor_or_conv_p, bool* parenthesized_p, - bool member_p, bool friend_p) + bool member_p, bool friend_p, bool typename_optional_p) { cp_declarator *declarator; enum tree_code code; @@ -20161,7 +20177,8 @@ cp_parser_declarator (cp_parser* parser, /*ctor_dtor_or_conv_p=*/NULL, /*parenthesized_p=*/NULL, /*member_p=*/false, - friend_p); + friend_p, + /*typename_optional_p=*/false); /* If we are parsing an abstract-declarator, we must handle the case where the dependent declarator is absent. */ @@ -20180,7 +20197,8 @@ cp_parser_declarator (cp_parser* parser, CPP_OPEN_PAREN); declarator = cp_parser_direct_declarator (parser, dcl_kind, ctor_dtor_or_conv_p, - member_p, friend_p); + member_p, friend_p, + typename_optional_p); } if (gnu_attributes && declarator && declarator != cp_error_declarator) @@ -20215,13 +20233,15 @@ cp_parser_declarator (cp_parser* parser, CP_PARSER_DECLARATOR_EITHER, if we can accept either - in the case of ambiguity we prefer an abstract declarator, as per [dcl.ambig.res]. CTOR_DTOR_OR_CONV_P, MEMBER_P, and FRIEND_P are - as for cp_parser_declarator. */ + as for cp_parser_declarator. TYPENAME_OPTIONAL_P is true if the + typename keyword is optional in this context. */ static cp_declarator * cp_parser_direct_declarator (cp_parser* parser, cp_parser_declarator_kind dcl_kind, int* ctor_dtor_or_conv_p, - bool member_p, bool friend_p) + bool member_p, bool friend_p, + bool typename_optional_p) { cp_token *token; cp_declarator *declarator = NULL; @@ -20306,7 +20326,9 @@ cp_parser_direct_declarator (cp_parser* parser, begin_scope (sk_function_parms, NULL_TREE); /* Parse the parameter-declaration-clause. */ - params = cp_parser_parameter_declaration_clause (parser); + params + = cp_parser_parameter_declaration_clause (parser, + typename_optional_p); /* Consume the `)'. */ parens.require_close (parser); @@ -20399,7 +20421,8 @@ cp_parser_direct_declarator (cp_parser* parser, declarator = cp_parser_declarator (parser, dcl_kind, ctor_dtor_or_conv_p, /*parenthesized_p=*/NULL, - member_p, friend_p); + member_p, friend_p, + typename_optional_p); parser->in_type_id_in_expr_p = saved_in_type_id_in_expr_p; first = false; /* Expect a `)'. */ @@ -21248,11 +21271,20 @@ cp_parser_declarator_id (cp_parser* parser, bool optional_p) type-id: type-specifier-seq abstract-declarator [opt] + If IS_TEMPLATE_ARG is true, we are parsing a template argument. + + If IS_TRAILING_RETURN is true, we are in a trailing-return-type, + i.e. we've just seen "->". + + TYPENAME_OPTIONAL_P is true if the typename keyword is optional in + this context. + Returns the TYPE specified. */ static tree -cp_parser_type_id_1 (cp_parser* parser, bool is_template_arg, - bool is_trailing_return, location_t * type_location) +cp_parser_type_id_1 (cp_parser *parser, bool is_template_arg, + bool is_trailing_return, bool typename_optional_p, + location_t *type_location) { cp_decl_specifier_seq type_specifier_seq; cp_declarator *abstract_declarator; @@ -21260,6 +21292,7 @@ cp_parser_type_id_1 (cp_parser* parser, bool is_template_arg, /* Parse the type-specifier-seq. */ cp_parser_type_specifier_seq (parser, /*is_declaration=*/false, is_trailing_return, + typename_optional_p, &type_specifier_seq); if (type_location) *type_location = type_specifier_seq.locations[ds_type_spec]; @@ -21284,7 +21317,8 @@ cp_parser_type_id_1 (cp_parser* parser, bool is_template_arg, = cp_parser_declarator (parser, CP_PARSER_DECLARATOR_ABSTRACT, NULL, /*parenthesized_p=*/NULL, /*member_p=*/false, - /*friend_p=*/false); + /*friend_p=*/false, + /*typename_optional_p=*/false); /* Check to see if there really was a declarator. */ if (!cp_parser_parse_definitely (parser)) abstract_declarator = NULL; @@ -21328,12 +21362,18 @@ cp_parser_type_id_1 (cp_parser* parser, bool is_template_arg, is_template_arg); } +/* Wrapper for cp_parser_type_id_1. */ + static tree -cp_parser_type_id (cp_parser *parser, location_t * type_location) +cp_parser_type_id (cp_parser *parser, location_t *type_location, + bool typename_optional_p) { - return cp_parser_type_id_1 (parser, false, false, type_location); + return cp_parser_type_id_1 (parser, false, false, typename_optional_p, + type_location); } +/* Wrapper for cp_parser_type_id_1. */ + static tree cp_parser_template_type_arg (cp_parser *parser) { @@ -21341,7 +21381,7 @@ cp_parser_template_type_arg (cp_parser *parser) const char *saved_message = parser->type_definition_forbidden_message; parser->type_definition_forbidden_message = G_("types may not be defined in template arguments"); - r = cp_parser_type_id_1 (parser, true, false, NULL); + r = cp_parser_type_id_1 (parser, true, false, false, NULL); parser->type_definition_forbidden_message = saved_message; if (cxx_dialect >= cxx14 && !flag_concepts && type_uses_auto (r)) { @@ -21351,10 +21391,13 @@ cp_parser_template_type_arg (cp_parser *parser) return r; } +/* Wrapper for cp_parser_type_id_1. */ + static tree cp_parser_trailing_type_id (cp_parser *parser) { - return cp_parser_type_id_1 (parser, false, true, NULL); + const bool typename_optional_p = (cxx_dialect >= cxx2a); + return cp_parser_type_id_1 (parser, false, true, typename_optional_p, NULL); } /* Parse a type-specifier-seq. @@ -21373,12 +21416,16 @@ cp_parser_trailing_type_id (cp_parser *parser) If IS_TRAILING_RETURN is true, we are in a trailing-return-type, i.e. we've just seen "->". + TYPENAME_OPTIONAL_P is true if the typename keyword is optional in + this context. + Sets *TYPE_SPECIFIER_SEQ to represent the sequence. */ static void cp_parser_type_specifier_seq (cp_parser* parser, bool is_declaration, bool is_trailing_return, + bool typename_optional_p, cp_decl_specifier_seq *type_specifier_seq) { bool seen_type_specifier = false; @@ -21394,6 +21441,9 @@ cp_parser_type_specifier_seq (cp_parser* parser, if (is_trailing_return) flags |= CP_PARSER_FLAGS_NO_TYPE_DEFINITIONS; + if (typename_optional_p) + flags |= CP_PARSER_FLAGS_TYPENAME_OPTIONAL; + /* Parse the type-specifiers and attributes. */ while (true) { @@ -21495,10 +21545,12 @@ function_being_declared_is_template_p (cp_parser* parser) Returns a representation for the parameter declarations. A return value of NULL indicates a parameter-declaration-clause consisting - only of an ellipsis. */ + only of an ellipsis. TYPENAME_OPTIONAL_P is true if the typename + keyword is optional in this context. */ static tree -cp_parser_parameter_declaration_clause (cp_parser* parser) +cp_parser_parameter_declaration_clause (cp_parser* parser, + bool typename_optional_p) { tree parameters; cp_token *token; @@ -21541,7 +21593,8 @@ cp_parser_parameter_declaration_clause (cp_parser* parser) } /* Parse the parameter-declaration-list. */ - parameters = cp_parser_parameter_declaration_list (parser); + parameters = cp_parser_parameter_declaration_list (parser, + typename_optional_p); /* If a parse error occurred while parsing the parameter-declaration-list, then the entire parameter-declaration-clause is erroneous. */ @@ -21586,10 +21639,13 @@ cp_parser_parameter_declaration_clause (cp_parser* parser) Returns a representation of the parameter-declaration-list, as for cp_parser_parameter_declaration_clause. However, the - `void_list_node' is never appended to the list. */ + `void_list_node' is never appended to the list. + TYPENAME_OPTIONAL_P is true if the typename keyword is optional in + this context. */ static tree -cp_parser_parameter_declaration_list (cp_parser* parser) +cp_parser_parameter_declaration_list (cp_parser* parser, + bool typename_optional_p) { tree parameters = NULL_TREE; tree *tail = ¶meters; @@ -21614,7 +21670,8 @@ cp_parser_parameter_declaration_list (cp_parser* parser) parameter = cp_parser_parameter_declaration (parser, /*template_parm_p=*/false, - &parenthesized_p); + &parenthesized_p, + typename_optional_p); /* We don't know yet if the enclosing context is deprecated, so wait and warn in grokparms if appropriate. */ @@ -21751,12 +21808,14 @@ cp_parser_parameter_declaration_list (cp_parser* parser) Returns a representation of the parameter, or NULL if an error occurs. If PARENTHESIZED_P is non-NULL, *PARENTHESIZED_P is set to - true iff the declarator is of the form "(p)". */ + true iff the declarator is of the form "(p)". TYPENAME_OPTIONAL_P is + true if the typename keyword is optional in this context. */ static cp_parameter_declarator * cp_parser_parameter_declaration (cp_parser *parser, bool template_parm_p, - bool *parenthesized_p) + bool *parenthesized_p, + bool typename_optional_p) { int declares_class_or_enum; cp_decl_specifier_seq decl_specifiers; @@ -21786,8 +21845,11 @@ cp_parser_parameter_declaration (cp_parser *parser, /* Parse the declaration-specifiers. */ cp_token *decl_spec_token_start = cp_lexer_peek_token (parser->lexer); + cp_parser_flags flags = CP_PARSER_FLAGS_NONE; + if (typename_optional_p) + flags |= CP_PARSER_FLAGS_TYPENAME_OPTIONAL; cp_parser_decl_specifier_seq (parser, - CP_PARSER_FLAGS_NONE, + flags, &decl_specifiers, &declares_class_or_enum); @@ -21849,7 +21911,8 @@ cp_parser_parameter_declaration (cp_parser *parser, /*ctor_dtor_or_conv_p=*/NULL, parenthesized_p, /*member_p=*/false, - /*friend_p=*/false); + /*friend_p=*/false, + /*typename_optional_p=*/false); parser->default_arg_ok_p = saved_default_arg_ok_p; /* After the declarator, allow more attributes. */ decl_specifiers.attributes @@ -23725,6 +23788,7 @@ cp_parser_member_declaration (cp_parser* parser) cp_token *initializer_token_start = NULL; int saved_pedantic; bool saved_colon_corrects_to_scope_p = parser->colon_corrects_to_scope_p; + cp_parser_flags flags = CP_PARSER_FLAGS_OPTIONAL; /* Check for the `__extension__' keyword. */ if (cp_parser_extension_opt (parser, &saved_pedantic)) @@ -23818,8 +23882,10 @@ cp_parser_member_declaration (cp_parser* parser) /* Parse the decl-specifier-seq. */ decl_spec_token_start = cp_lexer_peek_token (parser->lexer); + if (cxx_dialect >= cxx2a) + flags |= CP_PARSER_FLAGS_TYPENAME_OPTIONAL; cp_parser_decl_specifier_seq (parser, - CP_PARSER_FLAGS_OPTIONAL, + flags, &decl_specifiers, &declares_class_or_enum); /* Check for an invalid type-name. */ @@ -24049,6 +24115,7 @@ cp_parser_member_declaration (cp_parser* parser) cp_declarator *declarator; tree asm_specification; int ctor_dtor_or_conv_p; + const bool typename_optional_p = (cxx_dialect >= cxx2a); /* Parse the declarator. */ declarator @@ -24056,7 +24123,7 @@ cp_parser_member_declaration (cp_parser* parser) &ctor_dtor_or_conv_p, /*parenthesized_p=*/NULL, /*member_p=*/true, - friend_p); + friend_p, typename_optional_p); /* If something went wrong parsing the declarator, make sure that we at least consume some tokens. */ @@ -24917,6 +24984,7 @@ cp_parser_exception_declaration (cp_parser* parser) /* Parse the type-specifier-seq. */ cp_parser_type_specifier_seq (parser, /*is_declaration=*/true, /*is_trailing_return=*/false, + /*typename_optional_p=*/false, &type_specifiers); /* If it's a `)', then there is no declarator. */ if (cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN)) @@ -24926,7 +24994,8 @@ cp_parser_exception_declaration (cp_parser* parser) /*ctor_dtor_or_conv_p=*/NULL, /*parenthesized_p=*/NULL, /*member_p=*/false, - /*friend_p=*/false); + /*friend_p=*/false, + /*typename_optional_p=*/false); /* Restore the saved message. */ parser->type_definition_forbidden_message = saved_message; @@ -26078,7 +26147,9 @@ cp_parser_requirement_parameter_list (cp_parser *parser) if (!parens.require_open (parser)) return error_mark_node; - tree parms = cp_parser_parameter_declaration_clause (parser); + tree parms + = cp_parser_parameter_declaration_clause (parser, + /*typename_optional_p=*/false); if (!parens.require_close (parser)) return error_mark_node; @@ -26832,6 +26903,16 @@ cp_parser_constructor_declarator_p (cp_parser *parser, bool friend_p) /*type_p=*/false, /*is_declaration=*/false)); + /* Resolve the TYPENAME_TYPE, because the call above didn't do it. */ + if (nested_name_specifier + && TREE_CODE (nested_name_specifier) == TYPENAME_TYPE) + { + tree s = resolve_typename_type (nested_name_specifier, + /*only_current_p=*/false); + if (TREE_CODE (s) != TYPENAME_TYPE) + nested_name_specifier = s; + } + outside_class_specifier_p = (!at_class_scope_p () || !TYPE_BEING_DEFINED (current_class_type) || friend_p); @@ -27509,8 +27590,11 @@ cp_parser_single_declaration (cp_parser* parser, /* Try the `decl-specifier-seq [opt] init-declarator [opt]' alternative. */ decl_spec_token_start = cp_lexer_peek_token (parser->lexer); + cp_parser_flags flags = CP_PARSER_FLAGS_OPTIONAL; + if (cxx_dialect >= cxx2a) + flags |= CP_PARSER_FLAGS_TYPENAME_OPTIONAL; cp_parser_decl_specifier_seq (parser, - CP_PARSER_FLAGS_OPTIONAL, + flags, &decl_specifiers, &declares_class_or_enum); if (friend_p) @@ -27599,6 +27683,7 @@ cp_parser_single_declaration (cp_parser* parser, && (cp_lexer_next_token_is_not (parser->lexer, CPP_SEMICOLON) || decl_specifiers.type != error_mark_node)) { + const bool typename_optional_p = (cxx_dialect >= cxx2a); decl = cp_parser_init_declarator (parser, &decl_specifiers, checks, @@ -27606,7 +27691,8 @@ cp_parser_single_declaration (cp_parser* parser, member_p, declares_class_or_enum, &function_definition_p, - NULL, NULL, NULL); + NULL, NULL, NULL, + typename_optional_p); /* 7.1.1-1 [dcl.stc] @@ -29244,7 +29330,8 @@ cp_parser_cache_defarg (cp_parser *parser, bool nsdmi) &ctor_dtor_or_conv_p, /*parenthesized_p=*/NULL, /*member_p=*/true, - /*friend_p=*/false); + /*friend_p=*/false, + /*typename_optional_p=*/false); peek = cp_lexer_peek_token (parser->lexer); if (cp_parser_error_occurred (parser)) break; @@ -29260,7 +29347,7 @@ cp_parser_cache_defarg (cp_parser *parser, bool nsdmi) { cp_lexer_consume_token (parser->lexer); begin_scope (sk_function_parms, NULL_TREE); - if (cp_parser_parameter_declaration_list (parser) + if (cp_parser_parameter_declaration_list (parser, false) == error_mark_node) error = true; pop_bindings_and_leave_scope (); @@ -30276,7 +30363,7 @@ cp_parser_objc_method_tail_params_opt (cp_parser* parser, bool *ellipsisp, } /* TODO: parse attributes for tail parameters. */ - parmdecl = cp_parser_parameter_declaration (parser, false, NULL); + parmdecl = cp_parser_parameter_declaration (parser, false, NULL, false); parm = grokdeclarator (parmdecl->declarator, &parmdecl->decl_specifiers, PARM, /*initialized=*/0, @@ -30608,7 +30695,8 @@ cp_parser_objc_class_ivars (cp_parser* parser) &ctor_dtor_or_conv_p, /*parenthesized_p=*/NULL, /*member_p=*/false, - /*friend_p=*/false); + /*friend_p=*/false, + /*typename_optional_p=*/false); } /* Look for attributes that apply to the ivar. */ @@ -30958,7 +31046,7 @@ cp_parser_objc_try_catch_finally_statement (cp_parser *parser) { /* We have "@catch (NSException *exception)" or something like that. Parse the parameter declaration. */ - parm = cp_parser_parameter_declaration (parser, false, NULL); + parm = cp_parser_parameter_declaration (parser, false, NULL, false); if (parm == NULL) parameter_declaration = error_mark_node; else @@ -31164,7 +31252,7 @@ cp_parser_objc_struct_declaration (cp_parser *parser) /* Parse the declarator. */ declarator = cp_parser_declarator (parser, CP_PARSER_DECLARATOR_NAMED, - NULL, NULL, false, false); + NULL, NULL, false, false, false); /* Look for attributes that apply to the ivar. */ attributes = cp_parser_attributes_opt (parser); @@ -35774,6 +35862,7 @@ cp_parser_omp_for_loop_init (cp_parser *parser, cp_parser_parse_tentatively (parser); cp_parser_type_specifier_seq (parser, /*is_declaration=*/true, /*is_trailing_return=*/false, + /*typename_optional_p=*/false, &type_specifiers); if (cp_parser_parse_definitely (parser)) { @@ -35787,7 +35876,8 @@ cp_parser_omp_for_loop_init (cp_parser *parser, /*ctor_dtor_or_conv_p=*/NULL, /*parenthesized_p=*/NULL, /*member_p=*/false, - /*friend_p=*/false); + /*friend_p=*/false, + /*typename_optional_p=*/false); attributes = cp_parser_attributes_opt (parser); asm_specification = cp_parser_asm_specification_opt (parser); diff --git gcc/testsuite/g++.dg/cpp0x/alias-decl-43.C gcc/testsuite/g++.dg/cpp0x/alias-decl-43.C index 02eb33643ac..f9b4477448b 100644 --- gcc/testsuite/g++.dg/cpp0x/alias-decl-43.C +++ gcc/testsuite/g++.dg/cpp0x/alias-decl-43.C @@ -1,4 +1,4 @@ // PR c++/59120 // { dg-do compile { target c++11 } } -template<typename T> using X = int T::T*; // { dg-error "expected" } +template<typename T> using X = int T::T*; // { dg-error "expected|two or more" } diff --git gcc/testsuite/g++.dg/cpp0x/decltype67.C gcc/testsuite/g++.dg/cpp0x/decltype67.C index e8042ac59e7..f3bf9a24b04 100644 --- gcc/testsuite/g++.dg/cpp0x/decltype67.C +++ gcc/testsuite/g++.dg/cpp0x/decltype67.C @@ -3,5 +3,5 @@ template<typename T> struct A { - void foo(decltype(T())::Y); // { dg-error {decltype\(T\(\)\)::Y} } + void foo(decltype(T())::Y); // { dg-error "decltype\\(T\\(\\)\\)::Y" "" { target c++17_down } } }; diff --git gcc/testsuite/g++.dg/cpp1z/typename1.C gcc/testsuite/g++.dg/cpp1z/typename1.C new file mode 100644 index 00000000000..4c598c839f2 --- /dev/null +++ gcc/testsuite/g++.dg/cpp1z/typename1.C @@ -0,0 +1,117 @@ +// P0634R3 +// { dg-do compile { target c++17_only } } + +// (5.2.1) simple-declaration or a function-definition in namespace scope + +template<typename T> +T::X fn1 (int); // { dg-error "need .typename." } + +template<typename T> +T::X fn1 (int) // { dg-error "need .typename." } +{ + return 42; +} + +template<typename T> +T::X v1; // { dg-error "need .typename." } + +namespace N { + template<typename T> + T::X v2; // { dg-error "need .typename." } +} + +// (5.2.2) member-declaration + +template<typename T> +struct S { + [[noreturn]] T::X fn2 (); // { dg-error "need .typename." } + T::X fn3 (); // { dg-error "need .typename." } + T::X fn4 () { return 5; } // { dg-error "need .typename." } + T::X fn5 () final; // { dg-error "need .typename." } + T::X fn6 () = 0; // { dg-error "need .typename." } + T::X fn8 () override; // { dg-error "need .typename." } + T::X v3; // { dg-error "need .typename." } + T::X *v4; // { dg-error "need .typename." } + T::X v5[5]; // { dg-error "need .typename." } + T::X v6 = 0; // { dg-error "need .typename." } + T::X v7{0}; // { dg-error "need .typename.|;" } + T::X v8 : 16; // { dg-error "need .typename." } + static constexpr T::X v9 = 0; // { dg-error "need .typename." } + typedef T::X T2; // { dg-error "need .typename." } + friend T::X fn7<int> (); // { dg-error "need .typename." } + static inline T::X v10; // { dg-error "need .typename." } +}; + +// (5.2.3) parameter-declaration in a member-declaration, +// unless that parameter-declaration appears in a default argument + +template<typename T> +struct S2 { + friend int fn1<T::X> (); // { dg-error "" } + int fn2 (T::X p); // { dg-error "not a type" } + int fn5 (int = T::X); +}; + +// (5.2.4) parameter-declaration in a declarator of a function or function +// template declaration whose declarator-id is qualified, +// unless that parameter-declaration appears in a default argument +template<typename T> +int fn3 (T::X); + +template<typename T> +int fn4 (T::X p) { return p; } // { dg-error "" } + +// (5.2.6) parameter-declaration of a (non-type) template-parameter + +template<typename T, T::X N> // { dg-error "not a type" } +struct S3 { }; + +// default argument of a type-parameter of a template +template<typename T, typename U = T::X> // { dg-error "need .typename." } +struct S4 { }; + +// type-id of a static_cast, const_cast, reinterpret_cast, or dynamic_cast +template<typename T> +struct S5 { + void fn6 (T::X p) // { dg-error "not a type" } + { + int i = static_cast<T::Y>(p); // { dg-error "need .typename." } + i = dynamic_cast<T::Y>(p); // { dg-error "need .typename." } + i = reinterpret_cast<T::Y>(p); // { dg-error "need .typename." } + i = const_cast<T::Y>(p); // { dg-error "need .typename." } + } +}; + +template<typename T> +void fn7 (T::X p) // { dg-error "" } +{ + int i = static_cast<T::Y>(p); + i = dynamic_cast<T::Y>(p); + i = reinterpret_cast<T::Y>(p); + i = const_cast<T::Y>(p); +} + +// new-type-id +template<typename T> +void +fn8 () +{ + new T::X[10]; // { dg-error "need .typename." } +} + +// defining-type-id + +template<typename T> +struct W { typedef int M; }; + +template<typename T> +struct S6 { + using TT = T::X; // { dg-error "need .typename." } + using TT2 = W<T>::M; // { dg-error "need .typename." } +}; + +// trailing-return-type +template<typename T> +struct S7 { + auto fn9() -> W<T>::M; // { dg-error "need .typename." } +}; diff --git gcc/testsuite/g++.dg/cpp2a/typename1.C gcc/testsuite/g++.dg/cpp2a/typename1.C new file mode 100644 index 00000000000..833d3b86093 --- /dev/null +++ gcc/testsuite/g++.dg/cpp2a/typename1.C @@ -0,0 +1,42 @@ +// P0634R3 +// { dg-do compile { target c++2a } } + +// OK, return type of a function declaration at global scope. +template<class T> T::R f(); + +// Ill-formed (no diagnostic required), attempt to declare +// a void variable template +template<class T> void f(T::R); + +template <class T> struct A; +template <class T> using B = A<T>::U; + +template<typename T> +struct PtrTraits { typedef int Ptr; }; + +template<class T> struct S { + // OK, in a defining-type-id. + using Ptr = PtrTraits<T>::Ptr; + + // OK, trailing-return-type. + auto g() -> S<T*>::Ptr; + + // OK, class scope + T::R + f (T::P p) + { + // OK, type-id of a static_cast + return static_cast<T::R>(p); + } +}; + +template<typename T> +void f () +{ + // Variable pf of type void* initialized with T::X + void (*pf)(T::X); + + // Error: T::X at block scope does not denote a type + // (attempt to declare a void variable) + void g(T::X); // { dg-error "declared void" } +} diff --git gcc/testsuite/g++.dg/cpp2a/typename10.C gcc/testsuite/g++.dg/cpp2a/typename10.C new file mode 100644 index 00000000000..fa2cd000b5d --- /dev/null +++ gcc/testsuite/g++.dg/cpp2a/typename10.C @@ -0,0 +1,20 @@ +// P0634R3 +// { dg-do compile { target c++2a } } + +namespace N { + // template<typename T> extern T::type v; // #1a + template<typename T> T::type v(typename T::value); // #1b +} +template<typename T> T::type N::v(T::value); // #2 + +namespace N2 { + template<typename T> extern T::type v; // #1a + //template<typename T> T::type v(typename T::value); // #1b +} +template<typename T> T::type N2::v(T::value); // { dg-error "" } + +namespace A { + inline namespace B { template<typename T> int f(typename T::foo); } + inline namespace C { template<typename T> extern int f; } +} +template<typename T> int A::f(T::foo); // { dg-error "ambiguous" } diff --git gcc/testsuite/g++.dg/cpp2a/typename11.C gcc/testsuite/g++.dg/cpp2a/typename11.C new file mode 100644 index 00000000000..ed7ad958f62 --- /dev/null +++ gcc/testsuite/g++.dg/cpp2a/typename11.C @@ -0,0 +1,17 @@ +// P0634R3 +// { dg-do compile { target c++2a } } + +template<typename T> +struct A +{ + typedef int I; +}; + +template<typename T> struct B +{ + A<T>::I i; + typename A<T>::I i2; + + A<int>::I i3; + typename A<int>::I i4; +}; diff --git gcc/testsuite/g++.dg/cpp2a/typename2.C gcc/testsuite/g++.dg/cpp2a/typename2.C new file mode 100644 index 00000000000..7c926177004 --- /dev/null +++ gcc/testsuite/g++.dg/cpp2a/typename2.C @@ -0,0 +1,22 @@ +// P0634R3 +// { dg-do compile { target c++2a } } + +template<class T> typename T::R f(); + +template <class T> struct A; +template <class T> using B = typename A<T>::U; + +template<typename T> +struct PtrTraits { typedef int Ptr; }; + +template<class T> struct S { + using Ptr = typename PtrTraits<T>::Ptr; + + auto g() -> typename S<T*>::Ptr; + + typename T::R + f (typename T::P p) + { + return static_cast<typename T::R>(p); + } +}; diff --git gcc/testsuite/g++.dg/cpp2a/typename3.C gcc/testsuite/g++.dg/cpp2a/typename3.C new file mode 100644 index 00000000000..e64aa0316ef --- /dev/null +++ gcc/testsuite/g++.dg/cpp2a/typename3.C @@ -0,0 +1,23 @@ +// P0634R3 +// { dg-do compile { target c++2a } } + +template<class T> +void f(int i) +{ + T::x * i; // { dg-error "dependent-name" } +} + +struct Foo { + typedef int x; +}; + +struct Bar { + static int const x = 5; +}; + +int +main () +{ + f<Bar>(1); + f<Foo>(1); +} diff --git gcc/testsuite/g++.dg/cpp2a/typename4.C gcc/testsuite/g++.dg/cpp2a/typename4.C new file mode 100644 index 00000000000..69154e7daaf --- /dev/null +++ gcc/testsuite/g++.dg/cpp2a/typename4.C @@ -0,0 +1,8 @@ +// P0634R3 +// { dg-do compile { target c++2a } } + +template<class T> +struct A { + typedef int B; + B b; +}; diff --git gcc/testsuite/g++.dg/cpp2a/typename5.C gcc/testsuite/g++.dg/cpp2a/typename5.C new file mode 100644 index 00000000000..90f71028c65 --- /dev/null +++ gcc/testsuite/g++.dg/cpp2a/typename5.C @@ -0,0 +1,72 @@ +// P0634R3 +// { dg-do compile { target c++2a } } + +struct X { + template<typename T> + struct N { }; +}; + +template <typename T> +struct Y { + template<typename U> + struct N { }; +}; + +template <typename T> +struct A +{ + template <typename U> + struct N { }; + + // ??? Not sure if these are (in)valid. + typedef typename A::template N<int> a1; + typedef typename A::template N<T> a2; + typename A::template N<int> a3; + typename A::template N<T> a4; + typedef A::N<int> a5; // { dg-error "not a type|invalid" } + typedef A::N<T> a6; // { dg-error "not a type|invalid" } + typedef typename A::N<int> a7; // { dg-error "not a type" } + typedef typename A::N<T> a8; // { dg-error "not a type" } + A::N<int> a9; // { dg-error "not a type|invalid" } + A::N<T> a10; // { dg-error "not a type|invalid" } + typename A::N<int> a11; // { dg-error "not a type" } + typename A::N<T> a12; // { dg-error "not a type" } + typedef A<T>::N<int> a13; // { dg-error "not a type|invalid" } + typedef A<T>::N<T> a14; // { dg-error "not a type|invalid" } + + typedef typename X::template N<int> x1; + typedef typename X::template N<T> x2; + typename X::template N<int> x3; + typename X::template N<T> x4; + typedef X::N<int> x5; + typedef X::N<T> x6; + typedef typename X::N<int> x7; + typedef typename X::N<T> x8; + X::N<int> x9; + X::N<T> x10; + typename X::N<int> x11; + typename X::N<T> x12; + + typedef typename Y<int>::template N<int> y1; + typedef typename Y<int>::template N<T> y2; + typedef typename Y<T>::template N<int> y3; + typedef typename Y<T>::template N<T> y4; + typename Y<int>::template N<int> y5; + typename Y<int>::template N<T> y6; + typename Y<T>::template N<int> y7; + typename Y<T>::template N<T> y8; + typedef Y<int>::N<int> y9; + typedef Y<int>::N<T> y10; + typedef Y<T>::N<int> y11; // { dg-error "need .typename." } + typedef Y<T>::N<T> y12; // { dg-error "need .typename." } + typedef typename Y<int>::N<int> y13; + typedef typename Y<int>::N<T> y14; + Y<int>::N<int> y17; + Y<int>::N<T> y18; + typename Y<int>::N<int> y21; + typename Y<int>::N<T> y22; + typedef Y<int>::N<int> y25; + typedef Y<int>::N<T> y26; + typedef Y<T>::N<int> y27; // { dg-error "need .typename." } + typedef Y<T>::N<T> y28; // { dg-error "need .typename." } +}; diff --git gcc/testsuite/g++.dg/cpp2a/typename6.C gcc/testsuite/g++.dg/cpp2a/typename6.C new file mode 100644 index 00000000000..49e2235a53d --- /dev/null +++ gcc/testsuite/g++.dg/cpp2a/typename6.C @@ -0,0 +1,126 @@ +// P0634R3 +// { dg-do compile { target c++2a } } + +// (5.2.1) simple-declaration or a function-definition in namespace scope + +template<typename T> +T::X fn1 (int); + +template<typename T> +T::X fn1 (int) +{ + return 42; +} + +template<typename T> +T::X v1; + +namespace N { + template<typename T> + T::X v2; +} + +// (5.2.2) member-declaration + +template<typename T> +struct S { + [[noreturn]] T::X fn2 (); + T::X fn3 (); + T::X fn4 () { return 5; } + T::X fn5 () final; + T::X fn6 () = 0; + T::X fn8 () override; + T::X v3; + T::X *v4; + T::X v5[5]; + T::X v6 = 0; + T::X v7{0}; + T::X v8 : 16; + static constexpr T::X v9 = 0; + typedef T::X T2; + friend T::X fn7<int> (); + static inline T::X v10; +}; + +// (5.2.3) parameter-declaration in a member-declaration, +// unless that parameter-declaration appears in a default argument + +template<typename T> +struct S2 { + friend int fn1<T::X> (); + int fn2 (T::X p); + int fn5 (int = T::X); +}; + +// (5.2.4) parameter-declaration in a declarator of a function or function +// template declaration whose declarator-id is qualified, +// unless that parameter-declaration appears in a default argument +template<typename T> +int fn3 (T::X); + +template<typename T> +int fn4 (T::X p) { return p; } + +// (5.2.5) parameter-declaration in a lambda-declarator, +// unless that parameter-declaration appears in a default argument + +void +fn5 () +{ + auto j = []<typename T>(T::X t, int i) { return i; }; +} + +// (5.2.6) parameter-declaration of a (non-type) template-parameter + +template<typename T, T::X N> +struct S3 { }; + +// default argument of a type-parameter of a template +template<typename T, typename U = T::X> +struct S4 { }; + +// type-id of a static_cast, const_cast, reinterpret_cast, or dynamic_cast +template<typename T> +struct S5 { + void fn6 (T::X p) + { + int i = static_cast<T::Y>(p); + i = dynamic_cast<T::Y>(p); + i = reinterpret_cast<T::Y>(p); + i = const_cast<T::Y>(p); + } +}; + +template<typename T> +void fn7 (T::X p) +{ + int i = static_cast<T::Y>(p); + i = dynamic_cast<T::Y>(p); + i = reinterpret_cast<T::Y>(p); + i = const_cast<T::Y>(p); +} + +// new-type-id +template<typename T> +void +fn8 () +{ + new T::X[10]; +} + +// defining-type-id + +template<typename T> +struct W { typedef int M; }; + +template<typename T> +struct S6 { + using TT = T::X; + using TT2 = W<T>::M; +}; + +// trailing-return-type +template<typename T> +struct S7 { + auto fn9() -> W<T>::M; +}; diff --git gcc/testsuite/g++.dg/cpp2a/typename7.C gcc/testsuite/g++.dg/cpp2a/typename7.C new file mode 100644 index 00000000000..713db51d972 --- /dev/null +++ gcc/testsuite/g++.dg/cpp2a/typename7.C @@ -0,0 +1,26 @@ +// P0634R3 +// { dg-do compile { target c++2a } } + +// Not in namespace scope. +template<typename T> +void fn1 () +{ + // init-statement -> simple-declaration + if (T::X r = 0; 0) // { dg-error "need .typename.|expected" } + ; + + for (T::X g = 0; ;) // { dg-error "need .typename.|expected" } + ; +} + +template<typename T> +void +fn2 () +{ + T::X fn3 (); // { dg-error "need .typename.|expected" } + T::X v1; // { dg-error "need .typename.|expected" } + T::X v2 = 0; // { dg-error "need .typename.|expected" } + T::X v3{0}; // { dg-error "need .typename.|expected" } + static constexpr T::X v4 = 0; // { dg-error "need .typename." } + typedef T::X T2; // { dg-error "need .typename." } +} diff --git gcc/testsuite/g++.dg/cpp2a/typename8.C gcc/testsuite/g++.dg/cpp2a/typename8.C new file mode 100644 index 00000000000..3ebfde45ec5 --- /dev/null +++ gcc/testsuite/g++.dg/cpp2a/typename8.C @@ -0,0 +1,20 @@ +// P0634R3 +// { dg-do compile { target c++2a } } + +template<typename T> +struct S { + static int foo (); +}; + +template<typename T> +int +f () +{ + return S<T>::foo(); +} + +void +test () +{ + f<int>(); +} diff --git gcc/testsuite/g++.dg/cpp2a/typename9.C gcc/testsuite/g++.dg/cpp2a/typename9.C new file mode 100644 index 00000000000..7b1865222b3 --- /dev/null +++ gcc/testsuite/g++.dg/cpp2a/typename9.C @@ -0,0 +1,12 @@ +// P0634R3 +// { dg-do compile { target c++2a } } +// { dg-options "-fconcepts" } + +template <typename, typename> class A { class B; }; + +template <typename T, typename U> class A<T, U>::B { + B(A &); +}; + +template <typename T, typename U> +A<T, U>::B::B(A<T, U> &) {} diff --git gcc/testsuite/g++.dg/diagnostic/missing-typename.C gcc/testsuite/g++.dg/diagnostic/missing-typename.C index 21d1ed18a60..b088bebf92f 100644 --- gcc/testsuite/g++.dg/diagnostic/missing-typename.C +++ gcc/testsuite/g++.dg/diagnostic/missing-typename.C @@ -1,4 +1,5 @@ // fix-it hint for missing "typename" (PR c++/63392) +// { dg-do compile { target c++17_down } } // { dg-options "-fdiagnostics-show-caret" } template<typename T> diff --git gcc/testsuite/g++.dg/other/crash-9.C gcc/testsuite/g++.dg/other/crash-9.C index 0953fcbc46b..f5c4b2af329 100644 --- gcc/testsuite/g++.dg/other/crash-9.C +++ gcc/testsuite/g++.dg/other/crash-9.C @@ -11,5 +11,5 @@ class A }; }; template <typename T> template <typename U> -A<T>::B<U> A<T>::B<U>::foo() {} +A<T>::template B<U> A<T>::B<U>::foo() {} diff --git gcc/testsuite/g++.dg/other/nontype-1.C gcc/testsuite/g++.dg/other/nontype-1.C index 11bbfb82968..8d90c322a7e 100644 --- gcc/testsuite/g++.dg/other/nontype-1.C +++ gcc/testsuite/g++.dg/other/nontype-1.C @@ -1,7 +1,7 @@ template <class Op> bool asfun(Op f, - Op::first_argument_type a, // { dg-error "not a type" } - Op::second_argument_type b) // { dg-error "not a type" } + Op::first_argument_type a, // { dg-error "not a type" "" { target c++17_down } } + Op::second_argument_type b) // { dg-error "not a type" "" { target c++17_down } } { return Op(a, b); } diff --git gcc/testsuite/g++.dg/parse/crash13.C gcc/testsuite/g++.dg/parse/crash13.C index 3c298ec8ede..7a4939a462d 100644 --- gcc/testsuite/g++.dg/parse/crash13.C +++ gcc/testsuite/g++.dg/parse/crash13.C @@ -12,11 +12,11 @@ struct A }; template <typename T> -void func(A<T>::B* ) // { dg-error "variable|template|expression" } +void func(A<T>::B* ) // { dg-error "variable|template|expression" "" { target c++17_down } } { } int main() { - func<void>(0); // { dg-error "not declared|expression|;" } + func<void>(0); // { dg-error "not declared|expression|;" "" { target c++17_down } } } diff --git gcc/testsuite/g++.dg/parse/error36.C gcc/testsuite/g++.dg/parse/error36.C index 46080b4835d..7e52d1537e0 100644 --- gcc/testsuite/g++.dg/parse/error36.C +++ gcc/testsuite/g++.dg/parse/error36.C @@ -25,7 +25,7 @@ template <class T> struct B // PR c++/40738 template <class T> -void g(const A<T>::type &t); // { dg-error "typename" "typename" } +void g(const A<T>::type &t); // { dg-error "typename" "" { target c++17_down } } // PR c++/18451 -template <class T> A<T>::B A<T>::b; // { dg-error "typename" } +template <class T> A<T>::B A<T>::b; // { dg-error "typename" "" { target c++17_down } } diff --git gcc/testsuite/g++.dg/parse/typedef2.C gcc/testsuite/g++.dg/parse/typedef2.C index 1cc1ea0d58f..fd7554e19ac 100644 --- gcc/testsuite/g++.dg/parse/typedef2.C +++ gcc/testsuite/g++.dg/parse/typedef2.C @@ -1,2 +1,2 @@ template <typename T> struct B { typedef typename T::X X; }; -template <typename T> struct A { typedef B<T>::X::Y Z; }; // { dg-error "before 'B<T>::X::Y' because 'B<T>::X'" } +template <typename T> struct A { typedef B<T>::X::Y Z; }; // { dg-error "before 'B<T>::X::Y' because 'B<T>::X'" "" { target c++17_down } } diff --git gcc/testsuite/g++.dg/parse/typename11.C gcc/testsuite/g++.dg/parse/typename11.C index 22f300707c8..832913ffd67 100644 --- gcc/testsuite/g++.dg/parse/typename11.C +++ gcc/testsuite/g++.dg/parse/typename11.C @@ -10,7 +10,7 @@ template <int dim> struct Y : X<dim> { // note: I is nested type in X, not Y! template <int dim> -Y<dim>::I::I () {} // { dg-error "dependent typedef" "typedef" } -// { dg-error "no type|dependent type" "no type" { target *-*-* } .-1 } +Y<dim>::I::I () {} // { dg-error "expected|dependent typedef" "typedef" } +// { dg-error "no type|dependent type" "no type" { target c++17_down } .-1 } template struct Y<1>; diff --git gcc/testsuite/g++.dg/template/crash48.C gcc/testsuite/g++.dg/template/crash48.C index 6aa3aa3ed26..acbe4342542 100644 --- gcc/testsuite/g++.dg/template/crash48.C +++ gcc/testsuite/g++.dg/template/crash48.C @@ -7,4 +7,4 @@ template<typename T> struct A typedef typename T::X X; }; -template<typename T> A<T>::X::X() {} // { dg-error "no type|invalid use|not a type|dependent" } +template<typename T> A<T>::X::X() {} // { dg-error "expected|no type|invalid use|not a type|dependent" } diff --git gcc/testsuite/g++.dg/template/dependent-name5.C gcc/testsuite/g++.dg/template/dependent-name5.C index 681060c7002..3b99077edc9 100644 --- gcc/testsuite/g++.dg/template/dependent-name5.C +++ gcc/testsuite/g++.dg/template/dependent-name5.C @@ -17,12 +17,14 @@ struct A typedef Bar type1; typedef A::Bar type2; typedef A<T>::Bar type3; - typedef A<T*>::Bar type4; // { dg-error "" } + typedef A<T*>::Bar type4; // { dg-error "" "" { target c++17_down } } typedef typename A<T*>::Bar type5; typedef N<int> type6; typedef A::N<int> type7; +// { dg-error "" "" { target c++2a } .-1 } typedef A<T>::N<int> type8; +// { dg-error "" "" { target c++2a } .-1 } typedef A<T*>::template N<int> type9; // { dg-error "" } typedef typename A<T*>::template N<int> type10; @@ -36,7 +38,7 @@ struct A typedef A::N2 type12; typedef typename type12::K k2; - typedef type12::K k1; // { dg-error "" } + typedef type12::K k1; // { dg-error "" "" { target c++17_down } } // Check that A::Bar2 is not considered dependent even if we use // the typename keyword. diff --git gcc/testsuite/g++.dg/template/error29.C gcc/testsuite/g++.dg/template/error29.C index 2e2291d7e87..6e335487224 100644 --- gcc/testsuite/g++.dg/template/error29.C +++ gcc/testsuite/g++.dg/template/error29.C @@ -1,5 +1,5 @@ // PR c++/33209 -template<typename T> void foo(int, T::x); // { dg-error "T::x" } +template<typename T> void foo(int, T::x); // { dg-error "T::x" "" { target c++17_down } } -template<template<typename> class T> void foo2(int, T<int>::x); // { dg-error "T<int>::x" } +template<template<typename> class T> void foo2(int, T<int>::x); // { dg-error "T<int>::x" "" { target c++17_down } } diff --git gcc/testsuite/g++.dg/template/nested5.C gcc/testsuite/g++.dg/template/nested5.C index 3850fdace3a..e3e871e6d85 100644 --- gcc/testsuite/g++.dg/template/nested5.C +++ gcc/testsuite/g++.dg/template/nested5.C @@ -6,7 +6,7 @@ template <typename T> struct A { template <typename U> struct D {}; }; - template <typename S> static C::D<S> bar (S const &); + template <typename S> static C::template D<S> bar (S const &); }; struct E {}; diff --git gcc/testsuite/g++.dg/template/pr84789.C gcc/testsuite/g++.dg/template/pr84789.C index 63b9832fecf..bdf80dc3edf 100644 --- gcc/testsuite/g++.dg/template/pr84789.C +++ gcc/testsuite/g++.dg/template/pr84789.C @@ -9,5 +9,5 @@ template<typename> struct B : A {}; template<typename T> struct C : B<T> { - B<T>::A::I::I i; // { dg-error "not a class type|does not name a type|typename" } + B<T>::A::I::I i; // { dg-error "not a class type|does not name a type|typename" "" { target c++17_down } } }; diff --git gcc/testsuite/g++.dg/template/static30.C gcc/testsuite/g++.dg/template/static30.C index 07dafe23ffa..8b8637a1abe 100644 --- gcc/testsuite/g++.dg/template/static30.C +++ gcc/testsuite/g++.dg/template/static30.C @@ -6,5 +6,5 @@ template <int> struct A static const int i2; }; -template <int N> const int A<N>::i1(A<N>::i); +template <int N> const int A<N>::i1(A<N>::i); // { dg-error "no declaration matches" "" { target c++2a } } template <int N> const int A<N>::i2(3, A<N>::i); // { dg-error "expression list" } diff --git gcc/testsuite/g++.dg/template/typedef6.C gcc/testsuite/g++.dg/template/typedef6.C index c95945966fa..a6202b55181 100644 --- gcc/testsuite/g++.dg/template/typedef6.C +++ gcc/testsuite/g++.dg/template/typedef6.C @@ -5,4 +5,4 @@ template<typename T> struct A typedef struct typename T::X X; // { dg-error "expected identifier|two or more" } }; -template<typename T> A<T>::X::X() {} // { dg-error "not a type|forbids declaration|invalid use of" } +template<typename T> A<T>::X::X() {} // { dg-error "expected|not a type|forbids declaration|invalid use of" } diff --git gcc/testsuite/g++.dg/template/typename3.C gcc/testsuite/g++.dg/template/typename3.C index 0ad9a2a0c41..ae10af27763 100644 --- gcc/testsuite/g++.dg/template/typename3.C +++ gcc/testsuite/g++.dg/template/typename3.C @@ -3,5 +3,5 @@ template <class A> struct B { - typedef A::C::D E; // { dg-error "" } + typedef A::C::D E; // { dg-error "" "" { target c++17_down } } };