diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index 821f919d47..9578c20b08 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs @@ -9,7 +9,7 @@ use hir_def::{ builtin_type::BuiltinType, docs::Documentation, per_ns::PerNs, - resolver::{HasResolver, TypeNs}, + resolver::HasResolver, type_ref::{Mutability, TypeRef}, AdtId, AstItemDef, ConstId, ContainerId, DefWithBodyId, EnumId, FunctionId, GenericDefId, HasModule, ImplId, LocalEnumVariantId, LocalImportId, LocalModuleId, LocalStructFieldId, @@ -737,64 +737,7 @@ impl Trait { } pub fn items(self, db: &impl DefDatabase) -> Vec { - db.trait_data(self.id).items.iter().map(|it| (*it).into()).collect() - } - - fn direct_super_traits(self, db: &impl HirDatabase) -> Vec { - let resolver = self.id.resolver(db); - // returning the iterator directly doesn't easily work because of - // lifetime problems, but since there usually shouldn't be more than a - // few direct traits this should be fine (we could even use some kind of - // SmallVec if performance is a concern) - db.generic_params(self.id.into()) - .where_predicates - .iter() - .filter_map(|pred| match &pred.type_ref { - TypeRef::Path(p) if p.as_ident() == Some(&name::SELF_TYPE) => pred.bound.as_path(), - _ => None, - }) - .filter_map(|path| match resolver.resolve_path_in_type_ns_fully(db, path) { - Some(TypeNs::TraitId(t)) => Some(t), - _ => None, - }) - .map(Trait::from) - .collect() - } - - /// Returns an iterator over the whole super trait hierarchy (including the - /// trait itself). - pub fn all_super_traits(self, db: &impl HirDatabase) -> Vec { - // we need to take care a bit here to avoid infinite loops in case of cycles - // (i.e. if we have `trait A: B; trait B: A;`) - let mut result = vec![self]; - let mut i = 0; - while i < result.len() { - let t = result[i]; - // yeah this is quadratic, but trait hierarchies should be flat - // enough that this doesn't matter - for tt in t.direct_super_traits(db) { - if !result.contains(&tt) { - result.push(tt); - } - } - i += 1; - } - result - } - - pub fn associated_type_by_name(self, db: &impl DefDatabase, name: &Name) -> Option { - let trait_data = db.trait_data(self.id); - let res = - trait_data.associated_types().map(TypeAlias::from).find(|t| &t.name(db) == name)?; - Some(res) - } - - pub fn associated_type_by_name_including_super_traits( - self, - db: &impl HirDatabase, - name: &Name, - ) -> Option { - self.all_super_traits(db).into_iter().find_map(|t| t.associated_type_by_name(db, name)) + db.trait_data(self.id).items.iter().map(|(_name, it)| (*it).into()).collect() } pub fn trait_ref(self, db: &impl HirDatabase) -> TraitRef { diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index bd03055b93..2a2dc26b49 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -9,6 +9,7 @@ mod op; mod lower; mod infer; pub(crate) mod display; +pub(crate) mod utils; #[cfg(test)] mod tests; diff --git a/crates/ra_hir/src/ty/autoderef.rs b/crates/ra_hir/src/ty/autoderef.rs index 9e7593b8b0..ae68234aca 100644 --- a/crates/ra_hir/src/ty/autoderef.rs +++ b/crates/ra_hir/src/ty/autoderef.rs @@ -10,7 +10,7 @@ use hir_expand::name; use log::{info, warn}; use ra_db::CrateId; -use crate::{db::HirDatabase, Trait}; +use crate::db::HirDatabase; use super::{ traits::{InEnvironment, Solution}, @@ -49,12 +49,12 @@ fn deref_by_trait( ty: InEnvironment<&Canonical>, ) -> Option> { let deref_trait = match db.lang_item(krate.into(), "deref".into())? { - LangItemTarget::TraitId(t) => Trait::from(t), + LangItemTarget::TraitId(it) => it, _ => return None, }; - let target = deref_trait.associated_type_by_name(db, &name::TARGET_TYPE)?; + let target = db.trait_data(deref_trait).associated_type_by_name(&name::TARGET_TYPE)?; - let generic_params = db.generic_params(target.id.into()); + let generic_params = db.generic_params(target.into()); if generic_params.count_params_including_parent() != 1 { // the Target type + Deref trait should only have one generic parameter, // namely Deref's Self type @@ -69,7 +69,7 @@ fn deref_by_trait( let projection = super::traits::ProjectionPredicate { ty: Ty::Bound(0), - projection_ty: super::ProjectionTy { associated_ty: target.id, parameters }, + projection_ty: super::ProjectionTy { associated_ty: target, parameters }, }; let obligation = super::Obligation::Projection(projection); diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index fce45321df..b023ae6901 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs @@ -43,7 +43,7 @@ use crate::{ db::HirDatabase, expr::{BindingAnnotation, Body, ExprId, PatId}, ty::infer::diagnostics::InferenceDiagnostic, - Adt, AssocItem, DefWithBody, FloatTy, Function, IntTy, Path, StructField, Trait, VariantDef, + Adt, AssocItem, DefWithBody, FloatTy, Function, IntTy, Path, StructField, VariantDef, }; macro_rules! ty_app { @@ -582,20 +582,20 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { fn resolve_into_iter_item(&self) -> Option { let path = known::std_iter_into_iterator(); - let trait_: Trait = self.resolver.resolve_known_trait(self.db, &path)?.into(); - trait_.associated_type_by_name(self.db, &name::ITEM_TYPE) + let trait_ = self.resolver.resolve_known_trait(self.db, &path)?; + self.db.trait_data(trait_).associated_type_by_name(&name::ITEM_TYPE).map(TypeAlias::from) } fn resolve_ops_try_ok(&self) -> Option { let path = known::std_ops_try(); - let trait_: Trait = self.resolver.resolve_known_trait(self.db, &path)?.into(); - trait_.associated_type_by_name(self.db, &name::OK_TYPE) + let trait_ = self.resolver.resolve_known_trait(self.db, &path)?; + self.db.trait_data(trait_).associated_type_by_name(&name::OK_TYPE).map(TypeAlias::from) } fn resolve_future_future_output(&self) -> Option { let path = known::std_future_future(); - let trait_: Trait = self.resolver.resolve_known_trait(self.db, &path)?.into(); - trait_.associated_type_by_name(self.db, &name::OUTPUT_TYPE) + let trait_ = self.resolver.resolve_known_trait(self.db, &path)?; + self.db.trait_data(trait_).associated_type_by_name(&name::OUTPUT_TYPE).map(TypeAlias::from) } fn resolve_boxed_box(&self) -> Option { diff --git a/crates/ra_hir/src/ty/lower.rs b/crates/ra_hir/src/ty/lower.rs index 2d23890a58..805a73ff5e 100644 --- a/crates/ra_hir/src/ty/lower.rs +++ b/crates/ra_hir/src/ty/lower.rs @@ -28,6 +28,7 @@ use crate::{ db::HirDatabase, ty::{ primitive::{FloatTy, IntTy}, + utils::{all_super_traits, associated_type_by_name_including_super_traits}, Adt, }, util::make_mut_slice, @@ -169,14 +170,16 @@ impl Ty { ); return if remaining_segments.len() == 1 { let segment = &remaining_segments[0]; - match trait_ref - .trait_ - .associated_type_by_name_including_super_traits(db, &segment.name) - { + let associated_ty = associated_type_by_name_including_super_traits( + db, + trait_ref.trait_.id, + &segment.name, + ); + match associated_ty { Some(associated_ty) => { // FIXME handle type parameters on the segment Ty::Projection(ProjectionTy { - associated_ty: associated_ty.id, + associated_ty, parameters: trait_ref.substs, }) } @@ -260,18 +263,16 @@ impl Ty { GenericPredicate::Implemented(tr) if tr.self_ty() == &self_ty => Some(tr.trait_), _ => None, }); - let traits = traits_from_env.flat_map(|t| t.all_super_traits(db)); + let traits = traits_from_env.flat_map(|t| all_super_traits(db, t.id)).map(Trait::from); for t in traits { - if let Some(associated_ty) = t.associated_type_by_name(db, &segment.name) { + if let Some(associated_ty) = db.trait_data(t.id).associated_type_by_name(&segment.name) + { let substs = Substs::build_for_def(db, t.id) .push(self_ty.clone()) .fill_with_unknown() .build(); // FIXME handle type parameters on the segment - return Ty::Projection(ProjectionTy { - associated_ty: associated_ty.id, - parameters: substs, - }); + return Ty::Projection(ProjectionTy { associated_ty, parameters: substs }); } } Ty::Unknown @@ -509,10 +510,11 @@ fn assoc_type_bindings_from_type_bound<'a>( .flat_map(|args_and_bindings| args_and_bindings.bindings.iter()) .map(move |(name, type_ref)| { let associated_ty = - match trait_ref.trait_.associated_type_by_name_including_super_traits(db, &name) { - None => return GenericPredicate::Error, - Some(t) => t.id, - }; + associated_type_by_name_including_super_traits(db, trait_ref.trait_.id, &name); + let associated_ty = match associated_ty { + None => return GenericPredicate::Error, + Some(t) => t, + }; let projection_ty = ProjectionTy { associated_ty, parameters: trait_ref.substs.clone() }; let ty = Ty::from_hir(db, resolver, type_ref); diff --git a/crates/ra_hir/src/ty/method_resolution.rs b/crates/ra_hir/src/ty/method_resolution.rs index 489fcd64b9..9988570e83 100644 --- a/crates/ra_hir/src/ty/method_resolution.rs +++ b/crates/ra_hir/src/ty/method_resolution.rs @@ -16,7 +16,7 @@ use rustc_hash::FxHashMap; use crate::{ db::HirDatabase, ty::primitive::{FloatBitness, Uncertain}, - ty::{Ty, TypeCtor}, + ty::{utils::all_super_traits, Ty, TypeCtor}, AssocItem, Crate, Function, Mutability, Name, Trait, }; @@ -249,7 +249,8 @@ fn iterate_trait_method_candidates( let traits_from_env = env .trait_predicates_for_self_ty(&ty.value) .map(|tr| tr.trait_) - .flat_map(|t| t.all_super_traits(db)); + .flat_map(|t| all_super_traits(db, t.id)) + .map(Trait::from); let traits = inherent_trait .chain(traits_from_env) .chain(resolver.traits_in_scope(db).into_iter().map(Trait::from)); @@ -260,8 +261,8 @@ fn iterate_trait_method_candidates( // trait, but if we find out it doesn't, we'll skip the rest of the // iteration let mut known_implemented = false; - for &item in data.items.iter() { - if !is_valid_candidate(db, name, mode, item.into()) { + for (_name, item) in data.items.iter() { + if !is_valid_candidate(db, name, mode, (*item).into()) { continue; } if !known_implemented { @@ -271,7 +272,7 @@ fn iterate_trait_method_candidates( } } known_implemented = true; - if let Some(result) = callback(&ty.value, item.into()) { + if let Some(result) = callback(&ty.value, (*item).into()) { return Some(result); } } diff --git a/crates/ra_hir/src/ty/traits/chalk.rs b/crates/ra_hir/src/ty/traits/chalk.rs index 06388a3ce2..78f4b3e27e 100644 --- a/crates/ra_hir/src/ty/traits/chalk.rs +++ b/crates/ra_hir/src/ty/traits/chalk.rs @@ -9,7 +9,7 @@ use chalk_ir::{ }; use chalk_rust_ir::{AssociatedTyDatum, AssociatedTyValue, ImplDatum, StructDatum, TraitDatum}; -use hir_def::{lang_item::LangItemTarget, ContainerId, GenericDefId, Lookup, TypeAliasId}; +use hir_def::{lang_item::LangItemTarget, ContainerId, GenericDefId, Lookup, TraitId, TypeAliasId}; use hir_expand::name; use ra_db::salsa::{InternId, InternKey}; @@ -459,7 +459,7 @@ where [super::FnTrait::FnOnce, super::FnTrait::FnMut, super::FnTrait::Fn].iter() { if let Some(actual_trait) = get_fn_trait(self.db, self.krate, fn_trait) { - if trait_ == actual_trait { + if trait_.id == actual_trait { let impl_ = super::ClosureFnTraitImplData { def, expr, fn_trait }; result.push(Impl::ClosureFnTraitImpl(impl_).to_chalk(self.db)); } @@ -661,6 +661,7 @@ fn impl_block_datum( }; let impl_datum_bound = chalk_rust_ir::ImplDatumBound { trait_ref, where_clauses }; + let trait_data = db.trait_data(trait_.id); let associated_ty_value_ids = impl_block .items(db) .into_iter() @@ -670,7 +671,7 @@ fn impl_block_datum( }) .filter(|type_alias| { // don't include associated types that don't exist in the trait - trait_.associated_type_by_name(db, &type_alias.name(db)).is_some() + trait_data.associated_type_by_name(&type_alias.name(db)).is_some() }) .map(|type_alias| AssocTyValue::TypeAlias(type_alias).to_chalk(db)) .collect(); @@ -713,7 +714,7 @@ fn closure_fn_trait_impl_datum( // and don't want to return a valid value only to find out later that FnOnce // is broken let fn_once_trait = get_fn_trait(db, krate, super::FnTrait::FnOnce)?; - fn_once_trait.associated_type_by_name(db, &name::OUTPUT_TYPE)?; + let _output = db.trait_data(fn_once_trait).associated_type_by_name(&name::OUTPUT_TYPE)?; let num_args: u16 = match &db.body(data.def.into())[data.expr] { crate::expr::Expr::Lambda { args, .. } => args.len() as u16, @@ -735,8 +736,8 @@ fn closure_fn_trait_impl_datum( let self_ty = Ty::apply_one(TypeCtor::Closure { def: data.def, expr: data.expr }, sig_ty); let trait_ref = TraitRef { - trait_, - substs: Substs::build_for_def(db, trait_.id).push(self_ty).push(arg_ty).build(), + trait_: trait_.into(), + substs: Substs::build_for_def(db, trait_).push(self_ty).push(arg_ty).build(), }; let output_ty_id = AssocTyValue::ClosureFnTraitImplOutput(data.clone()).to_chalk(db); @@ -783,10 +784,10 @@ fn type_alias_associated_ty_value( .target_trait_ref(db) .expect("assoc ty value should not exist") // we don't return any assoc ty values if the impl'd trait can't be resolved .trait_; - let assoc_ty = trait_ - .associated_type_by_name(db, &type_alias.name(db)) - .expect("assoc ty value should not exist") // validated when building the impl data as well - .id; + let assoc_ty = db + .trait_data(trait_.id) + .associated_type_by_name(&type_alias.name(db)) + .expect("assoc ty value should not exist"); // validated when building the impl data as well let generic_params = db.generic_params(impl_block.id.into()); let bound_vars = Substs::bound_vars(&generic_params); let ty = db.type_for_def(type_alias.into(), crate::ty::Namespace::Types).subst(&bound_vars); @@ -819,10 +820,10 @@ fn closure_fn_trait_output_assoc_ty_value( let fn_once_trait = get_fn_trait(db, krate, super::FnTrait::FnOnce).expect("assoc ty value should not exist"); - let output_ty_id = fn_once_trait - .associated_type_by_name(db, &name::OUTPUT_TYPE) - .expect("assoc ty value should not exist") - .id; + let output_ty_id = db + .trait_data(fn_once_trait) + .associated_type_by_name(&name::OUTPUT_TYPE) + .expect("assoc ty value should not exist"); let value_bound = chalk_rust_ir::AssociatedTyValueBound { ty: output_ty.to_chalk(db) }; @@ -834,10 +835,10 @@ fn closure_fn_trait_output_assoc_ty_value( Arc::new(value) } -fn get_fn_trait(db: &impl HirDatabase, krate: Crate, fn_trait: super::FnTrait) -> Option { +fn get_fn_trait(db: &impl HirDatabase, krate: Crate, fn_trait: super::FnTrait) -> Option { let target = db.lang_item(krate.crate_id, fn_trait.lang_item_name().into())?; match target { - LangItemTarget::TraitId(t) => Some(t.into()), + LangItemTarget::TraitId(t) => Some(t), _ => None, } } diff --git a/crates/ra_hir/src/ty/utils.rs b/crates/ra_hir/src/ty/utils.rs new file mode 100644 index 0000000000..52994b9e34 --- /dev/null +++ b/crates/ra_hir/src/ty/utils.rs @@ -0,0 +1,63 @@ +//! Helper functions for working with def, which don't need to be a separate +//! query, but can't be computed directly from `*Data` (ie, which need a `db`). + +use hir_def::{ + db::DefDatabase, + resolver::{HasResolver, TypeNs}, + type_ref::TypeRef, + TraitId, TypeAliasId, +}; +use hir_expand::name::{self, Name}; + +// FIXME: this is wrong, b/c it can't express `trait T: PartialEq<()>`. +// We should return a `TraitREf` here. +fn direct_super_traits(db: &impl DefDatabase, trait_: TraitId) -> Vec { + let resolver = trait_.resolver(db); + // returning the iterator directly doesn't easily work because of + // lifetime problems, but since there usually shouldn't be more than a + // few direct traits this should be fine (we could even use some kind of + // SmallVec if performance is a concern) + db.generic_params(trait_.into()) + .where_predicates + .iter() + .filter_map(|pred| match &pred.type_ref { + TypeRef::Path(p) if p.as_ident() == Some(&name::SELF_TYPE) => pred.bound.as_path(), + _ => None, + }) + .filter_map(|path| match resolver.resolve_path_in_type_ns_fully(db, path) { + Some(TypeNs::TraitId(t)) => Some(t), + _ => None, + }) + .collect() +} + +/// Returns an iterator over the whole super trait hierarchy (including the +/// trait itself). +pub(crate) fn all_super_traits(db: &impl DefDatabase, trait_: TraitId) -> Vec { + // we need to take care a bit here to avoid infinite loops in case of cycles + // (i.e. if we have `trait A: B; trait B: A;`) + let mut result = vec![trait_]; + let mut i = 0; + while i < result.len() { + let t = result[i]; + // yeah this is quadratic, but trait hierarchies should be flat + // enough that this doesn't matter + for tt in direct_super_traits(db, t) { + if !result.contains(&tt) { + result.push(tt); + } + } + i += 1; + } + result +} + +pub(crate) fn associated_type_by_name_including_super_traits( + db: &impl DefDatabase, + trait_: TraitId, + name: &Name, +) -> Option { + all_super_traits(db, trait_) + .into_iter() + .find_map(|t| db.trait_data(t).associated_type_by_name(name)) +} diff --git a/crates/ra_hir_def/src/data.rs b/crates/ra_hir_def/src/data.rs index 68bea34df7..813099a058 100644 --- a/crates/ra_hir_def/src/data.rs +++ b/crates/ra_hir_def/src/data.rs @@ -87,7 +87,7 @@ impl TypeAliasData { #[derive(Debug, Clone, PartialEq, Eq)] pub struct TraitData { pub name: Option, - pub items: Vec, + pub items: Vec<(Name, AssocItemId)>, pub auto: bool, } @@ -97,28 +97,42 @@ impl TraitData { let name = src.value.name().map(|n| n.as_name()); let auto = src.value.is_auto(); let ast_id_map = db.ast_id_map(src.file_id); + + let container = ContainerId::TraitId(tr); let items = if let Some(item_list) = src.value.item_list() { item_list .impl_items() .map(|item_node| match item_node { - ast::ImplItem::FnDef(it) => FunctionLoc { - container: ContainerId::TraitId(tr), - ast_id: AstId::new(src.file_id, ast_id_map.ast_id(&it)), + ast::ImplItem::FnDef(it) => { + let name = it.name().map(|it| it.as_name()).unwrap_or_else(Name::missing); + let def = FunctionLoc { + container, + ast_id: AstId::new(src.file_id, ast_id_map.ast_id(&it)), + } + .intern(db) + .into(); + (name, def) } - .intern(db) - .into(), - ast::ImplItem::ConstDef(it) => ConstLoc { - container: ContainerId::TraitId(tr), - ast_id: AstId::new(src.file_id, ast_id_map.ast_id(&it)), + ast::ImplItem::ConstDef(it) => { + let name = it.name().map(|it| it.as_name()).unwrap_or_else(Name::missing); + let def = ConstLoc { + container, + ast_id: AstId::new(src.file_id, ast_id_map.ast_id(&it)), + } + .intern(db) + .into(); + (name, def) } - .intern(db) - .into(), - ast::ImplItem::TypeAliasDef(it) => TypeAliasLoc { - container: ContainerId::TraitId(tr), - ast_id: AstId::new(src.file_id, ast_id_map.ast_id(&it)), + ast::ImplItem::TypeAliasDef(it) => { + let name = it.name().map(|it| it.as_name()).unwrap_or_else(Name::missing); + let def = TypeAliasLoc { + container, + ast_id: AstId::new(src.file_id, ast_id_map.ast_id(&it)), + } + .intern(db) + .into(); + (name, def) } - .intern(db) - .into(), }) .collect() } else { @@ -128,11 +142,18 @@ impl TraitData { } pub fn associated_types(&self) -> impl Iterator + '_ { - self.items.iter().filter_map(|item| match item { + self.items.iter().filter_map(|(_name, item)| match item { AssocItemId::TypeAliasId(t) => Some(*t), _ => None, }) } + + pub fn associated_type_by_name(&self, name: &Name) -> Option { + self.items.iter().find_map(|(item_name, item)| match item { + AssocItemId::TypeAliasId(t) if item_name == name => Some(*t), + _ => None, + }) + } } #[derive(Debug, Clone, PartialEq, Eq)]