mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-28 12:55:11 +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::{
|
use crate::{
|
||||||
db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id,
|
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,
|
from_placeholder_idx, to_chalk_trait_id, utils::generics, AdtId, AliasEq, AliasTy, Binders,
|
||||||
CallableSig, FnPointer, ImplTraitId, Interner, Lifetime, ProjectionTy, QuantifiedWhereClause,
|
CallableDefId, CallableSig, FnPointer, ImplTraitId, Interner, Lifetime, ProjectionTy,
|
||||||
Substitution, TraitRef, Ty, TyBuilder, TyKind, WhereClause,
|
QuantifiedWhereClause, Substitution, TraitRef, Ty, TyBuilder, TyKind, WhereClause,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub trait TyExt {
|
pub trait TyExt {
|
||||||
|
@ -338,10 +338,13 @@ pub trait ProjectionTyExt {
|
||||||
|
|
||||||
impl ProjectionTyExt for ProjectionTy {
|
impl ProjectionTyExt for ProjectionTy {
|
||||||
fn trait_ref(&self, db: &dyn HirDatabase) -> TraitRef {
|
fn trait_ref(&self, db: &dyn HirDatabase) -> TraitRef {
|
||||||
TraitRef {
|
// FIXME: something like `Split` trait from chalk-solve might be nice.
|
||||||
trait_id: to_chalk_trait_id(self.trait_(db)),
|
let generics = generics(db.upcast(), from_assoc_type_id(self.associated_ty_id).into());
|
||||||
substitution: self.substitution.clone(),
|
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 {
|
fn trait_(&self, db: &dyn HirDatabase) -> TraitId {
|
||||||
|
|
|
@ -157,7 +157,7 @@ impl<'a> InferenceContext<'a> {
|
||||||
remaining_segments_for_ty,
|
remaining_segments_for_ty,
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
if let TyKind::Error = ty.kind(Interner) {
|
if ty.is_unknown() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -447,12 +447,31 @@ impl<'a> TyLoweringContext<'a> {
|
||||||
.db
|
.db
|
||||||
.trait_data(trait_ref.hir_trait_id())
|
.trait_data(trait_ref.hir_trait_id())
|
||||||
.associated_type_by_name(segment.name);
|
.associated_type_by_name(segment.name);
|
||||||
|
|
||||||
match found {
|
match found {
|
||||||
Some(associated_ty) => {
|
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 {
|
TyKind::Alias(AliasTy::Projection(ProjectionTy {
|
||||||
associated_ty_id: to_assoc_type_id(associated_ty),
|
associated_ty_id: to_assoc_type_id(associated_ty),
|
||||||
substitution: trait_ref.substitution,
|
substitution,
|
||||||
}))
|
}))
|
||||||
.intern(Interner)
|
.intern(Interner)
|
||||||
}
|
}
|
||||||
|
@ -590,36 +609,48 @@ impl<'a> TyLoweringContext<'a> {
|
||||||
res,
|
res,
|
||||||
Some(segment.name.clone()),
|
Some(segment.name.clone()),
|
||||||
move |name, t, associated_ty| {
|
move |name, t, associated_ty| {
|
||||||
if name == segment.name {
|
if name != segment.name {
|
||||||
let substs = match self.type_param_mode {
|
return None;
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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