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.rs153
1 files changed, 95 insertions, 58 deletions
diff --git a/crates/hir-ty/src/infer/pat.rs b/crates/hir-ty/src/infer/pat.rs
index 8019844b5d..1b8ce5ceaf 100644
--- a/crates/hir-ty/src/infer/pat.rs
+++ b/crates/hir-ty/src/infer/pat.rs
@@ -1,6 +1,6 @@
//! Type inference for patterns.
-use std::iter::repeat_with;
+use std::{cmp, iter};
use hir_def::{
HasModule,
@@ -9,18 +9,17 @@ use hir_def::{
};
use hir_expand::name::Name;
use rustc_ast_ir::Mutability;
-use rustc_type_ir::inherent::{GenericArg as _, GenericArgs as _, IntoKind, SliceLike, Ty as _};
+use rustc_type_ir::inherent::{GenericArg as _, GenericArgs as _, IntoKind, Ty as _};
use stdx::TupleExt;
use crate::{
DeclContext, DeclOrigin, InferenceDiagnostic,
consteval::{self, try_const_usize, usize_const},
infer::{
- AllowTwoPhase, BindingMode, Expectation, InferenceContext, TypeMismatch,
- coerce::CoerceNever, expr::ExprIsRead,
+ AllowTwoPhase, BindingMode, Expectation, InferenceContext, TypeMismatch, expr::ExprIsRead,
},
lower::lower_mutability,
- next_solver::{GenericArgs, Ty, TyKind},
+ next_solver::{GenericArgs, Ty, TyKind, Tys, infer::traits::ObligationCause},
};
impl<'db> InferenceContext<'_, 'db> {
@@ -83,7 +82,7 @@ impl<'db> InferenceContext<'_, 'db> {
{
// FIXME(DIAGNOSE): private tuple field
}
- let f = field_types[local_id];
+ let f = field_types[local_id].get();
let expected_ty = match substs {
Some(substs) => f.instantiate(self.interner(), substs),
None => f.instantiate(self.interner(), &[]),
@@ -147,7 +146,7 @@ impl<'db> InferenceContext<'_, 'db> {
variant: def,
});
}
- let f = field_types[local_id];
+ let f = field_types[local_id].get();
let expected_ty = match substs {
Some(substs) => f.instantiate(self.interner(), substs),
None => f.instantiate(self.interner(), &[]),
@@ -184,42 +183,61 @@ impl<'db> InferenceContext<'_, 'db> {
/// Ellipses found in the original pattern or expression must be filtered out.
pub(super) fn infer_tuple_pat_like(
&mut self,
+ pat: PatId,
expected: Ty<'db>,
default_bm: BindingMode,
ellipsis: Option<u32>,
- subs: &[PatId],
+ elements: &[PatId],
decl: Option<DeclContext>,
) -> Ty<'db> {
- let expected = self.table.structurally_resolve_type(expected);
- let expectations = match expected.kind() {
- TyKind::Tuple(parameters) => parameters,
- _ => self.types.empty_tys,
- };
+ let mut expected_len = elements.len();
+ if ellipsis.is_some() {
+ // Require known type only when `..` is present.
+ if let TyKind::Tuple(tys) = self.table.structurally_resolve_type(expected).kind() {
+ expected_len = tys.len();
+ }
+ }
+ let max_len = cmp::max(expected_len, elements.len());
- let ((pre, post), n_uncovered_patterns) = match ellipsis {
- Some(idx) => {
- (subs.split_at(idx as usize), expectations.len().saturating_sub(subs.len()))
+ let element_tys_iter = (0..max_len).map(|_| self.table.next_ty_var());
+ let element_tys = Tys::new_from_iter(self.interner(), element_tys_iter);
+ let pat_ty = Ty::new(self.interner(), TyKind::Tuple(element_tys));
+ if self.demand_eqtype(pat.into(), expected, pat_ty).is_err()
+ && let TyKind::Tuple(expected) = expected.kind()
+ {
+ // Equate expected type with the infer vars, for better diagnostics.
+ for (expected, elem_ty) in iter::zip(expected, element_tys) {
+ _ = self
+ .table
+ .at(&ObligationCause::dummy())
+ .eq(expected, elem_ty)
+ .map(|infer_ok| self.table.register_infer_ok(infer_ok));
}
- None => ((subs, &[][..]), 0),
+ }
+ let (before_ellipsis, after_ellipsis) = match ellipsis {
+ Some(ellipsis) => {
+ let element_tys = element_tys.as_slice();
+ // Don't check patterns twice.
+ let from_end_start = cmp::max(
+ element_tys.len().saturating_sub(elements.len() - ellipsis as usize),
+ ellipsis as usize,
+ );
+ (
+ element_tys.get(..ellipsis as usize).unwrap_or(element_tys),
+ element_tys.get(from_end_start..).unwrap_or_default(),
+ )
+ }
+ None => (element_tys.as_slice(), &[][..]),
};
- let mut expectations_iter =
- expectations.iter().chain(repeat_with(|| self.table.next_ty_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 = self.infer_pat(*pat, *ty, default_bm, decl);
+ for (&elem, &elem_ty) in iter::zip(elements, before_ellipsis.iter().chain(after_ellipsis)) {
+ self.infer_pat(elem, elem_ty, default_bm, decl);
}
-
- // 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, decl);
+ if let Some(uncovered) = elements.get(element_tys.len()..) {
+ for &elem in uncovered {
+ self.infer_pat(elem, self.types.types.error, default_bm, decl);
+ }
}
-
- Ty::new_tup_from_iter(self.interner(), inner_tys.into_iter())
+ pat_ty
}
/// The resolver needs to be updated to the surrounding expression when inside assignment
@@ -252,7 +270,7 @@ impl<'db> InferenceContext<'_, 'db> {
} else if self.is_non_ref_pat(self.body, pat) {
let mut pat_adjustments = Vec::new();
while let TyKind::Ref(_lifetime, inner, mutability) = expected.kind() {
- pat_adjustments.push(expected);
+ pat_adjustments.push(expected.store());
expected = self.table.try_structurally_resolve_type(inner);
default_bm = match default_bm {
BindingMode::Move => BindingMode::Ref(mutability),
@@ -273,7 +291,7 @@ impl<'db> InferenceContext<'_, 'db> {
let ty = match &self.body[pat] {
Pat::Tuple { args, ellipsis } => {
- self.infer_tuple_pat_like(expected, default_bm, *ellipsis, args, decl)
+ self.infer_tuple_pat_like(pat, expected, default_bm, *ellipsis, args, decl)
}
Pat::Or(pats) => {
for pat in pats.iter() {
@@ -306,16 +324,19 @@ impl<'db> InferenceContext<'_, 'db> {
expected,
ty_inserted_vars,
AllowTwoPhase::No,
- CoerceNever::Yes,
+ ExprIsRead::No,
) {
Ok(coerced_ty) => {
self.write_pat_ty(pat, coerced_ty);
return self.pat_ty_after_adjustment(pat);
}
Err(_) => {
- self.result.type_mismatches.insert(
+ self.result.type_mismatches.get_or_insert_default().insert(
pat.into(),
- TypeMismatch { expected, actual: ty_inserted_vars },
+ TypeMismatch {
+ expected: expected.store(),
+ actual: ty_inserted_vars.store(),
+ },
);
self.write_pat_ty(pat, ty);
// We return `expected` to prevent cascading errors. I guess an alternative is to
@@ -331,8 +352,15 @@ impl<'db> InferenceContext<'_, 'db> {
self.infer_slice_pat(expected, prefix, *slice, suffix, default_bm, decl)
}
Pat::Wild => expected,
- Pat::Range { .. } => {
- // FIXME: do some checks here.
+ Pat::Range { start, end, range_type: _ } => {
+ if let Some(start) = *start {
+ let start_ty = self.infer_expr(start, &Expectation::None, ExprIsRead::Yes);
+ _ = self.demand_eqtype(start.into(), expected, start_ty);
+ }
+ if let Some(end) = *end {
+ let end_ty = self.infer_expr(end, &Expectation::None, ExprIsRead::Yes);
+ _ = self.demand_eqtype(end.into(), expected, end_ty);
+ }
expected
}
&Pat::Lit(expr) => {
@@ -347,7 +375,7 @@ impl<'db> InferenceContext<'_, 'db> {
Some((adt, subst)) if adt == box_adt => {
(subst.type_at(0), subst.as_slice().get(1).and_then(|a| a.as_type()))
}
- _ => (self.types.error, None),
+ _ => (self.types.types.error, None),
};
let inner_ty = self.infer_pat(*inner, inner_ty, default_bm, decl);
@@ -357,7 +385,7 @@ impl<'db> InferenceContext<'_, 'db> {
GenericArgs::fill_with_defaults(
self.interner(),
box_adt.into(),
- std::iter::once(inner_ty.into()).chain(alloc_ty.map(Into::into)),
+ iter::once(inner_ty.into()).chain(alloc_ty.map(Into::into)),
|_, id, _| self.table.next_var_for_param(id),
),
)
@@ -374,22 +402,24 @@ impl<'db> InferenceContext<'_, 'db> {
Pat::Expr(expr) => {
let old_inside_assign = std::mem::replace(&mut self.inside_assignment, false);
// LHS of assignment doesn't constitute reads.
+ let expr_is_read = ExprIsRead::No;
let result =
- self.infer_expr_coerce(*expr, &Expectation::has_type(expected), ExprIsRead::No);
+ self.infer_expr_coerce(*expr, &Expectation::has_type(expected), expr_is_read);
// We are returning early to avoid the unifiability check below.
let lhs_ty = self.insert_type_vars_shallow(result);
let ty = match self.coerce(
- pat.into(),
+ (*expr).into(),
expected,
lhs_ty,
AllowTwoPhase::No,
- CoerceNever::Yes,
+ expr_is_read,
) {
Ok(ty) => ty,
Err(_) => {
- self.result
- .type_mismatches
- .insert(pat.into(), TypeMismatch { expected, actual: lhs_ty });
+ self.result.type_mismatches.get_or_insert_default().insert(
+ pat.into(),
+ TypeMismatch { expected: expected.store(), actual: lhs_ty.store() },
+ );
// `rhs_ty` is returned so no further type mismatches are
// reported because of this mismatch.
expected
@@ -405,19 +435,22 @@ impl<'db> InferenceContext<'_, 'db> {
let ty = self.insert_type_vars_shallow(ty);
// FIXME: This never check is odd, but required with out we do inference right now
if !expected.is_never() && !self.unify(ty, expected) {
- self.result.type_mismatches.insert(pat.into(), TypeMismatch { expected, actual: ty });
+ self.result.type_mismatches.get_or_insert_default().insert(
+ pat.into(),
+ TypeMismatch { expected: expected.store(), actual: ty.store() },
+ );
}
self.write_pat_ty(pat, ty);
self.pat_ty_after_adjustment(pat)
}
fn pat_ty_after_adjustment(&self, pat: PatId) -> Ty<'db> {
- *self
- .result
+ self.result
.pat_adjustments
.get(&pat)
- .and_then(|it| it.first())
- .unwrap_or(&self.result.type_of_pat[pat])
+ .and_then(|it| it.last())
+ .unwrap_or_else(|| &self.result.type_of_pat[pat])
+ .as_ref()
}
fn infer_ref_pat(
@@ -469,9 +502,9 @@ impl<'db> InferenceContext<'_, 'db> {
let bound_ty = match mode {
BindingMode::Ref(mutability) => {
let inner_lt = self.table.next_region_var();
- Ty::new_ref(self.interner(), inner_lt, inner_ty, mutability)
+ Ty::new_ref(self.interner(), inner_lt, expected, mutability)
}
- BindingMode::Move => inner_ty,
+ BindingMode::Move => expected,
};
self.write_pat_ty(pat, inner_ty);
self.write_binding_ty(binding, bound_ty);
@@ -541,10 +574,14 @@ impl<'db> InferenceContext<'_, 'db> {
{
let inner = self.table.try_structurally_resolve_type(inner);
if matches!(inner.kind(), TyKind::Slice(_)) {
- let elem_ty = self.types.u8;
+ let elem_ty = self.types.types.u8;
let slice_ty = Ty::new_slice(self.interner(), elem_ty);
- let ty =
- Ty::new_ref(self.interner(), self.types.re_static, slice_ty, Mutability::Not);
+ let ty = Ty::new_ref(
+ self.interner(),
+ self.types.regions.statik,
+ slice_ty,
+ Mutability::Not,
+ );
self.write_expr_ty(expr, ty);
return ty;
}