On Mon, Nov 25, 2024 at 10:49:02PM -0500, David Malcolm wrote:
> 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 (based on
> a brief review of the Gentoo bug tracker links given at
> https://gcc.gnu.org/pipermail/gcc/2024-November/245185.html).
> 
> 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
> 
> 
> Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
> OK for trunk?

Thanks for the patch.

For:

  typedef int _Bool;

or

  int _Bool;

this patch says:

  note: '_Bool' is a keyword with '-std=c23' onwards

but I don't think that's true: _Bool was added in C99 and is obsolescent in
C23.

> 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 (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>
> ---
>  gcc/c/c-decl.cc                          | 18 +++++++-
>  gcc/c/c-errors.cc                        | 13 ++++++
>  gcc/c/c-parser.cc                        | 53 +++++++++++++++++++-----
>  gcc/c/c-tree.h                           |  3 ++
>  gcc/testsuite/gcc.dg/auto-type-2.c       |  2 +-
>  gcc/testsuite/gcc.dg/c23-bool-errors-1.c |  9 ++++
>  gcc/testsuite/gcc.dg/c23-bool-errors-2.c |  9 ++++
>  gcc/testsuite/gcc.dg/c23-bool-errors-3.c | 15 +++++++
>  8 files changed, 109 insertions(+), 13 deletions(-)
>  create mode 100644 gcc/testsuite/gcc.dg/c23-bool-errors-1.c
>  create mode 100644 gcc/testsuite/gcc.dg/c23-bool-errors-2.c
>  create mode 100644 gcc/testsuite/gcc.dg/c23-bool-errors-3.c
> 
> diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
> index c58ff4ab2488..70198dacf85d 100644
> --- a/gcc/c/c-decl.cc
> +++ b/gcc/c/c-decl.cc
> @@ -12485,8 +12485,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, "-std=c23");
> +             }
> +           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 653ad8c5235f..8f0330acd7c1 100644
> --- a/gcc/c/c-errors.cc
> +++ b/gcc/c/c-errors.cc
> @@ -216,3 +216,16 @@ out:
>    va_end (ap);
>    return warned;
>  }
> +
> +/* 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,
> +                         const char *std_option)
> +{
> +  gcc_assert (TREE_CODE (keyword_id) == IDENTIFIER_NODE);
> +  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 f716c567e819..6c6d3b3c26c5 100644
> --- a/gcc/c/c-parser.cc
> +++ b/gcc/c/c-parser.cc
> @@ -3682,6 +3682,48 @@ 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,
> +                                       "-std=c23");
> +           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:
> @@ -3849,16 +3891,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 d39bd238103e..e98bd4f2fd8d 100644
> --- a/gcc/c/c-tree.h
> +++ b/gcc/c/c-tree.h
> @@ -955,6 +955,9 @@ 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,
> +                                     const char *std_option);
>  
>  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..f484acb4c916 100644
> --- a/gcc/testsuite/gcc.dg/auto-type-2.c
> +++ b/gcc/testsuite/gcc.dg/auto-type-2.c
> @@ -20,4 +20,4 @@ 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" } */
> 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..8a33d36a8891
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/c23-bool-errors-1.c
> @@ -0,0 +1,9 @@
> +/* 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 } */
> 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..634c8ac45a52
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/c23-bool-errors-3.c
> @@ -0,0 +1,15 @@
> +/* 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 } */

Should it test 'bool' too?

Marek

Reply via email to