]> git.lizzy.rs Git - rust.git/commitdiff
Fix assertion error in unification (hopefully)
authorFlorian Diebold <florian.diebold@freiheit.com>
Thu, 10 Jan 2019 21:49:43 +0000 (22:49 +0100)
committerFlorian Diebold <flodiebold@gmail.com>
Fri, 11 Jan 2019 21:59:00 +0000 (22:59 +0100)
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
crates/ra_hir/src/ty/tests.rs
crates/ra_hir/src/ty/tests/data/bug_484.txt [new file with mode: 0644]

index 0c24a065270f0f58d95a1c970f8625b45c68d8dc..2d533eb6a9157ccd1a07267ecf7fc37ff255f861 100644 (file)
@@ -18,6 +18,7 @@
 #[cfg(test)]
 mod tests;
 
+use std::borrow::Cow;
 use std::ops::Index;
 use std::sync::Arc;
 use std::{fmt, mem};
@@ -671,7 +672,10 @@ fn make_ty(&self, type_ref: &TypeRef) -> Cancelable<Ty> {
     }
 
     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 @@ fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool {
                 .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 @@ fn resolve_ty_as_possible(&mut self, ty: Ty) -> Ty {
         })
     }
 
+    /// 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 @@ fn infer_expr(&mut self, expr: ExprId, expected: &Expectation) -> Cancelable<Ty>
                 // 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)?,
index d8c0af32682d71fc0bbde62f01dca67bc0eb888b..815aecda76cc8bd28257bad707b30bab32138297 100644 (file)
@@ -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 (file)
index 0000000..3005305
--- /dev/null
@@ -0,0 +1,5 @@
+[11; 37) '{    l... {}; }': ()
+[20; 21) 'x': ()
+[24; 34) 'if true {}': ()
+[27; 31) 'true': bool
+[32; 34) '{}': ()