diff options
author | Jakub Jelinek <jakub@redhat.com> | 2024-10-15 20:41:18 +0200 |
---|---|---|
committer | Jakub Jelinek <jakub@gcc.gnu.org> | 2024-10-15 20:46:56 +0200 |
commit | 50f27896adb272b40ab03a56fd192e74789bef97 (patch) | |
tree | 6d615eb2d317bf8e36d494c96f2e3935c9364718 /gcc/c | |
parent | b110e092d260906432931796c1d96cba305c60e4 (diff) | |
download | gcc-50f27896adb272b40ab03a56fd192e74789bef97.zip gcc-50f27896adb272b40ab03a56fd192e74789bef97.tar.gz gcc-50f27896adb272b40ab03a56fd192e74789bef97.tar.bz2 |
c: Implement C2Y N3355 - Named Loops [PR117022]
The following patch implements the C2Y N3355 - Named Loops paper.
I've tried to implement it lazily, rather than proactively e.g. push
labels to a vector just in case the following statement is iteration
statement, switch statement or one of the loop pragmas followed by
iteration statement the patch just notes the last statement in
cur_stmt_list if any before c_parser_label/c_parser_all_labels and
passes it down to the iteration/switch statement parsing routines,
which then search backward for LABEL_EXPRs before they reach the given
stop statement.
The patch then adds one extra argument to
{FOR,WHILE,DO,BREAK,CONTINUE,SWITCH}_STMT, which is set to a canonical
name LABEL_DECL (the last named label before the construct).
If one just refers to the innermost construct with a fancy name,
it is in the end parsed the same as break/continue without an identifier
(i.e. NULL_TREE argument), and if a loop or switch has name(s) but
break/continue to that isn't used, the name is set to NULL_TREE.
At c-gimplify.cc time the name is then pushed into a hash map mapping
it to a pair of labels.
I've implemented it also for ObjC foreach loops (which have break/continue
handled during parsing, not during c-gimplify.cc).
As for OpenMP/OpenACC, the patch right now pretends no OpenMP loop
has a name, until something different is decided in the standard.
As shown in the testcases, most break identifier/continue identifier
cases aren't really useful in OpenMP code, a break identifier or
continue identifier jumping out of an OpenMP region is certainly invalid
(such regions have to be single entry single exit, so escaping it
through goto/break lab/continue lab violates that), similarly break
is disallowed in the innermost OpenMP nested loop, just continue
is allowed, so the only thing that would make sense for OpenMP (second
gomp testcase) would be allowing to give name to the innermost
loop in OpenMP canonical loop nest (except that labels aren't allowed
in the syntax right now in between the loops) and only continue to
that label. For collapse(1) loops that would be a label before
the #pragma or [[omp::directive (parallel for)]] etc. And of course,
what already works fine in the patch is break/continue to non-OpenMP loops
nested in OpenMP loops.
2024-10-12 Jakub Jelinek <jakub@redhat.com>
PR c/117022
gcc/c-family/
* c-common.def (FOR_STMT, WHILE_STMT, DO_STMT, BREAK_STMT,
CONTINUE_STMT, SWITCH_STMT): Add an extra operand, *_NAME
and document it.
* c-common.h (bc_hash_map_t): New typedef.
(struct bc_state): Add bc_hash_map member.
(WHILE_NAME, DO_NAME, FOR_NAME, BREAK_NAME, CONTINUE_NAME,
SWITCH_STMT_NAME): Define.
* c-pretty-print.cc (c_pretty_printer::statement): Print
BREAK_STMT or CONTINUE_STMT operand if any.
* c-gimplify.cc (bc_hash_map): New static variable.
(note_named_bc, release_named_bc): New functions.
(save_bc_state): Save and clear bc_hash_map.
(restore_bc_state): Assert NULL and restore bc_hash_map.
(genericize_c_loop): Add NAME argument, call note_named_bc
and release_named_bc if non-NULL around the body walk.
(genericize_for_stmt, genericize_while_stmt, genericize_do_stmt):
Adjust callers of it.
(genericize_switch_stmt): Rename break_block variable to blab.
Call note_named_bc and release_named_bc if SWITCH_STMT_NAME is
non-NULL around the body walk.
(genericize_continue_stmt): Handle non-NULL CONTINUE_NAME.
(genericize_break_stmt): Handle non-NULL BREAK_NAME.
(c_genericize): Delete and clear bc_hash_map.
gcc/c/
* c-tree.h: Implement C2Y N3355 - Named loops.
(C_DECL_LOOP_NAME, C_DECL_SWITCH_NAME, C_DECL_LOOP_SWITCH_NAME_VALID,
C_DECL_LOOP_SWITCH_NAME_USED, IN_NAMED_STMT): Define.
(c_get_loop_names, c_release_loop_names, c_finish_bc_name): Declare.
(c_start_switch): Add NAME argument.
(c_finish_bc_stmt): Likewise.
* c-lang.h (struct language_function): Add loop_names and
loop_names_hash members.
* c-parser.cc (c_parser_external_declaration,
c_parser_declaration_or_fndef, c_parser_struct_or_union_specifier,
c_parser_parameter_declaration): Adjust c_parser_pragma caller.
(get_before_labels): New function.
(c_parser_compound_statement_nostart): Call get_before_labels when
needed, adjust c_parser_pragma and c_parser_statement_after_labels
callers.
(c_parser_statement): Call get_before_labels first and pass it to
c_parser_statement_after_labels.
(c_parser_bc_name): New function.
(c_parser_statement_after_labels): Add BEFORE_LABELS argument. Pass
it down to c_parser_switch_statement, c_parser_while_statement,
c_parser_do_statement, c_parser_for_statement and c_parser_pragma.
Call c_parser_bc_name for RID_BREAK and RID_CONTINUE and pass it as
another argument to c_finish_bc_stmt.
(c_parser_if_body, c_parser_else_body): Call get_before_labels
early and pass it to c_parser_statement_after_labels.
(c_parser_switch_statement): Add BEFORE_LABELS argument. Call
c_get_loop_names, if named, pass switch_name to c_start_switch,
mark it valid and set IN_NAMED_STMT bit in in_statement before
parsing body, otherwise clear IN_NAMED_STMT bit before that parsing.
Run c_release_loop_names at the end.
(c_parser_while_statement, c_parser_do_statement,
c_parser_for_statement): Add BEFORE_LABELS argument. Call
c_get_loop_names, if named, mark it valid and set IN_NAMED_STMT bit
in in_statement before parsing body, otherwise clear IN_NAMED_STMT
before that parsing, arrange for the loop name if used to be
another *_STMT argument.
(c_parser_objc_class_instance_variables,
c_parser_objc_methodprotolist): Adjust c_parser_pragma callers.
(c_parser_pragma): Add BEFORE_LABELS argument. Pass it down to
c_parser_for_statement, c_parser_while_statement or
c_parser_do_statement.
(c_parser_omp_loop_nest, c_maybe_parse_omp_decl): Adjust
c_parser_pragma callers.
* c-decl.cc (loop_names, loop_names_hash): New static variables.
(add_stmt): Set STATEMENT_LIST_HAS_LABEL after push_stmt_list rather
than before it.
(c_push_function_context): Save and clear loop_names and
loop_names_hash.
(c_pop_function_context): Release or delete, restore and clear
loop_names and loop_names_hash.
(c_get_loop_names, c_release_loop_names, c_finish_bc_name): New
functions.
* c-typeck.cc (c_start_switch): Add SWITCH_NAME argument, pass it down
to build_stmt.
(c_finish_bc_stmt): Add NAME argument. Mark of IN_NAMED_STMT bit
of in_statement in swtiches. Use label for IN_OBJC_FOREACH only if
name is NULL. If name is non-NULL and C_DECL_LOOP_NAME and
C_DECL_SWITCH_NAME are both set, assume outer ObjC foreach and
dig labels from DECL_CHAIN of name. Pass NAME to build_stmt
otherwise.
gcc/cp/
* semantics.cc (begin_while_stmt, begin_do_stmt, begin_for_stmt,
finish_break_stmt, finish_continue_stmt, begin_switch_stmt): Pass
another NULL_TREE to build_stmt calls.
gcc/testsuite/
* gcc.dg/c23-named-loops-1.c: New test.
* gcc.dg/c23-named-loops-5.c: New test.
* gcc.dg/c2y-named-loops-1.c: New test.
* gcc.dg/c2y-named-loops-2.c: New test.
* gcc.dg/c2y-named-loops-4.c: New test.
* gcc.dg/c2y-named-loops-5.c: New test.
* gcc.dg/c2y-named-loops-6.c: New test.
* gcc.dg/c2y-named-loops-7.c: New test.
* gcc.dg/gnu99-named-loops-1.c: New test.
* gcc.dg/gnu99-named-loops-2.c: New test.
* gcc.dg/gnu99-named-loops-3.c: New test.
* gcc.dg/gnu99-named-loops-4.c: New test.
* gcc.dg/gnu2y-named-loops-3.c: New test.
* gcc.dg/gomp/named-loops-1.c: New test.
* gcc.dg/gomp/named-loops-2.c: New test.
* objc.dg/named-loops-1.m: New test.
Diffstat (limited to 'gcc/c')
-rw-r--r-- | gcc/c/c-decl.cc | 233 | ||||
-rw-r--r-- | gcc/c/c-lang.h | 2 | ||||
-rw-r--r-- | gcc/c/c-parser.cc | 231 | ||||
-rw-r--r-- | gcc/c/c-tree.h | 30 | ||||
-rw-r--r-- | gcc/c/c-typeck.cc | 24 |
5 files changed, 461 insertions, 59 deletions
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc index 224c015..eec69d4 100644 --- a/gcc/c/c-decl.cc +++ b/gcc/c/c-decl.cc @@ -162,6 +162,14 @@ vec<c_omp_declare_target_attr, va_gc> *current_omp_declare_target_attribute; #pragma omp begin assumes ... #pragma omp end assumes regions we are in. */ vec<c_omp_begin_assumes_data, va_gc> *current_omp_begin_assumes; + +/* Vector of loop names with C_DECL_LOOP_NAME or C_DECL_SWITCH_NAME marked + LABEL_DECL as the last and canonical for each loop or switch. */ +static vec<tree> loop_names; + +/* Hash table mapping LABEL_DECLs to the canonical LABEL_DECLs if LOOP_NAMES + vector becomes too long. */ +static decl_tree_map *loop_names_hash; /* Each c_binding structure describes one binding of an identifier to a decl. All the decls in a scope - irrespective of namespace - are @@ -694,13 +702,14 @@ add_stmt (tree t) SET_EXPR_LOCATION (t, input_location); } - if (code == LABEL_EXPR || code == CASE_LABEL_EXPR) - STATEMENT_LIST_HAS_LABEL (cur_stmt_list) = 1; - /* Add T to the statement-tree. Non-side-effect statements need to be recorded during statement expressions. */ if (!building_stmt_list_p ()) push_stmt_list (); + + if (code == LABEL_EXPR || code == CASE_LABEL_EXPR) + STATEMENT_LIST_HAS_LABEL (cur_stmt_list) = 1; + append_to_statement_list_force (t, &cur_stmt_list); return t; @@ -11683,6 +11692,10 @@ c_push_function_context (void) c_stmt_tree.x_cur_stmt_list = vec_safe_copy (c_stmt_tree.x_cur_stmt_list); p->x_in_statement = in_statement; p->x_switch_stack = c_switch_stack; + p->loop_names = loop_names; + loop_names = vNULL; + p->loop_names_hash = loop_names_hash; + loop_names_hash = NULL; p->arg_info = current_function_arg_info; p->returns_value = current_function_returns_value; p->returns_null = current_function_returns_null; @@ -11722,6 +11735,12 @@ c_pop_function_context (void) p->base.x_stmt_tree.x_cur_stmt_list = NULL; in_statement = p->x_in_statement; c_switch_stack = p->x_switch_stack; + loop_names.release (); + loop_names = p->loop_names; + p->loop_names = vNULL; + delete loop_names_hash; + loop_names_hash = p->loop_names_hash; + p->loop_names_hash = NULL; current_function_arg_info = p->arg_info; current_function_returns_value = p->returns_value; current_function_returns_null = p->returns_null; @@ -13804,4 +13823,212 @@ c_check_in_current_scope (tree decl) return b != NULL && B_IN_CURRENT_SCOPE (b); } +/* Search for loop or switch names. BEFORE_LABELS is last statement before + possible labels and SWITCH_P true for a switch, false for loops. + Searches through last statements in cur_stmt_list, stops when seeing + BEFORE_LABELs, or statement other than LABEL_EXPR or CASE_LABEL_EXPR. + Returns number of loop/switch names found and if any are found, sets + *LAST_P to the canonical loop/switch name LABEL_DECL. */ + +int +c_get_loop_names (tree before_labels, bool switch_p, tree *last_p) +{ + *last_p = NULL_TREE; + if (!building_stmt_list_p () + || !STATEMENT_LIST_HAS_LABEL (cur_stmt_list) + || before_labels == void_list_node) + return 0; + + int ret = 0; + tree last = NULL_TREE; + for (tree_stmt_iterator tsi = tsi_last (cur_stmt_list); + !tsi_end_p (tsi); tsi_prev (&tsi)) + { + tree stmt = tsi_stmt (tsi); + if (stmt == before_labels) + break; + else if (TREE_CODE (stmt) == LABEL_EXPR) + { + if (last == NULL_TREE) + last = LABEL_EXPR_LABEL (stmt); + else + { + loop_names.safe_push (LABEL_EXPR_LABEL (stmt)); + ++ret; + } + } + else if (TREE_CODE (stmt) != CASE_LABEL_EXPR) + break; + } + if (last) + { + if (switch_p) + C_DECL_SWITCH_NAME (last) = 1; + else + C_DECL_LOOP_NAME (last) = 1; + loop_names.safe_push (last); + ++ret; + if (loop_names.length () > 16) + { + unsigned int first = 0, i; + tree l, c = NULL_TREE; + if (loop_names_hash == NULL) + loop_names_hash = new decl_tree_map (ret); + else + first = loop_names.length () - ret; + FOR_EACH_VEC_ELT_REVERSE (loop_names, i, l) + { + if (C_DECL_LOOP_NAME (l) || C_DECL_SWITCH_NAME (l)) + c = l; + loop_names_hash->put (l, c); + if (i == first) + break; + } + } + *last_p = last; + } + return ret; +} + +/* Undoes what get_loop_names did when it returned NUM_NAMES. */ + +void +c_release_loop_names (int num_names) +{ + unsigned len = loop_names.length () - num_names; + if (loop_names_hash) + { + if (len <= 16) + { + delete loop_names_hash; + loop_names_hash = NULL; + } + else + { + unsigned int i; + tree l; + FOR_EACH_VEC_ELT_REVERSE (loop_names, i, l) + { + loop_names_hash->remove (l); + if (i == len) + break; + } + } + } + loop_names.truncate (len); +} + +/* Finish processing of break or continue identifier operand. + NAME is the identifier operand of break or continue and + IS_BREAK is true iff it is break stmt. Returns the operand + to use for BREAK_STMT or CONTINUE_STMT, either NULL_TREE or + canonical loop/switch name LABEL_DECL. */ + +tree +c_finish_bc_name (location_t loc, tree name, bool is_break) +{ + tree label = NULL_TREE, lab; + pedwarn_c23 (loc, OPT_Wpedantic, + "ISO C does not support %qs statement with an identifier " + "operand before C2Y", is_break ? "break" : "continue"); + + /* If I_LABEL_DECL is NULL or not from current function, don't waste time + trying to find it among loop_names, it can't be there. */ + if (!loop_names.is_empty () + && current_function_scope + && (lab = I_LABEL_DECL (name)) + && DECL_CONTEXT (lab) == current_function_decl) + { + unsigned int i; + tree l, c = NULL_TREE; + if (loop_names_hash) + { + if (tree *val = loop_names_hash->get (lab)) + label = *val; + } + else + FOR_EACH_VEC_ELT_REVERSE (loop_names, i, l) + { + if (C_DECL_LOOP_NAME (l) || C_DECL_SWITCH_NAME (l)) + c = l; + if (l == lab) + { + label = c; + break; + } + } + if (label) + TREE_USED (lab) = 1; + } + if (label == NULL_TREE) + { + auto_vec<const char *> candidates; + unsigned int i; + tree l, c = NULL_TREE; + FOR_EACH_VEC_ELT_REVERSE (loop_names, i, l) + { + if (C_DECL_LOOP_NAME (l) || C_DECL_SWITCH_NAME (l)) + c = l; + if (is_break || C_DECL_LOOP_NAME (c)) + candidates.safe_push (IDENTIFIER_POINTER (DECL_NAME (l))); + } + const char *hint = find_closest_string (IDENTIFIER_POINTER (name), + &candidates); + if (hint) + { + gcc_rich_location richloc (loc); + richloc.add_fixit_replace (hint); + if (is_break) + error_at (&richloc, "%<break%> statement operand %qE does not " + "refer to a named loop or %<switch%>; " + "did you mean %qs?", name, hint); + else + error_at (&richloc, "%<continue%> statement operand %qE does not " + "refer to a named loop; did you mean %qs?", + name, hint); + } + else if (is_break) + error_at (loc, "%<break%> statement operand %qE does not refer to a " + "named loop or %<switch%>", name); + else + error_at (loc, "%<continue%> statement operand %qE does not refer to " + "a named loop", name); + } + else if (!C_DECL_LOOP_NAME (label) && !is_break) + { + auto_diagnostic_group d; + error_at (loc, "%<continue%> statement operand %qE refers to a named " + "%<switch%>", name); + inform (DECL_SOURCE_LOCATION (label), "%<switch%> name defined here"); + label = NULL_TREE; + } + else if (!C_DECL_LOOP_SWITCH_NAME_VALID (label)) + { + auto_diagnostic_group d; + if (C_DECL_LOOP_NAME (label)) + { + error_at (loc, "%qs statement operand %qE refers to a loop outside " + "of its body", is_break ? "break" : "continue", name); + inform (DECL_SOURCE_LOCATION (label), "loop name defined here"); + } + else + { + error_at (loc, "%<break%> statement operand %qE refers to a " + "%<switch%> outside of its body", name); + inform (DECL_SOURCE_LOCATION (label), + "%<switch%> name defined here"); + } + label = NULL_TREE; + } + else if (label == loop_names.last () && (in_statement & IN_NAMED_STMT) != 0) + /* If it is just a fancy reference to the innermost construct, handle it + just like break; or continue; though tracking cheaply what is the + innermost loop for continue when nested in switches would require + another global variable and updating it. */ + label = NULL_TREE; + else + C_DECL_LOOP_SWITCH_NAME_USED (label) = 1; + return label; +} + #include "gt-c-c-decl.h" diff --git a/gcc/c/c-lang.h b/gcc/c/c-lang.h index e512644..7888ab1 100644 --- a/gcc/c/c-lang.h +++ b/gcc/c/c-lang.h @@ -54,6 +54,8 @@ struct GTY(()) language_function { unsigned char x_in_statement; struct c_switch * GTY((skip)) x_switch_stack; struct c_arg_info * GTY((skip)) arg_info; + vec<tree> GTY((skip)) loop_names; + decl_tree_map * GTY((skip)) loop_names_hash; int returns_value; int returns_null; int returns_abnormally; diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc index fe01f95..0ffbdc7 100644 --- a/gcc/c/c-parser.cc +++ b/gcc/c/c-parser.cc @@ -1670,18 +1670,19 @@ 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 *, tree); static void c_parser_statement (c_parser *, bool *, location_t * = NULL); -static void c_parser_statement_after_labels (c_parser *, bool *, +static void c_parser_statement_after_labels (c_parser *, bool *, tree, vec<tree> * = NULL, attr_state = {}); static tree c_parser_c99_block_statement (c_parser *, bool *, location_t * = NULL); static void c_parser_if_statement (c_parser *, bool *, vec<tree> *); -static void c_parser_switch_statement (c_parser *, bool *); +static void c_parser_switch_statement (c_parser *, bool *, tree); static void c_parser_while_statement (c_parser *, bool, unsigned short, bool, - bool *); -static void c_parser_do_statement (c_parser *, bool, unsigned short, bool); + bool *, tree); +static void c_parser_do_statement (c_parser *, bool, unsigned short, bool, + tree); static void c_parser_for_statement (c_parser *, bool, unsigned short, bool, - bool *); + bool *, tree); static tree c_parser_asm_statement (c_parser *); static tree c_parser_asm_operands (c_parser *); static tree c_parser_asm_goto_operands (c_parser *); @@ -1735,7 +1736,7 @@ static void c_parser_omp_nothing (c_parser *); enum pragma_context { pragma_external, pragma_struct, pragma_param, pragma_stmt, pragma_compound }; -static bool c_parser_pragma (c_parser *, enum pragma_context, bool *); +static bool c_parser_pragma (c_parser *, enum pragma_context, bool *, tree); static bool c_parser_omp_cancellation_point (c_parser *, enum pragma_context); static bool c_parser_omp_target (c_parser *, enum pragma_context, bool *); static void c_parser_omp_begin (c_parser *); @@ -2038,7 +2039,7 @@ c_parser_external_declaration (c_parser *parser) break; case CPP_PRAGMA: mark_valid_location_for_stdc_pragma (true); - c_parser_pragma (parser, pragma_external, NULL); + c_parser_pragma (parser, pragma_external, NULL, NULL_TREE); mark_valid_location_for_stdc_pragma (false); break; case CPP_PLUS: @@ -2377,7 +2378,7 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, while (parser->in_omp_attribute_pragma) { gcc_assert (c_parser_next_token_is (parser, CPP_PRAGMA)); - c_parser_pragma (parser, pragma_external, NULL); + c_parser_pragma (parser, pragma_external, NULL, NULL_TREE); } c_parser_consume_token (parser); return; @@ -4071,7 +4072,7 @@ c_parser_struct_or_union_specifier (c_parser *parser) /* Accept #pragmas at struct scope. */ if (c_parser_next_token_is (parser, CPP_PRAGMA)) { - c_parser_pragma (parser, pragma_struct, NULL); + c_parser_pragma (parser, pragma_struct, NULL, NULL_TREE); continue; } /* Parse some comma-separated declarations, but not the @@ -5023,7 +5024,7 @@ c_parser_parameter_declaration (c_parser *parser, tree attrs, /* Accept #pragmas between parameter declarations. */ while (c_parser_next_token_is (parser, CPP_PRAGMA)) - c_parser_pragma (parser, pragma_param, NULL); + c_parser_pragma (parser, pragma_param, NULL, NULL_TREE); if (!c_parser_next_token_starts_declspecs (parser) && !c_parser_nth_token_starts_std_attributes (parser, 1)) @@ -7013,6 +7014,24 @@ c_parser_handle_musttail (c_parser *parser, tree std_attrs, attr_state &attr) return std_attrs; } +/* Return a statement before optional series of LABEL_EXPR/CASE_LABEL_EXPRs. + Instead of collecting vectors of labels before each stmt just in case + the statement would be iteration or switch statement for named loops, + we just remember last emitted statement and let the iteration/switch + statement search backwards in cur_stmt_list until that stmt for loop + names if any. */ + +static tree +get_before_labels () +{ + if (!building_stmt_list_p ()) + return NULL_TREE; + tree_stmt_iterator tsi = tsi_last (cur_stmt_list); + if (tsi_end_p (tsi)) + return NULL_TREE; + return tsi_stmt (tsi); +} + /* Parse a compound statement except for the opening brace. This is used for parsing both compound statements and statement expressions (which follow different paths to handling the opening). */ @@ -7089,6 +7108,7 @@ c_parser_compound_statement_nostart (c_parser *parser) c_parser_consume_token (parser); return endloc; } + tree before_labels = get_before_labels (); while (c_parser_next_token_is_not (parser, CPP_CLOSE_BRACE)) { location_t loc = c_parser_peek_token (parser)->location; @@ -7119,6 +7139,7 @@ c_parser_compound_statement_nostart (c_parser *parser) omp_for_parse_state->depth--; sl = push_stmt_list (); parser->error = false; + before_labels = get_before_labels (); continue; } else if (want_nested_loop @@ -7132,7 +7153,7 @@ c_parser_compound_statement_nostart (c_parser *parser) tree pre_sl = pop_stmt_list (sl); tree nested_sl = push_stmt_list (); mark_valid_location_for_stdc_pragma (false); - c_parser_statement_after_labels (parser, NULL); + c_parser_statement_after_labels (parser, NULL, NULL_TREE); nested_sl = pop_stmt_list (nested_sl); if (omp_for_parse_state->want_nested_loop) { @@ -7151,6 +7172,7 @@ c_parser_compound_statement_nostart (c_parser *parser) sl = push_stmt_list (); } parser->error = false; + before_labels = get_before_labels (); continue; } else if (c_parser_next_token_is (parser, CPP_SEMICOLON)) @@ -7161,6 +7183,7 @@ c_parser_compound_statement_nostart (c_parser *parser) do that, as an extension. */ /* FIXME: Maybe issue a warning or something here? */ c_parser_consume_token (parser); + before_labels = get_before_labels (); continue; } } @@ -7222,6 +7245,7 @@ c_parser_compound_statement_nostart (c_parser *parser) "ISO C90 forbids mixed declarations and code"); last_stmt = fallthru_attr_p; last_label = false; + before_labels = get_before_labels (); } else if (c_parser_next_token_is_keyword (parser, RID_EXTENSION)) { @@ -7263,6 +7287,7 @@ c_parser_compound_statement_nostart (c_parser *parser) pedwarn_c90 (loc, OPT_Wdeclaration_after_statement, "ISO C90 forbids mixed declarations and code"); last_stmt = false; + before_labels = get_before_labels (); } else goto statement; @@ -7282,7 +7307,7 @@ c_parser_compound_statement_nostart (c_parser *parser) omp_for_parse_state->want_nested_loop = false; if (c_parser_pragma (parser, last_label ? pragma_stmt : pragma_compound, - NULL)) + NULL, before_labels)) { last_label = false; last_stmt = true; @@ -7291,6 +7316,7 @@ c_parser_compound_statement_nostart (c_parser *parser) } if (omp_for_parse_state) omp_for_parse_state->want_nested_loop = want_nested_loop; + before_labels = get_before_labels (); } else if (c_parser_next_token_is (parser, CPP_EOF)) { @@ -7310,6 +7336,7 @@ c_parser_compound_statement_nostart (c_parser *parser) { error_at (loc, "%<else%> without a previous %<if%>"); c_parser_consume_token (parser); + before_labels = get_before_labels (); continue; } } @@ -7321,7 +7348,8 @@ c_parser_compound_statement_nostart (c_parser *parser) last_stmt = true; mark_valid_location_for_stdc_pragma (false); if (!omp_for_parse_state) - c_parser_statement_after_labels (parser, NULL, NULL, a); + c_parser_statement_after_labels (parser, NULL, before_labels, + NULL, a); else { /* In canonical loop nest form, nested loops can only appear @@ -7331,9 +7359,10 @@ c_parser_compound_statement_nostart (c_parser *parser) it must be intervening code. */ omp_for_parse_state->want_nested_loop = false; check_omp_intervening_code (parser); - c_parser_statement_after_labels (parser, NULL); + c_parser_statement_after_labels (parser, NULL, before_labels); omp_for_parse_state->want_nested_loop = want_nested_loop; } + before_labels = get_before_labels (); } parser->error = false; @@ -7645,11 +7674,26 @@ c_parser_label (c_parser *parser, tree std_attrs) static void c_parser_statement (c_parser *parser, bool *if_p, location_t *loc_after_labels) { + tree before_labels = get_before_labels (); attr_state a = c_parser_all_labels (parser); if (loc_after_labels) *loc_after_labels = c_parser_peek_token (parser)->location; parser->omp_attrs_forbidden_p = false; - c_parser_statement_after_labels (parser, if_p, NULL, a); + c_parser_statement_after_labels (parser, if_p, before_labels, NULL, a); +} + +/* Parse and handle optional identifier after break or continue keywords. */ + +static tree +c_parser_bc_name (c_parser *parser, bool is_break) +{ + if (!c_parser_next_token_is (parser, CPP_NAME)) + return NULL_TREE; + + c_token *tok = c_parser_peek_token (parser); + tree label = c_finish_bc_name (tok->location, tok->value, is_break); + c_parser_consume_token (parser); + return label; } /* Parse a statement, other than a labeled statement. CHAIN is a vector @@ -7658,10 +7702,14 @@ c_parser_statement (c_parser *parser, bool *if_p, location_t *loc_after_labels) IF_P is used to track whether there's a (possibly labeled) if statement which is not enclosed in braces and has an else clause. This is used to - implement -Wparentheses. ASTATE is an earlier parsed attribute state. */ + implement -Wparentheses. ASTATE is an earlier parsed attribute state. + + BEFORE_LABELS is last statement before possible labels, see + get_before_labels description for details. */ static void c_parser_statement_after_labels (c_parser *parser, bool *if_p, + tree before_labels, vec<tree> *chain, attr_state astate) { location_t loc = c_parser_peek_token (parser)->location; @@ -7687,16 +7735,16 @@ c_parser_statement_after_labels (c_parser *parser, bool *if_p, c_parser_if_statement (parser, if_p, chain); break; case RID_SWITCH: - c_parser_switch_statement (parser, if_p); + c_parser_switch_statement (parser, if_p, before_labels); break; case RID_WHILE: - c_parser_while_statement (parser, false, 0, false, if_p); + c_parser_while_statement (parser, false, 0, false, if_p, before_labels); break; case RID_DO: - c_parser_do_statement (parser, false, 0, false); + c_parser_do_statement (parser, false, 0, false, before_labels); break; case RID_FOR: - c_parser_for_statement (parser, false, 0, false, if_p); + c_parser_for_statement (parser, false, 0, false, if_p, before_labels); break; case RID_GOTO: c_parser_consume_token (parser); @@ -7720,11 +7768,13 @@ c_parser_statement_after_labels (c_parser *parser, bool *if_p, goto expect_semicolon; case RID_CONTINUE: c_parser_consume_token (parser); - stmt = c_finish_bc_stmt (loc, objc_foreach_continue_label, false); + stmt = c_finish_bc_stmt (loc, objc_foreach_continue_label, false, + c_parser_bc_name (parser, false)); goto expect_semicolon; case RID_BREAK: c_parser_consume_token (parser); - stmt = c_finish_bc_stmt (loc, objc_foreach_break_label, true); + stmt = c_finish_bc_stmt (loc, objc_foreach_break_label, true, + c_parser_bc_name (parser, true)); goto expect_semicolon; case RID_RETURN: c_parser_consume_token (parser); @@ -7840,7 +7890,7 @@ c_parser_statement_after_labels (c_parser *parser, bool *if_p, c_parser_consume_token (parser); break; case CPP_PRAGMA: - if (!c_parser_pragma (parser, pragma_stmt, if_p)) + if (!c_parser_pragma (parser, pragma_stmt, if_p, before_labels)) goto restart; break; default: @@ -7935,6 +7985,7 @@ c_parser_if_body (c_parser *parser, bool *if_p, location_t body_loc_after_labels = UNKNOWN_LOCATION; token_indent_info body_tinfo = get_token_indent_info (c_parser_peek_token (parser)); + tree before_labels = get_before_labels (); c_parser_all_labels (parser); if (c_parser_next_token_is (parser, CPP_SEMICOLON)) @@ -7951,7 +8002,7 @@ c_parser_if_body (c_parser *parser, bool *if_p, else { body_loc_after_labels = c_parser_peek_token (parser)->location; - c_parser_statement_after_labels (parser, if_p); + c_parser_statement_after_labels (parser, if_p, before_labels); } token_indent_info next_tinfo @@ -7979,6 +8030,7 @@ c_parser_else_body (c_parser *parser, const token_indent_info &else_tinfo, token_indent_info body_tinfo = get_token_indent_info (c_parser_peek_token (parser)); location_t body_loc_after_labels = UNKNOWN_LOCATION; + tree before_labels = get_before_labels (); c_parser_all_labels (parser); if (c_parser_next_token_is (parser, CPP_SEMICOLON)) @@ -7994,7 +8046,7 @@ c_parser_else_body (c_parser *parser, const token_indent_info &else_tinfo, { if (!c_parser_next_token_is (parser, CPP_OPEN_BRACE)) body_loc_after_labels = c_parser_peek_token (parser)->location; - c_parser_statement_after_labels (parser, NULL, chain); + c_parser_statement_after_labels (parser, NULL, before_labels, chain); } token_indent_info next_tinfo @@ -8134,10 +8186,12 @@ c_parser_if_statement (c_parser *parser, bool *if_p, vec<tree> *chain) switch-statement: switch (expression) statement -*/ + + BEFORE_LABELS is last statement before possible labels, see + get_before_labels description for details. */ static void -c_parser_switch_statement (c_parser *parser, bool *if_p) +c_parser_switch_statement (c_parser *parser, bool *if_p, tree before_labels) { struct c_expr ce; tree block, expr, body; @@ -8146,6 +8200,8 @@ c_parser_switch_statement (c_parser *parser, bool *if_p) location_t switch_cond_loc; gcc_assert (c_parser_next_token_is_keyword (parser, RID_SWITCH)); c_parser_consume_token (parser); + tree switch_name; + int num_names = c_get_loop_names (before_labels, true, &switch_name); block = c_begin_compound_stmt (flag_isoc99); bool explicit_cast_p = false; matching_parens parens; @@ -8167,9 +8223,18 @@ c_parser_switch_statement (c_parser *parser, bool *if_p) expr = error_mark_node; ce.original_type = error_mark_node; } - c_start_switch (switch_loc, switch_cond_loc, expr, explicit_cast_p); + tree stmt + = c_start_switch (switch_loc, switch_cond_loc, expr, explicit_cast_p, + switch_name); save_in_statement = in_statement; in_statement |= IN_SWITCH_STMT; + if (switch_name) + { + C_DECL_LOOP_SWITCH_NAME_VALID (switch_name) = 1; + in_statement |= IN_NAMED_STMT; + } + else + in_statement &= ~IN_NAMED_STMT; location_t loc_after_labels; bool open_brace_p = c_parser_peek_token (parser)->type == CPP_OPEN_BRACE; body = c_parser_c99_block_statement (parser, if_p, &loc_after_labels); @@ -8179,6 +8244,14 @@ c_parser_switch_statement (c_parser *parser, bool *if_p) RID_SWITCH); c_finish_switch (body, ce.original_type); in_statement = save_in_statement; + if (num_names) + { + if (!C_DECL_LOOP_SWITCH_NAME_USED (switch_name)) + SWITCH_STMT_NAME (stmt) = NULL_TREE; + else + SWITCH_STMT_NO_BREAK_P (stmt) = 0; + c_release_loop_names (num_names); + } add_stmt (c_end_compound_stmt (switch_loc, block, flag_isoc99)); c_parser_maybe_reclassify_token (parser); } @@ -8190,11 +8263,14 @@ c_parser_switch_statement (c_parser *parser, bool *if_p) IF_P is used to track whether there's a (possibly labeled) if statement which is not enclosed in braces and has an else clause. This is used to - implement -Wparentheses. */ + implement -Wparentheses. + + BEFORE_LABELS is last statement before possible labels, see + get_before_labels description for details. */ static void c_parser_while_statement (c_parser *parser, bool ivdep, unsigned short unroll, - bool novector, bool *if_p) + bool novector, bool *if_p, tree before_labels) { tree block, cond, body; unsigned char save_in_statement; @@ -8202,6 +8278,8 @@ c_parser_while_statement (c_parser *parser, bool ivdep, unsigned short unroll, gcc_assert (c_parser_next_token_is_keyword (parser, RID_WHILE)); token_indent_info while_tinfo = get_token_indent_info (c_parser_peek_token (parser)); + tree loop_name; + int num_names = c_get_loop_names (before_labels, false, &loop_name); if (parser->omp_for_parse_state) { @@ -8231,6 +8309,11 @@ c_parser_while_statement (c_parser *parser, bool ivdep, unsigned short unroll, integer_zero_node); save_in_statement = in_statement; in_statement = IN_ITERATION_STMT; + if (loop_name) + { + C_DECL_LOOP_SWITCH_NAME_VALID (loop_name) = 1; + in_statement |= IN_NAMED_STMT; + } token_indent_info body_tinfo = get_token_indent_info (c_parser_peek_token (parser)); @@ -8238,9 +8321,13 @@ c_parser_while_statement (c_parser *parser, bool ivdep, unsigned short unroll, location_t loc_after_labels; bool open_brace = c_parser_next_token_is (parser, CPP_OPEN_BRACE); body = c_parser_c99_block_statement (parser, if_p, &loc_after_labels); - add_stmt (build_stmt (loc, WHILE_STMT, cond, body)); + if (loop_name && !C_DECL_LOOP_SWITCH_NAME_USED (loop_name)) + loop_name = NULL_TREE; + add_stmt (build_stmt (loc, WHILE_STMT, cond, body, loop_name)); add_stmt (c_end_compound_stmt (loc, block, flag_isoc99)); c_parser_maybe_reclassify_token (parser); + if (num_names) + c_release_loop_names (num_names); token_indent_info next_tinfo = get_token_indent_info (c_parser_peek_token (parser)); @@ -8257,16 +8344,20 @@ c_parser_while_statement (c_parser *parser, bool ivdep, unsigned short unroll, do-statement: do statement while ( expression ) ; -*/ + + BEFORE_LABELS is last statement before possible labels, see + get_before_labels description for details. */ static void c_parser_do_statement (c_parser *parser, bool ivdep, unsigned short unroll, - bool novector) + bool novector, tree before_labels) { tree block, cond, body; unsigned char save_in_statement; location_t loc; gcc_assert (c_parser_next_token_is_keyword (parser, RID_DO)); + tree loop_name; + int num_names = c_get_loop_names (before_labels, false, &loop_name); if (parser->omp_for_parse_state) { @@ -8284,9 +8375,20 @@ c_parser_do_statement (c_parser *parser, bool ivdep, unsigned short unroll, loc = c_parser_peek_token (parser)->location; save_in_statement = in_statement; in_statement = IN_ITERATION_STMT; + if (loop_name) + { + C_DECL_LOOP_SWITCH_NAME_VALID (loop_name) = 1; + in_statement |= IN_NAMED_STMT; + } body = c_parser_c99_block_statement (parser, NULL); c_parser_require_keyword (parser, RID_WHILE, "expected %<while%>"); in_statement = save_in_statement; + if (num_names) + { + if (!C_DECL_LOOP_SWITCH_NAME_USED (loop_name)) + loop_name = NULL_TREE; + c_release_loop_names (num_names); + } cond = c_parser_paren_condition (parser); if (ivdep && cond != error_mark_node) cond = build3 (ANNOTATE_EXPR, TREE_TYPE (cond), cond, @@ -8306,7 +8408,7 @@ c_parser_do_statement (c_parser *parser, bool ivdep, unsigned short unroll, if (!c_parser_require (parser, CPP_SEMICOLON, "expected %<;%>")) c_parser_skip_to_end_of_block_or_statement (parser); - add_stmt (build_stmt (loc, DO_STMT, cond, body)); + add_stmt (build_stmt (loc, DO_STMT, cond, body, loop_name)); add_stmt (c_end_compound_stmt (loc, block, flag_isoc99)); } @@ -8367,11 +8469,14 @@ c_parser_do_statement (c_parser *parser, bool ivdep, unsigned short unroll, IF_P is used to track whether there's a (possibly labeled) if statement which is not enclosed in braces and has an else clause. This is used to - implement -Wparentheses. */ + implement -Wparentheses. + + BEFORE_LABELS is last statement before possible labels, see + get_before_labels description for details. */ static void c_parser_for_statement (c_parser *parser, bool ivdep, unsigned short unroll, - bool novector, bool *if_p) + bool novector, bool *if_p, tree before_labels) { tree block, cond, incr, body; unsigned char save_in_statement; @@ -8386,6 +8491,8 @@ c_parser_for_statement (c_parser *parser, bool ivdep, unsigned short unroll, gcc_assert (c_parser_next_token_is_keyword (parser, RID_FOR)); token_indent_info for_tinfo = get_token_indent_info (c_parser_peek_token (parser)); + tree loop_name; + int num_names = c_get_loop_names (before_labels, false, &loop_name); if (parser->omp_for_parse_state) { @@ -8578,9 +8685,22 @@ c_parser_for_statement (c_parser *parser, bool ivdep, unsigned short unroll, save_objc_foreach_continue_label = objc_foreach_continue_label; objc_foreach_break_label = create_artificial_label (loc); objc_foreach_continue_label = create_artificial_label (loc); + if (loop_name) + { + gcc_checking_assert (!DECL_CHAIN (loop_name) + && !DECL_CHAIN (objc_foreach_break_label)); + C_DECL_SWITCH_NAME (loop_name) = 1; + DECL_CHAIN (loop_name) = objc_foreach_break_label; + DECL_CHAIN (objc_foreach_break_label) = objc_foreach_continue_label; + } } else in_statement = IN_ITERATION_STMT; + if (loop_name) + { + C_DECL_LOOP_SWITCH_NAME_VALID (loop_name) = 1; + in_statement |= IN_NAMED_STMT; + } token_indent_info body_tinfo = get_token_indent_info (c_parser_peek_token (parser)); @@ -8589,6 +8709,16 @@ c_parser_for_statement (c_parser *parser, bool ivdep, unsigned short unroll, bool open_brace = c_parser_next_token_is (parser, CPP_OPEN_BRACE); body = c_parser_c99_block_statement (parser, if_p, &loc_after_labels); + if (loop_name && is_foreach_statement) + { + gcc_checking_assert (DECL_CHAIN (loop_name) == objc_foreach_break_label + && (DECL_CHAIN (objc_foreach_break_label) + == objc_foreach_continue_label)); + C_DECL_SWITCH_NAME (loop_name) = 0; + DECL_CHAIN (loop_name) = NULL_TREE; + DECL_CHAIN (objc_foreach_break_label) = NULL_TREE; + } + if (is_foreach_statement) objc_finish_foreach_loop (for_loc, object_expression, collection_expression, body, @@ -8596,7 +8726,9 @@ c_parser_for_statement (c_parser *parser, bool ivdep, unsigned short unroll, objc_foreach_continue_label); else add_stmt (build_stmt (for_loc, FOR_STMT, NULL_TREE, cond, incr, - body, NULL_TREE)); + body, NULL_TREE, + loop_name && C_DECL_LOOP_SWITCH_NAME_USED (loop_name) + ? loop_name : NULL_TREE)); add_stmt (c_end_compound_stmt (for_loc, block, flag_isoc99 || c_dialect_objc ())); c_parser_maybe_reclassify_token (parser); @@ -8610,6 +8742,8 @@ c_parser_for_statement (c_parser *parser, bool ivdep, unsigned short unroll, for_tinfo.location, RID_FOR); in_statement = save_in_statement; + if (num_names) + c_release_loop_names (num_names); if (is_foreach_statement) { objc_foreach_break_label = save_objc_foreach_break_label; @@ -13245,7 +13379,7 @@ c_parser_objc_class_instance_variables (c_parser *parser) } else if (c_parser_next_token_is (parser, CPP_PRAGMA)) { - c_parser_pragma (parser, pragma_external, NULL); + c_parser_pragma (parser, pragma_external, NULL, NULL_TREE); continue; } @@ -13516,7 +13650,7 @@ c_parser_objc_methodprotolist (c_parser *parser) c_parser_objc_methodproto (parser); break; case CPP_PRAGMA: - c_parser_pragma (parser, pragma_external, NULL); + c_parser_pragma (parser, pragma_external, NULL, NULL_TREE); break; case CPP_EOF: return; @@ -14585,10 +14719,12 @@ c_parser_pragma_unroll (c_parser *parser) /* Handle pragmas. Some OpenMP pragmas are associated with, and therefore should be considered, statements. ALLOW_STMT is true if we're within the context of a function and such pragmas are to be allowed. Returns - true if we actually parsed such a pragma. */ + true if we actually parsed such a pragma. BEFORE_LABELS is last statement + before possible labels, see get_before_labels description for details. */ static bool -c_parser_pragma (c_parser *parser, enum pragma_context context, bool *if_p) +c_parser_pragma (c_parser *parser, enum pragma_context context, bool *if_p, + tree before_labels) { unsigned int id; const char *construct = NULL; @@ -14841,11 +14977,14 @@ c_parser_pragma (c_parser *parser, enum pragma_context context, bool *if_p) return false; } if (c_parser_next_token_is_keyword (parser, RID_FOR)) - c_parser_for_statement (parser, ivdep, unroll, novector, if_p); + c_parser_for_statement (parser, ivdep, unroll, novector, if_p, + before_labels); else if (c_parser_next_token_is_keyword (parser, RID_WHILE)) - c_parser_while_statement (parser, ivdep, unroll, novector, if_p); + c_parser_while_statement (parser, ivdep, unroll, novector, if_p, + before_labels); else - c_parser_do_statement (parser, ivdep, unroll, novector); + c_parser_do_statement (parser, ivdep, unroll, novector, + before_labels); } return true; @@ -22568,7 +22707,7 @@ c_parser_omp_loop_nest (c_parser *parser, bool *if_p) } break; default: - c_parser_pragma (parser, pragma_stmt, NULL); + c_parser_pragma (parser, pragma_stmt, NULL, void_list_node); break; } if (transform == NULL_TREE) @@ -25468,7 +25607,7 @@ c_maybe_parse_omp_decl (tree decl, tree d) parser->tokens = toks->address (); parser->tokens_avail = toks->length (); parser->in_omp_attribute_pragma = toks; - c_parser_pragma (parser, pragma_external, NULL); + c_parser_pragma (parser, pragma_external, NULL, NULL_TREE); parser->in_omp_decl_attribute = NULL_TREE; return true; } diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h index b3e7bb0..c35ea36 100644 --- a/gcc/c/c-tree.h +++ b/gcc/c/c-tree.h @@ -90,6 +90,24 @@ along with GCC; see the file COPYING3. If not see #define C_DECL_BUILTIN_PROTOTYPE(EXP) \ DECL_LANG_FLAG_6 (FUNCTION_DECL_CHECK (EXP)) +/* For LABEL_DECLs marks canonical name of a loop. */ +#define C_DECL_LOOP_NAME(EXP) DECL_LANG_FLAG_3 (LABEL_DECL_CHECK (EXP)) + +/* For LABEL_DECLs marks canonical name of a switch. During parsing of + ObjC foreach named loop both C_DECL_LOOP_NAME and C_DECL_SWITCH_NAME + are temporarily set. */ +#define C_DECL_SWITCH_NAME(EXP) DECL_LANG_FLAG_5 (LABEL_DECL_CHECK (EXP)) + +/* For LABEL_DECLs marks canonical name of a loop or switch being + valid for use in break identifier or continue identifier statements. */ +#define C_DECL_LOOP_SWITCH_NAME_VALID(EXP) \ + DECL_LANG_FLAG_6 (LABEL_DECL_CHECK (EXP)) + +/* For LABEL_DECLs marks canonical loop or switch names which were actually + used in one or more break identifier or continue identifier statements. */ +#define C_DECL_LOOP_SWITCH_NAME_USED(EXP) \ + DECL_LANG_FLAG_8 (LABEL_DECL_CHECK (EXP)) + /* Record whether a decl was declared register. This is strictly a front-end flag, whereas DECL_REGISTER is used for code generation; they may differ for structures with volatile fields. */ @@ -611,12 +629,15 @@ extern struct obstack parser_obstack; to IN_OMP_BLOCK if parsing OpenMP structured block and IN_OMP_FOR if parsing OpenMP loop. If parsing a switch statement, this is bitwise ORed with IN_SWITCH_STMT, unless parsing an - iteration-statement, OpenMP block or loop within that switch. */ + iteration-statement, OpenMP block or loop within that switch. + If the innermost iteration/switch statement is named, IN_NAMED_STMT + is additionally bitwise ORed into it. */ #define IN_SWITCH_STMT 1 #define IN_ITERATION_STMT 2 #define IN_OMP_BLOCK 4 #define IN_OMP_FOR 8 #define IN_OBJC_FOREACH 16 +#define IN_NAMED_STMT 32 extern unsigned char in_statement; extern bool switch_statement_break_seen_p; @@ -723,6 +744,9 @@ extern struct c_declspecs *declspecs_add_alignas (location_t, struct c_declspecs *, tree); extern struct c_declspecs *finish_declspecs (struct c_declspecs *); extern size_t c_tree_size (enum tree_code); +extern int c_get_loop_names (tree, bool, tree *); +extern void c_release_loop_names (int); +extern tree c_finish_bc_name (location_t, tree, bool); /* in c-objc-common.cc */ extern bool c_objc_common_init (void); @@ -812,7 +836,7 @@ extern void process_init_element (location_t, struct c_expr, bool, extern tree build_compound_literal (location_t, tree, tree, bool, unsigned int, struct c_declspecs *); extern void check_compound_literal_type (location_t, struct c_type_name *); -extern tree c_start_switch (location_t, location_t, tree, bool); +extern tree c_start_switch (location_t, location_t, tree, bool, tree); extern void c_finish_switch (tree, tree); extern tree build_asm_expr (location_t, tree, tree, tree, tree, tree, bool, bool); @@ -828,7 +852,7 @@ extern tree c_finish_stmt_expr (location_t, tree); extern tree c_process_expr_stmt (location_t, tree); extern tree c_finish_expr_stmt (location_t, tree); extern tree c_finish_return (location_t, tree, tree, bool = false); -extern tree c_finish_bc_stmt (location_t, tree, bool); +extern tree c_finish_bc_stmt (location_t, tree, bool, tree); extern tree c_finish_goto_label (location_t, tree); extern tree c_finish_goto_ptr (location_t, c_expr val); extern tree c_expr_to_decl (tree, bool *, bool *); diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc index ba6d96d..869b3a6 100644 --- a/gcc/c/c-typeck.cc +++ b/gcc/c/c-typeck.cc @@ -11943,7 +11943,7 @@ struct c_switch *c_switch_stack; tree c_start_switch (location_t switch_loc, location_t switch_cond_loc, - tree exp, bool explicit_cast_p) + tree exp, bool explicit_cast_p, tree switch_name) { tree orig_type = error_mark_node; bool bool_cond_p = false; @@ -11996,7 +11996,7 @@ c_start_switch (location_t switch_loc, /* Add this new SWITCH_STMT to the stack. */ cs = XNEW (struct c_switch); cs->switch_stmt = build_stmt (switch_loc, SWITCH_STMT, exp, - NULL_TREE, orig_type, NULL_TREE); + NULL_TREE, orig_type, NULL_TREE, switch_name); cs->orig_type = orig_type; cs->cases = splay_tree_new (case_compare, NULL, NULL); cs->bindings = c_get_switch_bindings (); @@ -12097,7 +12097,7 @@ c_finish_if_stmt (location_t if_locus, tree cond, tree then_block, } tree -c_finish_bc_stmt (location_t loc, tree label, bool is_break) +c_finish_bc_stmt (location_t loc, tree label, bool is_break, tree name) { /* In switch statements break is sometimes stylistically used after a return statement. This can lead to spurious warnings about @@ -12109,7 +12109,7 @@ c_finish_bc_stmt (location_t loc, tree label, bool is_break) bool skip = !block_may_fallthru (cur_stmt_list); if (is_break) - switch (in_statement) + switch (in_statement & ~IN_NAMED_STMT) { case 0: error_at (loc, "break statement not within loop or switch"); @@ -12129,7 +12129,7 @@ c_finish_bc_stmt (location_t loc, tree label, bool is_break) break; } else - switch (in_statement & ~IN_SWITCH_STMT) + switch (in_statement & ~(IN_SWITCH_STMT | IN_NAMED_STMT)) { case 0: error_at (loc, "continue statement not within a loop"); @@ -12148,14 +12148,24 @@ c_finish_bc_stmt (location_t loc, tree label, bool is_break) if (skip) return NULL_TREE; else if ((in_statement & IN_OBJC_FOREACH) - && !(is_break && (in_statement & IN_SWITCH_STMT))) + && !(is_break && (in_statement & IN_SWITCH_STMT)) + && name == NULL_TREE) { /* The foreach expander produces low-level code using gotos instead of a structured loop construct. */ gcc_assert (label); return add_stmt (build_stmt (loc, GOTO_EXPR, label)); } - return add_stmt (build_stmt (loc, (is_break ? BREAK_STMT : CONTINUE_STMT))); + else if (name && C_DECL_LOOP_NAME (name) && C_DECL_SWITCH_NAME (name)) + { + label = DECL_CHAIN (name); + if (!is_break) + label = DECL_CHAIN (label); + /* Foreach expander from some outer level. */ + return add_stmt (build_stmt (loc, GOTO_EXPR, label)); + } + return add_stmt (build_stmt (loc, is_break ? BREAK_STMT : CONTINUE_STMT, + name)); } /* A helper routine for c_process_expr_stmt and c_finish_stmt_expr. */ |