[gcc r17-3113] gccrs: Improve resolution and compilation of control flow loops and labels

Arthur Cohen via Gcc-cvs <[email protected]>
Newsgroups gmane.comp.gcc.cvs
Message-ID <[email protected]>
https://gcc.gnu.org/g:b825f4316b362380e0092e06314b2d6693678d03

commit r17-3113-gb825f4316b362380e0092e06314b2d6693678d03
Author: Islam-Imad <[email protected]>
Date:   Sun Apr 12 19:29:25 2026 +0200

    gccrs: Improve resolution and compilation of control flow loops and labels
    
    This batch fixes and improves the handling of loop expressions, particularly
    focusing on `break` and `continue` statements within labeled and nested loops.
    
    gcc/rust/ChangeLog:
    
            * backend/rust-compile-context.h (RUST_COMPILE_CONTEXT): loop
            labels context functions.
            - insert_break_label
            - lookup_break_label
            - insert_continue_label
            - lookup_continue_label
            - push/pop/peek end_label (similar to begin_label).
            * backend/rust-compile-expr.cc (CompileExpr::visit): handle loop
            labels construction in WhileExpr, ForExpr, ContinueExpr and BreakExpr.
            (CompileExpr::construct_block_label): construct break label
            instead of normal label to unify handling break statement.
            (CompileExpr::construct_loop_labels): construct while/for loops
            labels (continue label before loop, break label after loop).
            * backend/rust-compile-expr.h (RUST_COMPILE_EXPR):
            construct_loop_labels function header.
            * resolve/rust-default-resolver.cc (DefaultResolver::visit): Fix
            labels.
            * resolve/rust-default-resolver.h: Fix labels.
            * resolve/rust-name-resolution-context.cc (NameResolutionContext::insert): Fix labels.
            (NameResolutionContext::insert_shadowable): Fix labels.
            (NameResolutionContext::insert_globbed): Fix labels.
            (NameResolutionContext::scoped): Fix labels.
    
    gcc/testsuite/ChangeLog:
    
            * rust/execute/cf-break-continue.rs: New test.
            * rust/execute/cf-label-shadowing.rs: New test.
            * rust/execute/cf-labeled-break-nested.rs: New test.
            * rust/execute/cf-labeled-continue-nested.rs: New test.
            * rust/execute/cf-labeled-loops.rs: New test.
            * rust/execute/cf-loop-break-continue.rs: New test.
            * rust/execute/cf-mixed-labeled-unlabeled.rs: New test.
            * rust/execute/cf-nested-loops.rs: New test.
    
    Signed-off-by: Islam-Imad <[email protected]>

Diff:
---
 gcc/rust/backend/rust-compile-context.h            |  46 +++++++
 gcc/rust/backend/rust-compile-expr.cc              | 141 ++++++++++++---------
 gcc/rust/backend/rust-compile-expr.h               |   3 +
 gcc/rust/resolve/rust-default-resolver.cc          |  20 +++
 gcc/rust/resolve/rust-default-resolver.h           |   3 +
 gcc/rust/resolve/rust-name-resolution-context.cc   |  14 +-
 gcc/testsuite/rust/execute/cf-break-continue.rs    |  49 +++++++
 gcc/testsuite/rust/execute/cf-label-shadowing.rs   |  47 +++++++
 .../rust/execute/cf-labeled-break-nested.rs        |  39 ++++++
 .../rust/execute/cf-labeled-continue-nested.rs     |  46 +++++++
 gcc/testsuite/rust/execute/cf-labeled-loops.rs     |  34 +++++
 .../rust/execute/cf-loop-break-continue.rs         |  51 ++++++++
 .../rust/execute/cf-mixed-labeled-unlabeled.rs     |  80 ++++++++++++
 gcc/testsuite/rust/execute/cf-nested-loops.rs      |  36 ++++++
 14 files changed, 546 insertions(+), 63 deletions(-)

diff --git a/gcc/rust/backend/rust-compile-context.h b/gcc/rust/backend/rust-compile-context.h
index 7f67b9382e05..2c71f7778a55 100644
--- a/gcc/rust/backend/rust-compile-context.h
+++ b/gcc/rust/backend/rust-compile-context.h
@@ -19,6 +19,7 @@
 #ifndef RUST_COMPILE_CONTEXT
 #define RUST_COMPILE_CONTEXT
 
+#include "optional.h"
 #include "rust-system.h"
 #include "rust-compile-drop-candidate.h"
 #include "rust-hir-map.h"
@@ -264,6 +265,32 @@ public:
     return true;
   }
 
+  void insert_break_label (HirId id, tree label)
+  {
+    compiled_break_labels[id] = label;
+  }
+
+  tl::optional<tree> lookup_break_label (HirId id)
+  {
+    auto it = compiled_break_labels.find (id);
+    if (it == compiled_break_labels.end ())
+      return tl::nullopt;
+    return it->second;
+  }
+
+  void insert_continue_label (HirId id, tree label)
+  {
+    compiled_continue_labels[id] = label;
+  }
+
+  tl::optional<tree> lookup_continue_label (HirId id)
+  {
+    auto it = compiled_continue_labels.find (id);
+    if (it == compiled_continue_labels.end ())
+      return tl::nullopt;
+    return it->second;
+  }
+
   void insert_pattern_binding (HirId id, tree binding)
   {
     implicit_pattern_bindings[id] = binding;
@@ -368,6 +395,22 @@ public:
     return pop;
   }
 
+  void push_loop_end_label (tree label) { loop_end_labels.push_back (label); }
+
+  tree peek_loop_end_label ()
+  {
+    rust_assert (!loop_end_labels.empty ());
+    return loop_end_labels.back ();
+  }
+
+  tree pop_loop_end_label ()
+  {
+    rust_assert (!loop_end_labels.empty ());
+    tree pop = loop_end_labels.back ();
+    loop_end_labels.pop_back ();
+    return pop;
+  }
+
   void push_const_context (void) { const_context++; }
   void pop_const_context (void)
   {
@@ -466,11 +509,14 @@ private:
   std::map<HirId, tree> compiled_consts;
   std::map<HirId, tree> compiled_labels;
   std::map<std::pair<size_t, size_t>, ::Bvariable *> compiled_vtables;
+  std::map<HirId, tree> compiled_break_labels;
+  std::map<HirId, tree> compiled_continue_labels;
   std::vector<::std::vector<tree>> statements;
   std::vector<tree> scope_stack;
   std::vector<::std::vector<DropCandidate>> block_drop_candidates;
   std::vector<::Bvariable *> loop_value_stack;
   std::vector<tree> loop_begin_labels;
+  std::vector<tree> loop_end_labels;
   std::map<DefId, std::vector<std::pair<const TyTy::BaseType *, tree>>>
     mono_fns;
   std::map<DefId, std::vector<std::pair<const TyTy::ClosureType *, tree>>>
diff --git a/gcc/rust/backend/rust-compile-expr.cc b/gcc/rust/backend/rust-compile-expr.cc
index 52ddffd1df5e..c0d4ab0a04b9 100644
--- a/gcc/rust/backend/rust-compile-expr.cc
+++ b/gcc/rust/backend/rust-compile-expr.cc
@@ -17,6 +17,8 @@
 // <http://www.gnu.org/licenses/>.
 
 #include "rust-compile-expr.h"
+#include "line-map.h"
+#include "optional.h"
 #include "rust-backend.h"
 #include "rust-compile-context.h"
 #include "rust-compile-type.h"
@@ -38,7 +40,6 @@
 #include "rust-hir-bound.h"
 #include "rust-hir-expr.h"
 #include "rust-rib.h"
-#include "rust-system.h"
 #include "rust-tree.h"
 #include "rust-tyty.h"
 #include "tree-core.h"
@@ -944,29 +945,21 @@ CompileExpr::visit (HIR::LoopExpr &expr)
   ctx->add_statement (ret_var_stmt);
   ctx->push_loop_context (tmp);
 
+  tl::optional<HIR::LoopLabel> loop_label = tl::nullopt;
   if (expr.has_loop_label ())
     {
-      HIR::LoopLabel &loop_label = expr.get_loop_label ();
-      tree label
-	= Backend::label (fnctx.fndecl, loop_label.get_lifetime ().get_name (),
-			  loop_label.get_locus ());
-      tree label_decl = Backend::label_definition_statement (label);
-      ctx->add_statement (label_decl);
-      ctx->insert_label_decl (
-	loop_label.get_lifetime ().get_mappings ().get_hirid (), label);
-      // Associate the loop's result temporary with the label so that a
-      // `break 'label value` can locate it (see visit (HIR::BreakExpr)).
+      loop_label = expr.get_loop_label ();
       ctx->insert_var_decl (
-	loop_label.get_lifetime ().get_mappings ().get_hirid (), tmp);
+	loop_label.value ().get_lifetime ().get_mappings ().get_hirid (), tmp);
     }
+  std::pair<tree, tree> loop_labels = construct_loop_labels (loop_label);
+  tree loop_begin_label = loop_labels.first;
+  tree loop_end_label = loop_labels.second;
 
-  tree loop_begin_label
-    = Backend::label (fnctx.fndecl, tl::nullopt, expr.get_locus ());
-  tree loop_begin_label_decl
-    = Backend::label_definition_statement (loop_begin_label);
-  ctx->add_statement (loop_begin_label_decl);
-  ctx->push_loop_begin_label (loop_begin_label);
+  // label before the loop - continue should goto here
+  ctx->add_statement (loop_begin_label);
 
+  // loop body
   tree code_block
     = CompileBlock::compile (expr.get_loop_block (), ctx, nullptr);
   tree loop_expr = Backend::loop_expression (code_block, expr.get_locus ());
@@ -975,40 +968,34 @@ CompileExpr::visit (HIR::LoopExpr &expr)
   ctx->pop_loop_context ();
   translated = Backend::var_expression (tmp, expr.get_locus ());
 
+  // label after the loop - break should goto here
+  ctx->add_statement (loop_end_label);
+
+  // in construct_loop fn we push these labels
   ctx->pop_loop_begin_label ();
+  ctx->pop_loop_end_label ();
 }
 
 void
 CompileExpr::visit (HIR::WhileLoopExpr &expr)
 {
   fncontext fnctx = ctx->peek_fn ();
+  tree enclosing_scope = ctx->peek_enclosing_scope ();
+  ctx->push_loop_context (nullptr);
+  tl::optional<HIR::LoopLabel> loop_label = tl::nullopt;
   if (expr.has_loop_label ())
-    {
-      HIR::LoopLabel &loop_label = expr.get_loop_label ();
-      tree label
-	= Backend::label (fnctx.fndecl, loop_label.get_lifetime ().get_name (),
-			  loop_label.get_locus ());
-      tree label_decl = Backend::label_definition_statement (label);
-      ctx->add_statement (label_decl);
-      ctx->insert_label_decl (
-	loop_label.get_lifetime ().get_mappings ().get_hirid (), label);
-    }
-
+    loop_label = tl::optional<HIR::LoopLabel> (expr.get_loop_label ());
+  std::pair<tree, tree> loop_labels = construct_loop_labels (loop_label);
+  tree loop_begin_label = loop_labels.first;
+  tree loop_end_label = loop_labels.second;
   std::vector<Bvariable *> locals;
   location_t start_location = expr.get_loop_block ().get_locus ();
   location_t end_location = expr.get_loop_block ().get_locus (); // FIXME
 
-  tree enclosing_scope = ctx->peek_enclosing_scope ();
   tree loop_block = Backend::block (fnctx.fndecl, enclosing_scope, locals,
 				    start_location, end_location);
   ctx->push_block (loop_block);
-
-  tree loop_begin_label
-    = Backend::label (fnctx.fndecl, tl::nullopt, expr.get_locus ());
-  tree loop_begin_label_decl
-    = Backend::label_definition_statement (loop_begin_label);
-  ctx->add_statement (loop_begin_label_decl);
-  ctx->push_loop_begin_label (loop_begin_label);
+  ctx->add_statement (loop_begin_label);
 
   HIR::Expr &predicate = expr.get_predicate_expr ();
   TyTy::BaseType *predicate_type = nullptr;
@@ -1037,6 +1024,9 @@ CompileExpr::visit (HIR::WhileLoopExpr &expr)
 
   tree loop_expr = Backend::loop_expression (loop_block, expr.get_locus ());
   ctx->add_statement (loop_expr);
+  ctx->add_statement (loop_end_label);
+  ctx->pop_loop_end_label ();
+
   translated = unit_expression (expr.get_locus ());
 }
 
@@ -1051,8 +1041,12 @@ CompileExpr::visit (HIR::BreakExpr &expr)
       tree assign
 	= Backend::assignment_statement (tvar->get_tree (label.get_locus ()),
 					 value, label.get_locus ());
-      tree block_label = lookup_label (label.get_mappings ().get_nodeid ());
-      tree go_to = Backend::goto_statement (block_label, label.get_locus ());
+      HirId label_hirid = resolve_nodeid (label.get_mappings ().get_nodeid (),
+					  Resolver2_0::Namespace::Labels);
+      tl::optional<tree> block_label = ctx->lookup_break_label (label_hirid);
+      rust_assert (block_label.has_value ());
+      tree go_to
+	= Backend::goto_statement (block_label.value (), label.get_locus ());
       ctx->add_statement (assign);
       ctx->add_statement (go_to);
       return;
@@ -1066,6 +1060,9 @@ CompileExpr::visit (HIR::BreakExpr &expr)
 	return;
 
       Bvariable *loop_result_holder = ctx->peek_loop_context ();
+      if (loop_result_holder == nullptr)
+	return;
+
       tree result_reference
 	= Backend::var_expression (loop_result_holder,
 				   expr.get_expr ().get_locus ());
@@ -1104,15 +1101,10 @@ CompileExpr::visit (HIR::BreakExpr &expr)
 	}
       auto ref = hid.value ();
 
-      tree label = NULL_TREE;
-      if (!ctx->lookup_label_decl (ref, &label))
-	{
-	  rust_error_at (expr.get_label ().get_locus (),
-			 "failed to lookup compiled label");
-	  return;
-	}
-
-      tree goto_label = Backend::goto_statement (label, expr.get_locus ());
+      tl::optional<tree> label = ctx->lookup_break_label (ref);
+      rust_assert (label.has_value ());
+      tree goto_label
+	= Backend::goto_statement (label.value (), expr.get_locus ());
       ctx->add_statement (goto_label);
     }
   else
@@ -1128,8 +1120,7 @@ void
 CompileExpr::visit (HIR::ContinueExpr &expr)
 {
   translated = error_mark_node;
-  if (!ctx->have_loop_context ())
-    return;
+  rust_assert (ctx->have_loop_context () && "continue is outside of loop");
 
   tree label = ctx->peek_loop_begin_label ();
   if (expr.has_label ())
@@ -1160,13 +1151,9 @@ CompileExpr::visit (HIR::ContinueExpr &expr)
 	  return;
 	}
       auto ref = hid.value ();
-
-      if (!ctx->lookup_label_decl (ref, &label))
-	{
-	  rust_error_at (expr.get_label ().get_locus (),
-			 "failed to lookup compiled label");
-	  return;
-	}
+      tl::optional<tree> opt_label = ctx->lookup_continue_label (ref);
+      rust_assert (opt_label.has_value ());
+      label = opt_label.value ();
     }
 
   translated = Backend::goto_statement (label, expr.get_locus ());
@@ -3046,7 +3033,7 @@ CompileExpr::construct_block_label (HIR::BlockExpr &expr)
       tree label_decl
 	= Backend::label (fnctx.fndecl, label_name, label.get_locus ());
       tree label_expr = Backend::label_definition_statement (label_decl);
-      ctx->insert_label_decl (label_id, label_decl);
+      ctx->insert_break_label (label_id, label_decl);
       return label_expr;
     }
   return NULL_TREE;
@@ -3087,5 +3074,43 @@ CompileExpr::resolve_nodeid (NodeId to_be_resolved, Resolver2_0::Namespace ns)
   return ref;
 }
 
+std::pair<tree, tree>
+CompileExpr::construct_loop_labels (tl::optional<HIR::LoopLabel> opt_loop_label)
+{
+  fncontext fnctx = ctx->peek_fn ();
+  tree break_label_decl = NULL_TREE;
+  tree break_label_expr = NULL_TREE;
+  tree continue_label_decl = NULL_TREE;
+  tree continue_label_expr = NULL_TREE;
+  location_t label_locus = UNKNOWN_LOCATION;
+  tl::optional<std::string> continue_label_name = tl::nullopt;
+  tl::optional<std::string> break_label_name = tl::nullopt;
+  tl::optional<HirId> label_hirid = tl::nullopt;
+  if (opt_loop_label.has_value ())
+    {
+      label_locus = opt_loop_label.value ().get_locus ();
+      HIR::LoopLabel &loop_label = opt_loop_label.value ();
+      std::string label_name = loop_label.get_lifetime ().get_name ();
+      continue_label_name = label_name + "_continue";
+      break_label_name = label_name + "_break";
+      label_hirid = loop_label.get_lifetime ().get_mappings ().get_hirid ();
+    }
+  continue_label_decl
+    = Backend::label (fnctx.fndecl, continue_label_name, label_locus);
+  continue_label_expr
+    = Backend::label_definition_statement (continue_label_decl);
+  break_label_decl
+    = Backend::label (fnctx.fndecl, break_label_name, label_locus);
+  break_label_expr = Backend::label_definition_statement (break_label_decl);
+  if (label_hirid.has_value ())
+    {
+      ctx->insert_continue_label (label_hirid.value (), continue_label_decl);
+      ctx->insert_break_label (label_hirid.value (), break_label_decl);
+    }
+  ctx->push_loop_begin_label (continue_label_decl);
+  ctx->push_loop_end_label (break_label_decl);
+  return std::make_pair (continue_label_expr, break_label_expr);
+}
+
 } // namespace Compile
 } // namespace Rust
diff --git a/gcc/rust/backend/rust-compile-expr.h b/gcc/rust/backend/rust-compile-expr.h
index ad5c483221a9..452fa28cff05 100644
--- a/gcc/rust/backend/rust-compile-expr.h
+++ b/gcc/rust/backend/rust-compile-expr.h
@@ -19,6 +19,7 @@
 #ifndef RUST_COMPILE_EXPR
 #define RUST_COMPILE_EXPR
 
+#include "optional.h"
 #include "rust-compile-base.h"
 #include "rust-gcc.h"
 #include "rust-hir-expr.h"
@@ -165,6 +166,8 @@ protected:
   tree lookup_label (NodeId to_be_resolved);
   Bvariable *lookup_label_temp_var (NodeId to_be_resolved);
   HirId resolve_nodeid (NodeId to_be_resolved, Resolver2_0::Namespace ns);
+  std::pair<tree, tree>
+  construct_loop_labels (tl::optional<HIR::LoopLabel> loop_label);
 
 private:
   CompileExpr (Context *ctx);
diff --git a/gcc/rust/resolve/rust-default-resolver.cc b/gcc/rust/resolve/rust-default-resolver.cc
index ba65308688f4..9b23ab38bc23 100644
--- a/gcc/rust/resolve/rust-default-resolver.cc
+++ b/gcc/rust/resolve/rust-default-resolver.cc
@@ -90,6 +90,26 @@ DefaultResolver::visit (AST::Function &function)
 	      function.get_function_name ());
 }
 
+void
+DefaultResolver::visit (AST::LoopExpr &expr)
+{
+  ctx.scoped (Rib::Kind::Normal, expr.get_node_id (),
+	      [this, &expr] () { AST::DefaultASTVisitor::visit (expr); });
+}
+
+void
+DefaultResolver::visit (AST::WhileLoopExpr &expr)
+{
+  ctx.scoped (Rib::Kind::Normal, expr.get_node_id (),
+	      [this, &expr] () { AST::DefaultASTVisitor::visit (expr); });
+}
+
+void
+DefaultResolver::visit (AST::WhileLetLoopExpr &expr)
+{
+  ctx.scoped (Rib::Kind::Normal, expr.get_node_id (),
+	      [this, &expr] () { AST::DefaultASTVisitor::visit (expr); });
+}
 void
 DefaultResolver::visit (AST::ForLoopExpr &expr)
 {
diff --git a/gcc/rust/resolve/rust-default-resolver.h b/gcc/rust/resolve/rust-default-resolver.h
index d3e4e17f882f..2d53ff3a73bb 100644
--- a/gcc/rust/resolve/rust-default-resolver.h
+++ b/gcc/rust/resolve/rust-default-resolver.h
@@ -47,6 +47,9 @@ public:
   void visit (AST::BlockExpr &) override;
   void visit (AST::Module &) override;
   void visit (AST::Function &) override;
+  void visit (AST::LoopExpr &expr) override;
+  void visit (AST::WhileLoopExpr &expr) override;
+  void visit (AST::WhileLetLoopExpr &expr) override;
   void visit (AST::ForLoopExpr &expr) override;
   virtual void visit_if_let_patterns (AST::IfLetExpr &expr);
   void visit (AST::IfLetExpr &expr) override;
diff --git a/gcc/rust/resolve/rust-name-resolution-context.cc b/gcc/rust/resolve/rust-name-resolution-context.cc
index cff4c9ecc18f..1817627600ce 100644
--- a/gcc/rust/resolve/rust-name-resolution-context.cc
+++ b/gcc/rust/resolve/rust-name-resolution-context.cc
@@ -224,7 +224,7 @@ NameResolutionContext::insert (Identifier name, NodeId id, Namespace ns)
       return macros.insert (name, id);
     case Namespace::Labels:
     default:
-      // return labels.insert (name, id);
+      return labels.insert (name, id);
       rust_unreachable ();
     }
 }
@@ -252,8 +252,8 @@ NameResolutionContext::insert_shadowable (Identifier name, NodeId id,
     case Namespace::Macros:
       return macros.insert_shadowable (name, id);
     case Namespace::Labels:
+      return labels.insert (name, id);
     default:
-      // return labels.insert (name, id);
       rust_unreachable ();
     }
 }
@@ -270,8 +270,8 @@ NameResolutionContext::insert_globbed (Identifier name, NodeId id, Namespace ns)
     case Namespace::Macros:
       return macros.insert_globbed (name, id);
     case Namespace::Labels:
+      return labels.insert (name, id);
     default:
-      // return labels.insert (name, id);
       rust_unreachable ();
     }
 }
@@ -348,14 +348,14 @@ NameResolutionContext::scoped (Rib::Kind rib_kind, NodeId id,
   values.push (rib_kind, id, path);
   types.push (rib_kind, id, path);
   macros.push (rib_kind, id, path);
-  // labels.push (rib, id);
+  labels.push (rib_kind, id, path);
 
   lambda ();
 
   values.pop ();
   types.pop ();
   macros.pop ();
-  // labels.pop (rib);
+  labels.pop ();
 }
 
 void
@@ -376,6 +376,8 @@ NameResolutionContext::scoped (Rib::Kind rib_kind, Namespace ns,
       types.push (rib_kind, scope_id, path);
       break;
     case Namespace::Labels:
+      labels.push (rib_kind, scope_id, path);
+      break;
     case Namespace::Macros:
       gcc_unreachable ();
     }
@@ -391,6 +393,8 @@ NameResolutionContext::scoped (Rib::Kind rib_kind, Namespace ns,
       types.pop ();
       break;
     case Namespace::Labels:
+      labels.pop ();
+      break;
     case Namespace::Macros:
       gcc_unreachable ();
     }
diff --git a/gcc/testsuite/rust/execute/cf-break-continue.rs b/gcc/testsuite/rust/execute/cf-break-continue.rs
new file mode 100644
index 000000000000..aaa7074215a6
--- /dev/null
+++ b/gcc/testsuite/rust/execute/cf-break-continue.rs
@@ -0,0 +1,49 @@
+// { dg-options "-w" }
+// { dg-output "prime\r*\nnot_prime\r*\nprime\r*\nprime\r*\nnot_prime\r*\n" }
+#![feature(no_core)]
+#![no_core]
+extern "C" {
+    fn puts(s: *const i8);
+}
+fn dump(message: &str) {
+    unsafe {
+        let b = message as *const str;
+        let c = b as *const i8;
+        puts(c);
+    }
+}
+fn is_prime(number: i32) -> bool {
+    if number <= 1 {
+        return false;
+    }
+    let mut i = 1;
+    'prime: loop {
+        i += 1;
+        if i * i >= number {
+            break 'prime;
+        }
+        if number % i != 0 {
+            continue 'prime;
+        }
+        return false;
+    }
+    return true;
+}
+
+fn debug_prime(number: i32) {
+    let state = is_prime(number);
+    if state {
+        dump("prime");
+    } else {
+        dump("not_prime");
+    }
+}
+
+fn main() -> i32 {
+    debug_prime(11);
+    debug_prime(12);
+    debug_prime(13);
+    debug_prime(17);
+    debug_prime(100);
+    0
+}
diff --git a/gcc/testsuite/rust/execute/cf-label-shadowing.rs b/gcc/testsuite/rust/execute/cf-label-shadowing.rs
new file mode 100644
index 000000000000..9c2cad791ff2
--- /dev/null
+++ b/gcc/testsuite/rust/execute/cf-label-shadowing.rs
@@ -0,0 +1,47 @@
+// { dg-output "100\r*\n200\r*\n300\r*\n201\r*\n302\r*\n101\r*\n210\r*\n310\r*\n211\r*\n312\r*\n102\r*\n220\r*\n320\r*\n221\r*\n322\r*\n999" }
+#![feature(no_core)]
+#![no_core]
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+fn dump_number(num: i32) {
+    unsafe {
+        let fmt = "%i\n\0";
+        let c = fmt as *const str as *const i8;
+        printf(c, num);
+    }
+}
+
+fn main() -> i32 {
+    let mut outer = 0;
+
+    'l: while outer < 3 {
+        dump_number(100 + outer);
+
+        let mut middle = 0;
+        'l: loop {
+            dump_number(200 + outer * 10 + middle);
+
+            let mut inner = 0;
+            'l: loop {
+                dump_number(300 + outer * 10 + middle * 2 + inner);
+                break 'l;
+            }
+
+            middle += 1;
+            if middle >= 2 {
+                break 'l;
+            }
+        }
+
+        outer += 1;
+        if outer < 3 {
+            continue 'l;
+        }
+
+        break 'l;
+    }
+
+    dump_number(999);
+    0
+}
diff --git a/gcc/testsuite/rust/execute/cf-labeled-break-nested.rs b/gcc/testsuite/rust/execute/cf-labeled-break-nested.rs
new file mode 100644
index 000000000000..e655d7f2d38a
--- /dev/null
+++ b/gcc/testsuite/rust/execute/cf-labeled-break-nested.rs
@@ -0,0 +1,39 @@
+// { dg-output "999\r*\n3000\r*\n" }
+#![feature(no_core)]
+#![no_core]
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+fn dump_number(num: i32) {
+    unsafe {
+        let fmt = "%i\n\0";
+        let c = fmt as *const str as *const i8;
+        printf(c, num);
+    }
+}
+
+fn run() -> i32 {
+    let mut outer = 0;
+    let mut score = 0;
+
+    'outer: while outer < 5 {
+        let mut inner = 0;
+        'inner: loop {
+            if inner == 3 {
+                break 'outer;
+            }
+            score += 1000;
+            inner += 1;
+        }
+        score += 1000;
+        outer += 1;
+    }
+    score
+}
+
+fn main() -> i32 {
+    let total = run();
+    dump_number(999);
+    dump_number(total);
+    0
+}
diff --git a/gcc/testsuite/rust/execute/cf-labeled-continue-nested.rs b/gcc/testsuite/rust/execute/cf-labeled-continue-nested.rs
new file mode 100644
index 000000000000..8d3a8ddd2e0e
--- /dev/null
+++ b/gcc/testsuite/rust/execute/cf-labeled-continue-nested.rs
@@ -0,0 +1,46 @@
+// { dg-output "10\r*\n15\r*\n35\r*\n" }
+#![feature(no_core)]
+#![no_core]
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+fn dump_number(num: i32) {
+    unsafe {
+        let fmt = "%i\n\0";
+        let c = fmt as *const str as *const i8;
+        printf(c, num);
+    }
+}
+
+fn run() {
+    let mut sum = 0;
+    let mut i = 0;
+
+    'outer: while i < 5 {
+        i += 1;
+
+        let mut j = 0;
+        'inner: loop {
+            j += 1;
+
+            if j == 2 {
+                continue 'outer;
+            }
+
+            if j > 3 {
+                break 'inner;
+            }
+
+            sum += i * j;
+        }
+    }
+
+    dump_number(10);
+    dump_number(sum);
+    dump_number(35);
+}
+
+fn main() -> i32 {
+    run();
+    0
+}
diff --git a/gcc/testsuite/rust/execute/cf-labeled-loops.rs b/gcc/testsuite/rust/execute/cf-labeled-loops.rs
new file mode 100644
index 000000000000..43e02e045fd7
--- /dev/null
+++ b/gcc/testsuite/rust/execute/cf-labeled-loops.rs
@@ -0,0 +1,34 @@
+// { dg-output "91\r*\n45\r*\n" }
+#![feature(no_core)]
+#![no_core]
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+fn dump_number(num: i32) {
+    unsafe {
+        let a = "%i\n\0";
+        let c = a as *const str as *const i8;
+        printf(c, num);
+    }
+}
+
+fn play(b: i32) -> i32 {
+    let mut res = 0;
+    let mut i = 0;
+    'calculation: while i < b {
+        res += i;
+        i += 1;
+        if res + i >= 99 {
+            break 'calculation;
+        }
+    }
+    res
+}
+
+fn main() -> i32 {
+    let a: i32 = play(111);
+    let b: i32 = play(10);
+    dump_number(a);
+    dump_number(b);
+    0
+}
diff --git a/gcc/testsuite/rust/execute/cf-loop-break-continue.rs b/gcc/testsuite/rust/execute/cf-loop-break-continue.rs
new file mode 100644
index 000000000000..ab25898fcdc3
--- /dev/null
+++ b/gcc/testsuite/rust/execute/cf-loop-break-continue.rs
@@ -0,0 +1,51 @@
+// { dg-options "-w" }
+// { dg-output "73\r*\n37\r*\n24\r*\n24\r*\n" }
+#![feature(no_core)]
+#![no_core]
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+fn dump_number(num: i32) {
+    unsafe {
+        let fmt = "%i\n\0";
+        let c = fmt as *const str as *const i8;
+        printf(c, num);
+    }
+}
+
+fn compute(limit: i32, stop_at: i32) -> i32 {
+    let mut i = 0;
+    let mut sum = 0;
+
+    loop {
+        if i >= limit {
+            break;
+        }
+
+        i += 1;
+
+        if i % 2 == 0 {
+            continue;
+        }
+
+        if i % 3 == 0 {
+            continue;
+        }
+
+        sum += i;
+
+        if sum >= stop_at {
+            break;
+        }
+    }
+
+    sum
+}
+
+fn main() -> i32 {
+    dump_number(compute(20, 100));
+    dump_number(compute(30, 26));
+    dump_number(compute(18, 17));
+    dump_number(compute(15, 14));
+    0
+}
diff --git a/gcc/testsuite/rust/execute/cf-mixed-labeled-unlabeled.rs b/gcc/testsuite/rust/execute/cf-mixed-labeled-unlabeled.rs
new file mode 100644
index 000000000000..68fb5990003a
--- /dev/null
+++ b/gcc/testsuite/rust/execute/cf-mixed-labeled-unlabeled.rs
@@ -0,0 +1,80 @@
+// { dg-output "73\r*\n12\r*\n5\r*\n" }
+#![feature(no_core)]
+#![no_core]
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+fn dump_number(num: i32) {
+    unsafe {
+        let a = "%i\n\0";
+        let c = a as *const str as *const i8;
+        printf(c, num);
+    }
+}
+fn mixed_flow(limit: i32) -> i32 {
+    let mut sum = 0;
+    let mut i = 0;
+
+    'outer: while i < limit {
+        i += 1;
+
+        if i % 2 == 0 {
+            continue;
+        }
+
+        let mut j = 0;
+        loop {
+            j += 1;
+
+            if j == 2 {
+                continue;
+            }
+
+            if i == 5 && j == 3 {
+                continue 'outer;
+            }
+
+            if i == 9 && j == 3 {
+                break 'outer;
+            }
+
+            sum += i + j;
+
+            if j >= 4 {
+                break;
+            }
+        }
+    }
+
+    sum
+}
+
+fn unlabeled_only(limit: i32) -> i32 {
+    let mut i = 0;
+    let mut acc = 0;
+
+    while i < limit {
+        i += 1;
+
+        if i == 3 {
+            continue;
+        }
+
+        acc += i;
+
+        if i == 5 {
+            break;
+        }
+    }
+
+    acc
+}
+
+fn main() -> i32 {
+    let a = mixed_flow(20);
+    let b = unlabeled_only(9);
+    dump_number(a);
+    dump_number(b);
+    dump_number(5);
+    0
+}
diff --git a/gcc/testsuite/rust/execute/cf-nested-loops.rs b/gcc/testsuite/rust/execute/cf-nested-loops.rs
new file mode 100644
index 000000000000..d16d6cd9fd3b
--- /dev/null
+++ b/gcc/testsuite/rust/execute/cf-nested-loops.rs
@@ -0,0 +1,36 @@
+// { dg-output "1\r*\n2\r*\n3\r*\n1\r*\n99\r*\n" }
+#![feature(no_core)]
+#![no_core]
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+fn dump_number(num: i32) {
+    unsafe {
+        let a = "%i\n\0";
+        let c = a as *const str as *const i8;
+        printf(c, num);
+    }
+}
+
+fn play1(b: i32) -> i32 {
+    'low: loop {
+        dump_number(1);
+        'mid: loop {
+            dump_number(2);
+            'high: loop {
+                dump_number(3);
+                break 'mid;
+                dump_number(9);
+            }
+            dump_number(7);
+        }
+        dump_number(1);
+        break 'low;
+    }
+    b
+}
+
+fn main() -> i32 {
+    dump_number(play1(99));
+    0
+}
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.