https://gcc.gnu.org/g:88bb791cf749c413d5307fc28fb3c1a138be0096
commit r17-4103-g88bb791cf749c413d5307fc28fb3c1a138be0096 Author: Lishin <[email protected]> Date: Thu Jul 16 10:57:56 2026 +0000 gccrs: Run block cleanup for unlabeled break and continue Make unlabeled break return its translated tree, matching continue. Emit tail-position break and continue expressions as statements so they stay inside the TRY_FINALLY_EXPR body and run block cleanup. gcc/rust/ChangeLog: * backend/rust-compile-block.cc (is_control_flow_expr): New helper. (CompileBlock::visit): Emit tail-position break and continue expressions as statements. * backend/rust-compile-expr.cc (CompileExpr::visit): Return the translated expression for unlabeled break instead of emitting it directly. gcc/testsuite/ChangeLog: * rust/execute/drop-unlabeled-break-continue.rs: New test. Signed-off-by: Lishin <[email protected]> Diff: --- gcc/rust/backend/rust-compile-block.cc | 21 +- gcc/rust/backend/rust-compile-expr.cc | 3 +- .../rust/execute/drop-unlabeled-break-continue.rs | 263 +++++++++++++++++++++ 3 files changed, 283 insertions(+), 4 deletions(-) diff --git a/gcc/rust/backend/rust-compile-block.cc b/gcc/rust/backend/rust-compile-block.cc index ca69315c3b5d..6f61856c3800 100644 --- a/gcc/rust/backend/rust-compile-block.cc +++ b/gcc/rust/backend/rust-compile-block.cc @@ -25,6 +25,15 @@ namespace Rust { namespace Compile { +static bool +is_control_flow_expr (HIR::Expr &expr) +{ + HIR::Expr::ExprType expr_type = expr.get_expression_type (); + + return expr_type == HIR::Expr::ExprType::Break + || expr_type == HIR::Expr::ExprType::Continue; +} + CompileBlock::CompileBlock (Context *ctx, Bvariable *result) : HIRCompileBase (ctx), translated (nullptr), result (result) {} @@ -62,10 +71,12 @@ CompileBlock::visit (HIR::BlockExpr &expr) if (expr.has_expr ()) { - tree compiled_expr = CompileExpr::Compile (expr.get_final_expr (), ctx); + HIR::Expr &final_expr = expr.get_final_expr (); + tree compiled_expr = CompileExpr::Compile (final_expr, ctx); + if (result != nullptr) { - location_t locus = expr.get_final_expr ().get_locus (); + location_t locus = final_expr.get_locus (); tree result_reference = Backend::var_expression (result, locus); tree assignment @@ -73,6 +84,12 @@ CompileBlock::visit (HIR::BlockExpr &expr) expr.get_locus ()); ctx->add_statement (assignment); } + else if (compiled_expr != nullptr && is_control_flow_expr (final_expr) + && compiled_expr != error_mark_node) + { + tree stmt = convert_to_void (compiled_expr, ICV_STATEMENT); + ctx->add_statement (stmt); + } } else if (result != nullptr) { diff --git a/gcc/rust/backend/rust-compile-expr.cc b/gcc/rust/backend/rust-compile-expr.cc index 2c901187da4a..9eac66e8bcc6 100644 --- a/gcc/rust/backend/rust-compile-expr.cc +++ b/gcc/rust/backend/rust-compile-expr.cc @@ -1132,10 +1132,9 @@ CompileExpr::visit (HIR::BreakExpr &expr) } else { - tree exit_expr + translated = Backend::exit_expression (Backend::boolean_constant_expression (true), expr.get_locus ()); - ctx->add_statement (exit_expr); } } diff --git a/gcc/testsuite/rust/execute/drop-unlabeled-break-continue.rs b/gcc/testsuite/rust/execute/drop-unlabeled-break-continue.rs new file mode 100644 index 000000000000..5bcae5592894 --- /dev/null +++ b/gcc/testsuite/rust/execute/drop-unlabeled-break-continue.rs @@ -0,0 +1,263 @@ +// { dg-output "continue inner\r*\ncontinue outer\r*\ncontinue tail inner\r*\ncontinue tail outer\r*\nbreak inner\r*\nbreak outer\r*\nbreak tail inner\r*\nbreak tail outer\r*\nbreak value\r*\nvalue ok\r*\nwhile continue inner\r*\nwhile continue outer\r*\nwhile break inner\r*\nwhile break outer\r*\n" } +// { dg-additional-options "-w" } +#![feature(no_core)] +#![feature(lang_items)] +#![no_core] + +extern "C" { + fn printf(s: *const i8, ...); +} + +#[lang = "sized"] +pub trait Sized {} + +#[lang = "drop"] +pub trait Drop { + fn drop(&mut self); +} + +struct ContinueOuter; +struct ContinueInner; +struct ContinueTailOuter; +struct ContinueTailInner; +struct BreakOuter; +struct BreakInner; +struct BreakTailOuter; +struct BreakTailInner; +struct BreakValue; +struct WhileContinueOuter; +struct WhileContinueInner; +struct WhileBreakOuter; +struct WhileBreakInner; + +impl Drop for ContinueOuter { + fn drop(&mut self) { + let msg = "continue outer\n\0" as *const str as *const i8; + unsafe { + printf(msg); + } + } +} + +impl Drop for ContinueInner { + fn drop(&mut self) { + let msg = "continue inner\n\0" as *const str as *const i8; + unsafe { + printf(msg); + } + } +} + +impl Drop for ContinueTailOuter { + fn drop(&mut self) { + let msg = "continue tail outer\n\0" as *const str as *const i8; + unsafe { + printf(msg); + } + } +} + +impl Drop for ContinueTailInner { + fn drop(&mut self) { + let msg = "continue tail inner\n\0" as *const str as *const i8; + unsafe { + printf(msg); + } + } +} + +impl Drop for BreakOuter { + fn drop(&mut self) { + let msg = "break outer\n\0" as *const str as *const i8; + unsafe { + printf(msg); + } + } +} + +impl Drop for BreakInner { + fn drop(&mut self) { + let msg = "break inner\n\0" as *const str as *const i8; + unsafe { + printf(msg); + } + } +} + +impl Drop for BreakTailOuter { + fn drop(&mut self) { + let msg = "break tail outer\n\0" as *const str as *const i8; + unsafe { + printf(msg); + } + } +} + +impl Drop for BreakTailInner { + fn drop(&mut self) { + let msg = "break tail inner\n\0" as *const str as *const i8; + unsafe { + printf(msg); + } + } +} + +impl Drop for BreakValue { + fn drop(&mut self) { + let msg = "break value\n\0" as *const str as *const i8; + unsafe { + printf(msg); + } + } +} + +impl Drop for WhileContinueOuter { + fn drop(&mut self) { + let msg = "while continue outer\n\0" as *const str as *const i8; + unsafe { + printf(msg); + } + } +} + +impl Drop for WhileContinueInner { + fn drop(&mut self) { + let msg = "while continue inner\n\0" as *const str as *const i8; + unsafe { + printf(msg); + } + } +} + +impl Drop for WhileBreakOuter { + fn drop(&mut self) { + let msg = "while break outer\n\0" as *const str as *const i8; + unsafe { + printf(msg); + } + } +} + +impl Drop for WhileBreakInner { + fn drop(&mut self) { + let msg = "while break inner\n\0" as *const str as *const i8; + unsafe { + printf(msg); + } + } +} + +fn test_continue() { + let mut done = false; + + loop { + if done { + break; + } + + { + let _outer = ContinueOuter; + + { + let _inner = ContinueInner; + done = true; + continue; + } + } + } +} + +fn test_continue_tail_expr() { + let mut done = false; + + loop { + if done { + break; + } + + { + let _outer = ContinueTailOuter; + + { + let _inner = ContinueTailInner; + done = true; + continue + } + } + } +} + +fn test_break() { + loop { + let _outer = BreakOuter; + + { + let _inner = BreakInner; + break; + } + } +} + +fn test_break_tail_expr() { + loop { + let _outer = BreakTailOuter; + + { + let _inner = BreakTailInner; + break + } + } +} + +fn test_break_value() { + let x = loop { + let _value = BreakValue; + break 123; + }; + + if x == 123 { + let msg = "value ok\n\0" as *const str as *const i8; + unsafe { + printf(msg); + } + } +} + +fn test_while_continue() { + let mut i = 0; + + while i < 1 { + let _outer = WhileContinueOuter; + + { + let _inner = WhileContinueInner; + i = i + 1; + continue; + } + } +} + +fn test_while_break() { + let i = 0; + + while i < 1 { + let _outer = WhileBreakOuter; + + { + let _inner = WhileBreakInner; + break; + } + } +} + +fn main() -> i32 { + test_continue(); + test_continue_tail_expr(); + test_break(); + test_break_tail_expr(); + test_break_value(); + test_while_continue(); + test_while_break(); + + 0 +}
