Unnamed repository; edit this file 'description' to name the repository.
fix: better resolve assoc item with type bound
austaras 2023-11-16
parent 58de0b1 · commit e95ec55
-rw-r--r--crates/hir-ty/src/infer/unify.rs29
-rw-r--r--crates/hir-ty/src/lower.rs23
-rw-r--r--crates/hir-ty/src/method_resolution.rs50
-rw-r--r--crates/ide-completion/src/completions/dot.rs77
4 files changed, 156 insertions, 23 deletions
diff --git a/crates/hir-ty/src/infer/unify.rs b/crates/hir-ty/src/infer/unify.rs
index 0a68a9f3b5..78bdd62a27 100644
--- a/crates/hir-ty/src/infer/unify.rs
+++ b/crates/hir-ty/src/infer/unify.rs
@@ -495,6 +495,35 @@ impl<'a> InferenceTable<'a> {
solution
}
+ pub(crate) fn try_resolve_alias(&mut self, goal: Goal) -> bool {
+ let in_env = InEnvironment::new(&self.trait_env.env, goal);
+ let canonicalized = self.canonicalize(in_env);
+ let solution = self.db.trait_solve(
+ self.trait_env.krate,
+ self.trait_env.block,
+ canonicalized.value.clone(),
+ );
+
+ match solution {
+ Some(Solution::Unique(canonical_subst)) => {
+ canonicalized.apply_solution(
+ self,
+ Canonical {
+ binders: canonical_subst.binders,
+ value: canonical_subst.value.subst,
+ },
+ );
+ true
+ }
+ Some(Solution::Ambig(Guidance::Definite(substs))) => {
+ canonicalized.apply_solution(self, substs);
+ true
+ }
+ Some(_) => true,
+ None => false,
+ }
+ }
+
pub(crate) fn register_obligation(&mut self, goal: Goal) {
let in_env = InEnvironment::new(&self.trait_env.env, goal);
self.register_obligation_in_env(in_env)
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..04a55c3536 100644
--- a/crates/hir-ty/src/method_resolution.rs
+++ b/crates/hir-ty/src/method_resolution.rs
@@ -1478,26 +1478,38 @@ 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 mut alias = Vec::new();
+ let mut other_predicate = Vec::new();
+
+ for predicate in predicates.iter() {
+ 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);
+
+ if let WhereClause::AliasEq(_) = p {
+ alias.push(p);
+ } else {
+ other_predicate.push(p);
+ }
+ }
+
+ for p in alias {
+ if !table.try_resolve_alias(p.cast(Interner)) {
+ return IsValidCandidate::No;
+ }
}
+
+ for p in other_predicate {
+ if table.try_obligation(p.cast(Interner)).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/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
+ "#]],
+ );
+ }
}