From afdbd6cce247d057e1e4014d20b79bb2a2d4bfac Mon Sep 17 00:00:00 2001 From: Ryo Yoshida Date: Sun, 3 Jul 2022 01:57:23 +0900 Subject: [PATCH] fix: infer lhs first on ordinary assignment expressions --- crates/hir-ty/src/infer/expr.rs | 24 ++++++++++++++-- crates/hir-ty/src/tests/coercion.rs | 44 +++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index 3cb7afef749..08abf8a461b 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -593,8 +593,28 @@ impl<'a> InferenceContext<'a> { } Expr::BinaryOp { lhs, rhs, op } => match op { Some(BinaryOp::Assignment { op: None }) => { - let rhs_ty = self.infer_expr(*rhs, &Expectation::none()); - self.infer_assignee_expr(*lhs, &rhs_ty); + let lhs = *lhs; + let is_ordinary = match &self.body[lhs] { + Expr::Array(_) + | Expr::RecordLit { .. } + | Expr::Tuple { .. } + | Expr::Underscore => false, + Expr::Call { callee, .. } => !matches!(&self.body[*callee], Expr::Path(_)), + _ => true, + }; + + // In ordinary (non-destructuring) assignments, the type of + // `lhs` must be inferred first so that the ADT fields + // instantiations in RHS can be coerced to it. Note that this + // cannot happen in destructuring assignments because of how + // they are desugared. + if is_ordinary { + let lhs_ty = self.infer_expr(lhs, &Expectation::none()); + self.infer_expr_coerce(*rhs, &Expectation::has_type(lhs_ty)); + } else { + let rhs_ty = self.infer_expr(*rhs, &Expectation::none()); + self.infer_assignee_expr(lhs, &rhs_ty); + } self.result.standard_types.unit.clone() } Some(BinaryOp::LogicOp(_)) => { diff --git a/crates/hir-ty/src/tests/coercion.rs b/crates/hir-ty/src/tests/coercion.rs index 0e512ef5ec9..bf59fadc2c3 100644 --- a/crates/hir-ty/src/tests/coercion.rs +++ b/crates/hir-ty/src/tests/coercion.rs @@ -709,3 +709,47 @@ fn test() { "#, ); } + +#[test] +fn assign_coerce_struct_fields() { + check_no_mismatches( + r#" +//- minicore: coerce_unsized +struct S; +trait Tr {} +impl Tr for S {} +struct V { t: T } + +fn main() { + let a: V<&dyn Tr>; + a = V { t: &S }; + + let mut a: V<&dyn Tr> = V { t: &S }; + a = V { t: &S }; +} + "#, + ); +} + +#[test] +fn destructuring_assign_coerce_struct_fields() { + check( + r#" +//- minicore: coerce_unsized +struct S; +trait Tr {} +impl Tr for S {} +struct V { t: T } + +fn main() { + let a: V<&dyn Tr>; + (a,) = V { t: &S }; + //^^^^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>,) +} + "#, + ); +}