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?
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 <[email protected]>
---
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 } */
--
2.26.3