diff mbox series

c, v3: Implement C2Y N3355 - Named Loops [PR117022]

Message ID Zw6n0zIMda2/CwTs@tucnak
State New
Headers show
Series c, v3: Implement C2Y N3355 - Named Loops [PR117022] | expand

Commit Message

Jakub Jelinek Oct. 15, 2024, 5:35 p.m. UTC
On Tue, Oct 15, 2024 at 05:00:04PM +0000, Joseph Myers wrote:
> What happens with a statement attribute on the iteration or switch 
> statement?
> 
>   label: [[]] for (;;) break label;

Except for the omp::directive/omp::sequence attributes label is accepted
as loop label.
Those OpenMP attributes make the label ignored right now (it effectively
is parsed as if there is a #pragma omp in between and loops are parsed
completely differently).

> There are no standard statement attributes (and the only non-OMP one we 
> handle is musttail, which isn't applicable on such statements) so this 
> example uses [[]].  The wording in the paper about "is an iteration or 
> switch statement" isn't wonderfully clear about the case of an iteration 
> or switch statement with attributes on it (and while we discussed the 
> pragma case in WG14, the attribute case wasn't mentioned).  Whatever we 
> do, there should be an associated test.

Added test coverage.

> >  /* Parse a statement, other than a labeled statement.  CHAIN is a vector
> > @@ -7662,6 +7706,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)
> 
> Should update the comment on this function to mention the new parameter.  
> Likewise all other functions with this parameter added.

Done.

Here is a new version of the patch, tested on the dg.exp=*named-loops*
tests fine, I think it doesn't need more testing given that it is just
comment changes in code plus testsuite changes.

2024-10-12  Jakub Jelinek  <jakub@redhat.com>

	PR c/117022
gcc/c-family/
	* c-common.def (FOR_STMT, WHILE_STMT, DO_STMT, BREAK_STMT,
	CONTINUE_STMT, SWITCH_STMT): Add an extra operand, *_NAME
	and document it.
	* c-common.h (bc_hash_map_t): New typedef.
	(struct bc_state): Add bc_hash_map member.
	(WHILE_NAME, DO_NAME, FOR_NAME, BREAK_NAME, CONTINUE_NAME,
	SWITCH_STMT_NAME): Define.
	* c-pretty-print.cc (c_pretty_printer::statement): Print
	BREAK_STMT or CONTINUE_STMT operand if any.
	* c-gimplify.cc (bc_hash_map): New static variable.
	(note_named_bc, release_named_bc): New functions.
	(save_bc_state): Save and clear bc_hash_map.
	(restore_bc_state): Assert NULL and restore bc_hash_map.
	(genericize_c_loop): Add NAME argument, call note_named_bc
	and release_named_bc if non-NULL around the body walk.
	(genericize_for_stmt, genericize_while_stmt, genericize_do_stmt):
	Adjust callers of it.
	(genericize_switch_stmt): Rename break_block variable to blab.
	Call note_named_bc and release_named_bc if SWITCH_STMT_NAME is
	non-NULL around the body walk.
	(genericize_continue_stmt): Handle non-NULL CONTINUE_NAME.
	(genericize_break_stmt): Handle non-NULL BREAK_NAME.
	(c_genericize): Delete and clear bc_hash_map.
gcc/c/
	* c-tree.h: Implement C2Y N3355 - Named loops.
	(C_DECL_LOOP_NAME, C_DECL_SWITCH_NAME, C_DECL_LOOP_SWITCH_NAME_VALID,
	C_DECL_LOOP_SWITCH_NAME_USED, IN_NAMED_STMT): Define.
	(c_get_loop_names, c_release_loop_names, c_finish_bc_name): Declare.
	(c_start_switch): Add NAME argument.
	(c_finish_bc_stmt): Likewise.
	* c-lang.h (struct language_function): Add loop_names and
	loop_names_hash members.
	* c-parser.cc (c_parser_external_declaration,
	c_parser_declaration_or_fndef, c_parser_struct_or_union_specifier,
	c_parser_parameter_declaration): Adjust c_parser_pragma caller.
	(get_before_labels): New function.
	(c_parser_compound_statement_nostart): Call get_before_labels when
	needed, adjust c_parser_pragma and c_parser_statement_after_labels
	callers.
	(c_parser_statement): Call get_before_labels first and pass it to
	c_parser_statement_after_labels.
	(c_parser_bc_name): New function.
	(c_parser_statement_after_labels): Add BEFORE_LABELS argument.  Pass
	it down to c_parser_switch_statement, c_parser_while_statement,
	c_parser_do_statement, c_parser_for_statement and c_parser_pragma.
	Call c_parser_bc_name for RID_BREAK and RID_CONTINUE and pass it as
	another argument to c_finish_bc_stmt.
	(c_parser_if_body, c_parser_else_body): Call get_before_labels
	early and pass it to c_parser_statement_after_labels.
	(c_parser_switch_statement): Add BEFORE_LABELS argument.  Call
	c_get_loop_names, if named, pass switch_name to c_start_switch,
	mark it valid and set IN_NAMED_STMT bit in in_statement before
	parsing body, otherwise clear IN_NAMED_STMT bit before that parsing.
	Run c_release_loop_names at the end.
	(c_parser_while_statement, c_parser_do_statement,
	c_parser_for_statement): Add BEFORE_LABELS argument.  Call
	c_get_loop_names, if named, mark it valid and set IN_NAMED_STMT bit
	in in_statement before parsing body, otherwise clear IN_NAMED_STMT
	before that parsing, arrange for the loop name if used to be another
	*_STMT argument.
	(c_parser_objc_class_instance_variables,
	c_parser_objc_methodprotolist): Adjust c_parser_pragma callers.
	(c_parser_pragma): Add BEFORE_LABELS argument.  Pass it down to
	c_parser_for_statement, c_parser_while_statement or
	c_parser_do_statement.
	(c_parser_omp_loop_nest, c_maybe_parse_omp_decl): Adjust
	c_parser_pragma callers.
	* c-decl.cc (loop_names, loop_names_hash): New static variables.
	(add_stmt): Set STATEMENT_LIST_HAS_LABEL after push_stmt_list rather
	than before it.
	(c_push_function_context): Save and clear loop_names and
	loop_names_hash.
	(c_pop_function_context): Release or delete, restore and clear
	loop_names and loop_names_hash.
	(c_get_loop_names, c_release_loop_names, c_finish_bc_name): New
	functions.
	* c-typeck.cc (c_start_switch): Add SWITCH_NAME argument, pass it down
	to build_stmt.
	(c_finish_bc_stmt): Add NAME argument.  Mark of IN_NAMED_STMT bit
	of in_statement in swtiches.  Use label for IN_OBJC_FOREACH only if
	name is NULL.  If name is non-NULL and C_DECL_LOOP_NAME and
	C_DECL_SWITCH_NAME are both set, assume outer ObjC foreach and
	dig labels from DECL_CHAIN of name.  Pass NAME to build_stmt
	otherwise.
gcc/cp/
	* semantics.cc (begin_while_stmt, begin_do_stmt, begin_for_stmt,
	finish_break_stmt, finish_continue_stmt, begin_switch_stmt): Pass
	another NULL_TREE to build_stmt calls.
gcc/testsuite/
	* gcc.dg/c23-named-loops-1.c: New test.
	* gcc.dg/c23-named-loops-5.c: New test.
	* gcc.dg/c2y-named-loops-1.c: New test.
	* gcc.dg/c2y-named-loops-2.c: New test.
	* gcc.dg/c2y-named-loops-4.c: New test.
	* gcc.dg/c2y-named-loops-5.c: New test.
	* gcc.dg/c2y-named-loops-6.c: New test.
	* gcc.dg/c2y-named-loops-7.c: New test.
	* gcc.dg/gnu99-named-loops-1.c: New test.
	* gcc.dg/gnu99-named-loops-2.c: New test.
	* gcc.dg/gnu99-named-loops-3.c: New test.
	* gcc.dg/gnu99-named-loops-4.c: New test.
	* gcc.dg/gnu2y-named-loops-3.c: New test.
	* gcc.dg/gomp/named-loops-1.c: New test.
	* gcc.dg/gomp/named-loops-2.c: New test.
	* objc.dg/named-loops-1.m: New test.



	Jakub

Comments

Joseph Myers Oct. 15, 2024, 6:18 p.m. UTC | #1
On Tue, 15 Oct 2024, Jakub Jelinek wrote:

> Here is a new version of the patch, tested on the dg.exp=*named-loops*
> tests fine, I think it doesn't need more testing given that it is just
> comment changes in code plus testsuite changes.

This version is OK.
diff mbox series

Patch

--- gcc/c-family/c-common.def.jj	2024-10-12 10:40:15.807536031 +0200
+++ gcc/c-family/c-common.def	2024-10-15 19:10:03.854008532 +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-10-12 10:40:15.841535558 +0200
+++ gcc/c-family/c-common.h	2024-10-15 19:10:03.854008532 +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-10-12 10:40:15.932534293 +0200
+++ gcc/c-family/c-pretty-print.cc	2024-10-15 19:10:03.855008518 +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-10-12 10:40:15.883534974 +0200
+++ gcc/c-family/c-gimplify.cc	2024-10-15 19:10:03.856008504 +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-12 10:40:16.164531067 +0200
+++ gcc/c/c-tree.h	2024-10-15 19:10:03.856008504 +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);
@@ -812,7 +836,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);
@@ -828,7 +852,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-10-12 10:40:16.047532694 +0200
+++ gcc/c/c-lang.h	2024-10-15 19:10:03.856008504 +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-15 19:09:09.716747876 +0200
+++ gcc/c/c-parser.cc	2024-10-15 19:13:39.533066744 +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))
@@ -7013,6 +7014,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).  */
@@ -7089,6 +7108,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;
@@ -7119,6 +7139,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
@@ -7132,7 +7153,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)
 		{
@@ -7151,6 +7172,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))
@@ -7161,6 +7183,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;
 	    }
 	}
@@ -7222,6 +7245,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))
 	{
@@ -7263,6 +7287,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;
@@ -7282,7 +7307,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;
@@ -7291,6 +7316,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))
 	{
@@ -7310,6 +7336,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;
             }
         }
@@ -7321,7 +7348,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
@@ -7331,9 +7359,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;
@@ -7645,11 +7674,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
@@ -7658,10 +7702,14 @@  c_parser_statement (c_parser *parser, bo
 
    IF_P is used to track whether there's a (possibly labeled) if statement
    which is not enclosed in braces and has an else clause.  This is used to
-   implement -Wparentheses.  ASTATE is an earlier parsed attribute state.  */
+   implement -Wparentheses.  ASTATE is an earlier parsed attribute state.
+
+   BEFORE_LABELS is last statement before possible labels, see
+   get_before_labels description for details.  */
 
 static void
 c_parser_statement_after_labels (c_parser *parser, bool *if_p,
+				 tree before_labels,
 				 vec<tree> *chain, attr_state astate)
 {
   location_t loc = c_parser_peek_token (parser)->location;
@@ -7687,16 +7735,16 @@  c_parser_statement_after_labels (c_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);
@@ -7720,11 +7768,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);
@@ -7840,7 +7890,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:
@@ -7935,6 +7985,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))
@@ -7951,7 +8002,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
@@ -7979,6 +8030,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))
@@ -7994,7 +8046,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
@@ -8134,10 +8186,12 @@  c_parser_if_statement (c_parser *parser,
 
    switch-statement:
      switch (expression) statement
-*/
+
+   BEFORE_LABELS is last statement before possible labels, see
+   get_before_labels description for details.  */
 
 static void
-c_parser_switch_statement (c_parser *parser, bool *if_p)
+c_parser_switch_statement (c_parser *parser, bool *if_p, tree before_labels)
 {
   struct c_expr ce;
   tree block, expr, body;
@@ -8146,6 +8200,8 @@  c_parser_switch_statement (c_parser *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;
@@ -8167,9 +8223,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);
@@ -8179,6 +8244,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);
 }
@@ -8190,11 +8263,14 @@  c_parser_switch_statement (c_parser *par
 
    IF_P is used to track whether there's a (possibly labeled) if statement
    which is not enclosed in braces and has an else clause.  This is used to
-   implement -Wparentheses.  */
+   implement -Wparentheses.
+
+   BEFORE_LABELS is last statement before possible labels, see
+   get_before_labels description for details.  */
 
 static void
 c_parser_while_statement (c_parser *parser, bool ivdep, unsigned short unroll,
-			  bool novector, bool *if_p)
+			  bool novector, bool *if_p, tree before_labels)
 {
   tree block, cond, body;
   unsigned char save_in_statement;
@@ -8202,6 +8278,8 @@  c_parser_while_statement (c_parser *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)
     {
@@ -8231,6 +8309,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));
@@ -8238,9 +8321,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));
@@ -8257,16 +8344,20 @@  c_parser_while_statement (c_parser *pars
 
    do-statement:
      do statement while ( expression ) ;
-*/
+
+   BEFORE_LABELS is last statement before possible labels, see
+   get_before_labels description for details.  */
 
 static void
 c_parser_do_statement (c_parser *parser, bool ivdep, unsigned short unroll,
-		       bool novector)
+		       bool novector, tree before_labels)
 {
   tree block, cond, body;
   unsigned char save_in_statement;
   location_t loc;
   gcc_assert (c_parser_next_token_is_keyword (parser, RID_DO));
+  tree loop_name;
+  int num_names = c_get_loop_names (before_labels, false, &loop_name);
 
   if (parser->omp_for_parse_state)
     {
@@ -8284,9 +8375,20 @@  c_parser_do_statement (c_parser *parser,
   loc = c_parser_peek_token (parser)->location;
   save_in_statement = in_statement;
   in_statement = IN_ITERATION_STMT;
+  if (loop_name)
+    {
+      C_DECL_LOOP_SWITCH_NAME_VALID (loop_name) = 1;
+      in_statement |= IN_NAMED_STMT;
+    }
   body = c_parser_c99_block_statement (parser, NULL);
   c_parser_require_keyword (parser, RID_WHILE, "expected %<while%>");
   in_statement = save_in_statement;
+  if (num_names)
+    {
+      if (!C_DECL_LOOP_SWITCH_NAME_USED (loop_name))
+	loop_name = NULL_TREE;
+      c_release_loop_names (num_names);
+    }
   cond = c_parser_paren_condition (parser);
   if (ivdep && cond != error_mark_node)
     cond = build3 (ANNOTATE_EXPR, TREE_TYPE (cond), cond,
@@ -8306,7 +8408,7 @@  c_parser_do_statement (c_parser *parser,
   if (!c_parser_require (parser, CPP_SEMICOLON, "expected %<;%>"))
     c_parser_skip_to_end_of_block_or_statement (parser);
 
-  add_stmt (build_stmt (loc, DO_STMT, cond, body));
+  add_stmt (build_stmt (loc, DO_STMT, cond, body, loop_name));
   add_stmt (c_end_compound_stmt (loc, block, flag_isoc99));
 }
 
@@ -8367,11 +8469,14 @@  c_parser_do_statement (c_parser *parser,
 
    IF_P is used to track whether there's a (possibly labeled) if statement
    which is not enclosed in braces and has an else clause.  This is used to
-   implement -Wparentheses.  */
+   implement -Wparentheses.
+
+   BEFORE_LABELS is last statement before possible labels, see
+   get_before_labels description for details.  */
 
 static void
 c_parser_for_statement (c_parser *parser, bool ivdep, unsigned short unroll,
-			bool novector, bool *if_p)
+			bool novector, bool *if_p, tree before_labels)
 {
   tree block, cond, incr, body;
   unsigned char save_in_statement;
@@ -8386,6 +8491,8 @@  c_parser_for_statement (c_parser *parser
   gcc_assert (c_parser_next_token_is_keyword (parser, RID_FOR));
   token_indent_info for_tinfo
     = get_token_indent_info (c_parser_peek_token (parser));
+  tree loop_name;
+  int num_names = c_get_loop_names (before_labels, false, &loop_name);
 
   if (parser->omp_for_parse_state)
     {
@@ -8578,9 +8685,22 @@  c_parser_for_statement (c_parser *parser
       save_objc_foreach_continue_label = objc_foreach_continue_label;
       objc_foreach_break_label = create_artificial_label (loc);
       objc_foreach_continue_label = create_artificial_label (loc);
+      if (loop_name)
+	{
+	  gcc_checking_assert (!DECL_CHAIN (loop_name)
+			       && !DECL_CHAIN (objc_foreach_break_label));
+	  C_DECL_SWITCH_NAME (loop_name) = 1;
+	  DECL_CHAIN (loop_name) = objc_foreach_break_label;
+	  DECL_CHAIN (objc_foreach_break_label) = objc_foreach_continue_label;
+	}
     }
   else
     in_statement = IN_ITERATION_STMT;
+  if (loop_name)
+    {
+      C_DECL_LOOP_SWITCH_NAME_VALID (loop_name) = 1;
+      in_statement |= IN_NAMED_STMT;
+    }
 
   token_indent_info body_tinfo
     = get_token_indent_info (c_parser_peek_token (parser));
@@ -8589,6 +8709,16 @@  c_parser_for_statement (c_parser *parser
   bool open_brace = c_parser_next_token_is (parser, CPP_OPEN_BRACE);
   body = c_parser_c99_block_statement (parser, if_p, &loc_after_labels);
 
+  if (loop_name && is_foreach_statement)
+    {
+      gcc_checking_assert (DECL_CHAIN (loop_name) == objc_foreach_break_label
+			   && (DECL_CHAIN (objc_foreach_break_label)
+			       == objc_foreach_continue_label));
+      C_DECL_SWITCH_NAME (loop_name) = 0;
+      DECL_CHAIN (loop_name) = NULL_TREE;
+      DECL_CHAIN (objc_foreach_break_label) = NULL_TREE;
+    }
+
   if (is_foreach_statement)
     objc_finish_foreach_loop (for_loc, object_expression,
 			      collection_expression, body,
@@ -8596,7 +8726,9 @@  c_parser_for_statement (c_parser *parser
 			      objc_foreach_continue_label);
   else
     add_stmt (build_stmt (for_loc, FOR_STMT, NULL_TREE, cond, incr,
-			  body, NULL_TREE));
+			  body, NULL_TREE,
+			  loop_name && C_DECL_LOOP_SWITCH_NAME_USED (loop_name)
+			  ? loop_name : NULL_TREE));
   add_stmt (c_end_compound_stmt (for_loc, block,
 				 flag_isoc99 || c_dialect_objc ()));
   c_parser_maybe_reclassify_token (parser);
@@ -8610,6 +8742,8 @@  c_parser_for_statement (c_parser *parser
 				    for_tinfo.location, RID_FOR);
 
   in_statement = save_in_statement;
+  if (num_names)
+    c_release_loop_names (num_names);
   if (is_foreach_statement)
     {
       objc_foreach_break_label = save_objc_foreach_break_label;
@@ -13245,7 +13379,7 @@  c_parser_objc_class_instance_variables (
 	}
       else if (c_parser_next_token_is (parser, CPP_PRAGMA))
 	{
-	  c_parser_pragma (parser, pragma_external, NULL);
+	  c_parser_pragma (parser, pragma_external, NULL, NULL_TREE);
 	  continue;
 	}
 
@@ -13516,7 +13650,7 @@  c_parser_objc_methodprotolist (c_parser
 	  c_parser_objc_methodproto (parser);
 	  break;
 	case CPP_PRAGMA:
-	  c_parser_pragma (parser, pragma_external, NULL);
+	  c_parser_pragma (parser, pragma_external, NULL, NULL_TREE);
 	  break;
 	case CPP_EOF:
 	  return;
@@ -14585,10 +14719,12 @@  c_parser_pragma_unroll (c_parser *parser
 /* Handle pragmas.  Some OpenMP pragmas are associated with, and therefore
    should be considered, statements.  ALLOW_STMT is true if we're within
    the context of a function and such pragmas are to be allowed.  Returns
-   true if we actually parsed such a pragma.  */
+   true if we actually parsed such a pragma.  BEFORE_LABELS is last statement
+   before possible labels, see get_before_labels description for details.  */
 
 static bool
-c_parser_pragma (c_parser *parser, enum pragma_context context, bool *if_p)
+c_parser_pragma (c_parser *parser, enum pragma_context context, bool *if_p,
+		 tree before_labels)
 {
   unsigned int id;
   const char *construct = NULL;
@@ -14841,11 +14977,14 @@  c_parser_pragma (c_parser *parser, enum
 	    return false;
 	  }
 	if (c_parser_next_token_is_keyword (parser, RID_FOR))
-	  c_parser_for_statement (parser, ivdep, unroll, novector, if_p);
+	  c_parser_for_statement (parser, ivdep, unroll, novector, if_p,
+				  before_labels);
 	else if (c_parser_next_token_is_keyword (parser, RID_WHILE))
-	  c_parser_while_statement (parser, ivdep, unroll, novector, if_p);
+	  c_parser_while_statement (parser, ivdep, unroll, novector, if_p,
+				    before_labels);
 	else
-	  c_parser_do_statement (parser, ivdep, unroll, novector);
+	  c_parser_do_statement (parser, ivdep, unroll, novector,
+				 before_labels);
       }
       return true;
 
@@ -22568,7 +22707,7 @@  c_parser_omp_loop_nest (c_parser *parser
 	    }
 	  break;
 	default:
-	  c_parser_pragma (parser, pragma_stmt, NULL);
+	  c_parser_pragma (parser, pragma_stmt, NULL, void_list_node);
 	  break;
 	}
       if (transform == NULL_TREE)
@@ -25468,7 +25607,7 @@  c_maybe_parse_omp_decl (tree decl, tree
   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-12 10:40:15.999533361 +0200
+++ gcc/c/c-decl.cc	2024-10-15 19:10:03.861008436 +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,212 @@  c_check_in_current_scope (tree decl)
   return b != NULL && B_IN_CURRENT_SCOPE (b);
 }
 
+/* Search for loop or switch names.  BEFORE_LABELS is last statement before
+   possible labels and SWITCH_P true for a switch, false for loops.
+   Searches through last statements in cur_stmt_list, stops when seeing
+   BEFORE_LABELs, or statement other than LABEL_EXPR or CASE_LABEL_EXPR.
+   Returns number of loop/switch names found and if any are found, sets
+   *LAST_P to the canonical loop/switch name LABEL_DECL.  */
+
+int
+c_get_loop_names (tree before_labels, bool switch_p, tree *last_p)
+{
+  *last_p = NULL_TREE;
+  if (!building_stmt_list_p ()
+      || !STATEMENT_LIST_HAS_LABEL (cur_stmt_list)
+      || before_labels == void_list_node)
+    return 0;
+
+  int ret = 0;
+  tree last = NULL_TREE;
+  for (tree_stmt_iterator tsi = tsi_last (cur_stmt_list);
+       !tsi_end_p (tsi); tsi_prev (&tsi))
+    {
+      tree stmt = tsi_stmt (tsi);
+      if (stmt == before_labels)
+	break;
+      else if (TREE_CODE (stmt) == LABEL_EXPR)
+	{
+	  if (last == NULL_TREE)
+	    last = LABEL_EXPR_LABEL (stmt);
+	  else
+	    {
+	      loop_names.safe_push (LABEL_EXPR_LABEL (stmt));
+	      ++ret;
+	    }
+	}
+      else if (TREE_CODE (stmt) != CASE_LABEL_EXPR)
+	break;
+    }
+  if (last)
+    {
+      if (switch_p)
+	C_DECL_SWITCH_NAME (last) = 1;
+      else
+	C_DECL_LOOP_NAME (last) = 1;
+      loop_names.safe_push (last);
+      ++ret;
+      if (loop_names.length () > 16)
+	{
+	  unsigned int first = 0, i;
+	  tree l, c = NULL_TREE;
+	  if (loop_names_hash == NULL)
+	    loop_names_hash = new decl_tree_map (ret);
+	  else
+	    first = loop_names.length () - ret;
+	  FOR_EACH_VEC_ELT_REVERSE (loop_names, i, l)
+	    {
+	      if (C_DECL_LOOP_NAME (l) || C_DECL_SWITCH_NAME (l))
+		c = l;
+	      loop_names_hash->put (l, c);
+	      if (i == first)
+		break;
+	    }
+	}
+      *last_p = last;
+    }
+  return ret;
+}
+
+/* Undoes what get_loop_names did when it returned NUM_NAMES.  */
+
+void
+c_release_loop_names (int num_names)
+{
+  unsigned len = loop_names.length () - num_names;
+  if (loop_names_hash)
+    {
+      if (len <= 16)
+	{
+	  delete loop_names_hash;
+	  loop_names_hash = NULL;
+	}
+      else
+	{
+	  unsigned int i;
+	  tree l;
+	  FOR_EACH_VEC_ELT_REVERSE (loop_names, i, l)
+	    {
+	      loop_names_hash->remove (l);
+	      if (i == len)
+		break;
+	    }
+	}
+    }
+  loop_names.truncate (len);
+}
+
+/* Finish processing of break or continue identifier operand.
+   NAME is the identifier operand of break or continue and
+   IS_BREAK is true iff it is break stmt.  Returns the operand
+   to use for BREAK_STMT or CONTINUE_STMT, either NULL_TREE or
+   canonical loop/switch name LABEL_DECL.  */
+
+tree
+c_finish_bc_name (location_t loc, tree name, bool is_break)
+{
+  tree label = NULL_TREE, lab;
+  pedwarn_c23 (loc, OPT_Wpedantic,
+	       "ISO C does not support %qs statement with an identifier "
+	       "operand before C2Y", is_break ? "break" : "continue");
+
+  /* If I_LABEL_DECL is NULL or not from current function, don't waste time
+     trying to find it among loop_names, it can't be there.  */
+  if (!loop_names.is_empty ()
+      && current_function_scope
+      && (lab = I_LABEL_DECL (name))
+      && DECL_CONTEXT (lab) == current_function_decl)
+    {
+      unsigned int i;
+      tree l, c = NULL_TREE;
+      if (loop_names_hash)
+	{
+	  if (tree *val = loop_names_hash->get (lab))
+	    label = *val;
+	}
+      else
+	FOR_EACH_VEC_ELT_REVERSE (loop_names, i, l)
+	  {
+	    if (C_DECL_LOOP_NAME (l) || C_DECL_SWITCH_NAME (l))
+	      c = l;
+	    if (l == lab)
+	      {
+		label = c;
+		break;
+	      }
+	  }
+      if (label)
+	TREE_USED (lab) = 1;
+    }
+  if (label == NULL_TREE)
+    {
+      auto_vec<const char *> candidates;
+      unsigned int i;
+      tree l, c = NULL_TREE;
+      FOR_EACH_VEC_ELT_REVERSE (loop_names, i, l)
+	{
+	  if (C_DECL_LOOP_NAME (l) || C_DECL_SWITCH_NAME (l))
+	    c = l;
+	  if (is_break || C_DECL_LOOP_NAME (c))
+	    candidates.safe_push (IDENTIFIER_POINTER (DECL_NAME (l)));
+	}
+      const char *hint = find_closest_string (IDENTIFIER_POINTER (name),
+					      &candidates);
+      if (hint)
+	{
+	  gcc_rich_location richloc (loc);
+	  richloc.add_fixit_replace (hint);
+	  if (is_break)
+	    error_at (&richloc, "%<break%> statement operand %qE does not "
+				"refer to a named loop or %<switch%>; "
+				"did you mean %qs?", name, hint);
+	  else
+	    error_at (&richloc, "%<continue%> statement operand %qE does not "
+				"refer to a named loop; did you mean %qs?",
+		      name, hint);
+	}
+      else if (is_break)
+	error_at (loc, "%<break%> statement operand %qE does not refer to a "
+		       "named loop or %<switch%>", name);
+      else
+	error_at (loc, "%<continue%> statement operand %qE does not refer to "
+		       "a named loop", name);
+    }
+  else if (!C_DECL_LOOP_NAME (label) && !is_break)
+    {
+      auto_diagnostic_group d;
+      error_at (loc, "%<continue%> statement operand %qE refers to a named "
+		     "%<switch%>", name);
+      inform (DECL_SOURCE_LOCATION (label), "%<switch%> name defined here");
+      label = NULL_TREE;
+    }
+  else if (!C_DECL_LOOP_SWITCH_NAME_VALID (label))
+    {
+      auto_diagnostic_group d;
+      if (C_DECL_LOOP_NAME (label))
+	{
+	  error_at (loc, "%qs statement operand %qE refers to a loop outside "
+			 "of its body", is_break ? "break" : "continue", name);
+	  inform (DECL_SOURCE_LOCATION (label), "loop name defined here");
+	}
+      else
+	{
+	  error_at (loc, "%<break%> statement operand %qE refers to a "
+			 "%<switch%> outside of its body", name);
+	  inform (DECL_SOURCE_LOCATION (label),
+		  "%<switch%> name defined here");
+	}
+      label = NULL_TREE;
+    }
+  else if (label == loop_names.last () && (in_statement & IN_NAMED_STMT) != 0)
+    /* If it is just a fancy reference to the innermost construct, handle it
+       just like break; or continue; though tracking cheaply what is the
+       innermost loop for continue when nested in switches would require
+       another global variable and updating it.  */
+    label = NULL_TREE;
+  else
+    C_DECL_LOOP_SWITCH_NAME_USED (label) = 1;
+  return label;
+}
+
 #include "gt-c-c-decl.h"
--- gcc/c/c-typeck.cc.jj	2024-10-15 19:09:09.784746947 +0200
+++ gcc/c/c-typeck.cc	2024-10-15 19:10:03.863008409 +0200
@@ -11943,7 +11943,7 @@  struct c_switch *c_switch_stack;
 tree
 c_start_switch (location_t switch_loc,
 		location_t switch_cond_loc,
-		tree exp, bool explicit_cast_p)
+		tree exp, bool explicit_cast_p, tree switch_name)
 {
   tree orig_type = error_mark_node;
   bool bool_cond_p = false;
@@ -11996,7 +11996,7 @@  c_start_switch (location_t switch_loc,
   /* Add this new SWITCH_STMT to the stack.  */
   cs = XNEW (struct c_switch);
   cs->switch_stmt = build_stmt (switch_loc, SWITCH_STMT, exp,
-				NULL_TREE, orig_type, NULL_TREE);
+				NULL_TREE, orig_type, NULL_TREE, switch_name);
   cs->orig_type = orig_type;
   cs->cases = splay_tree_new (case_compare, NULL, NULL);
   cs->bindings = c_get_switch_bindings ();
@@ -12097,7 +12097,7 @@  c_finish_if_stmt (location_t if_locus, 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
@@ -12109,7 +12109,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");
@@ -12129,7 +12129,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");
@@ -12148,14 +12148,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-12 10:40:16.252529844 +0200
+++ gcc/cp/semantics.cc	2024-10-15 19:10:03.865008381 +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/c23-named-loops-1.c.jj	2024-10-15 19:10:03.865008381 +0200
+++ gcc/testsuite/gcc.dg/c23-named-loops-1.c	2024-10-15 19:22:52.560529971 +0200
@@ -0,0 +1,144 @@ 
+/* N3355 - Named loops.  */
+/* { dg-do compile } */
+/* { dg-options "-std=c23 -pedantic-errors" } */
+
+void
+foo (int w)
+{
+  d: e: f:;
+  a: b: c:
+  for (int x = 0; x < 32; ++x)
+    {
+      if (x == 0)
+	continue a;	/* { dg-error "ISO C does not support 'continue' statement with an identifier operand before" } */
+      else if (x == 1)
+	continue b;	/* { dg-error "ISO C does not support 'continue' statement with an identifier operand before" } */
+      else if (x == 2)
+	continue c;	/* { dg-error "ISO C does not support 'continue' statement with an identifier operand before" } */
+      else if (x == 31)
+	break b;	/* { dg-error "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-error "ISO C does not support 'continue' statement with an identifier operand before" } */
+      else if (y == 13)
+	continue h;	/* { dg-error "ISO C does not support 'continue' statement with an identifier operand before" } */
+      else if (y == 14)
+	break g;	/* { dg-error "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-error "ISO C does not support 'break' statement with an identifier operand before" } */
+    case 8:
+      break l;		/* { dg-error "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-error "ISO C does not support 'break' statement with an identifier operand before" } */
+		  else if (w == 1)
+		    break ab;		/* { dg-error "ISO C does not support 'break' statement with an identifier operand before" } */
+		  else if (w == 2)
+		    break z;		/* { dg-error "ISO C does not support 'break' statement with an identifier operand before" } */
+		  else if (w == 3)
+		    break v;		/* { dg-error "ISO C does not support 'break' statement with an identifier operand before" } */
+		  else if (w == 4)
+		    break s;		/* { dg-error "ISO C does not support 'break' statement with an identifier operand before" } */
+		  else if (w == 5)
+		    break p;		/* { dg-error "ISO C does not support 'break' statement with an identifier operand before" } */
+		  else if (w == 6)
+		    break;
+		  else if (w == 7)
+		    continue aa;	/* { dg-error "ISO C does not support 'continue' statement with an identifier operand before" } */
+		  else if (w == 8)
+		    continue y;		/* { dg-error "ISO C does not support 'continue' statement with an identifier operand before" } */
+		  else if (w == 9)
+		    continue x;		/* { dg-error "ISO C does not support 'continue' statement with an identifier operand before" } */
+		  else if (w == 10)
+		    continue m;		/* { dg-error "ISO C does not support 'continue' statement with an identifier operand before" } */
+		  ag: ah:
+		  do
+		    {
+		      if (w == 11)
+			break ag;	/* { dg-error "ISO C does not support 'break' statement with an identifier operand before" } */
+		      else
+			continue ah;	/* { dg-error "ISO C does not support 'continue' statement with an identifier operand before" } */
+		    }
+		  while (0);
+		  break;
+		default:
+		  break;
+		}
+	break;
+      default:
+	break;
+      }
+  [[]] [[]] ai:
+  [[]] [[]] aj:
+  [[]] [[]] ak:
+  [[]] [[]] [[]]
+  for (int x = 0; x < 32; ++x)
+    if (x == 31)
+      break ak;				/* { dg-error "ISO C does not support 'break' statement with an identifier operand before" } */
+    else if (x == 30)
+      break aj;				/* { dg-error "ISO C does not support 'break' statement with an identifier operand before" } */
+    else if (x == 29)
+      continue ai;			/* { dg-error "ISO C does not support 'continue' statement with an identifier operand before" } */
+  al:
+  [[]] am:
+  [[]]
+  do
+    {
+      if (w == 42)
+	continue am;			/* { dg-error "ISO C does not support 'continue' statement with an identifier operand before" } */
+      else if (w == 41)
+	break al;			/* { dg-error "ISO C does not support 'break' statement with an identifier operand before" } */
+    }
+  while (1);
+  an:
+  [[]] ao:
+  [[]] [[]]
+  while (w)
+    {
+      if (w == 40)
+	break ao;			/* { dg-error "ISO C does not support 'break' statement with an identifier operand before" } */
+      else if (w == 39)
+	continue an;			/* { dg-error "ISO C does not support 'continue' statement with an identifier operand before" } */
+    }
+  [[]] ap:
+  [[]] aq:
+  [[]]
+  switch (w)
+    {
+    case 42:
+      break ap;				/* { dg-error "ISO C does not support 'break' statement with an identifier operand before" } */
+    default:
+      break aq;				/* { dg-error "ISO C does not support 'break' statement with an identifier operand before" } */
+    }
+}
--- gcc/testsuite/gcc.dg/c23-named-loops-5.c.jj	2024-10-15 19:10:03.865008381 +0200
+++ gcc/testsuite/gcc.dg/c23-named-loops-5.c	2024-10-15 19:10:03.865008381 +0200
@@ -0,0 +1,5 @@ 
+/* N3355 - Named loops.  */
+/* { dg-do compile } */
+/* { dg-options "-std=c23 -pedantic-errors -Wno-c23-c2y-compat" } */
+
+#include "c23-named-loops-1.c"
--- gcc/testsuite/gcc.dg/c2y-named-loops-1.c.jj	2024-10-15 19:10:03.866008367 +0200
+++ gcc/testsuite/gcc.dg/c2y-named-loops-1.c	2024-10-15 19:23:34.073964218 +0200
@@ -0,0 +1,144 @@ 
+/* N3355 - Named loops.  */
+/* { dg-do compile } */
+/* { dg-options "-std=c2y -Wc23-c2y-compat" } */
+
+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;
+      }
+  [[]] [[]] ai:
+  [[]] [[]] aj:
+  [[]] [[]] ak:
+  [[]] [[]] [[]]
+  for (int x = 0; x < 32; ++x)
+    if (x == 31)
+      break ak;				/* { dg-warning "ISO C does not support 'break' statement with an identifier operand before" } */
+    else if (x == 30)
+      break aj;				/* { dg-warning "ISO C does not support 'break' statement with an identifier operand before" } */
+    else if (x == 29)
+      continue ai;			/* { dg-warning "ISO C does not support 'continue' statement with an identifier operand before" } */
+  al:
+  [[]] am:
+  [[]]
+  do
+    {
+      if (w == 42)
+	continue am;			/* { dg-warning "ISO C does not support 'continue' statement with an identifier operand before" } */
+      else if (w == 41)
+	break al;			/* { dg-warning "ISO C does not support 'break' statement with an identifier operand before" } */
+    }
+  while (1);
+  an:
+  [[]] ao:
+  [[]] [[]]
+  while (w)
+    {
+      if (w == 40)
+	break ao;			/* { dg-warning "ISO C does not support 'break' statement with an identifier operand before" } */
+      else if (w == 39)
+	continue an;			/* { dg-warning "ISO C does not support 'continue' statement with an identifier operand before" } */
+    }
+  [[]] ap:
+  [[]] aq:
+  [[]]
+  switch (w)
+    {
+    case 42:
+      break ap;				/* { dg-warning "ISO C does not support 'break' statement with an identifier operand before" } */
+    default:
+      break aq;				/* { dg-warning "ISO C does not support 'break' statement with an identifier operand before" } */
+    }
+}
--- gcc/testsuite/gcc.dg/c2y-named-loops-2.c.jj	2024-10-15 19:10:03.866008367 +0200
+++ gcc/testsuite/gcc.dg/c2y-named-loops-2.c	2024-10-15 19:10:03.866008367 +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-15 19:10:03.866008367 +0200
+++ gcc/testsuite/gcc.dg/c2y-named-loops-4.c	2024-10-15 19:10:03.866008367 +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/c2y-named-loops-5.c.jj	2024-10-15 19:10:03.866008367 +0200
+++ gcc/testsuite/gcc.dg/c2y-named-loops-5.c	2024-10-15 19:26:54.725229702 +0200
@@ -0,0 +1,5 @@ 
+/* N3355 - Named loops.  */
+/* { dg-do compile } */
+/* { dg-options "-std=c2y -pedantic-errors" } */
+
+#include "c2y-named-loops-1.c"
--- gcc/testsuite/gcc.dg/c2y-named-loops-6.c.jj	2024-10-15 19:10:03.866008367 +0200
+++ gcc/testsuite/gcc.dg/c2y-named-loops-6.c	2024-10-15 19:10:03.866008367 +0200
@@ -0,0 +1,32 @@ 
+/* N3355 - Named loops.  */
+/* { dg-do compile } */
+/* { dg-options "-std=c2y -Wall" } */
+
+void
+foo (int x)
+{
+ lab0:
+  switch (x)
+    {
+    case 1:
+      ++x;
+      /* FALLTHRU */
+    lab1:
+    case 2:
+      /* FALLTHRU */
+    case 3:
+    lab2:
+      for (int i = 0; i < 4; ++i)
+	if (i == 0)
+	  continue lab2;
+	else if (i == 1)
+	  continue lab1;
+	else if (x == 2)
+	  break lab1;
+	else
+	  break lab0;
+      break;
+    default:
+      break;
+    }
+}
--- gcc/testsuite/gcc.dg/c2y-named-loops-7.c.jj	2024-10-15 19:10:03.866008367 +0200
+++ gcc/testsuite/gcc.dg/c2y-named-loops-7.c	2024-10-15 19:10:03.866008367 +0200
@@ -0,0 +1,32 @@ 
+/* N3355 - Named loops.  */
+/* { dg-do compile } */
+/* { dg-options "-std=c2y -Wall" } */
+
+void
+foo (int x)
+{
+ lab0:
+  switch (x)
+    {
+    case 1:
+      ++x;
+      [[fallthrough]];
+    lab1:			/* { dg-warning "label 'lab1' defined but not used" } */
+    case 2:
+      [[fallthrough]];
+    case 3:
+    lab2:
+      for (int i = 0; i < 4; ++i)
+	if (i == 0)
+	  continue lab2;
+	else if (i == 1)
+	  continue lab1;	/* { dg-error "'continue' statement operand 'lab1' does not refer to a named loop; did you mean 'lab2'\\\?" } */
+	else if (x == 2)
+	  break lab1;		/* { dg-error "'break' statement operand 'lab1' does not refer to a named loop or 'switch'; did you mean 'lab2'\\\?" } */
+	else
+	  break lab0;
+      break;
+    default:
+      break;
+    }
+}
--- gcc/testsuite/gcc.dg/gnu99-named-loops-1.c.jj	2024-10-15 19:10:03.866008367 +0200
+++ gcc/testsuite/gcc.dg/gnu99-named-loops-1.c	2024-10-15 19:28:04.481279053 +0200
@@ -0,0 +1,144 @@ 
+/* 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;
+      }
+  [[]] [[]] ai:				/* { dg-warning "ISO C does not support '\\\[\\\[\\\]\\\]' attributes before C23" } */
+  [[]] [[]] aj:				/* { dg-warning "ISO C does not support '\\\[\\\[\\\]\\\]' attributes before C23" } */
+  [[]] [[]] ak:				/* { dg-warning "ISO C does not support '\\\[\\\[\\\]\\\]' attributes before C23" } */
+  [[]] [[]] [[]]			/* { dg-warning "ISO C does not support '\\\[\\\[\\\]\\\]' attributes before C23" } */
+  for (int x = 0; x < 32; ++x)
+    if (x == 31)
+      break ak;				/* { dg-warning "ISO C does not support 'break' statement with an identifier operand before" } */
+    else if (x == 30)
+      break aj;				/* { dg-warning "ISO C does not support 'break' statement with an identifier operand before" } */
+    else if (x == 29)
+      continue ai;			/* { dg-warning "ISO C does not support 'continue' statement with an identifier operand before" } */
+  al:
+  [[]] am:				/* { dg-warning "ISO C does not support '\\\[\\\[\\\]\\\]' attributes before C23" } */
+  [[]]					/* { dg-warning "ISO C does not support '\\\[\\\[\\\]\\\]' attributes before C23" } */
+  do
+    {
+      if (w == 42)
+	continue am;			/* { dg-warning "ISO C does not support 'continue' statement with an identifier operand before" } */
+      else if (w == 41)
+	break al;			/* { dg-warning "ISO C does not support 'break' statement with an identifier operand before" } */
+    }
+  while (1);
+  an:
+  [[]] ao:				/* { dg-warning "ISO C does not support '\\\[\\\[\\\]\\\]' attributes before C23" } */
+  [[]] [[]]				/* { dg-warning "ISO C does not support '\\\[\\\[\\\]\\\]' attributes before C23" } */
+  while (w)
+    {
+      if (w == 40)
+	break ao;			/* { dg-warning "ISO C does not support 'break' statement with an identifier operand before" } */
+      else if (w == 39)
+	continue an;			/* { dg-warning "ISO C does not support 'continue' statement with an identifier operand before" } */
+    }
+  [[]] ap:				/* { dg-warning "ISO C does not support '\\\[\\\[\\\]\\\]' attributes before C23" } */
+  [[]] aq:				/* { dg-warning "ISO C does not support '\\\[\\\[\\\]\\\]' attributes before C23" } */
+  [[]]					/* { dg-warning "ISO C does not support '\\\[\\\[\\\]\\\]' attributes before C23" } */
+  switch (w)
+    {
+    case 42:
+      break ap;				/* { dg-warning "ISO C does not support 'break' statement with an identifier operand before" } */
+    default:
+      break aq;				/* { dg-warning "ISO C does not support 'break' statement with an identifier operand before" } */
+    }
+}
--- gcc/testsuite/gcc.dg/gnu99-named-loops-2.c.jj	2024-10-15 19:10:03.866008367 +0200
+++ gcc/testsuite/gcc.dg/gnu99-named-loops-2.c	2024-10-15 19:10:03.866008367 +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-15 19:10:03.867008354 +0200
+++ gcc/testsuite/gcc.dg/gnu99-named-loops-3.c	2024-10-15 19:10:03.867008354 +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-15 19:10:03.867008354 +0200
+++ gcc/testsuite/gcc.dg/gnu99-named-loops-4.c	2024-10-15 19:10:03.867008354 +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-15 19:10:03.867008354 +0200
+++ gcc/testsuite/gcc.dg/gnu2y-named-loops-3.c	2024-10-15 19:10:03.867008354 +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-15 19:10:03.867008354 +0200
+++ gcc/testsuite/gcc.dg/gomp/named-loops-1.c	2024-10-15 19:10:03.867008354 +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-15 19:10:03.867008354 +0200
+++ gcc/testsuite/gcc.dg/gomp/named-loops-2.c	2024-10-15 19:10:03.867008354 +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-15 19:10:03.867008354 +0200
+++ gcc/testsuite/objc.dg/named-loops-1.m	2024-10-15 19:10:03.867008354 +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;
+}