fix need-mut false positive in closure capture of match scrutinee

This commit is contained in:
hkalbasi 2023-05-26 02:08:33 +03:30
parent 7ef185d65e
commit 780349bdaf
4 changed files with 72 additions and 14 deletions

View file

@ -227,9 +227,8 @@ impl Body {
}); });
} }
pub fn walk_pats(&self, pat_id: PatId, f: &mut impl FnMut(PatId)) { pub fn walk_pats_shallow(&self, pat_id: PatId, mut f: impl FnMut(PatId)) {
let pat = &self[pat_id]; let pat = &self[pat_id];
f(pat_id);
match pat { match pat {
Pat::Range { .. } Pat::Range { .. }
| Pat::Lit(..) | Pat::Lit(..)
@ -239,23 +238,28 @@ impl Body {
| Pat::Missing => {} | Pat::Missing => {}
&Pat::Bind { subpat, .. } => { &Pat::Bind { subpat, .. } => {
if let Some(subpat) = subpat { if let Some(subpat) = subpat {
self.walk_pats(subpat, f); f(subpat);
} }
} }
Pat::Or(args) | Pat::Tuple { args, .. } | Pat::TupleStruct { args, .. } => { Pat::Or(args) | Pat::Tuple { args, .. } | Pat::TupleStruct { args, .. } => {
args.iter().copied().for_each(|p| self.walk_pats(p, f)); args.iter().copied().for_each(|p| f(p));
} }
Pat::Ref { pat, .. } => self.walk_pats(*pat, f), Pat::Ref { pat, .. } => f(*pat),
Pat::Slice { prefix, slice, suffix } => { Pat::Slice { prefix, slice, suffix } => {
let total_iter = prefix.iter().chain(slice.iter()).chain(suffix.iter()); let total_iter = prefix.iter().chain(slice.iter()).chain(suffix.iter());
total_iter.copied().for_each(|p| self.walk_pats(p, f)); total_iter.copied().for_each(|p| f(p));
} }
Pat::Record { args, .. } => { Pat::Record { args, .. } => {
args.iter().for_each(|RecordFieldPat { pat, .. }| self.walk_pats(*pat, f)); args.iter().for_each(|RecordFieldPat { pat, .. }| f(*pat));
} }
Pat::Box { inner } => self.walk_pats(*inner, f), Pat::Box { inner } => f(*inner),
} }
} }
pub fn walk_pats(&self, pat_id: PatId, f: &mut impl FnMut(PatId)) {
f(pat_id);
self.walk_pats_shallow(pat_id, |p| self.walk_pats(p, f));
}
} }
impl Default for Body { impl Default for Body {

View file

@ -643,7 +643,21 @@ impl InferenceContext<'_> {
} }
None => *result = Some(ck), None => *result = Some(ck),
}; };
self.body.walk_pats(pat, &mut |p| match &self.body[p] {
self.walk_pat_inner(
pat,
&mut update_result,
BorrowKind::Mut { allow_two_phase_borrow: false },
);
}
fn walk_pat_inner(
&mut self,
p: PatId,
update_result: &mut impl FnMut(CaptureKind),
mut for_mut: BorrowKind,
) {
match &self.body[p] {
Pat::Ref { .. } Pat::Ref { .. }
| Pat::Box { .. } | Pat::Box { .. }
| Pat::Missing | Pat::Missing
@ -678,13 +692,15 @@ impl InferenceContext<'_> {
} }
} }
crate::BindingMode::Ref(r) => match r { crate::BindingMode::Ref(r) => match r {
Mutability::Mut => update_result(CaptureKind::ByRef(BorrowKind::Mut { Mutability::Mut => update_result(CaptureKind::ByRef(for_mut)),
allow_two_phase_borrow: false,
})),
Mutability::Not => update_result(CaptureKind::ByRef(BorrowKind::Shared)), Mutability::Not => update_result(CaptureKind::ByRef(BorrowKind::Shared)),
}, },
}, },
}); }
if self.result.pat_adjustments.get(&p).map_or(false, |x| !x.is_empty()) {
for_mut = BorrowKind::Unique;
}
self.body.walk_pats_shallow(p, |p| self.walk_pat_inner(p, update_result, for_mut));
} }
fn expr_ty(&self, expr: ExprId) -> Ty { fn expr_ty(&self, expr: ExprId) -> Ty {

View file

@ -201,7 +201,7 @@ fn match_pattern() {
] ]
|x: i64| { |x: i64| {
match y { match y {
X(_a, _b, _c) => x, X(_a, _, _c) => x,
} }
} }
} }
@ -217,6 +217,18 @@ fn match_pattern() {
} }
} }
} }
size_and_align_expr! {
minicore: copy;
stmts: [
struct X(i64, i32, (u8, i128));
let y: X = X(2, 5, (7, 3));
]
|x: i64| {
match y {
ref _y => x,
}
}
}
} }
#[test] #[test]

View file

@ -352,6 +352,32 @@ fn main() {
); );
} }
#[test]
fn match_closure_capture() {
check_diagnostics(
r#"
//- minicore: option
fn main() {
let mut v = &mut Some(2);
//^^^^^ 💡 weak: variable does not need to be mutable
let _ = || match v {
Some(k) => {
*k = 5;
}
None => {}
};
let v = &mut Some(2);
let _ = || match v {
//^ 💡 error: cannot mutate immutable variable `v`
ref mut k => {
*k = &mut Some(5);
}
};
}
"#,
);
}
#[test] #[test]
fn match_bindings() { fn match_bindings() {
check_diagnostics( check_diagnostics(