Resolve paths to defs (functions currently) during type inference

This commit is contained in:
Florian Diebold 2018-12-23 17:13:11 +01:00
parent 93ffbf80c6
commit ef67581104
17 changed files with 215 additions and 87 deletions

1
Cargo.lock generated
View File

@ -695,6 +695,7 @@ name = "ra_hir"
version = "0.1.0"
dependencies = [
"arrayvec 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)",
"flexi_logger 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)",
"id-arena 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",

View File

@ -94,6 +94,7 @@ salsa::database_storage! {
fn fn_syntax() for hir::db::FnSyntaxQuery;
fn submodules() for hir::db::SubmodulesQuery;
fn infer() for hir::db::InferQuery;
fn type_for_def() for hir::db::TypeForDefQuery;
}
}
}

View File

@ -524,7 +524,7 @@ impl AnalysisImpl {
let function = ctry!(source_binder::function_from_source(
&*self.db, file_id, parent_fn
)?);
let infer = function.infer(&*self.db);
let infer = function.infer(&*self.db)?;
Ok(infer.type_of_node(node).map(|t| t.to_string()))
}

View File

@ -16,3 +16,6 @@ ra_syntax = { path = "../ra_syntax" }
ra_editor = { path = "../ra_editor" }
ra_db = { path = "../ra_db" }
test_utils = { path = "../test_utils" }
[dev-dependencies]
flexi_logger = "0.10.0"

View File

@ -14,7 +14,7 @@ use crate::{
function::FnId,
module::{ModuleId, ModuleTree, ModuleSource,
nameres::{ItemMap, InputModuleItems}},
ty::InferenceResult,
ty::{InferenceResult, Ty},
};
salsa::query_group! {
@ -31,11 +31,16 @@ pub trait HirDatabase: SyntaxDatabase
use fn query_definitions::fn_syntax;
}
fn infer(fn_id: FnId) -> Arc<InferenceResult> {
fn infer(fn_id: FnId) -> Cancelable<Arc<InferenceResult>> {
type InferQuery;
use fn query_definitions::infer;
}
fn type_for_def(def_id: DefId) -> Cancelable<Ty> {
type TypeForDefQuery;
use fn query_definitions::type_for_def;
}
fn file_items(file_id: FileId) -> Arc<SourceFileItems> {
type SourceFileItemsQuery;
use fn query_definitions::file_items;

View File

@ -5,12 +5,13 @@ use std::{
sync::Arc,
};
use ra_db::Cancelable;
use ra_syntax::{
TextRange, TextUnit,
ast::{self, AstNode, DocCommentsOwner, NameOwner},
};
use crate::{ DefId, HirDatabase, ty::InferenceResult };
use crate::{ DefId, HirDatabase, ty::InferenceResult, Module };
pub use self::scope::FnScopes;
@ -18,7 +19,7 @@ pub use self::scope::FnScopes;
pub struct FnId(pub(crate) DefId);
pub struct Function {
fn_id: FnId,
pub(crate) fn_id: FnId,
}
impl Function {
@ -27,6 +28,10 @@ impl Function {
Function { fn_id }
}
pub fn syntax(&self, db: &impl HirDatabase) -> ast::FnDefNode {
db.fn_syntax(self.fn_id)
}
pub fn scopes(&self, db: &impl HirDatabase) -> Arc<FnScopes> {
db.fn_scopes(self.fn_id)
}
@ -36,9 +41,14 @@ impl Function {
FnSignatureInfo::new(syntax.borrowed())
}
pub fn infer(&self, db: &impl HirDatabase) -> Arc<InferenceResult> {
pub fn infer(&self, db: &impl HirDatabase) -> Cancelable<Arc<InferenceResult>> {
db.infer(self.fn_id)
}
pub fn module(&self, db: &impl HirDatabase) -> Cancelable<Module> {
let loc = self.fn_id.0.loc(db);
Module::new(db, loc.source_root_id, loc.module_id)
}
}
#[derive(Debug, Clone)]

View File

@ -29,7 +29,7 @@ mod ty;
use std::ops::Index;
use ra_syntax::{SyntaxNodeRef, SyntaxNode};
use ra_syntax::{SyntaxNodeRef, SyntaxNode, SyntaxKind};
use ra_db::{LocationIntener, SourceRootId, FileId, Cancelable};
use crate::{
@ -67,6 +67,23 @@ pub struct DefLoc {
source_item_id: SourceItemId,
}
impl DefKind {
pub(crate) fn for_syntax_kind(kind: SyntaxKind) -> Option<DefKind> {
match kind {
SyntaxKind::FN_DEF => Some(DefKind::Function),
SyntaxKind::MODULE => Some(DefKind::Module),
// These define items, but don't have their own DefKinds yet:
SyntaxKind::STRUCT_DEF => Some(DefKind::Item),
SyntaxKind::ENUM_DEF => Some(DefKind::Item),
SyntaxKind::TRAIT_DEF => Some(DefKind::Item),
SyntaxKind::TYPE_DEF => Some(DefKind::Item),
SyntaxKind::CONST_DEF => Some(DefKind::Item),
SyntaxKind::STATIC_DEF => Some(DefKind::Item),
_ => None,
}
}
}
impl DefId {
pub(crate) fn loc(self, db: &impl AsRef<LocationIntener<DefLoc, DefId>>) -> DefLoc {
db.as_ref().id2loc(self)

View File

@ -192,6 +192,7 @@ salsa::database_storage! {
fn fn_syntax() for db::FnSyntaxQuery;
fn submodules() for db::SubmodulesQuery;
fn infer() for db::InferQuery;
fn type_for_def() for db::TypeForDefQuery;
}
}
}

View File

@ -2,6 +2,7 @@ pub(super) mod imp;
pub(super) mod nameres;
use std::sync::Arc;
use log;
use ra_syntax::{
algo::generate,

View File

@ -272,13 +272,13 @@ where
}
}
}
// Populate explicitelly declared items, except modules
// Populate explicitly declared items, except modules
for item in input.items.iter() {
if item.kind == MODULE {
continue;
}
let def_loc = DefLoc {
kind: DefKind::Item,
kind: DefKind::for_syntax_kind(item.kind).unwrap_or(DefKind::Item),
source_root_id: self.source_root,
module_id,
source_item_id: SourceItemId {

View File

@ -11,7 +11,7 @@ use ra_syntax::{
use ra_db::{SourceRootId, FileId, Cancelable,};
use crate::{
SourceFileItems, SourceItemId, DefKind,
SourceFileItems, SourceItemId, DefKind, Function, DefId,
db::HirDatabase,
function::{FnScopes, FnId},
module::{
@ -19,7 +19,7 @@ use crate::{
imp::Submodule,
nameres::{InputModuleItems, ItemMap, Resolver},
},
ty::{self, InferenceResult}
ty::{self, InferenceResult, Ty}
};
/// Resolve `FnId` to the corresponding `SyntaxNode`
@ -36,11 +36,13 @@ pub(super) fn fn_scopes(db: &impl HirDatabase, fn_id: FnId) -> Arc<FnScopes> {
Arc::new(res)
}
pub(super) fn infer(db: &impl HirDatabase, fn_id: FnId) -> Arc<InferenceResult> {
let syntax = db.fn_syntax(fn_id);
let scopes = db.fn_scopes(fn_id);
let res = ty::infer(db, syntax.borrowed(), scopes);
Arc::new(res)
pub(super) fn infer(db: &impl HirDatabase, fn_id: FnId) -> Cancelable<Arc<InferenceResult>> {
let function = Function { fn_id };
ty::infer(db, function).map(Arc::new)
}
pub(super) fn type_for_def(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Ty> {
ty::type_for_def(db, def_id)
}
pub(super) fn file_items(db: &impl HirDatabase, file_id: FileId) -> Arc<SourceFileItems> {

View File

@ -5,21 +5,17 @@ mod tests;
use std::sync::Arc;
use std::fmt;
use log;
use rustc_hash::{FxHashMap};
use ra_db::LocalSyntaxPtr;
use ra_db::{LocalSyntaxPtr, Cancelable};
use ra_syntax::{
SmolStr,
ast::{self, AstNode, LoopBodyOwner, ArgListOwner},
SyntaxNodeRef
};
use crate::{
FnScopes,
db::HirDatabase,
};
// pub(crate) type TypeId = Id<Ty>;
use crate::{Def, DefId, FnScopes, Module, Function, Path, db::HirDatabase};
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub enum Ty {
@ -65,18 +61,6 @@ pub enum Ty {
/// `&'a mut T` or `&'a T`.
// Ref(Region<'tcx>, Ty<'tcx>, hir::Mutability),
/// The anonymous type of a function declaration/definition. Each
/// function has a unique type, which is output (for a function
/// named `foo` returning an `i32`) as `fn() -> i32 {foo}`.
///
/// For example the type of `bar` here:
///
/// ```rust
/// fn foo() -> i32 { 1 }
/// let bar = foo; // bar: fn() -> i32 {foo}
/// ```
// FnDef(DefId, &'tcx Substs<'tcx>),
/// A pointer to a function. Written as `fn() -> i32`.
///
/// For example the type of `bar` here:
@ -85,7 +69,7 @@ pub enum Ty {
/// fn foo() -> i32 { 1 }
/// let bar: fn() -> i32 = foo;
/// ```
// FnPtr(PolyFnSig<'tcx>),
FnPtr(Arc<FnSig>),
/// A trait, defined with `trait`.
// Dynamic(Binder<&'tcx List<ExistentialPredicate<'tcx>>>, ty::Region<'tcx>),
@ -139,6 +123,12 @@ pub enum Ty {
type TyRef = Arc<Ty>;
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct FnSig {
input: Vec<Ty>,
output: Ty,
}
impl Ty {
pub fn new(node: ast::TypeRef) -> Self {
use ra_syntax::ast::TypeRef::*;
@ -208,11 +198,55 @@ impl fmt::Display for Ty {
}
write!(f, ")")
}
Ty::FnPtr(sig) => {
write!(f, "fn(")?;
for t in &sig.input {
write!(f, "{},", t)?;
}
write!(f, ") -> {}", sig.output)
}
Ty::Unknown => write!(f, "[unknown]"),
}
}
}
pub fn type_for_fn(db: &impl HirDatabase, f: Function) -> Cancelable<Ty> {
eprintln!("type_for_fn {:?}", f.fn_id);
let syntax = f.syntax(db);
let node = syntax.borrowed();
// TODO we ignore type parameters for now
let input = node
.param_list()
.map(|pl| {
pl.params()
.map(|p| p.type_ref().map(|t| Ty::new(t)).unwrap_or(Ty::Unknown))
.collect()
})
.unwrap_or_else(Vec::new);
let output = node
.ret_type()
.and_then(|rt| rt.type_ref())
.map(|t| Ty::new(t))
.unwrap_or(Ty::Unknown);
let sig = FnSig { input, output };
Ok(Ty::FnPtr(Arc::new(sig)))
}
pub fn type_for_def(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Ty> {
let def = def_id.resolve(db)?;
match def {
Def::Module(..) => {
log::debug!("trying to get type for module {:?}", def_id);
Ok(Ty::Unknown)
}
Def::Function(f) => type_for_fn(db, f),
Def::Item => {
log::debug!("trying to get type for item of unknown type {:?}", def_id);
Ok(Ty::Unknown)
}
}
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct InferenceResult {
type_for: FxHashMap<LocalSyntaxPtr, Ty>,
@ -224,18 +258,22 @@ impl InferenceResult {
}
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct InferenceContext {
#[derive(Clone, Debug)]
pub struct InferenceContext<'a, D: HirDatabase> {
db: &'a D,
scopes: Arc<FnScopes>,
module: Module,
// TODO unification tables...
type_for: FxHashMap<LocalSyntaxPtr, Ty>,
}
impl InferenceContext {
fn new(scopes: Arc<FnScopes>) -> Self {
impl<'a, D: HirDatabase> InferenceContext<'a, D> {
fn new(db: &'a D, scopes: Arc<FnScopes>, module: Module) -> Self {
InferenceContext {
type_for: FxHashMap::default(),
db,
scopes,
module,
}
}
@ -262,36 +300,42 @@ impl InferenceContext {
self.unify(ty1, ty2)
}
fn infer_path_expr(&mut self, expr: ast::PathExpr) -> Option<Ty> {
let p = expr.path()?;
if p.qualifier().is_none() {
let name = p.segment().and_then(|s| s.name_ref())?;
let scope_entry = self.scopes.resolve_local_name(name)?;
let ty = self.type_for.get(&scope_entry.ptr())?;
Some(ty.clone())
} else {
// TODO resolve path
Some(Ty::Unknown)
}
fn infer_path_expr(&mut self, expr: ast::PathExpr) -> Cancelable<Option<Ty>> {
let ast_path = ctry!(expr.path());
let path = ctry!(Path::from_ast(ast_path));
if path.is_ident() {
// resolve locally
let name = ctry!(ast_path.segment().and_then(|s| s.name_ref()));
if let Some(scope_entry) = self.scopes.resolve_local_name(name) {
let ty = ctry!(self.type_for.get(&scope_entry.ptr()));
return Ok(Some(ty.clone()));
};
};
// resolve in module
let resolved = ctry!(self.module.resolve_path(self.db, path)?);
let ty = self.db.type_for_def(resolved)?;
// TODO we will need to add type variables for type parameters etc. here
Ok(Some(ty))
}
fn infer_expr(&mut self, expr: ast::Expr) -> Ty {
fn infer_expr(&mut self, expr: ast::Expr) -> Cancelable<Ty> {
let ty = match expr {
ast::Expr::IfExpr(e) => {
if let Some(condition) = e.condition() {
if let Some(e) = condition.expr() {
// TODO if no pat, this should be bool
self.infer_expr(e);
self.infer_expr(e)?;
}
// TODO write type for pat
};
let if_ty = if let Some(block) = e.then_branch() {
self.infer_block(block)
self.infer_block(block)?
} else {
Ty::Unknown
};
let else_ty = if let Some(block) = e.else_branch() {
self.infer_block(block)
self.infer_block(block)?
} else {
Ty::Unknown
};
@ -304,14 +348,14 @@ impl InferenceContext {
}
ast::Expr::BlockExpr(e) => {
if let Some(block) = e.block() {
self.infer_block(block)
self.infer_block(block)?
} else {
Ty::Unknown
}
}
ast::Expr::LoopExpr(e) => {
if let Some(block) = e.loop_body() {
self.infer_block(block);
self.infer_block(block)?;
};
// TODO never, or the type of the break param
Ty::Unknown
@ -320,59 +364,69 @@ impl InferenceContext {
if let Some(condition) = e.condition() {
if let Some(e) = condition.expr() {
// TODO if no pat, this should be bool
self.infer_expr(e);
self.infer_expr(e)?;
}
// TODO write type for pat
};
if let Some(block) = e.loop_body() {
// TODO
self.infer_block(block);
self.infer_block(block)?;
};
// TODO always unit?
Ty::Unknown
}
ast::Expr::ForExpr(e) => {
if let Some(expr) = e.iterable() {
self.infer_expr(expr);
self.infer_expr(expr)?;
}
if let Some(_pat) = e.pat() {
// TODO write type for pat
}
if let Some(block) = e.loop_body() {
self.infer_block(block);
self.infer_block(block)?;
}
// TODO always unit?
Ty::Unknown
}
ast::Expr::LambdaExpr(e) => {
let _body_ty = if let Some(body) = e.body() {
self.infer_expr(body)
self.infer_expr(body)?
} else {
Ty::Unknown
};
Ty::Unknown
}
ast::Expr::CallExpr(e) => {
let _callee_ty = if let Some(e) = e.expr() {
self.infer_expr(e)?
} else {
Ty::Unknown
};
if let Some(arg_list) = e.arg_list() {
for arg in arg_list.args() {
// TODO unify / expect argument type
self.infer_expr(arg);
self.infer_expr(arg)?;
}
}
Ty::Unknown
}
ast::Expr::MethodCallExpr(e) => {
let _receiver_ty = if let Some(e) = e.expr() {
self.infer_expr(e)?
} else {
Ty::Unknown
};
if let Some(arg_list) = e.arg_list() {
for arg in arg_list.args() {
// TODO unify / expect argument type
self.infer_expr(arg);
self.infer_expr(arg)?;
}
}
Ty::Unknown
}
ast::Expr::MatchExpr(e) => {
let _ty = if let Some(match_expr) = e.expr() {
self.infer_expr(match_expr)
self.infer_expr(match_expr)?
} else {
Ty::Unknown
};
@ -381,7 +435,7 @@ impl InferenceContext {
// TODO type the bindings in pat
// TODO type the guard
let _ty = if let Some(e) = arm.expr() {
self.infer_expr(e)
self.infer_expr(e)?
} else {
Ty::Unknown
};
@ -394,12 +448,12 @@ impl InferenceContext {
}
ast::Expr::TupleExpr(_e) => Ty::Unknown,
ast::Expr::ArrayExpr(_e) => Ty::Unknown,
ast::Expr::PathExpr(e) => self.infer_path_expr(e).unwrap_or(Ty::Unknown),
ast::Expr::PathExpr(e) => self.infer_path_expr(e)?.unwrap_or(Ty::Unknown),
ast::Expr::ContinueExpr(_e) => Ty::Never,
ast::Expr::BreakExpr(_e) => Ty::Never,
ast::Expr::ParenExpr(e) => {
if let Some(e) = e.expr() {
self.infer_expr(e)
self.infer_expr(e)?
} else {
Ty::Unknown
}
@ -408,7 +462,7 @@ impl InferenceContext {
ast::Expr::ReturnExpr(e) => {
if let Some(e) = e.expr() {
// TODO unify with return type
self.infer_expr(e);
self.infer_expr(e)?;
};
Ty::Never
}
@ -425,7 +479,7 @@ impl InferenceContext {
ast::Expr::FieldExpr(_e) => Ty::Unknown,
ast::Expr::TryExpr(e) => {
let _inner_ty = if let Some(e) = e.expr() {
self.infer_expr(e)
self.infer_expr(e)?
} else {
Ty::Unknown
};
@ -433,7 +487,7 @@ impl InferenceContext {
}
ast::Expr::CastExpr(e) => {
let _inner_ty = if let Some(e) = e.expr() {
self.infer_expr(e)
self.infer_expr(e)?
} else {
Ty::Unknown
};
@ -443,7 +497,7 @@ impl InferenceContext {
}
ast::Expr::RefExpr(e) => {
let _inner_ty = if let Some(e) = e.expr() {
self.infer_expr(e)
self.infer_expr(e)?
} else {
Ty::Unknown
};
@ -451,7 +505,7 @@ impl InferenceContext {
}
ast::Expr::PrefixExpr(e) => {
let _inner_ty = if let Some(e) = e.expr() {
self.infer_expr(e)
self.infer_expr(e)?
} else {
Ty::Unknown
};
@ -462,10 +516,10 @@ impl InferenceContext {
ast::Expr::Literal(_e) => Ty::Unknown,
};
self.write_ty(expr.syntax(), ty.clone());
ty
Ok(ty)
}
fn infer_block(&mut self, node: ast::Block) -> Ty {
fn infer_block(&mut self, node: ast::Block) -> Cancelable<Ty> {
for stmt in node.statements() {
match stmt {
ast::Stmt::LetStmt(stmt) => {
@ -476,7 +530,7 @@ impl InferenceContext {
};
let ty = if let Some(expr) = stmt.initializer() {
// TODO pass expectation
let expr_ty = self.infer_expr(expr);
let expr_ty = self.infer_expr(expr)?;
self.unify_with_coercion(&expr_ty, &decl_ty)
.unwrap_or(decl_ty)
} else {
@ -489,23 +543,28 @@ impl InferenceContext {
}
ast::Stmt::ExprStmt(expr_stmt) => {
if let Some(expr) = expr_stmt.expr() {
self.infer_expr(expr);
self.infer_expr(expr)?;
}
}
}
}
let ty = if let Some(expr) = node.expr() {
self.infer_expr(expr)
self.infer_expr(expr)?
} else {
Ty::unit()
};
self.write_ty(node.syntax(), ty.clone());
ty
Ok(ty)
}
}
pub fn infer(_db: &impl HirDatabase, node: ast::FnDef, scopes: Arc<FnScopes>) -> InferenceResult {
let mut ctx = InferenceContext::new(scopes);
pub fn infer(db: &impl HirDatabase, function: Function) -> Cancelable<InferenceResult> {
let scopes = function.scopes(db);
let module = function.module(db)?;
let mut ctx = InferenceContext::new(db, scopes, module);
let syntax = function.syntax(db);
let node = syntax.borrowed();
if let Some(param_list) = node.param_list() {
for param in param_list.params() {
@ -529,12 +588,12 @@ pub fn infer(_db: &impl HirDatabase, node: ast::FnDef, scopes: Arc<FnScopes>) ->
// (see Expectation in rustc_typeck)
if let Some(block) = node.body() {
ctx.infer_block(block);
ctx.infer_block(block)?;
}
// TODO 'resolve' the types: replace inference variables by their inferred results
InferenceResult {
Ok(InferenceResult {
type_for: ctx.type_for,
}
})
}

View File

@ -1,5 +1,8 @@
use std::fmt::Write;
use std::path::{PathBuf};
use std::sync::Once;
use flexi_logger::Logger;
use ra_db::{SyntaxDatabase};
use ra_syntax::ast::{self, AstNode};
@ -22,7 +25,7 @@ fn infer_file(content: &str) -> String {
let func = source_binder::function_from_source(&db, file_id, fn_def)
.unwrap()
.unwrap();
let inference_result = func.infer(&db);
let inference_result = func.infer(&db).unwrap();
for (syntax_ptr, ty) in &inference_result.type_for {
let node = syntax_ptr.resolve(&source_file);
write!(
@ -58,6 +61,8 @@ fn ellipsize(mut text: String, max_len: usize) -> String {
#[test]
pub fn infer_tests() {
static INIT: Once = Once::new();
INIT.call_once(|| Logger::with_env().start().unwrap());
dir_tests(&test_data_dir(), &["."], |text, _path| infer_file(text));
}

View File

@ -0,0 +1,10 @@
fn a() -> u32 { 1 }
mod b {
fn c() -> u32 { 1 }
}
fn test() {
a();
b::c();
}

View File

@ -0,0 +1,9 @@
[16; 17) '1': [unknown]
[14; 19) '{ 1 }': [unknown]
[47; 52) '{ 1 }': [unknown]
[49; 50) '1': [unknown]
[81; 87) 'b::c()': [unknown]
[66; 90) '{ ...c(); }': ()
[72; 73) 'a': fn() -> u32
[72; 75) 'a()': [unknown]
[81; 85) 'b::c': fn() -> u32

View File

@ -3083,7 +3083,11 @@ impl<R: TreeRoot<RaTypes>> RetTypeNode<R> {
}
impl<'a> RetType<'a> {}
impl<'a> RetType<'a> {
pub fn type_ref(self) -> Option<TypeRef<'a>> {
super::child_opt(self)
}
}
// ReturnExpr
#[derive(Debug, Clone, Copy,)]

View File

@ -254,7 +254,7 @@ Grammar(
],
options: [ "ParamList", ["body", "Block"], "RetType" ],
),
"RetType": (),
"RetType": (options: ["TypeRef"]),
"StructDef": (
traits: [
"NameOwner",