Message ID | 20241104232647.142859-1-polacek@redhat.com |
---|---|
State | New |
Headers | show |
Series | c: Implement C2y N3356, if declarations [PR117019] | expand |
+ Joseph On Mon, Nov 04, 2024 at 06:26:47PM -0500, Marek Polacek wrote: > Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk? > > -- >8 -- > This patch implements C2y N3356, if declarations as described at > <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3356.htm>. > > This feature is cognate with C++17 Selection statements with initializer > <https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0305r1.html>, > but they are not the same yet. For example, C++17 allows > > if (lock (); int i = getval ()) > > whereas C2y does not. > > The proposal adds new grammar productions. selection-header is handled > in c_parser_selection_header which is the gist of the patch. > simple-declaration is handled by c_parser_declaration_or_fndef, which > gets a new parameter. > > PR c/117019 > > gcc/c/ChangeLog: > > * c-parser.cc (c_parser_declaration_or_fndef): Adjust declaration. > (c_parser_external_declaration): Adjust a call to > c_parser_declaration_or_fndef. > (c_parser_declaration_or_fndef): New bool parameter. Return a tree > instead of void. Add an error. Adjust for N3356. Adjust a call to > c_parser_declaration_or_fndef. > (c_parser_compound_statement_nostart): Adjust calls to > c_parser_declaration_or_fndef. > (c_parser_selection_header): New. > (c_parser_paren_selection_header): New. > (c_parser_if_statement): Call c_parser_paren_selection_header > instead of c_parser_paren_condition. > (c_parser_switch_statement): Call c_parser_selection_header instead of > c_parser_expression. > (c_parser_for_statement): Adjust calls to c_parser_declaration_or_fndef. > (c_parser_objc_methodprotolist): Likewise. > (c_parser_oacc_routine): Likewise. > (c_parser_omp_loop_nest): Likewise. > (c_parser_omp_declare_simd): Likewise. > > gcc/testsuite/ChangeLog: > > * gcc.dg/c23-if-decls-1.c: New test. > * gcc.dg/c23-if-decls-2.c: New test. > * gcc.dg/c2y-if-decls-1.c: New test. > * gcc.dg/c2y-if-decls-2.c: New test. > * gcc.dg/c2y-if-decls-3.c: New test. > * gcc.dg/c2y-if-decls-4.c: New test. > * gcc.dg/c2y-if-decls-5.c: New test. > * gcc.dg/c2y-if-decls-6.c: New test. > * gcc.dg/c2y-if-decls-7.c: New test. > * gcc.dg/gnu2y-if-decls-1.c: New test. > * gcc.dg/gnu99-if-decls-1.c: New test. > * gcc.dg/gnu99-if-decls-2.c: New test. > --- > gcc/c/c-parser.cc | 250 ++++++++++++++++++------ > gcc/testsuite/gcc.dg/c23-if-decls-1.c | 15 ++ > gcc/testsuite/gcc.dg/c23-if-decls-2.c | 6 + > gcc/testsuite/gcc.dg/c2y-if-decls-1.c | 154 +++++++++++++++ > gcc/testsuite/gcc.dg/c2y-if-decls-2.c | 38 ++++ > gcc/testsuite/gcc.dg/c2y-if-decls-3.c | 40 ++++ > gcc/testsuite/gcc.dg/c2y-if-decls-4.c | 191 ++++++++++++++++++ > gcc/testsuite/gcc.dg/c2y-if-decls-5.c | 35 ++++ > gcc/testsuite/gcc.dg/c2y-if-decls-6.c | 27 +++ > gcc/testsuite/gcc.dg/c2y-if-decls-7.c | 21 ++ > gcc/testsuite/gcc.dg/gnu2y-if-decls-1.c | 15 ++ > gcc/testsuite/gcc.dg/gnu99-if-decls-1.c | 15 ++ > gcc/testsuite/gcc.dg/gnu99-if-decls-2.c | 15 ++ > 13 files changed, 766 insertions(+), 56 deletions(-) > create mode 100644 gcc/testsuite/gcc.dg/c23-if-decls-1.c > create mode 100644 gcc/testsuite/gcc.dg/c23-if-decls-2.c > create mode 100644 gcc/testsuite/gcc.dg/c2y-if-decls-1.c > create mode 100644 gcc/testsuite/gcc.dg/c2y-if-decls-2.c > create mode 100644 gcc/testsuite/gcc.dg/c2y-if-decls-3.c > create mode 100644 gcc/testsuite/gcc.dg/c2y-if-decls-4.c > create mode 100644 gcc/testsuite/gcc.dg/c2y-if-decls-5.c > create mode 100644 gcc/testsuite/gcc.dg/c2y-if-decls-6.c > create mode 100644 gcc/testsuite/gcc.dg/c2y-if-decls-7.c > create mode 100644 gcc/testsuite/gcc.dg/gnu2y-if-decls-1.c > create mode 100644 gcc/testsuite/gcc.dg/gnu99-if-decls-1.c > create mode 100644 gcc/testsuite/gcc.dg/gnu99-if-decls-2.c > > diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc > index 179c772fb76..7ed5b40cf1d 100644 > --- a/gcc/c/c-parser.cc > +++ b/gcc/c/c-parser.cc > @@ -1634,8 +1634,8 @@ static bool c_parser_nth_token_starts_std_attributes (c_parser *, > static tree c_parser_std_attribute_specifier_sequence (c_parser *); > static void c_parser_external_declaration (c_parser *); > static void c_parser_asm_definition (c_parser *); > -static void c_parser_declaration_or_fndef (c_parser *, bool, bool, bool, > - bool, bool, tree * = NULL, > +static tree c_parser_declaration_or_fndef (c_parser *, bool, bool, bool, > + bool, bool, bool, tree * = NULL, > vec<c_token> * = NULL, > bool have_attrs = false, > tree attrs = NULL, > @@ -2060,7 +2060,8 @@ c_parser_external_declaration (c_parser *parser) > an @interface or @protocol with prefix attributes). We can > only tell which after parsing the declaration specifiers, if > any, and the first declarator. */ > - c_parser_declaration_or_fndef (parser, true, true, true, false, true); > + c_parser_declaration_or_fndef (parser, true, true, true, false, true, > + false); > break; > } > } > @@ -2145,7 +2146,13 @@ handle_assume_attribute (location_t here, tree attrs, bool nested) > parsed in the caller (in contexts where such attributes had to be > parsed to determine whether what follows is a declaration or a > statement); HAVE_ATTRS says whether there were any such attributes > - (even empty). > + (even empty). If SIMPLE_OK, the construct can be a simple-declaration; > + in that case, the ';' is not consumed (left to the caller so that it > + can figure out if there was a simple-declaration or not), there must > + be an initializer, and only one object may be declared. When SIMPLE_OK > + is true we are called from c_parser_selection_header. > + > + Returns the resulting declaration, if there was any with an initializer. > > declaration: > declaration-specifiers init-declarator-list[opt] ; > @@ -2167,6 +2174,10 @@ handle_assume_attribute (location_t here, tree attrs, bool nested) > declarator simple-asm-expr[opt] gnu-attributes[opt] > declarator simple-asm-expr[opt] gnu-attributes[opt] = initializer > > + simple-declaration: > + attribute-specifier-sequence[opt] declaration-specifiers declarator > + = initializer > + > GNU extensions: > > nested-function-definition: > @@ -2213,10 +2224,11 @@ handle_assume_attribute (location_t here, tree attrs, bool nested) > declaration-specifiers[opt] __RTL (gimple-or-rtl-pass-list) declarator > declaration-list[opt] compound-statement */ > > -static void > +static tree > c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, > bool static_assert_ok, bool empty_ok, > bool nested, bool start_attr_ok, > + bool simple_ok, > tree *objc_foreach_object_declaration > /* = NULL */, > vec<c_token> *omp_declare_simd_clauses > @@ -2232,6 +2244,7 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, > tree all_prefix_attrs; > bool diagnosed_no_specs = false; > location_t here = c_parser_peek_token (parser)->location; > + tree result = NULL_TREE; > > add_debug_begin_stmt (c_parser_peek_token (parser)->location); > > @@ -2239,7 +2252,7 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, > && c_parser_next_token_is_keyword (parser, RID_STATIC_ASSERT)) > { > c_parser_static_assert_declaration (parser); > - return; > + return result; > } > specs = build_null_declspecs (); > > @@ -2325,13 +2338,13 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, > if (parser->error) > { > c_parser_skip_to_end_of_block_or_statement (parser); > - return; > + return result; > } > if (nested && !specs->declspecs_seen_p) > { > c_parser_error (parser, "expected declaration specifiers"); > c_parser_skip_to_end_of_block_or_statement (parser); > - return; > + return result; > } > > finish_declspecs (specs); > @@ -2366,6 +2379,10 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, > break; > } > } > + > + if (simple_ok && specs->storage_class != csc_none) > + error_at (here, "invalid storage class in condition"); > + > if (c_parser_next_token_is (parser, CPP_SEMICOLON)) > { > bool handled_assume = false; > @@ -2383,7 +2400,7 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, > c_parser_pragma (parser, pragma_external, NULL, NULL_TREE); > } > c_parser_consume_token (parser); > - return; > + return result; > } > if (specs->typespec_kind == ctsk_none > && lookup_attribute ("gnu", "assume", specs->attrs)) > @@ -2425,7 +2442,7 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, > c_parser_consume_token (parser); > if (oacc_routine_data) > c_finish_oacc_routine (oacc_routine_data, NULL_TREE, false); > - return; > + return result; > } > > /* Provide better error recovery. Note that a type name here is usually > @@ -2438,7 +2455,7 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, > c_parser_error (parser, "expected %<;%>, identifier or %<(%>"); > parser->error = false; > shadow_tag_warned (specs, 1); > - return; > + return result; > } > else if (c_dialect_objc () && !any_auto_type_p) > { > @@ -2448,7 +2465,7 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, > case CPP_PLUS: > case CPP_MINUS: > if (c_parser_objc_diagnose_bad_element_prefix (parser, specs)) > - return; > + return result; > if (specs->attrs) > { > warning_at (c_parser_peek_token (parser)->location, > @@ -2460,7 +2477,7 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, > c_parser_objc_method_definition (parser); > else > c_parser_objc_methodproto (parser); > - return; > + return result; > break; > default: > break; > @@ -2475,15 +2492,15 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, > case RID_AT_INTERFACE: > { > if (c_parser_objc_diagnose_bad_element_prefix (parser, specs)) > - return; > + return result; > c_parser_objc_class_definition (parser, specs->attrs); > - return; > + return result; > } > break; > case RID_AT_IMPLEMENTATION: > { > if (c_parser_objc_diagnose_bad_element_prefix (parser, specs)) > - return; > + return result; > if (specs->attrs) > { > warning_at (c_parser_peek_token (parser)->location, > @@ -2492,15 +2509,15 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, > specs->attrs = NULL_TREE; > } > c_parser_objc_class_definition (parser, NULL_TREE); > - return; > + return result; > } > break; > case RID_AT_PROTOCOL: > { > if (c_parser_objc_diagnose_bad_element_prefix (parser, specs)) > - return; > + return result; > c_parser_objc_protocol_definition (parser, specs->attrs); > - return; > + return result; > } > break; > case RID_AT_ALIAS: > @@ -2532,6 +2549,7 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, > prefix_attrs = specs->attrs; > all_prefix_attrs = prefix_attrs; > specs->attrs = NULL_TREE; > + bool more_than_one_decl = false; > while (true) > { > struct c_declarator *declarator; > @@ -2554,8 +2572,10 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, > omp_declare_simd_clauses); > if (oacc_routine_data) > c_finish_oacc_routine (oacc_routine_data, NULL_TREE, false); > - c_parser_skip_to_end_of_block_or_statement (parser); > - return; > + /* This check is here purely to improve the diagnostic. */ > + if (!simple_ok) > + c_parser_skip_to_end_of_block_or_statement (parser); > + return result; > } > if (flag_openmp || flag_openmp_simd) > { > @@ -2572,7 +2592,7 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, > "%<__auto_type%> requires a plain identifier" > " as declarator"); > c_parser_skip_to_end_of_block_or_statement (parser); > - return; > + return result; > } > if (std_auto_type_p) > { > @@ -2585,7 +2605,7 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, > "%<auto%> requires a plain identifier, possibly with" > " attributes, as declarator"); > c_parser_skip_to_end_of_block_or_statement (parser); > - return; > + return result; > } > underspec_name = d->u.id.id; > } > @@ -2626,7 +2646,7 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, > error_at (here, "attributes should be specified before the " > "declarator in a function definition"); > c_parser_skip_to_end_of_block_or_statement (parser); > - return; > + return result; > } > } > if (c_parser_next_token_is (parser, CPP_EQ)) > @@ -2766,6 +2786,7 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, > maybe_warn_string_init (init_loc, TREE_TYPE (d), init); > finish_decl (d, init_loc, init.value, > init.original_type, asm_name); > + result = d; > } > } > else > @@ -2776,7 +2797,7 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, > "%qs requires an initialized data declaration", > any_auto_type_p ? auto_type_keyword : "constexpr"); > c_parser_skip_to_end_of_block_or_statement (parser); > - return; > + return result; > } > > location_t lastloc = UNKNOWN_LOCATION; > @@ -2869,13 +2890,14 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, > } > if (c_parser_next_token_is (parser, CPP_COMMA)) > { > + more_than_one_decl = true; > if (any_auto_type_p || specs->constexpr_p) > { > error_at (here, > "%qs may only be used with a single declarator", > any_auto_type_p ? auto_type_keyword : "constexpr"); > c_parser_skip_to_end_of_block_or_statement (parser); > - return; > + return result; > } > c_parser_consume_token (parser); > if (c_parser_next_token_is_keyword (parser, RID_ATTRIBUTE)) > @@ -2887,8 +2909,9 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, > } > else if (c_parser_next_token_is (parser, CPP_SEMICOLON)) > { > - c_parser_consume_token (parser); > - return; > + if (!simple_ok) > + c_parser_consume_token (parser); > + return result; > } > else if (c_parser_next_token_is_keyword (parser, RID_IN)) > { > @@ -2897,13 +2920,20 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, > Objective-C foreach statement. Do not consume the > token, so that the caller can use it to determine > that this indeed is a foreach context. */ > - return; > + return result; > } > else > { > - c_parser_error (parser, "expected %<,%> or %<;%>"); > - c_parser_skip_to_end_of_block_or_statement (parser); > - return; > + if (!simple_ok) > + { > + c_parser_error (parser, "expected %<,%> or %<;%>"); > + c_parser_skip_to_end_of_block_or_statement (parser); > + } > + /* It's not valid to use if (int i = 2, j = 3). */ > + else if (more_than_one_decl) > + error_at (here, "declaration in condition can only declare " > + "a single object"); > + return result; > } > } > else if (any_auto_type_p || specs->constexpr_p) > @@ -2912,14 +2942,19 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, > "%qs requires an initialized data declaration", > any_auto_type_p ? auto_type_keyword : "constexpr"); > c_parser_skip_to_end_of_block_or_statement (parser); > - return; > + return result; > } > else if (!fndef_ok) > { > - c_parser_error (parser, "expected %<=%>, %<,%>, %<;%>, " > - "%<asm%> or %<__attribute__%>"); > - c_parser_skip_to_end_of_block_or_statement (parser); > - return; > + if (simple_ok && c_parser_next_token_is (parser, CPP_CLOSE_PAREN)) > + /* Let c_parser_selection_header emit the error. */; > + else > + { > + c_parser_error (parser, "expected %<=%>, %<,%>, %<;%>, " > + "%<asm%> or %<__attribute__%>"); > + c_parser_skip_to_end_of_block_or_statement (parser); > + } > + return result; > } > /* Function definition (nested or otherwise). */ > if (nested) > @@ -2988,7 +3023,7 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, > while (c_parser_next_token_is_not (parser, CPP_EOF) > && c_parser_next_token_is_not (parser, CPP_OPEN_BRACE)) > c_parser_declaration_or_fndef (parser, false, false, false, > - true, false); > + true, false, false); > debug_nonbind_markers_p = save_debug_nonbind_markers_p; > store_parm_decls (); > if (omp_declare_simd_clauses) > @@ -3018,7 +3053,7 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, > pop_scope (); > > finish_function (endloc); > - return; > + return result; > } > /* If the definition was marked with __GIMPLE then parse the > function body as GIMPLE. */ > @@ -3059,6 +3094,8 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, > > break; > } > + > + return result; > } > > /* Parse an asm-definition (asm() outside a function body). This is a > @@ -7378,7 +7415,7 @@ c_parser_compound_statement_nostart (c_parser *parser) > 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, > + true, true, true, false, NULL, > NULL, have_std_attrs, std_attrs, > NULL, &fallthru_attr_p); > > @@ -7421,7 +7458,7 @@ c_parser_compound_statement_nostart (c_parser *parser) > } > mark_valid_location_for_stdc_pragma (false); > c_parser_declaration_or_fndef (parser, true, true, true, true, > - true); > + true, false); > if (in_omp_loop_block) > omp_for_parse_state->want_nested_loop = want_nested_loop; > /* Following the old parser, __extension__ does not > @@ -8075,11 +8112,12 @@ c_parser_condition (c_parser *parser) > return cond; > } > > -/* Parse a parenthesized condition from an if, do or while statement. > +/* Parse a parenthesized condition from a do or while statement. > > condition: > ( expression ) > */ > + > static tree > c_parser_paren_condition (c_parser *parser) > { > @@ -8092,6 +8130,103 @@ c_parser_paren_condition (c_parser *parser) > return cond; > } > > +/* Parse a selection-header: > + > + selection-header: > + expression > + declaration expression > + simple-declaration > + > + simple-declaration: > + attribute-specifier-sequence[opt] declaration-specifiers declarator > + = initializer > + > + SWITCH_P is true if we are called from c_parser_switch_statement; in > + that case, don't perform the truthvalue conversion. */ > + > +static c_expr > +c_parser_selection_header (c_parser *parser, bool switch_p) > +{ > + location_t loc = c_parser_peek_token (parser)->location; > + c_expr expr; > + bool parse_expr = true; > + tree std_attrs; > + bool have_std_attrs = c_parser_nth_token_starts_std_attributes (parser, 1); > + if (have_std_attrs) > + std_attrs = c_parser_std_attribute_specifier_sequence (parser); > + else > + std_attrs = NULL_TREE; > + if (c_parser_next_tokens_start_declaration (parser)) > + { > + pedwarn_c23 (loc, OPT_Wpedantic, > + "ISO C does not support if declarations before C2Y"); > + expr.value > + = c_parser_declaration_or_fndef (parser, > + /*fndef_ok=*/false, > + /*static_assert_ok=*/false, > + /*empty_ok=*/false, > + /*nested=*/true, > + /*start_attr_ok=*/true, > + /*simple_ok=*/true, > + /*objc_foreach_object_decl=*/nullptr, > + /*omp_declare_simd_clauses=*/nullptr, > + have_std_attrs, > + std_attrs); > + if (c_parser_next_token_is (parser, CPP_SEMICOLON)) > + c_parser_consume_token (parser); > + else > + { > + /* A simple-declaration is a declaration that can appear in > + place of the controlling expression of a selection statement. > + In that case, there shall be an initializer. */ > + if (!expr.value) > + { > + error_at (loc, "declaration in the controlling expression must " > + "have an initializer"); > + expr.original_type = error_mark_node; > + expr.set_error (); > + return expr; > + } > + parse_expr = false; > + expr.original_type = TREE_TYPE (expr.value); > + } > + } > + else if (have_std_attrs) > + { > + c_parser_error (parser, "expected declaration"); > + expr.original_type = error_mark_node; > + expr.set_error (); > + return expr; > + } > + > + if (parse_expr) > + expr = c_parser_expression_conv (parser); > + if (!switch_p) > + { > + expr.value = c_objc_common_truthvalue_conversion (loc, expr.value); > + expr.value = c_fully_fold (expr.value, false, NULL); > + if (warn_sequence_point) > + verify_sequence_points (expr.value); > + } > + return expr; > +} > + > +/* Parse a selection-header enclosed in parentheses: > + > + ( selection-header ) > +*/ > + > +static tree > +c_parser_paren_selection_header (c_parser *parser) > +{ > + matching_parens parens; > + if (!parens.require_open (parser)) > + return error_mark_node; > + tree cond = c_parser_selection_header (parser, /*switch_p=*/false).value; > + parens.skip_until_found_close (parser); > + return cond; > +} > + > /* Parse a statement which is a block in C99. > > IF_P is used to track whether there's a (possibly labeled) if statement > @@ -8244,8 +8379,8 @@ c_parser_maybe_reclassify_token (c_parser *parser) > /* Parse an if statement (C90 6.6.4, C99 6.8.4, C11 6.8.4). > > if-statement: > - if ( expression ) statement > - if ( expression ) statement else statement > + if ( selection-header ) statement > + if ( selection-header ) statement else statement > > CHAIN is a vector of if-else-if conditions. > IF_P is used to track whether there's a (possibly labeled) if statement > @@ -8268,7 +8403,7 @@ c_parser_if_statement (c_parser *parser, bool *if_p, vec<tree> *chain) > c_parser_consume_token (parser); > block = c_begin_compound_stmt (flag_isoc99); > loc = c_parser_peek_token (parser)->location; > - cond = c_parser_paren_condition (parser); > + cond = c_parser_paren_selection_header (parser); > in_if_block = parser->in_if_block; > parser->in_if_block = true; > first_body = c_parser_if_body (parser, &nested_if, if_tinfo); > @@ -8355,7 +8490,9 @@ c_parser_switch_statement (c_parser *parser, bool *if_p, tree before_labels) > if (c_parser_next_token_is (parser, CPP_OPEN_PAREN) > && c_token_starts_typename (c_parser_peek_2nd_token (parser))) > explicit_cast_p = true; > - ce = c_parser_expression (parser); > + ce = c_parser_selection_header (parser, /*switch_p=*/true); > + /* The call above already performed convert_lvalue_to_rvalue, but with > + read_p=false. */ > ce = convert_lvalue_to_rvalue (switch_cond_loc, ce, true, true); > expr = ce.value; > /* ??? expr has no valid location? */ > @@ -8667,7 +8804,7 @@ c_parser_for_statement (c_parser *parser, bool ivdep, unsigned short unroll, > || c_parser_nth_token_starts_std_attributes (parser, 1)) > { > c_parser_declaration_or_fndef (parser, true, true, true, true, true, > - &object_expression); > + false, &object_expression); > parser->objc_could_be_foreach_context = false; > > if (c_parser_next_token_is_keyword (parser, RID_IN)) > @@ -8698,7 +8835,7 @@ c_parser_for_statement (c_parser *parser, bool ivdep, unsigned short unroll, > ext = disable_extension_diagnostics (); > c_parser_consume_token (parser); > c_parser_declaration_or_fndef (parser, true, true, true, true, > - true, &object_expression); > + true, false, &object_expression); > parser->objc_could_be_foreach_context = false; > > restore_extension_diagnostics (ext); > @@ -14050,7 +14187,7 @@ c_parser_objc_methodprotolist (c_parser *parser) > } > else > c_parser_declaration_or_fndef (parser, false, false, true, > - false, true); > + false, true, false); > break; > } > } > @@ -21170,12 +21307,12 @@ c_parser_oacc_routine (c_parser *parser, enum pragma_context context) > while (c_parser_next_token_is (parser, CPP_KEYWORD) > && c_parser_peek_token (parser)->keyword == RID_EXTENSION); > c_parser_declaration_or_fndef (parser, true, true, true, false, true, > - NULL, NULL, false, NULL, &data); > + false, NULL, NULL, false, NULL, &data); > restore_extension_diagnostics (ext); > } > else > c_parser_declaration_or_fndef (parser, true, true, true, false, true, > - NULL, NULL, false, NULL, &data); > + false, NULL, NULL, false, NULL, &data); > } > } > > @@ -23136,7 +23273,8 @@ c_parser_omp_loop_nest (c_parser *parser, bool *if_p) > /* This is a declaration, which must be added to the pre_body code. */ > tree this_pre_body = push_stmt_list (); > c_in_omp_for = true; > - c_parser_declaration_or_fndef (parser, true, true, true, true, true); > + c_parser_declaration_or_fndef (parser, true, true, true, true, true, > + false); > c_in_omp_for = false; > this_pre_body = pop_stmt_list (this_pre_body); > append_to_statement_list_force (this_pre_body, > @@ -25378,12 +25516,12 @@ c_parser_omp_declare_simd (c_parser *parser, enum pragma_context context) > while (c_parser_next_token_is (parser, CPP_KEYWORD) > && c_parser_peek_token (parser)->keyword == RID_EXTENSION); > c_parser_declaration_or_fndef (parser, true, true, true, false, true, > - NULL, &clauses); > + false, NULL, &clauses); > restore_extension_diagnostics (ext); > } > else > c_parser_declaration_or_fndef (parser, true, true, true, false, true, > - NULL, &clauses); > + false, NULL, &clauses); > break; > case pragma_struct: > case pragma_param: > @@ -25412,7 +25550,7 @@ c_parser_omp_declare_simd (c_parser *parser, enum pragma_context context) > || c_parser_nth_token_starts_std_attributes (parser, 1)) > { > c_parser_declaration_or_fndef (parser, true, true, true, true, > - true, NULL, &clauses, > + true, false, NULL, &clauses, > have_std_attrs, std_attrs); > restore_extension_diagnostics (ext); > break; > @@ -25422,7 +25560,7 @@ c_parser_omp_declare_simd (c_parser *parser, enum pragma_context context) > else if (c_parser_next_tokens_start_declaration (parser)) > { > c_parser_declaration_or_fndef (parser, true, true, true, true, true, > - NULL, &clauses, have_std_attrs, > + false, NULL, &clauses, have_std_attrs, > std_attrs); > break; > } > diff --git a/gcc/testsuite/gcc.dg/c23-if-decls-1.c b/gcc/testsuite/gcc.dg/c23-if-decls-1.c > new file mode 100644 > index 00000000000..ea968c67c6a > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/c23-if-decls-1.c > @@ -0,0 +1,15 @@ > +/* N3356 - if declarations. */ > +/* PR c/117019 */ > +/* { dg-do compile } */ > +/* { dg-options "-std=c23 -pedantic-errors" } */ > + > +void > +g () > +{ > + if (int i = 42); /* { dg-error "ISO C does not support if declarations before C2Y" } */ > + if (int i = 42; i > 10); /* { dg-error "ISO C does not support if declarations before C2Y" } */ > + if (int i, j; i = 42); /* { dg-error "ISO C does not support if declarations before C2Y" } */ > + switch (int i = 42); /* { dg-error "ISO C does not support if declarations before C2Y" } */ > + switch (int i = 42; i); /* { dg-error "ISO C does not support if declarations before C2Y" } */ > + switch (int i, j; i = 42); /* { dg-error "ISO C does not support if declarations before C2Y" } */ > +} > diff --git a/gcc/testsuite/gcc.dg/c23-if-decls-2.c b/gcc/testsuite/gcc.dg/c23-if-decls-2.c > new file mode 100644 > index 00000000000..d53db715377 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/c23-if-decls-2.c > @@ -0,0 +1,6 @@ > +/* N3356 - if declarations. */ > +/* PR c/117019 */ > +/* { dg-do compile } */ > +/* { dg-options "-std=c23 -pedantic-errors -Wno-c23-c2y-compat" } */ > + > +#include "c23-if-decls-1.c" > diff --git a/gcc/testsuite/gcc.dg/c2y-if-decls-1.c b/gcc/testsuite/gcc.dg/c2y-if-decls-1.c > new file mode 100644 > index 00000000000..417b8052775 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/c2y-if-decls-1.c > @@ -0,0 +1,154 @@ > +/* N3356 - if declarations. */ > +/* PR c/117019 */ > +/* { dg-do run } */ > +/* { dg-options "-std=c2y -Wc23-c2y-compat" } */ > +/* Test C2Y if declarations. Valid usages. */ > + > +int get () { return 42; } > +int foo (int i) { return i; } > + > +enum E { X = 1, Y }; > + > +void > +simple () > +{ > + if (int i = get ()) /* { dg-warning "if declarations before C2Y" } */ > + foo (i); > + else > + __builtin_abort (); > + > + if (int i = 0) /* { dg-warning "if declarations before C2Y" } */ > + __builtin_abort (); > + else > + foo (i); > + > + if (auto i = get ()) /* { dg-warning "if declarations before C2Y" } */ > + foo (i); > + else > + __builtin_abort (); > + > + if (__typeof__(get ()) i = get ()) /* { dg-warning "if declarations before C2Y" } */ > + foo (i); > + else > + __builtin_abort (); > + > + if (auto i = 0) /* { dg-warning "if declarations before C2Y" } */ > + __builtin_abort (); > + else > + foo (i); > + > + if (int (*f)(int) = foo) /* { dg-warning "if declarations before C2Y" } */ > + f (1); > + else > + __builtin_abort (); > + > + if ([[maybe_unused]] int i = get ()) /* { dg-warning "if declarations before C2Y" } */ > + foo (i); > + else > + __builtin_abort (); > + > + if (__attribute__((unused)) int i = get ()) /* { dg-warning "if declarations before C2Y" } */ > + foo (i); > + else > + __builtin_abort (); > + > + if (enum E e = X) /* { dg-warning "if declarations before C2Y" } */ > + foo (e); > + else > + __builtin_abort (); > + > + if (constexpr int i = 42) /* { dg-warning "if declarations before C2Y" } */ > + foo (i); > + else > + __builtin_abort (); > + > + if (int i = 1) /* { dg-warning "if declarations before C2Y" } */ > + if (int j = 2) /* { dg-warning "if declarations before C2Y" } */ > + if (int k = 3) /* { dg-warning "if declarations before C2Y" } */ > + foo (i + j + k); > + > + double i; > +} > + > +void > +expr () > +{ > + if (int i = get (); i == 42) /* { dg-warning "if declarations before C2Y" } */ > + foo (i); > + else > + __builtin_abort (); > + > + if (int i = get (); i != 42) /* { dg-warning "if declarations before C2Y" } */ > + __builtin_abort (); > + else > + foo (i); > + > + if (auto i = get (); i == 42) /* { dg-warning "if declarations before C2Y" } */ > + foo (i); > + else > + __builtin_abort (); > + > + if (__typeof__(get ()) i = get (); i == 42) /* { dg-warning "if declarations before C2Y" } */ > + foo (i); > + else > + __builtin_abort (); > + > + if (auto i = get (); i != 42) /* { dg-warning "if declarations before C2Y" } */ > + __builtin_abort (); > + else > + foo (i); > + > + if (int (*f)(int) = foo; f (42)) /* { dg-warning "if declarations before C2Y" } */ > + f (1); > + else > + __builtin_abort (); > + > + if ([[maybe_unused]] int i = get (); i == 42) /* { dg-warning "if declarations before C2Y" } */ > + foo (i); > + else > + __builtin_abort (); > + > + if (__attribute__((unused)) int i = get (); i == 42) /* { dg-warning "if declarations before C2Y" } */ > + foo (i); > + else > + __builtin_abort (); > + > + if (enum E e = X; e == X) /* { dg-warning "if declarations before C2Y" } */ > + foo (e); > + else > + __builtin_abort (); > + > + if (constexpr int i = 42; i == 42) /* { dg-warning "if declarations before C2Y" } */ > + foo (i); > + else > + __builtin_abort (); > + > + if (int i = 1; i) /* { dg-warning "if declarations before C2Y" } */ > + if (int j = 2; j) /* { dg-warning "if declarations before C2Y" } */ > + if (int k = 3; k) /* { dg-warning "if declarations before C2Y" } */ > + foo (i + j + k); > + > + if (int i = 2, j = get (); i + j > 0) /* { dg-warning "if declarations before C2Y" } */ > + foo (i + j); > + else > + __builtin_abort (); > + > + if (int i; i = 1) /* { dg-warning "if declarations before C2Y" } */ > + foo (i); > + else > + __builtin_abort (); > + > + if (int arr[] = { 1, 2, 3}; arr[0]) /* { dg-warning "if declarations before C2Y" } */ > + foo (arr[0]); > + else > + __builtin_abort (); > + > + double i; > +} > + > +int > +main () > +{ > + simple (); > + expr (); > +} > diff --git a/gcc/testsuite/gcc.dg/c2y-if-decls-2.c b/gcc/testsuite/gcc.dg/c2y-if-decls-2.c > new file mode 100644 > index 00000000000..e06344e4fe0 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/c2y-if-decls-2.c > @@ -0,0 +1,38 @@ > +/* N3356 - if declarations. */ > +/* PR c/117019 */ > +/* { dg-do compile } */ > +/* { dg-options "-std=c2y" } */ > +/* Test C2Y if declarations. Invalid usages. */ > + > +void > +g (int g) > +{ > + if (;); /* { dg-error "expected" } */ > + if (int); /* { dg-error "expected|initializer" } */ > + if (auto); /* { dg-error "expected|initializer" } */ > + if (int;); /* { dg-error "initializer" } */ > + /* { dg-warning "empty" "" { target *-*-* } .-1 } */ > + if (auto;); /* { dg-error "empty|initializer" } */ > + if (int i); /* { dg-error "initializer" } */ > + if (int i;); /* { dg-error "expected" } */ > + if (int i = 0;); /* { dg-error "expected" } */ > + > + if (extern int i = 0); /* { dg-error "in condition|both .extern. and initializer" } */ > + if (register int i = 0); /* { dg-error "in condition" } */ > + if (static int i = 0); /* { dg-error "in condition" } */ > + if (thread_local int i = 0); /* { dg-error "in condition|function-scope" } */ > + if (typedef int i); /* { dg-error "in condition|initializer" } */ > + if (typedef int i = 0); /* { dg-error "in condition|initialized" } */ > + > + if (int i = 2, j = 3); /* { dg-error "only declare a single object" } */ > + > + if (void (*fp)(int)); /* { dg-error "initializer" } */ > + if ([[maybe_unused]] g); /* { dg-error "expected" } */ > + if ([[maybe_unused]] 42); /* { dg-error "expected" } */ > + if ([[maybe_unused]] int); /* { dg-error "expected|initializer" } */ > + if (__attribute__((unused)) g); /* { dg-error "initializer" } */ > + if (__attribute__((unused)) 42); /* { dg-error "expected|initializer" } */ > + if (__attribute__((unused)) int); /* { dg-error "expected|initializer" } */ > + > + if (int arr[] = { 1 }); /* { dg-error "scalar is required" } */ > +} > diff --git a/gcc/testsuite/gcc.dg/c2y-if-decls-3.c b/gcc/testsuite/gcc.dg/c2y-if-decls-3.c > new file mode 100644 > index 00000000000..685f826e695 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/c2y-if-decls-3.c > @@ -0,0 +1,40 @@ > +/* N3356 - if declarations. */ > +/* PR c/117019 */ > +/* { dg-do compile } */ > +/* { dg-options "-std=c2y" } */ > +/* Test C2Y if declarations. Invalid usages. */ > + > +void > +g (int g) > +{ > + switch (;); /* { dg-error "expected" } */ > + switch (int); /* { dg-error "expected identifier" } */ > + /* { dg-error "declaration" "" { target *-*-* } .-1 } */ > + switch (auto); /* { dg-error "expected identifier" } */ > + /* { dg-error "declaration" "" { target *-*-* } .-1 } */ > + switch (int;); /* { dg-error "declaration" } */ > + /* { dg-warning "empty" "" { target *-*-* } .-1 } */ > + switch (auto;); /* { dg-error "empty|initializer" } */ > + switch (int i); /* { dg-error "initializer" } */ > + switch (int i;); /* { dg-error "expected" } */ > + switch (int i = 0;); /* { dg-error "expected" } */ > + > + switch (extern int i = 0); /* { dg-error "in condition|both .extern. and initializer" } */ > + switch (register int i = 0); /* { dg-error "in condition" } */ > + switch (static int i = 0); /* { dg-error "in condition" } */ > + switch (thread_local int i = 0); /* { dg-error "in condition|function-scope" } */ > + switch (typedef int i); /* { dg-error "in condition|initializer" } */ > + switch (typedef int i = 0); /* { dg-error "in condition|initialized" } */ > + > + switch (int i = 2, j = 3); /* { dg-error "only declare a single object" } */ > + > + switch (void (*fp)(int)); /* { dg-error "initializer" } */ > + switch ([[maybe_unused]] g); /* { dg-error "expected" } */ > + switch ([[maybe_unused]] 42); /* { dg-error "expected" } */ > + switch ([[maybe_unused]] int); /* { dg-error "expected|initializer" } */ > + switch (__attribute__((unused)) g); /* { dg-error "initializer" } */ > + switch (__attribute__((unused)) 42); /* { dg-error "expected|initializer" } */ > + switch (__attribute__((unused)) int); /* { dg-error "expected|initializer" } */ > + > + switch (int arr[] = { 1 }); /* { dg-error "switch quantity not an integer" } */ > +} > diff --git a/gcc/testsuite/gcc.dg/c2y-if-decls-4.c b/gcc/testsuite/gcc.dg/c2y-if-decls-4.c > new file mode 100644 > index 00000000000..f518adf7dd1 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/c2y-if-decls-4.c > @@ -0,0 +1,191 @@ > +/* N3356 - if declarations. */ > +/* PR c/117019 */ > +/* { dg-do run } */ > +/* { dg-options "-std=c2y -Wc23-c2y-compat" } */ > +/* Test C2Y if declarations. Valid usages. */ > + > +int get () { return 42; } > +int foo (int i) { return i; } > + > +enum E { X = 1, Y }; > + > +void > +simple () > +{ > + switch (int i = get ()) /* { dg-warning "if declarations before C2Y" } */ > + { > + case 42: > + foo (i); > + break; > + default: > + __builtin_abort (); > + } > + > + switch (int i = 0) /* { dg-warning "if declarations before C2Y" } */ > + { > + case 0: > + foo (i); > + break; > + default: > + __builtin_abort (); > + } > + > + switch (auto i = get ()) /* { dg-warning "if declarations before C2Y" } */ > + { > + case 42: > + foo (i); > + break; > + default: > + __builtin_abort (); > + } > + > + switch (__typeof__(get ()) i = get ()) /* { dg-warning "if declarations before C2Y" } */ > + { > + case 42: > + foo (i); > + break; > + default: > + __builtin_abort (); > + } > + > + switch (auto i = 0) /* { dg-warning "if declarations before C2Y" } */ > + { > + case 0: > + foo (i); > + break; > + default: > + __builtin_abort (); > + } > + > + switch ([[maybe_unused]] int i = get ()) /* { dg-warning "if declarations before C2Y" } */ > + { > + case 42: > + foo (i); > + break; > + default: > + __builtin_abort (); > + } > + > + switch (__attribute__((unused)) int i = get ()) /* { dg-warning "if declarations before C2Y" } */ > + { > + case 42: > + foo (i); > + break; > + default: > + __builtin_abort (); > + } > + > + switch (enum E e = X) /* { dg-warning "if declarations before C2Y" } */ > + { > + case X: > + foo (X); > + break; > + default: > + __builtin_abort (); > + } > + > + switch (constexpr int i = 42) /* { dg-warning "if declarations before C2Y" } */ > + { > + case 42: > + foo (i); > + break; > + default: > + __builtin_abort (); > + } > + > + double i; > +} > + > +void > +expr () > +{ > + switch (int i = get (); i) /* { dg-warning "if declarations before C2Y" } */ > + { > + case 42: > + foo (i); > + break; > + default: > + __builtin_abort (); > + } > + > + switch (auto i = get (); i) /* { dg-warning "if declarations before C2Y" } */ > + { > + case 42: > + foo (i); > + break; > + default: > + __builtin_abort (); > + } > + > + switch (__typeof__(get ()) i = get (); i) /* { dg-warning "if declarations before C2Y" } */ > + { > + case 42: > + foo (i); > + break; > + default: > + __builtin_abort (); > + } > + > + switch (int (*f)(int) = foo; f (42)) /* { dg-warning "if declarations before C2Y" } */ > + { > + case 42: > + foo (42); > + break; > + default: > + __builtin_abort (); > + } > + > + switch ([[maybe_unused]] int i = get (); i) /* { dg-warning "if declarations before C2Y" } */ > + { > + case 42: > + foo (i); > + break; > + default: > + __builtin_abort (); > + } > + > + switch (__attribute__((unused)) int i = get (); i) /* { dg-warning "if declarations before C2Y" } */ > + { > + case 42: > + foo (i); > + break; > + default: > + __builtin_abort (); > + } > + > + switch (enum E e = X; e) /* { dg-warning "if declarations before C2Y" } */ > + { > + case X: > + foo (X); > + break; > + default: > + __builtin_abort (); > + } > + > + switch (constexpr int i = 42; i) /* { dg-warning "if declarations before C2Y" } */ > + { > + case 42: > + foo (i); > + break; > + default: > + __builtin_abort (); > + } > + > + switch (int arr[] = { 1, 2, 3}; arr[0]) /* { dg-warning "if declarations before C2Y" } */ > + { > + case 1: > + foo (arr[0]); > + break; > + default: > + __builtin_abort (); > + } > + > + double i; > +} > + > +int > +main () > +{ > + simple (); > + expr (); > +} > diff --git a/gcc/testsuite/gcc.dg/c2y-if-decls-5.c b/gcc/testsuite/gcc.dg/c2y-if-decls-5.c > new file mode 100644 > index 00000000000..dfab357c312 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/c2y-if-decls-5.c > @@ -0,0 +1,35 @@ > +/* N3356 - if declarations. */ > +/* PR c/117019 */ > +/* { dg-do run } */ > +/* { dg-options "-std=c2y" } */ > + > +int g; > +int get () { ++g; return 42; } > + > +struct S { int i; }; > + > +int > +main () > +{ > + if (auto x = get (); get (), x); > + if (g != 2) > + __builtin_abort (); > + > + switch (auto x = get (); get (), x); > + if (g != 4) > + __builtin_abort (); > + > + if (struct S s = { 42 }; s.i != 42) > + __builtin_abort (); > + > + if (int i = 42) > + { > + i = 0; > + if (int j = 42) > + j = 0; > + else > + j = 42; > + } > + else > + i = 42; > +} > diff --git a/gcc/testsuite/gcc.dg/c2y-if-decls-6.c b/gcc/testsuite/gcc.dg/c2y-if-decls-6.c > new file mode 100644 > index 00000000000..57dd9e01bde > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/c2y-if-decls-6.c > @@ -0,0 +1,27 @@ > +/* N3356 - if declarations. */ > +/* PR c/117019 */ > +/* { dg-do run } */ > +/* { dg-options "-std=c2y -Wall -Wextra" } */ > +/* Test VLAs. */ > + > +void foo (int) { } > + > +int > +main () > +{ > + int i = 3; > + > + if (int arr[i] = { }; !arr[0]) > + foo (arr[0]); > + else > + __builtin_abort (); > + > + switch (int arr[i] = { }; arr[0]) > + { > + case 0: > + foo (arr[0]); > + break; > + default: > + __builtin_abort (); > + } > +} > diff --git a/gcc/testsuite/gcc.dg/c2y-if-decls-7.c b/gcc/testsuite/gcc.dg/c2y-if-decls-7.c > new file mode 100644 > index 00000000000..df451e3e779 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/c2y-if-decls-7.c > @@ -0,0 +1,21 @@ > +/* N3356 - if declarations. */ > +/* PR c/117019 */ > +/* { dg-do compile } */ > +/* { dg-options "-std=c2y -Wall -Wextra" } */ > +/* Test VLAs. Invalid code. */ > + > +void foo (int) { } > + > +void > +g () > +{ > + int i = 3; > + > + switch (i) /* { dg-message "switch starts here" } */ > + { /* { dg-warning "statement will never be executed" } */ > + int arr[i] = { }; > + default: /* { dg-error "switch jumps into scope" } */ > + i = arr[0]; > + break; > + } > +} > diff --git a/gcc/testsuite/gcc.dg/gnu2y-if-decls-1.c b/gcc/testsuite/gcc.dg/gnu2y-if-decls-1.c > new file mode 100644 > index 00000000000..65f526e9dc2 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/gnu2y-if-decls-1.c > @@ -0,0 +1,15 @@ > +/* N3356 - if declarations. */ > +/* PR c/117019 */ > +/* { dg-do compile } */ > +/* { dg-options "-std=gnu2y" } */ > + > +void > +g () > +{ > + if (int i = 42); > + if (int i = 42; i > 10); > + if (int i, j; i = 42); > + switch (int i = 42); > + switch (int i = 42; i); > + switch (int i, j; i = 42); > +} > diff --git a/gcc/testsuite/gcc.dg/gnu99-if-decls-1.c b/gcc/testsuite/gcc.dg/gnu99-if-decls-1.c > new file mode 100644 > index 00000000000..6d7a3bc196b > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/gnu99-if-decls-1.c > @@ -0,0 +1,15 @@ > +/* N3356 - if declarations. */ > +/* PR c/117019 */ > +/* { dg-do compile } */ > +/* { dg-options "-std=gnu99 -Wpedantic" } */ > + > +void > +g () > +{ > + if (int i = 42); /* { dg-warning "ISO C does not support if declarations before C2Y" } */ > + if (int i = 42; i > 10); /* { dg-warning "ISO C does not support if declarations before C2Y" } */ > + if (int i, j; i = 42); /* { dg-warning "ISO C does not support if declarations before C2Y" } */ > + switch (int i = 42); /* { dg-warning "ISO C does not support if declarations before C2Y" } */ > + switch (int i = 42; i); /* { dg-warning "ISO C does not support if declarations before C2Y" } */ > + switch (int i, j; i = 42); /* { dg-warning "ISO C does not support if declarations before C2Y" } */ > +} > diff --git a/gcc/testsuite/gcc.dg/gnu99-if-decls-2.c b/gcc/testsuite/gcc.dg/gnu99-if-decls-2.c > new file mode 100644 > index 00000000000..82f779c71c2 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/gnu99-if-decls-2.c > @@ -0,0 +1,15 @@ > +/* N3356 - if declarations. */ > +/* PR c/117019 */ > +/* { dg-do compile } */ > +/* { dg-options "-std=gnu99" } */ > + > +void > +g () > +{ > + if (int i = 42); > + if (int i = 42; i > 10); > + if (int i, j; i = 42); > + switch (int i = 42); > + switch (int i = 42; i); > + switch (int i, j; i = 42); > +} > > base-commit: fe97ac43e05a8da8a12fbad2208a1ebb19d2d6c9 > -- > 2.47.0 > Marek
diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc index 179c772fb76..7ed5b40cf1d 100644 --- a/gcc/c/c-parser.cc +++ b/gcc/c/c-parser.cc @@ -1634,8 +1634,8 @@ static bool c_parser_nth_token_starts_std_attributes (c_parser *, static tree c_parser_std_attribute_specifier_sequence (c_parser *); static void c_parser_external_declaration (c_parser *); static void c_parser_asm_definition (c_parser *); -static void c_parser_declaration_or_fndef (c_parser *, bool, bool, bool, - bool, bool, tree * = NULL, +static tree c_parser_declaration_or_fndef (c_parser *, bool, bool, bool, + bool, bool, bool, tree * = NULL, vec<c_token> * = NULL, bool have_attrs = false, tree attrs = NULL, @@ -2060,7 +2060,8 @@ c_parser_external_declaration (c_parser *parser) an @interface or @protocol with prefix attributes). We can only tell which after parsing the declaration specifiers, if any, and the first declarator. */ - c_parser_declaration_or_fndef (parser, true, true, true, false, true); + c_parser_declaration_or_fndef (parser, true, true, true, false, true, + false); break; } } @@ -2145,7 +2146,13 @@ handle_assume_attribute (location_t here, tree attrs, bool nested) parsed in the caller (in contexts where such attributes had to be parsed to determine whether what follows is a declaration or a statement); HAVE_ATTRS says whether there were any such attributes - (even empty). + (even empty). If SIMPLE_OK, the construct can be a simple-declaration; + in that case, the ';' is not consumed (left to the caller so that it + can figure out if there was a simple-declaration or not), there must + be an initializer, and only one object may be declared. When SIMPLE_OK + is true we are called from c_parser_selection_header. + + Returns the resulting declaration, if there was any with an initializer. declaration: declaration-specifiers init-declarator-list[opt] ; @@ -2167,6 +2174,10 @@ handle_assume_attribute (location_t here, tree attrs, bool nested) declarator simple-asm-expr[opt] gnu-attributes[opt] declarator simple-asm-expr[opt] gnu-attributes[opt] = initializer + simple-declaration: + attribute-specifier-sequence[opt] declaration-specifiers declarator + = initializer + GNU extensions: nested-function-definition: @@ -2213,10 +2224,11 @@ handle_assume_attribute (location_t here, tree attrs, bool nested) declaration-specifiers[opt] __RTL (gimple-or-rtl-pass-list) declarator declaration-list[opt] compound-statement */ -static void +static tree c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, bool static_assert_ok, bool empty_ok, bool nested, bool start_attr_ok, + bool simple_ok, tree *objc_foreach_object_declaration /* = NULL */, vec<c_token> *omp_declare_simd_clauses @@ -2232,6 +2244,7 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, tree all_prefix_attrs; bool diagnosed_no_specs = false; location_t here = c_parser_peek_token (parser)->location; + tree result = NULL_TREE; add_debug_begin_stmt (c_parser_peek_token (parser)->location); @@ -2239,7 +2252,7 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, && c_parser_next_token_is_keyword (parser, RID_STATIC_ASSERT)) { c_parser_static_assert_declaration (parser); - return; + return result; } specs = build_null_declspecs (); @@ -2325,13 +2338,13 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, if (parser->error) { c_parser_skip_to_end_of_block_or_statement (parser); - return; + return result; } if (nested && !specs->declspecs_seen_p) { c_parser_error (parser, "expected declaration specifiers"); c_parser_skip_to_end_of_block_or_statement (parser); - return; + return result; } finish_declspecs (specs); @@ -2366,6 +2379,10 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, break; } } + + if (simple_ok && specs->storage_class != csc_none) + error_at (here, "invalid storage class in condition"); + if (c_parser_next_token_is (parser, CPP_SEMICOLON)) { bool handled_assume = false; @@ -2383,7 +2400,7 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, c_parser_pragma (parser, pragma_external, NULL, NULL_TREE); } c_parser_consume_token (parser); - return; + return result; } if (specs->typespec_kind == ctsk_none && lookup_attribute ("gnu", "assume", specs->attrs)) @@ -2425,7 +2442,7 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, c_parser_consume_token (parser); if (oacc_routine_data) c_finish_oacc_routine (oacc_routine_data, NULL_TREE, false); - return; + return result; } /* Provide better error recovery. Note that a type name here is usually @@ -2438,7 +2455,7 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, c_parser_error (parser, "expected %<;%>, identifier or %<(%>"); parser->error = false; shadow_tag_warned (specs, 1); - return; + return result; } else if (c_dialect_objc () && !any_auto_type_p) { @@ -2448,7 +2465,7 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, case CPP_PLUS: case CPP_MINUS: if (c_parser_objc_diagnose_bad_element_prefix (parser, specs)) - return; + return result; if (specs->attrs) { warning_at (c_parser_peek_token (parser)->location, @@ -2460,7 +2477,7 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, c_parser_objc_method_definition (parser); else c_parser_objc_methodproto (parser); - return; + return result; break; default: break; @@ -2475,15 +2492,15 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, case RID_AT_INTERFACE: { if (c_parser_objc_diagnose_bad_element_prefix (parser, specs)) - return; + return result; c_parser_objc_class_definition (parser, specs->attrs); - return; + return result; } break; case RID_AT_IMPLEMENTATION: { if (c_parser_objc_diagnose_bad_element_prefix (parser, specs)) - return; + return result; if (specs->attrs) { warning_at (c_parser_peek_token (parser)->location, @@ -2492,15 +2509,15 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, specs->attrs = NULL_TREE; } c_parser_objc_class_definition (parser, NULL_TREE); - return; + return result; } break; case RID_AT_PROTOCOL: { if (c_parser_objc_diagnose_bad_element_prefix (parser, specs)) - return; + return result; c_parser_objc_protocol_definition (parser, specs->attrs); - return; + return result; } break; case RID_AT_ALIAS: @@ -2532,6 +2549,7 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, prefix_attrs = specs->attrs; all_prefix_attrs = prefix_attrs; specs->attrs = NULL_TREE; + bool more_than_one_decl = false; while (true) { struct c_declarator *declarator; @@ -2554,8 +2572,10 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, omp_declare_simd_clauses); if (oacc_routine_data) c_finish_oacc_routine (oacc_routine_data, NULL_TREE, false); - c_parser_skip_to_end_of_block_or_statement (parser); - return; + /* This check is here purely to improve the diagnostic. */ + if (!simple_ok) + c_parser_skip_to_end_of_block_or_statement (parser); + return result; } if (flag_openmp || flag_openmp_simd) { @@ -2572,7 +2592,7 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, "%<__auto_type%> requires a plain identifier" " as declarator"); c_parser_skip_to_end_of_block_or_statement (parser); - return; + return result; } if (std_auto_type_p) { @@ -2585,7 +2605,7 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, "%<auto%> requires a plain identifier, possibly with" " attributes, as declarator"); c_parser_skip_to_end_of_block_or_statement (parser); - return; + return result; } underspec_name = d->u.id.id; } @@ -2626,7 +2646,7 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, error_at (here, "attributes should be specified before the " "declarator in a function definition"); c_parser_skip_to_end_of_block_or_statement (parser); - return; + return result; } } if (c_parser_next_token_is (parser, CPP_EQ)) @@ -2766,6 +2786,7 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, maybe_warn_string_init (init_loc, TREE_TYPE (d), init); finish_decl (d, init_loc, init.value, init.original_type, asm_name); + result = d; } } else @@ -2776,7 +2797,7 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, "%qs requires an initialized data declaration", any_auto_type_p ? auto_type_keyword : "constexpr"); c_parser_skip_to_end_of_block_or_statement (parser); - return; + return result; } location_t lastloc = UNKNOWN_LOCATION; @@ -2869,13 +2890,14 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, } if (c_parser_next_token_is (parser, CPP_COMMA)) { + more_than_one_decl = true; if (any_auto_type_p || specs->constexpr_p) { error_at (here, "%qs may only be used with a single declarator", any_auto_type_p ? auto_type_keyword : "constexpr"); c_parser_skip_to_end_of_block_or_statement (parser); - return; + return result; } c_parser_consume_token (parser); if (c_parser_next_token_is_keyword (parser, RID_ATTRIBUTE)) @@ -2887,8 +2909,9 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, } else if (c_parser_next_token_is (parser, CPP_SEMICOLON)) { - c_parser_consume_token (parser); - return; + if (!simple_ok) + c_parser_consume_token (parser); + return result; } else if (c_parser_next_token_is_keyword (parser, RID_IN)) { @@ -2897,13 +2920,20 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, Objective-C foreach statement. Do not consume the token, so that the caller can use it to determine that this indeed is a foreach context. */ - return; + return result; } else { - c_parser_error (parser, "expected %<,%> or %<;%>"); - c_parser_skip_to_end_of_block_or_statement (parser); - return; + if (!simple_ok) + { + c_parser_error (parser, "expected %<,%> or %<;%>"); + c_parser_skip_to_end_of_block_or_statement (parser); + } + /* It's not valid to use if (int i = 2, j = 3). */ + else if (more_than_one_decl) + error_at (here, "declaration in condition can only declare " + "a single object"); + return result; } } else if (any_auto_type_p || specs->constexpr_p) @@ -2912,14 +2942,19 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, "%qs requires an initialized data declaration", any_auto_type_p ? auto_type_keyword : "constexpr"); c_parser_skip_to_end_of_block_or_statement (parser); - return; + return result; } else if (!fndef_ok) { - c_parser_error (parser, "expected %<=%>, %<,%>, %<;%>, " - "%<asm%> or %<__attribute__%>"); - c_parser_skip_to_end_of_block_or_statement (parser); - return; + if (simple_ok && c_parser_next_token_is (parser, CPP_CLOSE_PAREN)) + /* Let c_parser_selection_header emit the error. */; + else + { + c_parser_error (parser, "expected %<=%>, %<,%>, %<;%>, " + "%<asm%> or %<__attribute__%>"); + c_parser_skip_to_end_of_block_or_statement (parser); + } + return result; } /* Function definition (nested or otherwise). */ if (nested) @@ -2988,7 +3023,7 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, while (c_parser_next_token_is_not (parser, CPP_EOF) && c_parser_next_token_is_not (parser, CPP_OPEN_BRACE)) c_parser_declaration_or_fndef (parser, false, false, false, - true, false); + true, false, false); debug_nonbind_markers_p = save_debug_nonbind_markers_p; store_parm_decls (); if (omp_declare_simd_clauses) @@ -3018,7 +3053,7 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, pop_scope (); finish_function (endloc); - return; + return result; } /* If the definition was marked with __GIMPLE then parse the function body as GIMPLE. */ @@ -3059,6 +3094,8 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, break; } + + return result; } /* Parse an asm-definition (asm() outside a function body). This is a @@ -7378,7 +7415,7 @@ c_parser_compound_statement_nostart (c_parser *parser) 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, + true, true, true, false, NULL, NULL, have_std_attrs, std_attrs, NULL, &fallthru_attr_p); @@ -7421,7 +7458,7 @@ c_parser_compound_statement_nostart (c_parser *parser) } mark_valid_location_for_stdc_pragma (false); c_parser_declaration_or_fndef (parser, true, true, true, true, - true); + true, false); if (in_omp_loop_block) omp_for_parse_state->want_nested_loop = want_nested_loop; /* Following the old parser, __extension__ does not @@ -8075,11 +8112,12 @@ c_parser_condition (c_parser *parser) return cond; } -/* Parse a parenthesized condition from an if, do or while statement. +/* Parse a parenthesized condition from a do or while statement. condition: ( expression ) */ + static tree c_parser_paren_condition (c_parser *parser) { @@ -8092,6 +8130,103 @@ c_parser_paren_condition (c_parser *parser) return cond; } +/* Parse a selection-header: + + selection-header: + expression + declaration expression + simple-declaration + + simple-declaration: + attribute-specifier-sequence[opt] declaration-specifiers declarator + = initializer + + SWITCH_P is true if we are called from c_parser_switch_statement; in + that case, don't perform the truthvalue conversion. */ + +static c_expr +c_parser_selection_header (c_parser *parser, bool switch_p) +{ + location_t loc = c_parser_peek_token (parser)->location; + c_expr expr; + bool parse_expr = true; + tree std_attrs; + bool have_std_attrs = c_parser_nth_token_starts_std_attributes (parser, 1); + if (have_std_attrs) + std_attrs = c_parser_std_attribute_specifier_sequence (parser); + else + std_attrs = NULL_TREE; + if (c_parser_next_tokens_start_declaration (parser)) + { + pedwarn_c23 (loc, OPT_Wpedantic, + "ISO C does not support if declarations before C2Y"); + expr.value + = c_parser_declaration_or_fndef (parser, + /*fndef_ok=*/false, + /*static_assert_ok=*/false, + /*empty_ok=*/false, + /*nested=*/true, + /*start_attr_ok=*/true, + /*simple_ok=*/true, + /*objc_foreach_object_decl=*/nullptr, + /*omp_declare_simd_clauses=*/nullptr, + have_std_attrs, + std_attrs); + if (c_parser_next_token_is (parser, CPP_SEMICOLON)) + c_parser_consume_token (parser); + else + { + /* A simple-declaration is a declaration that can appear in + place of the controlling expression of a selection statement. + In that case, there shall be an initializer. */ + if (!expr.value) + { + error_at (loc, "declaration in the controlling expression must " + "have an initializer"); + expr.original_type = error_mark_node; + expr.set_error (); + return expr; + } + parse_expr = false; + expr.original_type = TREE_TYPE (expr.value); + } + } + else if (have_std_attrs) + { + c_parser_error (parser, "expected declaration"); + expr.original_type = error_mark_node; + expr.set_error (); + return expr; + } + + if (parse_expr) + expr = c_parser_expression_conv (parser); + if (!switch_p) + { + expr.value = c_objc_common_truthvalue_conversion (loc, expr.value); + expr.value = c_fully_fold (expr.value, false, NULL); + if (warn_sequence_point) + verify_sequence_points (expr.value); + } + return expr; +} + +/* Parse a selection-header enclosed in parentheses: + + ( selection-header ) +*/ + +static tree +c_parser_paren_selection_header (c_parser *parser) +{ + matching_parens parens; + if (!parens.require_open (parser)) + return error_mark_node; + tree cond = c_parser_selection_header (parser, /*switch_p=*/false).value; + parens.skip_until_found_close (parser); + return cond; +} + /* Parse a statement which is a block in C99. IF_P is used to track whether there's a (possibly labeled) if statement @@ -8244,8 +8379,8 @@ c_parser_maybe_reclassify_token (c_parser *parser) /* Parse an if statement (C90 6.6.4, C99 6.8.4, C11 6.8.4). if-statement: - if ( expression ) statement - if ( expression ) statement else statement + if ( selection-header ) statement + if ( selection-header ) statement else statement CHAIN is a vector of if-else-if conditions. IF_P is used to track whether there's a (possibly labeled) if statement @@ -8268,7 +8403,7 @@ c_parser_if_statement (c_parser *parser, bool *if_p, vec<tree> *chain) c_parser_consume_token (parser); block = c_begin_compound_stmt (flag_isoc99); loc = c_parser_peek_token (parser)->location; - cond = c_parser_paren_condition (parser); + cond = c_parser_paren_selection_header (parser); in_if_block = parser->in_if_block; parser->in_if_block = true; first_body = c_parser_if_body (parser, &nested_if, if_tinfo); @@ -8355,7 +8490,9 @@ c_parser_switch_statement (c_parser *parser, bool *if_p, tree before_labels) if (c_parser_next_token_is (parser, CPP_OPEN_PAREN) && c_token_starts_typename (c_parser_peek_2nd_token (parser))) explicit_cast_p = true; - ce = c_parser_expression (parser); + ce = c_parser_selection_header (parser, /*switch_p=*/true); + /* The call above already performed convert_lvalue_to_rvalue, but with + read_p=false. */ ce = convert_lvalue_to_rvalue (switch_cond_loc, ce, true, true); expr = ce.value; /* ??? expr has no valid location? */ @@ -8667,7 +8804,7 @@ c_parser_for_statement (c_parser *parser, bool ivdep, unsigned short unroll, || c_parser_nth_token_starts_std_attributes (parser, 1)) { c_parser_declaration_or_fndef (parser, true, true, true, true, true, - &object_expression); + false, &object_expression); parser->objc_could_be_foreach_context = false; if (c_parser_next_token_is_keyword (parser, RID_IN)) @@ -8698,7 +8835,7 @@ c_parser_for_statement (c_parser *parser, bool ivdep, unsigned short unroll, ext = disable_extension_diagnostics (); c_parser_consume_token (parser); c_parser_declaration_or_fndef (parser, true, true, true, true, - true, &object_expression); + true, false, &object_expression); parser->objc_could_be_foreach_context = false; restore_extension_diagnostics (ext); @@ -14050,7 +14187,7 @@ c_parser_objc_methodprotolist (c_parser *parser) } else c_parser_declaration_or_fndef (parser, false, false, true, - false, true); + false, true, false); break; } } @@ -21170,12 +21307,12 @@ c_parser_oacc_routine (c_parser *parser, enum pragma_context context) while (c_parser_next_token_is (parser, CPP_KEYWORD) && c_parser_peek_token (parser)->keyword == RID_EXTENSION); c_parser_declaration_or_fndef (parser, true, true, true, false, true, - NULL, NULL, false, NULL, &data); + false, NULL, NULL, false, NULL, &data); restore_extension_diagnostics (ext); } else c_parser_declaration_or_fndef (parser, true, true, true, false, true, - NULL, NULL, false, NULL, &data); + false, NULL, NULL, false, NULL, &data); } } @@ -23136,7 +23273,8 @@ c_parser_omp_loop_nest (c_parser *parser, bool *if_p) /* This is a declaration, which must be added to the pre_body code. */ tree this_pre_body = push_stmt_list (); c_in_omp_for = true; - c_parser_declaration_or_fndef (parser, true, true, true, true, true); + c_parser_declaration_or_fndef (parser, true, true, true, true, true, + false); c_in_omp_for = false; this_pre_body = pop_stmt_list (this_pre_body); append_to_statement_list_force (this_pre_body, @@ -25378,12 +25516,12 @@ c_parser_omp_declare_simd (c_parser *parser, enum pragma_context context) while (c_parser_next_token_is (parser, CPP_KEYWORD) && c_parser_peek_token (parser)->keyword == RID_EXTENSION); c_parser_declaration_or_fndef (parser, true, true, true, false, true, - NULL, &clauses); + false, NULL, &clauses); restore_extension_diagnostics (ext); } else c_parser_declaration_or_fndef (parser, true, true, true, false, true, - NULL, &clauses); + false, NULL, &clauses); break; case pragma_struct: case pragma_param: @@ -25412,7 +25550,7 @@ c_parser_omp_declare_simd (c_parser *parser, enum pragma_context context) || c_parser_nth_token_starts_std_attributes (parser, 1)) { c_parser_declaration_or_fndef (parser, true, true, true, true, - true, NULL, &clauses, + true, false, NULL, &clauses, have_std_attrs, std_attrs); restore_extension_diagnostics (ext); break; @@ -25422,7 +25560,7 @@ c_parser_omp_declare_simd (c_parser *parser, enum pragma_context context) else if (c_parser_next_tokens_start_declaration (parser)) { c_parser_declaration_or_fndef (parser, true, true, true, true, true, - NULL, &clauses, have_std_attrs, + false, NULL, &clauses, have_std_attrs, std_attrs); break; } diff --git a/gcc/testsuite/gcc.dg/c23-if-decls-1.c b/gcc/testsuite/gcc.dg/c23-if-decls-1.c new file mode 100644 index 00000000000..ea968c67c6a --- /dev/null +++ b/gcc/testsuite/gcc.dg/c23-if-decls-1.c @@ -0,0 +1,15 @@ +/* N3356 - if declarations. */ +/* PR c/117019 */ +/* { dg-do compile } */ +/* { dg-options "-std=c23 -pedantic-errors" } */ + +void +g () +{ + if (int i = 42); /* { dg-error "ISO C does not support if declarations before C2Y" } */ + if (int i = 42; i > 10); /* { dg-error "ISO C does not support if declarations before C2Y" } */ + if (int i, j; i = 42); /* { dg-error "ISO C does not support if declarations before C2Y" } */ + switch (int i = 42); /* { dg-error "ISO C does not support if declarations before C2Y" } */ + switch (int i = 42; i); /* { dg-error "ISO C does not support if declarations before C2Y" } */ + switch (int i, j; i = 42); /* { dg-error "ISO C does not support if declarations before C2Y" } */ +} diff --git a/gcc/testsuite/gcc.dg/c23-if-decls-2.c b/gcc/testsuite/gcc.dg/c23-if-decls-2.c new file mode 100644 index 00000000000..d53db715377 --- /dev/null +++ b/gcc/testsuite/gcc.dg/c23-if-decls-2.c @@ -0,0 +1,6 @@ +/* N3356 - if declarations. */ +/* PR c/117019 */ +/* { dg-do compile } */ +/* { dg-options "-std=c23 -pedantic-errors -Wno-c23-c2y-compat" } */ + +#include "c23-if-decls-1.c" diff --git a/gcc/testsuite/gcc.dg/c2y-if-decls-1.c b/gcc/testsuite/gcc.dg/c2y-if-decls-1.c new file mode 100644 index 00000000000..417b8052775 --- /dev/null +++ b/gcc/testsuite/gcc.dg/c2y-if-decls-1.c @@ -0,0 +1,154 @@ +/* N3356 - if declarations. */ +/* PR c/117019 */ +/* { dg-do run } */ +/* { dg-options "-std=c2y -Wc23-c2y-compat" } */ +/* Test C2Y if declarations. Valid usages. */ + +int get () { return 42; } +int foo (int i) { return i; } + +enum E { X = 1, Y }; + +void +simple () +{ + if (int i = get ()) /* { dg-warning "if declarations before C2Y" } */ + foo (i); + else + __builtin_abort (); + + if (int i = 0) /* { dg-warning "if declarations before C2Y" } */ + __builtin_abort (); + else + foo (i); + + if (auto i = get ()) /* { dg-warning "if declarations before C2Y" } */ + foo (i); + else + __builtin_abort (); + + if (__typeof__(get ()) i = get ()) /* { dg-warning "if declarations before C2Y" } */ + foo (i); + else + __builtin_abort (); + + if (auto i = 0) /* { dg-warning "if declarations before C2Y" } */ + __builtin_abort (); + else + foo (i); + + if (int (*f)(int) = foo) /* { dg-warning "if declarations before C2Y" } */ + f (1); + else + __builtin_abort (); + + if ([[maybe_unused]] int i = get ()) /* { dg-warning "if declarations before C2Y" } */ + foo (i); + else + __builtin_abort (); + + if (__attribute__((unused)) int i = get ()) /* { dg-warning "if declarations before C2Y" } */ + foo (i); + else + __builtin_abort (); + + if (enum E e = X) /* { dg-warning "if declarations before C2Y" } */ + foo (e); + else + __builtin_abort (); + + if (constexpr int i = 42) /* { dg-warning "if declarations before C2Y" } */ + foo (i); + else + __builtin_abort (); + + if (int i = 1) /* { dg-warning "if declarations before C2Y" } */ + if (int j = 2) /* { dg-warning "if declarations before C2Y" } */ + if (int k = 3) /* { dg-warning "if declarations before C2Y" } */ + foo (i + j + k); + + double i; +} + +void +expr () +{ + if (int i = get (); i == 42) /* { dg-warning "if declarations before C2Y" } */ + foo (i); + else + __builtin_abort (); + + if (int i = get (); i != 42) /* { dg-warning "if declarations before C2Y" } */ + __builtin_abort (); + else + foo (i); + + if (auto i = get (); i == 42) /* { dg-warning "if declarations before C2Y" } */ + foo (i); + else + __builtin_abort (); + + if (__typeof__(get ()) i = get (); i == 42) /* { dg-warning "if declarations before C2Y" } */ + foo (i); + else + __builtin_abort (); + + if (auto i = get (); i != 42) /* { dg-warning "if declarations before C2Y" } */ + __builtin_abort (); + else + foo (i); + + if (int (*f)(int) = foo; f (42)) /* { dg-warning "if declarations before C2Y" } */ + f (1); + else + __builtin_abort (); + + if ([[maybe_unused]] int i = get (); i == 42) /* { dg-warning "if declarations before C2Y" } */ + foo (i); + else + __builtin_abort (); + + if (__attribute__((unused)) int i = get (); i == 42) /* { dg-warning "if declarations before C2Y" } */ + foo (i); + else + __builtin_abort (); + + if (enum E e = X; e == X) /* { dg-warning "if declarations before C2Y" } */ + foo (e); + else + __builtin_abort (); + + if (constexpr int i = 42; i == 42) /* { dg-warning "if declarations before C2Y" } */ + foo (i); + else + __builtin_abort (); + + if (int i = 1; i) /* { dg-warning "if declarations before C2Y" } */ + if (int j = 2; j) /* { dg-warning "if declarations before C2Y" } */ + if (int k = 3; k) /* { dg-warning "if declarations before C2Y" } */ + foo (i + j + k); + + if (int i = 2, j = get (); i + j > 0) /* { dg-warning "if declarations before C2Y" } */ + foo (i + j); + else + __builtin_abort (); + + if (int i; i = 1) /* { dg-warning "if declarations before C2Y" } */ + foo (i); + else + __builtin_abort (); + + if (int arr[] = { 1, 2, 3}; arr[0]) /* { dg-warning "if declarations before C2Y" } */ + foo (arr[0]); + else + __builtin_abort (); + + double i; +} + +int +main () +{ + simple (); + expr (); +} diff --git a/gcc/testsuite/gcc.dg/c2y-if-decls-2.c b/gcc/testsuite/gcc.dg/c2y-if-decls-2.c new file mode 100644 index 00000000000..e06344e4fe0 --- /dev/null +++ b/gcc/testsuite/gcc.dg/c2y-if-decls-2.c @@ -0,0 +1,38 @@ +/* N3356 - if declarations. */ +/* PR c/117019 */ +/* { dg-do compile } */ +/* { dg-options "-std=c2y" } */ +/* Test C2Y if declarations. Invalid usages. */ + +void +g (int g) +{ + if (;); /* { dg-error "expected" } */ + if (int); /* { dg-error "expected|initializer" } */ + if (auto); /* { dg-error "expected|initializer" } */ + if (int;); /* { dg-error "initializer" } */ + /* { dg-warning "empty" "" { target *-*-* } .-1 } */ + if (auto;); /* { dg-error "empty|initializer" } */ + if (int i); /* { dg-error "initializer" } */ + if (int i;); /* { dg-error "expected" } */ + if (int i = 0;); /* { dg-error "expected" } */ + + if (extern int i = 0); /* { dg-error "in condition|both .extern. and initializer" } */ + if (register int i = 0); /* { dg-error "in condition" } */ + if (static int i = 0); /* { dg-error "in condition" } */ + if (thread_local int i = 0); /* { dg-error "in condition|function-scope" } */ + if (typedef int i); /* { dg-error "in condition|initializer" } */ + if (typedef int i = 0); /* { dg-error "in condition|initialized" } */ + + if (int i = 2, j = 3); /* { dg-error "only declare a single object" } */ + + if (void (*fp)(int)); /* { dg-error "initializer" } */ + if ([[maybe_unused]] g); /* { dg-error "expected" } */ + if ([[maybe_unused]] 42); /* { dg-error "expected" } */ + if ([[maybe_unused]] int); /* { dg-error "expected|initializer" } */ + if (__attribute__((unused)) g); /* { dg-error "initializer" } */ + if (__attribute__((unused)) 42); /* { dg-error "expected|initializer" } */ + if (__attribute__((unused)) int); /* { dg-error "expected|initializer" } */ + + if (int arr[] = { 1 }); /* { dg-error "scalar is required" } */ +} diff --git a/gcc/testsuite/gcc.dg/c2y-if-decls-3.c b/gcc/testsuite/gcc.dg/c2y-if-decls-3.c new file mode 100644 index 00000000000..685f826e695 --- /dev/null +++ b/gcc/testsuite/gcc.dg/c2y-if-decls-3.c @@ -0,0 +1,40 @@ +/* N3356 - if declarations. */ +/* PR c/117019 */ +/* { dg-do compile } */ +/* { dg-options "-std=c2y" } */ +/* Test C2Y if declarations. Invalid usages. */ + +void +g (int g) +{ + switch (;); /* { dg-error "expected" } */ + switch (int); /* { dg-error "expected identifier" } */ + /* { dg-error "declaration" "" { target *-*-* } .-1 } */ + switch (auto); /* { dg-error "expected identifier" } */ + /* { dg-error "declaration" "" { target *-*-* } .-1 } */ + switch (int;); /* { dg-error "declaration" } */ + /* { dg-warning "empty" "" { target *-*-* } .-1 } */ + switch (auto;); /* { dg-error "empty|initializer" } */ + switch (int i); /* { dg-error "initializer" } */ + switch (int i;); /* { dg-error "expected" } */ + switch (int i = 0;); /* { dg-error "expected" } */ + + switch (extern int i = 0); /* { dg-error "in condition|both .extern. and initializer" } */ + switch (register int i = 0); /* { dg-error "in condition" } */ + switch (static int i = 0); /* { dg-error "in condition" } */ + switch (thread_local int i = 0); /* { dg-error "in condition|function-scope" } */ + switch (typedef int i); /* { dg-error "in condition|initializer" } */ + switch (typedef int i = 0); /* { dg-error "in condition|initialized" } */ + + switch (int i = 2, j = 3); /* { dg-error "only declare a single object" } */ + + switch (void (*fp)(int)); /* { dg-error "initializer" } */ + switch ([[maybe_unused]] g); /* { dg-error "expected" } */ + switch ([[maybe_unused]] 42); /* { dg-error "expected" } */ + switch ([[maybe_unused]] int); /* { dg-error "expected|initializer" } */ + switch (__attribute__((unused)) g); /* { dg-error "initializer" } */ + switch (__attribute__((unused)) 42); /* { dg-error "expected|initializer" } */ + switch (__attribute__((unused)) int); /* { dg-error "expected|initializer" } */ + + switch (int arr[] = { 1 }); /* { dg-error "switch quantity not an integer" } */ +} diff --git a/gcc/testsuite/gcc.dg/c2y-if-decls-4.c b/gcc/testsuite/gcc.dg/c2y-if-decls-4.c new file mode 100644 index 00000000000..f518adf7dd1 --- /dev/null +++ b/gcc/testsuite/gcc.dg/c2y-if-decls-4.c @@ -0,0 +1,191 @@ +/* N3356 - if declarations. */ +/* PR c/117019 */ +/* { dg-do run } */ +/* { dg-options "-std=c2y -Wc23-c2y-compat" } */ +/* Test C2Y if declarations. Valid usages. */ + +int get () { return 42; } +int foo (int i) { return i; } + +enum E { X = 1, Y }; + +void +simple () +{ + switch (int i = get ()) /* { dg-warning "if declarations before C2Y" } */ + { + case 42: + foo (i); + break; + default: + __builtin_abort (); + } + + switch (int i = 0) /* { dg-warning "if declarations before C2Y" } */ + { + case 0: + foo (i); + break; + default: + __builtin_abort (); + } + + switch (auto i = get ()) /* { dg-warning "if declarations before C2Y" } */ + { + case 42: + foo (i); + break; + default: + __builtin_abort (); + } + + switch (__typeof__(get ()) i = get ()) /* { dg-warning "if declarations before C2Y" } */ + { + case 42: + foo (i); + break; + default: + __builtin_abort (); + } + + switch (auto i = 0) /* { dg-warning "if declarations before C2Y" } */ + { + case 0: + foo (i); + break; + default: + __builtin_abort (); + } + + switch ([[maybe_unused]] int i = get ()) /* { dg-warning "if declarations before C2Y" } */ + { + case 42: + foo (i); + break; + default: + __builtin_abort (); + } + + switch (__attribute__((unused)) int i = get ()) /* { dg-warning "if declarations before C2Y" } */ + { + case 42: + foo (i); + break; + default: + __builtin_abort (); + } + + switch (enum E e = X) /* { dg-warning "if declarations before C2Y" } */ + { + case X: + foo (X); + break; + default: + __builtin_abort (); + } + + switch (constexpr int i = 42) /* { dg-warning "if declarations before C2Y" } */ + { + case 42: + foo (i); + break; + default: + __builtin_abort (); + } + + double i; +} + +void +expr () +{ + switch (int i = get (); i) /* { dg-warning "if declarations before C2Y" } */ + { + case 42: + foo (i); + break; + default: + __builtin_abort (); + } + + switch (auto i = get (); i) /* { dg-warning "if declarations before C2Y" } */ + { + case 42: + foo (i); + break; + default: + __builtin_abort (); + } + + switch (__typeof__(get ()) i = get (); i) /* { dg-warning "if declarations before C2Y" } */ + { + case 42: + foo (i); + break; + default: + __builtin_abort (); + } + + switch (int (*f)(int) = foo; f (42)) /* { dg-warning "if declarations before C2Y" } */ + { + case 42: + foo (42); + break; + default: + __builtin_abort (); + } + + switch ([[maybe_unused]] int i = get (); i) /* { dg-warning "if declarations before C2Y" } */ + { + case 42: + foo (i); + break; + default: + __builtin_abort (); + } + + switch (__attribute__((unused)) int i = get (); i) /* { dg-warning "if declarations before C2Y" } */ + { + case 42: + foo (i); + break; + default: + __builtin_abort (); + } + + switch (enum E e = X; e) /* { dg-warning "if declarations before C2Y" } */ + { + case X: + foo (X); + break; + default: + __builtin_abort (); + } + + switch (constexpr int i = 42; i) /* { dg-warning "if declarations before C2Y" } */ + { + case 42: + foo (i); + break; + default: + __builtin_abort (); + } + + switch (int arr[] = { 1, 2, 3}; arr[0]) /* { dg-warning "if declarations before C2Y" } */ + { + case 1: + foo (arr[0]); + break; + default: + __builtin_abort (); + } + + double i; +} + +int +main () +{ + simple (); + expr (); +} diff --git a/gcc/testsuite/gcc.dg/c2y-if-decls-5.c b/gcc/testsuite/gcc.dg/c2y-if-decls-5.c new file mode 100644 index 00000000000..dfab357c312 --- /dev/null +++ b/gcc/testsuite/gcc.dg/c2y-if-decls-5.c @@ -0,0 +1,35 @@ +/* N3356 - if declarations. */ +/* PR c/117019 */ +/* { dg-do run } */ +/* { dg-options "-std=c2y" } */ + +int g; +int get () { ++g; return 42; } + +struct S { int i; }; + +int +main () +{ + if (auto x = get (); get (), x); + if (g != 2) + __builtin_abort (); + + switch (auto x = get (); get (), x); + if (g != 4) + __builtin_abort (); + + if (struct S s = { 42 }; s.i != 42) + __builtin_abort (); + + if (int i = 42) + { + i = 0; + if (int j = 42) + j = 0; + else + j = 42; + } + else + i = 42; +} diff --git a/gcc/testsuite/gcc.dg/c2y-if-decls-6.c b/gcc/testsuite/gcc.dg/c2y-if-decls-6.c new file mode 100644 index 00000000000..57dd9e01bde --- /dev/null +++ b/gcc/testsuite/gcc.dg/c2y-if-decls-6.c @@ -0,0 +1,27 @@ +/* N3356 - if declarations. */ +/* PR c/117019 */ +/* { dg-do run } */ +/* { dg-options "-std=c2y -Wall -Wextra" } */ +/* Test VLAs. */ + +void foo (int) { } + +int +main () +{ + int i = 3; + + if (int arr[i] = { }; !arr[0]) + foo (arr[0]); + else + __builtin_abort (); + + switch (int arr[i] = { }; arr[0]) + { + case 0: + foo (arr[0]); + break; + default: + __builtin_abort (); + } +} diff --git a/gcc/testsuite/gcc.dg/c2y-if-decls-7.c b/gcc/testsuite/gcc.dg/c2y-if-decls-7.c new file mode 100644 index 00000000000..df451e3e779 --- /dev/null +++ b/gcc/testsuite/gcc.dg/c2y-if-decls-7.c @@ -0,0 +1,21 @@ +/* N3356 - if declarations. */ +/* PR c/117019 */ +/* { dg-do compile } */ +/* { dg-options "-std=c2y -Wall -Wextra" } */ +/* Test VLAs. Invalid code. */ + +void foo (int) { } + +void +g () +{ + int i = 3; + + switch (i) /* { dg-message "switch starts here" } */ + { /* { dg-warning "statement will never be executed" } */ + int arr[i] = { }; + default: /* { dg-error "switch jumps into scope" } */ + i = arr[0]; + break; + } +} diff --git a/gcc/testsuite/gcc.dg/gnu2y-if-decls-1.c b/gcc/testsuite/gcc.dg/gnu2y-if-decls-1.c new file mode 100644 index 00000000000..65f526e9dc2 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gnu2y-if-decls-1.c @@ -0,0 +1,15 @@ +/* N3356 - if declarations. */ +/* PR c/117019 */ +/* { dg-do compile } */ +/* { dg-options "-std=gnu2y" } */ + +void +g () +{ + if (int i = 42); + if (int i = 42; i > 10); + if (int i, j; i = 42); + switch (int i = 42); + switch (int i = 42; i); + switch (int i, j; i = 42); +} diff --git a/gcc/testsuite/gcc.dg/gnu99-if-decls-1.c b/gcc/testsuite/gcc.dg/gnu99-if-decls-1.c new file mode 100644 index 00000000000..6d7a3bc196b --- /dev/null +++ b/gcc/testsuite/gcc.dg/gnu99-if-decls-1.c @@ -0,0 +1,15 @@ +/* N3356 - if declarations. */ +/* PR c/117019 */ +/* { dg-do compile } */ +/* { dg-options "-std=gnu99 -Wpedantic" } */ + +void +g () +{ + if (int i = 42); /* { dg-warning "ISO C does not support if declarations before C2Y" } */ + if (int i = 42; i > 10); /* { dg-warning "ISO C does not support if declarations before C2Y" } */ + if (int i, j; i = 42); /* { dg-warning "ISO C does not support if declarations before C2Y" } */ + switch (int i = 42); /* { dg-warning "ISO C does not support if declarations before C2Y" } */ + switch (int i = 42; i); /* { dg-warning "ISO C does not support if declarations before C2Y" } */ + switch (int i, j; i = 42); /* { dg-warning "ISO C does not support if declarations before C2Y" } */ +} diff --git a/gcc/testsuite/gcc.dg/gnu99-if-decls-2.c b/gcc/testsuite/gcc.dg/gnu99-if-decls-2.c new file mode 100644 index 00000000000..82f779c71c2 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gnu99-if-decls-2.c @@ -0,0 +1,15 @@ +/* N3356 - if declarations. */ +/* PR c/117019 */ +/* { dg-do compile } */ +/* { dg-options "-std=gnu99" } */ + +void +g () +{ + if (int i = 42); + if (int i = 42; i > 10); + if (int i, j; i = 42); + switch (int i = 42); + switch (int i = 42; i); + switch (int i, j; i = 42); +}