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 +}
