diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index 0c24a06527..2d533eb6a9 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -18,6 +18,7 @@ mod primitive; #[cfg(test)] mod tests; +use std::borrow::Cow; use std::ops::Index; use std::sync::Arc; use std::{fmt, mem}; @@ -671,7 +672,10 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { } fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool { - match (ty1, ty2) { + // try to resolve type vars first + let ty1 = self.resolve_ty_shallow(ty1); + let ty2 = self.resolve_ty_shallow(ty2); + match (&*ty1, &*ty2) { (Ty::Unknown, ..) => true, (.., Ty::Unknown) => true, (Ty::Bool, _) @@ -698,10 +702,12 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { .zip(ts2.iter()) .all(|(t1, t2)| self.unify(t1, t2)), (Ty::Infer(InferTy::TypeVar(tv1)), Ty::Infer(InferTy::TypeVar(tv2))) => { + // both type vars are unknown since we tried to resolve them self.var_unification_table.union(*tv1, *tv2); true } (Ty::Infer(InferTy::TypeVar(tv)), other) | (other, Ty::Infer(InferTy::TypeVar(tv))) => { + // the type var is unknown since we tried to resolve it self.var_unification_table .union_value(*tv, TypeVarValue::Known(other.clone())); true @@ -746,6 +752,23 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { }) } + /// If `ty` is a type variable with known type, returns that type; + /// otherwise, return ty. + fn resolve_ty_shallow<'b>(&mut self, ty: &'b Ty) -> Cow<'b, Ty> { + match ty { + Ty::Infer(InferTy::TypeVar(tv)) => { + match self.var_unification_table.probe_value(*tv).known() { + Some(known_ty) => { + // The known_ty can't be a type var itself + Cow::Owned(known_ty.clone()) + } + _ => Cow::Borrowed(ty), + } + } + _ => Cow::Borrowed(ty), + } + } + /// Resolves the type completely; type variables without known type are /// replaced by Ty::Unknown. fn resolve_ty_completely(&mut self, ty: Ty) -> Ty { @@ -816,12 +839,15 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { // if let is desugared to match, so this is always simple if self.infer_expr(*condition, &Expectation::has_type(Ty::Bool))?; let then_ty = self.infer_expr(*then_branch, expected)?; - if let Some(else_branch) = else_branch { - self.infer_expr(*else_branch, expected)?; - } else { - // no else branch -> unit - self.unify(&expected.ty, &Ty::unit()); // actually coerce - } + match else_branch { + Some(else_branch) => { + self.infer_expr(*else_branch, expected)?; + } + None => { + // no else branch -> unit + self.unify(&then_ty, &Ty::unit()); // actually coerce + } + }; then_ty } Expr::Block { statements, tail } => self.infer_block(statements, *tail, expected)?, diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index d8c0af3268..815aecda76 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -230,6 +230,18 @@ fn test2(a1: *const A, a2: *mut A) { ); } +#[test] +fn infer_bug_484() { + check_inference( + r#" +fn test() { + let x = if true {}; +} +"#, + "bug_484.txt", + ); +} + fn infer(content: &str) -> String { let (db, _, file_id) = MockDatabase::with_single_file(content); let source_file = db.source_file(file_id); diff --git a/crates/ra_hir/src/ty/tests/data/bug_484.txt b/crates/ra_hir/src/ty/tests/data/bug_484.txt new file mode 100644 index 0000000000..3005305513 --- /dev/null +++ b/crates/ra_hir/src/ty/tests/data/bug_484.txt @@ -0,0 +1,5 @@ +[11; 37) '{ l... {}; }': () +[20; 21) 'x': () +[24; 34) 'if true {}': () +[27; 31) 'true': bool +[32; 34) '{}': ()