Repository: rust-analyzer/rust-analyzer
Pull request #1823 (2019-09-11 23:25:02)

Support type coercion

matklad: @uHOOCCOOHu are you on Zulip? We are having a weekly sync-up in https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Fwg-rls-2.2E0/topic/weekly.20sync-up, you might be interested in joining that
uHOOCCOOHu: I dig a little about how `!` type work with coercion in branches in rustc, and it seems quite weird to me. I've added them into test cases.

Hope my implementation make sense. Well, at least it works like rustc now, about the `!` type.

uHOOCCOOHu: @flodiebold 
> I'd rather improve calculate_least_upper_bound and implement a proper CoerceMany similar to rustc.

I've not read the rustc source and don't know how it is designed. But I found this code does not compile: (The order matters. Moving the last arm up will make it compile well)
```rust
fn main() {
    let t = &mut 1;
    match 1 {
        1 => t as *mut i32,
        2 => t as &i32,
        _ => t as *const i32,
    };
}
```
`*const i32` is a "least upper bound" here, but it cannot auto-coerce first two arms into it.
So I think it may just mutual coerce arm by arm, and that's why I also do so.



flodiebold: (Also, after this PR, we should extract the coercion code either to `unify.rs` or to its own module. `infer.rs` is far too big.)
flodiebold: @uHOOCCOOHu I think we should merge this before it gets even bigger :) We can refactor it further afterwards (in fact, I really want to split `infer.rs` further, but refrained from it so far). Let's rebase this and merge. (The PR title still says WIP, but I assume you just forgot to change it.)

bors delegate+

I don't really see any blocking reason why we shouldn't be able to do the `CoerceUnsized` handling with Chalk though. We would need to provide the impls for `Unsize`, but that should be doable similar to the way we now provide impls for the `Fn` traits.
bors[bot]: :v: uHOOCCOOHu can now approve this pull request. To approve and merge a pull request, simply reply with `bors r+`. More detailed instructions are available [here](https://bors.tech/documentation/getting-started/#reviewing-pull-requests).
uHOOCCOOHu: @flodiebold It is WIP because I'm planning to support coercion in all coercion is now only implemented in block expression of `if` and `match` and function arguments. And I think other places like struct fields would be easy to implement.

For `Unsize`, I just cannot figure out how to pass it to Chalk before. You're right, and maybe we can refactor this part in another PR.


flodiebold: There's no reason you need to do that in this PR, though. Let's merge this and do further improvements in the next one :)

Patch:
diff --git a/crates/ra_hir/src/marks.rs b/crates/ra_hir/src/marks.rs
index fe119b97c5..b2111be053 100644
--- a/crates/ra_hir/src/marks.rs
+++ b/crates/ra_hir/src/marks.rs
@@ -13,4 +13,5 @@ test_utils::marks!(
     infer_while_let
     macro_rules_from_other_crates_are_visible_with_macro_use
     prelude_is_macro_use
+    coerce_merge_fail_fallback
 );
diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs
index 76b4b6faa4..db33773572 100644
--- a/crates/ra_hir/src/ty/infer.rs
+++ b/crates/ra_hir/src/ty/infer.rs
@@ -14,7 +14,7 @@
 //! the `ena` crate, which is extracted from rustc.
 
 use std::borrow::Cow;
-use std::iter::repeat;
+use std::iter::{repeat, repeat_with};
 use std::mem;
 use std::ops::Index;
 use std::sync::Arc;
@@ -42,6 +42,7 @@ use crate::{
         RecordFieldPat, Statement, UnaryOp,
     },
     generics::{GenericParams, HasGenericParams},
+    lang_item::LangItemTarget,
     name,
     nameres::Namespace,
     path::{known, GenericArg, GenericArgs},
@@ -188,6 +189,21 @@ struct InferenceContext<'a, D: HirDatabase> {
     result: InferenceResult,
     /// The return type of the function being inferred.
     return_ty: Ty,
+
+    /// Impls of `CoerceUnsized` used in coercion.
+    /// (from_ty_ctor, to_ty_ctor) => coerce_generic_index
+    // FIXME: Use trait solver for this.
+    // Chalk seems unable to work well with builtin impl of `Unsize` now.
+    coerce_unsized_map: FxHashMap<(TypeCtor, TypeCtor), usize>,
+}
+
+macro_rules! ty_app {
+    ($ctor:pat, $param:pat) => {
+        Ty::Apply(ApplicationTy { ctor: $ctor, parameters: $param })
+    };
+    ($ctor:pat) => {
+        ty_app!($ctor, _)
+    };
 }
 
 impl<'a, D: HirDatabase> InferenceContext<'a, D> {
@@ -198,12 +214,52 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
             obligations: Vec::default(),
             return_ty: Ty::Unknown, // set in collect_fn_signature
             trait_env: lower::trait_env(db, &resolver),
+            coerce_unsized_map: Self::init_coerce_unsized_map(db, &resolver),
             db,
             body,
             resolver,
         }
     }
 
+    fn init_coerce_unsized_map(
+        db: &'a D,
+        resolver: &Resolver,
+    ) -> FxHashMap<(TypeCtor, TypeCtor), usize> {
+        let krate = resolver.krate().unwrap();
+        let impls = match db.lang_item(krate, "coerce_unsized".into()) {
+            Some(LangItemTarget::Trait(trait_)) => db.impls_for_trait(krate, trait_),
+            _ => return FxHashMap::default(),
+        };
+
+        impls
+            .iter()
+            .filter_map(|impl_block| {
+                // `CoerseUnsized` has one generic parameter for the target type.
+                let trait_ref = impl_block.target_trait_ref(db)?;
+                let cur_from_ty = trait_ref.substs.0.get(0)?;
+                let cur_to_ty = trait_ref.substs.0.get(1)?;
+
+                match (&cur_from_ty, cur_to_ty) {
+                    (ty_app!(ctor1, st1), ty_app!(ctor2, st2)) => {
+                        // FIXME: We return the first non-equal bound as the type parameter to coerce to unsized type.
+                        // This works for smart-pointer-like coercion, which covers all impls from std.
+                        st1.iter().zip(st2.iter()).enumerate().find_map(|(i, (ty1, ty2))| {
+                            match (ty1, ty2) {
+                                (Ty::Param { idx: p1, .. }, Ty::Param { idx: p2, .. })
+                                    if p1 != p2 =>
+                                {
+                                    Some(((*ctor1, *ctor2), i))
+                                }
+                                _ => None,
+                            }
+                        })
+                    }
+                    _ => None,
+                }
+            })
+            .collect()
+    }
+
     fn resolve_all(mut self) -> InferenceResult {
         // FIXME resolve obligations as well (use Guidance if necessary)
         let mut result = mem::replace(&mut self.result, InferenceResult::default());
@@ -278,27 +334,45 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
         let ty1 = self.resolve_ty_shallow(ty1);
         let ty2 = self.resolve_ty_shallow(ty2);
         match (&*ty1, &*ty2) {
-            (Ty::Unknown, _) | (_, Ty::Unknown) => true,
             (Ty::Apply(a_ty1), Ty::Apply(a_ty2)) if a_ty1.ctor == a_ty2.ctor => {
                 self.unify_substs(&a_ty1.parameters, &a_ty2.parameters, depth + 1)
             }
+            _ => self.unify_inner_trivial(&ty1, &ty2),
+        }
+    }
+
+    fn unify_inner_trivial(&mut self, ty1: &Ty, ty2: &Ty) -> bool {
+        match (ty1, ty2) {
+            (Ty::Unknown, _) | (_, Ty::Unknown) => true,
+
             (Ty::Infer(InferTy::TypeVar(tv1)), Ty::Infer(InferTy::TypeVar(tv2)))
             | (Ty::Infer(InferTy::IntVar(tv1)), Ty::Infer(InferTy::IntVar(tv2)))
-            | (Ty::Infer(InferTy::FloatVar(tv1)), Ty::Infer(InferTy::FloatVar(tv2))) => {
+            | (Ty::Infer(InferTy::FloatVar(tv1)), Ty::Infer(InferTy::FloatVar(tv2)))
+            | (
+                Ty::Infer(InferTy::MaybeNeverTypeVar(tv1)),
+                Ty::Infer(InferTy::MaybeNeverTypeVar(tv2)),
+            ) => {
                 // both type vars are unknown since we tried to resolve them
                 self.var_unification_table.union(*tv1, *tv2);
                 true
             }
+
+            // The order of MaybeNeverTypeVar matters here.
+            // Unifying MaybeNeverTypeVar and TypeVar will let the latter become MaybeNeverTypeVar.
+            // Unifying MaybeNeverTypeVar and other concrete type will let the former become it.
             (Ty::Infer(InferTy::TypeVar(tv)), other)
             | (other, Ty::Infer(InferTy::TypeVar(tv)))
-            | (Ty::Infer(InferTy::IntVar(tv)), other)
-            | (other, Ty::Infer(InferTy::IntVar(tv)))
-            | (Ty::Infer(InferTy::FloatVar(tv)), other)
-            | (other, Ty::Infer(InferTy::FloatVar(tv))) => {
+            | (Ty::Infer(InferTy::MaybeNeverTypeVar(tv)), other)
+            | (other, Ty::Infer(InferTy::MaybeNeverTypeVar(tv)))
+            | (Ty::Infer(InferTy::IntVar(tv)), other @ ty_app!(TypeCtor::Int(_)))
+            | (other @ ty_app!(TypeCtor::Int(_)), Ty::Infer(InferTy::IntVar(tv)))
+            | (Ty::Infer(InferTy::FloatVar(tv)), other @ ty_app!(TypeCtor::Float(_)))
+            | (other @ ty_app!(TypeCtor::Float(_)), Ty::Infer(InferTy::FloatVar(tv))) => {
                 // the type var is unknown since we tried to resolve it
                 self.var_unification_table.union_value(*tv, TypeVarValue::Known(other.clone()));
                 true
             }
+
             _ => false,
         }
     }
@@ -315,6 +389,12 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
         Ty::Infer(InferTy::FloatVar(self.var_unification_table.new_key(TypeVarValue::Unknown)))
     }
 
+    fn new_maybe_never_type_var(&mut self) -> Ty {
+        Ty::Infer(InferTy::MaybeNeverTypeVar(
+            self.var_unification_table.new_key(TypeVarValue::Unknown),
+        ))
+    }
+
     /// Replaces Ty::Unknown by a new type var, so we can maybe still infer it.
     fn insert_type_vars_shallow(&mut self, ty: Ty) -> Ty {
         match ty {
@@ -795,17 +875,304 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
         ret_ty
     }
 
-    /// This is similar to unify, but it makes the first type coerce to the
-    /// second one.
-    fn coerce(&mut self, from_ty: &Ty, to_ty: &Ty) -> bool {
-        if is_never(from_ty) {
-            // ! coerces to any type
-            true
+    /// Infer type of expression with possibly implicit coerce to the expected type.
+    /// Return the type after possible coercion.
+    fn infer_expr_coerce(&mut self, expr: ExprId, expected: &Expectation) -> Ty {
+        let ty = self.infer_expr_inner(expr, &expected);
+        let ty = if !self.coerce(&ty, &expected.ty) {
+            self.result
+                .type_mismatches
+                .insert(expr, TypeMismatch { expected: expected.ty.clone(), actual: ty.clone() });
+            // Return actual type when type mismatch.
+            // This is needed for diagnostic when return type mismatch.
+            ty
+        } else if expected.ty == Ty::Unknown {
+            ty
         } else {
-            self.unify(from_ty, to_ty)
+            expected.ty.clone()
+        };
+
+        self.resolve_ty_as_possible(&mut vec![], ty)
+    }
+
+    /// Merge two types from different branches, with possible implicit coerce.
+    ///
+    /// Note that it is only possible that one type are coerced to another.
+    /// Coercing both types to another least upper bound type is not possible in rustc,
+    /// which will simply result in "incompatible types" error.
+    fn coerce_merge_branch<'t>(&mut self, ty1: &Ty, ty2: &Ty) -> Ty {
+        if self.coerce(ty1, ty2) {
+            ty2.clone()
+        } else if self.coerce(ty2, ty1) {
+            ty1.clone()
+        } else {
+            tested_by!(coerce_merge_fail_fallback);
+            // For incompatible types, we use the latter one as result
+            // to be better recovery for `if` without `else`.
+            ty2.clone()
+        }
+    }
+
+    /// Unify two types, but may coerce the first one to the second one
+    /// using "implicit coercion rules" if needed.
+    ///
+    /// See: https://doc.rust-lang.org/nomicon/coercions.html
+    fn coerce(&mut self, from_ty: &Ty, to_ty: &Ty) -> bool {
+        let from_ty = self.resolve_ty_shallow(from_ty).into_owned();
+        let to_ty = self.resolve_ty_shallow(to_ty);
+        self.coerce_inner(from_ty, &to_ty)
+    }
+
+    fn coerce_inner(&mut self, mut from_ty: Ty, to_ty: &Ty) -> bool {
+        match (&from_ty, to_ty) {
+            // Never type will make type variable to fallback to Never Type instead of Unknown.
+            (ty_app!(TypeCtor::Never), Ty::Infer(InferTy::TypeVar(tv))) => {
+                let var = self.new_maybe_never_type_var();
+                self.var_unification_table.union_value(*tv, TypeVarValue::Known(var));
+                return true;
+            }
+            (ty_app!(TypeCtor::Never), _) => return true,
+
+            // Trivial cases, this should go after `never` check to
+            // avoid infer result type to be never
+            _ => {
+                if self.unify_inner_trivial(&from_ty, &to_ty) {
+                    return true;
+                }
+            }
+        }
+
+        // Pointer weakening and function to pointer
+        match (&mut from_ty, to_ty) {
+            // `*mut T`, `&mut T, `&T`` -> `*const T`
+            // `&mut T` -> `&T`
+            // `&mut T` -> `*mut T`
+            (ty_app!(c1@TypeCtor::RawPtr(_)), ty_app!(c2@TypeCtor::RawPtr(Mutability::Shared)))
+            | (ty_app!(c1@TypeCtor::Ref(_)), ty_app!(c2@TypeCtor::RawPtr(Mutability::Shared)))
+            | (ty_app!(c1@TypeCtor::Ref(_)), ty_app!(c2@TypeCtor::Ref(Mutability::Shared)))
+            | (ty_app!(c1@TypeCtor::Ref(Mutability::Mut)), ty_app!(c2@TypeCtor::RawPtr(_))) => {
+                *c1 = *c2;
+            }
+
+            // Illegal mutablity conversion
+            (
+                ty_app!(TypeCtor::RawPtr(Mutability::Shared)),
+                ty_app!(TypeCtor::RawPtr(Mutability::Mut)),
+            )
+            | (
+                ty_app!(TypeCtor::Ref(Mutability::Shared)),
+                ty_app!(TypeCtor::Ref(Mutability::Mut)),
+            ) => return false,
+
+            // `{function_type}` -> `fn()`
+            (ty_app!(TypeCtor::FnDef(_)), ty_app!(TypeCtor::FnPtr { .. })) => {
+                match from_ty.callable_sig(self.db) {
+                    None => return false,
+                    Some(sig) => {
+                        let num_args = sig.params_and_return.len() as u16 - 1;
+                        from_ty =
+                            Ty::apply(TypeCtor::FnPtr { num_args }, Substs(sig.params_and_return));
+                    }
+                }
+            }
+
+            _ => {}
+        }
+
+        if let Some(ret) = self.try_coerce_unsized(&from_ty, &to_ty) {
+            return ret;
+        }
+
+        // Auto Deref if cannot coerce
+        match (&from_ty, to_ty) {
+            // FIXME: DerefMut
+            (ty_app!(TypeCtor::Ref(_), st1), ty_app!(TypeCtor::Ref(_), st2)) => {
+                self.unify_autoderef_behind_ref(&st1[0], &st2[0])
+            }
+
+            // Otherwise, normal unify
+            _ => self.unify(&from_ty, to_ty),
+        }
+    }
+
+    /// Coerce a type using `from_ty: CoerceUnsized<ty_ty>`
+    ///
+    /// See: https://doc.rust-lang.org/nightly/std/marker/trait.CoerceUnsized.html
+    fn try_coerce_unsized(&mut self, from_ty: &Ty, to_ty: &Ty) -> Option<bool> {
+        let (ctor1, st1, ctor2, st2) = match (from_ty, to_ty) {
+            (ty_app!(ctor1, st1), ty_app!(ctor2, st2)) => (ctor1, st1, ctor2, st2),
+            _ => return None,
+        };
+
+        let coerce_generic_index = *self.coerce_unsized_map.get(&(*ctor1, *ctor2))?;
+
+        // Check `Unsize` first
+        match self.check_unsize_and_coerce(
+            st1.0.get(coerce_generic_index)?,
+            st2.0.get(coerce_generic_index)?,
+            0,
+        ) {
+            Some(true) => {}
+            ret => return ret,
+        }
+
+        let ret = st1
+            .iter()
+            .zip(st2.iter())
+            .enumerate()
+            .filter(|&(idx, _)| idx != coerce_generic_index)
+            .all(|(_, (ty1, ty2))| self.unify(ty1, ty2));
+
+        Some(ret)
+    }
+
+    /// Check if `from_ty: Unsize<to_ty>`, and coerce to `to_ty` if it holds.
+    ///
+    /// It should not be directly called. It is only used by `try_coerce_unsized`.
+    ///
+    /// See: https://doc.rust-lang.org/nightly/std/marker/trait.Unsize.html
+    fn check_unsize_and_coerce(&mut self, from_ty: &Ty, to_ty: &Ty, depth: usize) -> Option<bool> {
+        if depth > 1000 {
+            panic!("Infinite recursion in coercion");
+        }
+
+        match (&from_ty, &to_ty) {
+            // `[T; N]` -> `[T]`
+            (ty_app!(TypeCtor::Array, st1), ty_app!(TypeCtor::Slice, st2)) => {
+                Some(self.unify(&st1[0], &st2[0]))
+            }
+
+            // `T` -> `dyn Trait` when `T: Trait`
+            (_, Ty::Dyn(_)) => {
+                // FIXME: Check predicates
+                Some(true)
+            }
+
+            // `(..., T)` -> `(..., U)` when `T: Unsize<U>`
+            (
+                ty_app!(TypeCtor::Tuple { cardinality: len1 }, st1),
+                ty_app!(TypeCtor::Tuple { cardinality: len2 }, st2),
+            ) => {
+                if len1 != len2 || *len1 == 0 {
+                    return None;
+                }
+
+                match self.check_unsize_and_coerce(
+                    st1.last().unwrap(),
+                    st2.last().unwrap(),
+                    depth + 1,
+                ) {
+                    Some(true) => {}
+                    ret => return ret,
+                }
+
+                let ret = st1[..st1.len() - 1]
+                    .iter()
+                    .zip(&st2[..st2.len() - 1])
+                    .all(|(ty1, ty2)| self.unify(ty1, ty2));
+
+                Some(ret)
+            }
+
+            // Foo<..., T, ...> is Unsize<Foo<..., U, ...>> if:
+            // - T: Unsize<U>
+            // - Foo is a struct
+            // - Only the last field of Foo has a type involving T
+            // - T is not part of the type of any other fields
+            // - Bar<T>: Unsize<Bar<U>>, if the last field of Foo has type Bar<T>
+            (
+                ty_app!(TypeCtor::Adt(Adt::Struct(struct1)), st1),
+                ty_app!(TypeCtor::Adt(Adt::Struct(struct2)), st2),
+            ) if struct1 == struct2 => {
+                let fields = struct1.fields(self.db);
+                let (last_field, prev_fields) = fields.split_last()?;
+
+                // Get the generic parameter involved in the last field.
+                let unsize_generic_index = {
+                    let mut index = None;
+                    let mut multiple_param = false;
+                    last_field.ty(self.db).walk(&mut |ty| match ty {
+                        &Ty::Param { idx, .. } => {
+                            if index.is_none() {
+                                index = Some(idx);
+                            } else if Some(idx) != index {
+                                multiple_param = true;
+                            }
+                        }
+                        _ => {}
+                    });
+
+                    if multiple_param {
+                        return None;
+                    }
+                    index?
+                };
+
+                // Check other fields do not involve it.
+                let mut multiple_used = false;
+                prev_fields.iter().for_each(|field| {
+                    field.ty(self.db).walk(&mut |ty| match ty {
+                        &Ty::Param { idx, .. } if idx == unsize_generic_index => {
+                            multiple_used = true
+                        }
+                        _ => {}
+                    })
+                });
+                if multiple_used {
+                    return None;
+                }
+
+                let unsize_generic_index = unsize_generic_index as usize;
+
+                // Check `Unsize` first
+                match self.check_unsize_and_coerce(
+                    st1.get(unsize_generic_index)?,
+                    st2.get(unsize_generic_index)?,
+                    depth + 1,
+                ) {
+                    Some(true) => {}
+                    ret => return ret,
+                }
+
+                // Then unify other parameters
+                let ret = st1
+                    .iter()
+                    .zip(st2.iter())
+                    .enumerate()
+                    .filter(|&(idx, _)| idx != unsize_generic_index)
+                    .all(|(_, (ty1, ty2))| self.unify(ty1, ty2));
+
+                Some(ret)
+            }
+
+            _ => None,
         }
     }
 
+    /// Unify `from_ty` to `to_ty` with optional auto Deref
+    ///
+    /// Note that the parameters are already stripped the outer reference.
+    fn unify_autoderef_behind_ref(&mut self, from_ty: &Ty, to_ty: &Ty) -> bool {
+        let canonicalized = self.canonicalizer().canonicalize_ty(from_ty.clone());
+        let to_ty = self.resolve_ty_shallow(&to_ty);
+        // FIXME: Auto DerefMut
+        for derefed_ty in
+            autoderef::autoderef(self.db, &self.resolver.clone(), canonicalized.value.clone())
+        {
+            let derefed_ty = canonicalized.decanonicalize_ty(derefed_ty.value);
+            match (&*self.resolve_ty_shallow(&derefed_ty), &*to_ty) {
+                // Stop when constructor matches.
+                (ty_app!(from_ctor, st1), ty_app!(to_ctor, st2)) if from_ctor == to_ctor => {
+                    // It will not recurse to `coerce`.
+                    return self.unify_substs(st1, st2, 0);
+                }
+                _ => {}
+            }
+        }
+
+        false
+    }
+
     fn infer_expr(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
         let ty = self.infer_expr_inner(tgt_expr, expected);
         let could_unify = self.unify(&ty, &expected.ty);
@@ -828,15 +1195,12 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
                 self.infer_expr(*condition, &Expectation::has_type(Ty::simple(TypeCtor::Bool)));
 
                 let then_ty = self.infer_expr_inner(*then_branch, &expected);
-                self.coerce(&then_ty, &expected.ty);
-
                 let else_ty = match else_branch {
                     Some(else_branch) => self.infer_expr_inner(*else_branch, &expected),
                     None => Ty::unit(),
                 };
-                self.coerce(&else_ty, &expected.ty);
 
-                expected.ty.clone()
+                self.coerce_merge_branch(&then_ty, &else_ty)
             }
             Expr::Block { statements, tail } => self.infer_block(statements, *tail, expected),
             Expr::TryBlock { body } => {
@@ -932,13 +1296,8 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
                 .infer_method_call(tgt_expr, *receiver, &args, &method_name, generic_args.as_ref()),
             Expr::Match { expr, arms } => {
                 let input_ty = self.infer_expr(*expr, &Expectation::none());
-                let expected = if expected.ty == Ty::Unknown {
-                    Expectation::has_type(self.new_type_var())
-                } else {
-                    expected.clone()
-                };
 
-                let mut arm_tys = Vec::with_capacity(arms.len());
+                let mut result_ty = self.new_maybe_never_type_var();
 
                 for arm in arms {
                     for &pat in &arm.pats {
@@ -950,16 +1309,12 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
                             &Expectation::has_type(Ty::simple(TypeCtor::Bool)),
                         );
                     }
-                    arm_tys.push(self.infer_expr_inner(arm.expr, &expected));
-                }
-
-                let lub_ty = calculate_least_upper_bound(expected.ty, &arm_tys);
 
-                for arm_ty in &arm_tys {
-                    self.coerce(arm_ty, &lub_ty);
+                    let arm_ty = self.infer_expr_inner(arm.expr, &expected);
+                    result_ty = self.coerce_merge_branch(&result_ty, &arm_ty);
                 }
 
-                lub_ty
+                result_ty
             }
             Expr::Path(p) => {
                 // FIXME this could be more efficient...
@@ -986,6 +1341,8 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
                     self.write_variant_resolution(tgt_expr.into(), variant);
                 }
 
+                self.unify(&ty, &expected.ty);
+
                 let substs = ty.substs().unwrap_or_else(Substs::empty);
                 for (field_idx, field) in fields.iter().enumerate() {
                     let field_ty = def_id
@@ -1001,7 +1358,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
                         })
                         .map_or(Ty::Unknown, |field| field.ty(self.db))
                         .subst(&substs);
-                    self.infer_expr(field.expr, &Expectation::has_type(field_ty));
+                    self.infer_expr_coerce(field.expr, &Expectation::has_type(field_ty));
                 }
                 if let Some(expr) = spread {
                     self.infer_expr(*expr, &Expectation::has_type(ty.clone()));
@@ -1171,35 +1528,41 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
                 Ty::Unknown
             }
             Expr::Tuple { exprs } => {
-                let mut ty_vec = Vec::with_capacity(exprs.len());
-                for arg in exprs.iter() {
-                    ty_vec.push(self.infer_expr(*arg, &Expectation::none()));
+                let mut tys = match &expected.ty {
+                    ty_app!(TypeCtor::Tuple { .. }, st) => st
+                        .iter()
+                        .cloned()
+                        .chain(repeat_with(|| self.new_type_var()))
+                        .take(exprs.len())
+                        .collect::<Vec<_>>(),
+                    _ => (0..exprs.len()).map(|_| self.new_type_var()).collect(),
+                };
+
+                for (expr, ty) in exprs.iter().zip(tys.iter_mut()) {
+                    self.infer_expr_coerce(*expr, &Expectation::has_type(ty.clone()));
                 }
 
-                Ty::apply(
-                    TypeCtor::Tuple { cardinality: ty_vec.len() as u16 },
-                    Substs(ty_vec.into()),
-                )
+                Ty::apply(TypeCtor::Tuple { cardinality: tys.len() as u16 }, Substs(tys.into()))
             }
             Expr::Array(array) => {
                 let elem_ty = match &expected.ty {
-                    Ty::Apply(a_ty) => match a_ty.ctor {
-                        TypeCtor::Slice | TypeCtor::Array => {
-                            Ty::clone(&a_ty.parameters.as_single())
-                        }
-                        _ => self.new_type_var(),
-                    },
+                    ty_app!(TypeCtor::Array, st) | ty_app!(TypeCtor::Slice, st) => {
+                        st.as_single().clone()
+                    }
                     _ => self.new_type_var(),
                 };
 
                 match array {
                     Array::ElementList(items) => {
                         for expr in items.iter() {
-                            self.infer_expr(*expr, &Expectation::has_type(elem_ty.clone()));
+                            self.infer_expr_coerce(*expr, &Expectation::has_type(elem_ty.clone()));
                         }
                     }
                     Array::Repeat { initializer, repeat } => {
-                        self.infer_expr(*initializer, &Expectation::has_type(elem_ty.clone()));
+                        self.infer_expr_coerce(
+                            *initializer,
+                            &Expectation::has_type(elem_ty.clone()),
+                        );
                         self.infer_expr(
                             *repeat,
                             &Expectation::has_type(Ty::simple(TypeCtor::Int(
@@ -1246,14 +1609,19 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
                 Statement::Let { pat, type_ref, initializer } => {
                     let decl_ty =
                         type_ref.as_ref().map(|tr| self.make_ty(tr)).unwrap_or(Ty::Unknown);
-                    let decl_ty = self.insert_type_vars(decl_ty);
-                    let ty = if let Some(expr) = initializer {
-                        let expr_ty = self.infer_expr(*expr, &Expectation::has_type(decl_ty));
-                        expr_ty
-                    } else {
-                        decl_ty
-                    };
 
+                    // Always use the declared type when specified
+                    let mut ty = decl_ty.clone();
+
+                    if let Some(expr) = initializer {
+                        let actual_ty =
+                            self.infer_expr_coerce(*expr, &Expectation::has_type(decl_ty.clone()));
+                        if decl_ty == Ty::Unknown {
+                            ty = actual_ty;
+                        }
+                    }
+
+                    let ty = self.resolve_ty_as_possible(&mut vec![], ty);
                     self.infer_pat(*pat, &ty, BindingMode::default());
                 }
                 Statement::Expr(expr) => {
@@ -1261,9 +1629,13 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
                 }
             }
         }
-        let ty =
-            if let Some(expr) = tail { self.infer_expr_inner(expr, expected) } else { Ty::unit() };
-        ty
+
+        if let Some(expr) = tail {
+            self.infer_expr_coerce(expr, expected)
+        } else {
+            self.coerce(&Ty::unit(), &expected.ty);
+            Ty::unit()
+        }
     }
 
     fn check_call_arguments(&mut self, args: &[ExprId], param_tys: &[Ty]) {
@@ -1285,7 +1657,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
                 }
 
                 let param_ty = self.normalize_associated_types_in(param_ty);
-                self.infer_expr(arg, &Expectation::has_type(param_ty));
+                self.infer_expr_coerce(arg, &Expectation::has_type(param_ty.clone()));
             }
         }
     }
@@ -1400,12 +1772,16 @@ pub enum InferTy {
     TypeVar(TypeVarId),
     IntVar(TypeVarId),
     FloatVar(TypeVarId),
+    MaybeNeverTypeVar(TypeVarId),
 }
 
 impl InferTy {
     fn to_inner(self) -> TypeVarId {
         match self {
-            InferTy::TypeVar(ty) | InferTy::IntVar(ty) | InferTy::FloatVar(ty) => ty,
+            InferTy::TypeVar(ty)
+            | InferTy::IntVar(ty)
+            | InferTy::FloatVar(ty)
+            | InferTy::MaybeNeverTypeVar(ty) => ty,
         }
     }
 
@@ -1418,6 +1794,7 @@ impl InferTy {
             InferTy::FloatVar(..) => Ty::simple(TypeCtor::Float(
                 primitive::UncertainFloatTy::Known(primitive::FloatTy::f64()),
             )),
+            InferTy::MaybeNeverTypeVar(..) => Ty::simple(TypeCtor::Never),
         }
     }
 }
@@ -1475,37 +1852,3 @@ mod diagnostics {
         }
     }
 }
-
-fn is_never(ty: &Ty) -> bool {
-    if let Ty::Apply(ApplicationTy { ctor: TypeCtor::Never, .. }) = ty {
-        true
-    } else {
-        false
-    }
-}
-
-fn calculate_least_upper_bound(expected_ty: Ty, actual_tys: &[Ty]) -> Ty {
-    let mut all_never = true;
-    let mut last_never_ty = None;
-    let mut least_upper_bound = expected_ty;
-
-    for actual_ty in actual_tys {
-        if is_never(actual_ty) {
-            last_never_ty = Some(actual_ty.clone());
-        } else {
-            all_never = false;
-            least_upper_bound = match (actual_ty, &least_upper_bound) {
-                (_, Ty::Unknown)
-                | (Ty::Infer(_), Ty::Infer(InferTy::TypeVar(_)))
-                | (Ty::Apply(_), _) => actual_ty.clone(),
-                _ => least_upper_bound,
-            }
-        }
-    }
-
-    if all_never && last_never_ty.is_some() {
-        last_never_ty.unwrap()
-    } else {
-        least_upper_bound
-    }
-}
diff --git a/crates/ra_hir/src/ty/infer/unify.rs b/crates/ra_hir/src/ty/infer/unify.rs
index 9a0d2d8f9c..b6ebee3b1b 100644
--- a/crates/ra_hir/src/ty/infer/unify.rs
+++ b/crates/ra_hir/src/ty/infer/unify.rs
@@ -63,6 +63,7 @@ where
                         InferTy::TypeVar(_) => InferTy::TypeVar(root),
                         InferTy::IntVar(_) => InferTy::IntVar(root),
                         InferTy::FloatVar(_) => InferTy::FloatVar(root),
+                        InferTy::MaybeNeverTypeVar(_) => InferTy::MaybeNeverTypeVar(root),
                     };
                     let position = self.add(free_var);
                     Ty::Bound(position as u32)


Test patch:
diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs
index f4f63ca934..4362bb27a3 100644
--- a/crates/ra_hir/src/ty/tests.rs
+++ b/crates/ra_hir/src/ty/tests.rs
@@ -20,6 +20,9 @@ use crate::{
 // against snapshots of the expected results using insta. Use cargo-insta to
 // update the snapshots.
 
+mod never_type;
+mod coercion;
+
 #[test]
 fn infer_await() {
     let (mut db, pos) = MockDatabase::with_position(
@@ -236,17 +239,23 @@ fn test() {
     let a = 1isize;
     let b: usize = 1;
     let c = b;
+    let d: u32;
+    let e;
+    let f: i32 = e;
 }
 "#),
         @r###"
-
-    [11; 71) '{     ...= b; }': ()
+    [11; 118) '{     ...= e; }': ()
     [21; 22) 'a': isize
     [25; 31) '1isize': isize
     [41; 42) 'b': usize
     [52; 53) '1': usize
     [63; 64) 'c': usize
     [67; 68) 'b': usize
+    [78; 79) 'd': u32
+    [94; 95) 'e': i32
+    [105; 106) 'f': i32
+    [114; 115) 'e': i32
     "###
     );
 }
@@ -328,14 +337,14 @@ fn test() {
 "#),
         @r###"
     [45; 49) 'self': &[T]
-    [56; 79) '{     ...     }': !
+    [56; 79) '{     ...     }': T
     [66; 73) 'loop {}': !
     [71; 73) '{}': ()
     [133; 160) '{     ...o"); }': ()
     [139; 149) '<[_]>::foo': fn foo<u8>(&[T]) -> T
     [139; 157) '<[_]>:..."foo")': u8
     [150; 156) 'b"foo"': &[u8]
-"###
+    "###
     );
 }
 
@@ -800,6 +809,130 @@ fn test2(a1: *const A, a2: *mut A) {
     );
 }
 
+#[test]
+fn infer_argument_autoderef() {
+    assert_snapshot!(
+        infer(r#"
+#[lang = "deref"]
+pub trait Deref {
+    type Target;
+    fn deref(&self) -> &Self::Target;
+}
+
+struct A<T>(T);
+
+impl<T> A<T> {
+    fn foo(&self) -> &T {
+        &self.0
+    }
+}
+
+struct B<T>(T);
+
+impl<T> Deref for B<T> {
+    type Target = T;
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+
+fn test() {
+    let t = A::foo(&&B(B(A(42))));
+}
+"#),
+        @r###"
+    [68; 72) 'self': &Self
+    [139; 143) 'self': &A<T>
+    [151; 174) '{     ...     }': &T
+    [161; 168) '&self.0': &T
+    [162; 166) 'self': &A<T>
+    [162; 168) 'self.0': T
+    [255; 259) 'self': &B<T>
+    [278; 301) '{     ...     }': &T
+    [288; 295) '&self.0': &T
+    [289; 293) 'self': &B<T>
+    [289; 295) 'self.0': T
+    [315; 353) '{     ...))); }': ()
+    [325; 326) 't': &i32
+    [329; 335) 'A::foo': fn foo<i32>(&A<T>) -> &T
+    [329; 350) 'A::foo...42))))': &i32
+    [336; 349) '&&B(B(A(42)))': &&B<B<A<i32>>>
+    [337; 349) '&B(B(A(42)))': &B<B<A<i32>>>
+    [338; 339) 'B': B<B<A<i32>>>(T) -> B<T>
+    [338; 349) 'B(B(A(42)))': B<B<A<i32>>>
+    [340; 341) 'B': B<A<i32>>(T) -> B<T>
+    [340; 348) 'B(A(42))': B<A<i32>>
+    [342; 343) 'A': A<i32>(T) -> A<T>
+    [342; 347) 'A(42)': A<i32>
+    [344; 346) '42': i32
+    "###
+    );
+}
+
+#[test]
+fn infer_method_argument_autoderef() {
+    assert_snapshot!(
+        infer(r#"
+#[lang = "deref"]
+pub trait Deref {
+    type Target;
+    fn deref(&self) -> &Self::Target;
+}
+
+struct A<T>(*mut T);
+
+impl<T> A<T> {
+    fn foo(&self, x: &A<T>) -> &T {
+        &*x.0
+    }
+}
+
+struct B<T>(T);
+
+impl<T> Deref for B<T> {
+    type Target = T;
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+
+fn test(a: A<i32>) {
+    let t = A(0 as *mut _).foo(&&B(B(a)));
+}
+"#),
+        @r###"
+    [68; 72) 'self': &Self
+    [144; 148) 'self': &A<T>
+    [150; 151) 'x': &A<T>
+    [166; 187) '{     ...     }': &T
+    [176; 181) '&*x.0': &T
+    [177; 181) '*x.0': T
+    [178; 179) 'x': &A<T>
+    [178; 181) 'x.0': *mut T
+    [268; 272) 'self': &B<T>
+    [291; 314) '{     ...     }': &T
+    [301; 308) '&self.0': &T
+    [302; 306) 'self': &B<T>
+    [302; 308) 'self.0': T
+    [326; 327) 'a': A<i32>
+    [337; 383) '{     ...))); }': ()
+    [347; 348) 't': &i32
+    [351; 352) 'A': A<i32>(*mut T) -> A<T>
+    [351; 365) 'A(0 as *mut _)': A<i32>
+    [351; 380) 'A(0 as...B(a)))': &i32
+    [353; 354) '0': i32
+    [353; 364) '0 as *mut _': *mut i32
+    [370; 379) '&&B(B(a))': &&B<B<A<i32>>>
+    [371; 379) '&B(B(a))': &B<B<A<i32>>>
+    [372; 373) 'B': B<B<A<i32>>>(T) -> B<T>
+    [372; 379) 'B(B(a))': B<B<A<i32>>>
+    [374; 375) 'B': B<A<i32>>(T) -> B<T>
+    [374; 378) 'B(a)': B<A<i32>>
+    [376; 377) 'a': A<i32>
+"###
+    );
+}
+
 #[test]
 fn bug_484() {
     assert_snapshot!(
@@ -983,14 +1116,12 @@ fn test(x: &str, y: isize) {
 
     let b = [a, ["b"]];
     let x: [u8; 0] = [];
-    let z: &[u8] = &[1, 2, 3];
 }
 "#),
         @r###"
-
     [9; 10) 'x': &str
     [18; 19) 'y': isize
-    [28; 324) '{     ... 3]; }': ()
+    [28; 293) '{     ... []; }': ()
     [38; 39) 'a': [&str;_]
     [42; 45) '[x]': [&str;_]
     [43; 44) 'x': &str
@@ -1040,12 +1171,6 @@ fn test(x: &str, y: isize) {
     [260; 263) '"b"': &str
     [275; 276) 'x': [u8;_]
     [288; 290) '[]': [u8;_]
-    [300; 301) 'z': &[u8;_]
-    [311; 321) '&[1, 2, 3]': &[u8;_]
-    [312; 321) '[1, 2, 3]': [u8;_]
-    [313; 314) '1': u8
-    [316; 317) '2': u8
-    [319; 320) '3': u8
     "###
     );
 }
@@ -1767,8 +1892,7 @@ fn test() {
 }
 "#),
         @r###"
-
-    [80; 104) '{     ...     }': !
+    [80; 104) '{     ...     }': Gen<T>
     [90; 98) 'loop { }': !
     [95; 98) '{ }': ()
     [118; 146) '{     ...e(); }': ()
@@ -1798,8 +1922,7 @@ fn test() {
 }
 "#),
         @r###"
-
-    [76; 100) '{     ...     }': !
+    [76; 100) '{     ...     }': Gen<T>
     [86; 94) 'loop { }': !
     [91; 94) '{ }': ()
     [114; 149) '{     ...e(); }': ()
@@ -1830,8 +1953,7 @@ fn test() {
 }
 "#),
         @r###"
-
-    [102; 126) '{     ...     }': !
+    [102; 126) '{     ...     }': Gen<u32, T>
     [112; 120) 'loop { }': !
     [117; 120) '{ }': ()
     [140; 180) '{     ...e(); }': ()
@@ -1973,7 +2095,6 @@ fn test() {
 }
 "#),
         @r###"
-
     [11; 48) '{     ...&y]; }': ()
     [21; 22) 'y': &{unknown}
     [25; 32) 'unknown': &{unknown}
@@ -1998,14 +2119,13 @@ fn test() {
 }
 "#),
         @r###"
-
     [11; 80) '{     ...x)]; }': ()
     [21; 22) 'x': &&{unknown}
     [25; 32) 'unknown': &&{unknown}
     [42; 43) 'y': &&{unknown}
     [46; 53) 'unknown': &&{unknown}
-    [59; 77) '[(x, y..., &x)]': [(&&{unknown}, &&{unknown});_]
-    [60; 66) '(x, y)': (&&{unknown}, &&{unknown})
+    [59; 77) '[(x, y..., &x)]': [(&&&{unknown}, &&&{unknown});_]
+    [60; 66) '(x, y)': (&&&{unknown}, &&&{unknown})
     [61; 62) 'x': &&{unknown}
     [64; 65) 'y': &&{unknown}
     [68; 76) '(&y, &x)': (&&&{unknown}, &&&{unknown})
@@ -2026,7 +2146,7 @@ fn id<T>(x: T) -> T {
 }
 
 fn clone<T>(x: &T) -> T {
-    x
+    *x
 }
 
 fn test() {
@@ -2037,26 +2157,26 @@ fn test() {
 }
 "#),
         @r###"
-
     [10; 11) 'x': T
     [21; 30) '{     x }': T
     [27; 28) 'x': T
     [44; 45) 'x': &T
-    [56; 65) '{     x }': &T
-    [62; 63) 'x': &T
-    [77; 157) '{     ...(1); }': ()
-    [87; 88) 'y': u32
-    [91; 96) '10u32': u32
-    [102; 104) 'id': fn id<u32>(T) -> T
-    [102; 107) 'id(y)': u32
-    [105; 106) 'y': u32
-    [117; 118) 'x': bool
-    [127; 132) 'clone': fn clone<bool>(&T) -> T
-    [127; 135) 'clone(z)': bool
-    [133; 134) 'z': &bool
-    [141; 151) 'id::<i128>': fn id<i128>(T) -> T
-    [141; 154) 'id::<i128>(1)': i128
-    [152; 153) '1': i128
+    [56; 66) '{     *x }': T
+    [62; 64) '*x': T
+    [63; 64) 'x': &T
+    [78; 158) '{     ...(1); }': ()
+    [88; 89) 'y': u32
+    [92; 97) '10u32': u32
+    [103; 105) 'id': fn id<u32>(T) -> T
+    [103; 108) 'id(y)': u32
+    [106; 107) 'y': u32
+    [118; 119) 'x': bool
+    [128; 133) 'clone': fn clone<bool>(&T) -> T
+    [128; 136) 'clone(z)': bool
+    [134; 135) 'z': &bool
+    [142; 152) 'id::<i128>': fn id<i128>(T) -> T
+    [142; 155) 'id::<i128>(1)': i128
+    [153; 154) '1': i128
     "###
     );
 }
@@ -2181,7 +2301,6 @@ fn extra_compiler_flags() {
 }
 "#),
         @r###"
-
     [27; 323) '{     ...   } }': ()
     [33; 321) 'for co...     }': ()
     [37; 44) 'content': &{unknown}
@@ -2195,8 +2314,8 @@ fn extra_compiler_flags() {
     [135; 167) '{     ...     }': &&{unknown}
     [149; 157) '&content': &&{unknown}
     [150; 157) 'content': &{unknown}
-    [182; 189) 'content': &&{unknown}
-    [192; 314) 'if ICE...     }': &&{unknown}
+    [182; 189) 'content': &{unknown}
+    [192; 314) 'if ICE...     }': &{unknown}
     [195; 232) 'ICE_RE..._VALUE': {unknown}
     [195; 248) 'ICE_RE...&name)': bool
     [242; 247) '&name': &&&{unknown}
@@ -3282,7 +3401,7 @@ impl S {
 }
 
 fn test(s: Arc<S>) {
-    (*s, s.foo())<|>
+    (*s, s.foo())<|>;
 }
 "#,
     );
@@ -3356,7 +3475,7 @@ trait Deref {
 }
 
 struct Arc<T>;
-impl<T: ?Sized> Deref for Arc<T> {
+impl<T> Deref for Arc<T> {
     type Target = T;
 }
 
@@ -3366,7 +3485,7 @@ impl S {
 }
 
 fn test(s: Arc<S>) {
-    (*s, s.foo())<|>
+    (*s, s.foo())<|>;
 }
 "#,
     );
@@ -4406,121 +4525,3 @@ fn no_such_field_diagnostics() {
     "###
     );
 }
-
-mod branching_with_never_tests {
-    use super::type_at;
-
-    #[test]
-    fn if_never() {
-        let t = type_at(
-            r#"
-//- /main.rs
-fn test() {
-    let i = if true {
-        loop {}
-    } else {
-        3.0
-    };
-    i<|>
-    ()
-}
-"#,
-        );
-        assert_eq!(t, "f64");
-    }
-
-    #[test]
-    fn if_else_never() {
-        let t = type_at(
-            r#"
-//- /main.rs
-fn test(input: bool) {
-    let i = if input {
-        2.0
-    } else {
-        return
-    };
-    i<|>
-    ()
-}
-"#,
-        );
-        assert_eq!(t, "f64");
-    }
-
-    #[test]
-    fn match_first_arm_never() {
-        let t = type_at(
-            r#"
-//- /main.rs
-fn test(a: i32) {
-    let i = match a {
-        1 => return,
-        2 => 2.0,
-        3 => loop {},
-        _ => 3.0,
-    };
-    i<|>
-    ()
-}
-"#,
-        );
-        assert_eq!(t, "f64");
-    }
-
-    #[test]
-    fn match_second_arm_never() {
-        let t = type_at(
-            r#"
-//- /main.rs
-fn test(a: i32) {
-    let i = match a {
-        1 => 3.0,
-        2 => loop {},
-        3 => 3.0,
-        _ => return,
-    };
-    i<|>
-    ()
-}
-"#,
-        );
-        assert_eq!(t, "f64");
-    }
-
-    #[test]
-    fn match_all_arms_never() {
-        let t = type_at(
-            r#"
-//- /main.rs
-fn test(a: i32) {
-    let i = match a {
-        2 => return,
-        _ => loop {},
-    };
-    i<|>
-    ()
-}
-"#,
-        );
-        assert_eq!(t, "!");
-    }
-
-    #[test]
-    fn match_no_never_arms() {
-        let t = type_at(
-            r#"
-//- /main.rs
-fn test(a: i32) {
-    let i = match a {
-        2 => 2.0,
-        _ => 3.0,
-    };
-    i<|>
-    ()
-}
-"#,
-        );
-        assert_eq!(t, "f64");
-    }
-}
diff --git a/crates/ra_hir/src/ty/tests/coercion.rs b/crates/ra_hir/src/ty/tests/coercion.rs
new file mode 100644
index 0000000000..1530fcc637
--- /dev/null
+++ b/crates/ra_hir/src/ty/tests/coercion.rs
@@ -0,0 +1,369 @@
+use insta::assert_snapshot;
+use test_utils::covers;
+
+// Infer with some common definitions and impls.
+fn infer(source: &str) -> String {
+    let defs = r#"
+        #[lang = "sized"]
+        pub trait Sized {}
+        #[lang = "unsize"]
+        pub trait Unsize<T: ?Sized> {}
+        #[lang = "coerce_unsized"]
+        pub trait CoerceUnsized<T> {}
+
+        impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {}
+        impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for *mut T {}
+    "#;
+
+    // Append to the end to keep positions unchanged.
+    super::infer(&format!("{}{}", source, defs))
+}
+
+#[test]
+fn infer_block_expr_type_mismatch() {
+    assert_snapshot!(
+        infer(r#"
+fn test() {
+    let a: i32 = { 1i64 };
+}
+"#),
+        @r###"
+    [11; 41) '{     ...4 }; }': ()
+    [21; 22) 'a': i32
+    [30; 38) '{ 1i64 }': i64
+    [32; 36) '1i64': i64
+    "###);
+}
+
+#[test]
+fn coerce_places() {
+    assert_snapshot!(
+        infer(r#"
+struct S<T> { a: T }
+
+fn f<T>(_: &[T]) -> T { loop {} }
+fn g<T>(_: S<&[T]>) -> T { loop {} }
+
+fn gen<T>() -> *mut [T; 2] { loop {} }
+fn test1<U>() -> *mut [U] {
+    gen()
+}
+
+fn test2() {
+    let arr: &[u8; 1] = &[1];
+
+    let a: &[_] = arr;
+    let b = f(arr);
+    let c: &[_] = { arr };
+    let d = g(S { a: arr });
+    let e: [&[_]; 1] = [arr];
+    let f: [&[_]; 2] = [arr; 2];
+    let g: (&[_], &[_]) = (arr, arr);
+}
+"#),
+        @r###"
+    [31; 32) '_': &[T]
+    [45; 56) '{ loop {} }': T
+    [47; 54) 'loop {}': !
+    [52; 54) '{}': ()
+    [65; 66) '_': S<&[T]>
+    [82; 93) '{ loop {} }': T
+    [84; 91) 'loop {}': !
+    [89; 91) '{}': ()
+    [122; 133) '{ loop {} }': *mut [T;_]
+    [124; 131) 'loop {}': !
+    [129; 131) '{}': ()
+    [160; 173) '{     gen() }': *mut [U]
+    [166; 169) 'gen': fn gen<U>() -> *mut [T;_]
+    [166; 171) 'gen()': *mut [U;_]
+    [186; 420) '{     ...rr); }': ()
+    [196; 199) 'arr': &[u8;_]
+    [212; 216) '&[1]': &[u8;_]
+    [213; 216) '[1]': [u8;_]
+    [214; 215) '1': u8
+    [227; 228) 'a': &[u8]
+    [237; 240) 'arr': &[u8;_]
+    [250; 251) 'b': u8
+    [254; 255) 'f': fn f<u8>(&[T]) -> T
+    [254; 260) 'f(arr)': u8
+    [256; 259) 'arr': &[u8;_]
+    [270; 271) 'c': &[u8]
+    [280; 287) '{ arr }': &[u8]
+    [282; 285) 'arr': &[u8;_]
+    [297; 298) 'd': u8
+    [301; 302) 'g': fn g<u8>(S<&[T]>) -> T
+    [301; 316) 'g(S { a: arr })': u8
+    [303; 315) 'S { a: arr }': S<&[u8]>
+    [310; 313) 'arr': &[u8;_]
+    [326; 327) 'e': [&[u8];_]
+    [341; 346) '[arr]': [&[u8];_]
+    [342; 345) 'arr': &[u8;_]
+    [356; 357) 'f': [&[u8];_]
+    [371; 379) '[arr; 2]': [&[u8];_]
+    [372; 375) 'arr': &[u8;_]
+    [377; 378) '2': usize
+    [389; 390) 'g': (&[u8], &[u8])
+    [407; 417) '(arr, arr)': (&[u8], &[u8])
+    [408; 411) 'arr': &[u8;_]
+    [413; 416) 'arr': &[u8;_]
+    "###
+    );
+}
+
+#[test]
+fn infer_let_stmt_coerce() {
+    assert_snapshot!(
+        infer(r#"
+fn test() {
+    let x: &[i32] = &[1];
+}
+"#),
+        @r###"
+    [11; 40) '{     ...[1]; }': ()
+    [21; 22) 'x': &[i32]
+    [33; 37) '&[1]': &[i32;_]
+    [34; 37) '[1]': [i32;_]
+    [35; 36) '1': i32
+    "###);
+}
+
+#[test]
+fn infer_custom_coerce_unsized() {
+    assert_snapshot!(
+        infer(r#"
+struct A<T: ?Sized>(*const T);
+struct B<T: ?Sized>(*const T);
+struct C<T: ?Sized> { inner: *const T }
+
+impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<B<U>> for B<T> {}
+impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<C<U>> for C<T> {}
+
+fn foo1<T>(x: A<[T]>) -> A<[T]> { x }
+fn foo2<T>(x: B<[T]>) -> B<[T]> { x }
+fn foo3<T>(x: C<[T]>) -> C<[T]> { x }
+
+fn test(a: A<[u8; 2]>, b: B<[u8; 2]>, c: C<[u8; 2]>) {
+    let d = foo1(a);
+    let e = foo2(b);
+    let f = foo3(c);
+}
+"#),
+        @r###"
+    [258; 259) 'x': A<[T]>
+    [279; 284) '{ x }': A<[T]>
+    [281; 282) 'x': A<[T]>
+    [296; 297) 'x': B<[T]>
+    [317; 322) '{ x }': B<[T]>
+    [319; 320) 'x': B<[T]>
+    [334; 335) 'x': C<[T]>
+    [355; 360) '{ x }': C<[T]>
+    [357; 358) 'x': C<[T]>
+    [370; 371) 'a': A<[u8;_]>
+    [385; 386) 'b': B<[u8;_]>
+    [400; 401) 'c': C<[u8;_]>
+    [415; 481) '{     ...(c); }': ()
+    [425; 426) 'd': A<[{unknown}]>
+    [429; 433) 'foo1': fn foo1<{unknown}>(A<[T]>) -> A<[T]>
+    [429; 436) 'foo1(a)': A<[{unknown}]>
+    [434; 435) 'a': A<[u8;_]>
+    [446; 447) 'e': B<[u8]>
+    [450; 454) 'foo2': fn foo2<u8>(B<[T]>) -> B<[T]>
+    [450; 457) 'foo2(b)': B<[u8]>
+    [455; 456) 'b': B<[u8;_]>
+    [467; 468) 'f': C<[u8]>
+    [471; 475) 'foo3': fn foo3<u8>(C<[T]>) -> C<[T]>
+    [471; 478) 'foo3(c)': C<[u8]>
+    [476; 477) 'c': C<[u8;_]>
+    "###
+    );
+}
+
+#[test]
+fn infer_if_coerce() {
+    assert_snapshot!(
+        infer(r#"
+fn foo<T>(x: &[T]) -> &[T] { loop {} }
+fn test() {
+    let x = if true {
+        foo(&[1])
+    } else {
+        &[1]
+    };
+}
+"#),
+        @r###"
+    [11; 12) 'x': &[T]
+    [28; 39) '{ loop {} }': &[T]
+    [30; 37) 'loop {}': !
+    [35; 37) '{}': ()
+    [50; 126) '{     ...  }; }': ()
+    [60; 61) 'x': &[i32]
+    [64; 123) 'if tru...     }': &[i32]
+    [67; 71) 'true': bool
+    [72; 97) '{     ...     }': &[i32]
+    [82; 85) 'foo': fn foo<i32>(&[T]) -> &[T]
+    [82; 91) 'foo(&[1])': &[i32]
+    [86; 90) '&[1]': &[i32;_]
+    [87; 90) '[1]': [i32;_]
+    [88; 89) '1': i32
+    [103; 123) '{     ...     }': &[i32;_]
+    [113; 117) '&[1]': &[i32;_]
+    [114; 117) '[1]': [i32;_]
+    [115; 116) '1': i32
+    "###
+    );
+}
+
+#[test]
+fn infer_if_else_coerce() {
+    assert_snapshot!(
+        infer(r#"
+fn foo<T>(x: &[T]) -> &[T] { loop {} }
+fn test() {
+    let x = if true {
+        &[1]
+    } else {
+        foo(&[1])
+    };
+}
+"#),
+        @r###"
+    [11; 12) 'x': &[T]
+    [28; 39) '{ loop {} }': &[T]
+    [30; 37) 'loop {}': !
+    [35; 37) '{}': ()
+    [50; 126) '{     ...  }; }': ()
+    [60; 61) 'x': &[i32]
+    [64; 123) 'if tru...     }': &[i32]
+    [67; 71) 'true': bool
+    [72; 92) '{     ...     }': &[i32;_]
+    [82; 86) '&[1]': &[i32;_]
+    [83; 86) '[1]': [i32;_]
+    [84; 85) '1': i32
+    [98; 123) '{     ...     }': &[i32]
+    [108; 111) 'foo': fn foo<i32>(&[T]) -> &[T]
+    [108; 117) 'foo(&[1])': &[i32]
+    [112; 116) '&[1]': &[i32;_]
+    [113; 116) '[1]': [i32;_]
+    [114; 115) '1': i32
+    "###
+    );
+}
+
+#[test]
+fn infer_match_first_coerce() {
+    assert_snapshot!(
+        infer(r#"
+fn foo<T>(x: &[T]) -> &[T] { loop {} }
+fn test(i: i32) {
+    let x = match i {
+        2 => foo(&[2]),
+        1 => &[1],
+        _ => &[3],
+    };
+}
+"#),
+        @r###"
+    [11; 12) 'x': &[T]
+    [28; 39) '{ loop {} }': &[T]
+    [30; 37) 'loop {}': !
+    [35; 37) '{}': ()
+    [48; 49) 'i': i32
+    [56; 150) '{     ...  }; }': ()
+    [66; 67) 'x': &[i32]
+    [70; 147) 'match ...     }': &[i32]
+    [76; 77) 'i': i32
+    [88; 89) '2': i32
+    [93; 96) 'foo': fn foo<i32>(&[T]) -> &[T]
+    [93; 102) 'foo(&[2])': &[i32]
+    [97; 101) '&[2]': &[i32;_]
+    [98; 101) '[2]': [i32;_]
+    [99; 100) '2': i32
+    [112; 113) '1': i32
+    [117; 121) '&[1]': &[i32;_]
+    [118; 121) '[1]': [i32;_]
+    [119; 120) '1': i32
+    [131; 132) '_': i32
+    [136; 140) '&[3]': &[i32;_]
+    [137; 140) '[3]': [i32;_]
+    [138; 139) '3': i32
+    "###
+    );
+}
+
+#[test]
+fn infer_match_second_coerce() {
+    assert_snapshot!(
+        infer(r#"
+fn foo<T>(x: &[T]) -> &[T] { loop {} }
+fn test(i: i32) {
+    let x = match i {
+        1 => &[1],
+        2 => foo(&[2]),
+        _ => &[3],
+    };
+}
+"#),
+        @r###"
+    [11; 12) 'x': &[T]
+    [28; 39) '{ loop {} }': &[T]
+    [30; 37) 'loop {}': !
+    [35; 37) '{}': ()
+    [48; 49) 'i': i32
+    [56; 150) '{     ...  }; }': ()
+    [66; 67) 'x': &[i32]
+    [70; 147) 'match ...     }': &[i32]
+    [76; 77) 'i': i32
+    [88; 89) '1': i32
+    [93; 97) '&[1]': &[i32;_]
+    [94; 97) '[1]': [i32;_]
+    [95; 96) '1': i32
+    [107; 108) '2': i32
+    [112; 115) 'foo': fn foo<i32>(&[T]) -> &[T]
+    [112; 121) 'foo(&[2])': &[i32]
+    [116; 120) '&[2]': &[i32;_]
+    [117; 120) '[2]': [i32;_]
+    [118; 119) '2': i32
+    [131; 132) '_': i32
+    [136; 140) '&[3]': &[i32;_]
+    [137; 140) '[3]': [i32;_]
+    [138; 139) '3': i32
+    "###
+    );
+}
+
+#[test]
+fn coerce_merge_one_by_one1() {
+    covers!(coerce_merge_fail_fallback);
+
+    assert_snapshot!(
+        infer(r#"
+fn test() {
+    let t = &mut 1;
+    let x = match 1 {
+        1 => t as *mut i32,
+        2 => t as &i32,
+        _ => t as *const i32,
+    };
+}
+"#),
+        @r###"
+    [11; 145) '{     ...  }; }': ()
+    [21; 22) 't': &mut i32
+    [25; 31) '&mut 1': &mut i32
+    [30; 31) '1': i32
+    [41; 42) 'x': *const i32
+    [45; 142) 'match ...     }': *const i32
+    [51; 52) '1': i32
+    [63; 64) '1': i32
+    [68; 69) 't': &mut i32
+    [68; 81) 't as *mut i32': *mut i32
+    [91; 92) '2': i32
+    [96; 97) 't': &mut i32
+    [96; 105) 't as &i32': &i32
+    [115; 116) '_': i32
+    [120; 121) 't': &mut i32
+    [120; 135) 't as *const i32': *const i32
+    "###
+    );
+}
diff --git a/crates/ra_hir/src/ty/tests/never_type.rs b/crates/ra_hir/src/ty/tests/never_type.rs
new file mode 100644
index 0000000000..c202f545a3
--- /dev/null
+++ b/crates/ra_hir/src/ty/tests/never_type.rs
@@ -0,0 +1,246 @@
+use super::type_at;
+
+#[test]
+fn infer_never1() {
+    let t = type_at(
+        r#"
+//- /main.rs
+fn test() {
+    let t = return;
+    t<|>;
+}
+"#,
+    );
+    assert_eq!(t, "!");
+}
+
+#[test]
+fn infer_never2() {
+    let t = type_at(
+        r#"
+//- /main.rs
+fn gen<T>() -> T { loop {} }
+
+fn test() {
+    let a = gen();
+    if false { a } else { loop {} };
+    a<|>;
+}
+"#,
+    );
+    assert_eq!(t, "!");
+}
+
+#[test]
+fn infer_never3() {
+    let t = type_at(
+        r#"
+//- /main.rs
+fn gen<T>() -> T { loop {} }
+
+fn test() {
+    let a = gen();
+    if false { loop {} } else { a };
+    a<|>;
+}
+"#,
+    );
+    assert_eq!(t, "!");
+}
+
+#[test]
+fn never_type_in_generic_args() {
+    let t = type_at(
+        r#"
+//- /main.rs
+enum Option<T> { None, Some(T) }
+
+fn test() {
+    let a = if true { Option::None } else { Option::Some(return) };
+    a<|>;
+}
+"#,
+    );
+    assert_eq!(t, "Option<!>");
+}
+
+#[test]
+fn never_type_can_be_reinferred1() {
+    let t = type_at(
+        r#"
+//- /main.rs
+fn gen<T>() -> T { loop {} }
+
+fn test() {
+    let a = gen();
+    if false { loop {} } else { a };
+    a<|>;
+    if false { a };
+}
+"#,
+    );
+    assert_eq!(t, "()");
+}
+
+#[test]
+fn never_type_can_be_reinferred2() {
+    let t = type_at(
+        r#"
+//- /main.rs
+enum Option<T> { None, Some(T) }
+
+fn test() {
+    let a = if true { Option::None } else { Option::Some(return) };
+    a<|>;
+    match 42 {
+        42 => a,
+        _ => Option::Some(42),
+    };
+}
+"#,
+    );
+    assert_eq!(t, "Option<i32>");
+}
+#[test]
+fn never_type_can_be_reinferred3() {
+    let t = type_at(
+        r#"
+//- /main.rs
+enum Option<T> { None, Some(T) }
+
+fn test() {
+    let a = if true { Option::None } else { Option::Some(return) };
+    a<|>;
+    match 42 {
+        42 => a,
+        _ => Option::Some("str"),
+    };
+}
+"#,
+    );
+    assert_eq!(t, "Option<&str>");
+}
+
+#[test]
+fn match_no_arm() {
+    let t = type_at(
+        r#"
+//- /main.rs
+enum Void {}
+
+fn test(a: Void) {
+    let t = match a {};
+    t<|>;
+}
+"#,
+    );
+    assert_eq!(t, "!");
+}
+
+#[test]
+fn if_never() {
+    let t = type_at(
+        r#"
+//- /main.rs
+fn test() {
+    let i = if true {
+        loop {}
+    } else {
+        3.0
+    };
+    i<|>;
+}
+"#,
+    );
+    assert_eq!(t, "f64");
+}
+
+#[test]
+fn if_else_never() {
+    let t = type_at(
+        r#"
+//- /main.rs
+fn test(input: bool) {
+    let i = if input {
+        2.0
+    } else {
+        return
+    };
+    i<|>;
+}
+"#,
+    );
+    assert_eq!(t, "f64");
+}
+
+#[test]
+fn match_first_arm_never() {
+    let t = type_at(
+        r#"
+//- /main.rs
+fn test(a: i32) {
+    let i = match a {
+        1 => return,
+        2 => 2.0,
+        3 => loop {},
+        _ => 3.0,
+    };
+    i<|>;
+}
+"#,
+    );
+    assert_eq!(t, "f64");
+}
+
+#[test]
+fn match_second_arm_never() {
+    let t = type_at(
+        r#"
+//- /main.rs
+fn test(a: i32) {
+    let i = match a {
+        1 => 3.0,
+        2 => loop {},
+        3 => 3.0,
+        _ => return,
+    };
+    i<|>;
+}
+"#,
+    );
+    assert_eq!(t, "f64");
+}
+
+#[test]
+fn match_all_arms_never() {
+    let t = type_at(
+        r#"
+//- /main.rs
+fn test(a: i32) {
+    let i = match a {
+        2 => return,
+        _ => loop {},
+    };
+    i<|>;
+}
+"#,
+    );
+    assert_eq!(t, "!");
+}
+
+#[test]
+fn match_no_never_arms() {
+    let t = type_at(
+        r#"
+//- /main.rs
+fn test(a: i32) {
+    let i = match a {
+        2 => 2.0,
+        _ => 3.0,
+    };
+    i<|>;
+}
+"#,
+    );
+    assert_eq!(t, "f64");
+}