//! Type inference for patterns.
-use std::iter::repeat;
-use std::sync::Arc;
+use std::{iter::repeat, sync::Arc};
use chalk_ir::Mutability;
use hir_def::{
expr::{BindingAnnotation, Expr, Literal, Pat, PatId, RecordFieldPat},
path::Path,
+ type_ref::ConstScalar,
};
use hir_expand::name::Name;
-use super::{BindingMode, Expectation, InferenceContext, TypeMismatch};
use crate::{
- lower::lower_to_chalk_mutability, static_lifetime, Interner, Substitution, Ty, TyBuilder,
- TyExt, TyKind,
+ infer::{
+ Adjust, Adjustment, AutoBorrow, BindingMode, Expectation, InferenceContext, TypeMismatch,
+ },
+ lower::lower_to_chalk_mutability,
+ static_lifetime, ConcreteConst, ConstValue, Interner, Substitution, Ty, TyBuilder, TyExt,
+ TyKind,
};
impl<'a> InferenceContext<'a> {
id: PatId,
ellipsis: Option<usize>,
) -> Ty {
- let (ty, def) = self.resolve_variant(path);
+ let (ty, def) = self.resolve_variant(path, true);
let var_data = def.map(|it| it.variant_data(self.db.upcast()));
if let Some(variant) = def {
self.write_variant_resolution(id.into(), variant);
self.unify(&ty, expected);
let substs =
- ty.as_adt().map(|(_, s)| s.clone()).unwrap_or_else(|| Substitution::empty(&Interner));
+ ty.as_adt().map(|(_, s)| s.clone()).unwrap_or_else(|| Substitution::empty(Interner));
let field_tys = def.map(|it| self.db.field_types(it)).unwrap_or_default();
let (pre, post) = match ellipsis {
.as_ref()
.and_then(|d| d.field(&Name::new_tuple_field(i)))
.map_or(self.err_ty(), |field| {
- field_tys[field].clone().substitute(&Interner, &substs)
+ field_tys[field].clone().substitute(Interner, &substs)
});
let expected_ty = self.normalize_associated_types_in(expected_ty);
self.infer_pat(subpat, &expected_ty, default_bm);
default_bm: BindingMode,
id: PatId,
) -> Ty {
- let (ty, def) = self.resolve_variant(path);
+ let (ty, def) = self.resolve_variant(path, false);
let var_data = def.map(|it| it.variant_data(self.db.upcast()));
if let Some(variant) = def {
self.write_variant_resolution(id.into(), variant);
self.unify(&ty, expected);
let substs =
- ty.as_adt().map(|(_, s)| s.clone()).unwrap_or_else(|| Substitution::empty(&Interner));
+ ty.as_adt().map(|(_, s)| s.clone()).unwrap_or_else(|| Substitution::empty(Interner));
let field_tys = def.map(|it| self.db.field_types(it)).unwrap_or_default();
for subpat in subpats {
let matching_field = var_data.as_ref().and_then(|it| it.field(&subpat.name));
let expected_ty = matching_field.map_or(self.err_ty(), |field| {
- field_tys[field].clone().substitute(&Interner, &substs)
+ field_tys[field].clone().substitute(Interner, &substs)
});
let expected_ty = self.normalize_associated_types_in(expected_ty);
self.infer_pat(subpat.pat, &expected_ty, default_bm);
let mut expected = self.resolve_ty_shallow(expected);
if is_non_ref_pat(&body, pat) {
+ let mut pat_adjustments = Vec::new();
while let Some((inner, _lifetime, mutability)) = expected.as_reference() {
+ pat_adjustments.push(Adjustment {
+ target: expected.clone(),
+ kind: Adjust::Borrow(AutoBorrow::Ref(mutability)),
+ });
expected = self.resolve_ty_shallow(inner);
default_bm = match default_bm {
BindingMode::Move => BindingMode::Ref(mutability),
BindingMode::Ref(Mutability::Mut) => BindingMode::Ref(mutability),
}
}
+
+ if !pat_adjustments.is_empty() {
+ pat_adjustments.shrink_to_fit();
+ self.result.pat_adjustments.insert(pat, pat_adjustments);
+ }
} else if let Pat::Ref { .. } = &body[pat] {
cov_mark::hit!(match_ergonomics_ref);
// When you encounter a `&pat` pattern, reset to Move.
let expected = expected;
let ty = match &body[pat] {
- &Pat::Tuple { ref args, ellipsis } => {
+ Pat::Tuple { args, ellipsis } => {
let expectations = match expected.as_tuple() {
- Some(parameters) => &*parameters.as_slice(&Interner),
+ Some(parameters) => &*parameters.as_slice(Interner),
_ => &[],
};
let ((pre, post), n_uncovered_patterns) = match ellipsis {
Some(idx) => {
- (args.split_at(idx), expectations.len().saturating_sub(args.len()))
+ (args.split_at(*idx), expectations.len().saturating_sub(args.len()))
}
None => ((&args[..], &[][..]), 0),
};
let err_ty = self.err_ty();
let mut expectations_iter =
- expectations.iter().map(|a| a.assert_ty_ref(&Interner)).chain(repeat(&err_ty));
+ expectations.iter().map(|a| a.assert_ty_ref(Interner)).chain(repeat(&err_ty));
let mut infer_pat = |(&pat, ty)| self.infer_pat(pat, ty, default_bm);
let mut inner_tys = Vec::with_capacity(n_uncovered_patterns + args.len());
inner_tys.extend(expectations_iter.by_ref().take(n_uncovered_patterns).cloned());
inner_tys.extend(post.iter().zip(expectations_iter).map(infer_pat));
- TyKind::Tuple(inner_tys.len(), Substitution::from_iter(&Interner, inner_tys))
- .intern(&Interner)
+ TyKind::Tuple(inner_tys.len(), Substitution::from_iter(Interner, inner_tys))
+ .intern(Interner)
}
- Pat::Or(ref pats) => {
+ Pat::Or(pats) => {
if let Some((first_pat, rest)) = pats.split_first() {
let ty = self.infer_pat(*first_pat, &expected, default_bm);
for pat in rest {
_ => self.result.standard_types.unknown.clone(),
};
let subty = self.infer_pat(*pat, &expectation, default_bm);
- TyKind::Ref(mutability, static_lifetime(), subty).intern(&Interner)
+ TyKind::Ref(mutability, static_lifetime(), subty).intern(Interner)
}
Pat::TupleStruct { path: p, args: subpats, ellipsis } => self.infer_tuple_struct_pat(
p.as_deref(),
Pat::Path(path) => {
// FIXME use correct resolver for the surrounding expression
let resolver = self.resolver.clone();
- self.infer_path(&resolver, &path, pat.into()).unwrap_or(self.err_ty())
+ self.infer_path(&resolver, path, pat.into()).unwrap_or_else(|| self.err_ty())
}
Pat::Bind { mode, name: _, subpat } => {
let mode = if mode == &BindingAnnotation::Unannotated {
} else {
BindingMode::convert(*mode)
};
- let inner_ty = if let Some(subpat) = subpat {
- self.infer_pat(*subpat, &expected, default_bm)
- } else {
- expected
+ self.result.pat_binding_modes.insert(pat, mode);
+
+ let inner_ty = match subpat {
+ Some(subpat) => self.infer_pat(*subpat, &expected, default_bm),
+ None => expected,
};
let inner_ty = self.insert_type_vars_shallow(inner_ty);
let bound_ty = match mode {
BindingMode::Ref(mutability) => {
TyKind::Ref(mutability, static_lifetime(), inner_ty.clone())
- .intern(&Interner)
+ .intern(Interner)
}
BindingMode::Move => inner_ty.clone(),
};
return inner_ty;
}
Pat::Slice { prefix, slice, suffix } => {
- let elem_ty = match expected.kind(&Interner) {
+ let elem_ty = match expected.kind(Interner) {
TyKind::Array(st, _) | TyKind::Slice(st) => st.clone(),
_ => self.err_ty(),
};
- for pat_id in prefix.iter().chain(suffix) {
- self.infer_pat(*pat_id, &elem_ty, default_bm);
+ for &pat_id in prefix.iter().chain(suffix.iter()) {
+ self.infer_pat(pat_id, &elem_ty, default_bm);
}
- let pat_ty = match expected.kind(&Interner) {
+ if let &Some(slice_pat_id) = slice {
+ let rest_pat_ty = match expected.kind(Interner) {
+ TyKind::Array(_, length) => {
+ let length = match length.data(Interner).value {
+ ConstValue::Concrete(ConcreteConst {
+ interned: ConstScalar::Usize(length),
+ }) => length.checked_sub((prefix.len() + suffix.len()) as u64),
+ _ => None,
+ };
+ TyKind::Array(elem_ty.clone(), crate::consteval::usize_const(length))
+ }
+ _ => TyKind::Slice(elem_ty.clone()),
+ }
+ .intern(Interner);
+ self.infer_pat(slice_pat_id, &rest_pat_ty, default_bm);
+ }
+
+ match expected.kind(Interner) {
TyKind::Array(_, const_) => TyKind::Array(elem_ty, const_.clone()),
_ => TyKind::Slice(elem_ty),
}
- .intern(&Interner);
- if let Some(slice_pat_id) = slice {
- self.infer_pat(*slice_pat_id, &pat_ty, default_bm);
- }
-
- pat_ty
+ .intern(Interner)
}
Pat::Wild => expected.clone(),
Pat::Range { start, end } => {
let start_ty = self.infer_expr(*start, &Expectation::has_type(expected.clone()));
- let end_ty = self.infer_expr(*end, &Expectation::has_type(start_ty));
- end_ty
+ self.infer_expr(*end, &Expectation::has_type(start_ty))
}
Pat::Lit(expr) => self.infer_expr(*expr, &Expectation::has_type(expected.clone())),
Pat::Box { inner } => match self.resolve_boxed_box() {
Some(box_adt) => {
let (inner_ty, alloc_ty) = match expected.as_adt() {
Some((adt, subst)) if adt == box_adt => (
- subst.at(&Interner, 0).assert_ty_ref(&Interner).clone(),
- subst.as_slice(&Interner).get(1).and_then(|a| a.ty(&Interner).cloned()),
+ subst.at(Interner, 0).assert_ty_ref(Interner).clone(),
+ subst.as_slice(Interner).get(1).and_then(|a| a.ty(Interner).cloned()),
),
_ => (self.result.standard_types.unknown.clone(), None),
};
if !self.unify(&ty, &expected) {
self.result
.type_mismatches
- .insert(pat.into(), TypeMismatch { expected: expected, actual: ty.clone() });
+ .insert(pat.into(), TypeMismatch { expected, actual: ty.clone() });
}
self.write_pat_ty(pat, ty.clone());
ty
// FIXME: ConstBlock/Path/Lit might actually evaluate to ref, but inference is unimplemented.
Pat::Path(..) => true,
Pat::ConstBlock(..) => true,
- Pat::Lit(expr) => match body[*expr] {
- Expr::Literal(Literal::String(..)) => false,
- _ => true,
- },
+ Pat::Lit(expr) => !matches!(body[*expr], Expr::Literal(Literal::String(..))),
+ Pat::Bind {
+ mode: BindingAnnotation::Mutable | BindingAnnotation::Unannotated,
+ subpat: Some(subpat),
+ ..
+ } => is_non_ref_pat(body, *subpat),
Pat::Wild | Pat::Bind { .. } | Pat::Ref { .. } | Pat::Box { .. } | Pat::Missing => false,
}
}