about summary refs log tree commit diff
diff options
context:
space:
mode:
authorStuart Cook <Zalathar@users.noreply.github.com>2025-07-29 20:19:49 +1000
committerGitHub <noreply@github.com>2025-07-29 20:19:49 +1000
commitdbcf16856850bc7f32256df3543e6936d2bbc864 (patch)
tree5a9a21823f954bb812f1d68fdd46b395eac3ee8b
parentb3962e88114a5f633011d8670f318e1c20ba41b6 (diff)
parentd87b4f2c77add3404f7469ea72f1d7c51e2a29dc (diff)
downloadrust-dbcf16856850bc7f32256df3543e6936d2bbc864.tar.gz
rust-dbcf16856850bc7f32256df3543e6936d2bbc864.zip
Rollup merge of #144451 - ShoyuVanilla:loop-match-upvar, r=oli-obk
fix: Reject upvar scrutinees for `loop_match`

Fixes https://github.com/rust-lang/rust/issues/144051

I think we should reject upvars as they are not locals but somewhat like field access
-rw-r--r--compiler/rustc_mir_build/src/thir/cx/expr.rs21
-rw-r--r--tests/ui/loop-match/upvar-scrutinee.rs81
-rw-r--r--tests/ui/loop-match/upvar-scrutinee.stderr18
3 files changed, 113 insertions, 7 deletions
diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs
index 33bf4e3e29f..16df58cd76d 100644
--- a/compiler/rustc_mir_build/src/thir/cx/expr.rs
+++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs
@@ -955,9 +955,13 @@ impl<'tcx> ThirBuildCx<'tcx> {
                         dcx.emit_fatal(LoopMatchBadRhs { span: block_body_expr.span })
                     };
 
-                    fn local(expr: &rustc_hir::Expr<'_>) -> Option<hir::HirId> {
+                    fn local(
+                        cx: &mut ThirBuildCx<'_>,
+                        expr: &rustc_hir::Expr<'_>,
+                    ) -> Option<hir::HirId> {
                         if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = expr.kind
                             && let Res::Local(hir_id) = path.res
+                            && !cx.is_upvar(hir_id)
                         {
                             return Some(hir_id);
                         }
@@ -965,11 +969,11 @@ impl<'tcx> ThirBuildCx<'tcx> {
                         None
                     }
 
-                    let Some(scrutinee_hir_id) = local(scrutinee) else {
+                    let Some(scrutinee_hir_id) = local(self, scrutinee) else {
                         dcx.emit_fatal(LoopMatchInvalidMatch { span: scrutinee.span })
                     };
 
-                    if local(state) != Some(scrutinee_hir_id) {
+                    if local(self, state) != Some(scrutinee_hir_id) {
                         dcx.emit_fatal(LoopMatchInvalidUpdate {
                             scrutinee: scrutinee.span,
                             lhs: state.span,
@@ -1260,10 +1264,7 @@ impl<'tcx> ThirBuildCx<'tcx> {
     fn convert_var(&mut self, var_hir_id: hir::HirId) -> ExprKind<'tcx> {
         // We want upvars here not captures.
         // Captures will be handled in MIR.
-        let is_upvar = self
-            .tcx
-            .upvars_mentioned(self.body_owner)
-            .is_some_and(|upvars| upvars.contains_key(&var_hir_id));
+        let is_upvar = self.is_upvar(var_hir_id);
 
         debug!(
             "convert_var({:?}): is_upvar={}, body_owner={:?}",
@@ -1443,6 +1444,12 @@ impl<'tcx> ThirBuildCx<'tcx> {
         }
     }
 
+    fn is_upvar(&mut self, var_hir_id: hir::HirId) -> bool {
+        self.tcx
+            .upvars_mentioned(self.body_owner)
+            .is_some_and(|upvars| upvars.contains_key(&var_hir_id))
+    }
+
     /// Converts a list of named fields (i.e., for struct-like struct/enum ADTs) into FieldExpr.
     fn field_refs(&mut self, fields: &'tcx [hir::ExprField<'tcx>]) -> Box<[FieldExpr]> {
         fields
diff --git a/tests/ui/loop-match/upvar-scrutinee.rs b/tests/ui/loop-match/upvar-scrutinee.rs
new file mode 100644
index 00000000000..a93e3a0e59a
--- /dev/null
+++ b/tests/ui/loop-match/upvar-scrutinee.rs
@@ -0,0 +1,81 @@
+#![allow(incomplete_features)]
+#![feature(loop_match)]
+
+#[derive(Clone, Copy)]
+enum State {
+    A,
+    B,
+}
+
+fn main() {
+    let mut state = State::A;
+
+    #[loop_match]
+    loop {
+        state = 'blk: {
+            match state {
+                State::A => {
+                    #[const_continue]
+                    break 'blk State::B;
+                }
+                State::B => {
+                    return;
+                }
+            }
+        }
+    }
+
+    || {
+        #[loop_match]
+        loop {
+            state = 'blk: {
+                match state {
+                    //~^ ERROR invalid match on `#[loop_match]` state
+                    State::A => {
+                        #[const_continue]
+                        break 'blk State::B;
+                    }
+                    State::B => {
+                        return;
+                    }
+                }
+            }
+        }
+    };
+
+    || {
+        let mut state = state;
+        #[loop_match]
+        loop {
+            state = 'blk: {
+                match state {
+                    State::A => {
+                        #[const_continue]
+                        break 'blk State::B;
+                    }
+                    State::B => {
+                        return;
+                    }
+                }
+            }
+        }
+    };
+
+    move || {
+        #[loop_match]
+        loop {
+            state = 'blk: {
+                match state {
+                    //~^ ERROR invalid match on `#[loop_match]` state
+                    State::A => {
+                        #[const_continue]
+                        break 'blk State::B;
+                    }
+                    State::B => {
+                        return;
+                    }
+                }
+            }
+        }
+    };
+}
diff --git a/tests/ui/loop-match/upvar-scrutinee.stderr b/tests/ui/loop-match/upvar-scrutinee.stderr
new file mode 100644
index 00000000000..b7a0a90193d
--- /dev/null
+++ b/tests/ui/loop-match/upvar-scrutinee.stderr
@@ -0,0 +1,18 @@
+error: invalid match on `#[loop_match]` state
+  --> $DIR/upvar-scrutinee.rs:32:23
+   |
+LL |                 match state {
+   |                       ^^^^^
+   |
+   = note: a local variable must be the scrutinee within a `#[loop_match]`
+
+error: invalid match on `#[loop_match]` state
+  --> $DIR/upvar-scrutinee.rs:68:23
+   |
+LL |                 match state {
+   |                       ^^^^^
+   |
+   = note: a local variable must be the scrutinee within a `#[loop_match]`
+
+error: aborting due to 2 previous errors
+