fix: Reject upvar scrutinees for loop_match

This commit is contained in:
Shoyu Vanilla 2025-07-25 23:07:13 +09:00
parent 65b6cdb6a6
commit d87b4f2c77
3 changed files with 113 additions and 7 deletions

View file

@ -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

View file

@ -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;
}
}
}
}
};
}

View file

@ -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