diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index a19ff8bf60..4b80f06a3a 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -125,6 +125,31 @@ impl Default for BindingMode { } } +/// Used to generalize patterns and assignee expressions. +trait PatLike: Into + Copy { + type BindingMode: Copy; + + fn infer( + this: &mut InferenceContext, + id: Self, + expected_ty: &Ty, + default_bm: Self::BindingMode, + ) -> Ty; +} + +impl PatLike for PatId { + type BindingMode = BindingMode; + + fn infer( + this: &mut InferenceContext, + id: Self, + expected_ty: &Ty, + default_bm: Self::BindingMode, + ) -> Ty { + this.infer_pat(id, expected_ty, default_bm) + } +} + #[derive(Debug)] pub(crate) struct InferOk { value: T, diff --git a/crates/hir-ty/src/infer/pat.rs b/crates/hir-ty/src/infer/pat.rs index c06d262f5e..dc86f696d4 100644 --- a/crates/hir-ty/src/infer/pat.rs +++ b/crates/hir-ty/src/infer/pat.rs @@ -4,7 +4,7 @@ use std::iter::repeat_with; use chalk_ir::Mutability; use hir_def::{ - expr::{BindingAnnotation, Expr, Literal, Pat, PatId, RecordFieldPat}, + expr::{BindingAnnotation, Expr, Literal, Pat, PatId}, path::Path, type_ref::ConstScalar, }; @@ -17,15 +17,20 @@ use crate::{ TyKind, }; +use super::PatLike; + impl<'a> InferenceContext<'a> { - fn infer_tuple_struct_pat( + /// Infers type for tuple struct pattern or its corresponding assignee expression. + /// + /// Ellipses found in the original pattern or expression must be filtered out. + pub(super) fn infer_tuple_struct_pat_like( &mut self, path: Option<&Path>, - subpats: &[PatId], expected: &Ty, - default_bm: BindingMode, - id: PatId, + default_bm: T::BindingMode, + id: T, ellipsis: Option, + subs: &[T], ) -> Ty { let (ty, def) = self.resolve_variant(path, true); let var_data = def.map(|it| it.variant_data(self.db.upcast())); @@ -39,8 +44,8 @@ impl<'a> InferenceContext<'a> { let field_tys = def.map(|it| self.db.field_types(it)).unwrap_or_default(); let (pre, post) = match ellipsis { - Some(idx) => subpats.split_at(idx), - None => (subpats, &[][..]), + Some(idx) => subs.split_at(idx), + None => (subs, &[][..]), }; let post_idx_offset = field_tys.iter().count().saturating_sub(post.len()); @@ -54,22 +59,22 @@ impl<'a> InferenceContext<'a> { 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); + T::infer(self, subpat, &expected_ty, default_bm); } ty } - fn infer_record_pat( + /// Infers type for record pattern or its corresponding assignee expression. + pub(super) fn infer_record_pat_like( &mut self, path: Option<&Path>, - subpats: &[RecordFieldPat], expected: &Ty, - default_bm: BindingMode, - id: PatId, + default_bm: T::BindingMode, + id: T, + subs: impl Iterator, ) -> Ty { 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); } @@ -80,18 +85,64 @@ impl<'a> InferenceContext<'a> { 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) - }); + let var_data = def.map(|it| it.variant_data(self.db.upcast())); + + for (name, inner) in subs { + let expected_ty = var_data + .as_ref() + .and_then(|it| it.field(&name)) + .map_or(self.err_ty(), |f| field_tys[f].clone().substitute(Interner, &substs)); let expected_ty = self.normalize_associated_types_in(expected_ty); - self.infer_pat(subpat.pat, &expected_ty, default_bm); + + T::infer(self, inner, &expected_ty, default_bm); } ty } + /// Infers type for tuple pattern or its corresponding assignee expression. + /// + /// Ellipses found in the original pattern or expression must be filtered out. + pub(super) fn infer_tuple_pat_like( + &mut self, + expected: &Ty, + default_bm: T::BindingMode, + ellipsis: Option, + subs: &[T], + ) -> Ty { + let expectations = match expected.as_tuple() { + Some(parameters) => &*parameters.as_slice(Interner), + _ => &[], + }; + + let ((pre, post), n_uncovered_patterns) = match ellipsis { + Some(idx) => (subs.split_at(idx), expectations.len().saturating_sub(subs.len())), + None => ((&subs[..], &[][..]), 0), + }; + let mut expectations_iter = expectations + .iter() + .cloned() + .map(|a| a.assert_ty_ref(Interner).clone()) + .chain(repeat_with(|| self.table.new_type_var())); + + let mut inner_tys = Vec::with_capacity(n_uncovered_patterns + subs.len()); + + inner_tys.extend(expectations_iter.by_ref().take(n_uncovered_patterns + subs.len())); + + // Process pre + for (ty, pat) in inner_tys.iter_mut().zip(pre) { + *ty = T::infer(self, *pat, ty, default_bm); + } + + // Process post + for (ty, pat) in inner_tys.iter_mut().skip(pre.len() + n_uncovered_patterns).zip(post) { + *ty = T::infer(self, *pat, ty, default_bm); + } + + TyKind::Tuple(inner_tys.len(), Substitution::from_iter(Interner, inner_tys)) + .intern(Interner) + } + pub(super) fn infer_pat( &mut self, pat: PatId, @@ -129,42 +180,7 @@ impl<'a> InferenceContext<'a> { let ty = match &self.body[pat] { Pat::Tuple { args, ellipsis } => { - let expectations = match expected.as_tuple() { - 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())) - } - None => ((&args[..], &[][..]), 0), - }; - let mut expectations_iter = expectations - .iter() - .cloned() - .map(|a| a.assert_ty_ref(Interner).clone()) - .chain(repeat_with(|| self.table.new_type_var())); - - let mut inner_tys = Vec::with_capacity(n_uncovered_patterns + args.len()); - - inner_tys - .extend(expectations_iter.by_ref().take(n_uncovered_patterns + args.len())); - - // Process pre - for (ty, pat) in inner_tys.iter_mut().zip(pre) { - *ty = self.infer_pat(*pat, ty, default_bm); - } - - // Process post - for (ty, pat) in - inner_tys.iter_mut().skip(pre.len() + n_uncovered_patterns).zip(post) - { - *ty = self.infer_pat(*pat, ty, default_bm); - } - - TyKind::Tuple(inner_tys.len(), Substitution::from_iter(Interner, inner_tys)) - .intern(Interner) + self.infer_tuple_pat_like(&expected, default_bm, *ellipsis, args) } Pat::Or(pats) => { if let Some((first_pat, rest)) = pats.split_first() { @@ -191,16 +207,18 @@ impl<'a> InferenceContext<'a> { let subty = self.infer_pat(*pat, &expectation, default_bm); TyKind::Ref(mutability, static_lifetime(), subty).intern(Interner) } - Pat::TupleStruct { path: p, args: subpats, ellipsis } => self.infer_tuple_struct_pat( - p.as_deref(), - subpats, - &expected, - default_bm, - pat, - *ellipsis, - ), + Pat::TupleStruct { path: p, args: subpats, ellipsis } => self + .infer_tuple_struct_pat_like( + p.as_deref(), + &expected, + default_bm, + pat, + *ellipsis, + subpats, + ), Pat::Record { path: p, args: fields, ellipsis: _ } => { - self.infer_record_pat(p.as_deref(), fields, &expected, default_bm, pat) + let subs = fields.iter().map(|f| (f.name.clone(), f.pat)); + self.infer_record_pat_like(p.as_deref(), &expected, default_bm, pat.into(), subs) } Pat::Path(path) => { // FIXME use correct resolver for the surrounding expression