Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 0c62106

Browse files
committed
Fix need-mut large span in closures and a false positive
1 parent 0289dfa commit 0c62106

File tree

4 files changed

+69
-29
lines changed

4 files changed

+69
-29
lines changed

crates/hir-ty/src/infer/closure.rs

Lines changed: 40 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use smallvec::SmallVec;
1818
use stdx::never;
1919

2020
use crate::{
21-
mir::{BorrowKind, ProjectionElem},
21+
mir::{BorrowKind, MirSpan, ProjectionElem},
2222
static_lifetime, to_chalk_trait_id,
2323
traits::FnTrait,
2424
utils::{self, pattern_matching_dereference_count},
@@ -121,6 +121,22 @@ impl HirPlace {
121121
}
122122
ty.clone()
123123
}
124+
125+
fn capture_kind_of_truncated_place(
126+
&self,
127+
mut current_capture: CaptureKind,
128+
len: usize,
129+
) -> CaptureKind {
130+
match current_capture {
131+
CaptureKind::ByRef(BorrowKind::Mut { .. }) => {
132+
if self.projections[len..].iter().any(|x| *x == ProjectionElem::Deref) {
133+
current_capture = CaptureKind::ByRef(BorrowKind::Unique);
134+
}
135+
}
136+
_ => (),
137+
}
138+
current_capture
139+
}
124140
}
125141

126142
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
@@ -133,13 +149,15 @@ pub(crate) enum CaptureKind {
133149
pub(crate) struct CapturedItem {
134150
pub(crate) place: HirPlace,
135151
pub(crate) kind: CaptureKind,
152+
pub(crate) span: MirSpan,
136153
pub(crate) ty: Ty,
137154
}
138155

139156
#[derive(Debug, Clone, PartialEq, Eq)]
140157
pub(crate) struct CapturedItemWithoutTy {
141158
pub(crate) place: HirPlace,
142159
pub(crate) kind: CaptureKind,
160+
pub(crate) span: MirSpan,
143161
}
144162

145163
impl CapturedItemWithoutTy {
@@ -155,7 +173,7 @@ impl CapturedItemWithoutTy {
155173
TyKind::Ref(m, static_lifetime(), ty).intern(Interner)
156174
}
157175
};
158-
CapturedItem { place: self.place, kind: self.kind, ty }
176+
CapturedItem { place: self.place, kind: self.kind, span: self.span, ty }
159177
}
160178
}
161179

@@ -211,14 +229,14 @@ impl InferenceContext<'_> {
211229

212230
fn ref_expr(&mut self, expr: ExprId) {
213231
if let Some(place) = self.place_of_expr(expr) {
214-
self.add_capture(place, CaptureKind::ByRef(BorrowKind::Shared));
232+
self.add_capture(place, CaptureKind::ByRef(BorrowKind::Shared), expr.into());
215233
}
216234
self.walk_expr(expr);
217235
}
218236

219-
fn add_capture(&mut self, place: HirPlace, kind: CaptureKind) {
237+
fn add_capture(&mut self, place: HirPlace, kind: CaptureKind, span: MirSpan) {
220238
if self.is_upvar(&place) {
221-
self.push_capture(CapturedItemWithoutTy { place, kind });
239+
self.push_capture(CapturedItemWithoutTy { place, kind, span });
222240
}
223241
}
224242

@@ -227,27 +245,28 @@ impl InferenceContext<'_> {
227245
self.add_capture(
228246
place,
229247
CaptureKind::ByRef(BorrowKind::Mut { allow_two_phase_borrow: false }),
248+
expr.into(),
230249
);
231250
}
232251
self.walk_expr(expr);
233252
}
234253

235254
fn consume_expr(&mut self, expr: ExprId) {
236255
if let Some(place) = self.place_of_expr(expr) {
237-
self.consume_place(place);
256+
self.consume_place(place, expr.into());
238257
}
239258
self.walk_expr(expr);
240259
}
241260

242-
fn consume_place(&mut self, place: HirPlace) {
261+
fn consume_place(&mut self, place: HirPlace, span: MirSpan) {
243262
if self.is_upvar(&place) {
244263
let ty = place.ty(self).clone();
245264
let kind = if self.is_ty_copy(ty) {
246265
CaptureKind::ByRef(BorrowKind::Shared)
247266
} else {
248267
CaptureKind::ByValue
249268
};
250-
self.push_capture(CapturedItemWithoutTy { place, kind });
269+
self.push_capture(CapturedItemWithoutTy { place, kind, span });
251270
}
252271
}
253272

@@ -281,9 +300,7 @@ impl InferenceContext<'_> {
281300
};
282301
if let Some(place) = self.place_of_expr_without_adjust(tgt_expr) {
283302
if let Some(place) = apply_adjusts_to_place(place, rest) {
284-
if self.is_upvar(&place) {
285-
self.push_capture(CapturedItemWithoutTy { place, kind: capture_kind });
286-
}
303+
self.add_capture(place, capture_kind, tgt_expr.into());
287304
}
288305
}
289306
self.walk_expr_with_adjust(tgt_expr, rest);
@@ -456,12 +473,9 @@ impl InferenceContext<'_> {
456473
"We sort closures, so we should always have data for inner closures",
457474
);
458475
let mut cc = mem::take(&mut self.current_captures);
459-
cc.extend(
460-
captures
461-
.iter()
462-
.filter(|x| self.is_upvar(&x.place))
463-
.map(|x| CapturedItemWithoutTy { place: x.place.clone(), kind: x.kind }),
464-
);
476+
cc.extend(captures.iter().filter(|x| self.is_upvar(&x.place)).map(|x| {
477+
CapturedItemWithoutTy { place: x.place.clone(), kind: x.kind, span: x.span }
478+
}));
465479
self.current_captures = cc;
466480
}
467481
Expr::Array(Array::ElementList { elements: exprs, is_assignee_expr: _ })
@@ -552,8 +566,11 @@ impl InferenceContext<'_> {
552566
};
553567
match prev_index {
554568
Some(p) => {
569+
let len = self.current_captures[p].place.projections.len();
570+
let kind_after_truncate =
571+
item.place.capture_kind_of_truncated_place(item.kind, len);
555572
self.current_captures[p].kind =
556-
cmp::max(item.kind, self.current_captures[p].kind);
573+
cmp::max(kind_after_truncate, self.current_captures[p].kind);
557574
}
558575
None => {
559576
hash_map.insert(item.place.clone(), self.current_captures.len());
@@ -603,7 +620,7 @@ impl InferenceContext<'_> {
603620
};
604621
match variant {
605622
VariantId::EnumVariantId(_) | VariantId::UnionId(_) => {
606-
self.consume_place(place)
623+
self.consume_place(place, pat.into())
607624
}
608625
VariantId::StructId(s) => {
609626
let vd = &*self.db.struct_data(s).variant_data;
@@ -632,21 +649,21 @@ impl InferenceContext<'_> {
632649
| Pat::Slice { .. }
633650
| Pat::ConstBlock(_)
634651
| Pat::Path(_)
635-
| Pat::Lit(_) => self.consume_place(place),
652+
| Pat::Lit(_) => self.consume_place(place, pat.into()),
636653
Pat::Bind { id, subpat: _ } => {
637654
let mode = self.body.bindings[*id].mode;
638655
if matches!(mode, BindingAnnotation::Ref | BindingAnnotation::RefMut) {
639656
bm = mode;
640657
}
641658
let capture_kind = match bm {
642659
BindingAnnotation::Unannotated | BindingAnnotation::Mutable => {
643-
self.consume_place(place);
660+
self.consume_place(place, pat.into());
644661
return;
645662
}
646663
BindingAnnotation::Ref => BorrowKind::Shared,
647664
BindingAnnotation::RefMut => BorrowKind::Mut { allow_two_phase_borrow: false },
648665
};
649-
self.add_capture(place, CaptureKind::ByRef(capture_kind));
666+
self.add_capture(place, CaptureKind::ByRef(capture_kind), pat.into());
650667
}
651668
Pat::TupleStruct { path: _, args, ellipsis } => {
652669
pattern_matching_dereference(&mut ty, &mut bm, &mut place);
@@ -659,7 +676,7 @@ impl InferenceContext<'_> {
659676
};
660677
match variant {
661678
VariantId::EnumVariantId(_) | VariantId::UnionId(_) => {
662-
self.consume_place(place)
679+
self.consume_place(place, pat.into())
663680
}
664681
VariantId::StructId(s) => {
665682
let vd = &*self.db.struct_data(s).variant_data;

crates/hir-ty/src/layout/tests/closure.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,20 @@ fn nested_closures() {
105105
}
106106
}
107107

108+
#[test]
109+
fn capture_specific_fields2() {
110+
size_and_align_expr! {
111+
minicore: copy;
112+
stmts: [
113+
let x = &mut 2;
114+
]
115+
|| {
116+
*x = 5;
117+
&x;
118+
}
119+
}
120+
}
121+
108122
#[test]
109123
fn capture_specific_fields() {
110124
size_and_align_expr! {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -898,7 +898,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
898898
current,
899899
tmp.clone(),
900900
Rvalue::Ref(bk.clone(), p),
901-
expr_id.into(),
901+
capture.span,
902902
);
903903
operands.push(Operand::Move(tmp));
904904
},

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

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -773,7 +773,7 @@ fn fn_once(mut x: impl FnOnce(u8) -> u8) -> u8 {
773773

774774
#[test]
775775
fn closure() {
776-
// FIXME: Diagnostic spans are too large
776+
// FIXME: Diagnostic spans are inconsistent inside and outside closure
777777
check_diagnostics(
778778
r#"
779779
//- minicore: copy, fn
@@ -786,11 +786,11 @@ fn fn_once(mut x: impl FnOnce(u8) -> u8) -> u8 {
786786
fn f() {
787787
let x = 5;
788788
let closure1 = || { x = 2; };
789-
//^^^^^^^^^^^^^ 💡 error: cannot mutate immutable variable `x`
789+
//^ 💡 error: cannot mutate immutable variable `x`
790790
let _ = closure1();
791791
//^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1`
792792
let closure2 = || { x = x; };
793-
//^^^^^^^^^^^^^ 💡 error: cannot mutate immutable variable `x`
793+
//^ 💡 error: cannot mutate immutable variable `x`
794794
let closure3 = || {
795795
let x = 2;
796796
x = 5;
@@ -799,7 +799,7 @@ fn fn_once(mut x: impl FnOnce(u8) -> u8) -> u8 {
799799
};
800800
let x = X;
801801
let closure4 = || { x.mutate(); };
802-
//^^^^^^^^^^^^^^^^^^ 💡 error: cannot mutate immutable variable `x`
802+
//^ 💡 error: cannot mutate immutable variable `x`
803803
}
804804
"#,
805805
);
@@ -829,7 +829,7 @@ fn f() {
829829
|| {
830830
let x = 2;
831831
|| { || { x = 5; } }
832-
//^^^^^^^^^^^^^^^^^^^^ 💡 error: cannot mutate immutable variable `x`
832+
//^ 💡 error: cannot mutate immutable variable `x`
833833
}
834834
}
835835
};
@@ -860,6 +860,15 @@ fn f() {
860860
let mut x = &mut 5;
861861
//^^^^^ 💡 weak: variable does not need to be mutable
862862
let closure1 = || { *x = 2; };
863+
let _ = closure1();
864+
//^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1`
865+
let mut x = &mut 5;
866+
//^^^^^ 💡 weak: variable does not need to be mutable
867+
let closure1 = || { *x = 2; &x; };
868+
let _ = closure1();
869+
//^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1`
870+
let mut x = &mut 5;
871+
let closure1 = || { *x = 2; &x; x = &mut 3; };
863872
let _ = closure1();
864873
//^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1`
865874
let mut x = &mut 5;

0 commit comments

Comments
 (0)