Hi! The following patch is on top of the https://gcc.gnu.org/pipermail/gcc-patches/2025-June/686210.html patch which stopped treating override as conditional keyword in class properties. This PR mentions another problem; we emit a bogus warning on code like struct C {}; struct C final = {}; in C++98. In this case we parse final as conditional keyword in C++ (including pedwarn) but the caller then immediately aborts the tentative parse because it isn't followed by { nor (in some cases) : . I think we certainly shouldn't pedwarn on it, but I think we even shouldn't warn for it say for -Wc++11-compat, because we don't actually treat the identifier as conditional keyword even in C++11 and later. The patch only does this if final is the only class property conditional keyword, if one uses struct S __final final __final = {}; one gets the warning and duplicate diagnostics and later parsing errors.
Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? The above mentioned patch has been successfully bootstrapped/regtested as well. 2025-06-11 Jakub Jelinek <ja...@redhat.com> PR c++/120628 * parser.cc (cp_parser_elaborated_type_specifier): Use cp_parser_nth_token_starts_class_definition_p with extra argument 1 instead of cp_parser_next_token_starts_class_definition_p. (cp_parser_class_property_specifier_seq_opt): For final conditional keyword in C++98 check if the token after it isn't cp_parser_nth_token_starts_class_definition_p nor CPP_NAME and in that case break without consuming it nor warning. (cp_parser_class_head): Use cp_parser_nth_token_starts_class_definition_p with extra argument 1 instead of cp_parser_next_token_starts_class_definition_p. (cp_parser_next_token_starts_class_definition_p): Renamed to ... (cp_parser_nth_token_starts_class_definition_p): ... this. Add N argument. Use cp_lexer_peek_nth_token instead of cp_lexer_peek_token. * g++.dg/cpp0x/final1.C: New test. * g++.dg/cpp0x/final2.C: New test. * g++.dg/cpp0x/override6.C: New test. --- gcc/cp/parser.cc.jj 2025-06-11 09:37:26.025518542 +0200 +++ gcc/cp/parser.cc 2025-06-11 10:09:56.366661769 +0200 @@ -3091,8 +3091,8 @@ static cp_token *cp_parser_require_keywo (cp_parser *, enum rid, required_token); static bool cp_parser_token_starts_function_definition_p (cp_token *); -static bool cp_parser_next_token_starts_class_definition_p - (cp_parser *); +static bool cp_parser_nth_token_starts_class_definition_p + (cp_parser *, size_t); static bool cp_parser_next_token_ends_template_argument_p (cp_parser *); static bool cp_parser_nth_token_starts_template_argument_list_p @@ -21987,7 +21987,7 @@ cp_parser_elaborated_type_specifier (cp_ bool template_p = (template_parm_lists_apply - && (cp_parser_next_token_starts_class_definition_p (parser) + && (cp_parser_nth_token_starts_class_definition_p (parser, 1) || cp_lexer_next_token_is (parser->lexer, CPP_SEMICOLON))); /* An unqualified name was used to reference this type, so there were no qualifying templates. */ @@ -28032,6 +28032,13 @@ cp_parser_class_property_specifier_seq_o break; if (id_equal (token->u.value, "final")) { + /* For C++98, quietly ignore final in e.g. + struct S final = 24; */ + if (cxx_dialect == cxx98 + && virt_specifiers == VIRT_SPEC_UNSPECIFIED + && !cp_parser_nth_token_starts_class_definition_p (parser, 2) + && !cp_lexer_nth_token_is (parser->lexer, 2, CPP_NAME)) + break; maybe_warn_cpp0x (CPP0X_OVERRIDE_CONTROLS); virt_specifier = VIRT_SPEC_FINAL; } @@ -28255,7 +28262,7 @@ cp_parser_class_head (cp_parser* parser, class-head, since a class-head only appears as part of a class-specifier. We have to detect this situation before calling xref_tag, since that has irreversible side-effects. */ - if (!cp_parser_next_token_starts_class_definition_p (parser)) + if (!cp_parser_nth_token_starts_class_definition_p (parser, 1)) { cp_parser_error (parser, "expected %<{%> or %<:%>"); type = error_mark_node; @@ -35630,15 +35637,15 @@ cp_parser_token_starts_function_definiti || token->keyword == RID_RETURN); } -/* Returns TRUE iff the next token is the ":" or "{" beginning a class +/* Returns TRUE iff the Nth token is the ":" or "{" beginning a class definition. */ static bool -cp_parser_next_token_starts_class_definition_p (cp_parser *parser) +cp_parser_nth_token_starts_class_definition_p (cp_parser *parser, size_t n) { cp_token *token; - token = cp_lexer_peek_token (parser->lexer); + token = cp_lexer_peek_nth_token (parser->lexer, n); return (token->type == CPP_OPEN_BRACE || (token->type == CPP_COLON && !parser->colon_doesnt_start_class_def_p)); --- gcc/testsuite/g++.dg/cpp0x/final1.C.jj 2025-06-11 09:52:09.497799889 +0200 +++ gcc/testsuite/g++.dg/cpp0x/final1.C 2025-06-11 09:53:35.574660627 +0200 @@ -0,0 +1,11 @@ +// PR c++/120628 +// { dg-do compile { target c++98_only } } + +namespace A { + struct B {}; + struct B final = {}; +} +namespace C { + struct D { D (int, int); }; + struct D final (42, 0); +} --- gcc/testsuite/g++.dg/cpp0x/final2.C.jj 2025-06-11 09:53:40.465595895 +0200 +++ gcc/testsuite/g++.dg/cpp0x/final2.C 2025-06-11 10:04:03.792343682 +0200 @@ -0,0 +1,26 @@ +// PR c++/120628 +// { dg-do compile } +// { dg-options "" } +// { dg-additional-options "-pedantic" { target c++14 } } + +namespace U { + struct A {}; + struct A final = {}; +} +namespace V { + template <int N> + struct B {}; + template <int N> + struct B<N> final = {}; // { dg-warning "variable templates only available with" "" { target c++11_down } } +} +struct C { + struct D {}; + static D foo (); + struct D final = foo (); // { dg-warning "non-static data member initializers only available with" "" { target c++98_only } } +}; +namespace W { + struct E { struct F {}; }; + struct E::F final = {}; +} +template <int N> +struct V::B<N> final = {}; // { dg-warning "variable templates only available with" "" { target c++11_down } } --- gcc/testsuite/g++.dg/cpp0x/override6.C.jj 2025-06-11 09:48:32.266675018 +0200 +++ gcc/testsuite/g++.dg/cpp0x/override6.C 2025-06-11 10:04:40.028862492 +0200 @@ -0,0 +1,26 @@ +// PR c++/120628 +// { dg-do compile } +// { dg-options "" } +// { dg-additional-options "-pedantic" { target c++14 } } + +namespace U { + struct A {}; + struct A override = {}; +} +namespace V { + template <int N> + struct B {}; + template <int N> + struct B<N> override = {}; // { dg-warning "variable templates only available with" "" { target c++11_down } } +} +struct C { + struct D {}; + static D foo (); + struct D override = foo (); // { dg-warning "non-static data member initializers only available with" "" { target c++98_only } } +}; +namespace W { + struct E { struct F {}; }; + struct E::F override = {}; +} +template <int N> +struct V::B<N> override = {}; // { dg-warning "variable templates only available with" "" { target c++11_down } } Jakub