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 {
/// `&'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:
/// 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>),
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::*;
}
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>,
}
}
-#[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,
}
}
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
};
}
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
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
};
// 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
};
}
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
}
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
}
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
};
}
ast::Expr::CastExpr(e) => {
let _inner_ty = if let Some(e) = e.expr() {
- self.infer_expr(e)
+ self.infer_expr(e)?
} else {
Ty::Unknown
};
}
ast::Expr::RefExpr(e) => {
let _inner_ty = if let Some(e) = e.expr() {
- self.infer_expr(e)
+ self.infer_expr(e)?
} else {
Ty::Unknown
};
}
ast::Expr::PrefixExpr(e) => {
let _inner_ty = if let Some(e) = e.expr() {
- self.infer_expr(e)
+ self.infer_expr(e)?
} else {
Ty::Unknown
};
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) => {
};
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 {
}
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() {
// (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,
- }
+ })
}