[COMMITTED 54/77] gccrs: Improve resolution and compilation of control flow loops and labels

[email protected]
Newsgroups gmane.comp.gcc.patches,gmane.comp.gcc.rust
Message-ID <[email protected]>
From: Islam-Imad <[email protected]>

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]>
---
 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 +
 .../resolve/rust-name-resolution-context.cc   |  14 +-
 .../rust/execute/cf-break-continue.rs         |  49 ++++++
 .../rust/execute/cf-label-shadowing.rs        |  47 ++++++
 .../rust/execute/cf-labeled-break-nested.rs   |  39 +++++
 .../execute/cf-labeled-continue-nested.rs     |  46 ++++++
 .../rust/execute/cf-labeled-loops.rs          |  34 +++++
 .../rust/execute/cf-loop-break-continue.rs    |  51 +++++++
 .../execute/cf-mixed-labeled-unlabeled.rs     |  80 ++++++++++
 gcc/testsuite/rust/execute/cf-nested-loops.rs |  36 +++++
 14 files changed, 546 insertions(+), 63 deletions(-)
 create mode 100644 gcc/testsuite/rust/execute/cf-break-continue.rs
 create mode 100644 gcc/testsuite/rust/execute/cf-label-shadowing.rs
 create mode 100644 gcc/testsuite/rust/execute/cf-labeled-break-nested.rs
 create mode 100644 gcc/testsuite/rust/execute/cf-labeled-continue-nested.rs
 create mode 100644 gcc/testsuite/rust/execute/cf-labeled-loops.rs
 create mode 100644 gcc/testsuite/rust/execute/cf-loop-break-continue.rs
 create mode 100644 gcc/testsuite/rust/execute/cf-mixed-labeled-unlabeled.rs
 create mode 100644 gcc/testsuite/rust/execute/cf-nested-loops.rs

diff --git a/gcc/rust/backend/rust-compile-context.h b/gcc/rust/backend/rust-compile-context.h
index 7f67b9382e0..2c71f7778a5 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 52ddffd1df5..c0d4ab0a04b 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 ad5c483221a..452fa28cff0 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 ba65308688f..9b23ab38bc2 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 d3e4e17f882..2d53ff3a73b 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 cff4c9ecc18..1817627600c 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 00000000000..aaa7074215a
--- /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 00000000000..9c2cad791ff
--- /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 00000000000..e655d7f2d38
--- /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 00000000000..8d3a8ddd2e0
--- /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 00000000000..43e02e045fd
--- /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 00000000000..ab25898fcdc
--- /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 00000000000..68fb5990003
--- /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 00000000000..d16d6cd9fd3
--- /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
+}
-- 
2.50.1
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.