From 1212e59beed25d768bfaf7bb202aa955a87106e9 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Thu, 10 Jan 2019 22:49:43 +0100 Subject: Fix assertion error in unification (hopefully) Currently, all types that we handle during inference need to be resolved as far as possible at the time. It's maybe too brittle of an invariant; I need to think how we can do this better. This should fix #484 though, I hope (if it's the same case as I managed to reproduce). --- crates/ra_hir/src/ty.rs | 40 ++++++++++++++++++++++++----- crates/ra_hir/src/ty/tests.rs | 12 +++++++++ crates/ra_hir/src/ty/tests/data/bug_484.txt | 5 ++++ 3 files changed, 50 insertions(+), 7 deletions(-) create mode 100644 crates/ra_hir/src/ty/tests/data/bug_484.txt (limited to 'crates/ra_hir') diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index 0c24a0652..2d533eb6a 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 d8c0af326..815aecda7 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 000000000..300530551 --- /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) '{}': () -- cgit v1.2.3