From: Jakub Jelinek Date: Thu, 10 Jul 2025 21:41:56 +0000 (+0200) Subject: c++: Don't incorrectly reject override after class head name [PR120569] X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=bcb51fe0e26bed7e2c44c4822ca6dec135ba61f3;p=thirdparty%2Fgcc.git c++: Don't incorrectly reject override after class head name [PR120569] While the https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p2786r13.html#c03-compatibility-changes-for-annex-c-diff.cpp03.dcl.dcl hunk dropped because struct C {}; struct C final {}; is actually not valid C++98 (which didn't have list initialization), we actually also reject struct D {}; struct D override {}; and that IMHO is valid all the way from C++11 onwards. Especially in the light of P2786R13 adding new contextual keywords, I think it is better to use a separate routine for parsing the class-virt-specifier-seq (in C++11, there was export next to final), class-virt-specifier (in C++14 to C++23) and class-property-specifier-seq (in C++26) instead of using the same function for virt-specifier-seq and class-property-specifier-seq. 2025-07-10 Jakub Jelinek PR c++/120569 * parser.cc (cp_parser_class_property_specifier_seq_opt): New function. (cp_parser_class_head): Use it instead of cp_parser_property_specifier_seq_opt. Don't diagnose VIRT_SPEC_OVERRIDE here. Formatting fix. * g++.dg/cpp0x/override2.C: Expect different diagnostics with override or duplicate final. * g++.dg/cpp0x/override5.C: New test. * g++.dg/cpp0x/duplicate1.C: Expect different diagnostics with duplicate final. --- diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index d96fdf8f927..1f58425a70b 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -28068,6 +28068,57 @@ cp_parser_class_specifier (cp_parser* parser) return type; } +/* Parse an (optional) class-property-specifier-seq. + + class-property-specifier-seq: + class-property-specifier class-property-specifier-seq [opt] + + class-property-specifier: + final + + Returns a bitmask representing the class-property-specifiers. */ + +static cp_virt_specifiers +cp_parser_class_property_specifier_seq_opt (cp_parser *parser) +{ + cp_virt_specifiers virt_specifiers = VIRT_SPEC_UNSPECIFIED; + + while (true) + { + cp_token *token; + cp_virt_specifiers virt_specifier; + + /* Peek at the next token. */ + token = cp_lexer_peek_token (parser->lexer); + /* See if it's a class-property-specifier. */ + if (token->type != CPP_NAME) + break; + if (id_equal (token->u.value, "final")) + { + maybe_warn_cpp0x (CPP0X_OVERRIDE_CONTROLS); + virt_specifier = VIRT_SPEC_FINAL; + } + else if (id_equal (token->u.value, "__final")) + virt_specifier = VIRT_SPEC_FINAL; + else + break; + + if (virt_specifiers & virt_specifier) + { + gcc_rich_location richloc (token->location); + richloc.add_fixit_remove (); + error_at (&richloc, "duplicate %qD specifier", token->u.value); + cp_lexer_purge_token (parser->lexer); + } + else + { + cp_lexer_consume_token (parser->lexer); + virt_specifiers |= virt_specifier; + } + } + return virt_specifiers; +} + /* Parse a class-head. class-head: @@ -28258,12 +28309,10 @@ cp_parser_class_head (cp_parser* parser, pop_deferring_access_checks (); if (id) - { - cp_parser_check_for_invalid_template_id (parser, id, - class_key, - type_start_token->location); - } - virt_specifiers = cp_parser_virt_specifier_seq_opt (parser); + cp_parser_check_for_invalid_template_id (parser, id, + class_key, + type_start_token->location); + virt_specifiers = cp_parser_class_property_specifier_seq_opt (parser); /* 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 @@ -28279,13 +28328,6 @@ cp_parser_class_head (cp_parser* parser, /* At this point, we're going ahead with the class-specifier, even if some other problem occurs. */ cp_parser_commit_to_tentative_parse (parser); - if (virt_specifiers & VIRT_SPEC_OVERRIDE) - { - cp_parser_error (parser, - "cannot specify % for a class"); - type = error_mark_node; - goto out; - } /* Issue the error about the overly-qualified name now. */ if (qualified_p) { diff --git a/gcc/testsuite/g++.dg/cpp0x/duplicate1.C b/gcc/testsuite/g++.dg/cpp0x/duplicate1.C index 1545e1cd4b5..4e85edc4c9e 100644 --- a/gcc/testsuite/g++.dg/cpp0x/duplicate1.C +++ b/gcc/testsuite/g++.dg/cpp0x/duplicate1.C @@ -6,7 +6,7 @@ struct A virtual void foo() const; }; -struct B final final : A /* { dg-error "duplicate virt-specifier" } +struct B final final : A /* { dg-error "duplicate 'final' specifier" } { dg-begin-multiline-output "" } struct B final final : A ^~~~~ diff --git a/gcc/testsuite/g++.dg/cpp0x/override2.C b/gcc/testsuite/g++.dg/cpp0x/override2.C index ab4dec486c6..d7f542e1d3f 100644 --- a/gcc/testsuite/g++.dg/cpp0x/override2.C +++ b/gcc/testsuite/g++.dg/cpp0x/override2.C @@ -23,9 +23,9 @@ struct D5 : B3 {}; struct D6 : B4 {}; // { dg-error "cannot derive from 'final' base" } -struct B6 final final {}; // { dg-error "duplicate virt-specifier" } +struct B6 final final {}; // { dg-error "duplicate 'final' specifier" } -struct B7 override {}; // { dg-error "cannot specify 'override' for a class" } +struct B7 override {}; // { dg-error "variable 'B7 override' has initializer but incomplete type" } namespace N { @@ -45,7 +45,7 @@ int main() struct B2 final; // { dg-error "redeclaration" } struct B2 override; // { dg-message "previously declared here" } struct B2 final {}; // { dg-error "redefinition" } - struct B2 override {}; // { dg-error "cannot specify 'override' for a class" } + struct B2 override {}; // { dg-error "redeclaration of 'main\\\(\\\)::B2 override'" } B2 override{}; // { dg-error "redeclaration" } struct foo final {}; // { dg-message "previous definition" } struct foo final {}; // { dg-error "redefinition" } diff --git a/gcc/testsuite/g++.dg/cpp0x/override5.C b/gcc/testsuite/g++.dg/cpp0x/override5.C new file mode 100644 index 00000000000..3382c142cf0 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/override5.C @@ -0,0 +1,26 @@ +// PR c++/120569 +// { dg-do compile } +// { dg-options "" } +// { dg-additional-options "-pedantic" { target c++14 } } + +namespace U { + struct A {}; + struct A override {}; // { dg-warning "extended initializer lists only available with" "" { target c++98_only } } +} +namespace V { + template + struct B {}; + template + struct B override {}; // { dg-warning "extended initializer lists only available with" "" { target c++98_only } } +} // { dg-warning "variable templates only available with" "" { target c++11_down } .-1 } +struct C { + struct D {}; + struct D override {}; // { dg-warning "extended initializer lists only available with" "" { target c++98_only } } +}; // { dg-warning "non-static data member initializers only available with" "" { target c++98_only } .-1 } +namespace W { + struct E { struct F {}; }; + struct E::F override {}; // { dg-warning "extended initializer lists only available with" "" { target c++98_only } } +} +template +struct V::B override {}; // { dg-warning "extended initializer lists only available with" "" { target c++98_only } } + // { dg-warning "variable templates only available with" "" { target c++11_down } .-1 }