Skip to content

Commit 497073a

Browse files
committed
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`.
1 parent ef67e0a commit 497073a

File tree

4 files changed

+119
-13
lines changed

4 files changed

+119
-13
lines changed

crates/ra_hir_ty/src/lib.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,18 @@ impl<T> Binders<T> {
487487
pub fn new(num_binders: usize, value: T) -> Self {
488488
Self { num_binders, value }
489489
}
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+
}
490502
}
491503

492504
impl<T: Clone> Binders<&T> {

crates/ra_hir_ty/src/lower.rs

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,11 @@ use crate::{
2828
db::HirDatabase,
2929
primitive::{FloatTy, IntTy},
3030
utils::{
31-
all_super_traits, associated_type_by_name_including_super_traits, generics, make_mut_slice,
32-
variant_data,
31+
all_super_trait_refs, associated_type_by_name_including_super_traits, generics,
32+
make_mut_slice, variant_data,
3333
},
3434
Binders, BoundVar, DebruijnIndex, FnSig, GenericPredicate, PolyFnSig, ProjectionPredicate,
35-
ProjectionTy, Substs, TraitEnvironment, TraitRef, Ty, TypeCtor,
35+
ProjectionTy, Substs, TraitEnvironment, TraitRef, Ty, TypeCtor, TypeWalk,
3636
};
3737

3838
#[derive(Debug)]
@@ -256,7 +256,7 @@ impl Ty {
256256
if remaining_segments.len() == 1 {
257257
// resolve unselected assoc types
258258
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)
260260
} else if remaining_segments.len() > 1 {
261261
// FIXME report error (ambiguous associated type)
262262
(Ty::Unknown, None)
@@ -380,21 +380,20 @@ impl Ty {
380380

381381
fn select_associated_type(
382382
ctx: &TyLoweringContext<'_>,
383-
self_ty: Ty,
384383
res: Option<TypeNs>,
385384
segment: PathSegment<'_>,
386385
) -> Ty {
387386
let traits_from_env: Vec<_> = match res {
388387
Some(TypeNs::SelfType(impl_id)) => match ctx.db.impl_trait(impl_id) {
389388
None => return Ty::Unknown,
390-
Some(trait_ref) => vec![trait_ref.value.trait_],
389+
Some(trait_ref) => vec![trait_ref.value],
391390
},
392391
Some(TypeNs::GenericParam(param_id)) => {
393392
let predicates = ctx.db.generic_predicates_for_param(param_id);
394393
let mut traits_: Vec<_> = predicates
395394
.iter()
396395
.filter_map(|pred| match &pred.value {
397-
GenericPredicate::Implemented(tr) => Some(tr.trait_),
396+
GenericPredicate::Implemented(tr) => Some(tr.clone()),
398397
_ => None,
399398
})
400399
.collect();
@@ -404,20 +403,37 @@ impl Ty {
404403
if generics.params.types[param_id.local_id].provenance
405404
== TypeParamProvenance::TraitSelf
406405
{
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);
408411
}
409412
}
410413
traits_
411414
}
412415
_ => return Ty::Unknown,
413416
};
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));
415418
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)
417421
{
418-
let substs =
419-
Substs::build_for_def(ctx.db, t).push(self_ty).fill_with_unknown().build();
420-
// FIXME handle type parameters on the segment
422+
let substs = match ctx.type_param_mode {
423+
TypeParamLoweringMode::Placeholder => {
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
421437
return Ty::Projection(ProjectionTy { associated_ty, parameters: substs });
422438
}
423439
}

crates/ra_hir_ty/src/tests/traits.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1897,6 +1897,36 @@ fn test() {
18971897
assert_eq!(t, "u32");
18981898
}
18991899

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+
19001930
#[test]
19011931
fn trait_impl_self_ty() {
19021932
let t = type_at(

crates/ra_hir_ty/src/utils.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ use hir_def::{
1414
};
1515
use hir_expand::name::{name, Name};
1616

17+
use crate::{db::HirDatabase, GenericPredicate, TraitRef};
18+
1719
fn direct_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec<TraitId> {
1820
let resolver = trait_.resolver(db);
1921
// 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> {
4143
.collect()
4244
}
4345

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+
4468
/// Returns an iterator over the whole super trait hierarchy (including the
4569
/// trait itself).
4670
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
6286
result
6387
}
6488

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+
65113
/// Finds a path from a trait to one of its super traits. Returns an empty
66114
/// vector if there is no path.
67115
pub(super) fn find_super_trait_path(

0 commit comments

Comments
 (0)