about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRyo Yoshida <low.ryoshida@gmail.com>2022-08-31 01:00:53 +0900
committerRyo Yoshida <low.ryoshida@gmail.com>2022-08-31 01:12:14 +0900
commit484d5b6e70e30e3e2b1714c9dcf641bb47f01b7c (patch)
tree4eacbf35b43e325d74e6476659435ddba3f0cbc8
parentf9e2ac56e56cd011929b28d20edda8bed33e9a76 (diff)
downloadrust-484d5b6e70e30e3e2b1714c9dcf641bb47f01b7c.tar.gz
rust-484d5b6e70e30e3e2b1714c9dcf641bb47f01b7c.zip
fix: handle trait methods as inherent methods for placeholders
-rw-r--r--crates/hir-ty/src/method_resolution.rs87
-rw-r--r--crates/hir-ty/src/tests/method_resolution.rs17
2 files changed, 77 insertions, 27 deletions
diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs
index c0ef4b2a9dc..dbf27500320 100644
--- a/crates/hir-ty/src/method_resolution.rs
+++ b/crates/hir-ty/src/method_resolution.rs
@@ -914,19 +914,10 @@ fn iterate_trait_method_candidates(
     let db = table.db;
     let env = table.trait_env.clone();
     let self_is_array = matches!(self_ty.kind(Interner), chalk_ir::TyKind::Array(..));
-    let env_traits = matches!(self_ty.kind(Interner), TyKind::Placeholder(_))
-        // if we have `T: Trait` in the param env, the trait doesn't need to be in scope
-        .then(|| {
-            env.traits_in_scope_from_clauses(self_ty.clone())
-                .flat_map(|t| all_super_traits(db.upcast(), t))
-        })
-        .into_iter()
-        .flatten();
-    let traits = env_traits.chain(traits_in_scope.iter().copied());
 
     let canonical_self_ty = table.canonicalize(self_ty.clone()).value;
 
-    'traits: for t in traits {
+    'traits: for &t in traits_in_scope {
         let data = db.trait_data(t);
 
         // Traits annotated with `#[rustc_skip_array_during_method_dispatch]` are skipped during
@@ -976,6 +967,43 @@ fn iterate_inherent_methods(
 ) -> ControlFlow<()> {
     let db = table.db;
     let env = table.trait_env.clone();
+
+    // For trait object types and placeholder types with trait bounds, the methods of the trait and
+    // its super traits are considered inherent methods. This matters because these methods have
+    // higher priority than the other traits' methods, which would be considered in
+    // `iterate_trait_method_candidates()` only after this function.
+    match self_ty.kind(Interner) {
+        TyKind::Placeholder(_) => {
+            let env = table.trait_env.clone();
+            let traits = env
+                .traits_in_scope_from_clauses(self_ty.clone())
+                .flat_map(|t| all_super_traits(db.upcast(), t));
+            iterate_inherent_trait_methods(
+                self_ty,
+                table,
+                name,
+                receiver_ty,
+                receiver_adjustments.clone(),
+                callback,
+                traits,
+            )?;
+        }
+        TyKind::Dyn(_) => {
+            let principal_trait = self_ty.dyn_trait().unwrap();
+            let traits = all_super_traits(db.upcast(), principal_trait);
+            iterate_inherent_trait_methods(
+                self_ty,
+                table,
+                name,
+                receiver_ty,
+                receiver_adjustments.clone(),
+                callback,
+                traits.into_iter(),
+            )?;
+        }
+        _ => {}
+    }
+
     let def_crates = match def_crates(db, self_ty, env.krate) {
         Some(k) => k,
         None => return ControlFlow::Continue(()),
@@ -987,23 +1015,6 @@ fn iterate_inherent_methods(
         VisibleFromModule::None => (None, None),
     };
 
-    // For trait object types, methods of the trait and its super traits are considered inherent
-    // methods. This matters because these trait methods have higher priority than the other
-    // traits' methods, which would be considered in `iterate_trait_method_candidates()` after this
-    // function.
-    let inherent_traits =
-        self_ty.dyn_trait().into_iter().flat_map(|t| all_super_traits(db.upcast(), t));
-    for t in inherent_traits {
-        let data = db.trait_data(t);
-        for &(_, item) in data.items.iter() {
-            // We don't pass `visible_from_module` as all trait items should be visible from the
-            // trait object.
-            if is_valid_candidate(table, name, receiver_ty, item, self_ty, None) {
-                callback(receiver_adjustments.clone().unwrap_or_default(), item)?;
-            }
-        }
-    }
-
     if let Some(block_id) = block {
         if let Some(impls) = db.inherent_impls_in_block(block_id) {
             impls_for_self_ty(
@@ -1034,6 +1045,28 @@ fn iterate_inherent_methods(
     }
     return ControlFlow::Continue(());
 
+    fn iterate_inherent_trait_methods(
+        self_ty: &Ty,
+        table: &mut InferenceTable<'_>,
+        name: Option<&Name>,
+        receiver_ty: Option<&Ty>,
+        receiver_adjustments: Option<ReceiverAdjustments>,
+        callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>,
+        traits: impl Iterator<Item = TraitId>,
+    ) -> ControlFlow<()> {
+        let db = table.db;
+        for t in traits {
+            let data = db.trait_data(t);
+            for &(_, item) in data.items.iter() {
+                // We don't pass `visible_from_module` as all trait items should be visible.
+                if is_valid_candidate(table, name, receiver_ty, item, self_ty, None) {
+                    callback(receiver_adjustments.clone().unwrap_or_default(), item)?;
+                }
+            }
+        }
+        ControlFlow::Continue(())
+    }
+
     fn impls_for_self_ty(
         impls: &InherentImpls,
         self_ty: &Ty,
diff --git a/crates/hir-ty/src/tests/method_resolution.rs b/crates/hir-ty/src/tests/method_resolution.rs
index fb2fc9369a7..ac8edb841a5 100644
--- a/crates/hir-ty/src/tests/method_resolution.rs
+++ b/crates/hir-ty/src/tests/method_resolution.rs
@@ -1236,6 +1236,23 @@ fn foo(a: &dyn Trait) {
 }
 
 #[test]
+fn trait_method_priority_for_placeholder_type() {
+    check_types(
+        r#"
+//- minicore: from
+trait Trait {
+    fn into(&self) -> usize { 0 }
+}
+
+fn foo<T: Trait>(a: &T) {
+    let _ = a.into();
+      //^usize
+}
+        "#,
+    );
+}
+
+#[test]
 fn autoderef_visibility_field() {
     check(
         r#"