about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-11-21 09:44:43 +0000
committerbors <bors@rust-lang.org>2023-11-21 09:44:43 +0000
commit8d3522e557d56bc4c40eed25403a060542167e62 (patch)
tree9d00d2671e1c26b85170b8aa710ca2f5f872a3e3
parent2e7e8cc7b988f1949e03a80ecf5d526d9cb055e4 (diff)
parent808f6687f7b796dd2c171e3be3829f1dedb6d537 (diff)
downloadrust-8d3522e557d56bc4c40eed25403a060542167e62.tar.gz
rust-8d3522e557d56bc4c40eed25403a060542167e62.zip
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.rs2
-rw-r--r--crates/hir-ty/src/lower.rs23
-rw-r--r--crates/hir-ty/src/method_resolution.rs69
-rw-r--r--crates/hir-ty/src/tests/traits.rs28
-rw-r--r--crates/ide-completion/src/completions/dot.rs77
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 0a68a9f3b58..ac39bdf5bf5 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 c8a85b4a9ff..04005311b67 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 87c93283361..732643566a2 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 48dd9540329..003ae60e8e5 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 c5bbb7f8d75..5bcc867fe18 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
+        "#]],
+        );
+    }
 }