mirror of
https://github.com/rust-lang/rust.git
synced 2025-01-08 05:47:40 +00:00
Merge #686
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:
commit
7f9a6521ef
@ -1,4 +1,6 @@
|
|||||||
test_utils::marks!(
|
test_utils::marks!(
|
||||||
name_res_works_for_broken_modules
|
name_res_works_for_broken_modules
|
||||||
item_map_enum_importing
|
item_map_enum_importing
|
||||||
|
type_var_cycles_resolve_completely
|
||||||
|
type_var_cycles_resolve_as_possible
|
||||||
);
|
);
|
||||||
|
@ -29,6 +29,8 @@ use ra_arena::map::ArenaMap;
|
|||||||
use join_to_string::join;
|
use join_to_string::join;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
|
||||||
|
use test_utils::tested_by;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Module, Function, Struct, StructField, Enum, EnumVariant, Path, Name, ImplBlock,
|
Module, Function, Struct, StructField, Enum, EnumVariant, Path, Name, ImplBlock,
|
||||||
FnSignature, FnScopes, ModuleDef, AdtDef,
|
FnSignature, FnScopes, ModuleDef, AdtDef,
|
||||||
@ -862,14 +864,15 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_all(mut self) -> InferenceResult {
|
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());
|
let mut expr_types = mem::replace(&mut self.type_of_expr, ArenaMap::default());
|
||||||
for ty in expr_types.values_mut() {
|
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;
|
*ty = resolved;
|
||||||
}
|
}
|
||||||
let mut pat_types = mem::replace(&mut self.type_of_pat, ArenaMap::default());
|
let mut pat_types = mem::replace(&mut self.type_of_pat, ArenaMap::default());
|
||||||
for ty in pat_types.values_mut() {
|
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;
|
*ty = resolved;
|
||||||
}
|
}
|
||||||
InferenceResult {
|
InferenceResult {
|
||||||
@ -1014,13 +1017,21 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||||||
/// by their known types. All types returned by the infer_* functions should
|
/// 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
|
/// be resolved as far as possible, i.e. contain no type variables with
|
||||||
/// known type.
|
/// 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.fold(&mut |ty| match ty {
|
||||||
Ty::Infer(tv) => {
|
Ty::Infer(tv) => {
|
||||||
let inner = tv.to_inner();
|
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() {
|
if let Some(known_ty) = self.var_unification_table.probe_value(inner).known() {
|
||||||
// known_ty may contain other variables that are known by now
|
// 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 {
|
} else {
|
||||||
ty
|
ty
|
||||||
}
|
}
|
||||||
@ -1049,13 +1060,21 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||||||
|
|
||||||
/// Resolves the type completely; type variables without known type are
|
/// Resolves the type completely; type variables without known type are
|
||||||
/// replaced by Ty::Unknown.
|
/// 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.fold(&mut |ty| match ty {
|
||||||
Ty::Infer(tv) => {
|
Ty::Infer(tv) => {
|
||||||
let inner = tv.to_inner();
|
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() {
|
if let Some(known_ty) = self.var_unification_table.probe_value(inner).known() {
|
||||||
// known_ty may contain other variables that are known by now
|
// 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 {
|
} else {
|
||||||
tv.fallback_value()
|
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);
|
let name = path.as_ident().cloned().unwrap_or_else(Name::self_param);
|
||||||
if let Some(scope_entry) = self.scopes.resolve_local_name(expr, name) {
|
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.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);
|
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
|
// use a new type variable if we got Ty::Unknown here
|
||||||
let ty = self.insert_type_vars_shallow(ty);
|
let ty = self.insert_type_vars_shallow(ty);
|
||||||
self.unify(&ty, expected);
|
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());
|
self.write_pat_ty(pat, ty.clone());
|
||||||
ty
|
ty
|
||||||
}
|
}
|
||||||
@ -1538,7 +1557,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||||||
// use a new type variable if we got Ty::Unknown here
|
// use a new type variable if we got Ty::Unknown here
|
||||||
let ty = self.insert_type_vars_shallow(ty);
|
let ty = self.insert_type_vars_shallow(ty);
|
||||||
self.unify(&ty, &expected.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());
|
self.write_expr_ty(tgt_expr, ty.clone());
|
||||||
ty
|
ty
|
||||||
}
|
}
|
||||||
|
14
crates/ra_hir/src/ty/snapshots/tests__recursive_vars.snap
Normal file
14
crates/ra_hir/src/ty/snapshots/tests__recursive_vars.snap
Normal 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]
|
||||||
|
|
21
crates/ra_hir/src/ty/snapshots/tests__recursive_vars_2.snap
Normal file
21
crates/ra_hir/src/ty/snapshots/tests__recursive_vars_2.snap
Normal 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]
|
||||||
|
|
@ -3,6 +3,7 @@ use std::fmt::Write;
|
|||||||
|
|
||||||
use ra_db::{SourceDatabase, salsa::Database};
|
use ra_db::{SourceDatabase, salsa::Database};
|
||||||
use ra_syntax::ast::{self, AstNode};
|
use ra_syntax::ast::{self, AstNode};
|
||||||
|
use test_utils::covers;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
source_binder,
|
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 {
|
fn infer(content: &str) -> String {
|
||||||
let (db, _, file_id) = MockDatabase::with_single_file(content);
|
let (db, _, file_id) = MockDatabase::with_single_file(content);
|
||||||
let source_file = db.parse(file_id);
|
let source_file = db.parse(file_id);
|
||||||
|
Loading…
Reference in New Issue
Block a user