Skip to content

Commit

Permalink
Merge #4162
Browse files Browse the repository at this point in the history
4162: Complete assoc. items on type parameters r=jonas-schievink a=jonas-schievink

This is fairly messy and seems to leak a lot through the `ra_hir` abstraction (`TypeNs`, `AssocItemId`, ...), so I'd be glad for any advise for how to improve this.

Co-authored-by: Jonas Schievink <jonasschievink@gmail.com>
  • Loading branch information
bors[bot] and jonas-schievink committed Apr 29, 2020
2 parents 4ff3573 + 0cd6a88 commit 913eff5
Show file tree
Hide file tree
Showing 5 changed files with 402 additions and 68 deletions.
10 changes: 10 additions & 0 deletions crates/ra_hir/src/code_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -953,6 +953,16 @@ impl TypeParam {
pub fn module(self, db: &dyn HirDatabase) -> Module {
self.id.parent.module(db.upcast()).into()
}

pub fn ty(self, db: &dyn HirDatabase) -> Type {
let resolver = self.id.parent.resolver(db.upcast());
let environment = TraitEnvironment::lower(db, &resolver);
let ty = Ty::Placeholder(self.id);
Type {
krate: self.id.parent.module(db.upcast()).krate,
ty: InEnvironment { value: ty, environment },
}
}
}

// FIXME: rename from `ImplDef` to `Impl`
Expand Down
42 changes: 41 additions & 1 deletion crates/ra_hir/src/semantics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use hir_def::{
AsMacroCall, TraitId,
};
use hir_expand::ExpansionInfo;
use hir_ty::associated_type_shorthand_candidates;
use itertools::Itertools;
use ra_db::{FileId, FileRange};
use ra_prof::profile;
Expand All @@ -24,8 +25,9 @@ use crate::{
semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx},
source_analyzer::{resolve_hir_path, SourceAnalyzer},
AssocItem, Field, Function, HirFileId, ImplDef, InFile, Local, MacroDef, Module, ModuleDef,
Name, Origin, Path, ScopeDef, Trait, Type, TypeParam,
Name, Origin, Path, ScopeDef, Trait, Type, TypeAlias, TypeParam,
};
use resolver::TypeNs;

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PathResolution {
Expand All @@ -40,6 +42,44 @@ pub enum PathResolution {
AssocItem(AssocItem),
}

impl PathResolution {
fn in_type_ns(&self) -> Option<TypeNs> {
match self {
PathResolution::Def(ModuleDef::Adt(adt)) => Some(TypeNs::AdtId((*adt).into())),
PathResolution::Def(ModuleDef::BuiltinType(builtin)) => {
Some(TypeNs::BuiltinType(*builtin))
}
PathResolution::Def(ModuleDef::Const(_))
| PathResolution::Def(ModuleDef::EnumVariant(_))
| PathResolution::Def(ModuleDef::Function(_))
| PathResolution::Def(ModuleDef::Module(_))
| PathResolution::Def(ModuleDef::Static(_))
| PathResolution::Def(ModuleDef::Trait(_)) => None,
PathResolution::Def(ModuleDef::TypeAlias(alias)) => {
Some(TypeNs::TypeAliasId((*alias).into()))
}
PathResolution::Local(_) | PathResolution::Macro(_) => None,
PathResolution::TypeParam(param) => Some(TypeNs::GenericParam((*param).into())),
PathResolution::SelfType(impl_def) => Some(TypeNs::SelfType((*impl_def).into())),
PathResolution::AssocItem(AssocItem::Const(_))
| PathResolution::AssocItem(AssocItem::Function(_)) => None,
PathResolution::AssocItem(AssocItem::TypeAlias(alias)) => {
Some(TypeNs::TypeAliasId((*alias).into()))
}
}
}

/// Returns an iterator over associated types that may be specified after this path (using
/// `Ty::Assoc` syntax).
pub fn assoc_type_shorthand_candidates<R>(
&self,
db: &dyn HirDatabase,
mut cb: impl FnMut(TypeAlias) -> Option<R>,
) -> Option<R> {
associated_type_shorthand_candidates(db, self.in_type_ns()?, |_, _, id| cb(id.into()))
}
}

/// Primary API to get semantic information, like types, from syntax trees.
pub struct Semantics<'db, DB> {
pub db: &'db DB,
Expand Down
3 changes: 2 additions & 1 deletion crates/ra_hir_ty/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ pub use autoderef::autoderef;
pub use infer::{InferTy, InferenceResult};
pub use lower::CallableDef;
pub use lower::{
callable_item_sig, ImplTraitLoweringMode, TyDefId, TyLoweringContext, ValueTyDefId,
associated_type_shorthand_candidates, callable_item_sig, ImplTraitLoweringMode, TyDefId,
TyLoweringContext, ValueTyDefId,
};
pub use traits::{InEnvironment, Obligation, ProjectionPredicate, TraitEnvironment};

Expand Down
143 changes: 88 additions & 55 deletions crates/ra_hir_ty/src/lower.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ use hir_def::{
path::{GenericArg, Path, PathSegment, PathSegments},
resolver::{HasResolver, Resolver, TypeNs},
type_ref::{TypeBound, TypeRef},
AdtId, AssocContainerId, ConstId, EnumId, EnumVariantId, FunctionId, GenericDefId, HasModule,
ImplId, LocalFieldId, Lookup, StaticId, StructId, TraitId, TypeAliasId, TypeParamId, UnionId,
VariantId,
AdtId, AssocContainerId, AssocItemId, ConstId, EnumId, EnumVariantId, FunctionId, GenericDefId,
HasModule, ImplId, LocalFieldId, Lookup, StaticId, StructId, TraitId, TypeAliasId, TypeParamId,
UnionId, VariantId,
};
use ra_arena::map::ArenaMap;
use ra_db::CrateId;
Expand All @@ -34,6 +34,7 @@ use crate::{
Binders, BoundVar, DebruijnIndex, FnSig, GenericPredicate, PolyFnSig, ProjectionPredicate,
ProjectionTy, Substs, TraitEnvironment, TraitRef, Ty, TypeCtor, TypeWalk,
};
use hir_expand::name::Name;

#[derive(Debug)]
pub struct TyLoweringContext<'a> {
Expand Down Expand Up @@ -383,61 +384,38 @@ impl Ty {
res: Option<TypeNs>,
segment: PathSegment<'_>,
) -> Ty {
let traits_from_env: Vec<_> = match res {
Some(TypeNs::SelfType(impl_id)) => match ctx.db.impl_trait(impl_id) {
None => return Ty::Unknown,
Some(trait_ref) => vec![trait_ref.value],
},
Some(TypeNs::GenericParam(param_id)) => {
let predicates = ctx.db.generic_predicates_for_param(param_id);
let mut traits_: Vec<_> = predicates
.iter()
.filter_map(|pred| match &pred.value {
GenericPredicate::Implemented(tr) => Some(tr.clone()),
_ => None,
})
.collect();
// Handle `Self::Type` referring to own associated type in trait definitions
if let GenericDefId::TraitId(trait_id) = param_id.parent {
let generics = generics(ctx.db.upcast(), trait_id.into());
if generics.params.types[param_id.local_id].provenance
== TypeParamProvenance::TraitSelf
{
let trait_ref = TraitRef {
trait_: trait_id,
substs: Substs::bound_vars(&generics, DebruijnIndex::INNERMOST),
if let Some(res) = res {
let ty =
associated_type_shorthand_candidates(ctx.db, res, move |name, t, associated_ty| {
if name == segment.name {
let substs = match ctx.type_param_mode {
TypeParamLoweringMode::Placeholder => {
// if we're lowering to placeholders, we have to put
// them in now
let s = Substs::type_params(
ctx.db,
ctx.resolver.generic_def().expect(
"there should be generics if there's a generic param",
),
);
t.substs.clone().subst_bound_vars(&s)
}
TypeParamLoweringMode::Variable => t.substs.clone(),
};
traits_.push(trait_ref);
// FIXME handle type parameters on the segment
return Some(Ty::Projection(ProjectionTy {
associated_ty,
parameters: substs,
}));
}
}
traits_
}
_ => return Ty::Unknown,
};
let traits = traits_from_env.into_iter().flat_map(|t| all_super_trait_refs(ctx.db, t));
for t in traits {
if let Some(associated_ty) =
ctx.db.trait_data(t.trait_).associated_type_by_name(&segment.name)
{
let substs = match ctx.type_param_mode {
TypeParamLoweringMode::Placeholder => {
// if we're lowering to placeholders, we have to put
// them in now
let s = Substs::type_params(
ctx.db,
ctx.resolver
.generic_def()
.expect("there should be generics if there's a generic param"),
);
t.substs.subst_bound_vars(&s)
}
TypeParamLoweringMode::Variable => t.substs,
};
// FIXME handle (forbid) type parameters on the segment
return Ty::Projection(ProjectionTy { associated_ty, parameters: substs });
}

None
});

ty.unwrap_or(Ty::Unknown)
} else {
Ty::Unknown
}
Ty::Unknown
}

fn from_hir_path_inner(
Expand Down Expand Up @@ -694,6 +672,61 @@ pub fn callable_item_sig(db: &dyn HirDatabase, def: CallableDef) -> PolyFnSig {
}
}

pub fn associated_type_shorthand_candidates<R>(
db: &dyn HirDatabase,
res: TypeNs,
mut cb: impl FnMut(&Name, &TraitRef, TypeAliasId) -> Option<R>,
) -> Option<R> {
let traits_from_env: Vec<_> = match res {
TypeNs::SelfType(impl_id) => match db.impl_trait(impl_id) {
None => vec![],
Some(trait_ref) => vec![trait_ref.value],
},
TypeNs::GenericParam(param_id) => {
let predicates = db.generic_predicates_for_param(param_id);
let mut traits_: Vec<_> = predicates
.iter()
.filter_map(|pred| match &pred.value {
GenericPredicate::Implemented(tr) => Some(tr.clone()),
_ => None,
})
.collect();
// Handle `Self::Type` referring to own associated type in trait definitions
if let GenericDefId::TraitId(trait_id) = param_id.parent {
let generics = generics(db.upcast(), trait_id.into());
if generics.params.types[param_id.local_id].provenance
== TypeParamProvenance::TraitSelf
{
let trait_ref = TraitRef {
trait_: trait_id,
substs: Substs::bound_vars(&generics, DebruijnIndex::INNERMOST),
};
traits_.push(trait_ref);
}
}
traits_
}
_ => vec![],
};

for t in traits_from_env.into_iter().flat_map(move |t| all_super_trait_refs(db, t)) {
let data = db.trait_data(t.trait_);

for (name, assoc_id) in &data.items {
match assoc_id {
AssocItemId::TypeAliasId(alias) => {
if let Some(result) = cb(name, &t, *alias) {
return Some(result);
}
}
AssocItemId::FunctionId(_) | AssocItemId::ConstId(_) => {}
}
}
}

None
}

/// Build the type of all specific fields of a struct or enum variant.
pub(crate) fn field_types_query(
db: &dyn HirDatabase,
Expand Down
Loading

0 comments on commit 913eff5

Please sign in to comment.