From 18b0bd9bffeeeaf664f4a21894d5bfff51e82b32 Mon Sep 17 00:00:00 2001 From: Ville Penttinen Date: Mon, 25 Feb 2019 09:27:47 +0200 Subject: Add const type inference --- crates/ra_hir/src/code_model_api.rs | 32 +++++++++++++++++++ crates/ra_hir/src/code_model_impl.rs | 1 + crates/ra_hir/src/code_model_impl/konst.rs | 26 +++++++++++++++ crates/ra_hir/src/db.rs | 4 +++ crates/ra_hir/src/lib.rs | 2 +- crates/ra_hir/src/ty/infer.rs | 20 ++++++++---- crates/ra_hir/src/ty/lower.rs | 22 ++++++++++--- .../snapshots/tests__infer_associated_const.snap | 14 ++++---- .../src/ty/snapshots/tests__infer_const.snap | 14 ++++++++ .../src/ty/snapshots/tests__infer_static.snap | 16 ++++++++++ crates/ra_hir/src/ty/tests.rs | 37 ++++++++++++++++++++++ 11 files changed, 168 insertions(+), 20 deletions(-) create mode 100644 crates/ra_hir/src/code_model_impl/konst.rs create mode 100644 crates/ra_hir/src/ty/snapshots/tests__infer_const.snap create mode 100644 crates/ra_hir/src/ty/snapshots/tests__infer_static.snap (limited to 'crates') diff --git a/crates/ra_hir/src/code_model_api.rs b/crates/ra_hir/src/code_model_api.rs index 278407504..9efd902fa 100644 --- a/crates/ra_hir/src/code_model_api.rs +++ b/crates/ra_hir/src/code_model_api.rs @@ -554,11 +554,26 @@ impl Const { self.id.module(db) } + pub fn signature(&self, db: &impl HirDatabase) -> Arc { + db.const_signature(*self) + } + /// The containing impl block, if this is a method. pub fn impl_block(&self, db: &impl PersistentHirDatabase) -> Option { let module_impls = db.impls_in_module(self.module(db)); ImplBlock::containing(module_impls, (*self).into()) } + + // TODO: move to a more general type for 'body-having' items + /// Builds a resolver for code inside this item. + pub fn resolver(&self, db: &impl HirDatabase) -> Resolver { + // take the outer scope... + let r = self + .impl_block(db) + .map(|ib| ib.resolver(db)) + .unwrap_or_else(|| self.module(db).resolver(db)); + r + } } impl Docs for Const { @@ -567,6 +582,23 @@ impl Docs for Const { } } +/// The declared signature of a const. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ConstSignature { + pub(crate) name: Name, + pub(crate) type_ref: TypeRef, +} + +impl ConstSignature { + pub fn name(&self) -> &Name { + &self.name + } + + pub fn type_ref(&self) -> &TypeRef { + &self.type_ref + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct Static { pub(crate) id: StaticId, diff --git a/crates/ra_hir/src/code_model_impl.rs b/crates/ra_hir/src/code_model_impl.rs index 1f28fab74..24df9a113 100644 --- a/crates/ra_hir/src/code_model_impl.rs +++ b/crates/ra_hir/src/code_model_impl.rs @@ -1,3 +1,4 @@ mod krate; // `crate` is invalid ident :( +mod konst; // `const` is invalid ident :( mod module; pub(crate) mod function; diff --git a/crates/ra_hir/src/code_model_impl/konst.rs b/crates/ra_hir/src/code_model_impl/konst.rs new file mode 100644 index 000000000..ee03e3374 --- /dev/null +++ b/crates/ra_hir/src/code_model_impl/konst.rs @@ -0,0 +1,26 @@ +use std::sync::Arc; + +use ra_syntax::ast::{NameOwner}; + +use crate::{ + Name, AsName, Const, ConstSignature, + type_ref::{TypeRef}, + PersistentHirDatabase, +}; + +impl ConstSignature { + pub(crate) fn const_signature_query( + db: &impl PersistentHirDatabase, + konst: Const, + ) -> Arc { + let (_, node) = konst.source(db); + + let name = node.name().map(|n| n.as_name()).unwrap_or_else(Name::missing); + + let type_ref = TypeRef::from_ast_opt(node.type_ref()); + + let sig = ConstSignature { name, type_ref }; + + Arc::new(sig) + } +} diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs index b8715abab..2be47c160 100644 --- a/crates/ra_hir/src/db.rs +++ b/crates/ra_hir/src/db.rs @@ -8,6 +8,7 @@ use crate::{ SourceFileItems, SourceItemId, Crate, Module, HirInterner, Function, FnSignature, ExprScopes, TypeAlias, Struct, Enum, StructField, + Const, ConstSignature, macros::MacroExpansion, module_tree::ModuleTree, nameres::{ItemMap, lower::{LoweredModule, ImportSourceMap}}, @@ -82,6 +83,9 @@ pub trait PersistentHirDatabase: SourceDatabase + AsRef { #[salsa::invoke(crate::type_alias::type_alias_ref_query)] fn type_alias_ref(&self, typ: TypeAlias) -> Arc; + + #[salsa::invoke(crate::ConstSignature::const_signature_query)] + fn const_signature(&self, konst: Const) -> Arc; } #[salsa::query_group(HirDatabaseStorage)] diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index aa5fc5607..edc1b4f57 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs @@ -71,6 +71,6 @@ pub use self::code_model_api::{ Struct, Enum, EnumVariant, Function, FnSignature, StructField, FieldSource, - Static, Const, + Static, Const, ConstSignature, Trait, TypeAlias, }; diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index 921130b71..f5f85308c 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs @@ -393,17 +393,22 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { // Attempt to find an impl_item for the type which has a name matching // the current segment log::debug!("looking for path segment: {:?}", segment); - let item = ty.iterate_impl_items(self.db, |item| match item { + let item: crate::ModuleDef = ty.iterate_impl_items(self.db, |item| match item { crate::ImplItem::Method(func) => { let sig = func.signature(self.db); if segment.name == *sig.name() { - return Some(func); + return Some(func.into()); } None } - // TODO: Resolve associated const - crate::ImplItem::Const(_) => None, + crate::ImplItem::Const(konst) => { + let sig = konst.signature(self.db); + if segment.name == *sig.name() { + return Some(konst.into()); + } + None + } // TODO: Resolve associated types crate::ImplItem::TypeAlias(_) => None, @@ -477,9 +482,10 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { let ty = self.insert_type_vars(ty.apply_substs(substs)); (ty, Some(var.into())) } - TypableDef::TypeAlias(_) | TypableDef::Function(_) | TypableDef::Enum(_) => { - (Ty::Unknown, None) - } + TypableDef::TypeAlias(_) + | TypableDef::Function(_) + | TypableDef::Enum(_) + | TypableDef::Const(_) => (Ty::Unknown, None), } } diff --git a/crates/ra_hir/src/ty/lower.rs b/crates/ra_hir/src/ty/lower.rs index b66b8e4a5..4a9b725d1 100644 --- a/crates/ra_hir/src/ty/lower.rs +++ b/crates/ra_hir/src/ty/lower.rs @@ -11,6 +11,7 @@ use std::sync::Arc; use crate::{ Function, Struct, StructField, Enum, EnumVariant, Path, Name, ModuleDef, TypeAlias, + Const, HirDatabase, type_ref::TypeRef, name::KnownName, @@ -125,6 +126,7 @@ impl Ty { TypableDef::Enum(e) => e.generic_params(db), TypableDef::EnumVariant(var) => var.parent_enum(db).generic_params(db), TypableDef::TypeAlias(t) => t.generic_params(db), + TypableDef::Const(_) => GenericParams::default().into(), }; let parent_param_count = def_generics.count_parent_params(); substs.extend((0..parent_param_count).map(|_| Ty::Unknown)); @@ -163,6 +165,7 @@ impl Ty { TypableDef::Function(_) | TypableDef::Struct(_) | TypableDef::Enum(_) + | TypableDef::Const(_) | TypableDef::TypeAlias(_) => last, TypableDef::EnumVariant(_) => { // the generic args for an enum variant may be either specified @@ -197,12 +200,14 @@ pub(crate) fn type_for_def(db: &impl HirDatabase, def: TypableDef, ns: Namespace (TypableDef::Enum(e), Namespace::Types) => type_for_enum(db, e), (TypableDef::EnumVariant(v), Namespace::Values) => type_for_enum_variant_constructor(db, v), (TypableDef::TypeAlias(t), Namespace::Types) => type_for_type_alias(db, t), + (TypableDef::Const(c), Namespace::Values) => type_for_const(db, c), // 'error' cases: (TypableDef::Function(_), Namespace::Types) => Ty::Unknown, (TypableDef::Enum(_), Namespace::Values) => Ty::Unknown, (TypableDef::EnumVariant(_), Namespace::Types) => Ty::Unknown, (TypableDef::TypeAlias(_), Namespace::Values) => Ty::Unknown, + (TypableDef::Const(_), Namespace::Types) => Ty::Unknown, } } @@ -233,6 +238,14 @@ fn type_for_fn(db: &impl HirDatabase, def: Function) -> Ty { Ty::FnDef { def: def.into(), sig, name, substs } } +/// Build the declared type of a const. +fn type_for_const(db: &impl HirDatabase, def: Const) -> Ty { + let signature = def.signature(db); + let resolver = def.resolver(db); + + Ty::from_hir(db, &resolver, signature.type_ref()) +} + /// Build the type of a tuple struct constructor. fn type_for_struct_constructor(db: &impl HirDatabase, def: Struct) -> Ty { let var_data = def.variant_data(db); @@ -318,8 +331,9 @@ pub enum TypableDef { Enum(Enum), EnumVariant(EnumVariant), TypeAlias(TypeAlias), + Const(Const), } -impl_froms!(TypableDef: Function, Struct, Enum, EnumVariant, TypeAlias); +impl_froms!(TypableDef: Function, Struct, Enum, EnumVariant, TypeAlias, Const); impl From for Option { fn from(def: ModuleDef) -> Option { @@ -329,10 +343,8 @@ impl From for Option { ModuleDef::Enum(e) => e.into(), ModuleDef::EnumVariant(v) => v.into(), ModuleDef::TypeAlias(t) => t.into(), - ModuleDef::Const(_) - | ModuleDef::Static(_) - | ModuleDef::Module(_) - | ModuleDef::Trait(_) => return None, + ModuleDef::Const(v) => v.into(), + ModuleDef::Static(_) | ModuleDef::Module(_) | ModuleDef::Trait(_) => return None, }; Some(res) } diff --git a/crates/ra_hir/src/ty/snapshots/tests__infer_associated_const.snap b/crates/ra_hir/src/ty/snapshots/tests__infer_associated_const.snap index 131d1fa16..51f3fd4c0 100644 --- a/crates/ra_hir/src/ty/snapshots/tests__infer_associated_const.snap +++ b/crates/ra_hir/src/ty/snapshots/tests__infer_associated_const.snap @@ -1,14 +1,14 @@ --- -created: "2019-02-21T21:51:46.497925200Z" +created: "2019-02-25T08:36:33.885804400Z" creator: insta@0.6.3 source: crates/ra_hir/src/ty/tests.rs expression: "&result" --- [227; 305) '{ ...:ID; }': () -[237; 238) 'x': [unknown] -[241; 252) 'Struct::FOO': [unknown] -[262; 263) 'y': [unknown] -[266; 275) 'Enum::BAR': [unknown] -[285; 286) 'z': [unknown] -[289; 302) 'TraitTest::ID': [unknown] +[237; 238) 'x': u32 +[241; 252) 'Struct::FOO': u32 +[262; 263) 'y': u32 +[266; 275) 'Enum::BAR': u32 +[285; 286) 'z': u32 +[289; 302) 'TraitTest::ID': u32 diff --git a/crates/ra_hir/src/ty/snapshots/tests__infer_const.snap b/crates/ra_hir/src/ty/snapshots/tests__infer_const.snap new file mode 100644 index 000000000..a5eba3980 --- /dev/null +++ b/crates/ra_hir/src/ty/snapshots/tests__infer_const.snap @@ -0,0 +1,14 @@ +--- +created: "2019-02-25T07:26:34.115351100Z" +creator: insta@0.6.3 +source: crates/ra_hir/src/ty/tests.rs +expression: "&result" +--- +[95; 213) '{ ...NST; }': () +[138; 139) 'x': [unknown] +[142; 153) 'LOCAL_CONST': [unknown] +[163; 164) 'z': u32 +[167; 179) 'GLOBAL_CONST': u32 +[189; 191) 'id': u32 +[194; 210) 'Foo::A..._CONST': u32 + diff --git a/crates/ra_hir/src/ty/snapshots/tests__infer_static.snap b/crates/ra_hir/src/ty/snapshots/tests__infer_static.snap new file mode 100644 index 000000000..ffe9ca66d --- /dev/null +++ b/crates/ra_hir/src/ty/snapshots/tests__infer_static.snap @@ -0,0 +1,16 @@ +--- +created: "2019-02-25T07:26:41.480764900Z" +creator: insta@0.6.3 +source: crates/ra_hir/src/ty/tests.rs +expression: "&result" +--- +[85; 280) '{ ...MUT; }': () +[173; 174) 'x': [unknown] +[177; 189) 'LOCAL_STATIC': [unknown] +[199; 200) 'y': [unknown] +[203; 219) 'LOCAL_...IC_MUT': [unknown] +[229; 230) 'z': [unknown] +[233; 246) 'GLOBAL_STATIC': [unknown] +[256; 257) 'w': [unknown] +[260; 277) 'GLOBAL...IC_MUT': [unknown] + diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 642259225..8de46a29e 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -1006,6 +1006,43 @@ mod foo { assert_eq!("i128", type_at_pos(&db, pos)); } +#[test] +fn infer_const() { + check_inference( + "infer_const", + r#" +struct Foo; +impl Foo { const ASSOC_CONST: u32 = 0; } +const GLOBAL_CONST: u32 = 101; +fn test() { + const LOCAL_CONST: u32 = 99; + let x = LOCAL_CONST; + let z = GLOBAL_CONST; + let id = Foo::ASSOC_CONST; +} +"#, + ); +} + +#[test] +fn infer_static() { + check_inference( + "infer_static", + r#" +static GLOBAL_STATIC: u32 = 101; +static mut GLOBAL_STATIC_MUT: u32 = 101; +fn test() { + static LOCAL_STATIC: u32 = 99; + static mut LOCAL_STATIC_MUT: u32 = 99; + let x = LOCAL_STATIC; + let y = LOCAL_STATIC_MUT; + let z = GLOBAL_STATIC; + let w = GLOBAL_STATIC_MUT; +} +"#, + ); +} + fn type_at_pos(db: &MockDatabase, pos: FilePosition) -> String { let func = source_binder::function_from_position(db, pos).unwrap(); let body_syntax_mapping = func.body_syntax_mapping(db); -- cgit v1.2.3