Unnamed repository; edit this file 'description' to name the repository.
Lower generic arguments for GATs in associated type bindings
Ryo Yoshida 2022-10-27
parent 63cba43 · commit 5fc18ad
-rw-r--r--crates/hir-ty/src/lower.rs31
-rw-r--r--crates/hir-ty/src/tests/traits.rs37
2 files changed, 66 insertions, 2 deletions
diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs
index 4447927451..22a85cf154 100644
--- a/crates/hir-ty/src/lower.rs
+++ b/crates/hir-ty/src/lower.rs
@@ -808,7 +808,15 @@ impl<'a> TyLoweringContext<'a> {
// handle defaults. In expression or pattern path segments without
// explicitly specified type arguments, missing type arguments are inferred
// (i.e. defaults aren't used).
- if !infer_args || had_explicit_args {
+ // Generic parameters for associated types are not supposed to have defaults, so we just
+ // ignore them.
+ let is_assoc_ty = if let GenericDefId::TypeAliasId(id) = def {
+ let container = id.lookup(self.db.upcast()).container;
+ matches!(container, ItemContainerId::TraitId(_))
+ } else {
+ false
+ };
+ if !is_assoc_ty && (!infer_args || had_explicit_args) {
let defaults = self.db.generic_defaults(def);
assert_eq!(total_len, defaults.len());
let parent_from = item_len - substs.len();
@@ -997,9 +1005,28 @@ impl<'a> TyLoweringContext<'a> {
None => return SmallVec::new(),
Some(t) => t,
};
+ // 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 (`super_trait_ref.substitution`).
+ let substitution = self.substs_from_path_segment(
+ // FIXME: This is hack. We shouldn't really build `PathSegment` directly.
+ PathSegment { name: &binding.name, args_and_bindings: binding.args.as_deref() },
+ Some(associated_ty.into()),
+ false, // this is not relevant
+ Some(super_trait_ref.self_type_parameter(Interner)),
+ );
+ let self_params = generics(self.db.upcast(), associated_ty.into()).len_self();
+ let substitution = Substitution::from_iter(
+ Interner,
+ substitution
+ .iter(Interner)
+ .take(self_params)
+ .chain(super_trait_ref.substitution.iter(Interner)),
+ );
let projection_ty = ProjectionTy {
associated_ty_id: to_assoc_type_id(associated_ty),
- substitution: super_trait_ref.substitution,
+ substitution,
};
let mut preds: SmallVec<[_; 1]> = SmallVec::with_capacity(
binding.type_ref.as_ref().map_or(0, |_| 1) + binding.bounds.len(),
diff --git a/crates/hir-ty/src/tests/traits.rs b/crates/hir-ty/src/tests/traits.rs
index 2b8468e85a..7995f6446d 100644
--- a/crates/hir-ty/src/tests/traits.rs
+++ b/crates/hir-ty/src/tests/traits.rs
@@ -4083,3 +4083,40 @@ fn f<'a>(v: &dyn Trait<Assoc<i32> = &'a i32>) {
"#]],
);
}
+
+#[test]
+fn gats_in_associated_type_binding() {
+ check_infer_with_mismatches(
+ r#"
+trait Trait {
+ type Assoc<T>;
+ fn get<U>(&self) -> Self::Assoc<U>;
+}
+
+fn f<T>(t: T)
+where
+ T: Trait<Assoc<i32> = u32>,
+ T: Trait<Assoc<isize> = usize>,
+{
+ let a = t.get::<i32>();
+ let a = t.get::<isize>();
+ let a = t.get::<()>();
+}
+
+ "#,
+ expect![[r#"
+ 48..52 'self': &Self
+ 84..85 't': T
+ 164..252 '{ ...>(); }': ()
+ 174..175 'a': u32
+ 178..179 't': T
+ 178..192 't.get::<i32>()': u32
+ 202..203 'a': usize
+ 206..207 't': T
+ 206..222 't.get:...ize>()': usize
+ 232..233 'a': Trait::Assoc<(), T>
+ 236..237 't': T
+ 236..249 't.get::<()>()': Trait::Assoc<(), T>
+ "#]],
+ )
+}