diff options
-rw-r--r-- | gcc/c/c-decl.cc | 18 | ||||
-rw-r--r-- | gcc/c/c-errors.cc | 36 | ||||
-rw-r--r-- | gcc/c/c-parser.cc | 52 | ||||
-rw-r--r-- | gcc/c/c-tree.h | 2 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/auto-type-2.c | 3 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/c23-bool-errors-1.c | 14 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/c23-bool-errors-2.c | 9 | ||||
-rw-r--r-- | 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 44b3b5c..f60b2a5 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 4a26eec..4682dca 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 d289af6..f72b0f0 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 ee18e09..743ec5c 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 761671b..e059912 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 0000000..e3ea98d --- /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 0000000..1a66e3a --- /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 0000000..51ac5c9 --- /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 } */ |