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 { + // 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, 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, @@ -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 @@ -1283,6 +1283,35 @@ fn f() { ); } +#[test] +fn resolve_const_generic_array_methods() { + check_types( + r#" +#[lang = "array"] +impl [T; N] { + pub fn map(self, f: F) -> [U; N] + where + F: FnMut(T) -> U, + { loop {} } +} + +#[lang = "slice"] +impl [T] { + pub fn map(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(