From 77f92674f9a2ea8e8cd303d15710621a4f7839cb Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 26 Jan 2019 23:48:01 +0100 Subject: Handle cycles in type vars This might be the cause of #587. --- crates/ra_hir/src/ty.rs | 33 ++++++++++++++++------ .../src/ty/snapshots/tests__recursive_vars.snap | 14 +++++++++ .../src/ty/snapshots/tests__recursive_vars_2.snap | 21 ++++++++++++++ crates/ra_hir/src/ty/tests.rs | 27 ++++++++++++++++++ 4 files changed, 86 insertions(+), 9 deletions(-) create mode 100644 crates/ra_hir/src/ty/snapshots/tests__recursive_vars.snap create mode 100644 crates/ra_hir/src/ty/snapshots/tests__recursive_vars_2.snap (limited to 'crates') diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index 179ebddee..67fcc5346 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -862,14 +862,15 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { } 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 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { /// 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, 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 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { /// 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, 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 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { 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 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { // 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 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { // 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 index 000000000..c3227ff7e --- /dev/null +++ b/crates/ra_hir/src/ty/snapshots/tests__recursive_vars.snap @@ -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 index 000000000..de124da5b --- /dev/null +++ b/crates/ra_hir/src/ty/snapshots/tests__recursive_vars_2.snap @@ -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] + diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index e1165f682..e34daa0f7 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -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); -- cgit v1.2.3