From b82db684003b817d47c1bc8c0d3c6afc88be2663 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sun, 17 Feb 2019 17:29:51 +0100 Subject: Handle tuple structs / enum variants properly in type inference --- crates/ra_hir/src/code_model_api.rs | 10 ++- crates/ra_hir/src/db.rs | 4 +- crates/ra_hir/src/nameres.rs | 4 +- crates/ra_hir/src/ty.rs | 96 +++++++++++++++++----- .../src/ty/snapshots/tests__infer_struct.snap | 18 ++-- 5 files changed, 95 insertions(+), 37 deletions(-) (limited to 'crates/ra_hir/src') diff --git a/crates/ra_hir/src/code_model_api.rs b/crates/ra_hir/src/code_model_api.rs index 26ebc445b..9da8a482d 100644 --- a/crates/ra_hir/src/code_model_api.rs +++ b/crates/ra_hir/src/code_model_api.rs @@ -290,7 +290,11 @@ impl Struct { } pub fn ty(&self, db: &impl HirDatabase) -> Ty { - db.type_for_def((*self).into()) + db.type_for_def((*self).into(), Namespace::Types) + } + + pub fn constructor_ty(&self, db: &impl HirDatabase) -> Ty { + db.type_for_def((*self).into(), Namespace::Values) } // TODO move to a more general type @@ -350,7 +354,7 @@ impl Enum { } pub fn ty(&self, db: &impl HirDatabase) -> Ty { - db.type_for_def((*self).into()) + db.type_for_def((*self).into(), Namespace::Types) } // TODO: move to a more general type @@ -425,7 +429,7 @@ pub struct Function { pub(crate) id: FunctionId, } -pub use crate::expr::ScopeEntryWithSyntax; +pub use crate::{ nameres::Namespace, expr::ScopeEntryWithSyntax}; /// The declared signature of a function. #[derive(Debug, Clone, PartialEq, Eq)] diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs index 740a7be7a..fc0ee068c 100644 --- a/crates/ra_hir/src/db.rs +++ b/crates/ra_hir/src/db.rs @@ -15,7 +15,7 @@ use crate::{ adt::{StructData, EnumData}, impl_block::{ModuleImplBlocks, ImplSourceMap}, generics::{GenericParams, GenericDef}, - ids::SourceFileItemId, + ids::SourceFileItemId, nameres::Namespace }; #[salsa::query_group(PersistentHirDatabaseStorage)] @@ -88,7 +88,7 @@ pub trait HirDatabase: PersistentHirDatabase { fn infer(&self, func: Function) -> Arc; #[salsa::invoke(crate::ty::type_for_def)] - fn type_for_def(&self, def: TypableDef) -> Ty; + fn type_for_def(&self, def: TypableDef, ns: Namespace) -> Ty; #[salsa::invoke(crate::ty::type_for_field)] fn type_for_field(&self, field: StructField) -> Ty; diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs index e35b4b129..bd920bfea 100644 --- a/crates/ra_hir/src/nameres.rs +++ b/crates/ra_hir/src/nameres.rs @@ -348,7 +348,7 @@ where .into_iter() .filter_map(|variant| { let res = Resolution { - def: PerNs::both(variant.into(), e.into()), + def: PerNs::both(variant.into(), variant.into()), import: Some(import_id), }; let name = variant.name(self.db)?; @@ -628,7 +628,7 @@ impl ItemMap { // enum variant tested_by!(item_map_enum_importing); match e.variant(db, &segment.name) { - Some(variant) => PerNs::both(variant.into(), (*e).into()), + Some(variant) => PerNs::both(variant.into(), variant.into()), None => PerNs::none(), } } diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index 562ad1f49..fb98ac907 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -42,7 +42,7 @@ use crate::{ generics::GenericParams, path::GenericArg, adt::VariantDef, - resolve::{Resolver, Resolution}, + resolve::{Resolver, Resolution}, nameres::Namespace }; /// The ID of a type variable. @@ -226,6 +226,8 @@ pub enum Ty { /// function has a unique type, which is output (for a function /// named `foo` returning an `i32`) as `fn() -> i32 {foo}`. /// + /// This includes tuple struct / enum variant constructors as well. + /// /// For example the type of `bar` here: /// /// ```rust @@ -233,8 +235,8 @@ pub enum Ty { /// let bar = foo; // bar: fn() -> i32 {foo} /// ``` FnDef { - // Function definition - def: Function, + /// The definition of the function / constructor. + def: CallableDef, /// For display name: Name, /// Parameters and return type @@ -396,7 +398,7 @@ impl Ty { None => return Ty::Unknown, Some(it) => it, }; - let ty = db.type_for_def(typable); + let ty = db.type_for_def(typable, Namespace::Types); let substs = Ty::substs_from_path(db, resolver, path, typable); ty.apply_substs(substs) } @@ -673,7 +675,47 @@ fn type_for_fn(db: &impl HirDatabase, def: Function) -> Ty { let output = Ty::from_hir(db, &resolver, signature.ret_type()); let sig = Arc::new(FnSig { input, output }); let substs = make_substs(&generics); - Ty::FnDef { def, sig, name, substs } + Ty::FnDef { def: def.into(), sig, name, substs } +} + +/// Compute 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); + let fields = match var_data.fields() { + Some(fields) => fields, + None => return type_for_struct(db, def), // Unit struct + }; + let resolver = def.resolver(db); + let generics = def.generic_params(db); + let name = def.name(db).unwrap_or_else(Name::missing); + let input = fields + .iter() + .map(|(_, field)| Ty::from_hir(db, &resolver, &field.type_ref)) + .collect::>(); + let output = type_for_struct(db, def); + let sig = Arc::new(FnSig { input, output }); + let substs = make_substs(&generics); + Ty::FnDef { def: def.into(), sig, name, substs } +} + +/// Compute the type of a tuple enum variant constructor. +fn type_for_enum_variant_constructor(db: &impl HirDatabase, def: EnumVariant) -> Ty { + let var_data = def.variant_data(db); + let fields = match var_data.fields() { + Some(fields) => fields, + None => return type_for_enum(db, def.parent_enum(db)), // Unit variant + }; + let resolver = def.parent_enum(db).resolver(db); + let generics = def.parent_enum(db).generic_params(db); + let name = def.name(db).unwrap_or_else(Name::missing); + let input = fields + .iter() + .map(|(_, field)| Ty::from_hir(db, &resolver, &field.type_ref)) + .collect::>(); + let output = type_for_enum(db, def.parent_enum(db)); + let sig = Arc::new(FnSig { input, output }); + let substs = make_substs(&generics); + Ty::FnDef { def: def.into(), sig, name, substs } } fn make_substs(generics: &GenericParams) -> Substs { @@ -703,12 +745,6 @@ pub(crate) fn type_for_enum(db: &impl HirDatabase, s: Enum) -> Ty { } } -pub(crate) fn type_for_enum_variant(db: &impl HirDatabase, ev: EnumVariant) -> Ty { - let enum_parent = ev.parent_enum(db); - - type_for_enum(db, enum_parent) -} - #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub enum TypableDef { Function(Function), @@ -735,12 +771,26 @@ impl From for Option { } } -pub(super) fn type_for_def(db: &impl HirDatabase, def: TypableDef) -> Ty { - match def { - TypableDef::Function(f) => type_for_fn(db, f), - TypableDef::Struct(s) => type_for_struct(db, s), - TypableDef::Enum(e) => type_for_enum(db, e), - TypableDef::EnumVariant(v) => type_for_enum_variant(db, v), +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub enum CallableDef { + Function(Function), + Struct(Struct), + EnumVariant(EnumVariant), +} +impl_froms!(CallableDef: Function, Struct, EnumVariant); + +pub(super) fn type_for_def(db: &impl HirDatabase, def: TypableDef, ns: Namespace) -> Ty { + match (def, ns) { + (TypableDef::Function(f), Namespace::Values) => type_for_fn(db, f), + (TypableDef::Struct(s), Namespace::Types) => type_for_struct(db, s), + (TypableDef::Struct(s), Namespace::Values) => type_for_struct_constructor(db, s), + (TypableDef::Enum(e), Namespace::Types) => type_for_enum(db, e), + (TypableDef::EnumVariant(v), Namespace::Values) => type_for_enum_variant_constructor(db, v), + + // 'error' cases: + (TypableDef::Function(_), Namespace::Types) => Ty::Unknown, + (TypableDef::Enum(_), Namespace::Values) => Ty::Unknown, + (TypableDef::EnumVariant(_), Namespace::Types) => Ty::Unknown, } } @@ -1127,7 +1177,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { let typable: Option = def.into(); let typable = typable?; let substs = Ty::substs_from_path(self.db, &self.resolver, path, typable); - let ty = self.db.type_for_def(typable).apply_substs(substs); + let ty = self.db.type_for_def(typable, Namespace::Values).apply_substs(substs); let ty = self.insert_type_vars(ty); Some(ty) } @@ -1178,12 +1228,12 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { let substs = Ty::substs_from_path(self.db, resolver, path, def); match def { TypableDef::Struct(s) => { - let ty = type_for_struct(self.db, s); + let ty = s.ty(self.db); let ty = self.insert_type_vars(ty.apply_substs(substs)); (ty, Some(s.into())) } TypableDef::EnumVariant(var) => { - let ty = type_for_enum_variant(self.db, var); + let ty = var.parent_enum(self.db).ty(self.db); let ty = self.insert_type_vars(ty.apply_substs(substs)); (ty, Some(var.into())) } @@ -1384,7 +1434,11 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { let (derefed_receiver_ty, method_ty, def_generics) = match resolved { Some((ty, func)) => { self.write_method_resolution(tgt_expr, func); - (ty, self.db.type_for_def(func.into()), Some(func.generic_params(self.db))) + ( + ty, + self.db.type_for_def(func.into(), Namespace::Values), + Some(func.generic_params(self.db)), + ) } None => (Ty::Unknown, receiver_ty, None), }; diff --git a/crates/ra_hir/src/ty/snapshots/tests__infer_struct.snap b/crates/ra_hir/src/ty/snapshots/tests__infer_struct.snap index 8747fae18..294186b06 100644 --- a/crates/ra_hir/src/ty/snapshots/tests__infer_struct.snap +++ b/crates/ra_hir/src/ty/snapshots/tests__infer_struct.snap @@ -1,21 +1,21 @@ --- -created: "2019-01-24T14:51:32.808861856+00:00" -creator: insta@0.5.2 -expression: "&result" +created: "2019-02-17T16:16:58.863630956Z" +creator: insta@0.6.2 source: crates/ra_hir/src/ty/tests.rs +expression: "&result" --- [72; 154) '{ ...a.c; }': () -[82; 83) 'c': [unknown] -[86; 87) 'C': C -[86; 90) 'C(1)': [unknown] -[88; 89) '1': i32 +[82; 83) 'c': C +[86; 87) 'C': fn C(usize) -> C +[86; 90) 'C(1)': C +[88; 89) '1': usize [96; 97) 'B': B [107; 108) 'a': A [114; 133) 'A { b:...C(1) }': A [121; 122) 'B': B -[127; 128) 'C': C +[127; 128) 'C': fn C(usize) -> C [127; 131) 'C(1)': C -[129; 130) '1': i32 +[129; 130) '1': usize [139; 140) 'a': A [139; 142) 'a.b': B [148; 149) 'a': A -- cgit v1.2.3