Add representations of associated types

This adds three different representations, copied from the Chalk model:
 - `Ty::Projection` is an associated type projection written somewhere in the
   code, like `<Foo as Trait>::Bar`.
 - `Ty::UnselectedProjection` is similar, but we don't know the trait
   yet (`Foo::Bar`).
 - The above representations are normalized to their actual types during type
   inference. When that isn't possible, for example for `T::Item` inside an `fn
   foo<T: Iterator>`, the type is normalized to an application type with
   `TypeCtor::AssociatedType`.
This commit is contained in:
Florian Diebold 2019-08-05 21:13:34 +02:00
parent 3a9a0bc968
commit 6cfdfdecba
3 changed files with 113 additions and 0 deletions

View file

@ -838,6 +838,10 @@ impl TypeAlias {
self.id.module(db)
}
pub fn krate(self, db: &impl DefDatabase) -> Option<Crate> {
self.module(db).krate(db)
}
/// The containing impl block, if this is a method.
pub fn impl_block(self, db: &impl DefDatabase) -> Option<ImplBlock> {
let module_impls = db.impls_in_module(self.module(db));

View file

@ -94,6 +94,12 @@ pub enum TypeCtor {
/// A tuple type. For example, `(i32, bool)`.
Tuple { cardinality: u16 },
/// Represents an associated item like `Iterator::Item`. This is used
/// when we have tried to normalize a projection like `T::Item` but
/// couldn't find a better representation. In that case, we generate
/// an **application type** like `(Iterator::Item)<T>`.
AssociatedType(TypeAlias),
}
/// A nominal type with (maybe 0) type parameters. This might be a primitive
@ -114,6 +120,12 @@ pub struct ProjectionTy {
pub parameters: Substs,
}
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
pub struct UnselectedProjectionTy {
pub type_name: Name,
pub parameters: Substs,
}
/// A type.
///
/// See also the `TyKind` enum in rustc (librustc/ty/sty.rs), which represents
@ -127,6 +139,18 @@ pub enum Ty {
/// several other things.
Apply(ApplicationTy),
/// A "projection" type corresponds to an (unnormalized)
/// projection like `<P0 as Trait<P1..Pn>>::Foo`. Note that the
/// trait and all its parameters are fully known.
Projection(ProjectionTy),
/// This is a variant of a projection in which the trait is
/// **not** known. It corresponds to a case where people write
/// `T::Item` without specifying the trait. We would then try to
/// figure out the trait by looking at all the traits that are in
/// scope.
UnselectedProjection(UnselectedProjectionTy),
/// A type parameter; for example, `T` in `fn f<T>(x: T) {}
Param {
/// The index of the parameter (starting with parameters from the
@ -352,6 +376,16 @@ impl Ty {
t.walk(f);
}
}
Ty::Projection(p_ty) => {
for t in p_ty.parameters.iter() {
t.walk(f);
}
}
Ty::UnselectedProjection(p_ty) => {
for t in p_ty.parameters.iter() {
t.walk(f);
}
}
Ty::Param { .. } | Ty::Bound(_) | Ty::Infer(_) | Ty::Unknown => {}
}
f(self);
@ -362,6 +396,12 @@ impl Ty {
Ty::Apply(a_ty) => {
a_ty.parameters.walk_mut(f);
}
Ty::Projection(p_ty) => {
p_ty.parameters.walk_mut(f);
}
Ty::UnselectedProjection(p_ty) => {
p_ty.parameters.walk_mut(f);
}
Ty::Param { .. } | Ty::Bound(_) | Ty::Infer(_) | Ty::Unknown => {}
}
f(self);
@ -572,7 +612,51 @@ impl HirDisplay for ApplicationTy {
write!(f, ">")?;
}
}
TypeCtor::AssociatedType(type_alias) => {
let trait_name = type_alias
.parent_trait(f.db)
.and_then(|t| t.name(f.db))
.unwrap_or_else(Name::missing);
let name = type_alias.name(f.db);
write!(f, "{}::{}", trait_name, name)?;
if self.parameters.len() > 0 {
write!(f, "<")?;
f.write_joined(&*self.parameters.0, ", ")?;
write!(f, ">")?;
}
}
}
Ok(())
}
}
impl HirDisplay for ProjectionTy {
fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result {
let trait_name = self
.associated_ty
.parent_trait(f.db)
.and_then(|t| t.name(f.db))
.unwrap_or_else(Name::missing);
write!(f, "<{} as {}", self.parameters[0].display(f.db), trait_name,)?;
if self.parameters.len() > 1 {
write!(f, "<")?;
f.write_joined(&self.parameters[1..], ", ")?;
write!(f, ">")?;
}
write!(f, ">::{}", self.associated_ty.name(f.db))?;
Ok(())
}
}
impl HirDisplay for UnselectedProjectionTy {
fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result {
write!(f, "{}", self.parameters[0].display(f.db))?;
if self.parameters.len() > 1 {
write!(f, "<")?;
f.write_joined(&self.parameters[1..], ", ")?;
write!(f, ">")?;
}
write!(f, "::{}", self.type_name)?;
Ok(())
}
}
@ -581,6 +665,8 @@ impl HirDisplay for Ty {
fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result {
match self {
Ty::Apply(a_ty) => a_ty.hir_fmt(f)?,
Ty::Projection(p_ty) => p_ty.hir_fmt(f)?,
Ty::UnselectedProjection(p_ty) => p_ty.hir_fmt(f)?,
Ty::Param { name, .. } => write!(f, "{}", name)?,
Ty::Bound(idx) => write!(f, "?{}", idx)?,
Ty::Unknown => write!(f, "{{unknown}}")?,

View file

@ -50,6 +50,19 @@ impl ToChalk for Ty {
let parameters = apply_ty.parameters.to_chalk(db);
chalk_ir::ApplicationTy { name, parameters }.cast()
}
Ty::Projection(proj_ty) => {
let associated_ty_id = proj_ty.associated_ty.to_chalk(db);
let parameters = proj_ty.parameters.to_chalk(db);
chalk_ir::ProjectionTy { associated_ty_id, parameters }.cast()
}
Ty::UnselectedProjection(proj_ty) => {
let type_name = lalrpop_intern::intern(&proj_ty.type_name.to_string());
let parameters = proj_ty.parameters.to_chalk(db);
chalk_ir::Ty::UnselectedProjection(chalk_ir::UnselectedProjectionTy {
type_name,
parameters,
})
}
Ty::Param { idx, .. } => {
PlaceholderIndex { ui: UniverseIndex::ROOT, idx: idx as usize }.to_ty()
}
@ -529,6 +542,16 @@ pub(crate) fn struct_datum_query(
adt.krate(db) != Some(krate),
)
}
TypeCtor::AssociatedType(type_alias) => {
let generic_params = type_alias.generic_params(db);
let bound_vars = Substs::bound_vars(&generic_params);
let where_clauses = convert_where_clauses(db, type_alias.into(), &bound_vars);
(
generic_params.count_params_including_parent(),
where_clauses,
type_alias.krate(db) != Some(krate),
)
}
};
let flags = chalk_rust_ir::StructFlags {
upstream,