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]>
---
This change was merged into the gccrs repository and is posted here for
upstream visibility and potential drive-by review, as requested by GCC
release managers.
Each commit email contains a link to its details on github from where you can
find the Pull-Request and associated discussions.


Commit on github: 
https://github.com/Rust-GCC/gccrs/commit/bf3ef5f03c3e2b340266a5d015c45bd47e890b42

The commit has NOT been mentioned in any issue.

The commit has been mentioned in the following pull-request(s):
 - https://github.com/Rust-GCC/gccrs/pull/4522

 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 7f67b9382..2c71f7778 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 52ddffd1d..c0d4ab0a0 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 ad5c48322..452fa28cf 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 ba6530868..9b23ab38b 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 d3e4e17f8..2d53ff3a7 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 cff4c9ecc..181762760 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 000000000..aaa707421
--- /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 000000000..9c2cad791
--- /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 000000000..e655d7f2d
--- /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 000000000..8d3a8ddd2
--- /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 000000000..43e02e045
--- /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 000000000..ab25898fc
--- /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 000000000..68fb59900
--- /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 000000000..d16d6cd9f
--- /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
+}

base-commit: f53262ab410a732629a2d2ea56ceffc9eb367637
-- 
2.54.0

Reply via email to