Skip to content

fix: Implement #[rustc_skip_array_during_method_dispatch] #9104

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion crates/hir_def/src/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,10 @@ 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,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this could maybe use a comment?

}

impl TraitData {
Expand All @@ -157,6 +161,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,
Expand All @@ -168,7 +176,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<Item = TypeAliasId> + '_ {
Expand Down
16 changes: 15 additions & 1 deletion crates/hir_ty/src/method_resolution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -639,6 +639,7 @@ fn iterate_trait_method_candidates(
receiver_ty: Option<&Canonical<Ty>>,
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));
Expand All @@ -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
Expand Down
49 changes: 49 additions & 0 deletions crates/hir_ty/src/tests/method_resolution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T> 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 {}
}
"#,
);
}