On 6/12/25 3:11 AM, Jakub Jelinek wrote:
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.
OK.
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