Skip to content

Commit 1742683

Browse files
committed
Auto merge of rust-lang#14972 - HKalbasi:mir-fix, r=HKalbasi
Fix `unused-mut` false positive for `Box`
2 parents 9d5c34a + 0408af6 commit 1742683

File tree

2 files changed

+83
-12
lines changed

2 files changed

+83
-12
lines changed

crates/hir-ty/src/mir/borrowck.rs

+31-12
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ pub fn borrowck_query(
7878
.map(|body| {
7979
let body = body?;
8080
Ok(BorrowckResult {
81-
mutability_of_locals: mutability_of_locals(&body),
81+
mutability_of_locals: mutability_of_locals(db, &body),
8282
moved_out_of_ref: moved_out_of_ref(db, &body),
8383
mir_body: body,
8484
})
@@ -186,10 +186,7 @@ fn moved_out_of_ref(db: &dyn HirDatabase, body: &MirBody) -> Vec<MovedOutOfRef>
186186
result
187187
}
188188

189-
fn is_place_direct(lvalue: &Place) -> bool {
190-
!lvalue.projection.iter().any(|x| *x == ProjectionElem::Deref)
191-
}
192-
189+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
193190
enum ProjectionCase {
194191
/// Projection is a local
195192
Direct,
@@ -199,12 +196,14 @@ enum ProjectionCase {
199196
Indirect,
200197
}
201198

202-
fn place_case(lvalue: &Place) -> ProjectionCase {
199+
fn place_case(db: &dyn HirDatabase, body: &MirBody, lvalue: &Place) -> ProjectionCase {
203200
let mut is_part_of = false;
204-
for proj in lvalue.projection.iter().rev() {
201+
let mut ty = body.locals[lvalue.local].ty.clone();
202+
for proj in lvalue.projection.iter() {
205203
match proj {
206-
ProjectionElem::Deref => return ProjectionCase::Indirect, // It's indirect
207-
ProjectionElem::ConstantIndex { .. }
204+
ProjectionElem::Deref if ty.as_adt().is_none() => return ProjectionCase::Indirect, // It's indirect in case of reference and raw
205+
ProjectionElem::Deref // It's direct in case of `Box<T>`
206+
| ProjectionElem::ConstantIndex { .. }
208207
| ProjectionElem::Subslice { .. }
209208
| ProjectionElem::Field(_)
210209
| ProjectionElem::TupleOrClosureField(_)
@@ -213,6 +212,23 @@ fn place_case(lvalue: &Place) -> ProjectionCase {
213212
}
214213
ProjectionElem::OpaqueCast(_) => (),
215214
}
215+
ty = proj.projected_ty(
216+
ty,
217+
db,
218+
|c, subst, f| {
219+
let (def, _) = db.lookup_intern_closure(c.into());
220+
let infer = db.infer(def);
221+
let (captures, _) = infer.closure_info(&c);
222+
let parent_subst = ClosureSubst(subst).parent_subst();
223+
captures
224+
.get(f)
225+
.expect("broken closure field")
226+
.ty
227+
.clone()
228+
.substitute(Interner, parent_subst)
229+
},
230+
body.owner.module(db.upcast()).krate(),
231+
);
216232
}
217233
if is_part_of {
218234
ProjectionCase::DirectPart
@@ -300,7 +316,10 @@ fn ever_initialized_map(body: &MirBody) -> ArenaMap<BasicBlockId, ArenaMap<Local
300316
result
301317
}
302318

303-
fn mutability_of_locals(body: &MirBody) -> ArenaMap<LocalId, MutabilityReason> {
319+
fn mutability_of_locals(
320+
db: &dyn HirDatabase,
321+
body: &MirBody,
322+
) -> ArenaMap<LocalId, MutabilityReason> {
304323
let mut result: ArenaMap<LocalId, MutabilityReason> =
305324
body.locals.iter().map(|x| (x.0, MutabilityReason::Not)).collect();
306325
let mut push_mut_span = |local, span| match &mut result[local] {
@@ -313,7 +332,7 @@ fn mutability_of_locals(body: &MirBody) -> ArenaMap<LocalId, MutabilityReason> {
313332
for statement in &block.statements {
314333
match &statement.kind {
315334
StatementKind::Assign(place, value) => {
316-
match place_case(place) {
335+
match place_case(db, body, place) {
317336
ProjectionCase::Direct => {
318337
if ever_init_map.get(place.local).copied().unwrap_or_default() {
319338
push_mut_span(place.local, statement.span);
@@ -328,7 +347,7 @@ fn mutability_of_locals(body: &MirBody) -> ArenaMap<LocalId, MutabilityReason> {
328347
ProjectionCase::Indirect => (),
329348
}
330349
if let Rvalue::Ref(BorrowKind::Mut { .. }, p) = value {
331-
if is_place_direct(p) {
350+
if place_case(db, body, p) != ProjectionCase::Indirect {
332351
push_mut_span(p.local, statement.span);
333352
}
334353
}

crates/ide-diagnostics/src/handlers/mutability_errors.rs

+52
Original file line numberDiff line numberDiff line change
@@ -993,6 +993,58 @@ fn f() {
993993
);
994994
}
995995

996+
#[test]
997+
fn boxes() {
998+
check_diagnostics(
999+
r#"
1000+
//- minicore: coerce_unsized, deref_mut, slice
1001+
use core::ops::{Deref, DerefMut};
1002+
use core::{marker::Unsize, ops::CoerceUnsized};
1003+
1004+
#[lang = "owned_box"]
1005+
pub struct Box<T: ?Sized> {
1006+
inner: *mut T,
1007+
}
1008+
impl<T> Box<T> {
1009+
fn new(t: T) -> Self {
1010+
#[rustc_box]
1011+
Box::new(t)
1012+
}
1013+
}
1014+
1015+
impl<T: ?Sized> Deref for Box<T> {
1016+
type Target = T;
1017+
1018+
fn deref(&self) -> &T {
1019+
&**self
1020+
}
1021+
}
1022+
1023+
impl<T: ?Sized> DerefMut for Box<T> {
1024+
fn deref_mut(&mut self) -> &mut T {
1025+
&mut **self
1026+
}
1027+
}
1028+
1029+
fn f() {
1030+
let x = Box::new(5);
1031+
x = Box::new(7);
1032+
//^^^^^^^^^^^^^^^ 💡 error: cannot mutate immutable variable `x`
1033+
let x = Box::new(5);
1034+
*x = 7;
1035+
//^^^^^^ 💡 error: cannot mutate immutable variable `x`
1036+
let mut y = Box::new(5);
1037+
//^^^^^ 💡 weak: variable does not need to be mutable
1038+
*x = *y;
1039+
//^^^^^^^ 💡 error: cannot mutate immutable variable `x`
1040+
let x = Box::new(5);
1041+
let closure = || *x = 2;
1042+
//^ 💡 error: cannot mutate immutable variable `x`
1043+
}
1044+
"#,
1045+
);
1046+
}
1047+
9961048
#[test]
9971049
fn allow_unused_mut_for_identifiers_starting_with_underline() {
9981050
check_diagnostics(

0 commit comments

Comments
 (0)