Hi! The testcase shows some accepts-invalid (the ones without alignas) and ice-on-invalid-code (the ones with alignas) cases. If the enum doesn't have an underlying type and is not a definition, the caller retries to parse it as elaborated type specifier. E.g. for enum struct S s it will then pedwarn that elaborated type specifier shouldn't have the struct/class keywords. The problem is if the enum specifier is not followed by { when it has underlying type. In that case we have already called cp_parser_parse_definitely to end the tentative parsing started at the beginning of cp_parser_enum_specifier. But the cp_parser_error (parser, "expected %<;%> or %<{%>"); doesn't emit any error because the whole function is called from yet another tentative parse and the caller starts parsing the elaborated type specifier where the cp_parser_enum_specifier stopped (i.e. after the underlying type token(s)). The ultimate caller than commits the tentative parsing (and even if it wouldn't, it wouldn't know what kind of error to report). I think after seeing enum {,struct,class} : type not being followed by { or ;, there is no reason not to report it right away, as it can't be valid C++, which is what the patch does. Not sure if we shouldn't also return error_mark_node instead of NULL_TREE, so that the caller doesn't try to parse it as elaborated type specifier (the patch doesn't do that right now).
Furthermore, while reading the code, I've noticed that parser->colon_corrects_to_scope_p is saved and set to false at the start of the function, but not restored back in some cases. Don't have a testcase where this would be a problem, but it just seems wrong. Either we can in the two spots replace return NULL_TREE; with { type = NULL_TREE; goto out; } or we could perhaps abuse warning_sentinel or create a special class with dtor to clean the flag up. And lastly, I've fixed some formatting issues in the function while reading it. Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? 2020-03-14 Jakub Jelinek <ja...@redhat.com> PR c++/90995 * parser.c (cp_parser_enum_specifier): Make sure to restore parser->colon_corrects_to_scope_p in all cases. If scoped enum or enum with underlying type is not followed by { or ;, call error_at rather than cp_parser_error. Formatting fixes. * g++.dg/cpp0x/enum40.C: New test. --- gcc/cp/parser.c.jj 2020-03-12 18:17:58.039230033 +0100 +++ gcc/cp/parser.c 2020-03-13 21:44:20.117085594 +0100 @@ -19043,24 +19043,24 @@ cp_parser_enum_specifier (cp_parser* par push_deferring_access_checks (dk_no_check); nested_name_specifier - = cp_parser_nested_name_specifier_opt (parser, - /*typename_keyword_p=*/true, - /*check_dependency_p=*/false, - /*type_p=*/false, - /*is_declaration=*/false); + = cp_parser_nested_name_specifier_opt (parser, + /*typename_keyword_p=*/true, + /*check_dependency_p=*/false, + /*type_p=*/false, + /*is_declaration=*/false); if (nested_name_specifier) { tree name; identifier = cp_parser_identifier (parser); - name = cp_parser_lookup_name (parser, identifier, - enum_type, - /*is_template=*/false, - /*is_namespace=*/false, - /*check_dependency=*/true, - /*ambiguous_decls=*/NULL, - input_location); + name = cp_parser_lookup_name (parser, identifier, + enum_type, + /*is_template=*/false, + /*is_namespace=*/false, + /*check_dependency=*/true, + /*ambiguous_decls=*/NULL, + input_location); if (name && name != error_mark_node) { type = TREE_TYPE (name); @@ -19117,7 +19117,10 @@ cp_parser_enum_specifier (cp_parser* par /* At this point this is surely not elaborated type specifier. */ if (!cp_parser_parse_definitely (parser)) - return NULL_TREE; + { + type = NULL_TREE; + goto out; + } if (cxx_dialect < cxx11) maybe_warn_cpp0x (CPP0X_SCOPED_ENUMS); @@ -19151,17 +19154,23 @@ cp_parser_enum_specifier (cp_parser* par if ((scoped_enum_p || underlying_type) && cp_lexer_next_token_is_not (parser->lexer, CPP_SEMICOLON)) { - cp_parser_error (parser, "expected %<;%> or %<{%>"); if (has_underlying_type) { + error_at (cp_lexer_peek_token (parser->lexer)->location, + "expected %<;%> or %<{%>"); type = NULL_TREE; goto out; } + else + cp_parser_error (parser, "expected %<;%> or %<{%>"); } } if (!has_underlying_type && !cp_parser_parse_definitely (parser)) - return NULL_TREE; + { + type = NULL_TREE; + goto out; + } if (nested_name_specifier) { @@ -19172,9 +19181,7 @@ cp_parser_enum_specifier (cp_parser* par push_scope (nested_name_specifier); } else if (TREE_CODE (nested_name_specifier) == NAMESPACE_DECL) - { - push_nested_namespace (nested_name_specifier); - } + push_nested_namespace (nested_name_specifier); } /* Issue an error message if type-definitions are forbidden here. */ --- gcc/testsuite/g++.dg/cpp0x/enum40.C.jj 2020-03-13 21:32:17.677763299 +0100 +++ gcc/testsuite/g++.dg/cpp0x/enum40.C 2020-03-13 21:31:51.283153431 +0100 @@ -0,0 +1,26 @@ +// PR c++/90995 +// { dg-do compile { target c++11 } } + +void +foo () +{ + enum : int a alignas; // { dg-error "expected" } +} + +void +bar () +{ + enum : int a; // { dg-error "expected" } +} + +void +baz () +{ + enum class a : int b alignas; // { dg-error "expected" } +} + +void +qux () +{ + enum class a : int b; // { dg-error "expected" } +} Jakub