Unnamed repository; edit this file 'description' to name the repository.
Lower generic arguments for associated types in paths
Ryo Yoshida 2022-10-27
parent 4dd6943 · commit f233ac4
-rw-r--r--crates/hir-ty/src/chalk_ext.rs17
-rw-r--r--crates/hir-ty/src/infer/path.rs2
-rw-r--r--crates/hir-ty/src/lower.rs93
-rw-r--r--crates/hir-ty/src/tests/traits.rs120
4 files changed, 193 insertions, 39 deletions
diff --git a/crates/hir-ty/src/chalk_ext.rs b/crates/hir-ty/src/chalk_ext.rs
index e2099d7e50..996b42f5bd 100644
--- a/crates/hir-ty/src/chalk_ext.rs
+++ b/crates/hir-ty/src/chalk_ext.rs
@@ -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 {
diff --git a/crates/hir-ty/src/infer/path.rs b/crates/hir-ty/src/infer/path.rs
index 7a4754cdc7..ebe9d6fb5e 100644
--- a/crates/hir-ty/src/infer/path.rs
+++ b/crates/hir-ty/src/infer/path.rs
@@ -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;
}
diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs
index 223d705b15..4447927451 100644
--- a/crates/hir-ty/src/lower.rs
+++ b/crates/hir-ty/src/lower.rs
@@ -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),
+ )
},
);
diff --git a/crates/hir-ty/src/tests/traits.rs b/crates/hir-ty/src/tests/traits.rs
index 555b6972fb..2b8468e85a 100644
--- a/crates/hir-ty/src/tests/traits.rs
+++ b/crates/hir-ty/src/tests/traits.rs
@@ -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
+ "#]],
+ );
+}