From d87b4f2c77add3404f7469ea72f1d7c51e2a29dc Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Fri, 25 Jul 2025 23:07:13 +0900 Subject: [PATCH] fix: Reject upvar scrutinees for `loop_match` --- compiler/rustc_mir_build/src/thir/cx/expr.rs | 21 +++-- tests/ui/loop-match/upvar-scrutinee.rs | 81 ++++++++++++++++++++ tests/ui/loop-match/upvar-scrutinee.stderr | 18 +++++ 3 files changed, 113 insertions(+), 7 deletions(-) create mode 100644 tests/ui/loop-match/upvar-scrutinee.rs create mode 100644 tests/ui/loop-match/upvar-scrutinee.stderr diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 33bf4e3e29fbe..16df58cd76df4 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 { + fn local( + cx: &mut ThirBuildCx<'_>, + expr: &rustc_hir::Expr<'_>, + ) -> Option { 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 0000000000000..a93e3a0e59ab1 --- /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 0000000000000..b7a0a90193d70 --- /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 +