Skip to content

Commit 13a2615

Browse files
committed
Auto merge of rust-lang#84147 - cuviper:array-method-dispatch, r=nikomatsakis,m-ou-se
Cautiously add IntoIterator for arrays by value Add the attribute described in rust-lang#84133, `#[rustc_skip_array_during_method_dispatch]`, which effectively hides a trait from method dispatch when the receiver type is an array. Then cherry-pick `IntoIterator for [T; N]` from rust-lang#65819 and gate it with that attribute. Arrays can now be used as `IntoIterator` normally, but `array.into_iter()` has edition-dependent behavior, returning `slice::Iter` for 2015 and 2018 editions, or `array::IntoIter` for 2021 and later. r? `@nikomatsakis` cc `@LukasKalbertodt` `@rust-lang/libs`
2 parents 5da10c0 + f6a90ca commit 13a2615

File tree

18 files changed

+237
-171
lines changed

18 files changed

+237
-171
lines changed

compiler/rustc_feature/src/builtin_attrs.rs

+5
Original file line numberDiff line numberDiff line change
@@ -544,6 +544,11 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
544544
rustc_main, Normal, template!(Word),
545545
"the `#[rustc_main]` attribute is used internally to specify test entry point function",
546546
),
547+
rustc_attr!(
548+
rustc_skip_array_during_method_dispatch, Normal, template!(Word),
549+
"the `#[rustc_skip_array_during_method_dispatch]` attribute is used to exclude a trait \
550+
from method dispatch when the receiver is an array, for compatibility in editions < 2021."
551+
),
547552

548553
// ==========================================================================
549554
// Internal attributes, Testing:

compiler/rustc_metadata/src/rmeta/decoder.rs

+2
Original file line numberDiff line numberDiff line change
@@ -757,6 +757,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
757757
data.paren_sugar,
758758
data.has_auto_impl,
759759
data.is_marker,
760+
data.skip_array_during_method_dispatch,
760761
data.specialization_kind,
761762
self.def_path_hash(item_id),
762763
)
@@ -767,6 +768,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
767768
false,
768769
false,
769770
false,
771+
false,
770772
ty::trait_def::TraitSpecializationKind::None,
771773
self.def_path_hash(item_id),
772774
),

compiler/rustc_metadata/src/rmeta/encoder.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1422,6 +1422,7 @@ impl EncodeContext<'a, 'tcx> {
14221422
paren_sugar: trait_def.paren_sugar,
14231423
has_auto_impl: self.tcx.trait_is_auto(def_id),
14241424
is_marker: trait_def.is_marker,
1425+
skip_array_during_method_dispatch: trait_def.skip_array_during_method_dispatch,
14251426
specialization_kind: trait_def.specialization_kind,
14261427
};
14271428

compiler/rustc_metadata/src/rmeta/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,7 @@ struct TraitData {
385385
paren_sugar: bool,
386386
has_auto_impl: bool,
387387
is_marker: bool,
388+
skip_array_during_method_dispatch: bool,
388389
specialization_kind: ty::trait_def::TraitSpecializationKind,
389390
}
390391

compiler/rustc_middle/src/ty/trait_def.rs

+7
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ pub struct TraitDef {
3535
/// and thus `impl`s of it are allowed to overlap.
3636
pub is_marker: bool,
3737

38+
/// If `true`, then this trait has the `#[rustc_skip_array_during_method_dispatch]`
39+
/// attribute, indicating that editions before 2021 should not consider this trait
40+
/// during method dispatch if the receiver is an array.
41+
pub skip_array_during_method_dispatch: bool,
42+
3843
/// Used to determine whether the standard library is allowed to specialize
3944
/// on this trait.
4045
pub specialization_kind: TraitSpecializationKind,
@@ -82,6 +87,7 @@ impl<'tcx> TraitDef {
8287
paren_sugar: bool,
8388
has_auto_impl: bool,
8489
is_marker: bool,
90+
skip_array_during_method_dispatch: bool,
8591
specialization_kind: TraitSpecializationKind,
8692
def_path_hash: DefPathHash,
8793
) -> TraitDef {
@@ -91,6 +97,7 @@ impl<'tcx> TraitDef {
9197
paren_sugar,
9298
has_auto_impl,
9399
is_marker,
100+
skip_array_during_method_dispatch,
94101
specialization_kind,
95102
def_path_hash,
96103
}

compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1033,6 +1033,7 @@ symbols! {
10331033
rustc_regions,
10341034
rustc_reservation_impl,
10351035
rustc_serialize,
1036+
rustc_skip_array_during_method_dispatch,
10361037
rustc_specialization_trait,
10371038
rustc_stable,
10381039
rustc_std_internal_symbol,

compiler/rustc_typeck/src/check/method/probe.rs

+10
Original file line numberDiff line numberDiff line change
@@ -1461,6 +1461,16 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
14611461
}
14621462

14631463
TraitCandidate(trait_ref) => {
1464+
if let Some(method_name) = self.method_name {
1465+
// Some trait methods are excluded for arrays before 2021.
1466+
// (`array.into_iter()` wants a slice iterator for compatibility.)
1467+
if self_ty.is_array() && !method_name.span.rust_2021() {
1468+
let trait_def = self.tcx.trait_def(trait_ref.def_id);
1469+
if trait_def.skip_array_during_method_dispatch {
1470+
return ProbeResult::NoMatch;
1471+
}
1472+
}
1473+
}
14641474
let predicate = trait_ref.without_const().to_predicate(self.tcx);
14651475
let obligation = traits::Obligation::new(cause, self.param_env, predicate);
14661476
if !self.predicate_may_hold(&obligation) {

compiler/rustc_typeck/src/collect.rs

+12-1
Original file line numberDiff line numberDiff line change
@@ -1191,6 +1191,8 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: DefId) -> ty::TraitDef {
11911191
}
11921192

11931193
let is_marker = tcx.has_attr(def_id, sym::marker);
1194+
let skip_array_during_method_dispatch =
1195+
tcx.has_attr(def_id, sym::rustc_skip_array_during_method_dispatch);
11941196
let spec_kind = if tcx.has_attr(def_id, sym::rustc_unsafe_specialization_marker) {
11951197
ty::trait_def::TraitSpecializationKind::Marker
11961198
} else if tcx.has_attr(def_id, sym::rustc_specialization_trait) {
@@ -1199,7 +1201,16 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: DefId) -> ty::TraitDef {
11991201
ty::trait_def::TraitSpecializationKind::None
12001202
};
12011203
let def_path_hash = tcx.def_path_hash(def_id);
1202-
ty::TraitDef::new(def_id, unsafety, paren_sugar, is_auto, is_marker, spec_kind, def_path_hash)
1204+
ty::TraitDef::new(
1205+
def_id,
1206+
unsafety,
1207+
paren_sugar,
1208+
is_auto,
1209+
is_marker,
1210+
skip_array_during_method_dispatch,
1211+
spec_kind,
1212+
def_path_hash,
1213+
)
12031214
}
12041215

12051216
fn has_late_bound_regions<'tcx>(tcx: TyCtxt<'tcx>, node: Node<'tcx>) -> Option<Span> {

library/core/src/array/mod.rs

+22
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,28 @@ impl<T: fmt::Debug, const N: usize> fmt::Debug for [T; N] {
155155
}
156156
}
157157

158+
// Note: the `#[rustc_skip_array_during_method_dispatch]` on `trait IntoIterator`
159+
// hides this implementation from explicit `.into_iter()` calls on editions < 2021,
160+
// so those calls will still resolve to the slice implementation, by reference.
161+
#[cfg(not(bootstrap))]
162+
#[stable(feature = "array_into_iter_impl", since = "1.53.0")]
163+
impl<T, const N: usize> IntoIterator for [T; N] {
164+
type Item = T;
165+
type IntoIter = IntoIter<T, N>;
166+
167+
/// Creates a consuming iterator, that is, one that moves each value out of
168+
/// the array (from start to end). The array cannot be used after calling
169+
/// this unless `T` implements `Copy`, so the whole array is copied.
170+
///
171+
/// Arrays have special behavior when calling `.into_iter()` prior to the
172+
/// 2021 edition -- see the [array] Editions section for more information.
173+
///
174+
/// [array]: prim@array
175+
fn into_iter(self) -> Self::IntoIter {
176+
IntoIter::new(self)
177+
}
178+
}
179+
158180
#[stable(feature = "rust1", since = "1.0.0")]
159181
impl<'a, T, const N: usize> IntoIterator for &'a [T; N] {
160182
type Item = &'a T;

library/core/src/iter/traits/collect.rs

+1
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ pub trait FromIterator<A>: Sized {
198198
/// }
199199
/// ```
200200
#[rustc_diagnostic_item = "IntoIterator"]
201+
#[cfg_attr(not(bootstrap), rustc_skip_array_during_method_dispatch)]
201202
#[stable(feature = "rust1", since = "1.0.0")]
202203
pub trait IntoIterator {
203204
/// The type of the elements being iterated over.

library/std/src/primitive_docs.rs

+57-20
Original file line numberDiff line numberDiff line change
@@ -498,7 +498,7 @@ mod prim_pointer {}
498498
/// - [`Copy`]
499499
/// - [`Clone`]
500500
/// - [`Debug`]
501-
/// - [`IntoIterator`] (implemented for `&[T; N]` and `&mut [T; N]`)
501+
/// - [`IntoIterator`] (implemented for `[T; N]`, `&[T; N]` and `&mut [T; N]`)
502502
/// - [`PartialEq`], [`PartialOrd`], [`Eq`], [`Ord`]
503503
/// - [`Hash`]
504504
/// - [`AsRef`], [`AsMut`]
@@ -517,7 +517,8 @@ mod prim_pointer {}
517517
///
518518
/// # Examples
519519
///
520-
/// ```
520+
#[cfg_attr(bootstrap, doc = "```ignore")]
521+
#[cfg_attr(not(bootstrap), doc = "```")]
521522
/// let mut array: [i32; 3] = [0; 3];
522523
///
523524
/// array[1] = 1;
@@ -526,31 +527,16 @@ mod prim_pointer {}
526527
/// assert_eq!([1, 2], &array[1..]);
527528
///
528529
/// // This loop prints: 0 1 2
529-
/// for x in &array {
530+
/// for x in array {
530531
/// print!("{} ", x);
531532
/// }
532533
/// ```
533534
///
534-
/// An array itself is not iterable:
535-
///
536-
/// ```compile_fail,E0277
537-
/// let array: [i32; 3] = [0; 3];
538-
///
539-
/// for x in array { }
540-
/// // error: the trait bound `[i32; 3]: std::iter::Iterator` is not satisfied
541-
/// ```
542-
///
543-
/// The solution is to coerce the array to a slice by calling a slice method:
535+
/// You can also iterate over reference to the array's elements:
544536
///
545537
/// ```
546-
/// # let array: [i32; 3] = [0; 3];
547-
/// for x in array.iter() { }
548-
/// ```
549-
///
550-
/// You can also use the array reference's [`IntoIterator`] implementation:
538+
/// let array: [i32; 3] = [0; 3];
551539
///
552-
/// ```
553-
/// # let array: [i32; 3] = [0; 3];
554540
/// for x in &array { }
555541
/// ```
556542
///
@@ -564,6 +550,57 @@ mod prim_pointer {}
564550
/// move_away(roa);
565551
/// ```
566552
///
553+
/// # Editions
554+
///
555+
/// Prior to Rust 1.53, arrays did not implement `IntoIterator` by value, so the method call
556+
/// `array.into_iter()` auto-referenced into a slice iterator. That behavior is preserved in the
557+
/// 2015 and 2018 editions of Rust for compatability, ignoring `IntoIterator` by value.
558+
///
559+
#[cfg_attr(bootstrap, doc = "```rust,edition2018,ignore")]
560+
#[cfg_attr(not(bootstrap), doc = "```rust,edition2018")]
561+
/// # #![allow(array_into_iter)] // override our `deny(warnings)`
562+
/// let array: [i32; 3] = [0; 3];
563+
///
564+
/// // This creates a slice iterator, producing references to each value.
565+
/// for item in array.into_iter().enumerate() {
566+
/// let (i, x): (usize, &i32) = item;
567+
/// println!("array[{}] = {}", i, x);
568+
/// }
569+
///
570+
/// // The `array_into_iter` lint suggests this change for future compatibility:
571+
/// for item in array.iter().enumerate() {
572+
/// let (i, x): (usize, &i32) = item;
573+
/// println!("array[{}] = {}", i, x);
574+
/// }
575+
///
576+
/// // You can explicitly iterate an array by value using
577+
/// // `IntoIterator::into_iter` or `std::array::IntoIter::new`:
578+
/// for item in IntoIterator::into_iter(array).enumerate() {
579+
/// let (i, x): (usize, i32) = item;
580+
/// println!("array[{}] = {}", i, x);
581+
/// }
582+
/// ```
583+
///
584+
/// Starting in the 2021 edition, `array.into_iter()` will use `IntoIterator` normally to iterate
585+
/// by value, and `iter()` should be used to iterate by reference like previous editions.
586+
///
587+
/// ```rust,edition2021,ignore
588+
/// # // FIXME: ignored because 2021 testing is still unstable
589+
/// let array: [i32; 3] = [0; 3];
590+
///
591+
/// // This iterates by reference:
592+
/// for item in array.iter().enumerate() {
593+
/// let (i, x): (usize, &i32) = item;
594+
/// println!("array[{}] = {}", i, x);
595+
/// }
596+
///
597+
/// // This iterates by value:
598+
/// for item in array.into_iter().enumerate() {
599+
/// let (i, x): (usize, i32) = item;
600+
/// println!("array[{}] = {}", i, x);
601+
/// }
602+
/// ```
603+
///
567604
/// [slice]: prim@slice
568605
/// [`Debug`]: fmt::Debug
569606
/// [`Hash`]: hash::Hash
+2-9
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,16 @@
1+
// check-pass
2+
13
fn main() {
24
for _ in [0..1] {}
3-
//~^ ERROR is not an iterator
45
for _ in [0..=1] {}
5-
//~^ ERROR is not an iterator
66
for _ in [0..] {}
7-
//~^ ERROR is not an iterator
87
for _ in [..1] {}
9-
//~^ ERROR is not an iterator
108
for _ in [..=1] {}
11-
//~^ ERROR is not an iterator
129
let start = 0;
1310
let end = 0;
1411
for _ in [start..end] {}
15-
//~^ ERROR is not an iterator
1612
let array_of_range = [start..end];
1713
for _ in array_of_range {}
18-
//~^ ERROR is not an iterator
1914
for _ in [0..1, 2..3] {}
20-
//~^ ERROR is not an iterator
2115
for _ in [0..=1] {}
22-
//~^ ERROR is not an iterator
2316
}

0 commit comments

Comments
 (0)