This little annoyance has bugged me a couple of times over the years:
template<typename T> struct A {}; template<typename T> using B = int; template<template<typename> class X> struct C; C<A> ca; // ok C<B> cb; // ok, not a class template template<template<typename> typename X> struct D; // error, cannot use typename here C++17 it seems allows typename in template template parms. Built and tested on x86_64-linux. Allowed with std=gnu/c++1z of course. OK?
cp/ 2014-07-22 Edward Smith-Rowland <3dw...@verizon.net> Implement N4051 - Allow typename in a template template parameter * parser.c (enum required_token): Add RT_TYPE_PARAMETER_KEY; (cp_parser_type_parameter_key): New funtion; (cp_parser_token_is_type_parameter_key): Ditto; (cp_parser_type_parameter): Look for type-parameter-key for cxx1z or or greater; (cp_parser_required_error): Error for RT_TYPE_PARAMETER_KEY. testsuite/ 2014-07-22 Edward Smith-Rowland <3dw...@verizon.net> Implement N4051 - Allow typename in a template template parameter * g++.dg/cpp1z/typename-tmpl-tmpl-parm.C: New.
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index 178114d..7fef8fc 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -177,6 +177,7 @@ typedef enum required_token { RT_INTERATION, /* iteration-statement */ RT_JUMP, /* jump-statement */ RT_CLASS_KEY, /* class-key */ + RT_TYPE_PARAMETER_KEY, /* type-parameter-key */ RT_CLASS_TYPENAME_TEMPLATE, /* class, typename, or template */ RT_TRANSACTION_ATOMIC, /* __transaction_atomic */ RT_TRANSACTION_RELAXED, /* __transaction_relaxed */ @@ -2151,6 +2152,8 @@ static tree cp_parser_class_head (cp_parser *, bool *); static enum tag_types cp_parser_class_key (cp_parser *); +static enum tag_types cp_parser_type_parameter_key + (cp_parser* parser); static void cp_parser_member_specification_opt (cp_parser *); static void cp_parser_member_declaration @@ -2409,6 +2412,8 @@ static bool cp_parser_nth_token_starts_template_argument_list_p (cp_parser *, size_t); static enum tag_types cp_parser_token_is_class_key (cp_token *); +static enum tag_types cp_parser_token_is_type_parameter_key + (cp_token *); static void cp_parser_check_class_key (enum tag_types, tree type); static void cp_parser_check_access_in_redeclaration @@ -13375,8 +13380,18 @@ cp_parser_type_parameter (cp_parser* parser, bool *is_parameter_pack) cp_parser_template_parameter_list (parser); /* Look for the `>'. */ cp_parser_require (parser, CPP_GREATER, RT_GREATER); - /* Look for the `class' keyword. */ - cp_parser_require_keyword (parser, RID_CLASS, RT_CLASS); + if (cxx_dialect < cxx1z) + { + /* Look for the `class' keyword. */ + cp_parser_require_keyword (parser, RID_CLASS, RT_CLASS); + } + else + { + /* Look for the `class' or 'typename' keywords. */ + enum tag_types tag_type = cp_parser_type_parameter_key (parser); + if (tag_type == none_type) + return error_mark_node; + } /* If the next token is an ellipsis, we have a template argument pack. */ if (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS)) @@ -20258,6 +20273,33 @@ cp_parser_class_key (cp_parser* parser) return tag_type; } +/* Parse a type-parameter-key. + + type-parameter-key: + class + typedef + + Returns the kind of type-parameter-key specified, or none_type to indicate + error. */ + +static enum tag_types +cp_parser_type_parameter_key (cp_parser* parser) +{ + cp_token *token; + enum tag_types tag_type; + + /* Look for the type-parameter-key. */ + token = cp_parser_require (parser, CPP_KEYWORD, RT_TYPE_PARAMETER_KEY); + if (!token) + return none_type; + + /* Check to see if the TOKEN is a type-parameter-key. */ + tag_type = cp_parser_token_is_type_parameter_key (token); + if (!tag_type) + cp_parser_error (parser, "expected type-parameter-key"); + return tag_type; +} + /* Parse an (optional) member-specification. member-specification: @@ -24543,6 +24585,9 @@ cp_parser_required_error (cp_parser *parser, case RT_CLASS_KEY: cp_parser_error (parser, "expected class-key"); return; + case RT_TYPE_PARAMETER_KEY: + cp_parser_error (parser, "expected %<class%> or %<typename%>"); + return; case RT_CLASS_TYPENAME_TEMPLATE: cp_parser_error (parser, "expected %<class%>, %<typename%>, or %<template%>"); @@ -24776,6 +24821,24 @@ cp_parser_token_is_class_key (cp_token* token) } } +/* Returns the kind of tag indicated by TOKEN, if it is a type-parameter-key, + or none_type otherwise. */ + +static enum tag_types +cp_parser_token_is_type_parameter_key (cp_token* token) +{ + switch (token->keyword) + { + case RID_CLASS: + return class_type; + case RID_TYPENAME: + return typename_type; + + default: + return none_type; + } +} + /* Issue an error message if the CLASS_KEY does not match the TYPE. */ static void diff --git a/gcc/testsuite/g++.dg/cpp1z/typename-tmpl-tmpl-parm.C b/gcc/testsuite/g++.dg/cpp1z/typename-tmpl-tmpl-parm.C new file mode 100644 index 0000000..0a4e26f --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/typename-tmpl-tmpl-parm.C @@ -0,0 +1,21 @@ +// { dg-options "-std=c++1z" } + +template<typename T> + struct A {}; + +template<typename T> + using B = int; + +template<template<typename> class X> + struct C {}; + +C<A> ca; + +C<B> cb; + +template<template<typename> typename X> + struct D {}; + +D<A> da; + +D<B> db;