Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 19 additions & 6 deletions crates/hir-ty/src/infer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -759,7 +759,6 @@ struct InternedStandardTypes<'db> {
re_erased: Region<'db>,

empty_args: GenericArgs<'db>,
empty_tys: Tys<'db>,
}

impl<'db> InternedStandardTypes<'db> {
Expand Down Expand Up @@ -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, []),
}
}
}
Expand Down Expand Up @@ -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>) {
Expand Down
2 changes: 1 addition & 1 deletion crates/hir-ty/src/infer/fallback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down
2 changes: 1 addition & 1 deletion crates/hir-ty/src/infer/op.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion crates/hir-ty/src/infer/opaques.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
85 changes: 52 additions & 33 deletions crates/hir-ty/src/infer/pat.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Type inference for patterns.

use std::iter::repeat_with;
use std::{cmp, iter};

use hir_def::{
HasModule,
Expand All @@ -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> {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -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),
),
)
Expand Down Expand Up @@ -416,7 +435,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])
}

Expand Down Expand Up @@ -469,9 +488,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);
Expand Down
4 changes: 2 additions & 2 deletions crates/hir-ty/src/tests/coercion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 + '?)>,)
}
"#,
);
Expand Down
19 changes: 19 additions & 0 deletions crates/hir-ty/src/tests/patterns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
"#,
);
}