diff options
author | Florian Diebold <[email protected]> | 2020-04-26 15:56:25 +0100 |
---|---|---|
committer | Florian Diebold <[email protected]> | 2020-04-26 15:58:08 +0100 |
commit | 497073abc66df21b178c931e91969fccd8afcdc3 (patch) | |
tree | 088b0fda66e9012959a6e26e460c2811156d3e83 | |
parent | ef67e0a497a3f0b65c11bf443e0d35c8e51bd26f (diff) |
For associated type shorthand (T::Item), use the substs from the where clause
So e.g. if we have `fn foo<T: SomeTrait<u32>>() -> T::Item`, we want to lower
that to `<T as SomeTrait<u32>>::Item` and not `<T as SomeTrait<_>>::Item`.
-rw-r--r-- | crates/ra_hir_ty/src/lib.rs | 12 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/lower.rs | 42 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/tests/traits.rs | 30 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/utils.rs | 48 |
4 files changed, 119 insertions, 13 deletions
diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs index 279c06d65..a8ef32ec5 100644 --- a/crates/ra_hir_ty/src/lib.rs +++ b/crates/ra_hir_ty/src/lib.rs | |||
@@ -487,6 +487,18 @@ impl<T> Binders<T> { | |||
487 | pub fn new(num_binders: usize, value: T) -> Self { | 487 | pub fn new(num_binders: usize, value: T) -> Self { |
488 | Self { num_binders, value } | 488 | Self { num_binders, value } |
489 | } | 489 | } |
490 | |||
491 | pub fn as_ref(&self) -> Binders<&T> { | ||
492 | Binders { num_binders: self.num_binders, value: &self.value } | ||
493 | } | ||
494 | |||
495 | pub fn map<U>(self, f: impl FnOnce(T) -> U) -> Binders<U> { | ||
496 | Binders { num_binders: self.num_binders, value: f(self.value) } | ||
497 | } | ||
498 | |||
499 | pub fn filter_map<U>(self, f: impl FnOnce(T) -> Option<U>) -> Option<Binders<U>> { | ||
500 | Some(Binders { num_binders: self.num_binders, value: f(self.value)? }) | ||
501 | } | ||
490 | } | 502 | } |
491 | 503 | ||
492 | impl<T: Clone> Binders<&T> { | 504 | impl<T: Clone> Binders<&T> { |
diff --git a/crates/ra_hir_ty/src/lower.rs b/crates/ra_hir_ty/src/lower.rs index b57214296..a6f893037 100644 --- a/crates/ra_hir_ty/src/lower.rs +++ b/crates/ra_hir_ty/src/lower.rs | |||
@@ -28,11 +28,11 @@ use crate::{ | |||
28 | db::HirDatabase, | 28 | db::HirDatabase, |
29 | primitive::{FloatTy, IntTy}, | 29 | primitive::{FloatTy, IntTy}, |
30 | utils::{ | 30 | utils::{ |
31 | all_super_traits, associated_type_by_name_including_super_traits, generics, make_mut_slice, | 31 | all_super_trait_refs, associated_type_by_name_including_super_traits, generics, |
32 | variant_data, | 32 | make_mut_slice, variant_data, |
33 | }, | 33 | }, |
34 | Binders, BoundVar, DebruijnIndex, FnSig, GenericPredicate, PolyFnSig, ProjectionPredicate, | 34 | Binders, BoundVar, DebruijnIndex, FnSig, GenericPredicate, PolyFnSig, ProjectionPredicate, |
35 | ProjectionTy, Substs, TraitEnvironment, TraitRef, Ty, TypeCtor, | 35 | ProjectionTy, Substs, TraitEnvironment, TraitRef, Ty, TypeCtor, TypeWalk, |
36 | }; | 36 | }; |
37 | 37 | ||
38 | #[derive(Debug)] | 38 | #[derive(Debug)] |
@@ -256,7 +256,7 @@ impl Ty { | |||
256 | if remaining_segments.len() == 1 { | 256 | if remaining_segments.len() == 1 { |
257 | // resolve unselected assoc types | 257 | // resolve unselected assoc types |
258 | let segment = remaining_segments.first().unwrap(); | 258 | let segment = remaining_segments.first().unwrap(); |
259 | (Ty::select_associated_type(ctx, ty, res, segment), None) | 259 | (Ty::select_associated_type(ctx, res, segment), None) |
260 | } else if remaining_segments.len() > 1 { | 260 | } else if remaining_segments.len() > 1 { |
261 | // FIXME report error (ambiguous associated type) | 261 | // FIXME report error (ambiguous associated type) |
262 | (Ty::Unknown, None) | 262 | (Ty::Unknown, None) |
@@ -380,21 +380,20 @@ impl Ty { | |||
380 | 380 | ||
381 | fn select_associated_type( | 381 | fn select_associated_type( |
382 | ctx: &TyLoweringContext<'_>, | 382 | ctx: &TyLoweringContext<'_>, |
383 | self_ty: Ty, | ||
384 | res: Option<TypeNs>, | 383 | res: Option<TypeNs>, |
385 | segment: PathSegment<'_>, | 384 | segment: PathSegment<'_>, |
386 | ) -> Ty { | 385 | ) -> Ty { |
387 | let traits_from_env: Vec<_> = match res { | 386 | let traits_from_env: Vec<_> = match res { |
388 | Some(TypeNs::SelfType(impl_id)) => match ctx.db.impl_trait(impl_id) { | 387 | Some(TypeNs::SelfType(impl_id)) => match ctx.db.impl_trait(impl_id) { |
389 | None => return Ty::Unknown, | 388 | None => return Ty::Unknown, |
390 | Some(trait_ref) => vec![trait_ref.value.trait_], | 389 | Some(trait_ref) => vec![trait_ref.value], |
391 | }, | 390 | }, |
392 | Some(TypeNs::GenericParam(param_id)) => { | 391 | Some(TypeNs::GenericParam(param_id)) => { |
393 | let predicates = ctx.db.generic_predicates_for_param(param_id); | 392 | let predicates = ctx.db.generic_predicates_for_param(param_id); |
394 | let mut traits_: Vec<_> = predicates | 393 | let mut traits_: Vec<_> = predicates |
395 | .iter() | 394 | .iter() |
396 | .filter_map(|pred| match &pred.value { | 395 | .filter_map(|pred| match &pred.value { |
397 | GenericPredicate::Implemented(tr) => Some(tr.trait_), | 396 | GenericPredicate::Implemented(tr) => Some(tr.clone()), |
398 | _ => None, | 397 | _ => None, |
399 | }) | 398 | }) |
400 | .collect(); | 399 | .collect(); |
@@ -404,20 +403,37 @@ impl Ty { | |||
404 | if generics.params.types[param_id.local_id].provenance | 403 | if generics.params.types[param_id.local_id].provenance |
405 | == TypeParamProvenance::TraitSelf | 404 | == TypeParamProvenance::TraitSelf |
406 | { | 405 | { |
407 | traits_.push(trait_id); | 406 | let trait_ref = TraitRef { |
407 | trait_: trait_id, | ||
408 | substs: Substs::bound_vars(&generics, DebruijnIndex::INNERMOST), | ||
409 | }; | ||
410 | traits_.push(trait_ref); | ||
408 | } | 411 | } |
409 | } | 412 | } |
410 | traits_ | 413 | traits_ |
411 | } | 414 | } |
412 | _ => return Ty::Unknown, | 415 | _ => return Ty::Unknown, |
413 | }; | 416 | }; |
414 | let traits = traits_from_env.into_iter().flat_map(|t| all_super_traits(ctx.db.upcast(), t)); | 417 | let traits = traits_from_env.into_iter().flat_map(|t| all_super_trait_refs(ctx.db, t)); |
415 | for t in traits { | 418 | for t in traits { |
416 | if let Some(associated_ty) = ctx.db.trait_data(t).associated_type_by_name(&segment.name) | 419 | if let Some(associated_ty) = |
420 | ctx.db.trait_data(t.trait_).associated_type_by_name(&segment.name) | ||
417 | { | 421 | { |
418 | let substs = | 422 | let substs = match ctx.type_param_mode { |
419 | Substs::build_for_def(ctx.db, t).push(self_ty).fill_with_unknown().build(); | 423 | TypeParamLoweringMode::Placeholder => { |
420 | // FIXME handle type parameters on the segment | 424 | // if we're lowering to placeholders, we have to put |
425 | // them in now | ||
426 | let s = Substs::type_params( | ||
427 | ctx.db, | ||
428 | ctx.resolver | ||
429 | .generic_def() | ||
430 | .expect("there should be generics if there's a generic param"), | ||
431 | ); | ||
432 | t.substs.subst_bound_vars(&s) | ||
433 | } | ||
434 | TypeParamLoweringMode::Variable => t.substs, | ||
435 | }; | ||
436 | // FIXME handle (forbid) type parameters on the segment | ||
421 | return Ty::Projection(ProjectionTy { associated_ty, parameters: substs }); | 437 | return Ty::Projection(ProjectionTy { associated_ty, parameters: substs }); |
422 | } | 438 | } |
423 | } | 439 | } |
diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs index f51cdd496..e555c879a 100644 --- a/crates/ra_hir_ty/src/tests/traits.rs +++ b/crates/ra_hir_ty/src/tests/traits.rs | |||
@@ -1898,6 +1898,36 @@ fn test() { | |||
1898 | } | 1898 | } |
1899 | 1899 | ||
1900 | #[test] | 1900 | #[test] |
1901 | fn unselected_projection_chalk_fold() { | ||
1902 | let t = type_at( | ||
1903 | r#" | ||
1904 | //- /main.rs | ||
1905 | trait Interner {} | ||
1906 | trait Fold<I: Interner, TI = I> { | ||
1907 | type Result; | ||
1908 | } | ||
1909 | |||
1910 | struct Ty<I: Interner> {} | ||
1911 | impl<I: Interner, TI: Interner> Fold<I, TI> for Ty<I> { | ||
1912 | type Result = Ty<TI>; | ||
1913 | } | ||
1914 | |||
1915 | fn fold<I: Interner, T>(interner: &I, t: T) -> T::Result | ||
1916 | where | ||
1917 | T: Fold<I, I>, | ||
1918 | { | ||
1919 | loop {} | ||
1920 | } | ||
1921 | |||
1922 | fn foo<I: Interner>(interner: &I, t: Ty<I>) { | ||
1923 | fold(interner, t)<|>; | ||
1924 | } | ||
1925 | "#, | ||
1926 | ); | ||
1927 | assert_eq!(t, "Ty<I>"); | ||
1928 | } | ||
1929 | |||
1930 | #[test] | ||
1901 | fn trait_impl_self_ty() { | 1931 | fn trait_impl_self_ty() { |
1902 | let t = type_at( | 1932 | let t = type_at( |
1903 | r#" | 1933 | r#" |
diff --git a/crates/ra_hir_ty/src/utils.rs b/crates/ra_hir_ty/src/utils.rs index 1e5022fa4..f98350bf9 100644 --- a/crates/ra_hir_ty/src/utils.rs +++ b/crates/ra_hir_ty/src/utils.rs | |||
@@ -14,6 +14,8 @@ use hir_def::{ | |||
14 | }; | 14 | }; |
15 | use hir_expand::name::{name, Name}; | 15 | use hir_expand::name::{name, Name}; |
16 | 16 | ||
17 | use crate::{db::HirDatabase, GenericPredicate, TraitRef}; | ||
18 | |||
17 | fn direct_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec<TraitId> { | 19 | fn direct_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec<TraitId> { |
18 | let resolver = trait_.resolver(db); | 20 | let resolver = trait_.resolver(db); |
19 | // returning the iterator directly doesn't easily work because of | 21 | // returning the iterator directly doesn't easily work because of |
@@ -41,6 +43,28 @@ fn direct_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec<TraitId> { | |||
41 | .collect() | 43 | .collect() |
42 | } | 44 | } |
43 | 45 | ||
46 | fn direct_super_trait_refs(db: &dyn HirDatabase, trait_ref: &TraitRef) -> Vec<TraitRef> { | ||
47 | // returning the iterator directly doesn't easily work because of | ||
48 | // lifetime problems, but since there usually shouldn't be more than a | ||
49 | // few direct traits this should be fine (we could even use some kind of | ||
50 | // SmallVec if performance is a concern) | ||
51 | let generic_params = db.generic_params(trait_ref.trait_.into()); | ||
52 | let trait_self = match generic_params.find_trait_self_param() { | ||
53 | Some(p) => TypeParamId { parent: trait_ref.trait_.into(), local_id: p }, | ||
54 | None => return Vec::new(), | ||
55 | }; | ||
56 | db.generic_predicates_for_param(trait_self) | ||
57 | .iter() | ||
58 | .filter_map(|pred| { | ||
59 | pred.as_ref().filter_map(|pred| match pred { | ||
60 | GenericPredicate::Implemented(tr) => Some(tr.clone()), | ||
61 | _ => None, | ||
62 | }) | ||
63 | }) | ||
64 | .map(|pred| pred.subst(&trait_ref.substs)) | ||
65 | .collect() | ||
66 | } | ||
67 | |||
44 | /// Returns an iterator over the whole super trait hierarchy (including the | 68 | /// Returns an iterator over the whole super trait hierarchy (including the |
45 | /// trait itself). | 69 | /// trait itself). |
46 | pub(super) fn all_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec<TraitId> { | 70 | pub(super) fn all_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec<TraitId> { |
@@ -62,6 +86,30 @@ pub(super) fn all_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec<Tra | |||
62 | result | 86 | result |
63 | } | 87 | } |
64 | 88 | ||
89 | /// Given a trait ref (`Self: Trait`), builds all the implied trait refs for | ||
90 | /// super traits. The original trait ref will be included. So the difference to | ||
91 | /// `all_super_traits` is that we keep track of type parameters; for example if | ||
92 | /// we have `Self: Trait<u32, i32>` and `Trait<T, U>: OtherTrait<U>` we'll get | ||
93 | /// `Self: OtherTrait<i32>`. | ||
94 | pub(super) fn all_super_trait_refs(db: &dyn HirDatabase, trait_ref: TraitRef) -> Vec<TraitRef> { | ||
95 | // we need to take care a bit here to avoid infinite loops in case of cycles | ||
96 | // (i.e. if we have `trait A: B; trait B: A;`) | ||
97 | let mut result = vec![trait_ref]; | ||
98 | let mut i = 0; | ||
99 | while i < result.len() { | ||
100 | let t = &result[i]; | ||
101 | // yeah this is quadratic, but trait hierarchies should be flat | ||
102 | // enough that this doesn't matter | ||
103 | for tt in direct_super_trait_refs(db, t) { | ||
104 | if !result.iter().any(|tr| tr.trait_ == tt.trait_) { | ||
105 | result.push(tt); | ||
106 | } | ||
107 | } | ||
108 | i += 1; | ||
109 | } | ||
110 | result | ||
111 | } | ||
112 | |||
65 | /// Finds a path from a trait to one of its super traits. Returns an empty | 113 | /// Finds a path from a trait to one of its super traits. Returns an empty |
66 | /// vector if there is no path. | 114 | /// vector if there is no path. |
67 | pub(super) fn find_super_trait_path( | 115 | pub(super) fn find_super_trait_path( |