Auto merge of #14576 - HKalbasi:dev2, r=HKalbasi

Fix explicit deref problems in closure capture

fix the `need-mut` part of #14562

Perhaps surprisingly, it wasn't unique immutable borrow. The code still doesn't emit any of them, and I think those won't happen in edition 2021 (which is currently the only thing implemented), since we always capture `&mut *x` instead of `&mut x`. But I'm not very sure about it.
This commit is contained in:
bors 2023-04-14 13:23:49 +00:00
commit b218009f46
4 changed files with 90 additions and 4 deletions

View File

@ -190,6 +190,16 @@ impl InferenceContext<'_> {
} }
return Some(place); return Some(place);
} }
Expr::UnaryOp { expr, op: UnaryOp::Deref } => {
if matches!(
self.expr_ty_after_adjustments(*expr).kind(Interner),
TyKind::Ref(..) | TyKind::Raw(..)
) {
let mut place = self.place_of_expr(*expr)?;
place.projections.push(ProjectionElem::Deref);
return Some(place);
}
}
_ => (), _ => (),
} }
None None
@ -371,7 +381,12 @@ impl InferenceContext<'_> {
} }
Expr::Field { expr, name: _ } => self.select_from_expr(*expr), Expr::Field { expr, name: _ } => self.select_from_expr(*expr),
Expr::UnaryOp { expr, op: UnaryOp::Deref } => { Expr::UnaryOp { expr, op: UnaryOp::Deref } => {
if let Some((f, _)) = self.result.method_resolution(tgt_expr) { if matches!(
self.expr_ty_after_adjustments(*expr).kind(Interner),
TyKind::Ref(..) | TyKind::Raw(..)
) {
self.select_from_expr(*expr);
} else if let Some((f, _)) = self.result.method_resolution(tgt_expr) {
let mutability = 'b: { let mutability = 'b: {
if let Some(deref_trait) = if let Some(deref_trait) =
self.resolve_lang_item(LangItem::DerefMut).and_then(|x| x.as_trait()) self.resolve_lang_item(LangItem::DerefMut).and_then(|x| x.as_trait())
@ -461,10 +476,20 @@ impl InferenceContext<'_> {
} }
} }
fn expr_ty(&mut self, expr: ExprId) -> Ty { fn expr_ty(&self, expr: ExprId) -> Ty {
self.result[expr].clone() self.result[expr].clone()
} }
fn expr_ty_after_adjustments(&self, e: ExprId) -> Ty {
let mut ty = None;
if let Some(x) = self.result.expr_adjustments.get(&e) {
if let Some(x) = x.last() {
ty = Some(x.target.clone());
}
}
ty.unwrap_or_else(|| self.expr_ty(e))
}
fn is_upvar(&self, place: &HirPlace) -> bool { fn is_upvar(&self, place: &HirPlace) -> bool {
let b = &self.body[place.local]; let b = &self.body[place.local];
if let Some(c) = self.current_closure { if let Some(c) = self.current_closure {
@ -701,7 +726,9 @@ impl InferenceContext<'_> {
}; };
self.consume_expr(*body); self.consume_expr(*body);
for item in &self.current_captures { for item in &self.current_captures {
if matches!(item.kind, CaptureKind::ByRef(BorrowKind::Mut { .. })) { if matches!(item.kind, CaptureKind::ByRef(BorrowKind::Mut { .. }))
&& !item.place.projections.contains(&ProjectionElem::Deref)
{
// FIXME: remove the `mutated_bindings_in_closure` completely and add proper fake reads in // FIXME: remove the `mutated_bindings_in_closure` completely and add proper fake reads in
// MIR. I didn't do that due duplicate diagnostics. // MIR. I didn't do that due duplicate diagnostics.
self.result.mutated_bindings_in_closure.insert(item.place.local); self.result.mutated_bindings_in_closure.insert(item.place.local);

View File

@ -40,6 +40,15 @@ fn ref_simple() {
y y
} }
} }
size_and_align_expr! {
minicore: copy, deref_mut;
stmts: [
let y: &mut i32 = &mut 5;
]
|x: i32| {
*y += x;
}
}
size_and_align_expr! { size_and_align_expr! {
minicore: copy; minicore: copy;
stmts: [ stmts: [
@ -50,6 +59,16 @@ fn ref_simple() {
x x
} }
} }
size_and_align_expr! {
minicore: copy, deref_mut;
stmts: [
struct X(i32, i64);
let x: &mut X = &mut X(2, 6);
]
|| {
(*x).0 as i64 + x.1
}
}
} }
#[test] #[test]

View File

@ -1564,7 +1564,10 @@ impl DefWithBody {
} }
(mir::MutabilityReason::Not, true) => { (mir::MutabilityReason::Not, true) => {
if !infer.mutated_bindings_in_closure.contains(&binding_id) { if !infer.mutated_bindings_in_closure.contains(&binding_id) {
acc.push(UnusedMut { local }.into()) let should_ignore = matches!(body[binding_id].name.as_str(), Some(x) if x.starts_with("_"));
if !should_ignore {
acc.push(UnusedMut { local }.into())
}
} }
} }
} }

View File

@ -851,6 +851,43 @@ fn f() {
} }
"#, "#,
); );
check_diagnostics(
r#"
//- minicore: copy, fn, deref_mut
struct X(i32, i64);
fn f() {
let mut x = &mut 5;
//^^^^^ 💡 weak: variable does not need to be mutable
let closure1 = || { *x = 2; };
let _ = closure1();
//^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1`
let mut x = &mut 5;
//^^^^^ 💡 weak: variable does not need to be mutable
let closure1 = move || { *x = 2; };
let _ = closure1();
//^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1`
let mut x = &mut X(1, 2);
//^^^^^ 💡 weak: variable does not need to be mutable
let closure1 = || { x.0 = 2; };
let _ = closure1();
//^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1`
}
"#,
);
}
#[test]
fn allow_unused_mut_for_identifiers_starting_with_underline() {
check_diagnostics(
r#"
fn f(_: i32) {}
fn main() {
let mut _x = 2;
f(_x);
}
"#,
);
} }
#[test] #[test]