]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++: Don't incorrectly reject override after class head name [PR120569]
authorJakub Jelinek <jakub@redhat.com>
Thu, 10 Jul 2025 21:41:56 +0000 (23:41 +0200)
committerJakub Jelinek <jakub@gcc.gnu.org>
Thu, 10 Jul 2025 21:41:56 +0000 (23:41 +0200)
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  <jakub@redhat.com>

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.

gcc/cp/parser.cc
gcc/testsuite/g++.dg/cpp0x/duplicate1.C
gcc/testsuite/g++.dg/cpp0x/override2.C
gcc/testsuite/g++.dg/cpp0x/override5.C [new file with mode: 0644]

index d96fdf8f9271b1fc3597e89aa3f069b203e43b85..1f58425a70b69649d4edf594894a987e64ae5116 100644 (file)
@@ -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 %<override%> for a class");
-      type = error_mark_node;
-      goto out;
-    }
   /* Issue the error about the overly-qualified name now.  */
   if (qualified_p)
     {
index 1545e1cd4b5cb16701f8637b1268e175f461176e..4e85edc4c9e9e358307658c79c934ac3a521d7fd 100644 (file)
@@ -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
                 ^~~~~
index ab4dec486c666a4d1ed04a6c110167bd556d9a8f..d7f542e1d3ff3d7669d682bda10cc12304a3190a 100644 (file)
@@ -23,9 +23,9 @@ struct D5 : B3<D5> {};
 
 struct D6 : B4<D6> {}; // { 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 (file)
index 0000000..3382c14
--- /dev/null
@@ -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 <int N>
+  struct B {};
+  template <int N>
+  struct B<N> 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 <int N>
+struct V::B<N> 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 }