Fix need-mut large span in closures and a false positive

This commit is contained in:
hkalbasi 2023-04-21 02:13:56 +03:30
parent 0289dfa261
commit 0c621065fb
4 changed files with 69 additions and 29 deletions

View file

@ -18,7 +18,7 @@ use smallvec::SmallVec;
use stdx::never; use stdx::never;
use crate::{ use crate::{
mir::{BorrowKind, ProjectionElem}, mir::{BorrowKind, MirSpan, ProjectionElem},
static_lifetime, to_chalk_trait_id, static_lifetime, to_chalk_trait_id,
traits::FnTrait, traits::FnTrait,
utils::{self, pattern_matching_dereference_count}, utils::{self, pattern_matching_dereference_count},
@ -121,6 +121,22 @@ impl HirPlace {
} }
ty.clone() ty.clone()
} }
fn capture_kind_of_truncated_place(
&self,
mut current_capture: CaptureKind,
len: usize,
) -> CaptureKind {
match current_capture {
CaptureKind::ByRef(BorrowKind::Mut { .. }) => {
if self.projections[len..].iter().any(|x| *x == ProjectionElem::Deref) {
current_capture = CaptureKind::ByRef(BorrowKind::Unique);
}
}
_ => (),
}
current_capture
}
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
@ -133,6 +149,7 @@ pub(crate) enum CaptureKind {
pub(crate) struct CapturedItem { pub(crate) struct CapturedItem {
pub(crate) place: HirPlace, pub(crate) place: HirPlace,
pub(crate) kind: CaptureKind, pub(crate) kind: CaptureKind,
pub(crate) span: MirSpan,
pub(crate) ty: Ty, pub(crate) ty: Ty,
} }
@ -140,6 +157,7 @@ pub(crate) struct CapturedItem {
pub(crate) struct CapturedItemWithoutTy { pub(crate) struct CapturedItemWithoutTy {
pub(crate) place: HirPlace, pub(crate) place: HirPlace,
pub(crate) kind: CaptureKind, pub(crate) kind: CaptureKind,
pub(crate) span: MirSpan,
} }
impl CapturedItemWithoutTy { impl CapturedItemWithoutTy {
@ -155,7 +173,7 @@ impl CapturedItemWithoutTy {
TyKind::Ref(m, static_lifetime(), ty).intern(Interner) TyKind::Ref(m, static_lifetime(), ty).intern(Interner)
} }
}; };
CapturedItem { place: self.place, kind: self.kind, ty } CapturedItem { place: self.place, kind: self.kind, span: self.span, ty }
} }
} }
@ -211,14 +229,14 @@ impl InferenceContext<'_> {
fn ref_expr(&mut self, expr: ExprId) { fn ref_expr(&mut self, expr: ExprId) {
if let Some(place) = self.place_of_expr(expr) { if let Some(place) = self.place_of_expr(expr) {
self.add_capture(place, CaptureKind::ByRef(BorrowKind::Shared)); self.add_capture(place, CaptureKind::ByRef(BorrowKind::Shared), expr.into());
} }
self.walk_expr(expr); self.walk_expr(expr);
} }
fn add_capture(&mut self, place: HirPlace, kind: CaptureKind) { fn add_capture(&mut self, place: HirPlace, kind: CaptureKind, span: MirSpan) {
if self.is_upvar(&place) { if self.is_upvar(&place) {
self.push_capture(CapturedItemWithoutTy { place, kind }); self.push_capture(CapturedItemWithoutTy { place, kind, span });
} }
} }
@ -227,6 +245,7 @@ impl InferenceContext<'_> {
self.add_capture( self.add_capture(
place, place,
CaptureKind::ByRef(BorrowKind::Mut { allow_two_phase_borrow: false }), CaptureKind::ByRef(BorrowKind::Mut { allow_two_phase_borrow: false }),
expr.into(),
); );
} }
self.walk_expr(expr); self.walk_expr(expr);
@ -234,12 +253,12 @@ impl InferenceContext<'_> {
fn consume_expr(&mut self, expr: ExprId) { fn consume_expr(&mut self, expr: ExprId) {
if let Some(place) = self.place_of_expr(expr) { if let Some(place) = self.place_of_expr(expr) {
self.consume_place(place); self.consume_place(place, expr.into());
} }
self.walk_expr(expr); self.walk_expr(expr);
} }
fn consume_place(&mut self, place: HirPlace) { fn consume_place(&mut self, place: HirPlace, span: MirSpan) {
if self.is_upvar(&place) { if self.is_upvar(&place) {
let ty = place.ty(self).clone(); let ty = place.ty(self).clone();
let kind = if self.is_ty_copy(ty) { let kind = if self.is_ty_copy(ty) {
@ -247,7 +266,7 @@ impl InferenceContext<'_> {
} else { } else {
CaptureKind::ByValue CaptureKind::ByValue
}; };
self.push_capture(CapturedItemWithoutTy { place, kind }); self.push_capture(CapturedItemWithoutTy { place, kind, span });
} }
} }
@ -281,9 +300,7 @@ impl InferenceContext<'_> {
}; };
if let Some(place) = self.place_of_expr_without_adjust(tgt_expr) { if let Some(place) = self.place_of_expr_without_adjust(tgt_expr) {
if let Some(place) = apply_adjusts_to_place(place, rest) { if let Some(place) = apply_adjusts_to_place(place, rest) {
if self.is_upvar(&place) { self.add_capture(place, capture_kind, tgt_expr.into());
self.push_capture(CapturedItemWithoutTy { place, kind: capture_kind });
}
} }
} }
self.walk_expr_with_adjust(tgt_expr, rest); self.walk_expr_with_adjust(tgt_expr, rest);
@ -456,12 +473,9 @@ impl InferenceContext<'_> {
"We sort closures, so we should always have data for inner closures", "We sort closures, so we should always have data for inner closures",
); );
let mut cc = mem::take(&mut self.current_captures); let mut cc = mem::take(&mut self.current_captures);
cc.extend( cc.extend(captures.iter().filter(|x| self.is_upvar(&x.place)).map(|x| {
captures CapturedItemWithoutTy { place: x.place.clone(), kind: x.kind, span: x.span }
.iter() }));
.filter(|x| self.is_upvar(&x.place))
.map(|x| CapturedItemWithoutTy { place: x.place.clone(), kind: x.kind }),
);
self.current_captures = cc; self.current_captures = cc;
} }
Expr::Array(Array::ElementList { elements: exprs, is_assignee_expr: _ }) Expr::Array(Array::ElementList { elements: exprs, is_assignee_expr: _ })
@ -552,8 +566,11 @@ impl InferenceContext<'_> {
}; };
match prev_index { match prev_index {
Some(p) => { Some(p) => {
let len = self.current_captures[p].place.projections.len();
let kind_after_truncate =
item.place.capture_kind_of_truncated_place(item.kind, len);
self.current_captures[p].kind = self.current_captures[p].kind =
cmp::max(item.kind, self.current_captures[p].kind); cmp::max(kind_after_truncate, self.current_captures[p].kind);
} }
None => { None => {
hash_map.insert(item.place.clone(), self.current_captures.len()); hash_map.insert(item.place.clone(), self.current_captures.len());
@ -603,7 +620,7 @@ impl InferenceContext<'_> {
}; };
match variant { match variant {
VariantId::EnumVariantId(_) | VariantId::UnionId(_) => { VariantId::EnumVariantId(_) | VariantId::UnionId(_) => {
self.consume_place(place) self.consume_place(place, pat.into())
} }
VariantId::StructId(s) => { VariantId::StructId(s) => {
let vd = &*self.db.struct_data(s).variant_data; let vd = &*self.db.struct_data(s).variant_data;
@ -632,7 +649,7 @@ impl InferenceContext<'_> {
| Pat::Slice { .. } | Pat::Slice { .. }
| Pat::ConstBlock(_) | Pat::ConstBlock(_)
| Pat::Path(_) | Pat::Path(_)
| Pat::Lit(_) => self.consume_place(place), | Pat::Lit(_) => self.consume_place(place, pat.into()),
Pat::Bind { id, subpat: _ } => { Pat::Bind { id, subpat: _ } => {
let mode = self.body.bindings[*id].mode; let mode = self.body.bindings[*id].mode;
if matches!(mode, BindingAnnotation::Ref | BindingAnnotation::RefMut) { if matches!(mode, BindingAnnotation::Ref | BindingAnnotation::RefMut) {
@ -640,13 +657,13 @@ impl InferenceContext<'_> {
} }
let capture_kind = match bm { let capture_kind = match bm {
BindingAnnotation::Unannotated | BindingAnnotation::Mutable => { BindingAnnotation::Unannotated | BindingAnnotation::Mutable => {
self.consume_place(place); self.consume_place(place, pat.into());
return; return;
} }
BindingAnnotation::Ref => BorrowKind::Shared, BindingAnnotation::Ref => BorrowKind::Shared,
BindingAnnotation::RefMut => BorrowKind::Mut { allow_two_phase_borrow: false }, BindingAnnotation::RefMut => BorrowKind::Mut { allow_two_phase_borrow: false },
}; };
self.add_capture(place, CaptureKind::ByRef(capture_kind)); self.add_capture(place, CaptureKind::ByRef(capture_kind), pat.into());
} }
Pat::TupleStruct { path: _, args, ellipsis } => { Pat::TupleStruct { path: _, args, ellipsis } => {
pattern_matching_dereference(&mut ty, &mut bm, &mut place); pattern_matching_dereference(&mut ty, &mut bm, &mut place);
@ -659,7 +676,7 @@ impl InferenceContext<'_> {
}; };
match variant { match variant {
VariantId::EnumVariantId(_) | VariantId::UnionId(_) => { VariantId::EnumVariantId(_) | VariantId::UnionId(_) => {
self.consume_place(place) self.consume_place(place, pat.into())
} }
VariantId::StructId(s) => { VariantId::StructId(s) => {
let vd = &*self.db.struct_data(s).variant_data; let vd = &*self.db.struct_data(s).variant_data;

View file

@ -105,6 +105,20 @@ fn nested_closures() {
} }
} }
#[test]
fn capture_specific_fields2() {
size_and_align_expr! {
minicore: copy;
stmts: [
let x = &mut 2;
]
|| {
*x = 5;
&x;
}
}
}
#[test] #[test]
fn capture_specific_fields() { fn capture_specific_fields() {
size_and_align_expr! { size_and_align_expr! {

View file

@ -898,7 +898,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
current, current,
tmp.clone(), tmp.clone(),
Rvalue::Ref(bk.clone(), p), Rvalue::Ref(bk.clone(), p),
expr_id.into(), capture.span,
); );
operands.push(Operand::Move(tmp)); operands.push(Operand::Move(tmp));
}, },

View file

@ -773,7 +773,7 @@ fn fn_once(mut x: impl FnOnce(u8) -> u8) -> u8 {
#[test] #[test]
fn closure() { fn closure() {
// FIXME: Diagnostic spans are too large // FIXME: Diagnostic spans are inconsistent inside and outside closure
check_diagnostics( check_diagnostics(
r#" r#"
//- minicore: copy, fn //- minicore: copy, fn
@ -786,11 +786,11 @@ fn fn_once(mut x: impl FnOnce(u8) -> u8) -> u8 {
fn f() { fn f() {
let x = 5; let x = 5;
let closure1 = || { x = 2; }; let closure1 = || { x = 2; };
//^^^^^^^^^^^^^ 💡 error: cannot mutate immutable variable `x` //^ 💡 error: cannot mutate immutable variable `x`
let _ = closure1(); let _ = closure1();
//^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1` //^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1`
let closure2 = || { x = x; }; let closure2 = || { x = x; };
//^^^^^^^^^^^^^ 💡 error: cannot mutate immutable variable `x` //^ 💡 error: cannot mutate immutable variable `x`
let closure3 = || { let closure3 = || {
let x = 2; let x = 2;
x = 5; x = 5;
@ -799,7 +799,7 @@ fn fn_once(mut x: impl FnOnce(u8) -> u8) -> u8 {
}; };
let x = X; let x = X;
let closure4 = || { x.mutate(); }; let closure4 = || { x.mutate(); };
//^^^^^^^^^^^^^^^^^^ 💡 error: cannot mutate immutable variable `x` //^ 💡 error: cannot mutate immutable variable `x`
} }
"#, "#,
); );
@ -829,7 +829,7 @@ fn f() {
|| { || {
let x = 2; let x = 2;
|| { || { x = 5; } } || { || { x = 5; } }
//^^^^^^^^^^^^^^^^^^^^ 💡 error: cannot mutate immutable variable `x` //^ 💡 error: cannot mutate immutable variable `x`
} }
} }
}; };
@ -860,6 +860,15 @@ fn f() {
let mut x = &mut 5; let mut x = &mut 5;
//^^^^^ 💡 weak: variable does not need to be mutable //^^^^^ 💡 weak: variable does not need to be mutable
let closure1 = || { *x = 2; }; 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 = || { *x = 2; &x; };
let _ = closure1();
//^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1`
let mut x = &mut 5;
let closure1 = || { *x = 2; &x; x = &mut 3; };
let _ = closure1(); let _ = closure1();
//^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1` //^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1`
let mut x = &mut 5; let mut x = &mut 5;