10180: Fix resolution for inherent array methods r=flodiebold a=yotamofek

My second attempt at fixing #9992 , previous attempt was here: #10017 , but the logic was broken.

I know that this is not an ideal solution.... that would require, IIUC, a pretty big overhaul of the const generics handling in `rust-analyzer`. But, given that some of the array methods were/are being stabilized (e.g. https://github.com/rust-lang/rust/pull/87174 ), I think it'll be very beneficial to `rust-analyzer` users to have some preliminary support for them. (I know it's something I've been running into quite a lot lately :) )

As far as my limited understanding of this project's architecture goes, I think this isn't the worst hack in the world, and shouldn't be too much of a hassle to undo if/when const generics become better supported. If the maintainers deem this approach viable, I'll want to add some comments, emphasizing the purpose of this code, and that it should be removed at some point in the future.

Co-authored-by: Yotam Ofek <yotam.ofek@gmail.com>
This commit is contained in:
bors[bot] 2021-09-08 12:46:34 +00:00 committed by GitHub
commit 108b0809e0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 84 additions and 2 deletions

View file

@ -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;
}
}

View file

@ -1283,6 +1283,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(