This patch fixes the above PR where it was reported that the C++ frontend does not reject the malformed class declaration
struct X<5>; Instead of rejecting it, the FE treats this declaration as if it were a forward declaration of a template specialization, i.e. as if it were written template<> struct X<5>; First off, the FE should reject the declaration because it is malformed (not 100% sure, though). Second, since the user probably intended to have written an explicit template instantiation (as in the PR), the FE should suggest adding "template" before such a declaration, that is the declaration struct X<5>; // error + suggest adding "template" This patch does both these things along with adding error messages + suggestions for struct X<5> { }; // error + suggest adding "template <>" and template struct X<5> { }; // error + suggest replacing with "template <>" Bootstrap and regtesting in progress. Does this patch look OK for trunk? gcc/cp/ChangeLog: PR c++/16160 * parser.c (cp_parser_class_head): Identify and reject malformed template-id declarations and definitions. --- gcc/cp/parser.c | 53 +++++++++++++++++++++++--------- gcc/testsuite/g++.dg/cpp0x/gen-attrs-9.C | 2 +- gcc/testsuite/g++.dg/ext/attrib9.C | 2 +- gcc/testsuite/g++.dg/template/crash54.C | 2 +- gcc/testsuite/g++.dg/template/error55.C | 11 +++++++ 5 files changed, 53 insertions(+), 17 deletions(-) create mode 100644 gcc/testsuite/g++.dg/template/error55.C diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index 3290dfa..f6dc004 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -20264,6 +20264,34 @@ cp_parser_class_head (cp_parser* parser, } virt_specifiers = cp_parser_virt_specifier_seq_opt (parser); + /* Make sure a top-level template-id declaration or definition is preceded + by "template" or "template <>". */ + if (template_id_p + && at_namespace_scope_p () + && parser->num_template_parameter_lists == 0 + && !processing_explicit_instantiation) + { + if (cp_parser_next_token_starts_class_definition_p (parser)) + { + error_at (type_start_token->location, + "an explicit specialization must be preceded by " + "%<template <>%>"); + invalid_explicit_specialization_p = true; + /* Try to recover gracefully by taking the same action that would + have been taken by cp_parser_explicit_specialization. */ + ++parser->num_template_parameter_lists; + begin_specialization (); + } + else if (cp_parser_declares_only_class_p (parser)) + { + error_at (type_start_token->location, + "an explicit instantiation must be preceded by " + "%<template%>"); + type = error_mark_node; + goto out; + } + } + /* If it's not a `:' or a `{' then we can't really be looking at a class-head, since a class-head only appears as part of a class-specifier. We have to detect this situation before calling @@ -20275,6 +20303,16 @@ cp_parser_class_head (cp_parser* parser, goto out; } + if (processing_explicit_instantiation) + { + error_at (type_start_token->location, + "an explicit instantiation may not have a definition"); + inform (type_start_token->location, + "use %<template <>%> to define an explicit specialization"); + type = error_mark_node; + goto out; + } + /* At this point, we're going ahead with the class-specifier, even if some other problem occurs. */ cp_parser_commit_to_tentative_parse (parser); @@ -20346,20 +20384,7 @@ cp_parser_class_head (cp_parser* parser, num_templates = 0; } } - /* An explicit-specialization must be preceded by "template <>". If - it is not, try to recover gracefully. */ - if (at_namespace_scope_p () - && parser->num_template_parameter_lists == 0 - && template_id_p) - { - error_at (type_start_token->location, - "an explicit specialization must be preceded by %<template <>%>"); - invalid_explicit_specialization_p = true; - /* Take the same action that would have been taken by - cp_parser_explicit_specialization. */ - ++parser->num_template_parameter_lists; - begin_specialization (); - } + /* There must be no "return" statements between this point and the end of this function; set "type "to the correct return value and use "goto done;" to return. */ diff --git a/gcc/testsuite/g++.dg/cpp0x/gen-attrs-9.C b/gcc/testsuite/g++.dg/cpp0x/gen-attrs-9.C index 3dc51ee..4957ba1 100644 --- a/gcc/testsuite/g++.dg/cpp0x/gen-attrs-9.C +++ b/gcc/testsuite/g++.dg/cpp0x/gen-attrs-9.C @@ -9,4 +9,4 @@ enum [[gnu::unused]] e; // { dg-warning "already defined" } struct [[gnu::unused]] B *p; // { dg-warning "attributes" } template <class T> struct A { }; -struct [[gnu::unused]] A<int>; // { dg-warning "attributes" } +struct [[gnu::unused]] A<int> y; // { dg-warning "attributes" } diff --git a/gcc/testsuite/g++.dg/ext/attrib9.C b/gcc/testsuite/g++.dg/ext/attrib9.C index 6672f75..e8e158c 100644 --- a/gcc/testsuite/g++.dg/ext/attrib9.C +++ b/gcc/testsuite/g++.dg/ext/attrib9.C @@ -7,4 +7,4 @@ enum __attribute__((unused)) e; // { dg-warning "already defined" } struct __attribute((unused)) B *p; // { dg-warning "attributes" } template <class T> struct A { }; -struct __attribute((unused)) A<int>; // { dg-warning "attributes" } +struct __attribute((unused)) A<int> y; // { dg-warning "attributes" } diff --git a/gcc/testsuite/g++.dg/template/crash54.C b/gcc/testsuite/g++.dg/template/crash54.C index 26b4875..b1dbec0 100644 --- a/gcc/testsuite/g++.dg/template/crash54.C +++ b/gcc/testsuite/g++.dg/template/crash54.C @@ -2,4 +2,4 @@ template<int> struct A; -struct __attribute__((unused)) A<0<; // { dg-error "template argument|unqualified-id" } +struct __attribute__((unused)) A<0<; // { dg-error "template argument|explicit instantiation" } diff --git a/gcc/testsuite/g++.dg/template/error55.C b/gcc/testsuite/g++.dg/template/error55.C new file mode 100644 index 0000000..e40b3a1 --- /dev/null +++ b/gcc/testsuite/g++.dg/template/error55.C @@ -0,0 +1,11 @@ +// PR c++/16160 + +template <int N> struct X { }; +template <int N> struct Y { }; +template <int N> struct Z { }; + +struct X<2>; // { dg-error "explicit instantiation" } + +struct Y<2> { }; // { dg-error "explicit specialization" } + +template struct Z<2> { }; // { dg-error "may not have a definition" } -- 2.3.0.rc0