From 955064b6aaa5c24e980328f9d9fbe731cc29636c Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Tue, 1 Jun 2021 21:33:14 +0200 Subject: Implement `#[rustc_skip_array_during_method_dispatch]` --- crates/hir_def/src/data.rs | 14 +++++++- crates/hir_ty/src/method_resolution.rs | 16 ++++++++- crates/hir_ty/src/tests/method_resolution.rs | 49 ++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 2 deletions(-) (limited to 'crates') diff --git a/crates/hir_def/src/data.rs b/crates/hir_def/src/data.rs index d2bb381be..2f06a6e29 100644 --- a/crates/hir_def/src/data.rs +++ b/crates/hir_def/src/data.rs @@ -143,6 +143,7 @@ pub struct TraitData { pub is_auto: bool, pub is_unsafe: bool, pub visibility: RawVisibility, + pub skip_array_during_method_dispatch: bool, } impl TraitData { @@ -157,6 +158,10 @@ impl TraitData { let container = AssocContainerId::TraitId(tr); let visibility = item_tree[tr_def.visibility].clone(); let mut expander = Expander::new(db, tr_loc.id.file_id(), module_id); + let skip_array_during_method_dispatch = item_tree + .attrs(db, tr_loc.container.krate(), ModItem::from(tr_loc.id.value).into()) + .by_key("rustc_skip_array_during_method_dispatch") + .exists(); let items = collect_items( db, @@ -168,7 +173,14 @@ impl TraitData { 100, ); - Arc::new(TraitData { name, items, is_auto, is_unsafe, visibility }) + Arc::new(TraitData { + name, + items, + is_auto, + is_unsafe, + visibility, + skip_array_during_method_dispatch, + }) } pub fn associated_types(&self) -> impl Iterator + '_ { diff --git a/crates/hir_ty/src/method_resolution.rs b/crates/hir_ty/src/method_resolution.rs index af6b6cda7..a23527f7d 100644 --- a/crates/hir_ty/src/method_resolution.rs +++ b/crates/hir_ty/src/method_resolution.rs @@ -5,7 +5,7 @@ use std::{iter, sync::Arc}; use arrayvec::ArrayVec; -use base_db::CrateId; +use base_db::{CrateId, Edition}; use chalk_ir::{cast::Cast, Mutability, UniverseIndex}; use hir_def::{ lang_item::LangItemTarget, nameres::DefMap, AssocContainerId, AssocItemId, FunctionId, @@ -639,6 +639,7 @@ fn iterate_trait_method_candidates( receiver_ty: Option<&Canonical>, callback: &mut dyn FnMut(&Ty, AssocItemId) -> bool, ) -> bool { + let receiver_is_array = matches!(self_ty.value.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.value.dyn_trait().into_iter().flat_map(|t| all_super_traits(db.upcast(), t)); @@ -655,6 +656,19 @@ fn iterate_trait_method_candidates( 'traits: for t in traits { let data = db.trait_data(t); + // Traits annotated with `#[rustc_skip_array_during_method_dispatch]` are skipped during + // method resolution, if the receiver is an array, and we're compiling for editions before + // 2021. + // This is to make `[a].into_iter()` not break code with the new `IntoIterator` impl for + // arrays. + if data.skip_array_during_method_dispatch && receiver_is_array { + // FIXME: this should really be using the edition of the method name's span, in case it + // comes from a macro + if db.crate_graph()[krate].edition < Edition::Edition2021 { + continue; + } + } + // we'll be lazy about checking whether the type implements the // trait, but if we find out it doesn't, we'll skip the rest of the // iteration diff --git a/crates/hir_ty/src/tests/method_resolution.rs b/crates/hir_ty/src/tests/method_resolution.rs index 058eb9129..f26b2c8a7 100644 --- a/crates/hir_ty/src/tests/method_resolution.rs +++ b/crates/hir_ty/src/tests/method_resolution.rs @@ -1349,3 +1349,52 @@ fn f() { "#, ); } + +#[test] +fn skip_array_during_method_dispatch() { + check_types( + r#" +//- /main2018.rs crate:main2018 deps:core +use core::IntoIterator; + +fn f() { + let v = [4].into_iter(); + v; + //^ &i32 + + let a = [0, 1].into_iter(); + a; + //^ &i32 +} + +//- /main2021.rs crate:main2021 deps:core edition:2021 +use core::IntoIterator; + +fn f() { + let v = [4].into_iter(); + v; + //^ i32 + + let a = [0, 1].into_iter(); + a; + //^ &i32 +} + +//- /core.rs crate:core +#[rustc_skip_array_during_method_dispatch] +pub trait IntoIterator { + type Out; + fn into_iter(self) -> Self::Out; +} + +impl IntoIterator for [T; 1] { + type Out = T; + fn into_iter(self) -> Self::Out {} +} +impl<'a, T> IntoIterator for &'a [T] { + type Out = &'a T; + fn into_iter(self) -> Self::Out {} +} + "#, + ); +} -- cgit v1.2.3 From 70e19fd1cb442587abb61258af341b6e530032d4 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Thu, 3 Jun 2021 13:51:43 +0200 Subject: Add comment --- crates/hir_def/src/data.rs | 3 +++ 1 file changed, 3 insertions(+) (limited to 'crates') diff --git a/crates/hir_def/src/data.rs b/crates/hir_def/src/data.rs index 2f06a6e29..52cb7777b 100644 --- a/crates/hir_def/src/data.rs +++ b/crates/hir_def/src/data.rs @@ -143,6 +143,9 @@ pub struct TraitData { pub is_auto: bool, pub is_unsafe: bool, pub visibility: RawVisibility, + /// Whether the trait has `#[rust_skip_array_during_method_dispatch]`. `hir_ty` will ignore + /// method calls to this trait's methods when the receiver is an array and the crate edition is + /// 2015 or 2018. pub skip_array_during_method_dispatch: bool, } -- cgit v1.2.3