diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index 746b07a054..def787fb1e 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs @@ -190,6 +190,15 @@ struct InferenceContext<'a, D: HirDatabase> { return_ty: Ty, } +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> { fn new(db: &'a D, body: Arc
, resolver: Resolver) -> Self { InferenceContext { @@ -278,10 +287,16 @@ 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))) => { @@ -795,50 +810,146 @@ 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. + /// Infer type of expression with possibly implicit coerce to the expected type. + fn infer_expr_coerce(&mut self, expr: ExprId, expected: &Expectation) -> Ty { + let ty = self.infer_expr_inner(expr, &expected); + self.coerce(&ty, &expected.ty); + ty + } + + /// 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 { - if is_never(from_ty) { - // ! coerces to any type - true - } else { - self.unify(from_ty, to_ty) + 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 (&mut from_ty, &*to_ty) { + // Top and bottom type + (ty_app!(TypeCtor::Never), _) => return true, + + // FIXME: Solve `FromTy: CoerceUnsized