Skip to content

Commit c7eb19e

Browse files
Merge #9104
9104: Implement `#[rustc_skip_array_during_method_dispatch]` r=flodiebold a=jonas-schievink haxx run the world Closes #8552 Part of #9056 Co-authored-by: Jonas Schievink <[email protected]>
2 parents 5093639 + 70e19fd commit c7eb19e

File tree

3 files changed

+80
-2
lines changed

3 files changed

+80
-2
lines changed

crates/hir_def/src/data.rs

+16-1
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,10 @@ pub struct TraitData {
143143
pub is_auto: bool,
144144
pub is_unsafe: bool,
145145
pub visibility: RawVisibility,
146+
/// Whether the trait has `#[rust_skip_array_during_method_dispatch]`. `hir_ty` will ignore
147+
/// method calls to this trait's methods when the receiver is an array and the crate edition is
148+
/// 2015 or 2018.
149+
pub skip_array_during_method_dispatch: bool,
146150
}
147151

148152
impl TraitData {
@@ -157,6 +161,10 @@ impl TraitData {
157161
let container = AssocContainerId::TraitId(tr);
158162
let visibility = item_tree[tr_def.visibility].clone();
159163
let mut expander = Expander::new(db, tr_loc.id.file_id(), module_id);
164+
let skip_array_during_method_dispatch = item_tree
165+
.attrs(db, tr_loc.container.krate(), ModItem::from(tr_loc.id.value).into())
166+
.by_key("rustc_skip_array_during_method_dispatch")
167+
.exists();
160168

161169
let items = collect_items(
162170
db,
@@ -168,7 +176,14 @@ impl TraitData {
168176
100,
169177
);
170178

171-
Arc::new(TraitData { name, items, is_auto, is_unsafe, visibility })
179+
Arc::new(TraitData {
180+
name,
181+
items,
182+
is_auto,
183+
is_unsafe,
184+
visibility,
185+
skip_array_during_method_dispatch,
186+
})
172187
}
173188

174189
pub fn associated_types(&self) -> impl Iterator<Item = TypeAliasId> + '_ {

crates/hir_ty/src/method_resolution.rs

+15-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
use std::{iter, sync::Arc};
66

77
use arrayvec::ArrayVec;
8-
use base_db::CrateId;
8+
use base_db::{CrateId, Edition};
99
use chalk_ir::{cast::Cast, Mutability, UniverseIndex};
1010
use hir_def::{
1111
lang_item::LangItemTarget, nameres::DefMap, AssocContainerId, AssocItemId, FunctionId,
@@ -639,6 +639,7 @@ fn iterate_trait_method_candidates(
639639
receiver_ty: Option<&Canonical<Ty>>,
640640
callback: &mut dyn FnMut(&Ty, AssocItemId) -> bool,
641641
) -> bool {
642+
let receiver_is_array = matches!(self_ty.value.kind(&Interner), chalk_ir::TyKind::Array(..));
642643
// if ty is `dyn Trait`, the trait doesn't need to be in scope
643644
let inherent_trait =
644645
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(
655656
'traits: for t in traits {
656657
let data = db.trait_data(t);
657658

659+
// Traits annotated with `#[rustc_skip_array_during_method_dispatch]` are skipped during
660+
// method resolution, if the receiver is an array, and we're compiling for editions before
661+
// 2021.
662+
// This is to make `[a].into_iter()` not break code with the new `IntoIterator` impl for
663+
// arrays.
664+
if data.skip_array_during_method_dispatch && receiver_is_array {
665+
// FIXME: this should really be using the edition of the method name's span, in case it
666+
// comes from a macro
667+
if db.crate_graph()[krate].edition < Edition::Edition2021 {
668+
continue;
669+
}
670+
}
671+
658672
// we'll be lazy about checking whether the type implements the
659673
// trait, but if we find out it doesn't, we'll skip the rest of the
660674
// iteration

crates/hir_ty/src/tests/method_resolution.rs

+49
Original file line numberDiff line numberDiff line change
@@ -1349,3 +1349,52 @@ fn f() {
13491349
"#,
13501350
);
13511351
}
1352+
1353+
#[test]
1354+
fn skip_array_during_method_dispatch() {
1355+
check_types(
1356+
r#"
1357+
//- /main2018.rs crate:main2018 deps:core
1358+
use core::IntoIterator;
1359+
1360+
fn f() {
1361+
let v = [4].into_iter();
1362+
v;
1363+
//^ &i32
1364+
1365+
let a = [0, 1].into_iter();
1366+
a;
1367+
//^ &i32
1368+
}
1369+
1370+
//- /main2021.rs crate:main2021 deps:core edition:2021
1371+
use core::IntoIterator;
1372+
1373+
fn f() {
1374+
let v = [4].into_iter();
1375+
v;
1376+
//^ i32
1377+
1378+
let a = [0, 1].into_iter();
1379+
a;
1380+
//^ &i32
1381+
}
1382+
1383+
//- /core.rs crate:core
1384+
#[rustc_skip_array_during_method_dispatch]
1385+
pub trait IntoIterator {
1386+
type Out;
1387+
fn into_iter(self) -> Self::Out;
1388+
}
1389+
1390+
impl<T> IntoIterator for [T; 1] {
1391+
type Out = T;
1392+
fn into_iter(self) -> Self::Out {}
1393+
}
1394+
impl<'a, T> IntoIterator for &'a [T] {
1395+
type Out = &'a T;
1396+
fn into_iter(self) -> Self::Out {}
1397+
}
1398+
"#,
1399+
);
1400+
}

0 commit comments

Comments
 (0)