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: Deref; +} + +fn f(p: P::Pointer) { + let a = *p; +} +fn g(p:

::Pointer) { + let a = *p; +} + "#, + expect![[r#" + 110..111 'p': PointerFamily::Pointer + 130..149 '{ ... *p; }': () + 140..141 'a': i32 + 144..146 '*p': i32 + 145..146 'p': PointerFamily::Pointer + 173..174 'p': PointerFamily::Pointer + 212..231 '{ ... *p; }': () + 222..223 'a': i32 + 226..228 '*p': i32 + 227..228 'p': PointerFamily::Pointer + "#]], + ); +} + +#[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: Deref; + fn get(&self) -> Self::Assoc; +} + +fn f(v: impl Trait) { + v.get::().deref(); + v.get::().deref(); +} +fn g(v: impl Trait = &'a T>) { + let a = v.get::(); + let a = v.get::<()>(); +} +fn h(v: impl Trait = &'a i32, Assoc = &'a i64> { + let a = v.get::(); + let a = v.get::(); +} + "#, + expect![[r#" + 90..94 'self': &Self + 126..127 'v': impl Trait + 141..198 '{ ...f(); }': () + 147..148 'v': impl Trait + 147..161 'v.get::()': Trait::Assoc + 147..169 'v.get:...eref()': &i32 + 175..176 'v': impl Trait + 175..187 'v.get::()': Trait::Assoc + 175..195 'v.get:...eref()': &T + 207..208 'v': impl Trait = &T> + 240..296 '{ ...>(); }': () + 250..251 'a': &T + 254..255 'v': impl Trait = &T> + 254..266 'v.get::()': &T + 276..277 'a': Trait::Assoc<(), impl Trait = &T>> + 280..281 'v': impl Trait = &T> + 280..293 'v.get::<()>()': Trait::Assoc<(), impl Trait = &T>> + 302..303 'v': impl Trait = &i32, Assoc = &i64> + 360..419 '{ ...>(); }': () + 370..371 'a': &i32 + 374..375 'v': impl Trait = &i32, Assoc = &i64> + 374..388 'v.get::()': &i32 + 398..399 'a': &i64 + 402..403 'v': impl Trait = &i32, Assoc = &i64> + 402..416 'v.get::()': &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: Deref; + fn get(&self) -> Self::Assoc; +} + +fn f<'a>(v: &dyn Trait = &'a i32>) { + v.get::().deref(); +} + "#, + expect![[r#" + 90..94 'self': &Self + 127..128 'v': &(dyn Trait = &i32>) + 164..195 '{ ...f(); }': () + 170..171 'v': &(dyn Trait = &i32>) + 170..184 'v.get::()': &i32 + 170..192 'v.get:...eref()': &i32 + "#]], + ); +}