diff options
author | Florian Diebold <[email protected]> | 2019-01-10 21:49:43 +0000 |
---|---|---|
committer | Florian Diebold <[email protected]> | 2019-01-11 21:59:00 +0000 |
commit | 1212e59beed25d768bfaf7bb202aa955a87106e9 (patch) | |
tree | e2996f670efd50d346cfa8dd0f836bcb65aca4dc /crates | |
parent | f60153ee9e1d598c170743633448aa3ede8cb72e (diff) |
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).
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_hir/src/ty.rs | 40 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/tests.rs | 12 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/tests/data/bug_484.txt | 5 |
3 files changed, 50 insertions, 7 deletions
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; | |||
18 | #[cfg(test)] | 18 | #[cfg(test)] |
19 | mod tests; | 19 | mod tests; |
20 | 20 | ||
21 | use std::borrow::Cow; | ||
21 | use std::ops::Index; | 22 | use std::ops::Index; |
22 | use std::sync::Arc; | 23 | use std::sync::Arc; |
23 | use std::{fmt, mem}; | 24 | use std::{fmt, mem}; |
@@ -671,7 +672,10 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
671 | } | 672 | } |
672 | 673 | ||
673 | fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool { | 674 | fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool { |
674 | match (ty1, ty2) { | 675 | // try to resolve type vars first |
676 | let ty1 = self.resolve_ty_shallow(ty1); | ||
677 | let ty2 = self.resolve_ty_shallow(ty2); | ||
678 | match (&*ty1, &*ty2) { | ||
675 | (Ty::Unknown, ..) => true, | 679 | (Ty::Unknown, ..) => true, |
676 | (.., Ty::Unknown) => true, | 680 | (.., Ty::Unknown) => true, |
677 | (Ty::Bool, _) | 681 | (Ty::Bool, _) |
@@ -698,10 +702,12 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
698 | .zip(ts2.iter()) | 702 | .zip(ts2.iter()) |
699 | .all(|(t1, t2)| self.unify(t1, t2)), | 703 | .all(|(t1, t2)| self.unify(t1, t2)), |
700 | (Ty::Infer(InferTy::TypeVar(tv1)), Ty::Infer(InferTy::TypeVar(tv2))) => { | 704 | (Ty::Infer(InferTy::TypeVar(tv1)), Ty::Infer(InferTy::TypeVar(tv2))) => { |
705 | // both type vars are unknown since we tried to resolve them | ||
701 | self.var_unification_table.union(*tv1, *tv2); | 706 | self.var_unification_table.union(*tv1, *tv2); |
702 | true | 707 | true |
703 | } | 708 | } |
704 | (Ty::Infer(InferTy::TypeVar(tv)), other) | (other, Ty::Infer(InferTy::TypeVar(tv))) => { | 709 | (Ty::Infer(InferTy::TypeVar(tv)), other) | (other, Ty::Infer(InferTy::TypeVar(tv))) => { |
710 | // the type var is unknown since we tried to resolve it | ||
705 | self.var_unification_table | 711 | self.var_unification_table |
706 | .union_value(*tv, TypeVarValue::Known(other.clone())); | 712 | .union_value(*tv, TypeVarValue::Known(other.clone())); |
707 | true | 713 | true |
@@ -746,6 +752,23 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
746 | }) | 752 | }) |
747 | } | 753 | } |
748 | 754 | ||
755 | /// If `ty` is a type variable with known type, returns that type; | ||
756 | /// otherwise, return ty. | ||
757 | fn resolve_ty_shallow<'b>(&mut self, ty: &'b Ty) -> Cow<'b, Ty> { | ||
758 | match ty { | ||
759 | Ty::Infer(InferTy::TypeVar(tv)) => { | ||
760 | match self.var_unification_table.probe_value(*tv).known() { | ||
761 | Some(known_ty) => { | ||
762 | // The known_ty can't be a type var itself | ||
763 | Cow::Owned(known_ty.clone()) | ||
764 | } | ||
765 | _ => Cow::Borrowed(ty), | ||
766 | } | ||
767 | } | ||
768 | _ => Cow::Borrowed(ty), | ||
769 | } | ||
770 | } | ||
771 | |||
749 | /// Resolves the type completely; type variables without known type are | 772 | /// Resolves the type completely; type variables without known type are |
750 | /// replaced by Ty::Unknown. | 773 | /// replaced by Ty::Unknown. |
751 | fn resolve_ty_completely(&mut self, ty: Ty) -> Ty { | 774 | fn resolve_ty_completely(&mut self, ty: Ty) -> Ty { |
@@ -816,12 +839,15 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
816 | // if let is desugared to match, so this is always simple if | 839 | // if let is desugared to match, so this is always simple if |
817 | self.infer_expr(*condition, &Expectation::has_type(Ty::Bool))?; | 840 | self.infer_expr(*condition, &Expectation::has_type(Ty::Bool))?; |
818 | let then_ty = self.infer_expr(*then_branch, expected)?; | 841 | let then_ty = self.infer_expr(*then_branch, expected)?; |
819 | if let Some(else_branch) = else_branch { | 842 | match else_branch { |
820 | self.infer_expr(*else_branch, expected)?; | 843 | Some(else_branch) => { |
821 | } else { | 844 | self.infer_expr(*else_branch, expected)?; |
822 | // no else branch -> unit | 845 | } |
823 | self.unify(&expected.ty, &Ty::unit()); // actually coerce | 846 | None => { |
824 | } | 847 | // no else branch -> unit |
848 | self.unify(&then_ty, &Ty::unit()); // actually coerce | ||
849 | } | ||
850 | }; | ||
825 | then_ty | 851 | then_ty |
826 | } | 852 | } |
827 | Expr::Block { statements, tail } => self.infer_block(statements, *tail, expected)?, | 853 | 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) { | |||
230 | ); | 230 | ); |
231 | } | 231 | } |
232 | 232 | ||
233 | #[test] | ||
234 | fn infer_bug_484() { | ||
235 | check_inference( | ||
236 | r#" | ||
237 | fn test() { | ||
238 | let x = if true {}; | ||
239 | } | ||
240 | "#, | ||
241 | "bug_484.txt", | ||
242 | ); | ||
243 | } | ||
244 | |||
233 | fn infer(content: &str) -> String { | 245 | fn infer(content: &str) -> String { |
234 | let (db, _, file_id) = MockDatabase::with_single_file(content); | 246 | let (db, _, file_id) = MockDatabase::with_single_file(content); |
235 | let source_file = db.source_file(file_id); | 247 | 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 @@ | |||
1 | [11; 37) '{ l... {}; }': () | ||
2 | [20; 21) 'x': () | ||
3 | [24; 34) 'if true {}': () | ||
4 | [27; 31) 'true': bool | ||
5 | [32; 34) '{}': () | ||