about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2025-09-01 14:42:07 +0000
committerbors <bors@rust-lang.org>2025-09-01 14:42:07 +0000
commit154037ffb82714a8d6264a9153622637b170c706 (patch)
treee8229b211c226a5fe2c7310112cbb0627839c285
parentc0bb3b98bb7aac24a37635e5d36d961e0b14f435 (diff)
parent3e76b58453461a7ac04db4914caff584345d8448 (diff)
downloadrust-154037ffb82714a8d6264a9153622637b170c706.tar.gz
rust-154037ffb82714a8d6264a9153622637b170c706.zip
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`
-rw-r--r--compiler/rustc_mir_build/src/builder/expr/into.rs8
-rw-r--r--compiler/rustc_mir_build/src/builder/matches/mod.rs2
-rw-r--r--tests/mir-opt/building/loop_match_diverges.break_to_block_unit.built.after.mir63
-rw-r--r--tests/mir-opt/building/loop_match_diverges.infinite_a.built.after.mir51
-rw-r--r--tests/mir-opt/building/loop_match_diverges.rs68
-rw-r--r--tests/mir-opt/building/loop_match_diverges.simple.built.after.mir187
-rw-r--r--tests/ui/loop-match/diverges.rs44
7 files changed, 419 insertions, 4 deletions
diff --git a/compiler/rustc_mir_build/src/builder/expr/into.rs b/compiler/rustc_mir_build/src/builder/expr/into.rs
index eb99c184bd2..7676b720e35 100644
--- a/compiler/rustc_mir_build/src/builder/expr/into.rs
+++ b/compiler/rustc_mir_build/src/builder/expr/into.rs
@@ -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,
diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs
index 6ee9674bb08..d216c4ecd11 100644
--- a/compiler/rustc_mir_build/src/builder/matches/mod.rs
+++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs
@@ -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,
diff --git a/tests/mir-opt/building/loop_match_diverges.break_to_block_unit.built.after.mir b/tests/mir-opt/building/loop_match_diverges.break_to_block_unit.built.after.mir
new file mode 100644
index 00000000000..6d779e46146
--- /dev/null
+++ b/tests/mir-opt/building/loop_match_diverges.break_to_block_unit.built.after.mir
@@ -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;
+    }
+}
diff --git a/tests/mir-opt/building/loop_match_diverges.infinite_a.built.after.mir b/tests/mir-opt/building/loop_match_diverges.infinite_a.built.after.mir
new file mode 100644
index 00000000000..e3766744790
--- /dev/null
+++ b/tests/mir-opt/building/loop_match_diverges.infinite_a.built.after.mir
@@ -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;
+    }
+}
diff --git a/tests/mir-opt/building/loop_match_diverges.rs b/tests/mir-opt/building/loop_match_diverges.rs
new file mode 100644
index 00000000000..774e195c33c
--- /dev/null
+++ b/tests/mir-opt/building/loop_match_diverges.rs
@@ -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,
+            }
+        }
+    }
+}
diff --git a/tests/mir-opt/building/loop_match_diverges.simple.built.after.mir b/tests/mir-opt/building/loop_match_diverges.simple.built.after.mir
new file mode 100644
index 00000000000..26476ad7762
--- /dev/null
+++ b/tests/mir-opt/building/loop_match_diverges.simple.built.after.mir
@@ -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;
+    }
+}
diff --git a/tests/ui/loop-match/diverges.rs b/tests/ui/loop-match/diverges.rs
new file mode 100644
index 00000000000..f1b3ffb2076
--- /dev/null
+++ b/tests/ui/loop-match/diverges.rs
@@ -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,
+            }
+        }
+    }
+}