Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions crates/hir-def/src/body.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,15 +221,15 @@ impl Body {

pub fn walk_bindings_in_pat(&self, pat_id: PatId, mut f: impl FnMut(BindingId)) {
self.walk_pats(pat_id, &mut |pat| {
if let Pat::Bind { id, .. } = pat {
if let Pat::Bind { id, .. } = &self[pat] {
f(*id);
}
});
}

pub fn walk_pats(&self, pat_id: PatId, f: &mut impl FnMut(&Pat)) {
pub fn walk_pats(&self, pat_id: PatId, f: &mut impl FnMut(PatId)) {
let pat = &self[pat_id];
f(pat);
f(pat_id);
match pat {
Pat::Range { .. }
| Pat::Lit(..)
Expand Down
2 changes: 1 addition & 1 deletion crates/hir-ty/src/diagnostics/match_check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ impl<'a> PatCtxt<'a> {
}

hir_def::hir::Pat::Bind { id, subpat, .. } => {
let bm = self.infer.pat_binding_modes[&pat];
let bm = self.infer.binding_modes[id];
ty = &self.infer[id];
let name = &self.body.bindings[id].name;
match (bm, ty.kind(Interner)) {
Expand Down
2 changes: 1 addition & 1 deletion crates/hir-ty/src/infer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,7 @@ pub struct InferenceResult {
standard_types: InternedStandardTypes,
/// Stores the types which were implicitly dereferenced in pattern binding modes.
pub pat_adjustments: FxHashMap<PatId, Vec<Ty>>,
pub pat_binding_modes: FxHashMap<PatId, BindingMode>,
pub binding_modes: ArenaMap<BindingId, BindingMode>,
pub expr_adjustments: FxHashMap<ExprId, Vec<Adjustment>>,
pub(crate) closure_info: FxHashMap<ClosureId, (Vec<CapturedItem>, FnTrait)>,
// FIXME: remove this field
Expand Down
75 changes: 72 additions & 3 deletions crates/hir-ty/src/infer/closure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -504,9 +504,27 @@ impl InferenceContext<'_> {
self.consume_exprs(args.iter().copied());
}
Expr::Match { expr, arms } => {
self.consume_expr(*expr);
for arm in arms.iter() {
self.consume_expr(arm.expr);
if let Some(guard) = arm.guard {
self.consume_expr(guard);
}
}
self.walk_expr(*expr);
if let Some(discr_place) = self.place_of_expr(*expr) {
if self.is_upvar(&discr_place) {
let mut capture_mode = None;
for arm in arms.iter() {
self.walk_pat(&mut capture_mode, arm.pat);
}
if let Some(c) = capture_mode {
self.push_capture(CapturedItemWithoutTy {
place: discr_place,
kind: c,
span: (*expr).into(),
})
}
}
}
}
Expr::Break { expr, label: _ }
Expand Down Expand Up @@ -618,6 +636,57 @@ impl InferenceContext<'_> {
}
}

fn walk_pat(&mut self, result: &mut Option<CaptureKind>, pat: PatId) {
let mut update_result = |ck: CaptureKind| match result {
Some(r) => {
*r = cmp::max(*r, ck);
}
None => *result = Some(ck),
};
self.body.walk_pats(pat, &mut |p| match &self.body[p] {
Pat::Ref { .. }
| Pat::Box { .. }
| Pat::Missing
| Pat::Wild
| Pat::Tuple { .. }
| Pat::Or(_) => (),
Pat::TupleStruct { .. } | Pat::Record { .. } => {
if let Some(variant) = self.result.variant_resolution_for_pat(p) {
let adt = variant.adt_id();
let is_multivariant = match adt {
hir_def::AdtId::EnumId(e) => self.db.enum_data(e).variants.len() != 1,
_ => false,
};
if is_multivariant {
update_result(CaptureKind::ByRef(BorrowKind::Shared));
}
}
}
Pat::Slice { .. }
| Pat::ConstBlock(_)
| Pat::Path(_)
| Pat::Lit(_)
| Pat::Range { .. } => {
update_result(CaptureKind::ByRef(BorrowKind::Shared));
}
Pat::Bind { id, .. } => match self.result.binding_modes[*id] {
crate::BindingMode::Move => {
if self.is_ty_copy(self.result.type_of_binding[*id].clone()) {
update_result(CaptureKind::ByRef(BorrowKind::Shared));
} else {
update_result(CaptureKind::ByValue);
}
}
crate::BindingMode::Ref(r) => match r {
Mutability::Mut => update_result(CaptureKind::ByRef(BorrowKind::Mut {
allow_two_phase_borrow: false,
})),
Mutability::Not => update_result(CaptureKind::ByRef(BorrowKind::Shared)),
},
},
});
}

fn expr_ty(&self, expr: ExprId) -> Ty {
self.result[expr].clone()
}
Expand All @@ -641,14 +710,14 @@ impl InferenceContext<'_> {
false
}

fn is_ty_copy(&self, ty: Ty) -> bool {
fn is_ty_copy(&mut self, ty: Ty) -> bool {
if let TyKind::Closure(id, _) = ty.kind(Interner) {
// FIXME: We handle closure as a special case, since chalk consider every closure as copy. We
// should probably let chalk know which closures are copy, but I don't know how doing it
// without creating query cycles.
return self.result.closure_info.get(id).map(|x| x.1 == FnTrait::Fn).unwrap_or(true);
}
ty.is_copy(self.db, self.owner)
self.table.resolve_completely(ty).is_copy(self.db, self.owner)
}

fn select_from_expr(&mut self, expr: ExprId) {
Expand Down
4 changes: 2 additions & 2 deletions crates/hir-ty/src/infer/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ impl<'a> InferenceContext<'a> {
} else {
BindingMode::convert(mode)
};
self.result.pat_binding_modes.insert(pat, mode);
self.result.binding_modes.insert(binding, mode);

let inner_ty = match subpat {
Some(subpat) => self.infer_pat(subpat, &expected, default_bm),
Expand Down Expand Up @@ -439,7 +439,7 @@ fn is_non_ref_pat(body: &hir_def::body::Body, pat: PatId) -> bool {
pub(super) fn contains_explicit_ref_binding(body: &Body, pat_id: PatId) -> bool {
let mut res = false;
body.walk_pats(pat_id, &mut |pat| {
res |= matches!(pat, Pat::Bind { id, .. } if body.bindings[*id].mode == BindingAnnotation::Ref);
res |= matches!(body[pat], Pat::Bind { id, .. } if body.bindings[id].mode == BindingAnnotation::Ref);
});
res
}
37 changes: 37 additions & 0 deletions crates/hir-ty/src/layout/tests/closure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,43 @@ fn capture_specific_fields() {
}
}

#[test]
fn match_pattern() {
size_and_align_expr! {
struct X(i64, i32, (u8, i128));
let y: X = X(2, 5, (7, 3));
move |x: i64| {
match y {
_ => x,
}
}
}
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 {
X(_a, _b, _c) => x,
}
}
}
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 {
_y => x,
}
}
}
}

#[test]
fn ellipsis_pattern() {
size_and_align_expr! {
Expand Down
4 changes: 2 additions & 2 deletions crates/hir/src/source_analyzer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -236,9 +236,9 @@ impl SourceAnalyzer {
_db: &dyn HirDatabase,
pat: &ast::IdentPat,
) -> Option<BindingMode> {
let pat_id = self.pat_id(&pat.clone().into())?;
let binding_id = self.binding_id_of_pat(pat)?;
let infer = self.infer.as_ref()?;
infer.pat_binding_modes.get(&pat_id).map(|bm| match bm {
infer.binding_modes.get(binding_id).map(|bm| match bm {
hir_ty::BindingMode::Move => BindingMode::Move,
hir_ty::BindingMode::Ref(hir_ty::Mutability::Mut) => BindingMode::Ref(Mutability::Mut),
hir_ty::BindingMode::Ref(hir_ty::Mutability::Not) => {
Expand Down
21 changes: 21 additions & 0 deletions crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,4 +151,25 @@ fn f(x: &mut X<'_>) {
"#,
);
}

#[test]
fn no_false_positive_match_and_closure_capture() {
check_diagnostics(
r#"
//- minicore: copy, fn
enum X {
Foo(u16),
Bar,
}

fn main() {
let x = &X::Bar;
let c = || match *x {
X::Foo(t) => t,
_ => 5,
};
}
"#,
);
}
}
27 changes: 27 additions & 0 deletions crates/ide/src/hover/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,33 @@ fn main() {
* `(*x.f2.0.0).f` by mutable borrow
"#]],
);
check(
r#"
//- minicore: copy, option

fn do_char(c: char) {}

fn main() {
let x = None;
let y = |$0| {
match x {
Some(c) => do_char(c),
None => x = None,
}
};
}
"#,
expect![[r#"
*|*
```rust
{closure#0} // size = 8, align = 8
impl FnMut()
```

## Captures
* `x` by mutable borrow
"#]],
);
}

#[test]
Expand Down