"__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)
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);
+}
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:
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;
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,
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 } */
--- /dev/null
+/* 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 } */
--- /dev/null
+/* 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 } */
--- /dev/null
+/* 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 } */