Unnamed repository; edit this file 'description' to name the repository.
Rewrite tuple pattern inference to match rustc
It was subtly incorrect in how it handles the expected type.
Chayim Refael Friedman 5 months ago
parent d58ea7d · commit ddf4636
-rw-r--r--crates/hir-ty/src/infer.rs25
-rw-r--r--crates/hir-ty/src/infer/fallback.rs2
-rw-r--r--crates/hir-ty/src/infer/op.rs2
-rw-r--r--crates/hir-ty/src/infer/opaques.rs2
-rw-r--r--crates/hir-ty/src/infer/pat.rs79
-rw-r--r--crates/hir-ty/src/tests/coercion.rs4
6 files changed, 73 insertions, 41 deletions
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs
index 37060bd4b1..02b8ab8cdd 100644
--- a/crates/hir-ty/src/infer.rs
+++ b/crates/hir-ty/src/infer.rs
@@ -759,7 +759,6 @@ struct InternedStandardTypes<'db> {
re_erased: Region<'db>,
empty_args: GenericArgs<'db>,
- empty_tys: Tys<'db>,
}
impl<'db> InternedStandardTypes<'db> {
@@ -795,7 +794,6 @@ impl<'db> InternedStandardTypes<'db> {
re_erased: Region::new_erased(interner),
empty_args: GenericArgs::new_from_iter(interner, []),
- empty_tys: Tys::new_from_iter(interner, []),
}
}
}
@@ -1475,15 +1473,30 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
self.resolve_associated_type_with_params(inner_ty, assoc_ty, &[])
}
- fn demand_eqtype(&mut self, expected: Ty<'db>, actual: Ty<'db>) {
+ fn demand_eqtype(
+ &mut self,
+ id: ExprOrPatId,
+ expected: Ty<'db>,
+ actual: Ty<'db>,
+ ) -> Result<(), ()> {
+ let result = self.demand_eqtype_fixme_no_diag(expected, actual);
+ if result.is_err() {
+ self.result.type_mismatches.insert(id, TypeMismatch { expected, actual });
+ }
+ result
+ }
+
+ fn demand_eqtype_fixme_no_diag(
+ &mut self,
+ expected: Ty<'db>,
+ actual: Ty<'db>,
+ ) -> Result<(), ()> {
let result = self
.table
.at(&ObligationCause::new())
.eq(expected, actual)
.map(|infer_ok| self.table.register_infer_ok(infer_ok));
- if let Err(_err) = result {
- // FIXME: Emit diagnostic.
- }
+ result.map_err(drop)
}
fn demand_suptype(&mut self, expected: Ty<'db>, actual: Ty<'db>) {
diff --git a/crates/hir-ty/src/infer/fallback.rs b/crates/hir-ty/src/infer/fallback.rs
index b1c9146cc8..d0ce8cba7a 100644
--- a/crates/hir-ty/src/infer/fallback.rs
+++ b/crates/hir-ty/src/infer/fallback.rs
@@ -160,7 +160,7 @@ impl<'db> InferenceContext<'_, 'db> {
};
debug!("fallback_if_possible(ty={:?}): defaulting to `{:?}`", ty, fallback);
- self.demand_eqtype(ty, fallback);
+ _ = self.demand_eqtype_fixme_no_diag(ty, fallback);
true
}
diff --git a/crates/hir-ty/src/infer/op.rs b/crates/hir-ty/src/infer/op.rs
index 57d49008fb..88319a8b1a 100644
--- a/crates/hir-ty/src/infer/op.rs
+++ b/crates/hir-ty/src/infer/op.rs
@@ -108,7 +108,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
{
let builtin_return_ty =
self.enforce_builtin_binop_types(lhs_ty, rhs_ty, category);
- self.demand_eqtype(builtin_return_ty, return_ty);
+ _ = self.demand_eqtype(expr.into(), builtin_return_ty, return_ty);
builtin_return_ty
} else {
return_ty
diff --git a/crates/hir-ty/src/infer/opaques.rs b/crates/hir-ty/src/infer/opaques.rs
index f7719f50ac..ba4b53a0d7 100644
--- a/crates/hir-ty/src/infer/opaques.rs
+++ b/crates/hir-ty/src/infer/opaques.rs
@@ -109,7 +109,7 @@ impl<'db> InferenceContext<'_, 'db> {
let expected =
EarlyBinder::bind(ty.ty).instantiate(interner, opaque_type_key.args);
- self.demand_eqtype(expected, hidden_type.ty);
+ _ = self.demand_eqtype_fixme_no_diag(expected, hidden_type.ty);
}
self.result.type_of_opaque.insert(def_id, ty.ty);
diff --git a/crates/hir-ty/src/infer/pat.rs b/crates/hir-ty/src/infer/pat.rs
index c0ca855617..b3cf94aef4 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,
@@ -19,7 +19,7 @@ use crate::{
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> {
@@ -183,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.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
@@ -272,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() {
@@ -356,7 +375,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),
),
)
diff --git a/crates/hir-ty/src/tests/coercion.rs b/crates/hir-ty/src/tests/coercion.rs
index 75800a038b..50aca16365 100644
--- a/crates/hir-ty/src/tests/coercion.rs
+++ b/crates/hir-ty/src/tests/coercion.rs
@@ -833,11 +833,11 @@ struct V<T> { t: T }
fn main() {
let a: V<&dyn Tr>;
(a,) = V { t: &S };
- //^^^^expected V<&'? S>, got (V<&'? (dyn Tr + 'static)>,)
+ //^^^^expected V<&'? S>, got (V<&'? (dyn Tr + '?)>,)
let mut a: V<&dyn Tr> = V { t: &S };
(a,) = V { t: &S };
- //^^^^expected V<&'? S>, got (V<&'? (dyn Tr + 'static)>,)
+ //^^^^expected V<&'? S>, got (V<&'? (dyn Tr + '?)>,)
}
"#,
);