Unnamed repository; edit this file 'description' to name the repository.
Auto merge of #15825 - Austaras:master, r=flodiebold
fix: better resolve assoc item with type bound
Closes #15772
| -rw-r--r-- | crates/hir-ty/src/infer/unify.rs | 2 | ||||
| -rw-r--r-- | crates/hir-ty/src/lower.rs | 23 | ||||
| -rw-r--r-- | crates/hir-ty/src/method_resolution.rs | 69 | ||||
| -rw-r--r-- | crates/hir-ty/src/tests/traits.rs | 28 | ||||
| -rw-r--r-- | crates/ide-completion/src/completions/dot.rs | 77 |
5 files changed, 173 insertions, 26 deletions
diff --git a/crates/hir-ty/src/infer/unify.rs b/crates/hir-ty/src/infer/unify.rs index 0a68a9f3b5..ac39bdf5bf 100644 --- a/crates/hir-ty/src/infer/unify.rs +++ b/crates/hir-ty/src/infer/unify.rs @@ -43,7 +43,7 @@ where } impl<T: HasInterner<Interner = Interner>> Canonicalized<T> { - pub(super) fn apply_solution( + pub(crate) fn apply_solution( &self, ctx: &mut InferenceTable<'_>, solution: Canonical<Substitution>, diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs index c8a85b4a9f..04005311b6 100644 --- a/crates/hir-ty/src/lower.rs +++ b/crates/hir-ty/src/lower.rs @@ -1097,10 +1097,25 @@ impl<'a> TyLoweringContext<'a> { binding.type_ref.as_ref().map_or(0, |_| 1) + binding.bounds.len(), ); if let Some(type_ref) = &binding.type_ref { - let ty = self.lower_ty(type_ref); - let alias_eq = - AliasEq { alias: AliasTy::Projection(projection_ty.clone()), ty }; - predicates.push(crate::wrap_empty_binders(WhereClause::AliasEq(alias_eq))); + if let (TypeRef::ImplTrait(bounds), ImplTraitLoweringState::Disallowed) = + (type_ref, &self.impl_trait_mode) + { + for bound in bounds { + predicates.extend( + self.lower_type_bound( + bound, + TyKind::Alias(AliasTy::Projection(projection_ty.clone())) + .intern(Interner), + false, + ), + ); + } + } else { + let ty = self.lower_ty(type_ref); + let alias_eq = + AliasEq { alias: AliasTy::Projection(projection_ty.clone()), ty }; + predicates.push(crate::wrap_empty_binders(WhereClause::AliasEq(alias_eq))); + } } for bound in binding.bounds.iter() { predicates.extend(self.lower_type_bound( diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs index 87c9328336..732643566a 100644 --- a/crates/hir-ty/src/method_resolution.rs +++ b/crates/hir-ty/src/method_resolution.rs @@ -27,8 +27,9 @@ use crate::{ primitive::{FloatTy, IntTy, UintTy}, static_lifetime, to_chalk_trait_id, utils::all_super_traits, - AdtId, Canonical, CanonicalVarKinds, DebruijnIndex, DynTyExt, ForeignDefId, InEnvironment, - Interner, Scalar, Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyExt, + AdtId, Canonical, CanonicalVarKinds, DebruijnIndex, DynTyExt, ForeignDefId, Goal, Guidance, + InEnvironment, Interner, Scalar, Solution, Substitution, TraitEnvironment, TraitRef, + TraitRefExt, Ty, TyBuilder, TyExt, }; /// This is used as a key for indexing impls. @@ -1478,26 +1479,52 @@ fn is_valid_fn_candidate( // We need to consider the bounds on the impl to distinguish functions of the same name // for a type. let predicates = db.generic_predicates(impl_id.into()); - let valid = predicates - .iter() - .map(|predicate| { - let (p, b) = predicate - .clone() - .substitute(Interner, &impl_subst) - // Skipping the inner binders is ok, as we don't handle quantified where - // clauses yet. - .into_value_and_skipped_binders(); - stdx::always!(b.len(Interner) == 0); - p - }) - // It's ok to get ambiguity here, as we may not have enough information to prove - // obligations. We'll check if the user is calling the selected method properly - // later anyway. - .all(|p| table.try_obligation(p.cast(Interner)).is_some()); - match valid { - true => IsValidCandidate::Yes, - false => IsValidCandidate::No, + let goals = predicates.iter().map(|p| { + let (p, b) = p + .clone() + .substitute(Interner, &impl_subst) + // Skipping the inner binders is ok, as we don't handle quantified where + // clauses yet. + .into_value_and_skipped_binders(); + stdx::always!(b.len(Interner) == 0); + + p.cast::<Goal>(Interner) + }); + + for goal in goals.clone() { + let in_env = InEnvironment::new(&table.trait_env.env, goal); + let canonicalized = table.canonicalize(in_env); + let solution = table.db.trait_solve( + table.trait_env.krate, + table.trait_env.block, + canonicalized.value.clone(), + ); + + match solution { + Some(Solution::Unique(canonical_subst)) => { + canonicalized.apply_solution( + table, + Canonical { + binders: canonical_subst.binders, + value: canonical_subst.value.subst, + }, + ); + } + Some(Solution::Ambig(Guidance::Definite(substs))) => { + canonicalized.apply_solution(table, substs); + } + Some(_) => (), + None => return IsValidCandidate::No, + } } + + for goal in goals { + if table.try_obligation(goal).is_none() { + return IsValidCandidate::No; + } + } + + IsValidCandidate::Yes } else { // For `ItemContainerId::TraitId`, we check if `self_ty` implements the trait in // `iterate_trait_method_candidates()`. diff --git a/crates/hir-ty/src/tests/traits.rs b/crates/hir-ty/src/tests/traits.rs index 48dd954032..003ae60e8e 100644 --- a/crates/hir-ty/src/tests/traits.rs +++ b/crates/hir-ty/src/tests/traits.rs @@ -2598,6 +2598,34 @@ fn test<T: Trait>() { } #[test] +fn associated_type_in_type_bound() { + check_types( + r#" +//- minicore: deref +fn fb(f: Foo<&u8>) { + f.foobar(); + //^^^^^^^^^^ u8 +} +trait Bar { + fn bar(&self) -> u8; +} +impl Bar for u8 { + fn bar(&self) -> u8 { *self } +} + +struct Foo<F> { + foo: F, +} +impl<F: core::ops::Deref<Target = impl Bar>> Foo<F> { + fn foobar(&self) -> u8 { + self.foo.deref().bar() + } +} +"#, + ) +} + +#[test] fn dyn_trait_through_chalk() { check_types( r#" diff --git a/crates/ide-completion/src/completions/dot.rs b/crates/ide-completion/src/completions/dot.rs index c5bbb7f8d7..5bcc867fe1 100644 --- a/crates/ide-completion/src/completions/dot.rs +++ b/crates/ide-completion/src/completions/dot.rs @@ -1095,4 +1095,81 @@ fn test(s: S<Unknown>) { "#]], ); } + + #[test] + fn assoc_impl_1() { + check( + r#" +//- minicore: deref +fn main() { + let foo: Foo<&u8> = Foo::new(&42_u8); + foo.$0 +} + +trait Bar { + fn bar(&self); +} + +impl Bar for u8 { + fn bar(&self) {} +} + +struct Foo<F> { + foo: F, +} + +impl<F> Foo<F> { + fn new(foo: F) -> Foo<F> { + Foo { foo } + } +} + +impl<F: core::ops::Deref<Target = impl Bar>> Foo<F> { + fn foobar(&self) { + self.foo.deref().bar() + } +} +"#, + expect![[r#" + fd foo &u8 + me foobar() fn(&self) + "#]], + ); + } + + #[test] + fn assoc_impl_2() { + check( + r#" +//- minicore: deref +fn main() { + let foo: Foo<&u8> = Foo::new(&42_u8); + foo.$0 +} + +trait Bar { + fn bar(&self); +} + +struct Foo<F> { + foo: F, +} + +impl<F> Foo<F> { + fn new(foo: F) -> Foo<F> { + Foo { foo } + } +} + +impl<B: Bar, F: core::ops::Deref<Target = B>> Foo<F> { + fn foobar(&self) { + self.foo.deref().bar() + } +} +"#, + expect![[r#" + fd foo &u8 + "#]], + ); + } } |