Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/infer/closure.rs')
-rw-r--r--crates/hir-ty/src/infer/closure.rs122
1 files changed, 100 insertions, 22 deletions
diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs
index 22a70f951e..32845ac2e3 100644
--- a/crates/hir-ty/src/infer/closure.rs
+++ b/crates/hir-ty/src/infer/closure.rs
@@ -5,7 +5,7 @@ use std::{cmp, convert::Infallible, mem};
use chalk_ir::{
cast::Cast,
fold::{FallibleTypeFolder, TypeFoldable},
- AliasEq, AliasTy, BoundVar, DebruijnIndex, FnSubst, Mutability, TyKind, WhereClause,
+ BoundVar, DebruijnIndex, FnSubst, Mutability, TyKind,
};
use either::Either;
use hir_def::{
@@ -22,13 +22,14 @@ use stdx::never;
use crate::{
db::{HirDatabase, InternedClosure},
- from_placeholder_idx, make_binders,
- mir::{BorrowKind, MirSpan, ProjectionElem},
+ from_chalk_trait_id, from_placeholder_idx, make_binders,
+ mir::{BorrowKind, MirSpan, MutBorrowKind, ProjectionElem},
static_lifetime, to_chalk_trait_id,
traits::FnTrait,
- utils::{self, generics, Generics},
- Adjust, Adjustment, Binders, BindingMode, ChalkTraitId, ClosureId, DynTy, FnAbi, FnPointer,
- FnSig, Interner, Substitution, Ty, TyExt,
+ utils::{self, elaborate_clause_supertraits, generics, Generics},
+ Adjust, Adjustment, AliasEq, AliasTy, Binders, BindingMode, ChalkTraitId, ClosureId, DynTy,
+ DynTyExt, FnAbi, FnPointer, FnSig, Interner, OpaqueTy, ProjectionTyExt, Substitution, Ty,
+ TyExt, WhereClause,
};
use super::{Expectation, InferenceContext};
@@ -47,6 +48,15 @@ impl InferenceContext<'_> {
None => return,
};
+ if let TyKind::Closure(closure_id, _) = closure_ty.kind(Interner) {
+ if let Some(closure_kind) = self.deduce_closure_kind_from_expectations(&expected_ty) {
+ self.result
+ .closure_info
+ .entry(*closure_id)
+ .or_insert_with(|| (Vec::new(), closure_kind));
+ }
+ }
+
// Deduction from where-clauses in scope, as well as fn-pointer coercion are handled here.
let _ = self.coerce(Some(closure_expr), closure_ty, &expected_ty);
@@ -65,6 +75,60 @@ impl InferenceContext<'_> {
}
}
+ // Closure kind deductions are mostly from `rustc_hir_typeck/src/closure.rs`.
+ // Might need to port closure sig deductions too.
+ fn deduce_closure_kind_from_expectations(&mut self, expected_ty: &Ty) -> Option<FnTrait> {
+ match expected_ty.kind(Interner) {
+ TyKind::Alias(AliasTy::Opaque(OpaqueTy { .. })) | TyKind::OpaqueType(..) => {
+ let clauses = expected_ty
+ .impl_trait_bounds(self.db)
+ .into_iter()
+ .flatten()
+ .map(|b| b.into_value_and_skipped_binders().0);
+ self.deduce_closure_kind_from_predicate_clauses(clauses)
+ }
+ TyKind::Dyn(dyn_ty) => dyn_ty.principal().and_then(|trait_ref| {
+ self.fn_trait_kind_from_trait_id(from_chalk_trait_id(trait_ref.trait_id))
+ }),
+ TyKind::InferenceVar(ty, chalk_ir::TyVariableKind::General) => {
+ let clauses = self.clauses_for_self_ty(*ty);
+ self.deduce_closure_kind_from_predicate_clauses(clauses.into_iter())
+ }
+ TyKind::Function(_) => Some(FnTrait::Fn),
+ _ => None,
+ }
+ }
+
+ fn deduce_closure_kind_from_predicate_clauses(
+ &self,
+ clauses: impl DoubleEndedIterator<Item = WhereClause>,
+ ) -> Option<FnTrait> {
+ let mut expected_kind = None;
+
+ for clause in elaborate_clause_supertraits(self.db, clauses.rev()) {
+ let trait_id = match clause {
+ WhereClause::AliasEq(AliasEq {
+ alias: AliasTy::Projection(projection), ..
+ }) => Some(projection.trait_(self.db)),
+ WhereClause::Implemented(trait_ref) => {
+ Some(from_chalk_trait_id(trait_ref.trait_id))
+ }
+ _ => None,
+ };
+ if let Some(closure_kind) =
+ trait_id.and_then(|trait_id| self.fn_trait_kind_from_trait_id(trait_id))
+ {
+ // `FnX`'s variants order is opposite from rustc, so use `cmp::max` instead of `cmp::min`
+ expected_kind = Some(
+ expected_kind
+ .map_or_else(|| closure_kind, |current| cmp::max(current, closure_kind)),
+ );
+ }
+ }
+
+ expected_kind
+ }
+
fn deduce_sig_from_dyn_ty(&self, dyn_ty: &DynTy) -> Option<FnPointer> {
// Search for a predicate like `<$self as FnX<Args>>::Output == Ret`
@@ -111,6 +175,10 @@ impl InferenceContext<'_> {
None
}
+
+ fn fn_trait_kind_from_trait_id(&self, trait_id: hir_def::TraitId) -> Option<FnTrait> {
+ FnTrait::from_lang_item(self.db.lang_attr(trait_id.into())?)
+ }
}
// The below functions handle capture and closure kind (Fn, FnMut, ..)
@@ -142,9 +210,13 @@ impl HirPlace {
mut current_capture: CaptureKind,
len: usize,
) -> CaptureKind {
- if let CaptureKind::ByRef(BorrowKind::Mut { .. }) = current_capture {
+ if let CaptureKind::ByRef(BorrowKind::Mut {
+ kind: MutBorrowKind::Default | MutBorrowKind::TwoPhasedBorrow,
+ }) = current_capture
+ {
if self.projections[len..].iter().any(|it| *it == ProjectionElem::Deref) {
- current_capture = CaptureKind::ByRef(BorrowKind::Unique);
+ current_capture =
+ CaptureKind::ByRef(BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture });
}
}
current_capture
@@ -377,7 +449,7 @@ impl InferenceContext<'_> {
if let Some(place) = self.place_of_expr(expr) {
self.add_capture(
place,
- CaptureKind::ByRef(BorrowKind::Mut { allow_two_phase_borrow: false }),
+ CaptureKind::ByRef(BorrowKind::Mut { kind: MutBorrowKind::Default }),
expr.into(),
);
}
@@ -426,9 +498,7 @@ impl InferenceContext<'_> {
fn ref_capture_with_adjusts(&mut self, m: Mutability, tgt_expr: ExprId, rest: &[Adjustment]) {
let capture_kind = match m {
- Mutability::Mut => {
- CaptureKind::ByRef(BorrowKind::Mut { allow_two_phase_borrow: false })
- }
+ Mutability::Mut => CaptureKind::ByRef(BorrowKind::Mut { kind: MutBorrowKind::Default }),
Mutability::Not => CaptureKind::ByRef(BorrowKind::Shared),
};
if let Some(place) = self.place_of_expr_without_adjust(tgt_expr) {
@@ -648,7 +718,7 @@ impl InferenceContext<'_> {
self.walk_pat_inner(
pat,
&mut update_result,
- BorrowKind::Mut { allow_two_phase_borrow: false },
+ BorrowKind::Mut { kind: MutBorrowKind::Default },
);
}
@@ -699,7 +769,7 @@ impl InferenceContext<'_> {
},
}
if self.result.pat_adjustments.get(&p).map_or(false, |it| !it.is_empty()) {
- for_mut = BorrowKind::Unique;
+ for_mut = BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture };
}
self.body.walk_pats_shallow(p, |p| self.walk_pat_inner(p, update_result, for_mut));
}
@@ -880,7 +950,7 @@ impl InferenceContext<'_> {
}
BindingMode::Ref(Mutability::Not) => BorrowKind::Shared,
BindingMode::Ref(Mutability::Mut) => {
- BorrowKind::Mut { allow_two_phase_borrow: false }
+ BorrowKind::Mut { kind: MutBorrowKind::Default }
}
};
self.add_capture(place, CaptureKind::ByRef(capture_kind), pat.into());
@@ -930,9 +1000,7 @@ impl InferenceContext<'_> {
r = cmp::min(
r,
match &it.kind {
- CaptureKind::ByRef(BorrowKind::Unique | BorrowKind::Mut { .. }) => {
- FnTrait::FnMut
- }
+ CaptureKind::ByRef(BorrowKind::Mut { .. }) => FnTrait::FnMut,
CaptureKind::ByRef(BorrowKind::Shallow | BorrowKind::Shared) => FnTrait::Fn,
CaptureKind::ByValue => FnTrait::FnOnce,
},
@@ -949,8 +1017,12 @@ impl InferenceContext<'_> {
};
self.consume_expr(*body);
for item in &self.current_captures {
- if matches!(item.kind, CaptureKind::ByRef(BorrowKind::Mut { .. }))
- && !item.place.projections.contains(&ProjectionElem::Deref)
+ if matches!(
+ item.kind,
+ CaptureKind::ByRef(BorrowKind::Mut {
+ kind: MutBorrowKind::Default | MutBorrowKind::TwoPhasedBorrow
+ })
+ ) && !item.place.projections.contains(&ProjectionElem::Deref)
{
// FIXME: remove the `mutated_bindings_in_closure` completely and add proper fake reads in
// MIR. I didn't do that due duplicate diagnostics.
@@ -958,8 +1030,14 @@ impl InferenceContext<'_> {
}
}
self.restrict_precision_for_unsafe();
- // closure_kind should be done before adjust_for_move_closure
- let closure_kind = self.closure_kind();
+ // `closure_kind` should be done before adjust_for_move_closure
+ // If there exists pre-deduced kind of a closure, use it instead of one determined by capture, as rustc does.
+ // rustc also does diagnostics here if the latter is not a subtype of the former.
+ let closure_kind = self
+ .result
+ .closure_info
+ .get(&closure)
+ .map_or_else(|| self.closure_kind(), |info| info.1);
match capture_by {
CaptureBy::Value => self.adjust_for_move_closure(),
CaptureBy::Ref => (),