Skip to content

Commit f233ac4

Browse files
committed
Lower generic arguments for associated types in paths
1 parent 4dd6943 commit f233ac4

File tree

4 files changed

+193
-39
lines changed

4 files changed

+193
-39
lines changed

crates/hir-ty/src/chalk_ext.rs

+10-7
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ use syntax::SmolStr;
1111

1212
use crate::{
1313
db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id,
14-
from_placeholder_idx, to_chalk_trait_id, AdtId, AliasEq, AliasTy, Binders, CallableDefId,
15-
CallableSig, FnPointer, ImplTraitId, Interner, Lifetime, ProjectionTy, QuantifiedWhereClause,
16-
Substitution, TraitRef, Ty, TyBuilder, TyKind, WhereClause,
14+
from_placeholder_idx, to_chalk_trait_id, utils::generics, AdtId, AliasEq, AliasTy, Binders,
15+
CallableDefId, CallableSig, FnPointer, ImplTraitId, Interner, Lifetime, ProjectionTy,
16+
QuantifiedWhereClause, Substitution, TraitRef, Ty, TyBuilder, TyKind, WhereClause,
1717
};
1818

1919
pub trait TyExt {
@@ -338,10 +338,13 @@ pub trait ProjectionTyExt {
338338

339339
impl ProjectionTyExt for ProjectionTy {
340340
fn trait_ref(&self, db: &dyn HirDatabase) -> TraitRef {
341-
TraitRef {
342-
trait_id: to_chalk_trait_id(self.trait_(db)),
343-
substitution: self.substitution.clone(),
344-
}
341+
// FIXME: something like `Split` trait from chalk-solve might be nice.
342+
let generics = generics(db.upcast(), from_assoc_type_id(self.associated_ty_id).into());
343+
let substitution = Substitution::from_iter(
344+
Interner,
345+
self.substitution.iter(Interner).skip(generics.len_self()),
346+
);
347+
TraitRef { trait_id: to_chalk_trait_id(self.trait_(db)), substitution }
345348
}
346349

347350
fn trait_(&self, db: &dyn HirDatabase) -> TraitId {

crates/hir-ty/src/infer/path.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ impl<'a> InferenceContext<'a> {
157157
remaining_segments_for_ty,
158158
true,
159159
);
160-
if let TyKind::Error = ty.kind(Interner) {
160+
if ty.is_unknown() {
161161
return None;
162162
}
163163

crates/hir-ty/src/lower.rs

+62-31
Original file line numberDiff line numberDiff line change
@@ -447,12 +447,31 @@ impl<'a> TyLoweringContext<'a> {
447447
.db
448448
.trait_data(trait_ref.hir_trait_id())
449449
.associated_type_by_name(segment.name);
450+
450451
match found {
451452
Some(associated_ty) => {
452-
// FIXME handle type parameters on the segment
453+
// FIXME: `substs_from_path_segment()` pushes `TyKind::Error` for every parent
454+
// generic params. It's inefficient to splice the `Substitution`s, so we may want
455+
// that method to optionally take parent `Substitution` as we already know them at
456+
// this point (`trait_ref.substitution`).
457+
let substitution = self.substs_from_path_segment(
458+
segment,
459+
Some(associated_ty.into()),
460+
false,
461+
None,
462+
);
463+
let len_self =
464+
generics(self.db.upcast(), associated_ty.into()).len_self();
465+
let substitution = Substitution::from_iter(
466+
Interner,
467+
substitution
468+
.iter(Interner)
469+
.take(len_self)
470+
.chain(trait_ref.substitution.iter(Interner)),
471+
);
453472
TyKind::Alias(AliasTy::Projection(ProjectionTy {
454473
associated_ty_id: to_assoc_type_id(associated_ty),
455-
substitution: trait_ref.substitution,
474+
substitution,
456475
}))
457476
.intern(Interner)
458477
}
@@ -590,36 +609,48 @@ impl<'a> TyLoweringContext<'a> {
590609
res,
591610
Some(segment.name.clone()),
592611
move |name, t, associated_ty| {
593-
if name == segment.name {
594-
let substs = match self.type_param_mode {
595-
ParamLoweringMode::Placeholder => {
596-
// if we're lowering to placeholders, we have to put
597-
// them in now
598-
let generics = generics(
599-
self.db.upcast(),
600-
self.resolver
601-
.generic_def()
602-
.expect("there should be generics if there's a generic param"),
603-
);
604-
let s = generics.placeholder_subst(self.db);
605-
s.apply(t.substitution.clone(), Interner)
606-
}
607-
ParamLoweringMode::Variable => t.substitution.clone(),
608-
};
609-
// We need to shift in the bound vars, since
610-
// associated_type_shorthand_candidates does not do that
611-
let substs = substs.shifted_in_from(Interner, self.in_binders);
612-
// FIXME handle type parameters on the segment
613-
Some(
614-
TyKind::Alias(AliasTy::Projection(ProjectionTy {
615-
associated_ty_id: to_assoc_type_id(associated_ty),
616-
substitution: substs,
617-
}))
618-
.intern(Interner),
619-
)
620-
} else {
621-
None
612+
if name != segment.name {
613+
return None;
622614
}
615+
616+
// FIXME: `substs_from_path_segment()` pushes `TyKind::Error` for every parent
617+
// generic params. It's inefficient to splice the `Substitution`s, so we may want
618+
// that method to optionally take parent `Substitution` as we already know them at
619+
// this point (`t.substitution`).
620+
let substs = self.substs_from_path_segment(
621+
segment.clone(),
622+
Some(associated_ty.into()),
623+
false,
624+
None,
625+
);
626+
627+
let len_self = generics(self.db.upcast(), associated_ty.into()).len_self();
628+
629+
let substs = Substitution::from_iter(
630+
Interner,
631+
substs.iter(Interner).take(len_self).chain(t.substitution.iter(Interner)),
632+
);
633+
634+
let substs = match self.type_param_mode {
635+
ParamLoweringMode::Placeholder => {
636+
// if we're lowering to placeholders, we have to put
637+
// them in now
638+
let generics = generics(self.db.upcast(), def);
639+
let s = generics.placeholder_subst(self.db);
640+
s.apply(substs, Interner)
641+
}
642+
ParamLoweringMode::Variable => substs,
643+
};
644+
// We need to shift in the bound vars, since
645+
// associated_type_shorthand_candidates does not do that
646+
let substs = substs.shifted_in_from(Interner, self.in_binders);
647+
Some(
648+
TyKind::Alias(AliasTy::Projection(ProjectionTy {
649+
associated_ty_id: to_assoc_type_id(associated_ty),
650+
substitution: substs,
651+
}))
652+
.intern(Interner),
653+
)
623654
},
624655
);
625656

crates/hir-ty/src/tests/traits.rs

+120
Original file line numberDiff line numberDiff line change
@@ -3963,3 +3963,123 @@ fn g(t: &(dyn T + Send)) {
39633963
"#,
39643964
);
39653965
}
3966+
3967+
#[test]
3968+
fn gats_in_path() {
3969+
check_infer_with_mismatches(
3970+
r#"
3971+
//- minicore: deref
3972+
use core::ops::Deref;
3973+
trait PointerFamily {
3974+
type Pointer<T>: Deref<Target = T>;
3975+
}
3976+
3977+
fn f<P: PointerFamily>(p: P::Pointer<i32>) {
3978+
let a = *p;
3979+
}
3980+
fn g<P: PointerFamily>(p: <P as PointerFamily>::Pointer<i32>) {
3981+
let a = *p;
3982+
}
3983+
"#,
3984+
expect![[r#"
3985+
110..111 'p': PointerFamily::Pointer<i32, P>
3986+
130..149 '{ ... *p; }': ()
3987+
140..141 'a': i32
3988+
144..146 '*p': i32
3989+
145..146 'p': PointerFamily::Pointer<i32, P>
3990+
173..174 'p': PointerFamily::Pointer<i32, P>
3991+
212..231 '{ ... *p; }': ()
3992+
222..223 'a': i32
3993+
226..228 '*p': i32
3994+
227..228 'p': PointerFamily::Pointer<i32, P>
3995+
"#]],
3996+
);
3997+
}
3998+
3999+
#[test]
4000+
fn gats_with_impl_trait() {
4001+
// FIXME: the last function (`fn h()`) is not valid Rust as of this writing because you cannot
4002+
// specify the same associated type multiple times even if their arguments are different.
4003+
// Reconsider how to treat these invalid types.
4004+
check_infer_with_mismatches(
4005+
r#"
4006+
//- minicore: deref
4007+
use core::ops::Deref;
4008+
4009+
trait Trait {
4010+
type Assoc<T>: Deref<Target = T>;
4011+
fn get<U>(&self) -> Self::Assoc<U>;
4012+
}
4013+
4014+
fn f<T>(v: impl Trait) {
4015+
v.get::<i32>().deref();
4016+
v.get::<T>().deref();
4017+
}
4018+
fn g<T>(v: impl Trait<Assoc<T> = &'a T>) {
4019+
let a = v.get::<T>();
4020+
let a = v.get::<()>();
4021+
}
4022+
fn h(v: impl Trait<Assoc<i32> = &'a i32, Assoc<i64> = &'a i64> {
4023+
let a = v.get::<i32>();
4024+
let a = v.get::<i64>();
4025+
}
4026+
"#,
4027+
expect![[r#"
4028+
90..94 'self': &Self
4029+
126..127 'v': impl Trait
4030+
141..198 '{ ...f(); }': ()
4031+
147..148 'v': impl Trait
4032+
147..161 'v.get::<i32>()': Trait::Assoc<i32, impl Trait>
4033+
147..169 'v.get:...eref()': &i32
4034+
175..176 'v': impl Trait
4035+
175..187 'v.get::<T>()': Trait::Assoc<T, impl Trait>
4036+
175..195 'v.get:...eref()': &T
4037+
207..208 'v': impl Trait<Assoc<T> = &T>
4038+
240..296 '{ ...>(); }': ()
4039+
250..251 'a': &T
4040+
254..255 'v': impl Trait<Assoc<T> = &T>
4041+
254..266 'v.get::<T>()': &T
4042+
276..277 'a': Trait::Assoc<(), impl Trait<Assoc<T> = &T>>
4043+
280..281 'v': impl Trait<Assoc<T> = &T>
4044+
280..293 'v.get::<()>()': Trait::Assoc<(), impl Trait<Assoc<T> = &T>>
4045+
302..303 'v': impl Trait<Assoc<i32> = &i32, Assoc<i64> = &i64>
4046+
360..419 '{ ...>(); }': ()
4047+
370..371 'a': &i32
4048+
374..375 'v': impl Trait<Assoc<i32> = &i32, Assoc<i64> = &i64>
4049+
374..388 'v.get::<i32>()': &i32
4050+
398..399 'a': &i64
4051+
402..403 'v': impl Trait<Assoc<i32> = &i32, Assoc<i64> = &i64>
4052+
402..416 'v.get::<i64>()': &i64
4053+
"#]],
4054+
);
4055+
}
4056+
4057+
#[test]
4058+
fn gats_with_dyn() {
4059+
// This test is here to keep track of how we infer things despite traits with GATs being not
4060+
// object-safe currently.
4061+
// FIXME: reconsider how to treat these invalid types.
4062+
check_infer_with_mismatches(
4063+
r#"
4064+
//- minicore: deref
4065+
use core::ops::Deref;
4066+
4067+
trait Trait {
4068+
type Assoc<T>: Deref<Target = T>;
4069+
fn get<U>(&self) -> Self::Assoc<U>;
4070+
}
4071+
4072+
fn f<'a>(v: &dyn Trait<Assoc<i32> = &'a i32>) {
4073+
v.get::<i32>().deref();
4074+
}
4075+
"#,
4076+
expect![[r#"
4077+
90..94 'self': &Self
4078+
127..128 'v': &(dyn Trait<Assoc<i32> = &i32>)
4079+
164..195 '{ ...f(); }': ()
4080+
170..171 'v': &(dyn Trait<Assoc<i32> = &i32>)
4081+
170..184 'v.get::<i32>()': &i32
4082+
170..192 'v.get:...eref()': &i32
4083+
"#]],
4084+
);
4085+
}

0 commit comments

Comments
 (0)