Auto merge of #144783 - folkertdev:loop-match-diverging-loop, r=SparrowLii

fix `#[loop_match]` on diverging loop

tracking issue: https://github.com/rust-lang/rust/issues/132306

fixes https://github.com/rust-lang/rust/issues/144492
fixes https://github.com/rust-lang/rust/issues/144493

fixes https://github.com/rust-lang/rust/issues/144781

this generated invalid MIR before. issue https://github.com/rust-lang/rust/issues/143806 still has an issue where we assign `state = state` which is invalid in MIR. Fixing that problem is tricky, so I'd like to do that separately.

r? `@bjorn3`
This commit is contained in:
bors 2025-09-01 14:42:07 +00:00
commit 154037ffb8
7 changed files with 419 additions and 4 deletions

View file

@ -293,9 +293,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
this.diverge_from(loop_block);
// Logic for `match`.
let scrutinee_place_builder =
unpack!(body_block = this.as_place_builder(body_block, scrutinee));
let scrutinee_span = this.thir.exprs[scrutinee].span;
let scrutinee_place_builder = unpack!(
body_block = this.lower_scrutinee(body_block, scrutinee, scrutinee_span)
);
let match_start_span = match_span.shrink_to_lo().to(scrutinee_span);
let mut patterns = Vec::with_capacity(arms.len());
@ -343,7 +345,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
expr_span,
|this| {
this.lower_match_arms(
destination,
state_place,
scrutinee_place_builder,
scrutinee_span,
arms,

View file

@ -388,7 +388,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
}
/// Evaluate the scrutinee and add the PlaceMention for it.
fn lower_scrutinee(
pub(crate) fn lower_scrutinee(
&mut self,
mut block: BasicBlock,
scrutinee_id: ExprId,

View file

@ -0,0 +1,63 @@
// MIR for `break_to_block_unit` after built
fn break_to_block_unit() -> u8 {
let mut _0: u8;
let mut _1: i32;
let mut _2: !;
scope 1 {
debug state => _1;
}
bb0: {
StorageLive(_1);
_1 = const 0_i32;
FakeRead(ForLet(None), _1);
StorageLive(_2);
goto -> bb1;
}
bb1: {
falseUnwind -> [real: bb2, unwind: bb10];
}
bb2: {
PlaceMention(_1);
_1 = const 2_i32;
goto -> bb5;
}
bb3: {
FakeRead(ForMatchedPlace(None), _1);
unreachable;
}
bb4: {
goto -> bb6;
}
bb5: {
goto -> bb6;
}
bb6: {
goto -> bb7;
}
bb7: {
goto -> bb1;
}
bb8: {
unreachable;
}
bb9: {
StorageDead(_2);
StorageDead(_1);
return;
}
bb10 (cleanup): {
resume;
}
}

View file

@ -0,0 +1,51 @@
// MIR for `infinite_a` after built
fn infinite_a(_1: u8) -> () {
debug state => _1;
let mut _0: ();
let mut _2: !;
let _3: u8;
scope 1 {
debug a => _3;
}
bb0: {
StorageLive(_2);
goto -> bb1;
}
bb1: {
falseUnwind -> [real: bb2, unwind: bb7];
}
bb2: {
PlaceMention(_1);
StorageLive(_3);
_3 = copy _1;
_1 = copy _3;
StorageDead(_3);
goto -> bb4;
}
bb3: {
FakeRead(ForMatchedPlace(None), _1);
unreachable;
}
bb4: {
goto -> bb1;
}
bb5: {
unreachable;
}
bb6: {
StorageDead(_2);
return;
}
bb7 (cleanup): {
resume;
}
}

View file

@ -0,0 +1,68 @@
// skip-filecheck
#![allow(incomplete_features)]
#![feature(loop_match)]
#![crate_type = "lib"]
// Test that a #[loop_match] without an explicit break from the loop generates valid MIR.
enum State {
A,
B,
C,
}
// EMIT_MIR loop_match_diverges.simple.built.after.mir
fn simple(mut state: State) -> State {
#[loop_match]
'a: loop {
state = 'blk: {
match state {
State::A => {
#[const_continue]
break 'blk State::B;
}
State::B => {
if true {
#[const_continue]
break 'blk State::C;
} else {
#[const_continue]
break 'blk State::A;
}
}
State::C => break 'a,
}
};
}
state
}
// EMIT_MIR loop_match_diverges.break_to_block_unit.built.after.mir
#[unsafe(no_mangle)]
fn break_to_block_unit() -> u8 {
let mut state = 0;
#[loop_match]
loop {
state = 'blk: {
match state {
_ => 'b: {
break 'b 2;
}
}
}
}
}
// EMIT_MIR loop_match_diverges.infinite_a.built.after.mir
#[unsafe(no_mangle)]
fn infinite_a(mut state: u8) {
#[loop_match]
loop {
state = 'blk: {
match state {
a => a,
}
}
}
}

View file

@ -0,0 +1,187 @@
// MIR for `simple` after built
fn simple(_1: State) -> State {
debug state => _1;
let mut _0: State;
let _2: ();
let mut _3: isize;
let mut _4: !;
let mut _5: isize;
let mut _6: bool;
let mut _7: !;
let mut _8: isize;
let mut _9: !;
let mut _10: isize;
let mut _11: !;
bb0: {
StorageLive(_2);
goto -> bb1;
}
bb1: {
falseUnwind -> [real: bb2, unwind: bb37];
}
bb2: {
PlaceMention(_1);
_3 = discriminant(_1);
switchInt(move _3) -> [0: bb4, 1: bb6, 2: bb8, otherwise: bb3];
}
bb3: {
FakeRead(ForMatchedPlace(None), _1);
unreachable;
}
bb4: {
falseEdge -> [real: bb11, imaginary: bb6];
}
bb5: {
goto -> bb3;
}
bb6: {
falseEdge -> [real: bb10, imaginary: bb8];
}
bb7: {
goto -> bb3;
}
bb8: {
_2 = const ();
goto -> bb36;
}
bb9: {
goto -> bb3;
}
bb10: {
StorageLive(_6);
_6 = const true;
switchInt(move _6) -> [0: bb17, otherwise: bb16];
}
bb11: {
_1 = State::B;
_5 = discriminant(_1);
falseEdge -> [real: bb12, imaginary: bb13];
}
bb12: {
goto -> bb10;
}
bb13: {
goto -> bb34;
}
bb14: {
unreachable;
}
bb15: {
goto -> bb32;
}
bb16: {
_1 = State::C;
_8 = discriminant(_1);
falseEdge -> [real: bb18, imaginary: bb19];
}
bb17: {
goto -> bb23;
}
bb18: {
goto -> bb20;
}
bb19: {
goto -> bb33;
}
bb20: {
StorageDead(_6);
goto -> bb8;
}
bb21: {
unreachable;
}
bb22: {
goto -> bb29;
}
bb23: {
_1 = State::A;
_10 = discriminant(_1);
falseEdge -> [real: bb24, imaginary: bb25];
}
bb24: {
goto -> bb26;
}
bb25: {
goto -> bb33;
}
bb26: {
StorageDead(_6);
goto -> bb11;
}
bb27: {
unreachable;
}
bb28: {
goto -> bb29;
}
bb29: {
StorageDead(_6);
goto -> bb32;
}
bb30: {
unreachable;
}
bb31: {
goto -> bb32;
}
bb32: {
goto -> bb35;
}
bb33: {
StorageDead(_6);
goto -> bb34;
}
bb34: {
goto -> bb35;
}
bb35: {
goto -> bb1;
}
bb36: {
StorageDead(_2);
_0 = move _1;
return;
}
bb37 (cleanup): {
resume;
}
}

View file

@ -0,0 +1,44 @@
//@ build-pass
//@ compile-flags: -Zvalidate-mir
#![allow(incomplete_features)]
#![feature(loop_match)]
#![crate_type = "lib"]
// Test that a #[loop_match] without an explicit break from the loop generates valid MIR.
fn break_to_block_unit() -> u8 {
let mut state = 0;
#[loop_match]
loop {
state = 'blk: {
match state {
_ => 'b: {
break 'b 2;
}
}
}
}
}
fn break_to_block_value() -> u8 {
let mut state = 0u8;
#[loop_match]
'a: loop {
state = 'blk: {
match state {
_ => break 'blk state,
}
}
}
}
fn infinite_a(mut state: u8) {
#[loop_match]
loop {
state = 'blk: {
match state {
a => a,
}
}
}
}