https://gcc.gnu.org/g:321983033d621e3f75e11d380c4463956a3f6e1e

commit r15-6507-g321983033d621e3f75e11d380c4463956a3f6e1e
Author: David Malcolm <dmalc...@redhat.com>
Date:   Thu Jan 2 15:10:15 2025 -0500

    c: special-case some "bool" errors with C23 (v2) [PR117629]
    
    Changed in v2:
    - distinguish between "bool" and "_Bool" when determining
      standard version
    
    This patch attempts to provide better error messages for
    code compiled with C23 that hasn't been updated for
    "bool", "true", and "false" becoming keywords.
    
    Specifically:
    
    (1) with "typedef int bool;" previously we emitted:
    
    t1.c:7:13: error: two or more data types in declaration specifiers
        7 | typedef int bool;
          |             ^~~~
    t1.c:7:1: warning: useless type name in empty declaration
        7 | typedef int bool;
          | ^~~~~~~
    
    whereas with this patch we emit:
    
    t1.c:7:13: error: 'bool' cannot be defined via 'typedef'
        7 | typedef int bool;
          |             ^~~~
    t1.c:7:13: note: 'bool' is a keyword with '-std=c23' onwards
    t1.c:7:1: warning: useless type name in empty declaration
        7 | typedef int bool;
          | ^~~~~~~
    
    (2) with "int bool;" previously we emitted:
    
    t2.c:7:5: error: two or more data types in declaration specifiers
        7 | int bool;
          |     ^~~~
    t2.c:7:1: warning: useless type name in empty declaration
        7 | int bool;
          | ^~~
    
    whereas with this patch we emit:
    
    t2.c:7:5: error: 'bool' cannot be used here
        7 | int bool;
          |     ^~~~
    t2.c:7:5: note: 'bool' is a keyword with '-std=c23' onwards
    t2.c:7:1: warning: useless type name in empty declaration
        7 | int bool;
          | ^~~
    
    (3) with "typedef enum { false = 0, true = 1 } _Bool;" previously we
    emitted:
    
    t3.c:7:16: error: expected identifier before 'false'
        7 | typedef enum { false = 0, true = 1 } _Bool;
          |                ^~~~~
    t3.c:7:38: error: expected ';', identifier or '(' before '_Bool'
        7 | typedef enum { false = 0, true = 1 } _Bool;
          |                                      ^~~~~
    t3.c:7:38: warning: useless type name in empty declaration
    
    whereas with this patch we emit:
    
    t3.c:7:16: error: cannot use keyword 'false' as enumeration constant
        7 | typedef enum { false = 0, true = 1 } _Bool;
          |                ^~~~~
    t3.c:7:16: note: 'false' is a keyword with '-std=c23' onwards
    t3.c:7:38: error: expected ';', identifier or '(' before '_Bool'
        7 | typedef enum { false = 0, true = 1 } _Bool;
          |                                      ^~~~~
    t3.c:7:38: warning: useless type name in empty declaration
    
    gcc/c/ChangeLog:
            PR c/117629
            * c-decl.cc (declspecs_add_type): Special-case attempts to use
            bool as a typedef name or declaration name.
            * c-errors.cc (get_std_for_keyword): New.
            (add_note_about_new_keyword): New.
            * c-parser.cc (report_bad_enum_name): New, split out from...
            (c_parser_enum_specifier): ...here, adding handling for RID_FALSE
            and RID_TRUE.
            * c-tree.h (add_note_about_new_keyword): New decl.
    
    gcc/testsuite/ChangeLog:
            PR c/117629
            * gcc.dg/auto-type-2.c: Update expected output with _Bool.
            * gcc.dg/c23-bool-errors-1.c: New test.
            * gcc.dg/c23-bool-errors-2.c: New test.
            * gcc.dg/c23-bool-errors-3.c: New test.
    
    Signed-off-by: David Malcolm <dmalc...@redhat.com>

Diff:
---
 gcc/c/c-decl.cc                          | 18 +++++++++--
 gcc/c/c-errors.cc                        | 36 ++++++++++++++++++++++
 gcc/c/c-parser.cc                        | 52 ++++++++++++++++++++++++++------
 gcc/c/c-tree.h                           |  2 ++
 gcc/testsuite/gcc.dg/auto-type-2.c       |  3 +-
 gcc/testsuite/gcc.dg/c23-bool-errors-1.c | 14 +++++++++
 gcc/testsuite/gcc.dg/c23-bool-errors-2.c |  9 ++++++
 gcc/testsuite/gcc.dg/c23-bool-errors-3.c | 18 +++++++++++
 8 files changed, 139 insertions(+), 13 deletions(-)

diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index 44b3b5c3b6ce..f60b2a54a17a 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -12493,8 +12493,22 @@ declspecs_add_type (location_t loc, struct c_declspecs 
*specs,
             "__auto_type".  */
          if (specs->typespec_word != cts_none)
            {
-             error_at (loc,
-                       "two or more data types in declaration specifiers");
+             if (i == RID_BOOL)
+               {
+                 auto_diagnostic_group d;
+                 if (specs->storage_class == csc_typedef)
+                   error_at (loc,
+                             "%qs cannot be defined via %<typedef%>",
+                             IDENTIFIER_POINTER (type));
+                 else
+                   error_at (loc,
+                             "%qs cannot be used here",
+                             IDENTIFIER_POINTER (type));
+                 add_note_about_new_keyword (loc, type);
+               }
+             else
+               error_at (loc,
+                         "two or more data types in declaration specifiers");
              return specs;
            }
          switch (i)
diff --git a/gcc/c/c-errors.cc b/gcc/c/c-errors.cc
index 4a26eec16c5e..4682dca56c2c 100644
--- a/gcc/c/c-errors.cc
+++ b/gcc/c/c-errors.cc
@@ -215,3 +215,39 @@ out:
   va_end (ap);
   return warned;
 }
+
+/* Determine in which version of the standard IDENTIFIER
+   became a keyword.  */
+
+static const char *
+get_std_for_keyword (tree identifier)
+{
+  switch (C_RID_CODE (identifier))
+    {
+    default:
+      gcc_unreachable ();
+    case RID_FALSE:
+    case RID_TRUE:
+      return "-std=c23";
+    case RID_BOOL:
+      if (IDENTIFIER_POINTER (identifier)[0] == 'b')
+       /* "bool".  */
+       return "-std=c23";
+      else
+       /* "_Bool".  */
+       return "-std=c99";
+    }
+}
+
+/* Issue a note to the user at LOC that KEYWORD_ID is a keyword
+   in STD_OPTION version of the standard onwards.  */
+
+void
+add_note_about_new_keyword (location_t loc,
+                           tree keyword_id)
+{
+  gcc_assert (TREE_CODE (keyword_id) == IDENTIFIER_NODE);
+  const char *std_option = get_std_for_keyword (keyword_id);
+  inform (loc, "%qs is a keyword with %qs onwards",
+         IDENTIFIER_POINTER (keyword_id), std_option);
+}
diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc
index d289af67937a..f72b0f0e39fd 100644
--- a/gcc/c/c-parser.cc
+++ b/gcc/c/c-parser.cc
@@ -3785,6 +3785,47 @@ c_parser_declspecs (c_parser *parser, struct c_declspecs 
*specs,
     specs->postfix_attrs = c_parser_std_attribute_specifier_sequence (parser);
 }
 
+/* Complain about a non-CPP_NAME within an enumerator list.  */
+
+static void
+report_bad_enum_name (c_parser *parser)
+{
+  if (!parser->error)
+    {
+      c_token *token = c_parser_peek_token (parser);
+      switch (token->type)
+       {
+       default:
+         break;
+       case CPP_CLOSE_BRACE:
+         /* Give a nicer error for "enum {}".  */
+         error_at (token->location,
+                   "empty enum is invalid");
+         parser->error = true;
+         return;
+       case CPP_KEYWORD:
+         /* Give a nicer error for attempts to use "true" and "false"
+            in enums with C23 onwards.  */
+         if (token->keyword == RID_FALSE
+             || token->keyword == RID_TRUE)
+           {
+             auto_diagnostic_group d;
+             error_at (token->location,
+                       "cannot use keyword %qs as enumeration constant",
+                       IDENTIFIER_POINTER (token->value));
+             add_note_about_new_keyword (token->location,
+                                         token->value);
+             parser->error = true;
+             return;
+           }
+         break;
+       }
+    }
+
+  /* Otherwise, a more generic error message.  */
+  c_parser_error (parser, "expected identifier");
+}
+
 /* Parse an enum specifier (C90 6.5.2.2, C99 6.7.2.2, C11 6.7.2.2).
 
    enum-specifier:
@@ -3952,16 +3993,7 @@ c_parser_enum_specifier (c_parser *parser)
          location_t decl_loc, value_loc;
          if (c_parser_next_token_is_not (parser, CPP_NAME))
            {
-             /* Give a nicer error for "enum {}".  */
-             if (c_parser_next_token_is (parser, CPP_CLOSE_BRACE)
-                 && !parser->error)
-               {
-                 error_at (c_parser_peek_token (parser)->location,
-                           "empty enum is invalid");
-                 parser->error = true;
-               }
-             else
-               c_parser_error (parser, "expected identifier");
+             report_bad_enum_name (parser);
              c_parser_skip_until_found (parser, CPP_CLOSE_BRACE, NULL);
              values = error_mark_node;
              break;
diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h
index ee18e093f996..743ec5cbae61 100644
--- a/gcc/c/c-tree.h
+++ b/gcc/c/c-tree.h
@@ -955,6 +955,8 @@ extern bool pedwarn_c11 (location_t, diagnostic_option_id, 
const char *, ...)
     ATTRIBUTE_GCC_DIAG(3,4);
 extern bool pedwarn_c23 (location_t, diagnostic_option_id, const char *, ...)
     ATTRIBUTE_GCC_DIAG(3,4);
+extern void add_note_about_new_keyword (location_t loc,
+                                       tree keyword_id);
 
 extern void
 set_c_expr_source_range (c_expr *expr,
diff --git a/gcc/testsuite/gcc.dg/auto-type-2.c 
b/gcc/testsuite/gcc.dg/auto-type-2.c
index 761671b3c5a4..e059912b8cc5 100644
--- a/gcc/testsuite/gcc.dg/auto-type-2.c
+++ b/gcc/testsuite/gcc.dg/auto-type-2.c
@@ -20,4 +20,5 @@ signed __auto_type e7 = 0; /* { dg-error "__auto_type" } */
 unsigned __auto_type e8 = 0; /* { dg-error "__auto_type" } */
 _Complex __auto_type e9 = 0; /* { dg-error "__auto_type" } */
 int __auto_type e10 = 0; /* { dg-error "two or more data types" } */
-__auto_type _Bool e11 = 0; /* { dg-error "two or more data types" } */
+__auto_type _Bool e11 = 0; /* { dg-error "'_Bool' cannot be used here" } */
+/* { dg-message "'_Bool' is a keyword with '-std=c99' onwards" "" { target 
*-*-* } .-1 } */
diff --git a/gcc/testsuite/gcc.dg/c23-bool-errors-1.c 
b/gcc/testsuite/gcc.dg/c23-bool-errors-1.c
new file mode 100644
index 000000000000..e3ea98d4a422
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-bool-errors-1.c
@@ -0,0 +1,14 @@
+/* Test error-handling for legacy code that tries to
+   define "bool" with C23.  */
+
+/* { dg-do compile } */
+/* { dg-options "-std=c23" } */
+
+typedef int bool; /* { dg-error "'bool' cannot be defined via 'typedef'" } */
+/* { dg-message "'bool' is a keyword with '-std=c23' onwards" "" { target 
*-*-* } .-1 } */
+/* { dg-warning "useless type name in empty declaration"  "" { target *-*-* } 
.-2 } */
+
+/* Also check that we report the correct standard version for "_Bool".  */
+typedef int _Bool; /* { dg-error "'_Bool' cannot be defined via 'typedef'" } */
+/* { dg-message "'_Bool' is a keyword with '-std=c99' onwards" "" { target 
*-*-* } .-1 } */
+/* { dg-warning "useless type name in empty declaration"  "" { target *-*-* } 
.-2 } */
diff --git a/gcc/testsuite/gcc.dg/c23-bool-errors-2.c 
b/gcc/testsuite/gcc.dg/c23-bool-errors-2.c
new file mode 100644
index 000000000000..1a66e3aea67e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-bool-errors-2.c
@@ -0,0 +1,9 @@
+/* Test error-handling for legacy code that tries to
+   use a variable named "bool" with C23.  */
+
+/* { dg-do compile } */
+/* { dg-options "-std=c23" } */
+
+int bool; /* { dg-error "'bool' cannot be used here" } */
+/* { dg-message "'bool' is a keyword with '-std=c23' onwards" "" { target 
*-*-* } .-1 } */
+/* { dg-warning "useless type name in empty declaration" "" { target *-*-* } 
.-2 } */
diff --git a/gcc/testsuite/gcc.dg/c23-bool-errors-3.c 
b/gcc/testsuite/gcc.dg/c23-bool-errors-3.c
new file mode 100644
index 000000000000..51ac5c9dc569
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-bool-errors-3.c
@@ -0,0 +1,18 @@
+/* Test error-handling for legacy code that tries to
+   define "false" or "true" within enums with C23.  */
+
+/* { dg-do compile } */
+/* { dg-options "-std=c23" } */
+
+typedef enum { false = 0, true = 1 } _Bool; /* { dg-error "cannot use keyword 
'false' as enumeration constant" }
+/* { dg-message "'false' is a keyword with '-std=c23' onwards" "" { target 
*-*-* } .-1 } */
+/* { dg-error "38: expected ';', identifier or '\\\(' before '_Bool'" "" { 
target *-*-* } .-2 } */
+/* { dg-warning "38: useless type name in empty declaration" "" { target *-*-* 
} .-3 } */
+
+typedef enum { true = 1, false = 0 } _Bool; /* { dg-error "cannot use keyword 
'true' as enumeration constant" }
+/* { dg-message "'true' is a keyword with '-std=c23' onwards" "" { target 
*-*-* } .-1 } */
+/* { dg-error "38: expected ';', identifier or '\\\(' before '_Bool'" "" { 
target *-*-* } .-2 } */
+/* { dg-warning "38: useless type name in empty declaration" "" { target *-*-* 
} .-3 } */
+
+typedef enum { False = 0, True = 1 } bool; /* { dg-error "expected ';', 
identifier or '\\(' before 'bool'" }
+/* { dg-warning "38: useless type name in empty declaration" "" { target *-*-* 
} .-1 } */

Reply via email to