From 9e729cca98b94c7e72f5e2b8b8fa4b17aba7621a Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Wed, 19 Nov 2025 03:12:20 +0200 Subject: [PATCH 1/3] The type after pattern adjustments is stored in the last adjustment, not the first --- crates/hir-ty/src/infer/pat.rs | 2 +- crates/hir-ty/src/tests/patterns.rs | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/crates/hir-ty/src/infer/pat.rs b/crates/hir-ty/src/infer/pat.rs index 257224b0693d..1f7465c7d9f4 100644 --- a/crates/hir-ty/src/infer/pat.rs +++ b/crates/hir-ty/src/infer/pat.rs @@ -416,7 +416,7 @@ impl<'db> InferenceContext<'_, 'db> { .result .pat_adjustments .get(&pat) - .and_then(|it| it.first()) + .and_then(|it| it.last()) .unwrap_or(&self.result.type_of_pat[pat]) } diff --git a/crates/hir-ty/src/tests/patterns.rs b/crates/hir-ty/src/tests/patterns.rs index aa1fff7642a5..5d81d52ec701 100644 --- a/crates/hir-ty/src/tests/patterns.rs +++ b/crates/hir-ty/src/tests/patterns.rs @@ -1259,3 +1259,22 @@ fn main() { "#, ); } + +#[test] +fn destructuring_assign_ref() { + check_no_mismatches( + r#" +struct Foo; + +fn foo() -> (&'static Foo, u32) { + (&Foo, 0) +} + +fn bar() { + let ext: &Foo; + let v; + (ext, v) = foo(); +} + "#, + ); +} From d58ea7d299be6cdba46ce589443a9c71d1e08d5a Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Wed, 19 Nov 2025 03:45:11 +0200 Subject: [PATCH 2/3] The type of a binding in a `Pat::Bind` is the expected type, not the inferred type of the pattern The inferred type is reconstructed with match ergonomics, e.g. matching against `(&&i32, &&i32)` could give `(i32, i32)`), but we of course cannot bind to that. --- crates/hir-ty/src/infer/pat.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/hir-ty/src/infer/pat.rs b/crates/hir-ty/src/infer/pat.rs index 1f7465c7d9f4..c0ca8556177c 100644 --- a/crates/hir-ty/src/infer/pat.rs +++ b/crates/hir-ty/src/infer/pat.rs @@ -469,9 +469,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); From ddf4636b8d9a87cbfbe7dd71f35da0d299a1f07d Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Wed, 19 Nov 2025 05:01:33 +0200 Subject: [PATCH 3/3] Rewrite tuple pattern inference to match rustc It was subtly incorrect in how it handles the expected type. --- crates/hir-ty/src/infer.rs | 25 ++++++--- crates/hir-ty/src/infer/fallback.rs | 2 +- crates/hir-ty/src/infer/op.rs | 2 +- crates/hir-ty/src/infer/opaques.rs | 2 +- crates/hir-ty/src/infer/pat.rs | 79 ++++++++++++++++++----------- crates/hir-ty/src/tests/coercion.rs | 4 +- 6 files changed, 73 insertions(+), 41 deletions(-) diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index 37060bd4b1fd..02b8ab8cdde6 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 b1c9146cc8b8..d0ce8cba7a88 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 57d49008fb75..88319a8b1ad4 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 f7719f50ac3e..ba4b53a0d794 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 c0ca8556177c..b3cf94aef4bd 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, - subs: &[PatId], + elements: &[PatId], decl: Option, ) -> 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 75800a038b86..50aca16365db 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 } 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 + '?)>,) } "#, );