686: Handle cycles in type vars r=matklad a=flodiebold

This might be the cause of #587.

Co-authored-by: Florian Diebold <flodiebold@gmail.com>
This commit is contained in:
bors[bot] 2019-01-26 23:11:13 +00:00
commit 7f9a6521ef
5 changed files with 97 additions and 9 deletions

View File

@ -1,4 +1,6 @@
test_utils::marks!(
name_res_works_for_broken_modules
item_map_enum_importing
type_var_cycles_resolve_completely
type_var_cycles_resolve_as_possible
);

View File

@ -29,6 +29,8 @@ use ra_arena::map::ArenaMap;
use join_to_string::join;
use rustc_hash::FxHashMap;
use test_utils::tested_by;
use crate::{
Module, Function, Struct, StructField, Enum, EnumVariant, Path, Name, ImplBlock,
FnSignature, FnScopes, ModuleDef, AdtDef,
@ -862,14 +864,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 +1017,21 @@ 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<TypeVarId>, ty: Ty) -> Ty {
ty.fold(&mut |ty| match ty {
Ty::Infer(tv) => {
let inner = tv.to_inner();
if tv_stack.contains(&inner) {
tested_by!(type_var_cycles_resolve_as_possible);
// 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 +1060,21 @@ 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<TypeVarId>, ty: Ty) -> Ty {
ty.fold(&mut |ty| match ty {
Ty::Infer(tv) => {
let inner = tv.to_inner();
if tv_stack.contains(&inner) {
tested_by!(type_var_cycles_resolve_completely);
// 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 +1089,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 +1258,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 +1557,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
}

View File

@ -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]

View File

@ -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]

View File

@ -3,6 +3,7 @@ use std::fmt::Write;
use ra_db::{SourceDatabase, salsa::Database};
use ra_syntax::ast::{self, AstNode};
use test_utils::covers;
use crate::{
source_binder,
@ -562,6 +563,37 @@ fn quux() {
);
}
#[test]
fn recursive_vars() {
covers!(type_var_cycles_resolve_completely);
covers!(type_var_cycles_resolve_as_possible);
check_inference(
"recursive_vars",
r#"
fn test() {
let y = unknown;
[y, &y];
}
"#,
);
}
#[test]
fn recursive_vars_2() {
covers!(type_var_cycles_resolve_completely);
covers!(type_var_cycles_resolve_as_possible);
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);