Unnamed repository; edit this file 'description' to name the repository.
-rw-r--r--crates/hir_ty/src/method_resolution.rs57
-rw-r--r--crates/hir_ty/src/tests/method_resolution.rs29
2 files changed, 84 insertions, 2 deletions
diff --git a/crates/hir_ty/src/method_resolution.rs b/crates/hir_ty/src/method_resolution.rs
index a90e3bee68..f12ced24cc 100644
--- a/crates/hir_ty/src/method_resolution.rs
+++ b/crates/hir_ty/src/method_resolution.rs
@@ -16,6 +16,7 @@ use rustc_hash::{FxHashMap, FxHashSet};
use crate::{
autoderef,
+ consteval::{self, ConstExt},
db::HirDatabase,
from_foreign_def_id,
primitive::{self, FloatTy, IntTy, UintTy},
@@ -708,6 +709,33 @@ fn iterate_trait_method_candidates(
false
}
+fn filter_inherent_impls_for_self_ty<'i>(
+ impls: &'i InherentImpls,
+ self_ty: &Ty,
+) -> impl Iterator<Item = &'i ImplId> {
+ // inherent methods on arrays are fingerprinted as [T; {unknown}], so we must also consider them when
+ // resolving a method call on an array with a known len
+ let array_impls = {
+ if let TyKind::Array(parameters, array_len) = self_ty.kind(&Interner) {
+ if !array_len.is_unknown() {
+ let unknown_array_len_ty =
+ TyKind::Array(parameters.clone(), consteval::usize_const(None))
+ .intern(&Interner);
+
+ Some(impls.for_self_ty(&unknown_array_len_ty))
+ } else {
+ None
+ }
+ } else {
+ None
+ }
+ }
+ .into_iter()
+ .flatten();
+
+ impls.for_self_ty(self_ty).iter().chain(array_impls)
+}
+
fn iterate_inherent_methods(
self_ty: &Canonical<Ty>,
db: &dyn HirDatabase,
@@ -725,7 +753,9 @@ fn iterate_inherent_methods(
for krate in def_crates {
let impls = db.inherent_impls_in_crate(krate);
- for &impl_def in impls.for_self_ty(&self_ty.value) {
+ let impls_for_self_ty = filter_inherent_impls_for_self_ty(&impls, &self_ty.value);
+
+ for &impl_def in impls_for_self_ty {
for &item in db.impl_data(impl_def).items.iter() {
if !is_valid_candidate(
db,
@@ -777,6 +807,28 @@ pub fn resolve_indexing_op(
None
}
+fn is_transformed_receiver_ty_equal(transformed_receiver_ty: &Ty, receiver_ty: &Ty) -> bool {
+ if transformed_receiver_ty == receiver_ty {
+ return true;
+ }
+
+ // a transformed receiver may be considered equal (and a valid method call candidate) if it is an array
+ // with an unknown (i.e. generic) length, and the receiver is an array with the same item type but a known len,
+ // this allows inherent methods on arrays to be considered valid resolution candidates
+ match (transformed_receiver_ty.kind(&Interner), receiver_ty.kind(&Interner)) {
+ (
+ TyKind::Array(transformed_array_ty, transformed_array_len),
+ TyKind::Array(receiver_array_ty, receiver_array_len),
+ ) if transformed_array_ty == receiver_array_ty
+ && transformed_array_len.is_unknown()
+ && !receiver_array_len.is_unknown() =>
+ {
+ true
+ }
+ _ => false,
+ }
+}
+
fn is_valid_candidate(
db: &dyn HirDatabase,
env: Arc<TraitEnvironment>,
@@ -802,7 +854,8 @@ fn is_valid_candidate(
Some(ty) => ty,
None => return false,
};
- if transformed_receiver_ty != receiver_ty.value {
+
+ if !is_transformed_receiver_ty_equal(&transformed_receiver_ty, &receiver_ty.value) {
return false;
}
}
diff --git a/crates/hir_ty/src/tests/method_resolution.rs b/crates/hir_ty/src/tests/method_resolution.rs
index f8d7f2b642..306da6738b 100644
--- a/crates/hir_ty/src/tests/method_resolution.rs
+++ b/crates/hir_ty/src/tests/method_resolution.rs
@@ -1284,6 +1284,35 @@ fn f() {
}
#[test]
+fn resolve_const_generic_array_methods() {
+ check_types(
+ r#"
+#[lang = "array"]
+impl<T, const N: usize> [T; N] {
+ pub fn map<F, U>(self, f: F) -> [U; N]
+ where
+ F: FnMut(T) -> U,
+ { loop {} }
+}
+
+#[lang = "slice"]
+impl<T> [T] {
+ pub fn map<F, U>(self, f: F) -> &[U]
+ where
+ F: FnMut(T) -> U,
+ { loop {} }
+}
+
+fn f() {
+ let v = [1, 2].map::<_, usize>(|x| -> x * 2);
+ v;
+ //^ [usize; _]
+}
+ "#,
+ );
+}
+
+#[test]
fn skip_array_during_method_dispatch() {
check_types(
r#"