From 966ab9abd2253e68d2e410a58dc1328805ee7f61 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Fri, 23 Aug 2019 14:29:30 +0200 Subject: Add test for assoc type bindings --- crates/ra_hir/src/ty/tests.rs | 65 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index cde9801f6..42bcae157 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -3552,6 +3552,71 @@ fn test() { ); } +#[test] +fn assoc_type_bindings() { + assert_snapshot!( + infer(r#" +trait Trait { + type Type; +} + +fn get(t: T) -> ::Type {} +fn get2>(t: T) -> U {} +fn set>(t: T) -> T {t} + +struct S; +impl Trait for S { type Type = T; } + +fn test>(x: T, y: impl Trait) { + get(x); + get2(x); + get(y); + get2(y); + get(set(S)); + get2(set(S)); + get2(S::); +} +"#), + @r###" + [50; 51) 't': T + [78; 80) '{}': () + [112; 113) 't': T + [123; 125) '{}': () + [155; 156) 't': T + [166; 169) '{t}': T + [167; 168) 't': T + [257; 258) 'x': T + [263; 264) 'y': impl Trait + [290; 398) '{ ...r>); }': () + [296; 299) 'get': fn get(T) -> ::Type + [296; 302) 'get(x)': {unknown} + [300; 301) 'x': T + [308; 312) 'get2': fn get2<{unknown}, T>(T) -> U + [308; 315) 'get2(x)': {unknown} + [313; 314) 'x': T + [321; 324) 'get': fn get(T) -> ::Type + [321; 327) 'get(y)': {unknown} + [325; 326) 'y': impl Trait + [333; 337) 'get2': fn get2<{unknown}, impl Trait>(T) -> U + [333; 340) 'get2(y)': {unknown} + [338; 339) 'y': impl Trait + [346; 349) 'get': fn get>(T) -> ::Type + [346; 357) 'get(set(S))': {unknown} + [350; 353) 'set': fn set>(T) -> T + [350; 356) 'set(S)': S<{unknown}> + [354; 355) 'S': S<{unknown}> + [363; 367) 'get2': fn get2<{unknown}, S<{unknown}>>(T) -> U + [363; 375) 'get2(set(S))': {unknown} + [368; 371) 'set': fn set>(T) -> T + [368; 374) 'set(S)': S<{unknown}> + [372; 373) 'S': S<{unknown}> + [381; 385) 'get2': fn get2<{unknown}, S>(T) -> U + [381; 395) 'get2(S::)': {unknown} + [386; 394) 'S::': S + "### + ); +} + fn type_at_pos(db: &MockDatabase, pos: FilePosition) -> String { let file = db.parse(pos.file_id).ok().unwrap(); let expr = algo::find_node_at_offset::(file.syntax(), pos.offset).unwrap(); -- cgit v1.2.3 From 741e350d4b7c3561f242207541ac9d7cab6ce45f Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Fri, 23 Aug 2019 17:19:37 +0200 Subject: Add support for associated type bindings (`where Trait`) --- crates/ra_hir/src/path.rs | 21 ++++++--- crates/ra_hir/src/ty.rs | 33 ++++++++++++++ crates/ra_hir/src/ty/lower.rs | 85 ++++++++++++++++++++++++------------ crates/ra_hir/src/ty/tests.rs | 62 ++++++++++++++++++-------- crates/ra_hir/src/ty/traits.rs | 27 ++++++++++++ crates/ra_hir/src/ty/traits/chalk.rs | 7 +++ 6 files changed, 183 insertions(+), 52 deletions(-) diff --git a/crates/ra_hir/src/path.rs b/crates/ra_hir/src/path.rs index 5ee71e421..24316fc91 100644 --- a/crates/ra_hir/src/path.rs +++ b/crates/ra_hir/src/path.rs @@ -31,7 +31,8 @@ pub struct GenericArgs { /// Self type. Otherwise, when we have a path `Trait`, the Self type /// is left out. pub has_self_type: bool, - // someday also bindings + /// Associated type bindings like in `Iterator`. + pub bindings: Vec<(Name, TypeRef)>, } /// A single generic argument. @@ -170,16 +171,24 @@ impl GenericArgs { let type_ref = TypeRef::from_ast_opt(type_arg.type_ref()); args.push(GenericArg::Type(type_ref)); } - // lifetimes and assoc type args ignored for now - if !args.is_empty() { - Some(GenericArgs { args, has_self_type: false }) - } else { + // lifetimes ignored for now + let mut bindings = Vec::new(); + for assoc_type_arg in node.assoc_type_args() { + if let Some(name_ref) = assoc_type_arg.name_ref() { + let name = name_ref.as_name(); + let type_ref = TypeRef::from_ast_opt(assoc_type_arg.type_ref()); + bindings.push((name, type_ref)); + } + } + if args.is_empty() && bindings.is_empty() { None + } else { + Some(GenericArgs { args, has_self_type: false, bindings }) } } pub(crate) fn empty() -> GenericArgs { - GenericArgs { args: Vec::new(), has_self_type: false } + GenericArgs { args: Vec::new(), has_self_type: false, bindings: Vec::new() } } } diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index b54c80318..035491bff 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -120,12 +120,32 @@ pub struct ProjectionTy { pub parameters: Substs, } +impl ProjectionTy { + pub fn walk(&self, f: &mut impl FnMut(&Ty)) { + self.parameters.walk(f); + } + + pub fn walk_mut(&mut self, f: &mut impl FnMut(&mut Ty)) { + self.parameters.walk_mut(f); + } +} + #[derive(Clone, PartialEq, Eq, Debug, Hash)] pub struct UnselectedProjectionTy { pub type_name: Name, pub parameters: Substs, } +impl UnselectedProjectionTy { + pub fn walk(&self, f: &mut impl FnMut(&Ty)) { + self.parameters.walk(f); + } + + pub fn walk_mut(&mut self, f: &mut impl FnMut(&mut Ty)) { + self.parameters.walk_mut(f); + } +} + /// A type. /// /// See also the `TyKind` enum in rustc (librustc/ty/sty.rs), which represents @@ -306,6 +326,8 @@ impl TraitRef { pub enum GenericPredicate { /// The given trait needs to be implemented for its type parameters. Implemented(TraitRef), + /// An associated type bindings like in `Iterator`. + Projection(ProjectionPredicate), /// We couldn't resolve the trait reference. (If some type parameters can't /// be resolved, they will just be Unknown). Error, @@ -324,6 +346,9 @@ impl GenericPredicate { GenericPredicate::Implemented(trait_ref) => { GenericPredicate::Implemented(trait_ref.subst(substs)) } + GenericPredicate::Projection(projection_predicate) => { + GenericPredicate::Projection(projection_predicate.subst(substs)) + } GenericPredicate::Error => self, } } @@ -331,6 +356,7 @@ impl GenericPredicate { pub fn walk(&self, f: &mut impl FnMut(&Ty)) { match self { GenericPredicate::Implemented(trait_ref) => trait_ref.walk(f), + GenericPredicate::Projection(projection_pred) => projection_pred.walk(f), GenericPredicate::Error => {} } } @@ -338,6 +364,7 @@ impl GenericPredicate { pub fn walk_mut(&mut self, f: &mut impl FnMut(&mut Ty)) { match self { GenericPredicate::Implemented(trait_ref) => trait_ref.walk_mut(f), + GenericPredicate::Projection(projection_pred) => projection_pred.walk_mut(f), GenericPredicate::Error => {} } } @@ -754,6 +781,9 @@ impl HirDisplay for Ty { GenericPredicate::Implemented(trait_ref) => { trait_ref.hir_fmt_ext(f, false)? } + GenericPredicate::Projection(_projection_pred) => { + // TODO show something + } GenericPredicate::Error => p.hir_fmt(f)?, } } @@ -800,6 +830,9 @@ impl HirDisplay for GenericPredicate { fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { match self { GenericPredicate::Implemented(trait_ref) => trait_ref.hir_fmt(f)?, + GenericPredicate::Projection(projection_pred) => { + // TODO print something + } GenericPredicate::Error => write!(f, "{{error}}")?, } Ok(()) diff --git a/crates/ra_hir/src/ty/lower.rs b/crates/ra_hir/src/ty/lower.rs index 47d161277..0011c06b4 100644 --- a/crates/ra_hir/src/ty/lower.rs +++ b/crates/ra_hir/src/ty/lower.rs @@ -8,7 +8,9 @@ use std::iter; use std::sync::Arc; -use super::{FnSig, GenericPredicate, ProjectionTy, Substs, TraitRef, Ty, TypeCtor}; +use super::{ + FnSig, GenericPredicate, ProjectionPredicate, ProjectionTy, Substs, TraitRef, Ty, TypeCtor, +}; use crate::{ adt::VariantDef, generics::HasGenericParams, @@ -62,7 +64,9 @@ impl Ty { let self_ty = Ty::Bound(0); let predicates = bounds .iter() - .map(|b| GenericPredicate::from_type_bound(db, resolver, b, self_ty.clone())) + .flat_map(|b| { + GenericPredicate::from_type_bound(db, resolver, b, self_ty.clone()) + }) .collect::>(); Ty::Dyn(predicates.into()) } @@ -70,7 +74,9 @@ impl Ty { let self_ty = Ty::Bound(0); let predicates = bounds .iter() - .map(|b| GenericPredicate::from_type_bound(db, resolver, b, self_ty.clone())) + .flat_map(|b| { + GenericPredicate::from_type_bound(db, resolver, b, self_ty.clone()) + }) .collect::>(); Ty::Opaque(predicates.into()) } @@ -326,15 +332,6 @@ impl TraitRef { TraitRef { trait_, substs } } - pub(crate) fn from_where_predicate( - db: &impl HirDatabase, - resolver: &Resolver, - pred: &WherePredicate, - ) -> Option { - let self_ty = Ty::from_hir(db, resolver, &pred.type_ref); - TraitRef::from_type_bound(db, resolver, &pred.bound, self_ty) - } - pub(crate) fn from_type_bound( db: &impl HirDatabase, resolver: &Resolver, @@ -349,26 +346,58 @@ impl TraitRef { } impl GenericPredicate { - pub(crate) fn from_where_predicate( - db: &impl HirDatabase, - resolver: &Resolver, - where_predicate: &WherePredicate, - ) -> GenericPredicate { - TraitRef::from_where_predicate(db, &resolver, where_predicate) - .map_or(GenericPredicate::Error, GenericPredicate::Implemented) + pub(crate) fn from_where_predicate<'a>( + db: &'a impl HirDatabase, + resolver: &'a Resolver, + where_predicate: &'a WherePredicate, + ) -> impl Iterator + 'a { + let self_ty = Ty::from_hir(db, resolver, &where_predicate.type_ref); + GenericPredicate::from_type_bound(db, resolver, &where_predicate.bound, self_ty) } - pub(crate) fn from_type_bound( - db: &impl HirDatabase, - resolver: &Resolver, - bound: &TypeBound, + pub(crate) fn from_type_bound<'a>( + db: &'a impl HirDatabase, + resolver: &'a Resolver, + bound: &'a TypeBound, self_ty: Ty, - ) -> GenericPredicate { - TraitRef::from_type_bound(db, &resolver, bound, self_ty) - .map_or(GenericPredicate::Error, GenericPredicate::Implemented) + ) -> impl Iterator + 'a { + let trait_ref = TraitRef::from_type_bound(db, &resolver, bound, self_ty); + iter::once(trait_ref.clone().map_or(GenericPredicate::Error, GenericPredicate::Implemented)) + .chain( + trait_ref.into_iter().flat_map(move |tr| { + assoc_type_bindings_from_type_bound(db, resolver, bound, tr) + }), + ) } } +fn assoc_type_bindings_from_type_bound<'a>( + db: &'a impl HirDatabase, + resolver: &'a Resolver, + bound: &'a TypeBound, + trait_ref: TraitRef, +) -> impl Iterator + 'a { + let last_segment = match bound { + TypeBound::Path(path) => path.segments.last(), + TypeBound::Error => None, + }; + last_segment + .into_iter() + .flat_map(|segment| segment.args_and_bindings.iter()) + .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(db, name.clone()) { + 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); + let projection_predicate = ProjectionPredicate { projection_ty, ty }; + GenericPredicate::Projection(projection_predicate) + }) +} + /// Build the declared type of an item. This depends on the namespace; e.g. for /// `struct Foo(usize)`, we have two types: The type of the struct itself, and /// the constructor function `(usize) -> Foo` which lives in the values @@ -425,7 +454,7 @@ pub(crate) fn trait_env( ) -> Arc { let predicates = resolver .where_predicates_in_scope() - .map(|pred| GenericPredicate::from_where_predicate(db, &resolver, pred)) + .flat_map(|pred| GenericPredicate::from_where_predicate(db, &resolver, pred)) .collect::>(); Arc::new(super::TraitEnvironment { predicates }) @@ -439,7 +468,7 @@ pub(crate) fn generic_predicates_query( let resolver = def.resolver(db); let predicates = resolver .where_predicates_in_scope() - .map(|pred| GenericPredicate::from_where_predicate(db, &resolver, pred)) + .flat_map(|pred| GenericPredicate::from_where_predicate(db, &resolver, pred)) .collect::>(); predicates.into() } diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 42bcae157..3e743ef58 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -3586,37 +3586,63 @@ fn test>(x: T, y: impl Trait) { [166; 169) '{t}': T [167; 168) 't': T [257; 258) 'x': T - [263; 264) 'y': impl Trait + [263; 264) 'y': impl Trait + [290; 398) '{ ...r>); }': () [296; 299) 'get': fn get(T) -> ::Type [296; 302) 'get(x)': {unknown} [300; 301) 'x': T - [308; 312) 'get2': fn get2<{unknown}, T>(T) -> U + [308; 312) 'get2': fn get2<{unknown}, S<{unknown}>>(T) -> U [308; 315) 'get2(x)': {unknown} [313; 314) 'x': T - [321; 324) 'get': fn get(T) -> ::Type + [321; 324) 'get': fn get(T) -> ::Type [321; 327) 'get(y)': {unknown} - [325; 326) 'y': impl Trait - [333; 337) 'get2': fn get2<{unknown}, impl Trait>(T) -> U + [325; 326) 'y': impl Trait + + [333; 337) 'get2': fn get2<{unknown}, S<{unknown}>>(T) -> U [333; 340) 'get2(y)': {unknown} - [338; 339) 'y': impl Trait - [346; 349) 'get': fn get>(T) -> ::Type - [346; 357) 'get(set(S))': {unknown} - [350; 353) 'set': fn set>(T) -> T - [350; 356) 'set(S)': S<{unknown}> - [354; 355) 'S': S<{unknown}> - [363; 367) 'get2': fn get2<{unknown}, S<{unknown}>>(T) -> U - [363; 375) 'get2(set(S))': {unknown} - [368; 371) 'set': fn set>(T) -> T - [368; 374) 'set(S)': S<{unknown}> - [372; 373) 'S': S<{unknown}> - [381; 385) 'get2': fn get2<{unknown}, S>(T) -> U - [381; 395) 'get2(S::)': {unknown} + [338; 339) 'y': impl Trait + + [346; 349) 'get': fn get>(T) -> ::Type + [346; 357) 'get(set(S))': u64 + [350; 353) 'set': fn set>(T) -> T + [350; 356) 'set(S)': S + [354; 355) 'S': S + [363; 367) 'get2': fn get2>(T) -> U + [363; 375) 'get2(set(S))': u64 + [368; 371) 'set': fn set>(T) -> T + [368; 374) 'set(S)': S + [372; 373) 'S': S + [381; 385) 'get2': fn get2>(T) -> U + [381; 395) 'get2(S::)': str [386; 394) 'S::': S "### ); } +#[test] +fn projection_eq_within_chalk() { + // std::env::set_var("CHALK_DEBUG", "1"); + assert_snapshot!( + infer(r#" +trait Trait1 { + type Type; +} +trait Trait2 { + fn foo(self) -> T; +} +impl Trait2 for U where U: Trait1 {} + +fn test>(x: T) { + x.foo(); +} +"#), + @r###" + [62; 66) 'self': Self + [164; 165) 'x': T + [170; 186) '{ ...o(); }': () + [176; 177) 'x': T + [176; 183) 'x.foo()': {unknown} + "### + ); +} fn type_at_pos(db: &MockDatabase, pos: FilePosition) -> String { let file = db.parse(pos.file_id).ok().unwrap(); let expr = algo::find_node_at_offset::(file.syntax(), pos.offset).unwrap(); diff --git a/crates/ra_hir/src/ty/traits.rs b/crates/ra_hir/src/ty/traits.rs index b634f0b79..25316bc02 100644 --- a/crates/ra_hir/src/ty/traits.rs +++ b/crates/ra_hir/src/ty/traits.rs @@ -124,6 +124,9 @@ impl Obligation { pub fn from_predicate(predicate: GenericPredicate) -> Option { match predicate { GenericPredicate::Implemented(trait_ref) => Some(Obligation::Trait(trait_ref)), + GenericPredicate::Projection(projection_pred) => { + Some(Obligation::Projection(projection_pred)) + } GenericPredicate::Error => None, } } @@ -135,6 +138,30 @@ pub struct ProjectionPredicate { pub ty: Ty, } +impl ProjectionPredicate { + pub fn subst(mut self, substs: &super::Substs) -> ProjectionPredicate { + self.walk_mut(&mut |ty| match ty { + Ty::Param { idx, .. } => { + if let Some(t) = substs.get(*idx as usize).cloned() { + *ty = t; + } + } + _ => {} + }); + self + } + + pub fn walk(&self, f: &mut impl FnMut(&Ty)) { + self.projection_ty.walk(f); + self.ty.walk(f); + } + + pub fn walk_mut(&mut self, f: &mut impl FnMut(&mut Ty)) { + self.projection_ty.walk_mut(f); + self.ty.walk_mut(f); + } +} + /// Solve a trait goal using Chalk. pub(crate) fn trait_solve_query( db: &impl HirDatabase, diff --git a/crates/ra_hir/src/ty/traits/chalk.rs b/crates/ra_hir/src/ty/traits/chalk.rs index 2ebc06135..3ab5b7cca 100644 --- a/crates/ra_hir/src/ty/traits/chalk.rs +++ b/crates/ra_hir/src/ty/traits/chalk.rs @@ -211,6 +211,13 @@ impl ToChalk for GenericPredicate { GenericPredicate::Implemented(trait_ref) => { make_binders(chalk_ir::WhereClause::Implemented(trait_ref.to_chalk(db)), 0) } + GenericPredicate::Projection(projection_pred) => make_binders( + chalk_ir::WhereClause::ProjectionEq(chalk_ir::ProjectionEq { + projection: projection_pred.projection_ty.to_chalk(db), + ty: projection_pred.ty.to_chalk(db), + }), + 0, + ), GenericPredicate::Error => { let impossible_trait_ref = chalk_ir::TraitRef { trait_id: UNKNOWN_TRAIT, -- cgit v1.2.3 From c4fcfa2b0d516b9790fa8abdf96bb2308657d60a Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sun, 25 Aug 2019 13:57:03 +0200 Subject: Properly format `impl Trait` types It's a bit complicated because we basically have to 'undo' the desugaring, and the result is very dependent on the specifics of the desugaring and will probably produce weird results otherwise. --- crates/ra_hir/src/ty.rs | 112 +++++++++++++++++++++++++++++++++++------- crates/ra_hir/src/ty/tests.rs | 8 +-- 2 files changed, 97 insertions(+), 23 deletions(-) diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index 035491bff..c0c609d78 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -121,6 +121,16 @@ pub struct ProjectionTy { } impl ProjectionTy { + pub fn trait_ref(&self, db: &impl HirDatabase) -> TraitRef { + TraitRef { + trait_: self + .associated_ty + .parent_trait(db) + .expect("projection ty without parent trait"), + substs: self.parameters.clone(), + } + } + pub fn walk(&self, f: &mut impl FnMut(&Ty)) { self.parameters.walk(f); } @@ -341,6 +351,21 @@ impl GenericPredicate { } } + pub fn is_implemented(&self) -> bool { + match self { + GenericPredicate::Implemented(_) => true, + _ => false, + } + } + + pub fn trait_ref(&self, db: &impl HirDatabase) -> Option { + match self { + GenericPredicate::Implemented(tr) => Some(tr.clone()), + GenericPredicate::Projection(proj) => Some(proj.projection_ty.trait_ref(db)), + GenericPredicate::Error => None, + } + } + pub fn subst(self, substs: &Substs) -> GenericPredicate { match self { GenericPredicate::Implemented(trait_ref) => { @@ -769,23 +794,66 @@ impl HirDisplay for Ty { Ty::Opaque(_) => write!(f, "impl ")?, _ => unreachable!(), }; - // looping by hand here just to format the bounds in a slightly nicer way + // Note: This code is written to produce nice results (i.e. + // corresponding to surface Rust) for types that can occur in + // actual Rust. It will have weird results if the predicates + // aren't as expected (i.e. self types = $0, projection + // predicates for a certain trait come after the Implemented + // predicate for that trait). let mut first = true; + let mut angle_open = false; for p in predicates.iter() { - if !first { - write!(f, " + ")?; - } - first = false; match p { - // don't show the $0 self type GenericPredicate::Implemented(trait_ref) => { - trait_ref.hir_fmt_ext(f, false)? + if angle_open { + write!(f, ">")?; + } + if !first { + write!(f, " + ")?; + } + // We assume that the self type is $0 (i.e. the + // existential) here, which is the only thing that's + // possible in actual Rust, and hence don't print it + write!( + f, + "{}", + trait_ref.trait_.name(f.db).unwrap_or_else(Name::missing) + )?; + if trait_ref.substs.len() > 1 { + write!(f, "<")?; + f.write_joined(&trait_ref.substs[1..], ", ")?; + // there might be assoc type bindings, so we leave the angle brackets open + angle_open = true; + } } - GenericPredicate::Projection(_projection_pred) => { - // TODO show something + GenericPredicate::Projection(projection_pred) => { + // in types in actual Rust, these will always come + // after the corresponding Implemented predicate + if angle_open { + write!(f, ", ")?; + } else { + write!(f, "<")?; + angle_open = true; + } + let name = projection_pred.projection_ty.associated_ty.name(f.db); + write!(f, "{} = ", name)?; + projection_pred.ty.hir_fmt(f)?; + } + GenericPredicate::Error => { + if angle_open { + // impl Trait + write!(f, ", ")?; + } else if !first { + // impl Trait + {error} + write!(f, " + ")?; + } + p.hir_fmt(f)?; } - GenericPredicate::Error => p.hir_fmt(f)?, } + first = false; + } + if angle_open { + write!(f, ">")?; } } Ty::Unknown => write!(f, "{{unknown}}")?, @@ -796,13 +864,12 @@ impl HirDisplay for Ty { } impl TraitRef { - fn hir_fmt_ext( - &self, - f: &mut HirFormatter, - with_self_ty: bool, - ) -> fmt::Result { - if with_self_ty { - write!(f, "{}: ", self.substs[0].display(f.db),)?; + fn hir_fmt_ext(&self, f: &mut HirFormatter, use_as: bool) -> fmt::Result { + self.substs[0].hir_fmt(f)?; + if use_as { + write!(f, " as ")?; + } else { + write!(f, ": ")?; } write!(f, "{}", self.trait_.name(f.db).unwrap_or_else(Name::missing))?; if self.substs.len() > 1 { @@ -816,7 +883,7 @@ impl TraitRef { impl HirDisplay for TraitRef { fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { - self.hir_fmt_ext(f, true) + self.hir_fmt_ext(f, false) } } @@ -831,7 +898,14 @@ impl HirDisplay for GenericPredicate { match self { GenericPredicate::Implemented(trait_ref) => trait_ref.hir_fmt(f)?, GenericPredicate::Projection(projection_pred) => { - // TODO print something + write!(f, "<")?; + projection_pred.projection_ty.trait_ref(f.db).hir_fmt_ext(f, true)?; + write!( + f, + ">::{} = {}", + projection_pred.projection_ty.associated_ty.name(f.db), + projection_pred.ty.display(f.db) + )?; } GenericPredicate::Error => write!(f, "{{error}}")?, } diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 3e743ef58..d92d4659b 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -3586,7 +3586,7 @@ fn test>(x: T, y: impl Trait) { [166; 169) '{t}': T [167; 168) 't': T [257; 258) 'x': T - [263; 264) 'y': impl Trait + + [263; 264) 'y': impl Trait [290; 398) '{ ...r>); }': () [296; 299) 'get': fn get(T) -> ::Type [296; 302) 'get(x)': {unknown} @@ -3594,12 +3594,12 @@ fn test>(x: T, y: impl Trait) { [308; 312) 'get2': fn get2<{unknown}, S<{unknown}>>(T) -> U [308; 315) 'get2(x)': {unknown} [313; 314) 'x': T - [321; 324) 'get': fn get(T) -> ::Type + [321; 324) 'get': fn get>(T) -> ::Type [321; 327) 'get(y)': {unknown} - [325; 326) 'y': impl Trait + + [325; 326) 'y': impl Trait [333; 337) 'get2': fn get2<{unknown}, S<{unknown}>>(T) -> U [333; 340) 'get2(y)': {unknown} - [338; 339) 'y': impl Trait + + [338; 339) 'y': impl Trait [346; 349) 'get': fn get>(T) -> ::Type [346; 357) 'get(set(S))': u64 [350; 353) 'set': fn set>(T) -> T -- cgit v1.2.3 From b8c1e402fa3a40c7e979750d60d7b003f9cb7b0d Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Tue, 3 Sep 2019 13:10:00 +0200 Subject: Make type walking infrastructure a bit nicer If/when we switch to using Chalk's Ty, we'll need to replace this by its `Fold` trait, but I didn't want to import the whole thing just yet. --- crates/ra_cli/src/analysis_stats.rs | 2 +- crates/ra_hir/src/lib.rs | 4 +- crates/ra_hir/src/ty.rs | 233 ++++++++++++----------- crates/ra_hir/src/ty/autoderef.rs | 2 +- crates/ra_hir/src/ty/infer.rs | 2 +- crates/ra_hir/src/ty/infer/unify.rs | 2 +- crates/ra_hir/src/ty/lower.rs | 1 + crates/ra_hir/src/ty/traits.rs | 20 +- crates/ra_hir/src/ty/traits/chalk.rs | 1 + crates/ra_ide_api/src/completion/presentation.rs | 2 +- 10 files changed, 134 insertions(+), 135 deletions(-) diff --git a/crates/ra_cli/src/analysis_stats.rs b/crates/ra_cli/src/analysis_stats.rs index 5c0a9dd98..1fad5b233 100644 --- a/crates/ra_cli/src/analysis_stats.rs +++ b/crates/ra_cli/src/analysis_stats.rs @@ -1,7 +1,7 @@ use std::{collections::HashSet, fmt::Write, path::Path, time::Instant}; use ra_db::SourceDatabase; -use ra_hir::{Crate, HasBodySource, HasSource, HirDisplay, ImplItem, ModuleDef, Ty}; +use ra_hir::{Crate, HasBodySource, HasSource, HirDisplay, ImplItem, ModuleDef, Ty, TypeWalk}; use ra_syntax::AstNode; use crate::Result; diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index 752653ad7..c3e589921 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs @@ -69,7 +69,9 @@ pub use self::{ resolve::Resolution, source_binder::{PathResolution, ScopeEntryWithSyntax, SourceAnalyzer}, source_id::{AstIdMap, ErasedFileAstId}, - ty::{display::HirDisplay, ApplicationTy, CallableDef, Substs, TraitRef, Ty, TypeCtor}, + ty::{ + display::HirDisplay, ApplicationTy, CallableDef, Substs, TraitRef, Ty, TypeCtor, TypeWalk, + }, type_ref::Mutability, }; diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index c0c609d78..a3df08827 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -130,12 +130,14 @@ impl ProjectionTy { substs: self.parameters.clone(), } } +} - pub fn walk(&self, f: &mut impl FnMut(&Ty)) { +impl TypeWalk for ProjectionTy { + fn walk(&self, f: &mut impl FnMut(&Ty)) { self.parameters.walk(f); } - pub fn walk_mut(&mut self, f: &mut impl FnMut(&mut Ty)) { + fn walk_mut(&mut self, f: &mut impl FnMut(&mut Ty)) { self.parameters.walk_mut(f); } } @@ -146,12 +148,12 @@ pub struct UnselectedProjectionTy { pub parameters: Substs, } -impl UnselectedProjectionTy { - pub fn walk(&self, f: &mut impl FnMut(&Ty)) { +impl TypeWalk for UnselectedProjectionTy { + fn walk(&self, f: &mut impl FnMut(&Ty)) { self.parameters.walk(f); } - pub fn walk_mut(&mut self, f: &mut impl FnMut(&mut Ty)) { + fn walk_mut(&mut self, f: &mut impl FnMut(&mut Ty)) { self.parameters.walk_mut(f); } } @@ -312,20 +314,14 @@ impl TraitRef { pub fn self_ty(&self) -> &Ty { &self.substs[0] } +} - pub fn subst(mut self, substs: &Substs) -> TraitRef { - self.substs.walk_mut(&mut |ty_mut| { - let ty = mem::replace(ty_mut, Ty::Unknown); - *ty_mut = ty.subst(substs); - }); - self - } - - pub fn walk(&self, f: &mut impl FnMut(&Ty)) { +impl TypeWalk for TraitRef { + fn walk(&self, f: &mut impl FnMut(&Ty)) { self.substs.walk(f); } - pub fn walk_mut(&mut self, f: &mut impl FnMut(&mut Ty)) { + fn walk_mut(&mut self, f: &mut impl FnMut(&mut Ty)) { self.substs.walk_mut(f); } } @@ -365,20 +361,10 @@ impl GenericPredicate { GenericPredicate::Error => None, } } +} - pub fn subst(self, substs: &Substs) -> GenericPredicate { - match self { - GenericPredicate::Implemented(trait_ref) => { - GenericPredicate::Implemented(trait_ref.subst(substs)) - } - GenericPredicate::Projection(projection_predicate) => { - GenericPredicate::Projection(projection_predicate.subst(substs)) - } - GenericPredicate::Error => self, - } - } - - pub fn walk(&self, f: &mut impl FnMut(&Ty)) { +impl TypeWalk for GenericPredicate { + fn walk(&self, f: &mut impl FnMut(&Ty)) { match self { GenericPredicate::Implemented(trait_ref) => trait_ref.walk(f), GenericPredicate::Projection(projection_pred) => projection_pred.walk(f), @@ -386,7 +372,7 @@ impl GenericPredicate { } } - pub fn walk_mut(&mut self, f: &mut impl FnMut(&mut Ty)) { + fn walk_mut(&mut self, f: &mut impl FnMut(&mut Ty)) { match self { GenericPredicate::Implemented(trait_ref) => trait_ref.walk_mut(f), GenericPredicate::Projection(projection_pred) => projection_pred.walk_mut(f), @@ -430,16 +416,16 @@ impl FnSig { pub fn ret(&self) -> &Ty { &self.params_and_return[self.params_and_return.len() - 1] } +} - /// Applies the given substitutions to all types in this signature and - /// returns the result. - pub fn subst(&self, substs: &Substs) -> FnSig { - let result: Vec<_> = - self.params_and_return.iter().map(|ty| ty.clone().subst(substs)).collect(); - FnSig { params_and_return: result.into() } +impl TypeWalk for FnSig { + fn walk(&self, f: &mut impl FnMut(&Ty)) { + for t in self.params_and_return.iter() { + t.walk(f); + } } - pub fn walk_mut(&mut self, f: &mut impl FnMut(&mut Ty)) { + fn walk_mut(&mut self, f: &mut impl FnMut(&mut Ty)) { // Without an Arc::make_mut_slice, we can't avoid the clone here: let mut v: Vec<_> = self.params_and_return.iter().cloned().collect(); for t in &mut v { @@ -463,64 +449,6 @@ impl Ty { Ty::apply(TypeCtor::Tuple { cardinality: 0 }, Substs::empty()) } - pub fn walk(&self, f: &mut impl FnMut(&Ty)) { - match self { - Ty::Apply(a_ty) => { - for t in a_ty.parameters.iter() { - 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::Dyn(predicates) | Ty::Opaque(predicates) => { - for p in predicates.iter() { - p.walk(f); - } - } - Ty::Param { .. } | Ty::Bound(_) | Ty::Infer(_) | Ty::Unknown => {} - } - f(self); - } - - fn walk_mut(&mut self, f: &mut impl FnMut(&mut Ty)) { - match self { - 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::Dyn(predicates) | Ty::Opaque(predicates) => { - let mut v: Vec<_> = predicates.iter().cloned().collect(); - for p in &mut v { - p.walk_mut(f); - } - *predicates = v.into(); - } - Ty::Param { .. } | Ty::Bound(_) | Ty::Infer(_) | Ty::Unknown => {} - } - f(self); - } - - fn fold(mut self, f: &mut impl FnMut(Ty) -> Ty) -> Ty { - self.walk_mut(&mut |ty_mut| { - let ty = mem::replace(ty_mut, Ty::Unknown); - *ty_mut = f(ty); - }); - self - } - pub fn as_reference(&self) -> Option<(&Ty, Mutability)> { match self { Ty::Apply(ApplicationTy { ctor: TypeCtor::Ref(mutability), parameters }) => { @@ -596,10 +524,53 @@ impl Ty { } } + /// Returns the type parameters of this type if it has some (i.e. is an ADT + /// or function); so if `self` is `Option`, this returns the `u32`. + pub fn substs(&self) -> Option { + match self { + Ty::Apply(ApplicationTy { parameters, .. }) => Some(parameters.clone()), + _ => None, + } + } + + /// If this is an `impl Trait` or `dyn Trait`, returns that trait. + pub fn inherent_trait(&self) -> Option { + match self { + Ty::Dyn(predicates) | Ty::Opaque(predicates) => { + predicates.iter().find_map(|pred| match pred { + GenericPredicate::Implemented(tr) => Some(tr.trait_), + _ => None, + }) + } + _ => None, + } + } +} + +/// This allows walking structures that contain types to do something with those +/// types, similar to Chalk's `Fold` trait. +pub trait TypeWalk { + fn walk(&self, f: &mut impl FnMut(&Ty)); + fn walk_mut(&mut self, f: &mut impl FnMut(&mut Ty)); + + fn fold(mut self, f: &mut impl FnMut(Ty) -> Ty) -> Self + where + Self: Sized, + { + self.walk_mut(&mut |ty_mut| { + let ty = mem::replace(ty_mut, Ty::Unknown); + *ty_mut = f(ty); + }); + self + } + /// Replaces type parameters in this type using the given `Substs`. (So e.g. /// if `self` is `&[T]`, where type parameter T has index 0, and the /// `Substs` contain `u32` at index 0, we'll have `&[u32]` afterwards.) - pub fn subst(self, substs: &Substs) -> Ty { + fn subst(self, substs: &Substs) -> Self + where + Self: Sized, + { self.fold(&mut |ty| match ty { Ty::Param { idx, name } => { substs.get(idx as usize).cloned().unwrap_or(Ty::Param { idx, name }) @@ -609,24 +580,21 @@ impl Ty { } /// Substitutes `Ty::Bound` vars (as opposed to type parameters). - pub fn subst_bound_vars(self, substs: &Substs) -> Ty { + fn subst_bound_vars(self, substs: &Substs) -> Self + where + Self: Sized, + { self.fold(&mut |ty| match ty { Ty::Bound(idx) => substs.get(idx as usize).cloned().unwrap_or_else(|| Ty::Bound(idx)), ty => ty, }) } - /// Returns the type parameters of this type if it has some (i.e. is an ADT - /// or function); so if `self` is `Option`, this returns the `u32`. - pub fn substs(&self) -> Option { - match self { - Ty::Apply(ApplicationTy { parameters, .. }) => Some(parameters.clone()), - _ => None, - } - } - /// Shifts up `Ty::Bound` vars by `n`. - pub fn shift_bound_vars(self, n: i32) -> Ty { + fn shift_bound_vars(self, n: i32) -> Self + where + Self: Sized, + { self.fold(&mut |ty| match ty { Ty::Bound(idx) => { assert!(idx as i32 >= -n); @@ -635,18 +603,57 @@ impl Ty { ty => ty, }) } +} - /// If this is an `impl Trait` or `dyn Trait`, returns that trait. - pub fn inherent_trait(&self) -> Option { +impl TypeWalk for Ty { + fn walk(&self, f: &mut impl FnMut(&Ty)) { match self { + Ty::Apply(a_ty) => { + for t in a_ty.parameters.iter() { + 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::Dyn(predicates) | Ty::Opaque(predicates) => { - predicates.iter().find_map(|pred| match pred { - GenericPredicate::Implemented(tr) => Some(tr.trait_), - _ => None, - }) + for p in predicates.iter() { + p.walk(f); + } } - _ => None, + Ty::Param { .. } | Ty::Bound(_) | Ty::Infer(_) | Ty::Unknown => {} + } + f(self); + } + + fn walk_mut(&mut self, f: &mut impl FnMut(&mut Ty)) { + match self { + 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::Dyn(predicates) | Ty::Opaque(predicates) => { + let mut v: Vec<_> = predicates.iter().cloned().collect(); + for p in &mut v { + p.walk_mut(f); + } + *predicates = v.into(); + } + Ty::Param { .. } | Ty::Bound(_) | Ty::Infer(_) | Ty::Unknown => {} } + f(self); } } diff --git a/crates/ra_hir/src/ty/autoderef.rs b/crates/ra_hir/src/ty/autoderef.rs index 2535d4ae7..08f52a53b 100644 --- a/crates/ra_hir/src/ty/autoderef.rs +++ b/crates/ra_hir/src/ty/autoderef.rs @@ -7,7 +7,7 @@ use std::iter::successors; use log::{info, warn}; -use super::{traits::Solution, Canonical, Ty}; +use super::{traits::Solution, Canonical, Ty, TypeWalk}; use crate::{HasGenericParams, HirDatabase, Name, Resolver}; const AUTODEREF_RECURSION_LIMIT: usize = 10; diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index b89a40b4b..ec3b7ffef 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs @@ -30,7 +30,7 @@ use super::{ autoderef, lower, method_resolution, op, primitive, traits::{Guidance, Obligation, ProjectionPredicate, Solution}, ApplicationTy, CallableDef, InEnvironment, ProjectionTy, Substs, TraitEnvironment, TraitRef, - Ty, TypableDef, TypeCtor, + Ty, TypableDef, TypeCtor, TypeWalk, }; use crate::{ adt::VariantDef, diff --git a/crates/ra_hir/src/ty/infer/unify.rs b/crates/ra_hir/src/ty/infer/unify.rs index e7e8825d1..9a0d2d8f9 100644 --- a/crates/ra_hir/src/ty/infer/unify.rs +++ b/crates/ra_hir/src/ty/infer/unify.rs @@ -3,7 +3,7 @@ use super::{InferenceContext, Obligation}; use crate::db::HirDatabase; use crate::ty::{ - Canonical, InEnvironment, InferTy, ProjectionPredicate, ProjectionTy, TraitRef, Ty, + Canonical, InEnvironment, InferTy, ProjectionPredicate, ProjectionTy, TraitRef, Ty, TypeWalk, }; impl<'a, D: HirDatabase> InferenceContext<'a, D> { diff --git a/crates/ra_hir/src/ty/lower.rs b/crates/ra_hir/src/ty/lower.rs index 0011c06b4..f6f0137cf 100644 --- a/crates/ra_hir/src/ty/lower.rs +++ b/crates/ra_hir/src/ty/lower.rs @@ -10,6 +10,7 @@ use std::sync::Arc; use super::{ FnSig, GenericPredicate, ProjectionPredicate, ProjectionTy, Substs, TraitRef, Ty, TypeCtor, + TypeWalk, }; use crate::{ adt::VariantDef, diff --git a/crates/ra_hir/src/ty/traits.rs b/crates/ra_hir/src/ty/traits.rs index 25316bc02..6e0271a96 100644 --- a/crates/ra_hir/src/ty/traits.rs +++ b/crates/ra_hir/src/ty/traits.rs @@ -8,7 +8,7 @@ use ra_db::salsa; use ra_prof::profile; use rustc_hash::FxHashSet; -use super::{Canonical, GenericPredicate, HirDisplay, ProjectionTy, TraitRef, Ty}; +use super::{Canonical, GenericPredicate, HirDisplay, ProjectionTy, TraitRef, Ty, TypeWalk}; use crate::{db::HirDatabase, Crate, ImplBlock, Trait}; use self::chalk::{from_chalk, ToChalk}; @@ -138,25 +138,13 @@ pub struct ProjectionPredicate { pub ty: Ty, } -impl ProjectionPredicate { - pub fn subst(mut self, substs: &super::Substs) -> ProjectionPredicate { - self.walk_mut(&mut |ty| match ty { - Ty::Param { idx, .. } => { - if let Some(t) = substs.get(*idx as usize).cloned() { - *ty = t; - } - } - _ => {} - }); - self - } - - pub fn walk(&self, f: &mut impl FnMut(&Ty)) { +impl TypeWalk for ProjectionPredicate { + fn walk(&self, f: &mut impl FnMut(&Ty)) { self.projection_ty.walk(f); self.ty.walk(f); } - pub fn walk_mut(&mut self, f: &mut impl FnMut(&mut Ty)) { + fn walk_mut(&mut self, f: &mut impl FnMut(&mut Ty)) { self.projection_ty.walk_mut(f); self.ty.walk_mut(f); } diff --git a/crates/ra_hir/src/ty/traits/chalk.rs b/crates/ra_hir/src/ty/traits/chalk.rs index 3ab5b7cca..c201c5e50 100644 --- a/crates/ra_hir/src/ty/traits/chalk.rs +++ b/crates/ra_hir/src/ty/traits/chalk.rs @@ -19,6 +19,7 @@ use crate::{ ty::display::HirDisplay, ty::{ ApplicationTy, CallableDef, GenericPredicate, ProjectionTy, Substs, TraitRef, Ty, TypeCtor, + TypeWalk, }, Crate, HasGenericParams, ImplBlock, ImplItem, Trait, TypeAlias, }; diff --git a/crates/ra_ide_api/src/completion/presentation.rs b/crates/ra_ide_api/src/completion/presentation.rs index f19eec9b7..db7e8348e 100644 --- a/crates/ra_ide_api/src/completion/presentation.rs +++ b/crates/ra_ide_api/src/completion/presentation.rs @@ -1,5 +1,5 @@ //! This modules takes care of rendering various defenitions as completion items. -use hir::{Docs, HasSource, HirDisplay, PerNs, Resolution, Ty}; +use hir::{Docs, HasSource, HirDisplay, PerNs, Resolution, Ty, TypeWalk}; use join_to_string::join; use ra_syntax::ast::NameOwner; use test_utils::tested_by; -- cgit v1.2.3