mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-14 14:13:58 +00:00
Lower generic arguments for associated types in paths
This commit is contained in:
parent
4dd694371a
commit
f233ac447f
4 changed files with 193 additions and 39 deletions
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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),
|
||||
)
|
||||
},
|
||||
);
|
||||
|
||||
|
|
|
@ -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
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue