https://gcc.gnu.org/g:8f063b40e5b8f23cb89fee21afaa71deedbdf2aa

commit r16-2185-g8f063b40e5b8f23cb89fee21afaa71deedbdf2aa
Author: Jakub Jelinek <ja...@redhat.com>
Date:   Thu Jul 10 23:47:42 2025 +0200

    c++: Fix up final handling in C++98 [PR120628]
    
    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.
    
    2025-07-10  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.

Diff:
---
 gcc/cp/parser.cc                       | 21 ++++++++++++++-------
 gcc/testsuite/g++.dg/cpp0x/final1.C    | 11 +++++++++++
 gcc/testsuite/g++.dg/cpp0x/final2.C    | 26 ++++++++++++++++++++++++++
 gcc/testsuite/g++.dg/cpp0x/override6.C | 26 ++++++++++++++++++++++++++
 4 files changed, 77 insertions(+), 7 deletions(-)

diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 1f58425a70b6..21bec72c7961 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -3091,8 +3091,8 @@ static cp_token *cp_parser_require_keyword
   (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
@@ -22031,7 +22031,7 @@ cp_parser_elaborated_type_specifier (cp_parser* parser,
 
          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.  */
@@ -28095,6 +28095,13 @@ cp_parser_class_property_specifier_seq_opt (cp_parser 
*parser)
        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;
        }
@@ -28318,7 +28325,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;
@@ -35696,15 +35703,15 @@ cp_parser_token_starts_function_definition_p 
(cp_token* token)
          || 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));
diff --git a/gcc/testsuite/g++.dg/cpp0x/final1.C 
b/gcc/testsuite/g++.dg/cpp0x/final1.C
new file mode 100644
index 000000000000..1d6409542bf4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/final1.C
@@ -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);
+}
diff --git a/gcc/testsuite/g++.dg/cpp0x/final2.C 
b/gcc/testsuite/g++.dg/cpp0x/final2.C
new file mode 100644
index 000000000000..d8d5866eb950
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/final2.C
@@ -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 } }
diff --git a/gcc/testsuite/g++.dg/cpp0x/override6.C 
b/gcc/testsuite/g++.dg/cpp0x/override6.C
new file mode 100644
index 000000000000..601e91dd5099
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/override6.C
@@ -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 } }

Reply via email to