Lower generic arguments for associated types in paths

This commit is contained in:
Ryo Yoshida 2022-10-26 17:21:08 +09:00
parent 4dd694371a
commit f233ac447f
No known key found for this signature in database
GPG key ID: E25698A930586171
4 changed files with 193 additions and 39 deletions

View file

@ -11,9 +11,9 @@ use syntax::SmolStr;
use crate::{
db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id,
from_placeholder_idx, to_chalk_trait_id, AdtId, AliasEq, AliasTy, Binders, CallableDefId,
CallableSig, FnPointer, ImplTraitId, Interner, Lifetime, ProjectionTy, QuantifiedWhereClause,
Substitution, TraitRef, Ty, TyBuilder, TyKind, WhereClause,
from_placeholder_idx, to_chalk_trait_id, utils::generics, AdtId, AliasEq, AliasTy, Binders,
CallableDefId, CallableSig, FnPointer, ImplTraitId, Interner, Lifetime, ProjectionTy,
QuantifiedWhereClause, Substitution, TraitRef, Ty, TyBuilder, TyKind, WhereClause,
};
pub trait TyExt {
@ -338,10 +338,13 @@ pub trait ProjectionTyExt {
impl ProjectionTyExt for ProjectionTy {
fn trait_ref(&self, db: &dyn HirDatabase) -> TraitRef {
TraitRef {
trait_id: to_chalk_trait_id(self.trait_(db)),
substitution: self.substitution.clone(),
}
// FIXME: something like `Split` trait from chalk-solve might be nice.
let generics = generics(db.upcast(), from_assoc_type_id(self.associated_ty_id).into());
let substitution = Substitution::from_iter(
Interner,
self.substitution.iter(Interner).skip(generics.len_self()),
);
TraitRef { trait_id: to_chalk_trait_id(self.trait_(db)), substitution }
}
fn trait_(&self, db: &dyn HirDatabase) -> TraitId {

View file

@ -157,7 +157,7 @@ impl<'a> InferenceContext<'a> {
remaining_segments_for_ty,
true,
);
if let TyKind::Error = ty.kind(Interner) {
if ty.is_unknown() {
return None;
}

View file

@ -447,12 +447,31 @@ impl<'a> TyLoweringContext<'a> {
.db
.trait_data(trait_ref.hir_trait_id())
.associated_type_by_name(segment.name);
match found {
Some(associated_ty) => {
// FIXME handle type parameters on the segment
// FIXME: `substs_from_path_segment()` pushes `TyKind::Error` for every parent
// generic params. It's inefficient to splice the `Substitution`s, so we may want
// that method to optionally take parent `Substitution` as we already know them at
// this point (`trait_ref.substitution`).
let substitution = self.substs_from_path_segment(
segment,
Some(associated_ty.into()),
false,
None,
);
let len_self =
generics(self.db.upcast(), associated_ty.into()).len_self();
let substitution = Substitution::from_iter(
Interner,
substitution
.iter(Interner)
.take(len_self)
.chain(trait_ref.substitution.iter(Interner)),
);
TyKind::Alias(AliasTy::Projection(ProjectionTy {
associated_ty_id: to_assoc_type_id(associated_ty),
substitution: trait_ref.substitution,
substitution,
}))
.intern(Interner)
}
@ -590,36 +609,48 @@ impl<'a> TyLoweringContext<'a> {
res,
Some(segment.name.clone()),
move |name, t, associated_ty| {
if name == segment.name {
let substs = match self.type_param_mode {
ParamLoweringMode::Placeholder => {
// if we're lowering to placeholders, we have to put
// them in now
let generics = generics(
self.db.upcast(),
self.resolver
.generic_def()
.expect("there should be generics if there's a generic param"),
);
let s = generics.placeholder_subst(self.db);
s.apply(t.substitution.clone(), Interner)
}
ParamLoweringMode::Variable => t.substitution.clone(),
};
// We need to shift in the bound vars, since
// associated_type_shorthand_candidates does not do that
let substs = substs.shifted_in_from(Interner, self.in_binders);
// FIXME handle type parameters on the segment
Some(
TyKind::Alias(AliasTy::Projection(ProjectionTy {
associated_ty_id: to_assoc_type_id(associated_ty),
substitution: substs,
}))
.intern(Interner),
)
} else {
None
if name != segment.name {
return None;
}
// FIXME: `substs_from_path_segment()` pushes `TyKind::Error` for every parent
// generic params. It's inefficient to splice the `Substitution`s, so we may want
// that method to optionally take parent `Substitution` as we already know them at
// this point (`t.substitution`).
let substs = self.substs_from_path_segment(
segment.clone(),
Some(associated_ty.into()),
false,
None,
);
let len_self = generics(self.db.upcast(), associated_ty.into()).len_self();
let substs = Substitution::from_iter(
Interner,
substs.iter(Interner).take(len_self).chain(t.substitution.iter(Interner)),
);
let substs = match self.type_param_mode {
ParamLoweringMode::Placeholder => {
// if we're lowering to placeholders, we have to put
// them in now
let generics = generics(self.db.upcast(), def);
let s = generics.placeholder_subst(self.db);
s.apply(substs, Interner)
}
ParamLoweringMode::Variable => substs,
};
// We need to shift in the bound vars, since
// associated_type_shorthand_candidates does not do that
let substs = substs.shifted_in_from(Interner, self.in_binders);
Some(
TyKind::Alias(AliasTy::Projection(ProjectionTy {
associated_ty_id: to_assoc_type_id(associated_ty),
substitution: substs,
}))
.intern(Interner),
)
},
);

View file

@ -3963,3 +3963,123 @@ fn g(t: &(dyn T + Send)) {
"#,
);
}
#[test]
fn gats_in_path() {
check_infer_with_mismatches(
r#"
//- minicore: deref
use core::ops::Deref;
trait PointerFamily {
type Pointer<T>: Deref<Target = T>;
}
fn f<P: PointerFamily>(p: P::Pointer<i32>) {
let a = *p;
}
fn g<P: PointerFamily>(p: <P as PointerFamily>::Pointer<i32>) {
let a = *p;
}
"#,
expect![[r#"
110..111 'p': PointerFamily::Pointer<i32, P>
130..149 '{ ... *p; }': ()
140..141 'a': i32
144..146 '*p': i32
145..146 'p': PointerFamily::Pointer<i32, P>
173..174 'p': PointerFamily::Pointer<i32, P>
212..231 '{ ... *p; }': ()
222..223 'a': i32
226..228 '*p': i32
227..228 'p': PointerFamily::Pointer<i32, P>
"#]],
);
}
#[test]
fn gats_with_impl_trait() {
// FIXME: the last function (`fn h()`) is not valid Rust as of this writing because you cannot
// specify the same associated type multiple times even if their arguments are different.
// Reconsider how to treat these invalid types.
check_infer_with_mismatches(
r#"
//- minicore: deref
use core::ops::Deref;
trait Trait {
type Assoc<T>: Deref<Target = T>;
fn get<U>(&self) -> Self::Assoc<U>;
}
fn f<T>(v: impl Trait) {
v.get::<i32>().deref();
v.get::<T>().deref();
}
fn g<T>(v: impl Trait<Assoc<T> = &'a T>) {
let a = v.get::<T>();
let a = v.get::<()>();
}
fn h(v: impl Trait<Assoc<i32> = &'a i32, Assoc<i64> = &'a i64> {
let a = v.get::<i32>();
let a = v.get::<i64>();
}
"#,
expect![[r#"
90..94 'self': &Self
126..127 'v': impl Trait
141..198 '{ ...f(); }': ()
147..148 'v': impl Trait
147..161 'v.get::<i32>()': Trait::Assoc<i32, impl Trait>
147..169 'v.get:...eref()': &i32
175..176 'v': impl Trait
175..187 'v.get::<T>()': Trait::Assoc<T, impl Trait>
175..195 'v.get:...eref()': &T
207..208 'v': impl Trait<Assoc<T> = &T>
240..296 '{ ...>(); }': ()
250..251 'a': &T
254..255 'v': impl Trait<Assoc<T> = &T>
254..266 'v.get::<T>()': &T
276..277 'a': Trait::Assoc<(), impl Trait<Assoc<T> = &T>>
280..281 'v': impl Trait<Assoc<T> = &T>
280..293 'v.get::<()>()': Trait::Assoc<(), impl Trait<Assoc<T> = &T>>
302..303 'v': impl Trait<Assoc<i32> = &i32, Assoc<i64> = &i64>
360..419 '{ ...>(); }': ()
370..371 'a': &i32
374..375 'v': impl Trait<Assoc<i32> = &i32, Assoc<i64> = &i64>
374..388 'v.get::<i32>()': &i32
398..399 'a': &i64
402..403 'v': impl Trait<Assoc<i32> = &i32, Assoc<i64> = &i64>
402..416 'v.get::<i64>()': &i64
"#]],
);
}
#[test]
fn gats_with_dyn() {
// This test is here to keep track of how we infer things despite traits with GATs being not
// object-safe currently.
// FIXME: reconsider how to treat these invalid types.
check_infer_with_mismatches(
r#"
//- minicore: deref
use core::ops::Deref;
trait Trait {
type Assoc<T>: Deref<Target = T>;
fn get<U>(&self) -> Self::Assoc<U>;
}
fn f<'a>(v: &dyn Trait<Assoc<i32> = &'a i32>) {
v.get::<i32>().deref();
}
"#,
expect![[r#"
90..94 'self': &Self
127..128 'v': &(dyn Trait<Assoc<i32> = &i32>)
164..195 '{ ...f(); }': ()
170..171 'v': &(dyn Trait<Assoc<i32> = &i32>)
170..184 'v.get::<i32>()': &i32
170..192 'v.get:...eref()': &i32
"#]],
);
}