]> git.lizzy.rs Git - rust.git/blobdiff - crates/hir-ty/src/method_resolution.rs
Adapt to the new generic parameter/argument order
[rust.git] / crates / hir-ty / src / method_resolution.rs
index d5285c1710604471fb08b5782fcb6fe7bbc0d0e6..5998680dcd395390dcee0197baf6c8b6052a8acb 100644 (file)
@@ -8,8 +8,9 @@
 use base_db::{CrateId, Edition};
 use chalk_ir::{cast::Cast, Mutability, UniverseIndex};
 use hir_def::{
-    item_scope::ItemScope, nameres::DefMap, AssocItemId, BlockId, ConstId, FunctionId,
-    GenericDefId, HasModule, ImplId, ItemContainerId, Lookup, ModuleDefId, ModuleId, TraitId,
+    data::ImplData, item_scope::ItemScope, nameres::DefMap, AssocItemId, BlockId, ConstId,
+    FunctionId, GenericDefId, HasModule, ImplId, ItemContainerId, Lookup, ModuleDefId, ModuleId,
+    TraitId,
 };
 use hir_expand::name::Name;
 use rustc_hash::{FxHashMap, FxHashSet};
@@ -136,7 +137,7 @@ pub struct TraitImpls {
 
 impl TraitImpls {
     pub(crate) fn trait_impls_in_crate_query(db: &dyn HirDatabase, krate: CrateId) -> Arc<Self> {
-        let _p = profile::span("trait_impls_in_crate_query");
+        let _p = profile::span("trait_impls_in_crate_query").detail(|| format!("{krate:?}"));
         let mut impls = Self { map: FxHashMap::default() };
 
         let crate_def_map = db.crate_def_map(krate);
@@ -161,7 +162,7 @@ pub(crate) fn trait_impls_in_block_query(
     }
 
     pub(crate) fn trait_impls_in_deps_query(db: &dyn HirDatabase, krate: CrateId) -> Arc<Self> {
-        let _p = profile::span("trait_impls_in_deps_query");
+        let _p = profile::span("trait_impls_in_deps_query").detail(|| format!("{krate:?}"));
         let crate_graph = db.crate_graph();
         let mut res = Self { map: FxHashMap::default() };
 
@@ -213,8 +214,7 @@ fn merge(&mut self, other: &Self) {
         for (trait_, other_map) in &other.map {
             let map = self.map.entry(*trait_).or_default();
             for (fp, impls) in other_map {
-                let vec = map.entry(*fp).or_default();
-                vec.extend(impls);
+                map.entry(*fp).or_default().extend(impls);
             }
         }
     }
@@ -247,7 +247,7 @@ pub fn for_trait_and_self_ty(
         self.map
             .get(&trait_)
             .into_iter()
-            .flat_map(move |map| map.get(&None).into_iter().chain(map.get(&Some(self_ty))))
+            .flat_map(move |map| map.get(&Some(self_ty)).into_iter().chain(map.get(&None)))
             .flat_map(|v| v.iter().copied())
     }
 
@@ -336,7 +336,7 @@ pub fn all_impls(&self) -> impl Iterator<Item = ImplId> + '_ {
     }
 }
 
-pub fn inherent_impl_crates_query(
+pub(crate) fn inherent_impl_crates_query(
     db: &dyn HirDatabase,
     krate: CrateId,
     fp: TyFingerprint,
@@ -419,6 +419,55 @@ pub fn def_crates(
     }
 }
 
+pub fn lang_names_for_bin_op(op: syntax::ast::BinaryOp) -> Option<(Name, Name)> {
+    use hir_expand::name;
+    use syntax::ast::{ArithOp, BinaryOp, CmpOp, Ordering};
+    Some(match op {
+        BinaryOp::LogicOp(_) => return None,
+        BinaryOp::ArithOp(aop) => match aop {
+            ArithOp::Add => (name!(add), name!(add)),
+            ArithOp::Mul => (name!(mul), name!(mul)),
+            ArithOp::Sub => (name!(sub), name!(sub)),
+            ArithOp::Div => (name!(div), name!(div)),
+            ArithOp::Rem => (name!(rem), name!(rem)),
+            ArithOp::Shl => (name!(shl), name!(shl)),
+            ArithOp::Shr => (name!(shr), name!(shr)),
+            ArithOp::BitXor => (name!(bitxor), name!(bitxor)),
+            ArithOp::BitOr => (name!(bitor), name!(bitor)),
+            ArithOp::BitAnd => (name!(bitand), name!(bitand)),
+        },
+        BinaryOp::Assignment { op: Some(aop) } => match aop {
+            ArithOp::Add => (name!(add_assign), name!(add_assign)),
+            ArithOp::Mul => (name!(mul_assign), name!(mul_assign)),
+            ArithOp::Sub => (name!(sub_assign), name!(sub_assign)),
+            ArithOp::Div => (name!(div_assign), name!(div_assign)),
+            ArithOp::Rem => (name!(rem_assign), name!(rem_assign)),
+            ArithOp::Shl => (name!(shl_assign), name!(shl_assign)),
+            ArithOp::Shr => (name!(shr_assign), name!(shr_assign)),
+            ArithOp::BitXor => (name!(bitxor_assign), name!(bitxor_assign)),
+            ArithOp::BitOr => (name!(bitor_assign), name!(bitor_assign)),
+            ArithOp::BitAnd => (name!(bitand_assign), name!(bitand_assign)),
+        },
+        BinaryOp::CmpOp(cop) => match cop {
+            CmpOp::Eq { negated: false } => (name!(eq), name!(eq)),
+            CmpOp::Eq { negated: true } => (name!(ne), name!(eq)),
+            CmpOp::Ord { ordering: Ordering::Less, strict: false } => {
+                (name!(le), name!(partial_ord))
+            }
+            CmpOp::Ord { ordering: Ordering::Less, strict: true } => {
+                (name!(lt), name!(partial_ord))
+            }
+            CmpOp::Ord { ordering: Ordering::Greater, strict: false } => {
+                (name!(ge), name!(partial_ord))
+            }
+            CmpOp::Ord { ordering: Ordering::Greater, strict: true } => {
+                (name!(gt), name!(partial_ord))
+            }
+        },
+        BinaryOp::Assignment { op: None } => return None,
+    })
+}
+
 /// Look up the method with the given name.
 pub(crate) fn lookup_method(
     ty: &Canonical<Ty>,
@@ -491,7 +540,7 @@ pub struct ReceiverAdjustments {
 }
 
 impl ReceiverAdjustments {
-    pub(crate) fn apply(&self, table: &mut InferenceTable, ty: Ty) -> (Ty, Vec<Adjustment>) {
+    pub(crate) fn apply(&self, table: &mut InferenceTable<'_>, ty: Ty) -> (Ty, Vec<Adjustment>) {
         let mut ty = ty;
         let mut adjust = Vec::new();
         for _ in 0..self.autoderefs {
@@ -575,6 +624,59 @@ pub(crate) fn iterate_method_candidates<T>(
     slot
 }
 
+pub fn lookup_impl_method(
+    self_ty: &Ty,
+    db: &dyn HirDatabase,
+    env: Arc<TraitEnvironment>,
+    trait_: TraitId,
+    name: &Name,
+) -> Option<FunctionId> {
+    let self_ty_fp = TyFingerprint::for_trait_impl(self_ty)?;
+    let trait_impls = db.trait_impls_in_deps(env.krate);
+    let impls = trait_impls.for_trait_and_self_ty(trait_, self_ty_fp);
+    let mut table = InferenceTable::new(db, env.clone());
+    find_matching_impl(impls, &mut table, &self_ty).and_then(|data| {
+        data.items.iter().find_map(|it| match it {
+            AssocItemId::FunctionId(f) => (db.function_data(*f).name == *name).then(|| *f),
+            _ => None,
+        })
+    })
+}
+
+fn find_matching_impl(
+    mut impls: impl Iterator<Item = ImplId>,
+    table: &mut InferenceTable<'_>,
+    self_ty: &Ty,
+) -> Option<Arc<ImplData>> {
+    let db = table.db;
+    loop {
+        let impl_ = impls.next()?;
+        let r = table.run_in_snapshot(|table| {
+            let impl_data = db.impl_data(impl_);
+            let substs =
+                TyBuilder::subst_for_def(db, impl_, None).fill_with_inference_vars(table).build();
+            let impl_ty = db.impl_self_ty(impl_).substitute(Interner, &substs);
+
+            table
+                .unify(self_ty, &impl_ty)
+                .then(|| {
+                    let wh_goals =
+                        crate::chalk_db::convert_where_clauses(db, impl_.into(), &substs)
+                            .into_iter()
+                            .map(|b| b.cast(Interner));
+
+                    let goal = crate::Goal::all(Interner, wh_goals);
+
+                    table.try_obligation(goal).map(|_| impl_data)
+                })
+                .flatten()
+        });
+        if r.is_some() {
+            break r;
+        }
+    }
+}
+
 pub fn iterate_path_candidates(
     ty: &Canonical<Ty>,
     db: &dyn HirDatabase,
@@ -802,7 +904,7 @@ fn iterate_method_candidates_for_self_ty(
 
 fn iterate_trait_method_candidates(
     self_ty: &Ty,
-    table: &mut InferenceTable,
+    table: &mut InferenceTable<'_>,
     traits_in_scope: &FxHashSet<TraitId>,
     name: Option<&Name>,
     receiver_ty: Option<&Ty>,
@@ -812,22 +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(..));
-    // if ty is `dyn Trait`, the trait doesn't need to be in scope
-    let inherent_trait =
-        self_ty.dyn_trait().into_iter().flat_map(|t| all_super_traits(db.upcast(), t));
-    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 = inherent_trait.chain(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
@@ -868,7 +958,7 @@ fn iterate_trait_method_candidates(
 
 fn iterate_inherent_methods(
     self_ty: &Ty,
-    table: &mut InferenceTable,
+    table: &mut InferenceTable<'_>,
     name: Option<&Name>,
     receiver_ty: Option<&Ty>,
     receiver_adjustments: Option<ReceiverAdjustments>,
@@ -877,6 +967,44 @@ 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(_) => {
+            if let Some(principal_trait) = self_ty.dyn_trait() {
+                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(()),
@@ -918,10 +1046,32 @@ 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,
-        table: &mut InferenceTable,
+        table: &mut InferenceTable<'_>,
         name: Option<&Name>,
         receiver_ty: Option<&Ty>,
         receiver_adjustments: Option<ReceiverAdjustments>,
@@ -962,8 +1112,16 @@ pub fn resolve_indexing_op(
     None
 }
 
+macro_rules! check_that {
+    ($cond:expr) => {
+        if !$cond {
+            return false;
+        }
+    };
+}
+
 fn is_valid_candidate(
-    table: &mut InferenceTable,
+    table: &mut InferenceTable<'_>,
     name: Option<&Name>,
     receiver_ty: Option<&Ty>,
     item: AssocItemId,
@@ -973,75 +1131,25 @@ fn is_valid_candidate(
     let db = table.db;
     match item {
         AssocItemId::FunctionId(m) => {
-            let data = db.function_data(m);
-            if let Some(name) = name {
-                if &data.name != name {
-                    return false;
-                }
-            }
-            table.run_in_snapshot(|table| {
-                let subst = TyBuilder::subst_for_def(db, m).fill_with_inference_vars(table).build();
-                let expected_self_ty = match m.lookup(db.upcast()).container {
-                    ItemContainerId::TraitId(_) => {
-                        subst.at(Interner, 0).assert_ty_ref(Interner).clone()
-                    }
-                    ItemContainerId::ImplId(impl_id) => {
-                        subst.apply(db.impl_self_ty(impl_id).skip_binders().clone(), Interner)
-                    }
-                    // We should only get called for associated items (impl/trait)
-                    ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => {
-                        unreachable!()
-                    }
-                };
-                if !table.unify(&expected_self_ty, &self_ty) {
-                    return false;
-                }
-                if let Some(receiver_ty) = receiver_ty {
-                    if !data.has_self_param() {
-                        return false;
-                    }
-
-                    let sig = db.callable_item_signature(m.into());
-                    let expected_receiver =
-                        sig.map(|s| s.params()[0].clone()).substitute(Interner, &subst);
-                    let receiver_matches = table.unify(&receiver_ty, &expected_receiver);
-
-                    if !receiver_matches {
-                        return false;
-                    }
-                }
-                if let Some(from_module) = visible_from_module {
-                    if !db.function_visibility(m).is_visible_from(db.upcast(), from_module) {
-                        cov_mark::hit!(autoderef_candidate_not_visible);
-                        return false;
-                    }
-                }
-
-                true
-            })
+            is_valid_fn_candidate(table, m, name, receiver_ty, self_ty, visible_from_module)
         }
         AssocItemId::ConstId(c) => {
             let data = db.const_data(c);
-            if receiver_ty.is_some() {
-                return false;
-            }
-            if let Some(name) = name {
-                if data.name.as_ref() != Some(name) {
-                    return false;
-                }
-            }
-            if let Some(from_module) = visible_from_module {
-                if !db.const_visibility(c).is_visible_from(db.upcast(), from_module) {
+            check_that!(receiver_ty.is_none());
+
+            check_that!(name.map_or(true, |n| data.name.as_ref() == Some(n)));
+            check_that!(visible_from_module.map_or(true, |from_module| {
+                let v = db.const_visibility(c).is_visible_from(db.upcast(), from_module);
+                if !v {
                     cov_mark::hit!(const_candidate_not_visible);
-                    return false;
                 }
-            }
+                v
+            }));
             if let ItemContainerId::ImplId(impl_id) = c.lookup(db.upcast()).container {
                 let self_ty_matches = table.run_in_snapshot(|table| {
-                    let subst =
-                        TyBuilder::subst_for_def(db, c).fill_with_inference_vars(table).build();
-                    let expected_self_ty =
-                        subst.apply(db.impl_self_ty(impl_id).skip_binders().clone(), Interner);
+                    let expected_self_ty = TyBuilder::impl_self_ty(db, impl_id)
+                        .fill_with_inference_vars(table)
+                        .build();
                     table.unify(&expected_self_ty, &self_ty)
                 });
                 if !self_ty_matches {
@@ -1055,6 +1163,89 @@ fn is_valid_candidate(
     }
 }
 
+fn is_valid_fn_candidate(
+    table: &mut InferenceTable<'_>,
+    fn_id: FunctionId,
+    name: Option<&Name>,
+    receiver_ty: Option<&Ty>,
+    self_ty: &Ty,
+    visible_from_module: Option<ModuleId>,
+) -> bool {
+    let db = table.db;
+    let data = db.function_data(fn_id);
+
+    check_that!(name.map_or(true, |n| n == &data.name));
+    check_that!(visible_from_module.map_or(true, |from_module| {
+        let v = db.function_visibility(fn_id).is_visible_from(db.upcast(), from_module);
+        if !v {
+            cov_mark::hit!(autoderef_candidate_not_visible);
+        }
+        v
+    }));
+
+    table.run_in_snapshot(|table| {
+        let container = fn_id.lookup(db.upcast()).container;
+        let (impl_subst, expect_self_ty) = match container {
+            ItemContainerId::ImplId(it) => {
+                let subst =
+                    TyBuilder::subst_for_def(db, it, None).fill_with_inference_vars(table).build();
+                let self_ty = db.impl_self_ty(it).substitute(Interner, &subst);
+                (subst, self_ty)
+            }
+            ItemContainerId::TraitId(it) => {
+                let subst =
+                    TyBuilder::subst_for_def(db, it, None).fill_with_inference_vars(table).build();
+                let self_ty = subst.at(Interner, 0).assert_ty_ref(Interner).clone();
+                (subst, self_ty)
+            }
+            _ => unreachable!(),
+        };
+
+        let fn_subst = TyBuilder::subst_for_def(db, fn_id, Some(impl_subst.clone()))
+            .fill_with_inference_vars(table)
+            .build();
+
+        check_that!(table.unify(&expect_self_ty, self_ty));
+
+        if let Some(receiver_ty) = receiver_ty {
+            check_that!(data.has_self_param());
+
+            let sig = db.callable_item_signature(fn_id.into());
+            let expected_receiver =
+                sig.map(|s| s.params()[0].clone()).substitute(Interner, &fn_subst);
+
+            check_that!(table.unify(&receiver_ty, &expected_receiver));
+        }
+
+        if let ItemContainerId::ImplId(impl_id) = container {
+            // 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());
+            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())
+        } else {
+            // For `ItemContainerId::TraitId`, we check if `self_ty` implements the trait in
+            // `iterate_trait_method_candidates()`.
+            // For others, this function shouldn't be called.
+            true
+        }
+    })
+}
+
 pub fn implements_trait(
     ty: &Canonical<Ty>,
     db: &dyn HirDatabase,
@@ -1112,7 +1303,7 @@ fn generic_implements_goal(
 }
 
 fn autoderef_method_receiver(
-    table: &mut InferenceTable,
+    table: &mut InferenceTable<'_>,
     ty: Ty,
 ) -> (Vec<Canonical<Ty>>, Vec<ReceiverAdjustments>) {
     let (mut deref_chain, mut adjustments): (Vec<_>, Vec<_>) = (Vec::new(), Vec::new());