From 6cfdfdecbaed38534397f16e1ea1cda38b0b9395 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Mon, 5 Aug 2019 21:13:34 +0200 Subject: 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 `::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`, the type is normalized to an application type with `TypeCtor::AssociatedType`. --- crates/ra_hir/src/code_model.rs | 4 ++ crates/ra_hir/src/ty.rs | 86 ++++++++++++++++++++++++++++++++++++ crates/ra_hir/src/ty/traits/chalk.rs | 23 ++++++++++ 3 files changed, 113 insertions(+) (limited to 'crates/ra_hir') diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index 779764590..89fc1d1a1 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs @@ -838,6 +838,10 @@ impl TypeAlias { self.id.module(db) } + pub fn krate(self, db: &impl DefDatabase) -> Option { + self.module(db).krate(db) + } + /// The containing impl block, if this is a method. pub fn impl_block(self, db: &impl DefDatabase) -> Option { let module_impls = db.impls_in_module(self.module(db)); diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index 82589e504..f9cf3ec72 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -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)`. + 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 `>::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(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) -> 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) -> 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) -> 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}}")?, diff --git a/crates/ra_hir/src/ty/traits/chalk.rs b/crates/ra_hir/src/ty/traits/chalk.rs index 9e7ae0724..21055dcfd 100644 --- a/crates/ra_hir/src/ty/traits/chalk.rs +++ b/crates/ra_hir/src/ty/traits/chalk.rs @@ -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, -- cgit v1.2.3