From 4142792d1fd21a512d1dd37d5822ac2ba470c547 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 29 Dec 2018 20:27:13 +0100 Subject: Add more docs in ty.rs Also get rid of the indirection through query_definitions for the type-related queries. --- crates/ra_hir/src/ty.rs | 77 +++++++++++++++++++++++++++++++++++++------------ 1 file changed, 58 insertions(+), 19 deletions(-) (limited to 'crates/ra_hir/src/ty.rs') diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index 4ebd44d27..719b3f7cd 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -1,3 +1,18 @@ +//! The type system. We currently use this to infer types for completion. +//! +//! For type inference, compare the implementations in rustc (the various +//! check_* methods in librustc_typeck/check/mod.rs are a good entry point) and +//! IntelliJ-Rust (org.rust.lang.core.types.infer). Our entry point for +//! inference here is the `infer` function, which infers the types of all +//! expressions in a given function. +//! +//! The central struct here is `Ty`, which represents a type. During inference, +//! it can contain type 'variables' which represent currently unknown types; as +//! we walk through the expressions, we might determine that certain variables +//! need to be equal to each other, or to certain types. To record this, we use +//! the union-find implementation from the `ena` crate, which is extracted from +//! rustc. + mod primitive; #[cfg(test)] mod tests; @@ -21,6 +36,7 @@ use crate::{ type_ref::{TypeRef, Mutability}, }; +/// The ID of a type variable. #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub struct TypeVarId(u32); @@ -40,6 +56,8 @@ impl UnifyKey for TypeVarId { } } +/// The value of a type variable: either we already know the type, or we don't +/// know it yet. #[derive(Clone, PartialEq, Eq, Debug)] pub enum TypeVarValue { Known(Ty), @@ -47,7 +65,7 @@ pub enum TypeVarValue { } impl TypeVarValue { - pub fn known(&self) -> Option<&Ty> { + fn known(&self) -> Option<&Ty> { match self { TypeVarValue::Known(ty) => Some(ty), TypeVarValue::Unknown => None, @@ -75,10 +93,13 @@ impl UnifyValue for TypeVarValue { } } +/// The kinds of placeholders we need during type inference. Currently, we only +/// have type variables; in the future, we will probably also need int and float +/// variables, for inference of literal values (e.g. `100` could be one of +/// several integer types). #[derive(Clone, PartialEq, Eq, Hash, Debug)] pub enum InferTy { TypeVar(TypeVarId), - // later we'll have IntVar and FloatVar as well } /// When inferring an expression, we propagate downward whatever type hint we @@ -92,15 +113,21 @@ struct Expectation { } impl Expectation { + /// The expectation that the type of the expression needs to equal the given + /// type. fn has_type(ty: Ty) -> Self { Expectation { ty } } + /// This expresses no expectation on the type. fn none() -> Self { Expectation { ty: Ty::Unknown } } } +/// A type. This is based on the `TyKind` enum in rustc (librustc/ty/sty.rs). +/// +/// This should be cheap to clone. #[derive(Clone, PartialEq, Eq, Hash, Debug)] pub enum Ty { /// The primitive boolean type. Written as `bool`. @@ -134,14 +161,14 @@ pub enum Ty { // An array with the given length. Written as `[T; n]`. // Array(Ty, ty::Const), /// The pointee of an array slice. Written as `[T]`. - Slice(TyRef), + Slice(Arc), /// A raw pointer. Written as `*mut T` or `*const T` - RawPtr(TyRef, Mutability), + RawPtr(Arc, Mutability), /// A reference; a pointer with an associated lifetime. Written as /// `&'a mut T` or `&'a T`. - Ref(TyRef, Mutability), + Ref(Arc, Mutability), /// A pointer to a function. Written as `fn() -> i32`. /// @@ -153,6 +180,10 @@ pub enum Ty { /// ``` FnPtr(Arc), + // rustc has a separate type for each function, which just coerces to the + // above function pointer type. Once we implement generics, we will probably + // need this as well. + // A trait, defined with `dyn trait`. // Dynamic(), // The anonymous type of a closure. Used to represent the type of @@ -166,7 +197,7 @@ pub enum Ty { // A type representin the types stored inside a generator. // This should only appear in GeneratorInteriors. // GeneratorWitness(Binder<&'tcx List>>), - /// The never type `!` + /// The never type `!`. Never, /// A tuple type. For example, `(i32, bool)`. @@ -177,10 +208,6 @@ pub enum Ty { // Projection(ProjectionTy), // Opaque (`impl Trait`) type found in a return type. - // The `DefId` comes either from - // * the `impl Trait` ast::Ty node, - // * or the `existential type` declaration - // The substitutions are for the generics of the function in question. // Opaque(DefId, Substs), // A type parameter; for example, `T` in `fn f(x: T) {} @@ -192,12 +219,12 @@ pub enum Ty { /// A placeholder for a type which could not be computed; this is propagated /// to avoid useless error messages. Doubles as a placeholder where type /// variables are inserted before type checking, since we want to try to - /// infer a better type here anyway. + /// infer a better type here anyway -- for the IDE use case, we want to try + /// to infer as much as possible even in the presence of type errors. Unknown, } -type TyRef = Arc; - +/// A function signature. #[derive(Clone, PartialEq, Eq, Hash, Debug)] pub struct FnSig { input: Vec, @@ -368,7 +395,11 @@ impl fmt::Display for Ty { } } -pub fn type_for_fn(db: &impl HirDatabase, f: Function) -> Cancelable { +// Functions returning declared types for items + +/// Compute the declared type of a function. This should not need to look at the +/// function body (but currently uses the function AST, so does anyway - TODO). +fn type_for_fn(db: &impl HirDatabase, f: Function) -> Cancelable { let syntax = f.syntax(db); let module = f.module(db)?; let node = syntax.borrowed(); @@ -390,7 +421,7 @@ pub fn type_for_fn(db: &impl HirDatabase, f: Function) -> Cancelable { Ok(Ty::FnPtr(Arc::new(sig))) } -pub fn type_for_struct(db: &impl HirDatabase, s: Struct) -> Cancelable { +fn type_for_struct(db: &impl HirDatabase, s: Struct) -> Cancelable { Ok(Ty::Adt { def_id: s.def_id(), name: s.name(db)?.unwrap_or_else(Name::missing), @@ -404,7 +435,7 @@ pub fn type_for_enum(db: &impl HirDatabase, s: Enum) -> Cancelable { }) } -pub fn type_for_def(db: &impl HirDatabase, def_id: DefId) -> Cancelable { +pub(super) fn type_for_def(db: &impl HirDatabase, def_id: DefId) -> Cancelable { let def = def_id.resolve(db)?; match def { Def::Module(..) => { @@ -444,19 +475,25 @@ pub(super) fn type_for_field(db: &impl HirDatabase, def_id: DefId, field: Name) Ty::from_hir(db, &module, &type_ref) } +/// The result of type inference: A mapping from expressions and patterns to types. #[derive(Clone, PartialEq, Eq, Debug)] pub struct InferenceResult { type_of: FxHashMap, } impl InferenceResult { + /// Returns the type of the given syntax node, if it was inferred. Will + /// return `None` for syntax nodes not in the inferred function or not + /// pointing to an expression/pattern, `Some(Ty::Unknown)` for + /// expressions/patterns that could not be inferred. pub fn type_of_node(&self, node: SyntaxNodeRef) -> Option { self.type_of.get(&LocalSyntaxPtr::new(node)).cloned() } } +/// The inference context contains all information needed during type inference. #[derive(Clone, Debug)] -pub struct InferenceContext<'a, D: HirDatabase> { +struct InferenceContext<'a, D: HirDatabase> { db: &'a D, scopes: Arc, module: Module, @@ -738,6 +775,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { ast::Expr::ParenExpr(e) => self.infer_expr_opt(e.expr(), expected)?, ast::Expr::Label(_e) => Ty::Unknown, ast::Expr::ReturnExpr(e) => { + // TODO expect return type of function self.infer_expr_opt(e.expr(), &Expectation::none())?; Ty::Never } @@ -870,7 +908,8 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { } } -pub fn infer(db: &impl HirDatabase, function: Function) -> Cancelable { +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 mut ctx = InferenceContext::new(db, scopes, module); @@ -909,5 +948,5 @@ pub fn infer(db: &impl HirDatabase, function: Function) -> Cancelable