]> git.lizzy.rs Git - rust.git/commitdiff
Handle cycles in type vars
authorFlorian Diebold <flodiebold@gmail.com>
Sat, 26 Jan 2019 22:48:01 +0000 (23:48 +0100)
committerFlorian Diebold <flodiebold@gmail.com>
Sat, 26 Jan 2019 22:48:01 +0000 (23:48 +0100)
This might be the cause of #587.

crates/ra_hir/src/ty.rs
crates/ra_hir/src/ty/snapshots/tests__recursive_vars.snap [new file with mode: 0644]
crates/ra_hir/src/ty/snapshots/tests__recursive_vars_2.snap [new file with mode: 0644]
crates/ra_hir/src/ty/tests.rs

index 179ebddeec9ab9f4c8915860f1fe2eceae816994..67fcc534610a5c13ca13fbe955b4f6de6a61961f 100644 (file)
@@ -862,14 +862,15 @@ fn new(
     }
 
     fn resolve_all(mut self) -> InferenceResult {
+        let mut tv_stack = Vec::new();
         let mut expr_types = mem::replace(&mut self.type_of_expr, ArenaMap::default());
         for ty in expr_types.values_mut() {
-            let resolved = self.resolve_ty_completely(mem::replace(ty, Ty::Unknown));
+            let resolved = self.resolve_ty_completely(&mut tv_stack, mem::replace(ty, Ty::Unknown));
             *ty = resolved;
         }
         let mut pat_types = mem::replace(&mut self.type_of_pat, ArenaMap::default());
         for ty in pat_types.values_mut() {
-            let resolved = self.resolve_ty_completely(mem::replace(ty, Ty::Unknown));
+            let resolved = self.resolve_ty_completely(&mut tv_stack, mem::replace(ty, Ty::Unknown));
             *ty = resolved;
         }
         InferenceResult {
@@ -1014,13 +1015,20 @@ fn insert_type_vars(&mut self, ty: Ty) -> Ty {
     /// by their known types. All types returned by the infer_* functions should
     /// be resolved as far as possible, i.e. contain no type variables with
     /// known type.
-    fn resolve_ty_as_possible(&mut self, ty: Ty) -> Ty {
+    fn resolve_ty_as_possible(&mut self, tv_stack: &mut Vec<TypeVarId>, ty: Ty) -> Ty {
         ty.fold(&mut |ty| match ty {
             Ty::Infer(tv) => {
                 let inner = tv.to_inner();
+                if tv_stack.contains(&inner) {
+                    // recursive type
+                    return tv.fallback_value();
+                }
                 if let Some(known_ty) = self.var_unification_table.probe_value(inner).known() {
                     // known_ty may contain other variables that are known by now
-                    self.resolve_ty_as_possible(known_ty.clone())
+                    tv_stack.push(inner);
+                    let result = self.resolve_ty_as_possible(tv_stack, known_ty.clone());
+                    tv_stack.pop();
+                    result
                 } else {
                     ty
                 }
@@ -1049,13 +1057,20 @@ fn resolve_ty_shallow<'b>(&mut self, ty: &'b Ty) -> Cow<'b, Ty> {
 
     /// Resolves the type completely; type variables without known type are
     /// replaced by Ty::Unknown.
-    fn resolve_ty_completely(&mut self, ty: Ty) -> Ty {
+    fn resolve_ty_completely(&mut self, tv_stack: &mut Vec<TypeVarId>, ty: Ty) -> Ty {
         ty.fold(&mut |ty| match ty {
             Ty::Infer(tv) => {
                 let inner = tv.to_inner();
+                if tv_stack.contains(&inner) {
+                    // recursive type
+                    return tv.fallback_value();
+                }
                 if let Some(known_ty) = self.var_unification_table.probe_value(inner).known() {
                     // known_ty may contain other variables that are known by now
-                    self.resolve_ty_completely(known_ty.clone())
+                    tv_stack.push(inner);
+                    let result = self.resolve_ty_completely(tv_stack, known_ty.clone());
+                    tv_stack.pop();
+                    result
                 } else {
                     tv.fallback_value()
                 }
@@ -1070,7 +1085,7 @@ fn infer_path_expr(&mut self, expr: ExprId, path: &Path) -> Option<Ty> {
             let name = path.as_ident().cloned().unwrap_or_else(Name::self_param);
             if let Some(scope_entry) = self.scopes.resolve_local_name(expr, name) {
                 let ty = self.type_of_pat.get(scope_entry.pat())?;
-                let ty = self.resolve_ty_as_possible(ty.clone());
+                let ty = self.resolve_ty_as_possible(&mut vec![], ty.clone());
                 return Some(ty);
             };
         };
@@ -1239,7 +1254,7 @@ fn infer_pat(&mut self, pat: PatId, expected: &Ty) -> Ty {
         // use a new type variable if we got Ty::Unknown here
         let ty = self.insert_type_vars_shallow(ty);
         self.unify(&ty, expected);
-        let ty = self.resolve_ty_as_possible(ty);
+        let ty = self.resolve_ty_as_possible(&mut vec![], ty);
         self.write_pat_ty(pat, ty.clone());
         ty
     }
@@ -1538,7 +1553,7 @@ fn infer_expr(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
         // use a new type variable if we got Ty::Unknown here
         let ty = self.insert_type_vars_shallow(ty);
         self.unify(&ty, &expected.ty);
-        let ty = self.resolve_ty_as_possible(ty);
+        let ty = self.resolve_ty_as_possible(&mut vec![], ty);
         self.write_expr_ty(tgt_expr, ty.clone());
         ty
     }
diff --git a/crates/ra_hir/src/ty/snapshots/tests__recursive_vars.snap b/crates/ra_hir/src/ty/snapshots/tests__recursive_vars.snap
new file mode 100644 (file)
index 0000000..c3227ff
--- /dev/null
@@ -0,0 +1,14 @@
+---
+created: "2019-01-26T22:42:22.329980185+00:00"
+creator: insta@0.5.2
+expression: "&result"
+source: crates/ra_hir/src/ty/tests.rs
+---
+[11; 48) '{     ...&y]; }': ()
+[21; 22) 'y': &[unknown]
+[25; 32) 'unknown': &[unknown]
+[38; 45) '[y, &y]': [&&[unknown]]
+[39; 40) 'y': &[unknown]
+[42; 44) '&y': &&[unknown]
+[43; 44) 'y': &[unknown]
+
diff --git a/crates/ra_hir/src/ty/snapshots/tests__recursive_vars_2.snap b/crates/ra_hir/src/ty/snapshots/tests__recursive_vars_2.snap
new file mode 100644 (file)
index 0000000..de124da
--- /dev/null
@@ -0,0 +1,21 @@
+---
+created: "2019-01-26T22:42:22.331805845+00:00"
+creator: insta@0.5.2
+expression: "&result"
+source: crates/ra_hir/src/ty/tests.rs
+---
+[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])
+[61; 62) 'x': &&[unknown]
+[64; 65) 'y': &&[unknown]
+[68; 76) '(&y, &x)': (&&&[unknown], &&&[unknown])
+[69; 71) '&y': &&&[unknown]
+[70; 71) 'y': &&[unknown]
+[73; 75) '&x': &&&[unknown]
+[74; 75) 'x': &&[unknown]
+
index e1165f6821a5c830ec900f2e4ff7e64d9711d679..e34daa0f79e9e7df1190020376053c630de29300 100644 (file)
@@ -562,6 +562,33 @@ fn quux() {
     );
 }
 
+#[test]
+fn recursive_vars() {
+    check_inference(
+        "recursive_vars",
+        r#"
+fn test() {
+    let y = unknown;
+    [y, &y];
+}
+"#,
+    );
+}
+
+#[test]
+fn recursive_vars_2() {
+    check_inference(
+        "recursive_vars_2",
+        r#"
+fn test() {
+    let x = unknown;
+    let y = unknown;
+    [(x, y), (&y, &x)];
+}
+"#,
+    );
+}
+
 fn infer(content: &str) -> String {
     let (db, _, file_id) = MockDatabase::with_single_file(content);
     let source_file = db.parse(file_id);