Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/infer/pat.rs')
| -rw-r--r-- | crates/hir-ty/src/infer/pat.rs | 169 |
1 files changed, 100 insertions, 69 deletions
diff --git a/crates/hir-ty/src/infer/pat.rs b/crates/hir-ty/src/infer/pat.rs index fee6755408..50e761196e 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 hir_def::{ body::Body, - hir::{Binding, BindingAnnotation, BindingId, Expr, ExprId, ExprOrPatId, Literal, Pat, PatId}, + hir::{Binding, BindingAnnotation, BindingId, Expr, ExprId, Literal, Pat, PatId}, path::Path, }; use hir_expand::name::Name; @@ -12,63 +12,28 @@ use stdx::TupleExt; use crate::{ consteval::{try_const_usize, usize_const}, - infer::{expr::ExprIsRead, BindingMode, Expectation, InferenceContext, TypeMismatch}, + infer::{ + coerce::CoerceNever, expr::ExprIsRead, BindingMode, Expectation, InferenceContext, + TypeMismatch, + }, lower::lower_to_chalk_mutability, primitive::UintTy, static_lifetime, InferenceDiagnostic, Interner, Mutability, Scalar, Substitution, Ty, TyBuilder, TyExt, TyKind, }; -/// Used to generalize patterns and assignee expressions. -pub(super) trait PatLike: Into<ExprOrPatId> + Copy { - type BindingMode: Copy; - - fn infer( - this: &mut InferenceContext<'_>, - id: Self, - expected_ty: &Ty, - default_bm: Self::BindingMode, - ) -> Ty; -} - -impl PatLike for ExprId { - type BindingMode = (); - - fn infer( - this: &mut InferenceContext<'_>, - id: Self, - expected_ty: &Ty, - (): Self::BindingMode, - ) -> Ty { - this.infer_assignee_expr(id, expected_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) - } -} - impl InferenceContext<'_> { /// 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<T: PatLike>( + pub(super) fn infer_tuple_struct_pat_like( &mut self, path: Option<&Path>, expected: &Ty, - default_bm: T::BindingMode, - id: T, + default_bm: BindingMode, + id: PatId, ellipsis: Option<u32>, - subs: &[T], + subs: &[PatId], ) -> Ty { let (ty, def) = self.resolve_variant(path, true); let var_data = def.map(|it| it.variant_data(self.db.upcast())); @@ -127,13 +92,13 @@ impl InferenceContext<'_> { } }; - T::infer(self, subpat, &expected_ty, default_bm); + self.infer_pat(subpat, &expected_ty, default_bm); } } None => { let err_ty = self.err_ty(); for &inner in subs { - T::infer(self, inner, &err_ty, default_bm); + self.infer_pat(inner, &err_ty, default_bm); } } } @@ -142,13 +107,13 @@ impl InferenceContext<'_> { } /// Infers type for record pattern or its corresponding assignee expression. - pub(super) fn infer_record_pat_like<T: PatLike>( + pub(super) fn infer_record_pat_like( &mut self, path: Option<&Path>, expected: &Ty, - default_bm: T::BindingMode, - id: T, - subs: impl ExactSizeIterator<Item = (Name, T)>, + default_bm: BindingMode, + id: PatId, + subs: impl ExactSizeIterator<Item = (Name, PatId)>, ) -> Ty { let (ty, def) = self.resolve_variant(path, false); if let Some(variant) = def { @@ -197,13 +162,13 @@ impl InferenceContext<'_> { } }; - T::infer(self, inner, &expected_ty, default_bm); + self.infer_pat(inner, &expected_ty, default_bm); } } None => { let err_ty = self.err_ty(); for (_, inner) in subs { - T::infer(self, inner, &err_ty, default_bm); + self.infer_pat(inner, &err_ty, default_bm); } } } @@ -214,12 +179,12 @@ impl InferenceContext<'_> { /// 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<T: PatLike>( + pub(super) fn infer_tuple_pat_like( &mut self, expected: &Ty, - default_bm: T::BindingMode, + default_bm: BindingMode, ellipsis: Option<u32>, - subs: &[T], + subs: &[PatId], ) -> Ty { let expected = self.resolve_ty_shallow(expected); let expectations = match expected.as_tuple() { @@ -244,18 +209,20 @@ impl InferenceContext<'_> { // Process pre for (ty, pat) in inner_tys.iter_mut().zip(pre) { - *ty = T::infer(self, *pat, ty, default_bm); + *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 = T::infer(self, *pat, ty, default_bm); + *ty = self.infer_pat(*pat, ty, default_bm); } TyKind::Tuple(inner_tys.len(), Substitution::from_iter(Interner, inner_tys)) .intern(Interner) } + /// The resolver needs to be updated to the surrounding expression when inside assignment + /// (because there, `Pat::Path` can refer to a variable). pub(super) fn infer_top_pat(&mut self, pat: PatId, expected: &Ty) { self.infer_pat(pat, expected, BindingMode::default()); } @@ -263,7 +230,14 @@ impl InferenceContext<'_> { fn infer_pat(&mut self, pat: PatId, expected: &Ty, mut default_bm: BindingMode) -> Ty { let mut expected = self.resolve_ty_shallow(expected); - if self.is_non_ref_pat(self.body, pat) { + if matches!(&self.body[pat], Pat::Ref { .. }) || self.inside_assignment { + cov_mark::hit!(match_ergonomics_ref); + // When you encounter a `&pat` pattern, reset to Move. + // This is so that `w` is by value: `let (_, &w) = &(1, &2);` + // Destructuring assignments also reset the binding mode and + // don't do match ergonomics. + default_bm = BindingMode::Move; + } else if self.is_non_ref_pat(self.body, pat) { let mut pat_adjustments = Vec::new(); while let Some((inner, _lifetime, mutability)) = expected.as_reference() { pat_adjustments.push(expected.clone()); @@ -279,11 +253,6 @@ impl InferenceContext<'_> { pat_adjustments.shrink_to_fit(); self.result.pat_adjustments.insert(pat, pat_adjustments); } - } else if let Pat::Ref { .. } = &self.body[pat] { - cov_mark::hit!(match_ergonomics_ref); - // When you encounter a `&pat` pattern, reset to Move. - // This is so that `w` is by value: `let (_, &w) = &(1, &2);` - default_bm = BindingMode::Move; } // Lose mutability. @@ -320,8 +289,34 @@ impl InferenceContext<'_> { self.infer_record_pat_like(p.as_deref(), &expected, default_bm, pat, subs) } Pat::Path(path) => { - // FIXME update resolver for the surrounding expression - self.infer_path(path, pat.into()).unwrap_or_else(|| self.err_ty()) + let ty = self.infer_path(path, pat.into()).unwrap_or_else(|| self.err_ty()); + let ty_inserted_vars = self.insert_type_vars_shallow(ty.clone()); + match self.table.coerce(&expected, &ty_inserted_vars, CoerceNever::Yes) { + Ok((adjustments, coerced_ty)) => { + if !adjustments.is_empty() { + self.result + .pat_adjustments + .entry(pat) + .or_default() + .extend(adjustments.into_iter().map(|adjust| adjust.target)); + } + self.write_pat_ty(pat, coerced_ty); + return self.pat_ty_after_adjustment(pat); + } + Err(_) => { + self.result.type_mismatches.insert( + pat.into(), + TypeMismatch { + expected: expected.clone(), + actual: ty_inserted_vars.clone(), + }, + ); + self.write_pat_ty(pat, ty); + // We return `expected` to prevent cascading errors. I guess an alternative is to + // not emit type mismatches for error types and emit an error type here. + return expected; + } + } } Pat::Bind { id, subpat } => { return self.infer_bind_pat(pat, *id, default_bm, *subpat, &expected); @@ -361,7 +356,40 @@ impl InferenceContext<'_> { None => self.err_ty(), }, Pat::ConstBlock(expr) => { - self.infer_expr(*expr, &Expectation::has_type(expected.clone()), ExprIsRead::Yes) + let old_inside_assign = std::mem::replace(&mut self.inside_assignment, false); + let result = self.infer_expr( + *expr, + &Expectation::has_type(expected.clone()), + ExprIsRead::Yes, + ); + self.inside_assignment = old_inside_assign; + result + } + Pat::Expr(expr) => { + let old_inside_assign = std::mem::replace(&mut self.inside_assignment, false); + // LHS of assignment doesn't constitute reads. + let result = self.infer_expr_coerce( + *expr, + &Expectation::has_type(expected.clone()), + ExprIsRead::No, + ); + // We are returning early to avoid the unifiability check below. + let lhs_ty = self.insert_type_vars_shallow(result); + let ty = match self.coerce(None, &expected, &lhs_ty, CoerceNever::Yes) { + Ok(ty) => ty, + Err(_) => { + self.result.type_mismatches.insert( + pat.into(), + TypeMismatch { expected: expected.clone(), actual: lhs_ty.clone() }, + ); + // `rhs_ty` is returned so no further type mismatches are + // reported because of this mismatch. + expected + } + }; + self.write_pat_ty(pat, ty.clone()); + self.inside_assignment = old_inside_assign; + return ty; } Pat::Missing => self.err_ty(), }; @@ -517,9 +545,12 @@ impl InferenceContext<'_> { body[*expr], Expr::Literal(Literal::String(..) | Literal::CString(..) | Literal::ByteString(..)) ), - Pat::Wild | Pat::Bind { .. } | Pat::Ref { .. } | Pat::Box { .. } | Pat::Missing => { - false - } + Pat::Wild + | Pat::Bind { .. } + | Pat::Ref { .. } + | Pat::Box { .. } + | Pat::Missing + | Pat::Expr(_) => false, } } } |