diff mbox series

c: Implement C2Y N3355 - Named Loops [PR117022]

Message ID ZwjPPAoHvJINSjJ2@tucnak
State New
Headers show
Series c: Implement C2Y N3355 - Named Loops [PR117022] | expand

Commit Message

Jakub Jelinek Oct. 11, 2024, 7:09 a.m. UTC
Hi!

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.

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2024-10-11  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/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/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.


	Jakub

Comments

Joseph Myers Oct. 11, 2024, 2:19 p.m. UTC | #1
There should definitely be a test that -std=c23 -pedantic-errors gives 
errors for these constructs (I'd say also test that -std=c23 
-pedantic-errors -Wno-c23-c2y-compat doesn't diagnose them, while -std=c2y 
-Wc23-c2y-compat does).  Not yet reviewed the rest of the patch.
diff mbox series

Patch

--- gcc/c-family/c-common.def.jj	2024-01-03 22:33:37.511703242 +0100
+++ gcc/c-family/c-common.def	2024-10-10 17:45:40.339267609 +0200
@@ -58,28 +58,31 @@  DEFTREECODE (SIZEOF_EXPR, "sizeof_expr",
 DEFTREECODE (PAREN_SIZEOF_EXPR, "paren_sizeof_expr", tcc_expression, 1)
 
 /* Used to represent a `for' statement. The operands are
-   FOR_INIT_STMT, FOR_COND, FOR_EXPR, FOR_BODY, and FOR_SCOPE,
+   FOR_INIT_STMT, FOR_COND, FOR_EXPR, FOR_BODY, FOR_SCOPE, and FOR_NAME
    respectively.  */
-DEFTREECODE (FOR_STMT, "for_stmt", tcc_statement, 5)
+DEFTREECODE (FOR_STMT, "for_stmt", tcc_statement, 6)
 
-/* Used to represent a 'while' statement. The operands are WHILE_COND
-   and WHILE_BODY, respectively.  */
-DEFTREECODE (WHILE_STMT, "while_stmt", tcc_statement, 2)
-
-/* Used to represent a 'do' statement. The operands are DO_COND and
-   DO_BODY, respectively.  */
-DEFTREECODE (DO_STMT, "do_stmt", tcc_statement, 2)
-
-/* Used to represent a 'break' statement.  */
-DEFTREECODE (BREAK_STMT, "break_stmt", tcc_statement, 0)
-
-/* Used to represent a 'continue' statement.  */
-DEFTREECODE (CONTINUE_STMT, "continue_stmt", tcc_statement, 0)
+/* Used to represent a 'while' statement. The operands are WHILE_COND,
+   WHILE_BODY, and WHILE_NAME, respectively.  */
+DEFTREECODE (WHILE_STMT, "while_stmt", tcc_statement, 3)
+
+/* Used to represent a 'do' statement. The operands are DO_COND, DO_BODY,
+   and DO_NAME, respectively.  */
+DEFTREECODE (DO_STMT, "do_stmt", tcc_statement, 3)
+
+/* Used to represent a 'break' statement.  The operand BREAK_NAME is
+   the {FOR,WHILE,DO,SWITCH}_NAME to which it applies.  NULL_TREE means
+   innermost.  */
+DEFTREECODE (BREAK_STMT, "break_stmt", tcc_statement, 1)
+
+/* Used to represent a 'continue' statement.  The operand CONTINUE_NAME is
+   the {FOR,WHILE,DO}_STMT to which it applies.  NULL_TREE means innermost.  */
+DEFTREECODE (CONTINUE_STMT, "continue_stmt", tcc_statement, 1)
 
 /* Used to represent a 'switch' statement. The operands are
-   SWITCH_STMT_COND, SWITCH_STMT_BODY, SWITCH_STMT_TYPE, and
-   SWITCH_STMT_SCOPE, respectively.  */
-DEFTREECODE (SWITCH_STMT, "switch_stmt", tcc_statement, 4)
+   SWITCH_STMT_COND, SWITCH_STMT_BODY, SWITCH_STMT_TYPE, SWITCH_STMT_SCOPE,
+   and SWITCH_STMT_NAME, respectively.  */
+DEFTREECODE (SWITCH_STMT, "switch_stmt", tcc_statement, 5)
 
 /* Extensions for C++ Concepts. */
 
--- gcc/c-family/c-common.h.jj	2024-09-10 17:31:15.237019906 +0200
+++ gcc/c-family/c-common.h	2024-10-10 17:45:40.340267596 +0200
@@ -1216,9 +1216,13 @@  extern const char *c_get_substring_locat
 					     location_t *out_loc);
 
 /* In c-gimplify.cc.  */
+typedef hash_map<tree, tree_pair,
+		 simple_hashmap_traits<tree_decl_hash,
+				       tree_pair>> bc_hash_map_t;
 typedef struct bc_state
 {
   tree bc_label[2];
+  bc_hash_map_t *bc_hash_map;
 } bc_state_t;
 extern void save_bc_state (bc_state_t *);
 extern void restore_bc_state (bc_state_t *);
@@ -1501,29 +1505,39 @@  extern tree build_userdef_literal (tree
 				   tree num_string);
 
 
-/* WHILE_STMT accessors. These give access to the condition of the
-   while statement and the body of the while statement, respectively.  */
+/* WHILE_STMT accessors.  These give access to the condition of the
+   while statement, the body and name of the while statement, respectively.  */
 #define WHILE_COND(NODE)	TREE_OPERAND (WHILE_STMT_CHECK (NODE), 0)
 #define WHILE_BODY(NODE)	TREE_OPERAND (WHILE_STMT_CHECK (NODE), 1)
+#define WHILE_NAME(NODE)	TREE_OPERAND (WHILE_STMT_CHECK (NODE), 2)
 
-/* DO_STMT accessors. These give access to the condition of the do
-   statement and the body of the do statement, respectively.  */
+/* DO_STMT accessors.  These give access to the condition of the do
+   statement, the body and name of the do statement, respectively.  */
 #define DO_COND(NODE)		TREE_OPERAND (DO_STMT_CHECK (NODE), 0)
 #define DO_BODY(NODE)		TREE_OPERAND (DO_STMT_CHECK (NODE), 1)
+#define DO_NAME(NODE)		TREE_OPERAND (DO_STMT_CHECK (NODE), 2)
 
-/* FOR_STMT accessors. These give access to the init statement,
-   condition, update expression, and body of the for statement,
+/* FOR_STMT accessors.  These give access to the init statement,
+   condition, update expression, body and name of the for statement,
    respectively.  */
 #define FOR_INIT_STMT(NODE)	TREE_OPERAND (FOR_STMT_CHECK (NODE), 0)
 #define FOR_COND(NODE)		TREE_OPERAND (FOR_STMT_CHECK (NODE), 1)
 #define FOR_EXPR(NODE)		TREE_OPERAND (FOR_STMT_CHECK (NODE), 2)
 #define FOR_BODY(NODE)		TREE_OPERAND (FOR_STMT_CHECK (NODE), 3)
 #define FOR_SCOPE(NODE)		TREE_OPERAND (FOR_STMT_CHECK (NODE), 4)
+#define FOR_NAME(NODE)		TREE_OPERAND (FOR_STMT_CHECK (NODE), 5)
+
+/* BREAK_STMT accessors.  */
+#define BREAK_NAME(NODE)	TREE_OPERAND (BREAK_STMT_CHECK (NODE), 0)
+
+/* CONTINUE_STMT accessors.  */
+#define CONTINUE_NAME(NODE)	TREE_OPERAND (CONTINUE_STMT_CHECK (NODE), 0)
 
 #define SWITCH_STMT_COND(NODE)	TREE_OPERAND (SWITCH_STMT_CHECK (NODE), 0)
 #define SWITCH_STMT_BODY(NODE)	TREE_OPERAND (SWITCH_STMT_CHECK (NODE), 1)
 #define SWITCH_STMT_TYPE(NODE)	TREE_OPERAND (SWITCH_STMT_CHECK (NODE), 2)
 #define SWITCH_STMT_SCOPE(NODE)	TREE_OPERAND (SWITCH_STMT_CHECK (NODE), 3)
+#define SWITCH_STMT_NAME(NODE)	TREE_OPERAND (SWITCH_STMT_CHECK (NODE), 4)
 /* True if there are case labels for all possible values of switch cond, either
    because there is a default: case label or because the case label ranges cover
    all values.  */
--- gcc/c-family/c-pretty-print.cc.jj	2024-09-25 17:25:07.118362089 +0200
+++ gcc/c-family/c-pretty-print.cc	2024-10-10 17:45:40.340267596 +0200
@@ -2943,8 +2943,23 @@  c_pretty_printer::statement (tree t)
 	    continue ;
 	    return expression(opt) ;  */
     case BREAK_STMT:
+      pp_string (this, "break");
+      if (BREAK_NAME (t))
+	{
+	  pp_space (this);
+	  pp_c_tree_decl_identifier (this, BREAK_NAME (t));
+	}
+      pp_c_semicolon (this);
+      pp_needs_newline (this) = true;
+      break;
+
     case CONTINUE_STMT:
-      pp_string (this, TREE_CODE (t) == BREAK_STMT ? "break" : "continue");
+      pp_string (this, "continue");
+      if (CONTINUE_NAME (t))
+	{
+	  pp_space (this);
+	  pp_c_tree_decl_identifier (this, CONTINUE_NAME (t));
+	}
       pp_c_semicolon (this);
       pp_needs_newline (this) = true;
       break;
--- gcc/c-family/c-gimplify.cc.jj	2024-06-06 22:11:22.789433332 +0200
+++ gcc/c-family/c-gimplify.cc	2024-10-10 17:45:40.341267582 +0200
@@ -133,6 +133,10 @@  enum bc_t { bc_break = 0, bc_continue =
    linked through TREE_CHAIN.  */
 static tree bc_label[2];
 
+/* Hash map from loop/switch names (identified by LABEL_DECL) to
+   corresponding break and (if any) continue labels.  */
+static bc_hash_map_t *bc_hash_map;
+
 /* Begin a scope which can be exited by a break or continue statement.  BC
    indicates which.
 
@@ -172,6 +176,26 @@  finish_bc_block (tree *block, enum bc_t
   DECL_CHAIN (label) = NULL_TREE;
 }
 
+/* For named loop or switch with NAME, remember corresponding break
+   label BLAB and continue label CLAB.  */
+
+static void
+note_named_bc (tree name, tree blab, tree clab)
+{
+  if (bc_hash_map == NULL)
+    bc_hash_map = new bc_hash_map_t (32);
+  bc_hash_map->put (name, std::make_pair (blab, clab));
+}
+
+/* Remove NAME from the map after processing body of the loop or
+   switch.  */
+
+static void
+release_named_bc (tree name)
+{
+  bc_hash_map->remove (name);
+}
+
 /* Allow saving and restoring break/continue state.  */
 
 void
@@ -179,8 +203,10 @@  save_bc_state (bc_state_t *state)
 {
   state->bc_label[bc_break] = bc_label[bc_break];
   state->bc_label[bc_continue] = bc_label[bc_continue];
+  state->bc_hash_map = bc_hash_map;
   bc_label[bc_break] = NULL_TREE;
   bc_label[bc_continue] = NULL_TREE;
+  bc_hash_map = NULL;
 }
 
 void
@@ -188,8 +214,10 @@  restore_bc_state (bc_state_t *state)
 {
   gcc_assert (bc_label[bc_break] == NULL);
   gcc_assert (bc_label[bc_continue] == NULL);
+  gcc_assert (bc_hash_map == NULL);
   bc_label[bc_break] = state->bc_label[bc_break];
   bc_label[bc_continue] = state->bc_label[bc_continue];
+  bc_hash_map = state->bc_hash_map;
 }
 
 /* Get the LABEL_EXPR to represent a break or continue statement
@@ -229,8 +257,9 @@  expr_loc_or_loc (const_tree expr, locati
 
 static void
 genericize_c_loop (tree *stmt_p, location_t start_locus, tree cond, tree body,
-		   tree incr, bool cond_is_first, int *walk_subtrees,
-		   void *data, walk_tree_fn func, walk_tree_lh lh)
+		   tree incr, tree name, bool cond_is_first,
+		   int *walk_subtrees, void *data, walk_tree_fn func,
+		   walk_tree_lh lh)
 {
   tree blab, clab;
   tree entry = NULL, exit = NULL, t;
@@ -245,10 +274,15 @@  genericize_c_loop (tree *stmt_p, locatio
 
   blab = begin_bc_block (bc_break, start_locus);
   clab = begin_bc_block (bc_continue, start_locus);
+  if (name)
+    note_named_bc (name, blab, clab);
 
   walk_tree_1 (&body, func, data, NULL, lh);
   *walk_subtrees = 0;
 
+  if (name)
+    release_named_bc (name);
+
   /* If condition is zero don't generate a loop construct.  */
   if (cond && integer_zerop (cond))
     {
@@ -373,8 +407,8 @@  genericize_for_stmt (tree *stmt_p, int *
     }
 
   genericize_c_loop (&loop, EXPR_LOCATION (stmt), FOR_COND (stmt),
-		     FOR_BODY (stmt), FOR_EXPR (stmt), 1, walk_subtrees,
-		     data, func, lh);
+		     FOR_BODY (stmt), FOR_EXPR (stmt), FOR_NAME (stmt), 1,
+		     walk_subtrees, data, func, lh);
   append_to_statement_list (loop, &expr);
   if (expr == NULL_TREE)
     expr = loop;
@@ -389,8 +423,8 @@  genericize_while_stmt (tree *stmt_p, int
 {
   tree stmt = *stmt_p;
   genericize_c_loop (stmt_p, EXPR_LOCATION (stmt), WHILE_COND (stmt),
-		     WHILE_BODY (stmt), NULL_TREE, 1, walk_subtrees,
-		     data, func, lh);
+		     WHILE_BODY (stmt), NULL_TREE, WHILE_NAME (stmt), 1,
+		     walk_subtrees, data, func, lh);
 }
 
 /* Genericize a DO_STMT node *STMT_P.  */
@@ -401,8 +435,8 @@  genericize_do_stmt (tree *stmt_p, int *w
 {
   tree stmt = *stmt_p;
   genericize_c_loop (stmt_p, EXPR_LOCATION (stmt), DO_COND (stmt),
-		     DO_BODY (stmt), NULL_TREE, 0, walk_subtrees,
-		     data, func, lh);
+		     DO_BODY (stmt), NULL_TREE, DO_NAME (stmt), 0,
+		     walk_subtrees, data, func, lh);
 }
 
 /* Genericize a SWITCH_STMT node *STMT_P by turning it into a SWITCH_EXPR.  */
@@ -412,7 +446,7 @@  genericize_switch_stmt (tree *stmt_p, in
 			walk_tree_fn func, walk_tree_lh lh)
 {
   tree stmt = *stmt_p;
-  tree break_block, body, cond, type;
+  tree blab, body, cond, type;
   location_t stmt_locus = EXPR_LOCATION (stmt);
 
   body = SWITCH_STMT_BODY (stmt);
@@ -423,19 +457,25 @@  genericize_switch_stmt (tree *stmt_p, in
 
   walk_tree_1 (&cond, func, data, NULL, lh);
 
-  break_block = begin_bc_block (bc_break, stmt_locus);
+  blab = begin_bc_block (bc_break, stmt_locus);
+  if (SWITCH_STMT_NAME (stmt))
+    note_named_bc (SWITCH_STMT_NAME (stmt), blab, NULL_TREE);
 
   walk_tree_1 (&body, func, data, NULL, lh);
+
+  if (SWITCH_STMT_NAME (stmt))
+    release_named_bc (SWITCH_STMT_NAME (stmt));
+
   walk_tree_1 (&type, func, data, NULL, lh);
   *walk_subtrees = 0;
 
-  if (TREE_USED (break_block))
-    SWITCH_BREAK_LABEL_P (break_block) = 1;
-  finish_bc_block (&body, bc_break, break_block);
+  if (TREE_USED (blab))
+    SWITCH_BREAK_LABEL_P (blab) = 1;
+  finish_bc_block (&body, bc_break, blab);
   *stmt_p = build2_loc (stmt_locus, SWITCH_EXPR, type, cond, body);
   SWITCH_ALL_CASES_P (*stmt_p) = SWITCH_STMT_ALL_CASES_P (stmt);
   gcc_checking_assert (!SWITCH_STMT_NO_BREAK_P (stmt)
-		       || !TREE_USED (break_block));
+		       || !TREE_USED (blab));
 }
 
 /* Genericize a CONTINUE_STMT node *STMT_P.  */
@@ -445,7 +485,16 @@  genericize_continue_stmt (tree *stmt_p)
 {
   tree stmt_list = NULL;
   tree pred = build_predict_expr (PRED_CONTINUE, NOT_TAKEN);
-  tree label = get_bc_label (bc_continue);
+  tree label;
+  if (CONTINUE_NAME (*stmt_p))
+    {
+      tree_pair *slot = bc_hash_map->get (CONTINUE_NAME (*stmt_p));
+      gcc_checking_assert (slot);
+      label = slot->second;
+      TREE_USED (label) = 1;
+    }
+  else
+    label = get_bc_label (bc_continue);
   location_t location = EXPR_LOCATION (*stmt_p);
   tree jump = build1_loc (location, GOTO_EXPR, void_type_node, label);
   append_to_statement_list_force (pred, &stmt_list);
@@ -458,7 +507,16 @@  genericize_continue_stmt (tree *stmt_p)
 static void
 genericize_break_stmt (tree *stmt_p)
 {
-  tree label = get_bc_label (bc_break);
+  tree label;
+  if (BREAK_NAME (*stmt_p))
+    {
+      tree_pair *slot = bc_hash_map->get (BREAK_NAME (*stmt_p));
+      gcc_checking_assert (slot);
+      label = slot->first;
+      TREE_USED (label) = 1;
+    }
+  else
+    label = get_bc_label (bc_break);
   location_t location = EXPR_LOCATION (*stmt_p);
   *stmt_p = build1_loc (location, GOTO_EXPR, void_type_node, label);
 }
@@ -615,6 +673,8 @@  c_genericize (tree fndecl)
       hash_set<tree> pset;
       walk_tree (&DECL_SAVED_TREE (fndecl), c_genericize_control_r, &pset,
 		 &pset);
+      delete bc_hash_map;
+      bc_hash_map = NULL;
       restore_bc_state (&save_state);
       pop_cfun ();
     }
--- gcc/c/c-tree.h.jj	2024-10-09 13:23:03.957530675 +0200
+++ gcc/c/c-tree.h	2024-10-10 18:25:04.153837402 +0200
@@ -90,6 +90,24 @@  along with GCC; see the file COPYING3.
 #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
 						  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);
@@ -813,7 +837,7 @@  extern void process_init_element (locati
 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);
@@ -829,7 +853,7 @@  extern tree c_finish_stmt_expr (location
 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 *);
--- gcc/c/c-lang.h.jj	2024-01-03 22:33:37.522703090 +0100
+++ gcc/c/c-lang.h	2024-10-10 17:45:40.339267609 +0200
@@ -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;
--- gcc/c/c-parser.cc.jj	2024-10-09 13:23:03.960530634 +0200
+++ gcc/c/c-parser.cc	2024-10-10 18:23:34.238070527 +0200
@@ -1670,18 +1670,19 @@  static tree c_parser_compound_statement
 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_pars
 
 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
       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
 	  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_pa
 	  /* 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
 
   /* 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))
@@ -7150,6 +7151,24 @@  c_parser_handle_musttail (c_parser *pars
   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).  */
@@ -7226,6 +7245,7 @@  c_parser_compound_statement_nostart (c_p
       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;
@@ -7256,6 +7276,7 @@  c_parser_compound_statement_nostart (c_p
 	      omp_for_parse_state->depth--;
 	      sl = push_stmt_list ();
 	      parser->error = false;
+	      before_labels = get_before_labels ();
 	      continue;
 	    }
 	  else if (want_nested_loop
@@ -7269,7 +7290,7 @@  c_parser_compound_statement_nostart (c_p
 	      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)
 		{
@@ -7288,6 +7309,7 @@  c_parser_compound_statement_nostart (c_p
 		  sl = push_stmt_list ();
 		}
 	      parser->error = false;
+	      before_labels = get_before_labels ();
 	      continue;
 	    }
 	  else if (c_parser_next_token_is (parser, CPP_SEMICOLON))
@@ -7298,6 +7320,7 @@  c_parser_compound_statement_nostart (c_p
 		 do that, as an extension.  */
 	      /* FIXME:  Maybe issue a warning or something here?  */
 	      c_parser_consume_token (parser);
+	      before_labels = get_before_labels ();
 	      continue;
 	    }
 	}
@@ -7359,6 +7382,7 @@  c_parser_compound_statement_nostart (c_p
 			 "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))
 	{
@@ -7400,6 +7424,7 @@  c_parser_compound_statement_nostart (c_p
 		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;
@@ -7419,7 +7444,7 @@  c_parser_compound_statement_nostart (c_p
 	    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;
@@ -7428,6 +7453,7 @@  c_parser_compound_statement_nostart (c_p
 	    }
 	  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))
 	{
@@ -7447,6 +7473,7 @@  c_parser_compound_statement_nostart (c_p
             {
               error_at (loc, "%<else%> without a previous %<if%>");
               c_parser_consume_token (parser);
+	      before_labels = get_before_labels ();
               continue;
             }
         }
@@ -7458,7 +7485,8 @@  c_parser_compound_statement_nostart (c_p
 	  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
@@ -7468,9 +7496,10 @@  c_parser_compound_statement_nostart (c_p
 		 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;
@@ -7782,11 +7811,26 @@  c_parser_label (c_parser *parser, tree s
 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
@@ -7799,6 +7843,7 @@  c_parser_statement (c_parser *parser, bo
 
 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;
@@ -7824,16 +7869,16 @@  c_parser_statement_after_labels (c_parse
 	  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);
@@ -7857,11 +7902,13 @@  c_parser_statement_after_labels (c_parse
 	  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);
@@ -7977,7 +8024,7 @@  c_parser_statement_after_labels (c_parse
       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:
@@ -8072,6 +8119,7 @@  c_parser_if_body (c_parser *parser, bool
   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))
@@ -8088,7 +8136,7 @@  c_parser_if_body (c_parser *parser, bool
   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
@@ -8116,6 +8164,7 @@  c_parser_else_body (c_parser *parser, co
   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))
@@ -8131,7 +8180,7 @@  c_parser_else_body (c_parser *parser, co
     {
       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
@@ -8274,7 +8323,7 @@  c_parser_if_statement (c_parser *parser,
 */
 
 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;
@@ -8283,6 +8332,8 @@  c_parser_switch_statement (c_parser *par
   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;
@@ -8304,9 +8355,18 @@  c_parser_switch_statement (c_parser *par
       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);
@@ -8316,6 +8376,14 @@  c_parser_switch_statement (c_parser *par
 				    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);
 }
@@ -8331,7 +8399,7 @@  c_parser_switch_statement (c_parser *par
 
 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;
@@ -8339,6 +8407,8 @@  c_parser_while_statement (c_parser *pars
   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)
     {
@@ -8368,6 +8438,11 @@  c_parser_while_statement (c_parser *pars
 		   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));
@@ -8375,9 +8450,13 @@  c_parser_while_statement (c_parser *pars
   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));
@@ -8398,12 +8477,14 @@  c_parser_while_statement (c_parser *pars
 
 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)
     {
@@ -8421,9 +8502,20 @@  c_parser_do_statement (c_parser *parser,
   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,
@@ -8443,7 +8535,7 @@  c_parser_do_statement (c_parser *parser,
   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));
 }
 
@@ -8508,7 +8600,7 @@  c_parser_do_statement (c_parser *parser,
 
 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;
@@ -8523,6 +8615,8 @@  c_parser_for_statement (c_parser *parser
   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)
     {
@@ -8715,9 +8809,22 @@  c_parser_for_statement (c_parser *parser
       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));
@@ -8726,6 +8833,16 @@  c_parser_for_statement (c_parser *parser
   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,
@@ -8733,7 +8850,9 @@  c_parser_for_statement (c_parser *parser
 			      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);
@@ -8747,6 +8866,8 @@  c_parser_for_statement (c_parser *parser
 				    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;
@@ -13448,7 +13569,7 @@  c_parser_objc_class_instance_variables (
 	}
       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;
 	}
 
@@ -13719,7 +13840,7 @@  c_parser_objc_methodprotolist (c_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;
@@ -14791,7 +14912,8 @@  c_parser_pragma_unroll (c_parser *parser
    true if we actually parsed such a pragma.  */
 
 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;
@@ -15044,11 +15166,14 @@  c_parser_pragma (c_parser *parser, enum
 	    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;
 
@@ -22771,7 +22896,7 @@  c_parser_omp_loop_nest (c_parser *parser
 	    }
 	  break;
 	default:
-	  c_parser_pragma (parser, pragma_stmt, NULL);
+	  c_parser_pragma (parser, pragma_stmt, NULL, void_list_node);
 	  break;
 	}
       if (transform == NULL_TREE)
@@ -25671,7 +25796,7 @@  c_maybe_parse_omp_decl (tree decl, tree
   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;
 }
--- gcc/c/c-decl.cc.jj	2024-10-07 21:23:25.835994350 +0200
+++ gcc/c/c-decl.cc	2024-10-10 18:16:01.535279006 +0200
@@ -162,6 +162,14 @@  vec<c_omp_declare_target_attr, va_gc> *c
    #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,210 @@  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 == 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"
--- gcc/c/c-typeck.cc.jj	2024-10-09 13:23:03.962530606 +0200
+++ gcc/c/c-typeck.cc	2024-10-10 18:28:09.413296711 +0200
@@ -12288,7 +12288,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;
@@ -12341,7 +12341,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 ();
@@ -12442,7 +12442,7 @@  c_finish_if_stmt (location_t if_locus, t
 }
 
 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
@@ -12454,7 +12454,7 @@  c_finish_bc_stmt (location_t loc, tree l
   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");
@@ -12474,7 +12474,7 @@  c_finish_bc_stmt (location_t loc, tree l
 	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");
@@ -12493,14 +12493,24 @@  c_finish_bc_stmt (location_t loc, tree l
   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.  */
--- gcc/cp/semantics.cc.jj	2024-10-03 20:10:07.360515713 +0200
+++ gcc/cp/semantics.cc	2024-10-10 17:45:40.327267774 +0200
@@ -1368,7 +1368,7 @@  tree
 begin_while_stmt (void)
 {
   tree r;
-  r = build_stmt (input_location, WHILE_STMT, NULL_TREE, NULL_TREE);
+  r = build_stmt (input_location, WHILE_STMT, NULL_TREE, NULL_TREE, NULL_TREE);
   add_stmt (r);
   WHILE_BODY (r) = do_pushlevel (sk_block);
   begin_cond (&WHILE_COND (r));
@@ -1425,7 +1425,8 @@  finish_while_stmt (tree while_stmt)
 tree
 begin_do_stmt (void)
 {
-  tree r = build_stmt (input_location, DO_STMT, NULL_TREE, NULL_TREE);
+  tree r = build_stmt (input_location, DO_STMT, NULL_TREE, NULL_TREE,
+		       NULL_TREE);
   begin_maybe_infinite_loop (boolean_true_node);
   add_stmt (r);
   DO_BODY (r) = push_stmt_list ();
@@ -1546,7 +1547,7 @@  begin_for_stmt (tree scope, tree init)
   tree r;
 
   r = build_stmt (input_location, FOR_STMT, NULL_TREE, NULL_TREE,
-		  NULL_TREE, NULL_TREE, NULL_TREE);
+		  NULL_TREE, NULL_TREE, NULL_TREE, NULL_TREE);
 
   if (scope == NULL_TREE)
     {
@@ -1783,7 +1784,7 @@  finish_break_stmt (void)
   if (!block_may_fallthru (cur_stmt_list))
     return void_node;
   note_break_stmt ();
-  return add_stmt (build_stmt (input_location, BREAK_STMT));
+  return add_stmt (build_stmt (input_location, BREAK_STMT, NULL_TREE));
 }
 
 /* Finish a continue-statement.  */
@@ -1791,7 +1792,7 @@  finish_break_stmt (void)
 tree
 finish_continue_stmt (void)
 {
-  return add_stmt (build_stmt (input_location, CONTINUE_STMT));
+  return add_stmt (build_stmt (input_location, CONTINUE_STMT, NULL_TREE));
 }
 
 /* Begin a switch-statement.  Returns a new SWITCH_STMT if
@@ -1803,7 +1804,8 @@  begin_switch_stmt (void)
   tree r, scope;
 
   scope = do_pushlevel (sk_cond);
-  r = build_stmt (input_location, SWITCH_STMT, NULL_TREE, NULL_TREE, NULL_TREE, scope);
+  r = build_stmt (input_location, SWITCH_STMT, NULL_TREE, NULL_TREE, NULL_TREE,
+		  scope, NULL_TREE);
 
   begin_cond (&SWITCH_STMT_COND (r));
 
--- gcc/testsuite/gcc.dg/c2y-named-loops-1.c.jj	2024-10-10 17:45:40.328267761 +0200
+++ gcc/testsuite/gcc.dg/c2y-named-loops-1.c	2024-10-10 17:45:40.328267761 +0200
@@ -0,0 +1,5 @@ 
+/* N3355 - Named loops.  */
+/* { dg-do compile } */
+/* { dg-options "-std=c2y -pedantic-errors" } */
+
+#include "gnu99-named-loops-1.c"
--- gcc/testsuite/gcc.dg/c2y-named-loops-2.c.jj	2024-10-10 17:45:40.328267761 +0200
+++ gcc/testsuite/gcc.dg/c2y-named-loops-2.c	2024-10-10 17:45:40.328267761 +0200
@@ -0,0 +1,45 @@ 
+/* N3355 - Named loops.  */
+/* { dg-do compile } */
+/* { dg-options "-std=c2y -pedantic-errors" } */
+
+void
+foo (int x)
+{
+ label1:
+  for (int i = 0; i < 16; ++i)
+   another_label1:
+    for (int j = 0; j < 16; ++j)
+      break label2;		/* { dg-error "'break' statement operand 'label2' does not refer to a named loop or 'switch'; did you mean 'label1'\\\?" } */
+  for (int i = 0; i < 16; ++i)
+    break label3;		/* { dg-error "'break' statement operand 'label3' does not refer to a named loop or 'switch'" } */
+ label4:			/* { dg-message "'switch' name defined here" } */
+  switch (x)
+    {
+    case 0:
+      for (int i = 0; i < 16; ++i)
+	continue label5;	/* { dg-error "'continue' statement operand 'label5' does not refer to a named loop" } */
+      break label4;
+    case 1:
+      for (int i = 0; i < 16; ++i)
+	continue label4;	/* { dg-error "'continue' statement operand 'label4' refers to a named 'switch'" } */
+    }
+ label6:
+  for (int i = 0; i < 16; ++i)
+    continue label7;		/* { dg-error "'continue' statement operand 'label7' does not refer to a named loop; did you mean 'label6'\\\?" } */
+ label2:
+  for (int i = 0; i < 16; ++i)
+    ;
+ label8:;
+  for (int i = 0; i < 16; ++i)
+    break label8;		/* { dg-error "'break' statement operand 'label8' does not refer to a named loop or 'switch'" } */
+ label9:;
+  for (int i = 0; i < 16; ++i)
+    continue label9;		/* { dg-error "'continue' statement operand 'label9' does not refer to a named loop" } */
+ label10:
+  ;
+  switch (x)
+    {
+    case 0:
+      break label10;		/* { dg-error "'break' statement operand 'label10' does not refer to a named loop or 'switch'" } */
+    }
+}
--- gcc/testsuite/gcc.dg/c2y-named-loops-4.c.jj	2024-10-10 17:45:40.328267761 +0200
+++ gcc/testsuite/gcc.dg/c2y-named-loops-4.c	2024-10-10 17:45:40.328267761 +0200
@@ -0,0 +1,159 @@ 
+/* N3355 - Named loops.  */
+/* { dg-do run } */
+/* { dg-options "-std=c2y -pedantic-errors" } */
+
+extern void abort (void);
+
+void
+foo (int x)
+{
+  int i, j, k, l, m;
+ label1:
+  for (i = 0; i < 2; ++i)
+    {
+      if (i == 1)
+	{
+	  if (x != 11)
+	    abort ();
+	  return;
+	}
+     label2:
+      switch (i)
+	{
+	 label3:
+	case 0:
+	  for (j = 0; j < 2; ++j)
+	    {
+	      if (j == 1)
+		{
+		  if (x != 8)
+		    abort ();
+		  return;
+		}
+	     label4:
+	      for (k = 0; k < 2; ++k)
+		{
+		  if (k == 1)
+		    {
+		      if (x != 6)
+			abort ();
+		      return;
+		    }
+		  l = 0;
+		 label5:
+		  while (l < 2)
+		    {
+		      if (l == 1)
+			{
+			  if (x != 4)
+			    abort ();
+			  return;
+			}
+		      ++l;
+		      m = 0;
+		     label6:
+		      do
+			{
+			  if (m == 1)
+			    {
+			      if (x != 2)
+				abort ();
+			      return;
+			    }
+			  ++m;
+			 label7:
+			  switch (x)
+			    {
+			    case 0:
+			      break label7;
+			    case 1:
+			      break label6;
+			    case 2:
+			      continue label6;
+			    case 3:
+			      break label5;
+			    case 4:
+			      continue label5;
+			    case 5:
+			      break label4;
+			    case 6:
+			      continue label4;
+			    case 7:
+			      break label3;
+			    case 8:
+			      continue label3;
+			    case 9:
+			      break label2;
+			    case 10:
+			      break label1;
+			    case 11:
+			      continue label1;
+			    default:
+			      abort ();
+			      break;
+			    }
+			  if (x)
+			    abort ();
+			  return;
+			}
+		      while (m < 2);
+		      if (x != 1 || m != 1)
+			abort ();
+		      return;
+		    }
+		  if (x != 3 || l != 1 || m != 1)
+		    abort ();
+		  return;
+		}
+	      if (x != 5 || k != 0 || l != 1 || m != 1)
+		abort ();
+	      return;
+	    }
+	  if (x != 7 || j != 0 || k != 0 || l != 1 || m != 1)
+	    abort ();
+	  return;
+	}
+      if (x != 9 || j != 0 || k != 0 || l != 1 || m != 1)
+	abort ();
+      return;
+    }
+  if (x != 10 || i != 0 || j != 0 || k != 0 || l != 1 || m != 1)
+    abort ();
+}
+
+void
+bar (int x)
+{
+  int i, j;
+ label1:
+  for (i = 0; i < 2; ++i)
+    {
+      if (i == 1)
+	{
+	  if (x != 1)
+	    abort ();
+	  return;
+	}
+      for (j = 0; j < 2; ++j)
+	if (j == 1)
+	  abort ();
+	else if (x == 0)
+	  break label1;
+	else if (x == 1)
+	  continue label1;
+	else
+	  abort ();
+      abort ();
+    }
+  if (x != 0)
+    abort ();
+}
+
+int
+main ()
+{
+  for (int n = 0; n <= 11; ++n)
+    foo (n);
+  bar (0);
+  bar (1);
+}
--- gcc/testsuite/gcc.dg/gnu99-named-loops-1.c.jj	2024-10-10 17:45:40.328267761 +0200
+++ gcc/testsuite/gcc.dg/gnu99-named-loops-1.c	2024-10-10 17:45:40.328267761 +0200
@@ -0,0 +1,102 @@ 
+/* N3355 - Named loops.  */
+/* { dg-do compile } */
+/* { dg-options "-std=gnu99 -Wpedantic" } */
+
+void
+foo (int w)
+{
+  d: e: f:;
+  a: b: c:
+  for (int x = 0; x < 32; ++x)
+    {
+      if (x == 0)
+	continue a;	/* { dg-warning "ISO C does not support 'continue' statement with an identifier operand before" } */
+      else if (x == 1)
+	continue b;	/* { dg-warning "ISO C does not support 'continue' statement with an identifier operand before" } */
+      else if (x == 2)
+	continue c;	/* { dg-warning "ISO C does not support 'continue' statement with an identifier operand before" } */
+      else if (x == 31)
+	break b;	/* { dg-warning "ISO C does not support 'break' statement with an identifier operand before" } */
+    }
+  int y = 0;
+  g: h:
+  #pragma GCC unroll 2
+  while (y < 16)
+    {
+      ++y;
+      if (y == 12)
+	continue g;	/* { dg-warning "ISO C does not support 'continue' statement with an identifier operand before" } */
+      else if (y == 13)
+	continue h;	/* { dg-warning "ISO C does not support 'continue' statement with an identifier operand before" } */
+      else if (y == 14)
+	break g;	/* { dg-warning "ISO C does not support 'break' statement with an identifier operand before" } */
+    }
+  i: j:;
+  k: l:
+  switch (y)
+    {
+    case 6:
+      break;
+    case 7:
+      break k;		/* { dg-warning "ISO C does not support 'break' statement with an identifier operand before" } */
+    case 8:
+      break l;		/* { dg-warning "ISO C does not support 'break' statement with an identifier operand before" } */
+    }
+  m: n: o: p:
+  for (int x = 0; x < 2; ++x)
+    q: r: s: t:
+    switch (x)
+      {
+      case 0:
+	u: v:
+      case 3:
+	w: x:
+	for (int y = 0; y < 2; ++y)
+	  y: z:
+	  for (int z = 0; z < 2; ++z)
+	    aa: ab: ac:
+	    for (int a = 0; a < 2; ++a)
+	      ad: ae: af:
+	      switch (a)
+		{
+		case 0:
+		  if (w == 0)
+		    break ae;		/* { dg-warning "ISO C does not support 'break' statement with an identifier operand before" } */
+		  else if (w == 1)
+		    break ab;		/* { dg-warning "ISO C does not support 'break' statement with an identifier operand before" } */
+		  else if (w == 2)
+		    break z;		/* { dg-warning "ISO C does not support 'break' statement with an identifier operand before" } */
+		  else if (w == 3)
+		    break v;		/* { dg-warning "ISO C does not support 'break' statement with an identifier operand before" } */
+		  else if (w == 4)
+		    break s;		/* { dg-warning "ISO C does not support 'break' statement with an identifier operand before" } */
+		  else if (w == 5)
+		    break p;		/* { dg-warning "ISO C does not support 'break' statement with an identifier operand before" } */
+		  else if (w == 6)
+		    break;
+		  else if (w == 7)
+		    continue aa;	/* { dg-warning "ISO C does not support 'continue' statement with an identifier operand before" } */
+		  else if (w == 8)
+		    continue y;		/* { dg-warning "ISO C does not support 'continue' statement with an identifier operand before" } */
+		  else if (w == 9)
+		    continue x;		/* { dg-warning "ISO C does not support 'continue' statement with an identifier operand before" } */
+		  else if (w == 10)
+		    continue m;		/* { dg-warning "ISO C does not support 'continue' statement with an identifier operand before" } */
+		  ag: ah:
+		  do
+		    {
+		      if (w == 11)
+			break ag;	/* { dg-warning "ISO C does not support 'break' statement with an identifier operand before" } */
+		      else
+			continue ah;	/* { dg-warning "ISO C does not support 'continue' statement with an identifier operand before" } */
+		    }
+		  while (0);
+		  break;
+		default:
+		  break;
+		}
+	break;
+      default:
+	break;
+      }
+}
--- gcc/testsuite/gcc.dg/gnu99-named-loops-2.c.jj	2024-10-10 17:45:40.327267774 +0200
+++ gcc/testsuite/gcc.dg/gnu99-named-loops-2.c	2024-10-10 17:45:40.327267774 +0200
@@ -0,0 +1,45 @@ 
+/* N3355 - Named loops.  */
+/* { dg-do compile } */
+/* { dg-options "-std=gnu99" } */
+
+void
+foo (int x)
+{
+ label1:
+  for (int i = 0; i < 16; ++i)
+   another_label1:
+    for (int j = 0; j < 16; ++j)
+      break label2;		/* { dg-error "'break' statement operand 'label2' does not refer to a named loop or 'switch'; did you mean 'label1'\\\?" } */
+  for (int i = 0; i < 16; ++i)
+    break label3;		/* { dg-error "'break' statement operand 'label3' does not refer to a named loop or 'switch'" } */
+ label4:			/* { dg-message "'switch' name defined here" } */
+  switch (x)
+    {
+    case 0:
+      for (int i = 0; i < 16; ++i)
+	continue label5;	/* { dg-error "'continue' statement operand 'label5' does not refer to a named loop" } */
+      break label4;
+    case 1:
+      for (int i = 0; i < 16; ++i)
+	continue label4;	/* { dg-error "'continue' statement operand 'label4' refers to a named 'switch'" } */
+    }
+ label6:
+  for (int i = 0; i < 16; ++i)
+    continue label7;		/* { dg-error "'continue' statement operand 'label7' does not refer to a named loop; did you mean 'label6'\\\?" } */
+ label2:
+  for (int i = 0; i < 16; ++i)
+    ;
+ label8:;
+  for (int i = 0; i < 16; ++i)
+    break label8;		/* { dg-error "'break' statement operand 'label8' does not refer to a named loop or 'switch'" } */
+ label9:;
+  for (int i = 0; i < 16; ++i)
+    continue label9;		/* { dg-error "'continue' statement operand 'label9' does not refer to a named loop" } */
+ label10:
+  ;
+  switch (x)
+    {
+    case 0:
+      break label10;		/* { dg-error "'break' statement operand 'label10' does not refer to a named loop or 'switch'" } */
+    }
+}
--- gcc/testsuite/gcc.dg/gnu99-named-loops-3.c.jj	2024-10-10 17:45:40.328267761 +0200
+++ gcc/testsuite/gcc.dg/gnu99-named-loops-3.c	2024-10-10 17:45:40.328267761 +0200
@@ -0,0 +1,117 @@ 
+/* N3355 - Named loops.  */
+/* { dg-do compile } */
+/* { dg-options "-std=gnu99" } */
+
+void
+foo (int x)
+{
+  for (int i = 0; i < 16; ++i)
+    {
+      int k;
+     label1:									/* { dg-message "loop name defined here" } */
+      for (int j = ({ if (x == 0) break label1; 0; }); j < 16; ++j)		/* { dg-error "'break' statement operand 'label1' refers to a loop outside of its body" } */
+	;
+     label2:									/* { dg-message "loop name defined here" } */
+      for (int j = ({ if (x == 1) continue label2; 0; }); j < 16; ++j)		/* { dg-error "'continue' statement operand 'label2' refers to a loop outside of its body" } */
+	;
+     label3:									/* { dg-message "loop name defined here" } */
+      for (int j = 0; j < ({ if (x == 2) break label3; 16; }); ++j)		/* { dg-error "'break' statement operand 'label3' refers to a loop outside of its body" } */
+	;
+     label4:									/* { dg-message "loop name defined here" } */
+      for (int j = 0; j < ({ if (x == 3) continue label4; 16; }); ++j)		/* { dg-error "'continue' statement operand 'label4' refers to a loop outside of its body" } */
+	;
+     label5:									/* { dg-message "loop name defined here" } */
+      for (int j = 0; j < 16; j += ({ if (x == 4) break label5; 1; }))		/* { dg-error "'break' statement operand 'label5' refers to a loop outside of its body" } */
+	;
+     label6:									/* { dg-message "loop name defined here" } */
+      for (int j = 0; j < 16; j += ({ if (x == 5) continue label6; 1; }))	/* { dg-error "'continue' statement operand 'label6' refers to a loop outside of its body" } */
+	;
+      k = 0;
+     label7:									/* { dg-message "loop name defined here" } */
+      while (k < ({ if (x == 6) break label7; 16; }))				/* { dg-error "'break' statement operand 'label7' refers to a loop outside of its body" } */
+	++k;
+      k = 0;
+     label8:									/* { dg-message "loop name defined here" } */
+      while (k < ({ if (x == 7) continue label8; 16; }))			/* { dg-error "'continue' statement operand 'label8' refers to a loop outside of its body" } */
+	++k;
+      k = 0;
+     label9:
+      do
+	++k;
+      while (k <= ({ if (x == 8) break label9; 16; }));				/* { dg-error "'break' statement operand 'label9' does not refer to a named loop or 'switch'" } */
+      k = 0;
+     label10:
+      do
+	++k;
+      while (k <= ({ if (x == 9) continue label10; 16; }));			/* { dg-error "'continue' statement operand 'label10' does not refer to a named loop" } */
+     label11:									/* { dg-message "'switch' name defined here" } */
+      switch (x + ({ if (x == 10) break label11; 0; }))				/* { dg-error "'break' statement operand 'label11' refers to a 'switch' outside of its body" } */
+	{
+	case 0:
+	  break;
+	}
+    }
+ label12:
+ label13:
+ label14:
+  for (int i = 0; i < 32; ++i)
+    {
+     label15:
+      switch (i)
+	{
+	 label16:
+	case 0:
+	 label17:
+	 label18:
+	 label19:
+	 label20:
+	 label21:
+	 label22:
+	 label23:
+	 label24:
+	 label25:
+	 label26:
+	 label27:
+	 label28:
+	 label29:
+	 label30:
+	  for (int j = 0; j < 32; ++j)
+	    {
+	      if (j == 31)
+		continue label14;
+	      else if (j == 30)
+		break label15;
+	      void bar (void)
+	      {
+	       label31:
+		for (int k = 0; k < 32; ++k)
+		  if (k == 31)
+		    continue label31;
+		  else if (k == 30)
+		    break label31;
+		  else if (k == 29)
+		    continue label22;						/* { dg-error "'continue' statement operand 'label22' does not refer to a named loop; did you mean 'label31'\\\?" } */
+		  else if (k == 28)
+		    break label20;						/* { dg-error "'break' statement operand 'label20' does not refer to a named loop or 'switch'; did you mean 'label31'\\\?" } */
+		  else if (k == 27)
+		    break label15;						/* { dg-error "'break' statement operand 'label15' does not refer to a named loop or 'switch'; did you mean 'label31'\\\?" } */
+		  else if (k == 26)
+		    continue label13;						/* { dg-error "'continue' statement operand 'label13' does not refer to a named loop; did you mean 'label31'\\\?" } */
+		  else if (k == 25)
+		    break label12;						/* { dg-error "'break' statement operand 'label12' does not refer to a named loop or 'switch'; did you mean 'label31'\\\?" } */
+	      }
+	      bar ();
+	      if (j == 29)
+		continue label22;
+	      else if (j == 28)
+		break label20;
+	      else if (j == 27)
+		break label15;
+	      else if (j == 26)
+		continue label13;
+	      else if (j == 25)
+		break label12;
+	    }
+	}
+    }
+}
--- gcc/testsuite/gcc.dg/gnu99-named-loops-4.c.jj	2024-10-10 17:45:40.328267761 +0200
+++ gcc/testsuite/gcc.dg/gnu99-named-loops-4.c	2024-10-10 17:45:40.328267761 +0200
@@ -0,0 +1,5 @@ 
+/* N3355 - Named loops.  */
+/* { dg-do run } */
+/* { dg-options "-std=gnu99" } */
+
+#include "c2y-named-loops-4.c"
--- gcc/testsuite/gcc.dg/gnu2y-named-loops-3.c.jj	2024-10-10 17:45:40.328267761 +0200
+++ gcc/testsuite/gcc.dg/gnu2y-named-loops-3.c	2024-10-10 17:45:40.328267761 +0200
@@ -0,0 +1,117 @@ 
+/* N3355 - Named loops.  */
+/* { dg-do compile } */
+/* { dg-options "-std=gnu2y" } */
+
+void
+foo (int x)
+{
+  for (int i = 0; i < 16; ++i)
+    {
+      int k;
+     label1:									/* { dg-message "loop name defined here" } */
+      for (int j = ({ if (x == 0) break label1; 0; }); j < 16; ++j)		/* { dg-error "'break' statement operand 'label1' refers to a loop outside of its body" } */
+	;
+     label2:									/* { dg-message "loop name defined here" } */
+      for (int j = ({ if (x == 1) continue label2; 0; }); j < 16; ++j)		/* { dg-error "'continue' statement operand 'label2' refers to a loop outside of its body" } */
+	;
+     label3:									/* { dg-message "loop name defined here" } */
+      for (int j = 0; j < ({ if (x == 2) break label3; 16; }); ++j)		/* { dg-error "'break' statement operand 'label3' refers to a loop outside of its body" } */
+	;
+     label4:									/* { dg-message "loop name defined here" } */
+      for (int j = 0; j < ({ if (x == 3) continue label4; 16; }); ++j)		/* { dg-error "'continue' statement operand 'label4' refers to a loop outside of its body" } */
+	;
+     label5:									/* { dg-message "loop name defined here" } */
+      for (int j = 0; j < 16; j += ({ if (x == 4) break label5; 1; }))		/* { dg-error "'break' statement operand 'label5' refers to a loop outside of its body" } */
+	;
+     label6:									/* { dg-message "loop name defined here" } */
+      for (int j = 0; j < 16; j += ({ if (x == 5) continue label6; 1; }))	/* { dg-error "'continue' statement operand 'label6' refers to a loop outside of its body" } */
+	;
+      k = 0;
+     label7:									/* { dg-message "loop name defined here" } */
+      while (k < ({ if (x == 6) break label7; 16; }))				/* { dg-error "'break' statement operand 'label7' refers to a loop outside of its body" } */
+	++k;
+      k = 0;
+     label8:									/* { dg-message "loop name defined here" } */
+      while (k < ({ if (x == 7) continue label8; 16; }))			/* { dg-error "'continue' statement operand 'label8' refers to a loop outside of its body" } */
+	++k;
+      k = 0;
+     label9:
+      do
+	++k;
+      while (k <= ({ if (x == 8) break label9; 16; }));				/* { dg-error "'break' statement operand 'label9' does not refer to a named loop or 'switch'" } */
+      k = 0;
+     label10:
+      do
+	++k;
+      while (k <= ({ if (x == 9) continue label10; 16; }));			/* { dg-error "'continue' statement operand 'label10' does not refer to a named loop" } */
+     label11:									/* { dg-message "'switch' name defined here" } */
+      switch (x + ({ if (x == 10) break label11; 0; }))				/* { dg-error "'break' statement operand 'label11' refers to a 'switch' outside of its body" } */
+	{
+	case 0:
+	  break;
+	}
+    }
+ label12:
+ label13:
+ label14:
+  for (int i = 0; i < 32; ++i)
+    {
+     label15:
+      switch (i)
+	{
+	 label16:
+	case 0:
+	 label17:
+	 label18:
+	 label19:
+	 label20:
+	 label21:
+	 label22:
+	 label23:
+	 label24:
+	 label25:
+	 label26:
+	 label27:
+	 label28:
+	 label29:
+	 label30:
+	  for (int j = 0; j < 32; ++j)
+	    {
+	      if (j == 31)
+		continue label14;
+	      else if (j == 30)
+		break label15;
+	      void bar (void)
+	      {
+	       label31:
+		for (int k = 0; k < 32; ++k)
+		  if (k == 31)
+		    continue label31;
+		  else if (k == 30)
+		    break label31;
+		  else if (k == 29)
+		    continue label22;						/* { dg-error "'continue' statement operand 'label22' does not refer to a named loop; did you mean 'label31'\\\?" } */
+		  else if (k == 28)
+		    break label20;						/* { dg-error "'break' statement operand 'label20' does not refer to a named loop or 'switch'; did you mean 'label31'\\\?" } */
+		  else if (k == 27)
+		    break label15;						/* { dg-error "'break' statement operand 'label15' does not refer to a named loop or 'switch'; did you mean 'label31'\\\?" } */
+		  else if (k == 26)
+		    continue label13;						/* { dg-error "'continue' statement operand 'label13' does not refer to a named loop; did you mean 'label31'\\\?" } */
+		  else if (k == 25)
+		    break label12;						/* { dg-error "'break' statement operand 'label12' does not refer to a named loop or 'switch'; did you mean 'label31'\\\?" } */
+	      }
+	      bar ();
+	      if (j == 29)
+		continue label22;
+	      else if (j == 28)
+		break label20;
+	      else if (j == 27)
+		break label15;
+	      else if (j == 26)
+		continue label13;
+	      else if (j == 25)
+		break label12;
+	    }
+	}
+    }
+}
--- gcc/testsuite/gcc.dg/gomp/named-loops-1.c.jj	2024-10-10 17:45:40.327267774 +0200
+++ gcc/testsuite/gcc.dg/gomp/named-loops-1.c	2024-10-10 17:45:40.327267774 +0200
@@ -0,0 +1,101 @@ 
+/* Cases which will be IMHO always invalid in OpenMP,
+   just perhaps could have different diagnostic wording.  */
+/* { dg-do compile } */
+/* { dg-options "-std=c2y -fopenmp" } */
+
+void
+foo ()
+{
+ label1:
+  for (int i = 0; i < 32; ++i)
+    #pragma omp parallel for
+    for (int j = 0; j < 32; ++j)
+      {
+	if (j == 31)
+	  break label1;			/* { dg-error "break statement used with OpenMP for loop" } */
+	else if (j == 30)
+	  continue label1;		/* { dg-error "invalid branch to/from OpenMP structured block" } */
+       label2:
+	for (int k = 0; k < 32; ++k)
+	  if (k == 31)
+	    break label2;
+	  else if (k == 30)
+	    continue label2;
+	  else if (k == 29)
+	    break label1;		/* { dg-error "invalid branch to/from OpenMP structured block" } */
+	  else if (k == 28)
+	    continue label1;		/* { dg-error "invalid branch to/from OpenMP structured block" } */
+      }
+}
+
+void
+bar ()
+{
+ label1:
+  #pragma omp parallel for
+  for (int i = 0; i < 32; ++i)
+    if (i == 31)
+      break label1;			/* { dg-error "break' statement operand 'label1' does not refer to a named loop or 'switch'" } */
+					/* { dg-error "break statement used with OpenMP for loop" "" { target *-*-* } .-1 } */
+ label2:
+  #pragma omp parallel for collapse(2)
+  for (int i = 0; i < 32; ++i)
+    for (int j = 0; j < 32; ++j)
+      if (i == 31 && j == 31)
+	break label2;			/* { dg-error "'break' statement operand 'label2' does not refer to a named loop or 'switch'" } */
+					/* { dg-error "break statement used with OpenMP for loop" "" { target *-*-* } .-1 } */
+      else if (i == 31 && j == 30)
+	continue label2;		/* { dg-error "'continue' statement operand 'label2' does not refer to a named loop" } */
+}
+
+void
+baz ()
+{
+ label1:
+  [[omp::directive (parallel for)]]
+  for (int i = 0; i < 32; ++i)
+    if (i == 31)
+      break label1;			/* { dg-error "break' statement operand 'label1' does not refer to a named loop or 'switch'" } */
+					/* { dg-error "break statement used with OpenMP for loop" "" { target *-*-* } .-1 } */
+ label2:
+  [[omp::directive (parallel for, collapse(2))]]
+  for (int i = 0; i < 32; ++i)
+    for (int j = 0; j < 32; ++j)
+      if (i == 31 && j == 31)
+	break label2;			/* { dg-error "'break' statement operand 'label2' does not refer to a named loop or 'switch'" } */
+					/* { dg-error "break statement used with OpenMP for loop" "" { target *-*-* } .-1 } */
+      else if (i == 31 && j == 30)
+	continue label2;		/* { dg-error "'continue' statement operand 'label2' does not refer to a named loop" } */
+}
+
+void
+qux ()
+{
+ label1:
+  #pragma omp parallel for collapse(2)
+  for (int i = 0; i < 32; ++i)		/* { dg-error "not enough nested loops" } */
+   label2:
+    for (int j = 0; j < 32; ++j)
+      if (j == 31)
+	break label1;			/* { dg-error "'break' statement operand 'label1' does not refer to a named loop or 'switch'; did you mean 'label2'\\\?" } */
+      else if (j == 30)
+	continue label1;		/* { dg-error "'continue' statement operand 'label1' does not refer to a named loop; did you mean 'label2'\\\?" } */
+      else if (j == 29)
+	break label2;			/* This is IMHO invalid too and currently just diagnosed by the not enough nested loops.  */
+}
+
+void
+garply ()
+{
+ label1:
+  [[omp::directive (parallel for, collapse(2))]]
+  for (int i = 0; i < 32; ++i)		/* { dg-error "not enough nested loops" } */
+   label2:
+    for (int j = 0; j < 32; ++j)
+      if (j == 31)
+	break label1;			/* { dg-error "'break' statement operand 'label1' does not refer to a named loop or 'switch'; did you mean 'label2'\\\?" } */
+      else if (j == 30)
+	continue label1;		/* { dg-error "'continue' statement operand 'label1' does not refer to a named loop; did you mean 'label2'\\\?" } */
+      else if (j == 29)
+	break label2;			/* This is IMHO invalid too and currently just diagnosed by the not enough nested loops.  */
+}
--- gcc/testsuite/gcc.dg/gomp/named-loops-2.c.jj	2024-10-10 17:45:40.328267761 +0200
+++ gcc/testsuite/gcc.dg/gomp/named-loops-2.c	2024-10-10 17:45:40.328267761 +0200
@@ -0,0 +1,82 @@ 
+/* Cases which perhaps could be valid in OpenMP one day, but aren't
+   accepted right now.  */
+/* { dg-do compile } */
+/* { dg-options "-std=c2y -fopenmp" } */
+
+void
+foo ()
+{
+ label1:
+  #pragma omp parallel for
+  for (int i = 0; i < 32; ++i)
+    if (i == 31)
+      continue label1;			/* { dg-error "'continue' statement operand 'label1' does not refer to a named loop" } */
+    else
+      {
+       label2:
+	for (int j = 0; j < 32; ++j)
+	  if (j == 31)
+	    continue label2;
+	  else if (j == 30)
+	    break label2;
+	  else if (j == 29)
+	    continue label1;		/* { dg-error "'continue' statement operand 'label1' does not refer to a named loop; did you mean 'label2'\\\?" } */
+      }
+}
+
+void
+bar ()
+{
+ label1:
+  [[omp::directive (parallel for)]]
+  for (int i = 0; i < 32; ++i)
+    if (i == 31)
+      continue label1;			/* { dg-error "'continue' statement operand 'label1' does not refer to a named loop" } */
+    else
+      {
+       label2:
+	for (int j = 0; j < 32; ++j)
+	  if (j == 31)
+	    continue label2;
+	  else if (j == 30)
+	    break label2;
+	  else if (j == 29)
+	    continue label1;		/* { dg-error "'continue' statement operand 'label1' does not refer to a named loop; did you mean 'label2'\\\?" } */
+      }
+}
+
+void
+baz ()
+{
+ label1:
+  #pragma omp parallel for collapse(2)
+  for (int i = 0; i < 32; ++i)		/* { dg-error "not enough nested loops" } */
+   label2:
+    for (int j = 0; j < 32; ++j)
+     label3:
+      for (int k = 0; k < 32; ++k)
+	if (k == 31)
+	  continue label3;
+	else if (k == 30)
+	  break label3;
+	else if (k == 29)
+	  continue label2;
+}
+
+void
+qux ()
+{
+ label1:
+  [[omp::directive (parallel for, collapse(2))]]
+  for (int i = 0; i < 32; ++i)		/* { dg-error "not enough nested loops" } */
+   label2:
+    for (int j = 0; j < 32; ++j)
+     label3:
+      for (int k = 0; k < 32; ++k)
+	if (k == 31)
+	  continue label3;
+	else if (k == 30)
+	  break label3;
+	else if (k == 29)
+	  continue label2;
+}
--- gcc/testsuite/objc.dg/named-loops-1.m.jj	2024-10-10 17:55:33.976125322 +0200
+++ gcc/testsuite/objc.dg/named-loops-1.m	2024-10-10 18:12:37.605076906 +0200
@@ -0,0 +1,172 @@ 
+/* Test basic Objective-C foreach syntax.  This tests iterations, with
+   the basic syntax 'for (object in array) statements'
+*/
+/* { dg-do run } */
+/* { dg-skip-if "No NeXT fast enum. pre-Darwin9" { *-*-darwin[5-8]* } { "-fnext-runtime" } { "" } } */
+/* { dg-xfail-run-if "Needs OBJC2 ABI" { *-*-darwin* && { lp64 && { ! objc2 } } } { "-fnext-runtime" } { "" } } */
+/* { dg-options "-mno-constant-cfstrings" { target *-*-darwin* } } */
+/* { dg-additional-sources "../objc-obj-c++-shared/nsconstantstring-class-impl.m" } */
+/* { dg-additional-options "-Wno-objc-root-class" } */
+
+#include "../objc-obj-c++-shared/TestsuiteObject.m"
+#ifndef __NEXT_RUNTIME__
+#include <objc/NXConstStr.h>
+#else
+#include "../objc-obj-c++-shared/nsconstantstring-class.h"
+#endif
+
+extern int printf (const char *, ...);
+#include <stdlib.h>
+
+/*
+struct __objcFastEnumerationState
+{
+  unsigned long state;
+  id            *itemsPtr;
+  unsigned long *mutationsPtr;
+  unsigned long extra[5];
+};
+*/
+
+ /* A mini-array implementation that can be used to test fast
+    enumeration.  You create the array with some objects; you can
+    mutate the array, and you can fast-enumerate it.
+ */
+@interface MyArray : TestsuiteObject
+{
+  unsigned int length;
+  id *objects;
+  unsigned long mutated;
+}
+- (id) initWithLength: (unsigned int)l  objects: (id *)o;
+- (void) mutate;
+- (unsigned long)countByEnumeratingWithState: (struct __objcFastEnumerationState *)state
+				     objects:(id *)stackbuf
+				       count:(unsigned long)len;
+@end
+
+@implementation MyArray : TestsuiteObject
+- (id) initWithLength: (unsigned int)l
+	      objects: (id *)o
+{
+  length = l;
+  objects = o;
+  mutated = 0;
+  return self;
+}
+- (void) mutate
+{
+  mutated = 1;
+}
+- (unsigned long)countByEnumeratingWithState: (struct __objcFastEnumerationState*)state
+		  		     objects: (id*)stackbuf
+			 	       count: (unsigned long)len
+{
+  unsigned long i, batch_size;
+
+  /* We keep how many objects we served in the state->state counter.  So the next batch
+     will contain up to length - state->state objects.  */
+  batch_size = length - state->state;
+
+  /* Make obvious adjustments.  */
+  if (batch_size < 0)
+    batch_size = 0;
+
+  if (batch_size > len)
+    batch_size = len;
+
+  /* Copy the objects.  */
+  for (i = 0; i < batch_size; i++)
+    stackbuf[i] = objects[i];
+
+  state->state += batch_size;
+  state->itemsPtr = stackbuf;
+  state->mutationsPtr = &mutated;
+
+  return batch_size;
+}
+@end
+
+void
+foo (MyArray *array, int x)
+{
+  TestsuiteObject *object;
+  int i, j, k;
+
+ label1:
+  for (i = 0; i < 2; ++i)
+    {
+      if (i == 1)
+	{
+	  if (x != 5)
+	    abort ();
+	  return;
+	}
+      k = 0;
+     label2:
+      for (object in array)
+	{
+	  if (k == 1)
+	    {
+	      if (x != 3)
+		abort ();
+	      return;
+	    }
+	  ++k;
+	 label3:
+	  for (j = 0; j < 2; ++j)
+	    {
+	      if (j == 1)
+		{
+		  if (x != 1)
+		    abort ();
+		  return;
+		}
+	     label4:
+	      switch (x)
+		{
+		case 0:
+		  break label4;
+		case 1:
+		  continue label3;
+		case 2:
+		  break label3;
+		case 3:
+		  continue label2;
+		case 4:
+		  break label2;
+		case 5:
+		  continue label1;
+		default:
+		  break label1;
+		}
+	      if (x != 0)
+		abort ();
+	      return;
+	    }
+	  if (x != 2)
+	    abort ();
+	  return;
+	}
+      if (x != 4)
+	abort ();
+      return;
+    }
+  if (x <= 5)
+    abort ();
+}
+
+int
+main ()
+{
+  MyArray *array;
+  id objects[2] = { @"object1", @"object2" };
+  int i;
+
+  array = [[MyArray alloc] initWithLength: 2
+			   objects: objects];
+  for (i = 0; i < 6; ++i)
+    foo (array, i);
+
+  return 0;
+}