From 226e31dae94f2c72f5cf650564e521b792793629 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Wed, 26 Dec 2018 21:28:05 +0100 Subject: Add test for self type inference --- crates/ra_hir/src/ty/tests.rs | 19 +++++++++++++++++++ crates/ra_hir/src/ty/tests/data/0007_self.txt | 4 ++++ 2 files changed, 23 insertions(+) create mode 100644 crates/ra_hir/src/ty/tests/data/0007_self.txt (limited to 'crates/ra_hir/src') diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 93bf431c4..fb53fcf0b 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -134,6 +134,25 @@ fn test() -> &mut &f64 { ); } +#[test] +fn infer_self() { + check_inference( + r#" +struct S; + +impl S { + fn test(&self) { + self; + } + fn test2(self: &Self) { + self; + } +} +"#, + "0007_self.txt", + ); +} + fn infer(content: &str) -> String { let (db, _, file_id) = MockDatabase::with_single_file(content); let source_file = db.source_file(file_id); diff --git a/crates/ra_hir/src/ty/tests/data/0007_self.txt b/crates/ra_hir/src/ty/tests/data/0007_self.txt new file mode 100644 index 000000000..18cd796c2 --- /dev/null +++ b/crates/ra_hir/src/ty/tests/data/0007_self.txt @@ -0,0 +1,4 @@ +[50; 54) 'self': [unknown] +[40; 61) '{ ... }': () +[88; 109) '{ ... }': () +[98; 102) 'self': [unknown] -- cgit v1.2.3 From ae9530addc4c5e9bbfd5c0287d3c3adb2de95e40 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Fri, 28 Dec 2018 14:34:00 +0100 Subject: Add HIR for impl blocks Since we need to be able to go from def to containing impl block, as well as the other direction, and to find all impls for a certain type, a design similar to the one for modules, where we collect all impls for the whole crate and keep them in an arena, seemed fitting. The ImplBlock type, which provides the public interface, then consists only of an Arc to the arena containing all impls, and the index into it. --- crates/ra_hir/src/db.rs | 8 +- crates/ra_hir/src/function.rs | 17 +++- crates/ra_hir/src/ids.rs | 13 ++- crates/ra_hir/src/impl_block.rs | 172 ++++++++++++++++++++++++++++++++++++++++ crates/ra_hir/src/krate.rs | 2 +- crates/ra_hir/src/lib.rs | 2 + crates/ra_hir/src/mock.rs | 1 + crates/ra_hir/src/module.rs | 15 ++++ 8 files changed, 225 insertions(+), 5 deletions(-) create mode 100644 crates/ra_hir/src/impl_block.rs (limited to 'crates/ra_hir/src') diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs index 73a4cdc5c..6d5235ba4 100644 --- a/crates/ra_hir/src/db.rs +++ b/crates/ra_hir/src/db.rs @@ -4,7 +4,7 @@ use ra_syntax::{SyntaxNode, SourceFileNode}; use ra_db::{SourceRootId, LocationIntener, SyntaxDatabase, Cancelable}; use crate::{ - DefLoc, DefId, MacroCallLoc, MacroCallId, Name, HirFileId, + Crate, DefLoc, DefId, MacroCallLoc, MacroCallId, Name, HirFileId, SourceFileItems, SourceItemId, query_definitions, FnScopes, @@ -13,6 +13,7 @@ use crate::{ nameres::{ItemMap, InputModuleItems}}, ty::{InferenceResult, Ty}, adt::{StructData, EnumData}, + impl_block::CrateImplBlocks, }; salsa::query_group! { @@ -87,6 +88,11 @@ pub trait HirDatabase: SyntaxDatabase type ModuleTreeQuery; use fn crate::module::imp::module_tree; } + + fn impls_in_crate(krate: Crate) -> Cancelable> { + type ImplsInCrateQuery; + use fn crate::impl_block::impls_in_crate; + } } } diff --git a/crates/ra_hir/src/function.rs b/crates/ra_hir/src/function.rs index 5a44132fc..75ef308ae 100644 --- a/crates/ra_hir/src/function.rs +++ b/crates/ra_hir/src/function.rs @@ -11,11 +11,11 @@ use ra_syntax::{ ast::{self, AstNode, DocCommentsOwner, NameOwner}, }; -use crate::{DefId, DefKind, HirDatabase, ty::InferenceResult, Module}; +use crate::{DefId, DefKind, HirDatabase, ty::InferenceResult, Module, Crate, impl_block::ImplBlock}; pub use self::scope::FnScopes; -#[derive(Debug)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct Function { def_id: DefId, } @@ -25,6 +25,10 @@ impl Function { Function { def_id } } + pub fn def_id(&self) -> DefId { + self.def_id + } + pub fn syntax(&self, db: &impl HirDatabase) -> ast::FnDefNode { let def_loc = self.def_id.loc(db); assert!(def_loc.kind == DefKind::Function); @@ -48,6 +52,15 @@ impl Function { pub fn module(&self, db: &impl HirDatabase) -> Cancelable { self.def_id.module(db) } + + pub fn krate(&self, db: &impl HirDatabase) -> Cancelable> { + self.def_id.krate(db) + } + + /// The containing impl block, if this is a method. + pub fn impl_block(&self, db: &impl HirDatabase) -> Cancelable> { + self.def_id.impl_block(db) + } } #[derive(Debug, Clone)] diff --git a/crates/ra_hir/src/ids.rs b/crates/ra_hir/src/ids.rs index 66adacc7d..c98be66f9 100644 --- a/crates/ra_hir/src/ids.rs +++ b/crates/ra_hir/src/ids.rs @@ -2,7 +2,7 @@ use ra_db::{SourceRootId, LocationIntener, Cancelable, FileId}; use ra_syntax::{SourceFileNode, SyntaxKind, SyntaxNode, SyntaxNodeRef, SourceFile, AstNode, ast}; use ra_arena::{Arena, RawId, impl_arena_id}; -use crate::{HirDatabase, PerNs, ModuleId, Module, Def, Function, Struct, Enum}; +use crate::{HirDatabase, PerNs, ModuleId, Module, Def, Function, Struct, Enum, ImplBlock, Crate}; /// hir makes a heavy use of ids: integer (u32) handlers to various things. You /// can think of id as a pointer (but without a lifetime) or a file descriptor @@ -177,6 +177,17 @@ impl DefId { let loc = self.loc(db); Module::new(db, loc.source_root_id, loc.module_id) } + + /// Returns the containing crate. + pub fn krate(&self, db: &impl HirDatabase) -> Cancelable> { + Ok(self.module(db)?.krate(db)) + } + + /// Returns the containing impl block, if this is an impl item. + pub fn impl_block(self, db: &impl HirDatabase) -> Cancelable> { + let crate_impls = db.impls_in_crate(ctry!(self.krate(db)?))?; + Ok(ImplBlock::containing(crate_impls, self)) + } } impl DefLoc { diff --git a/crates/ra_hir/src/impl_block.rs b/crates/ra_hir/src/impl_block.rs new file mode 100644 index 000000000..22f0a4461 --- /dev/null +++ b/crates/ra_hir/src/impl_block.rs @@ -0,0 +1,172 @@ +use std::sync::Arc; +use rustc_hash::FxHashMap; + +use ra_arena::{Arena, RawId, impl_arena_id}; +use ra_syntax::ast::{self, AstNode}; +use ra_db::{LocationIntener, Cancelable}; + +use crate::{ + Crate, DefId, DefLoc, DefKind, SourceItemId, SourceFileItems, + Module, Function, + db::HirDatabase, + type_ref::TypeRef, + module::{ModuleSourceNode}, +}; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ImplBlock { + crate_impl_blocks: Arc, + impl_id: ImplId, +} + +impl ImplBlock { + pub(crate) fn containing( + crate_impl_blocks: Arc, + def_id: DefId, + ) -> Option { + let impl_id = *crate_impl_blocks.impls_by_def.get(&def_id)?; + Some(ImplBlock { + crate_impl_blocks, + impl_id, + }) + } + + fn impl_data(&self) -> &ImplData { + &self.crate_impl_blocks.impls[self.impl_id] + } + + pub fn target(&self) -> &TypeRef { + &self.impl_data().impl_for + } + + pub fn items(&self) -> &[ImplItem] { + &self.impl_data().items + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ImplData { + impl_for: TypeRef, + items: Vec, +} + +impl ImplData { + pub(crate) fn from_ast( + db: &impl AsRef>, + file_items: &SourceFileItems, + module: &Module, + node: ast::ImplBlock, + ) -> Self { + let impl_for = TypeRef::from_ast_opt(node.target_type()); + let file_id = module.source().file_id(); + let items = if let Some(item_list) = node.item_list() { + item_list + .impl_items() + .map(|item_node| { + let kind = match item_node { + ast::ImplItem::FnDef(..) => DefKind::Function, + ast::ImplItem::ConstDef(..) => DefKind::Item, + ast::ImplItem::TypeDef(..) => DefKind::Item, + }; + let item_id = file_items.id_of_unchecked(item_node.syntax()); + let def_loc = DefLoc { + kind, + source_root_id: module.source_root_id, + module_id: module.module_id, + source_item_id: SourceItemId { + file_id, + item_id: Some(item_id), + }, + }; + let def_id = def_loc.id(db); + match item_node { + ast::ImplItem::FnDef(..) => ImplItem::Method(Function::new(def_id)), + ast::ImplItem::ConstDef(..) => ImplItem::Const(def_id), + ast::ImplItem::TypeDef(..) => ImplItem::Type(def_id), + } + }) + .collect() + } else { + Vec::new() + }; + ImplData { impl_for, items } + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ImplItem { + Method(Function), + // these don't have their own types yet + Const(DefId), + Type(DefId), + // Existential +} + +impl ImplItem { + pub fn def_id(&self) -> DefId { + match self { + ImplItem::Method(f) => f.def_id(), + ImplItem::Const(def_id) => *def_id, + ImplItem::Type(def_id) => *def_id, + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct ImplId(pub RawId); +impl_arena_id!(ImplId); + +/// We have to collect all impl blocks in a crate, to later be able to find +/// impls for specific types. +#[derive(Debug, PartialEq, Eq)] +pub struct CrateImplBlocks { + impls: Arena, + impls_by_def: FxHashMap, +} + +impl CrateImplBlocks { + fn new() -> Self { + CrateImplBlocks { + impls: Arena::default(), + impls_by_def: FxHashMap::default(), + } + } + + fn collect(&mut self, db: &impl HirDatabase, module: Module) -> Cancelable<()> { + let module_source_node = module.source().resolve(db); + let node = match &module_source_node { + ModuleSourceNode::SourceFile(node) => node.borrowed().syntax(), + ModuleSourceNode::Module(node) => node.borrowed().syntax(), + }; + + let source_file_items = db.file_items(module.source().file_id()); + + for impl_block_ast in node.children().filter_map(ast::ImplBlock::cast) { + let impl_block = ImplData::from_ast(db, &source_file_items, &module, impl_block_ast); + let id = self.impls.alloc(impl_block); + for impl_item in &self.impls[id].items { + self.impls_by_def.insert(impl_item.def_id(), id); + } + } + + for (_, child) in module.children() { + self.collect(db, child)?; + } + + Ok(()) + } +} + +pub(crate) fn impls_in_crate( + db: &impl HirDatabase, + krate: Crate, +) -> Cancelable> { + let mut result = CrateImplBlocks::new(); + let root_module = if let Some(root) = krate.root_module(db)? { + root + } else { + return Ok(Arc::new(result)); + }; + result.collect(db, root_module)?; + Ok(Arc::new(result)) +} diff --git a/crates/ra_hir/src/krate.rs b/crates/ra_hir/src/krate.rs index a0821d15d..5194e280b 100644 --- a/crates/ra_hir/src/krate.rs +++ b/crates/ra_hir/src/krate.rs @@ -5,7 +5,7 @@ use crate::{HirDatabase, Module, Name, AsName, HirFileId}; /// hir::Crate describes a single crate. It's the main inteface with which /// crate's dependencies interact. Mostly, it should be just a proxy for the /// root module. -#[derive(Debug)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Crate { crate_id: CrateId, } diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index 344b543b6..2abcec441 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs @@ -31,6 +31,7 @@ mod function; mod adt; mod type_ref; mod ty; +mod impl_block; use crate::{ db::HirDatabase, @@ -48,6 +49,7 @@ pub use self::{ function::{Function, FnScopes}, adt::{Struct, Enum}, ty::Ty, + impl_block::{ImplBlock, ImplItem}, }; pub use self::function::FnSignatureInfo; diff --git a/crates/ra_hir/src/mock.rs b/crates/ra_hir/src/mock.rs index 89b18194a..ef245ec7a 100644 --- a/crates/ra_hir/src/mock.rs +++ b/crates/ra_hir/src/mock.rs @@ -203,6 +203,7 @@ salsa::database_storage! { fn type_for_field() for db::TypeForFieldQuery; fn struct_data() for db::StructDataQuery; fn enum_data() for db::EnumDataQuery; + fn impls_in_crate() for db::ImplsInCrateQuery; } } } diff --git a/crates/ra_hir/src/module.rs b/crates/ra_hir/src/module.rs index c70dc54dd..b9821115c 100644 --- a/crates/ra_hir/src/module.rs +++ b/crates/ra_hir/src/module.rs @@ -71,6 +71,21 @@ impl Module { }) } + /// Returns an iterator of all children of this module. + pub fn children<'a>(&'a self) -> impl Iterator + 'a { + self.module_id + .children(&self.tree) + .map(move |(name, module_id)| { + ( + name, + Module { + module_id, + ..self.clone() + }, + ) + }) + } + /// Returns the crate this module is part of. pub fn krate(&self, db: &impl HirDatabase) -> Option { let root_id = self.module_id.crate_root(&self.tree); -- cgit v1.2.3 From 111126ed3c4f6358e0c833f80226e5192778f749 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 29 Dec 2018 21:32:07 +0100 Subject: Type the self parameter --- crates/ra_hir/src/mock.rs | 4 +++ crates/ra_hir/src/ty.rs | 38 ++++++++++++++++++++++----- crates/ra_hir/src/ty/tests/data/0007_self.txt | 2 ++ 3 files changed, 37 insertions(+), 7 deletions(-) (limited to 'crates/ra_hir/src') diff --git a/crates/ra_hir/src/mock.rs b/crates/ra_hir/src/mock.rs index ef245ec7a..2419d256a 100644 --- a/crates/ra_hir/src/mock.rs +++ b/crates/ra_hir/src/mock.rs @@ -30,6 +30,10 @@ impl MockDatabase { let file_id = db.add_file(&mut source_root, "/main.rs", text); db.query_mut(ra_db::SourceRootQuery) .set(WORKSPACE, Arc::new(source_root.clone())); + + let mut crate_graph = CrateGraph::default(); + crate_graph.add_crate_root(file_id); + db.set_crate_graph(crate_graph); (db, source_root, file_id) } diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index 719b3f7cd..c762ec606 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -918,22 +918,46 @@ pub fn infer(db: &impl HirDatabase, def_id: DefId) -> Cancelable ty, + ast::SelfParamFlavor::Ref => Ty::Ref(Arc::new(ty), Mutability::Shared), + ast::SelfParamFlavor::MutRef => Ty::Ref(Arc::new(ty), Mutability::Mut), + }; + ctx.insert_type_vars(ty) + } + } else { + log::debug!( + "No impl block found, but self param for function {:?}", + def_id + ); + ctx.new_type_var() + }; + if let Some(self_kw) = self_param.self_kw() { + ctx.type_of + .insert(LocalSyntaxPtr::new(self_kw.syntax()), self_type); + } + } for param in param_list.params() { let pat = if let Some(pat) = param.pat() { pat } else { continue; }; - if let Some(type_ref) = param.type_ref() { + let ty = if let Some(type_ref) = param.type_ref() { let ty = Ty::from_ast(db, &ctx.module, type_ref)?; - let ty = ctx.insert_type_vars(ty); - ctx.type_of.insert(LocalSyntaxPtr::new(pat.syntax()), ty); + ctx.insert_type_vars(ty) } else { - // TODO self param - let type_var = ctx.new_type_var(); - ctx.type_of - .insert(LocalSyntaxPtr::new(pat.syntax()), type_var); + // missing type annotation + ctx.new_type_var() }; + ctx.type_of.insert(LocalSyntaxPtr::new(pat.syntax()), ty); } } diff --git a/crates/ra_hir/src/ty/tests/data/0007_self.txt b/crates/ra_hir/src/ty/tests/data/0007_self.txt index 18cd796c2..3d3c8b260 100644 --- a/crates/ra_hir/src/ty/tests/data/0007_self.txt +++ b/crates/ra_hir/src/ty/tests/data/0007_self.txt @@ -1,4 +1,6 @@ [50; 54) 'self': [unknown] +[34; 38) 'self': &S [40; 61) '{ ... }': () [88; 109) '{ ... }': () [98; 102) 'self': [unknown] +[75; 79) 'self': &[unknown] -- cgit v1.2.3 From d4db61b9a151a2a46c4067e61b0a4b1a9e3c73ec Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 29 Dec 2018 22:59:18 +0100 Subject: Resolve the self parameter during type inference --- crates/ra_hir/src/path.rs | 5 +++++ crates/ra_hir/src/ty.rs | 14 ++++++++++++-- crates/ra_hir/src/ty/tests/data/0007_self.txt | 4 ++-- 3 files changed, 19 insertions(+), 4 deletions(-) (limited to 'crates/ra_hir/src') diff --git a/crates/ra_hir/src/path.rs b/crates/ra_hir/src/path.rs index 93f7203fe..9fdfa0d13 100644 --- a/crates/ra_hir/src/path.rs +++ b/crates/ra_hir/src/path.rs @@ -70,6 +70,11 @@ impl Path { self.kind == PathKind::Plain && self.segments.len() == 1 } + /// `true` if this path is just a standalone `self` + pub fn is_self(&self) -> bool { + self.kind == PathKind::Self_ && self.segments.len() == 0 + } + /// If this path is a single identifier, like `foo`, return its name. pub fn as_ident(&self) -> Option<&Name> { if self.kind != PathKind::Plain || self.segments.len() > 1 { diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index c762ec606..5ea62a14c 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -496,6 +496,8 @@ impl InferenceResult { struct InferenceContext<'a, D: HirDatabase> { db: &'a D, scopes: Arc, + /// The self param for the current method, if it exists. + self_param: Option, module: Module, var_unification_table: InPlaceUnificationTable, type_of: FxHashMap, @@ -506,6 +508,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { InferenceContext { type_of: FxHashMap::default(), var_unification_table: InPlaceUnificationTable::new(), + self_param: None, // set during parameter typing db, scopes, module, @@ -628,6 +631,12 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { let ty = self.resolve_ty_as_possible(ty.clone()); return Ok(Some(ty)); }; + } else if path.is_self() { + // resolve `self` param + let self_param = ctry!(self.self_param); + let ty = ctry!(self.type_of.get(&self_param)); + let ty = self.resolve_ty_as_possible(ty.clone()); + return Ok(Some(ty)); }; // resolve in module @@ -940,8 +949,9 @@ pub fn infer(db: &impl HirDatabase, def_id: DefId) -> Cancelable Date: Sat, 29 Dec 2018 23:20:12 +0100 Subject: Resolve the Self type --- crates/ra_hir/src/name.rs | 5 +- crates/ra_hir/src/ty.rs | 86 ++++++++++++++++++++------- crates/ra_hir/src/ty/tests/data/0007_self.txt | 4 +- 3 files changed, 71 insertions(+), 24 deletions(-) (limited to 'crates/ra_hir/src') diff --git a/crates/ra_hir/src/name.rs b/crates/ra_hir/src/name.rs index 51e8b3da8..017caf442 100644 --- a/crates/ra_hir/src/name.rs +++ b/crates/ra_hir/src/name.rs @@ -51,6 +51,7 @@ impl Name { "u128" => KnownName::U128, "f32" => KnownName::F32, "f64" => KnownName::F64, + "Self" => KnownName::Self_, _ => return None, }; Some(name) @@ -84,7 +85,7 @@ impl AsName for ra_db::Dependency { // const ISIZE: Name = Name::new("isize") // ``` // but const-fn is not that powerful yet. -#[derive(Debug)] +#[derive(Debug, PartialEq, Eq)] pub(crate) enum KnownName { Isize, I8, @@ -102,4 +103,6 @@ pub(crate) enum KnownName { F32, F64, + + Self_, } diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index 5ea62a14c..d11f80cff 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -31,9 +31,10 @@ use ra_syntax::{ }; use crate::{ - Def, DefId, FnScopes, Module, Function, Struct, Enum, Path, Name, AsName, + Def, DefId, FnScopes, Module, Function, Struct, Enum, Path, Name, AsName, ImplBlock, db::HirDatabase, type_ref::{TypeRef, Mutability}, + name::KnownName, }; /// The ID of a type variable. @@ -235,6 +236,7 @@ impl Ty { pub(crate) fn from_hir( db: &impl HirDatabase, module: &Module, + impl_block: Option<&ImplBlock>, type_ref: &TypeRef, ) -> Cancelable { Ok(match type_ref { @@ -242,29 +244,29 @@ impl Ty { TypeRef::Tuple(inner) => { let inner_tys = inner .iter() - .map(|tr| Ty::from_hir(db, module, tr)) + .map(|tr| Ty::from_hir(db, module, impl_block, tr)) .collect::>>()?; Ty::Tuple(inner_tys.into()) } - TypeRef::Path(path) => Ty::from_hir_path(db, module, path)?, + TypeRef::Path(path) => Ty::from_hir_path(db, module, impl_block, path)?, TypeRef::RawPtr(inner, mutability) => { - let inner_ty = Ty::from_hir(db, module, inner)?; + let inner_ty = Ty::from_hir(db, module, impl_block, inner)?; Ty::RawPtr(Arc::new(inner_ty), *mutability) } TypeRef::Array(_inner) => Ty::Unknown, // TODO TypeRef::Slice(inner) => { - let inner_ty = Ty::from_hir(db, module, inner)?; + let inner_ty = Ty::from_hir(db, module, impl_block, inner)?; Ty::Slice(Arc::new(inner_ty)) } TypeRef::Reference(inner, mutability) => { - let inner_ty = Ty::from_hir(db, module, inner)?; + let inner_ty = Ty::from_hir(db, module, impl_block, inner)?; Ty::Ref(Arc::new(inner_ty), *mutability) } TypeRef::Placeholder => Ty::Unknown, TypeRef::Fn(params) => { let mut inner_tys = params .iter() - .map(|tr| Ty::from_hir(db, module, tr)) + .map(|tr| Ty::from_hir(db, module, impl_block, tr)) .collect::>>()?; let return_ty = inner_tys .pop() @@ -279,9 +281,21 @@ impl Ty { }) } + pub(crate) fn from_hir_opt( + db: &impl HirDatabase, + module: &Module, + impl_block: Option<&ImplBlock>, + type_ref: Option<&TypeRef>, + ) -> Cancelable { + type_ref + .map(|t| Ty::from_hir(db, module, impl_block, t)) + .unwrap_or(Ok(Ty::Unknown)) + } + pub(crate) fn from_hir_path( db: &impl HirDatabase, module: &Module, + impl_block: Option<&ImplBlock>, path: &Path, ) -> Cancelable { if let Some(name) = path.as_ident() { @@ -291,6 +305,8 @@ impl Ty { return Ok(Ty::Uint(uint_ty)); } else if let Some(float_ty) = primitive::FloatTy::from_name(name) { return Ok(Ty::Float(float_ty)); + } else if name.as_known_name() == Some(KnownName::Self_) { + return Ty::from_hir_opt(db, module, None, impl_block.map(|i| i.target())); } } @@ -308,18 +324,20 @@ impl Ty { pub(crate) fn from_ast_opt( db: &impl HirDatabase, module: &Module, + impl_block: Option<&ImplBlock>, node: Option, ) -> Cancelable { - node.map(|n| Ty::from_ast(db, module, n)) + node.map(|n| Ty::from_ast(db, module, impl_block, n)) .unwrap_or(Ok(Ty::Unknown)) } pub(crate) fn from_ast( db: &impl HirDatabase, module: &Module, + impl_block: Option<&ImplBlock>, node: ast::TypeRef, ) -> Cancelable { - Ty::from_hir(db, module, &TypeRef::from_ast(node)) + Ty::from_hir(db, module, impl_block, &TypeRef::from_ast(node)) } pub fn unit() -> Self { @@ -402,18 +420,19 @@ impl fmt::Display for Ty { fn type_for_fn(db: &impl HirDatabase, f: Function) -> Cancelable { let syntax = f.syntax(db); let module = f.module(db)?; + let impl_block = f.impl_block(db)?; let node = syntax.borrowed(); // TODO we ignore type parameters for now let input = node .param_list() .map(|pl| { pl.params() - .map(|p| Ty::from_ast_opt(db, &module, p.type_ref())) + .map(|p| Ty::from_ast_opt(db, &module, impl_block.as_ref(), p.type_ref())) .collect() }) .unwrap_or_else(|| Ok(Vec::new()))?; let output = if let Some(type_ref) = node.ret_type().and_then(|rt| rt.type_ref()) { - Ty::from_ast(db, &module, type_ref)? + Ty::from_ast(db, &module, impl_block.as_ref(), type_ref)? } else { Ty::unit() }; @@ -467,12 +486,13 @@ pub(super) fn type_for_field(db: &impl HirDatabase, def_id: DefId, field: Name) ), }; let module = def_id.module(db)?; + let impl_block = def_id.impl_block(db)?; let type_ref = if let Some(tr) = variant_data.get_field_type_ref(&field) { tr } else { return Ok(Ty::Unknown); }; - Ty::from_hir(db, &module, &type_ref) + Ty::from_hir(db, &module, impl_block.as_ref(), &type_ref) } /// The result of type inference: A mapping from expressions and patterns to types. @@ -499,12 +519,18 @@ struct InferenceContext<'a, D: HirDatabase> { /// The self param for the current method, if it exists. self_param: Option, module: Module, + impl_block: Option, var_unification_table: InPlaceUnificationTable, type_of: FxHashMap, } impl<'a, D: HirDatabase> InferenceContext<'a, D> { - fn new(db: &'a D, scopes: Arc, module: Module) -> Self { + fn new( + db: &'a D, + scopes: Arc, + module: Module, + impl_block: Option, + ) -> Self { InferenceContext { type_of: FxHashMap::default(), var_unification_table: InPlaceUnificationTable::new(), @@ -512,6 +538,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { db, scopes, module, + impl_block, } } @@ -835,7 +862,12 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { } ast::Expr::CastExpr(e) => { let _inner_ty = self.infer_expr_opt(e.expr(), &Expectation::none())?; - let cast_ty = Ty::from_ast_opt(self.db, &self.module, e.type_ref())?; + let cast_ty = Ty::from_ast_opt( + self.db, + &self.module, + self.impl_block.as_ref(), + e.type_ref(), + )?; let cast_ty = self.insert_type_vars(cast_ty); // TODO do the coercion... cast_ty @@ -889,7 +921,12 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { for stmt in node.statements() { match stmt { ast::Stmt::LetStmt(stmt) => { - let decl_ty = Ty::from_ast_opt(self.db, &self.module, stmt.type_ref())?; + let decl_ty = Ty::from_ast_opt( + self.db, + &self.module, + self.impl_block.as_ref(), + stmt.type_ref(), + )?; let decl_ty = self.insert_type_vars(decl_ty); let ty = if let Some(expr) = stmt.initializer() { let expr_ty = self.infer_expr(expr, &Expectation::has_type(decl_ty))?; @@ -921,19 +958,26 @@ pub fn infer(db: &impl HirDatabase, def_id: DefId) -> Cancelable ty, ast::SelfParamFlavor::Ref => Ty::Ref(Arc::new(ty), Mutability::Shared), @@ -961,7 +1005,7 @@ pub fn infer(db: &impl HirDatabase, def_id: DefId) -> Cancelable Cancelable Date: Sat, 29 Dec 2018 23:35:57 +0100 Subject: Refactor a bit --- crates/ra_hir/src/ty.rs | 120 +++++++++++++++++++++++++----------------------- 1 file changed, 62 insertions(+), 58 deletions(-) (limited to 'crates/ra_hir/src') diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index d11f80cff..45a01679c 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -522,6 +522,8 @@ struct InferenceContext<'a, D: HirDatabase> { impl_block: Option, var_unification_table: InPlaceUnificationTable, type_of: FxHashMap, + /// The return type of the function being inferred. + return_ty: Ty, } impl<'a, D: HirDatabase> InferenceContext<'a, D> { @@ -534,7 +536,8 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { InferenceContext { type_of: FxHashMap::default(), var_unification_table: InPlaceUnificationTable::new(), - self_param: None, // set during parameter typing + self_param: None, // set during parameter typing + return_ty: Ty::Unknown, // set in collect_fn_signature db, scopes, module, @@ -555,6 +558,14 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { self.type_of.insert(LocalSyntaxPtr::new(node), ty); } + fn make_ty(&self, type_ref: &TypeRef) -> Cancelable { + Ty::from_hir(self.db, &self.module, self.impl_block.as_ref(), type_ref) + } + + fn make_ty_opt(&self, type_ref: Option<&TypeRef>) -> Cancelable { + Ty::from_hir_opt(self.db, &self.module, self.impl_block.as_ref(), type_ref) + } + fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool { match (ty1, ty2) { (Ty::Unknown, ..) => true, @@ -952,78 +963,71 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { self.write_ty(node.syntax(), ty.clone()); Ok(ty) } -} - -pub fn infer(db: &impl HirDatabase, def_id: DefId) -> Cancelable> { - let function = Function::new(def_id); // TODO: consts also need inference - let scopes = function.scopes(db); - let module = function.module(db)?; - let impl_block = function.impl_block(db)?; - let mut ctx = InferenceContext::new(db, scopes, module, impl_block); - let syntax = function.syntax(db); - let node = syntax.borrowed(); - - if let Some(param_list) = node.param_list() { - if let Some(self_param) = param_list.self_param() { - let self_type = if let Some(impl_block) = &ctx.impl_block { - if let Some(type_ref) = self_param.type_ref() { - let ty = Ty::from_ast(db, &ctx.module, ctx.impl_block.as_ref(), type_ref)?; - ctx.insert_type_vars(ty) + fn collect_fn_signature(&mut self, node: ast::FnDef) -> Cancelable<()> { + if let Some(param_list) = node.param_list() { + if let Some(self_param) = param_list.self_param() { + let self_type = if let Some(type_ref) = self_param.type_ref() { + let ty = self.make_ty(&TypeRef::from_ast(type_ref))?; + self.insert_type_vars(ty) } else { // TODO this should be handled by desugaring during HIR conversion - let ty = Ty::from_hir( - db, - &ctx.module, - ctx.impl_block.as_ref(), - impl_block.target(), - )?; + let ty = self.make_ty_opt(self.impl_block.as_ref().map(|i| i.target()))?; let ty = match self_param.flavor() { ast::SelfParamFlavor::Owned => ty, ast::SelfParamFlavor::Ref => Ty::Ref(Arc::new(ty), Mutability::Shared), ast::SelfParamFlavor::MutRef => Ty::Ref(Arc::new(ty), Mutability::Mut), }; - ctx.insert_type_vars(ty) + self.insert_type_vars(ty) + }; + if let Some(self_kw) = self_param.self_kw() { + let self_param = LocalSyntaxPtr::new(self_kw.syntax()); + self.self_param = Some(self_param); + self.type_of.insert(self_param, self_type); } - } else { - log::debug!( - "No impl block found, but self param for function {:?}", - def_id - ); - ctx.new_type_var() - }; - if let Some(self_kw) = self_param.self_kw() { - let self_param = LocalSyntaxPtr::new(self_kw.syntax()); - ctx.self_param = Some(self_param); - ctx.type_of.insert(self_param, self_type); + } + for param in param_list.params() { + let pat = if let Some(pat) = param.pat() { + pat + } else { + continue; + }; + let ty = if let Some(type_ref) = param.type_ref() { + let ty = self.make_ty(&TypeRef::from_ast(type_ref))?; + self.insert_type_vars(ty) + } else { + // missing type annotation + self.new_type_var() + }; + self.type_of.insert(LocalSyntaxPtr::new(pat.syntax()), ty); } } - for param in param_list.params() { - let pat = if let Some(pat) = param.pat() { - pat - } else { - continue; - }; - let ty = if let Some(type_ref) = param.type_ref() { - let ty = Ty::from_ast(db, &ctx.module, ctx.impl_block.as_ref(), type_ref)?; - ctx.insert_type_vars(ty) - } else { - // missing type annotation - ctx.new_type_var() - }; - ctx.type_of.insert(LocalSyntaxPtr::new(pat.syntax()), ty); - } + + self.return_ty = if let Some(type_ref) = node.ret_type().and_then(|n| n.type_ref()) { + let ty = self.make_ty(&TypeRef::from_ast(type_ref))?; + self.insert_type_vars(ty) + } else { + Ty::unit() + }; + + Ok(()) } +} - let ret_ty = if let Some(type_ref) = node.ret_type().and_then(|n| n.type_ref()) { - let ty = Ty::from_ast(db, &ctx.module, ctx.impl_block.as_ref(), type_ref)?; - ctx.insert_type_vars(ty) - } else { - Ty::unit() - }; +pub fn infer(db: &impl HirDatabase, def_id: DefId) -> Cancelable> { + let function = Function::new(def_id); // TODO: consts also need inference + let scopes = function.scopes(db); + let module = function.module(db)?; + let impl_block = function.impl_block(db)?; + let mut ctx = InferenceContext::new(db, scopes, module, impl_block); + + let syntax = function.syntax(db); + let node = syntax.borrowed(); + + ctx.collect_fn_signature(node)?; if let Some(block) = node.body() { - ctx.infer_block(block, &Expectation::has_type(ret_ty))?; + ctx.infer_block(block, &Expectation::has_type(ctx.return_ty.clone()))?; } Ok(Arc::new(ctx.resolve_all())) -- cgit v1.2.3 From 334ca0d9a790d14414301daa896848bf9a880982 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sun, 30 Dec 2018 19:59:49 +0100 Subject: Rename ImplBlock::target -> target_type, and add target_trait already --- crates/ra_hir/src/impl_block.rs | 20 +++++++++++++++----- crates/ra_hir/src/ty.rs | 4 ++-- 2 files changed, 17 insertions(+), 7 deletions(-) (limited to 'crates/ra_hir/src') diff --git a/crates/ra_hir/src/impl_block.rs b/crates/ra_hir/src/impl_block.rs index 22f0a4461..77fab24d0 100644 --- a/crates/ra_hir/src/impl_block.rs +++ b/crates/ra_hir/src/impl_block.rs @@ -35,8 +35,12 @@ impl ImplBlock { &self.crate_impl_blocks.impls[self.impl_id] } - pub fn target(&self) -> &TypeRef { - &self.impl_data().impl_for + pub fn target_trait(&self) -> Option<&TypeRef> { + self.impl_data().target_trait.as_ref() + } + + pub fn target_type(&self) -> &TypeRef { + &self.impl_data().target_type } pub fn items(&self) -> &[ImplItem] { @@ -46,7 +50,8 @@ impl ImplBlock { #[derive(Debug, Clone, PartialEq, Eq)] pub struct ImplData { - impl_for: TypeRef, + target_trait: Option, + target_type: TypeRef, items: Vec, } @@ -57,7 +62,8 @@ impl ImplData { module: &Module, node: ast::ImplBlock, ) -> Self { - let impl_for = TypeRef::from_ast_opt(node.target_type()); + let target_trait = node.target_type().map(TypeRef::from_ast); + let target_type = TypeRef::from_ast_opt(node.target_type()); let file_id = module.source().file_id(); let items = if let Some(item_list) = node.item_list() { item_list @@ -89,7 +95,11 @@ impl ImplData { } else { Vec::new() }; - ImplData { impl_for, items } + ImplData { + target_trait, + target_type, + items, + } } } diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index 45a01679c..e33762e0d 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -306,7 +306,7 @@ impl Ty { } else if let Some(float_ty) = primitive::FloatTy::from_name(name) { return Ok(Ty::Float(float_ty)); } else if name.as_known_name() == Some(KnownName::Self_) { - return Ty::from_hir_opt(db, module, None, impl_block.map(|i| i.target())); + return Ty::from_hir_opt(db, module, None, impl_block.map(|i| i.target_type())); } } @@ -972,7 +972,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { self.insert_type_vars(ty) } else { // TODO this should be handled by desugaring during HIR conversion - let ty = self.make_ty_opt(self.impl_block.as_ref().map(|i| i.target()))?; + let ty = self.make_ty_opt(self.impl_block.as_ref().map(|i| i.target_type()))?; let ty = match self_param.flavor() { ast::SelfParamFlavor::Owned => ty, ast::SelfParamFlavor::Ref => Ty::Ref(Arc::new(ty), Mutability::Shared), -- cgit v1.2.3 From 443ddb73c395a311b4ddff3bd8267a0eb7079216 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Fri, 4 Jan 2019 19:29:53 +0100 Subject: Do impl collection per module, not per crate --- crates/ra_hir/src/db.rs | 8 +++---- crates/ra_hir/src/ids.rs | 5 ++-- crates/ra_hir/src/impl_block.rs | 52 ++++++++++++++++++++--------------------- 3 files changed, 32 insertions(+), 33 deletions(-) (limited to 'crates/ra_hir/src') diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs index 6d5235ba4..a045bbb12 100644 --- a/crates/ra_hir/src/db.rs +++ b/crates/ra_hir/src/db.rs @@ -4,7 +4,7 @@ use ra_syntax::{SyntaxNode, SourceFileNode}; use ra_db::{SourceRootId, LocationIntener, SyntaxDatabase, Cancelable}; use crate::{ - Crate, DefLoc, DefId, MacroCallLoc, MacroCallId, Name, HirFileId, + DefLoc, DefId, MacroCallLoc, MacroCallId, Name, HirFileId, SourceFileItems, SourceItemId, query_definitions, FnScopes, @@ -13,7 +13,7 @@ use crate::{ nameres::{ItemMap, InputModuleItems}}, ty::{InferenceResult, Ty}, adt::{StructData, EnumData}, - impl_block::CrateImplBlocks, + impl_block::ModuleImplBlocks, }; salsa::query_group! { @@ -89,9 +89,9 @@ pub trait HirDatabase: SyntaxDatabase use fn crate::module::imp::module_tree; } - fn impls_in_crate(krate: Crate) -> Cancelable> { + fn impls_in_module(source_root_id: SourceRootId, module_id: ModuleId) -> Cancelable> { type ImplsInCrateQuery; - use fn crate::impl_block::impls_in_crate; + use fn crate::impl_block::impls_in_module; } } diff --git a/crates/ra_hir/src/ids.rs b/crates/ra_hir/src/ids.rs index c98be66f9..4d6378e02 100644 --- a/crates/ra_hir/src/ids.rs +++ b/crates/ra_hir/src/ids.rs @@ -185,8 +185,9 @@ impl DefId { /// Returns the containing impl block, if this is an impl item. pub fn impl_block(self, db: &impl HirDatabase) -> Cancelable> { - let crate_impls = db.impls_in_crate(ctry!(self.krate(db)?))?; - Ok(ImplBlock::containing(crate_impls, self)) + let loc = self.loc(db); + let module_impls = db.impls_in_module(loc.source_root_id, loc.module_id)?; + Ok(ImplBlock::containing(module_impls, self)) } } diff --git a/crates/ra_hir/src/impl_block.rs b/crates/ra_hir/src/impl_block.rs index 77fab24d0..01afa84c4 100644 --- a/crates/ra_hir/src/impl_block.rs +++ b/crates/ra_hir/src/impl_block.rs @@ -3,36 +3,36 @@ use rustc_hash::FxHashMap; use ra_arena::{Arena, RawId, impl_arena_id}; use ra_syntax::ast::{self, AstNode}; -use ra_db::{LocationIntener, Cancelable}; +use ra_db::{LocationIntener, Cancelable, SourceRootId}; use crate::{ - Crate, DefId, DefLoc, DefKind, SourceItemId, SourceFileItems, + DefId, DefLoc, DefKind, SourceItemId, SourceFileItems, Module, Function, db::HirDatabase, type_ref::TypeRef, - module::{ModuleSourceNode}, + module::{ModuleSourceNode, ModuleId}, }; #[derive(Debug, Clone, PartialEq, Eq)] pub struct ImplBlock { - crate_impl_blocks: Arc, + module_impl_blocks: Arc, impl_id: ImplId, } impl ImplBlock { pub(crate) fn containing( - crate_impl_blocks: Arc, + module_impl_blocks: Arc, def_id: DefId, ) -> Option { - let impl_id = *crate_impl_blocks.impls_by_def.get(&def_id)?; + let impl_id = *module_impl_blocks.impls_by_def.get(&def_id)?; Some(ImplBlock { - crate_impl_blocks, + module_impl_blocks, impl_id, }) } fn impl_data(&self) -> &ImplData { - &self.crate_impl_blocks.impls[self.impl_id] + &self.module_impl_blocks.impls[self.impl_id] } pub fn target_trait(&self) -> Option<&TypeRef> { @@ -126,17 +126,22 @@ impl ImplItem { pub struct ImplId(pub RawId); impl_arena_id!(ImplId); -/// We have to collect all impl blocks in a crate, to later be able to find -/// impls for specific types. +/// Collection of impl blocks is a two-step process: First we collect the blocks +/// per-module; then we build an index of all impl blocks in the crate. This +/// way, we avoid having to do this process for the whole crate whenever someone +/// types in any file; as long as the impl blocks in the file don't change, we +/// don't need to do the second step again. +/// +/// (The second step does not yet exist currently.) #[derive(Debug, PartialEq, Eq)] -pub struct CrateImplBlocks { +pub struct ModuleImplBlocks { impls: Arena, impls_by_def: FxHashMap, } -impl CrateImplBlocks { +impl ModuleImplBlocks { fn new() -> Self { - CrateImplBlocks { + ModuleImplBlocks { impls: Arena::default(), impls_by_def: FxHashMap::default(), } @@ -159,24 +164,17 @@ impl CrateImplBlocks { } } - for (_, child) in module.children() { - self.collect(db, child)?; - } - Ok(()) } } -pub(crate) fn impls_in_crate( +pub(crate) fn impls_in_module( db: &impl HirDatabase, - krate: Crate, -) -> Cancelable> { - let mut result = CrateImplBlocks::new(); - let root_module = if let Some(root) = krate.root_module(db)? { - root - } else { - return Ok(Arc::new(result)); - }; - result.collect(db, root_module)?; + source_root_id: SourceRootId, + module_id: ModuleId, +) -> Cancelable> { + let mut result = ModuleImplBlocks::new(); + let module = Module::new(db, source_root_id, module_id)?; + result.collect(db, module)?; Ok(Arc::new(result)) } -- cgit v1.2.3 From e6aeabf96f9cf339c81f3e79502d477269d141ed Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Fri, 4 Jan 2019 19:52:07 +0100 Subject: Rename ImplsInCrateQuery as well --- crates/ra_hir/src/db.rs | 2 +- crates/ra_hir/src/mock.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'crates/ra_hir/src') diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs index a045bbb12..58296fc6f 100644 --- a/crates/ra_hir/src/db.rs +++ b/crates/ra_hir/src/db.rs @@ -90,7 +90,7 @@ pub trait HirDatabase: SyntaxDatabase } fn impls_in_module(source_root_id: SourceRootId, module_id: ModuleId) -> Cancelable> { - type ImplsInCrateQuery; + type ImplsInModuleQuery; use fn crate::impl_block::impls_in_module; } } diff --git a/crates/ra_hir/src/mock.rs b/crates/ra_hir/src/mock.rs index 2419d256a..a9db932ff 100644 --- a/crates/ra_hir/src/mock.rs +++ b/crates/ra_hir/src/mock.rs @@ -207,7 +207,7 @@ salsa::database_storage! { fn type_for_field() for db::TypeForFieldQuery; fn struct_data() for db::StructDataQuery; fn enum_data() for db::EnumDataQuery; - fn impls_in_crate() for db::ImplsInCrateQuery; + fn impls_in_module() for db::ImplsInModuleQuery; } } } -- cgit v1.2.3