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

Reply via email to