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