Am Montag, den 14.09.2020, 20:30 +0000 schrieb Joseph Myers: > On Sun, 13 Sep 2020, Uecker, Martin wrote: > > > Hi Joseph, > > > > here is the (unfinished) patch to support > > mixing of labels in C2X. > > I think there should be explicit tests for old standard versions > (c11-labels-1.c etc.) that these usages are errors with -pedantic-errors > with the old -std option, are warnings with -pedantic, and are quietly > accepted with neither. In addition to using -pedantic-errors with the new > standard version test to confirm it's not diagnosed, and a test with the > new version and -Wc11-c2x-compat. > > By way of example of further places that I think need changing in the > patch: at present, c_parser_label gives an error (that you correctly > change to a pedwarn_c11) if the label is followed by a declaration - and > then parses the declaration itself rather than leaving it to be parsed in > the caller. So c_parser_compound_statement_nostart would parse a label > followed by a declaration, and at that point last_label would be set to > true, meaning that a second declaration would be rejected, when in C2x it > should be accepted. You can see this even without the patch with a test > such as: > > void > f (void) > { > label : int a; int b; > } > > I think that instead c_parser_label should never try to parse a > declaration following the label; that should be left for the caller to > handle. To avoid c_parser_label needing to return information about > standard attributes on a following declaration, maybe it shouldn't parse > standard attributes either (note that means that c_parser_all_labels would > need to handle parsing and warning about and discarding standard > attributes after each label instead - such attributes might be ones on a > statement, or ones on the next label in a sequence of labels). > > Then of course the checks of last_label in > c_parser_compound_statement_nostart would need to become checks for > whether to pedwarn_c11 about the use of a label in a given context, once > the code knows whether there is a declaration, rather than preventing > parsing a declaration there at all. So probably c_parser_label would no > longer have the pedwarn_c11 either; that would all be left to its callers.
ok, here is a new revision of the patch. Still needs some polish... I also made the attributes not be ignored for labels. Or should I separate this out? There is a new warning for the case of GNU-style attributes between a label and a declaration. It now applies to the label. Best, Martin diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c index b921c4e3852..aa039db087b 100644 --- a/gcc/c/c-parser.c +++ b/gcc/c/c-parser.c @@ -1521,7 +1521,7 @@ static void c_parser_initval (c_parser *, struct c_expr *, struct obstack *); static tree c_parser_compound_statement (c_parser *, location_t * = NULL); static location_t c_parser_compound_statement_nostart (c_parser *); -static void c_parser_label (c_parser *); +static void c_parser_label (c_parser *, tree); static void c_parser_statement (c_parser *, bool *, location_t * = NULL); static void c_parser_statement_after_labels (c_parser *, bool *, vec<tree> * = NULL); @@ -5523,7 +5523,7 @@ c_parser_initval (c_parser *parser, struct c_expr *after, } /* Parse a compound statement (possibly a function body) (C90 6.6.2, - C99 6.8.2, C11 6.8.2). + C99 6.8.2, C11 6.8.2, C2X 6.X.Y). compound-statement: { block-item-list[opt] } @@ -5674,7 +5674,7 @@ c_parser_compound_statement_nostart (c_parser *parser) { location_t loc = c_parser_peek_token (parser)->location; loc = expansion_point_location_if_in_system_header (loc); - /* Standard attributes may start a statement or a declaration. */ + /* Standard attributes may start a label, statement or declaration. */ bool have_std_attrs = c_parser_nth_token_starts_std_attributes (parser, 1); tree std_attrs = NULL_TREE; @@ -5685,7 +5685,6 @@ c_parser_compound_statement_nostart (c_parser *parser) || (c_parser_next_token_is (parser, CPP_NAME) && c_parser_peek_2nd_token (parser)->type == CPP_COLON)) { - c_warn_unused_attributes (std_attrs); if (c_parser_next_token_is_keyword (parser, RID_CASE)) label_loc = c_parser_peek_2nd_token (parser)->location; else @@ -5693,27 +5692,54 @@ c_parser_compound_statement_nostart (c_parser *parser) last_label = true; last_stmt = false; mark_valid_location_for_stdc_pragma (false); - c_parser_label (parser); + c_parser_label (parser, std_attrs); + + /* Allow '__attribute__((fallthrough));'. */ + if (c_parser_next_token_is_keyword (parser, RID_ATTRIBUTE)) + { + location_t loc = c_parser_peek_token (parser)->location; + tree attrs = c_parser_gnu_attributes (parser); + if (attribute_fallthrough_p (attrs)) + { + if (c_parser_next_token_is (parser, CPP_SEMICOLON)) + { + tree fn = build_call_expr_internal_loc (loc, + IFN_FALLTHROUGH, + void_type_node, 0); + add_stmt (fn); + } + else + warning_at (loc, OPT_Wattributes, "%<fallthrough%> attribute " + "not followed by %<;%>"); + } + else if (attrs != NULL_TREE) + warning_at (loc, OPT_Wattributes, "only attribute %<fallthrough%>" + " can be applied to a null statement"); + } } - else if (!last_label - && (c_parser_next_tokens_start_declaration (parser) - || (have_std_attrs - && c_parser_next_token_is (parser, CPP_SEMICOLON)))) + else if (c_parser_next_tokens_start_declaration (parser) + || (have_std_attrs + && c_parser_next_token_is (parser, CPP_SEMICOLON))) { - last_label = false; + if (last_label) + pedwarn_c11 (c_parser_peek_token (parser)->location, OPT_Wpedantic, + "a label can only be part of a statement and " + "a declaration is not a statement"); + mark_valid_location_for_stdc_pragma (false); bool fallthru_attr_p = false; c_parser_declaration_or_fndef (parser, true, !have_std_attrs, true, true, true, NULL, vNULL, have_std_attrs, std_attrs, NULL, &fallthru_attr_p); + if (last_stmt && !fallthru_attr_p) pedwarn_c90 (loc, OPT_Wdeclaration_after_statement, "ISO C90 forbids mixed declarations and code"); last_stmt = fallthru_attr_p; + last_label = false; } - else if (!last_label - && c_parser_next_token_is_keyword (parser, RID_EXTENSION)) + else if (c_parser_next_token_is_keyword (parser, RID_EXTENSION)) { /* __extension__ can start a declaration, but is also an unary operator that can start an expression. Consume all @@ -5796,7 +5822,7 @@ c_parser_compound_statement_nostart (c_parser *parser) parser->error = false; } if (last_label) - error_at (label_loc, "label at end of compound statement"); + pedwarn_c11 (label_loc, OPT_Wpedantic, "label at end of compound statement"); location_t endloc = c_parser_peek_token (parser)->location; c_parser_consume_token (parser); /* Restore the value we started with. */ @@ -5812,19 +5838,29 @@ c_parser_compound_statement_nostart (c_parser *parser) static void c_parser_all_labels (c_parser *parser) { + tree std_attrs = NULL; if (c_parser_nth_token_starts_std_attributes (parser, 1)) { - tree std_attrs = c_parser_std_attribute_specifier_sequence (parser); + std_attrs = c_parser_std_attribute_specifier_sequence (parser); if (c_parser_next_token_is (parser, CPP_SEMICOLON)) c_parser_error (parser, "expected statement"); - else - c_warn_unused_attributes (std_attrs); } while (c_parser_next_token_is_keyword (parser, RID_CASE) || c_parser_next_token_is_keyword (parser, RID_DEFAULT) || (c_parser_next_token_is (parser, CPP_NAME) && c_parser_peek_2nd_token (parser)->type == CPP_COLON)) - c_parser_label (parser); + { + c_parser_label (parser, std_attrs); + std_attrs = NULL; + if (c_parser_nth_token_starts_std_attributes (parser, 1)) + { + std_attrs = c_parser_std_attribute_specifier_sequence (parser); + if (c_parser_next_token_is (parser, CPP_SEMICOLON)) + c_parser_error (parser, "expected statement"); + } + } + if (std_attrs) + c_warn_unused_attributes (std_attrs); } /* Parse a label (C90 6.6.1, C99 6.8.1, C11 6.8.1). @@ -5843,12 +5879,10 @@ c_parser_all_labels (c_parser *parser) GNU C accepts any expressions without commas, non-constant expressions being rejected later. Any standard attribute-specifier-sequence before the first label has been parsed - in the caller, to distinguish statements from declarations. Any - attribute-specifier-sequence after the label is parsed in this - function. */ + in the caller, to distinguish statements from declarations. */ static void -c_parser_label (c_parser *parser) +c_parser_label (c_parser *parser, tree std_attrs) { location_t loc1 = c_parser_peek_token (parser)->location; tree label = NULL_TREE; @@ -5898,8 +5932,13 @@ c_parser_label (c_parser *parser) if (tlab) { decl_attributes (&tlab, attrs, 0); + decl_attributes (&tlab, std_attrs, 0); label = add_stmt (build_stmt (loc1, LABEL_EXPR, tlab)); } + if (attrs + && c_parser_next_tokens_start_declaration (parser)) + warning_at (loc2, OPT_Wattributes, "GNU-style attribute between" + " label and declaration appertains to the label."); } if (label) { @@ -5907,55 +5946,6 @@ c_parser_label (c_parser *parser) FALLTHROUGH_LABEL_P (LABEL_EXPR_LABEL (label)) = fallthrough_p; else FALLTHROUGH_LABEL_P (CASE_LABEL (label)) = fallthrough_p; - - /* Standard attributes are only allowed here if they start a - statement, not a declaration (including the case of an - attribute-declaration with only attributes). */ - bool have_std_attrs - = c_parser_nth_token_starts_std_attributes (parser, 1); - tree std_attrs = NULL_TREE; - if (have_std_attrs) - std_attrs = c_parser_std_attribute_specifier_sequence (parser); - - /* Allow '__attribute__((fallthrough));'. */ - if (!have_std_attrs - && c_parser_next_token_is_keyword (parser, RID_ATTRIBUTE)) - { - location_t loc = c_parser_peek_token (parser)->location; - tree attrs = c_parser_gnu_attributes (parser); - if (attribute_fallthrough_p (attrs)) - { - if (c_parser_next_token_is (parser, CPP_SEMICOLON)) - { - tree fn = build_call_expr_internal_loc (loc, - IFN_FALLTHROUGH, - void_type_node, 0); - add_stmt (fn); - } - else - warning_at (loc, OPT_Wattributes, "%<fallthrough%> attribute " - "not followed by %<;%>"); - } - else if (attrs != NULL_TREE) - warning_at (loc, OPT_Wattributes, "only attribute %<fallthrough%>" - " can be applied to a null statement"); - } - if (c_parser_next_tokens_start_declaration (parser) - || (have_std_attrs - && c_parser_next_token_is (parser, CPP_SEMICOLON))) - { - error_at (c_parser_peek_token (parser)->location, - "a label can only be part of a statement and " - "a declaration is not a statement"); - c_parser_declaration_or_fndef (parser, /*fndef_ok*/ false, - /*static_assert_ok*/ true, - /*empty_ok*/ true, /*nested*/ true, - /*start_attr_ok*/ true, NULL, - vNULL, have_std_attrs, std_attrs); - } - else if (std_attrs) - /* Nonempty attributes on the following statement are ignored. */ - c_warn_unused_attributes (std_attrs); } } diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index f14772f7d38..d0e134b859c 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -55,7 +55,7 @@ extensions, accepted by GCC in C90 mode and in C++. * Designated Inits:: Labeling elements of initializers. * Case Ranges:: `case 1 ... 9' and such. * Cast to Union:: Casting to union type from any member of the union. -* Mixed Declarations:: Mixing declarations and code. +* Mixed Labels and Declarations:: Mixing declarations, labels and code. * Function Attributes:: Declaring that functions have no side effects, or that they can never return. * Variable Attributes:: Specifying attributes of variables. @@ -2353,15 +2353,17 @@ void hack (union foo); hack ((union foo) x); @end smallexample -@node Mixed Declarations -@section Mixed Declarations and Code +@node Mixed Labels and Declarations +@section Mixed Declarations, Labels and Code @cindex mixed declarations and code @cindex declarations, mixed with code @cindex code, mixed with declarations ISO C99 and ISO C++ allow declarations and code to be freely mixed -within compound statements. As an extension, GNU C also allows this in -C90 mode. For example, you could do: +within compound statements. ISO C2X allows labels to be +placed before declarations and at the end of a compound statement. +As an extension, GNU C also allows all this in C90 mode. For example, +you could do: @smallexample int i; diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 492b7dcdf10..13b79da2c46 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -7592,7 +7592,7 @@ except when the same as the default promotion. @opindex Wno-declaration-after-statement Warn when a declaration is found after a statement in a block. This construct, known from C++, was introduced with ISO C99 and is by default -allowed in GCC@. It is not supported by ISO C90. @xref{Mixed Declarations}. +allowed in GCC@. It is not supported by ISO C90. @xref{Mixed Labels and Declarations}. @item -Wshadow @opindex Wshadow diff --git a/gcc/testsuite/gcc.dg/20031223-1.c b/gcc/testsuite/gcc.dg/20031223-1.c index 68aa74ffe50..c529739e7ec 100644 --- a/gcc/testsuite/gcc.dg/20031223-1.c +++ b/gcc/testsuite/gcc.dg/20031223-1.c @@ -3,11 +3,10 @@ because GCC was trying to expand the trees to rtl. */ /* { dg-do compile } */ -/* { dg-options "" } */ +/* { dg-options "-std=c17 -pedantic-errors" } */ void f () { l: int; /* { dg-error "a label can only be part of a statement and a declaration is not a statement" "not stmt" } */ - /* { dg-warning "useless type name in empty declaration" "type name" { target *-*-* } .-1 } */ - /* { dg-error "label at end of compound statement" "label" { target *-*-* } .-2 } */ + /* { dg-error "useless type name in empty declaration" "type name" { target *-*-* } .-1 } */ } diff --git a/gcc/testsuite/gcc.dg/c11-labels-1.c b/gcc/testsuite/gcc.dg/c11-labels-1.c new file mode 100644 index 00000000000..6350403bf38 --- /dev/null +++ b/gcc/testsuite/gcc.dg/c11-labels-1.c @@ -0,0 +1,15 @@ +/* Tests for labels before declarations and at ends of compound statements. */ +/* { dg-do compile } */ +/* { dg-options "-std=c11" } */ + +int f(int x) +{ + goto b; + a: int i = 2 * x; + goto c; + b: goto a; + { i *= 3; c: } + return i; + d: +} + diff --git a/gcc/testsuite/gcc.dg/c11-labels-2.c b/gcc/testsuite/gcc.dg/c11-labels-2.c new file mode 100644 index 00000000000..e9b492446b5 --- /dev/null +++ b/gcc/testsuite/gcc.dg/c11-labels-2.c @@ -0,0 +1,15 @@ +/* Tests for labels before declarations and at ends of compound statements. */ +/* { dg-do compile } */ +/* { dg-options "-std=c11 -pedantic" } */ + +int f(int x) +{ + goto b; + a: int i = 2 * x; /* { dg-warning "a label can only be part of a statement and a declaration is not a statement" } */ + goto c; + b: goto a; + { i *= 3; c: } /* { dg-warning "label at end of compound statement" } */ + return i; + d: /* { dg-warning "label at end of compound statement" } */ +} + diff --git a/gcc/testsuite/gcc.dg/c11-labels-3.c b/gcc/testsuite/gcc.dg/c11-labels-3.c new file mode 100644 index 00000000000..1e4be63af69 --- /dev/null +++ b/gcc/testsuite/gcc.dg/c11-labels-3.c @@ -0,0 +1,15 @@ +/* Tests for labels before declarations and at ends of compound statements. */ +/* { dg-do compile } */ +/* { dg-options "-std=c11 -pedantic-errors" } */ + +int f(int x) +{ + goto b; + a: int i = 2 * x; /* { dg-error "a label can only be part of a statement and a declaration is not a statement" } */ + goto c; + b: goto a; + { i *= 3; c: } /* { dg-error "label at end of compound statement" } */ + return i; + d: /* { dg-error "label at end of compound statement" } */ +} + diff --git a/gcc/testsuite/gcc.dg/c2x-attr-syntax-3.c b/gcc/testsuite/gcc.dg/c2x-attr-syntax-3.c index 1f883d825e0..2f0d9f60cc0 100644 --- a/gcc/testsuite/gcc.dg/c2x-attr-syntax-3.c +++ b/gcc/testsuite/gcc.dg/c2x-attr-syntax-3.c @@ -25,13 +25,14 @@ f2 (void) } /* Declarations, including attribute declarations, cannot appear after - labels. */ + labels when a statement is expected. */ void f3 (void) { - x: [[]];; /* { dg-error "can only be part of a statement" } */ -} + if (1) + x: [[]]; /* { dg-error "expected" } */ +} /* Prefix attributes cannot appear on type names. */ diff --git a/gcc/testsuite/gcc.dg/c2x-labels-1.c b/gcc/testsuite/gcc.dg/c2x-labels-1.c new file mode 100644 index 00000000000..439cf7834ee --- /dev/null +++ b/gcc/testsuite/gcc.dg/c2x-labels-1.c @@ -0,0 +1,23 @@ +/* Tests for labels before declarations and at ends of compound statements. */ +/* { dg-do run } */ +/* { dg-options "-std=c2x -pedantic-errors" } */ + +int f(int x) +{ + goto b; + a: int i = 2 * x; + aa: int u = 0; int v = 0; + goto c; + b: goto a; + { i *= 3; c: } + return i + u + v; + d: +} + +int main(void) +{ + if (2 != f(1)) + __builtin_abort(); + + return 0; +} diff --git a/gcc/testsuite/gcc.dg/c2x-labels-2.c b/gcc/testsuite/gcc.dg/c2x-labels-2.c new file mode 100644 index 00000000000..bd010e9ecfb --- /dev/null +++ b/gcc/testsuite/gcc.dg/c2x-labels-2.c @@ -0,0 +1,15 @@ +/* Tests for labels before declarations and at ends of compound statements. */ +/* { dg-do compile } */ +/* { dg-options "-std=c2x -Wc11-c2x-compat" } */ + +int f(int x) +{ + goto b; + a: int i = 2 * x; /* { dg-warning "a label can only be part of a statement and a declaration is not a statement" } */ + goto c; + b: goto a; + { i *= 3; c: } /* { dg-warning "label at end of compound statement" } */ + return i; + d: /* { dg-warning "label at end of compound statement" } */ +} + diff --git a/gcc/testsuite/gcc.dg/c2x-labels-3.c b/gcc/testsuite/gcc.dg/c2x-labels-3.c new file mode 100644 index 00000000000..159116db186 --- /dev/null +++ b/gcc/testsuite/gcc.dg/c2x-labels-3.c @@ -0,0 +1,38 @@ +/* Tests for labels before declarations and at ends of compound statements + * in combination with attributes. */ +/* { dg-do compile } */ +/* { dg-options "-std=c2x -Wall" } */ + +int f(void) +{ + goto b; + a: int i = 0; + aa: __attribute__((unused)) int u = 0; int v = 0; /* { dg-warning "GNU-style attribute between label and declaration appertains to the label" } */ + goto c; + { c: } + b: goto a; + return i + u + v; + d: __attribute__((unused)) (void)0; + e: __attribute__((unused)) +} + +int g(void) +{ + goto b; + a: int i = 0; + [[maybe_unused]] aa: int u = 0; int v = 0; + goto c; + { c: } + b: goto a; + return i + u + v; + [[maybe_unused]] d: (void)0; + [[maybe_unused]] e: +} + +void h(void) +{ + [[maybe_unused]] a: [[maybe_unused]] b: [[maybe_unused]] int x; + + if (1) + [[maybe_unused]] c: [[maybe_unused]] d: (void)0; +} diff --git a/gcc/testsuite/gcc.dg/decl-9.c b/gcc/testsuite/gcc.dg/decl-9.c index eeca8e05e14..9bb15609d83 100644 --- a/gcc/testsuite/gcc.dg/decl-9.c +++ b/gcc/testsuite/gcc.dg/decl-9.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-std=gnu89" } */ +/* { dg-options "-std=gnu89 -pedantic-errors" } */ w *x; /* { dg-error "unknown type name 'w'" } */ @@ -12,6 +12,7 @@ int f1() int d, e; d * e; /* { dg-bogus "unknown type name 'd'" } */ g * h; /* { dg-error "unknown type name 'g'" } */ + /* { dg-error "mixed declarations" "" { target *-*-* } .-1 } */ g i; /* { dg-error "unknown type name 'g'" } */ } diff --git a/gcc/testsuite/gcc.dg/gomp/barrier-2.c b/gcc/testsuite/gcc.dg/gomp/barrier-2.c index 5a7091946c4..98e16b2e051 100644 --- a/gcc/testsuite/gcc.dg/gomp/barrier-2.c +++ b/gcc/testsuite/gcc.dg/gomp/barrier-2.c @@ -1,4 +1,5 @@ /* { dg-do compile } */ +/* { dg-options "-Wall -std=c17 -fopenmp -pedantic-errors" } */ void f1(void) { @@ -16,6 +17,7 @@ void f1(void) void f2(void) { label: /* { dg-error "label at end of compound statement" } */ + /* { dg-error "defined but not used" "" { target *-*-* } .-1 } */ #pragma omp barrier /* { dg-error "may only be used in compound statements" } */ } diff --git a/gcc/testsuite/gcc.dg/gomp/declare-simd-5.c b/gcc/testsuite/gcc.dg/gomp/declare-simd-5.c index fe236525d62..51bd5f25bb0 100644 --- a/gcc/testsuite/gcc.dg/gomp/declare-simd-5.c +++ b/gcc/testsuite/gcc.dg/gomp/declare-simd-5.c @@ -1,5 +1,6 @@ /* Test parsing of #pragma omp declare simd */ /* { dg-do compile } */ +/* { dg-options "-std=c17 -fopenmp -pedantic-errors" } */ int f1 (int x) diff --git a/gcc/testsuite/gcc.dg/label-compound-stmt-1.c b/gcc/testsuite/gcc.dg/label-compound- stmt-1.c index 2f8fa4e65c2..2ae2b82ef1c 100644 --- a/gcc/testsuite/gcc.dg/label-compound-stmt-1.c +++ b/gcc/testsuite/gcc.dg/label-compound-stmt-1.c @@ -1,7 +1,7 @@ /* Test that labels at ends of compound statements are hard errors. */ /* Origin: Joseph Myers <j...@polyomino.org.uk> */ /* { dg-do compile } */ -/* { dg-options "" } */ +/* { dg-options "-std=c17 -pedantic-errors" } */ void f(void) { g: } /* { dg-bogus "warning" "warning in place of error" } */ /* { dg-error "label|parse|syntax" "label at end of compound statement" { target *-*-* } .-1 } */ diff --git a/gcc/testsuite/gcc.dg/parse-decl-after-label.c b/gcc/testsuite/gcc.dg/parse-decl-after- label.c index 9c9886a108f..6e2a047eb2e 100644 --- a/gcc/testsuite/gcc.dg/parse-decl-after-label.c +++ b/gcc/testsuite/gcc.dg/parse-decl-after-label.c @@ -1,6 +1,6 @@ /* PR 29062 { dg-do compile } -{ dg-options "-fsyntax-only" } +{ dg-options "-std=c17 -pedantic-errors -fsyntax-only" } */ int f(int x)