about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--crates/hir-ty/src/infer/expr.rs34
-rw-r--r--crates/hir-ty/src/tests/coercion.rs44
-rw-r--r--crates/hir-ty/src/tests/simple.rs27
3 files changed, 102 insertions, 3 deletions
diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs
index 3cb7afef749..6dee0322a9b 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(_)) => {
@@ -891,7 +911,15 @@ impl<'a> InferenceContext<'a> {
                 let lhs_ty = self.insert_type_vars_shallow(lhs_ty);
                 let ty = match self.coerce(None, &rhs_ty, &lhs_ty) {
                     Ok(ty) => ty,
-                    Err(_) => self.err_ty(),
+                    Err(_) => {
+                        self.result.type_mismatches.insert(
+                            lhs.into(),
+                            TypeMismatch { expected: rhs_ty.clone(), actual: lhs_ty.clone() },
+                        );
+                        // `rhs_ty` is returned so no further type mismatches are
+                        // reported because of this mismatch.
+                        rhs_ty
+                    }
                 };
                 self.write_expr_ty(lhs, ty.clone());
                 return ty;
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: 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: 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>,)
+}
+        "#,
+    );
+}
diff --git a/crates/hir-ty/src/tests/simple.rs b/crates/hir-ty/src/tests/simple.rs
index 535b948371c..5b08f552109 100644
--- a/crates/hir-ty/src/tests/simple.rs
+++ b/crates/hir-ty/src/tests/simple.rs
@@ -3043,3 +3043,30 @@ fn main() {
         "#,
     );
 }
+
+#[test]
+fn destructuring_assignment_type_mismatch_on_identifier() {
+    check(
+        r#"
+struct S { v: i64 }
+struct TS(i64);
+fn main() {
+    let mut a: usize = 0;
+    (a,) = (0i64,);
+   //^expected i64, got usize
+
+    let mut a: usize = 0;
+    [a,] = [0i64,];
+   //^expected i64, got usize
+
+    let mut a: usize = 0;
+    S { v: a } = S { v: 0 };
+         //^expected i64, got usize
+
+    let mut a: usize = 0;
+    TS(a) = TS(0);
+     //^expected i64, got usize
+}
+        "#,
+    );
+}