about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_feature/src/builtin_attrs.rs5
-rw-r--r--compiler/rustc_metadata/src/rmeta/decoder.rs2
-rw-r--r--compiler/rustc_metadata/src/rmeta/encoder.rs1
-rw-r--r--compiler/rustc_metadata/src/rmeta/mod.rs1
-rw-r--r--compiler/rustc_middle/src/ty/trait_def.rs7
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--compiler/rustc_typeck/src/check/method/probe.rs10
-rw-r--r--compiler/rustc_typeck/src/collect.rs13
-rw-r--r--library/core/src/array/mod.rs22
-rw-r--r--library/core/src/iter/traits/collect.rs1
-rw-r--r--library/std/src/primitive_docs.rs77
-rw-r--r--src/test/ui/iterators/array-of-ranges.rs11
-rw-r--r--src/test/ui/iterators/array-of-ranges.stderr102
-rw-r--r--src/test/ui/iterators/array.rs5
-rw-r--r--src/test/ui/iterators/array.stderr36
-rw-r--r--src/test/ui/iterators/into-iter-on-arrays-2018.rs39
-rw-r--r--src/test/ui/iterators/into-iter-on-arrays-2018.stderr42
-rw-r--r--src/test/ui/iterators/into-iter-on-arrays-2021.rs33
18 files changed, 237 insertions, 171 deletions
diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs
index 8dfc4572a84..b8a0b8debcd 100644
--- a/compiler/rustc_feature/src/builtin_attrs.rs
+++ b/compiler/rustc_feature/src/builtin_attrs.rs
@@ -544,6 +544,11 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
         rustc_main, Normal, template!(Word),
         "the `#[rustc_main]` attribute is used internally to specify test entry point function",
     ),
+    rustc_attr!(
+        rustc_skip_array_during_method_dispatch, Normal, template!(Word),
+        "the `#[rustc_skip_array_during_method_dispatch]` attribute is used to exclude a trait \
+        from method dispatch when the receiver is an array, for compatibility in editions < 2021."
+    ),
 
     // ==========================================================================
     // Internal attributes, Testing:
diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs
index 3d0a9d553b0..19ae5ce69c1 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder.rs
@@ -757,6 +757,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
                     data.paren_sugar,
                     data.has_auto_impl,
                     data.is_marker,
+                    data.skip_array_during_method_dispatch,
                     data.specialization_kind,
                     self.def_path_hash(item_id),
                 )
@@ -767,6 +768,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
                 false,
                 false,
                 false,
+                false,
                 ty::trait_def::TraitSpecializationKind::None,
                 self.def_path_hash(item_id),
             ),
diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs
index a5157854e15..e8f02b8e66f 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -1422,6 +1422,7 @@ impl EncodeContext<'a, 'tcx> {
                     paren_sugar: trait_def.paren_sugar,
                     has_auto_impl: self.tcx.trait_is_auto(def_id),
                     is_marker: trait_def.is_marker,
+                    skip_array_during_method_dispatch: trait_def.skip_array_during_method_dispatch,
                     specialization_kind: trait_def.specialization_kind,
                 };
 
diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs
index 7cfb051e703..9f665d5daaa 100644
--- a/compiler/rustc_metadata/src/rmeta/mod.rs
+++ b/compiler/rustc_metadata/src/rmeta/mod.rs
@@ -385,6 +385,7 @@ struct TraitData {
     paren_sugar: bool,
     has_auto_impl: bool,
     is_marker: bool,
+    skip_array_during_method_dispatch: bool,
     specialization_kind: ty::trait_def::TraitSpecializationKind,
 }
 
diff --git a/compiler/rustc_middle/src/ty/trait_def.rs b/compiler/rustc_middle/src/ty/trait_def.rs
index 33065bc3a7b..e9b8883f29a 100644
--- a/compiler/rustc_middle/src/ty/trait_def.rs
+++ b/compiler/rustc_middle/src/ty/trait_def.rs
@@ -35,6 +35,11 @@ pub struct TraitDef {
     /// and thus `impl`s of it are allowed to overlap.
     pub is_marker: bool,
 
+    /// If `true`, then this trait has the `#[rustc_skip_array_during_method_dispatch]`
+    /// attribute, indicating that editions before 2021 should not consider this trait
+    /// during method dispatch if the receiver is an array.
+    pub skip_array_during_method_dispatch: bool,
+
     /// Used to determine whether the standard library is allowed to specialize
     /// on this trait.
     pub specialization_kind: TraitSpecializationKind,
@@ -82,6 +87,7 @@ impl<'tcx> TraitDef {
         paren_sugar: bool,
         has_auto_impl: bool,
         is_marker: bool,
+        skip_array_during_method_dispatch: bool,
         specialization_kind: TraitSpecializationKind,
         def_path_hash: DefPathHash,
     ) -> TraitDef {
@@ -91,6 +97,7 @@ impl<'tcx> TraitDef {
             paren_sugar,
             has_auto_impl,
             is_marker,
+            skip_array_during_method_dispatch,
             specialization_kind,
             def_path_hash,
         }
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index fdfd226b425..4be187c5208 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -1033,6 +1033,7 @@ symbols! {
         rustc_regions,
         rustc_reservation_impl,
         rustc_serialize,
+        rustc_skip_array_during_method_dispatch,
         rustc_specialization_trait,
         rustc_stable,
         rustc_std_internal_symbol,
diff --git a/compiler/rustc_typeck/src/check/method/probe.rs b/compiler/rustc_typeck/src/check/method/probe.rs
index c2998c84131..c79743f2d73 100644
--- a/compiler/rustc_typeck/src/check/method/probe.rs
+++ b/compiler/rustc_typeck/src/check/method/probe.rs
@@ -1461,6 +1461,16 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
                 }
 
                 TraitCandidate(trait_ref) => {
+                    if let Some(method_name) = self.method_name {
+                        // Some trait methods are excluded for arrays before 2021.
+                        // (`array.into_iter()` wants a slice iterator for compatibility.)
+                        if self_ty.is_array() && !method_name.span.rust_2021() {
+                            let trait_def = self.tcx.trait_def(trait_ref.def_id);
+                            if trait_def.skip_array_during_method_dispatch {
+                                return ProbeResult::NoMatch;
+                            }
+                        }
+                    }
                     let predicate = trait_ref.without_const().to_predicate(self.tcx);
                     let obligation = traits::Obligation::new(cause, self.param_env, predicate);
                     if !self.predicate_may_hold(&obligation) {
diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs
index 1477418d5d8..3692642b6e2 100644
--- a/compiler/rustc_typeck/src/collect.rs
+++ b/compiler/rustc_typeck/src/collect.rs
@@ -1191,6 +1191,8 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: DefId) -> ty::TraitDef {
     }
 
     let is_marker = tcx.has_attr(def_id, sym::marker);
+    let skip_array_during_method_dispatch =
+        tcx.has_attr(def_id, sym::rustc_skip_array_during_method_dispatch);
     let spec_kind = if tcx.has_attr(def_id, sym::rustc_unsafe_specialization_marker) {
         ty::trait_def::TraitSpecializationKind::Marker
     } 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 {
         ty::trait_def::TraitSpecializationKind::None
     };
     let def_path_hash = tcx.def_path_hash(def_id);
-    ty::TraitDef::new(def_id, unsafety, paren_sugar, is_auto, is_marker, spec_kind, def_path_hash)
+    ty::TraitDef::new(
+        def_id,
+        unsafety,
+        paren_sugar,
+        is_auto,
+        is_marker,
+        skip_array_during_method_dispatch,
+        spec_kind,
+        def_path_hash,
+    )
 }
 
 fn has_late_bound_regions<'tcx>(tcx: TyCtxt<'tcx>, node: Node<'tcx>) -> Option<Span> {
diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs
index 5c7a8bdf6bd..8b56c9560aa 100644
--- a/library/core/src/array/mod.rs
+++ b/library/core/src/array/mod.rs
@@ -155,6 +155,28 @@ impl<T: fmt::Debug, const N: usize> fmt::Debug for [T; N] {
     }
 }
 
+// Note: the `#[rustc_skip_array_during_method_dispatch]` on `trait IntoIterator`
+// hides this implementation from explicit `.into_iter()` calls on editions < 2021,
+// so those calls will still resolve to the slice implementation, by reference.
+#[cfg(not(bootstrap))]
+#[stable(feature = "array_into_iter_impl", since = "1.53.0")]
+impl<T, const N: usize> IntoIterator for [T; N] {
+    type Item = T;
+    type IntoIter = IntoIter<T, N>;
+
+    /// Creates a consuming iterator, that is, one that moves each value out of
+    /// the array (from start to end). The array cannot be used after calling
+    /// this unless `T` implements `Copy`, so the whole array is copied.
+    ///
+    /// Arrays have special behavior when calling `.into_iter()` prior to the
+    /// 2021 edition -- see the [array] Editions section for more information.
+    ///
+    /// [array]: prim@array
+    fn into_iter(self) -> Self::IntoIter {
+        IntoIter::new(self)
+    }
+}
+
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<'a, T, const N: usize> IntoIterator for &'a [T; N] {
     type Item = &'a T;
diff --git a/library/core/src/iter/traits/collect.rs b/library/core/src/iter/traits/collect.rs
index 1ae6d15c12d..13a2e24cadd 100644
--- a/library/core/src/iter/traits/collect.rs
+++ b/library/core/src/iter/traits/collect.rs
@@ -198,6 +198,7 @@ pub trait FromIterator<A>: Sized {
 /// }
 /// ```
 #[rustc_diagnostic_item = "IntoIterator"]
+#[cfg_attr(not(bootstrap), rustc_skip_array_during_method_dispatch)]
 #[stable(feature = "rust1", since = "1.0.0")]
 pub trait IntoIterator {
     /// The type of the elements being iterated over.
diff --git a/library/std/src/primitive_docs.rs b/library/std/src/primitive_docs.rs
index 84d072b9860..c37111f665c 100644
--- a/library/std/src/primitive_docs.rs
+++ b/library/std/src/primitive_docs.rs
@@ -498,7 +498,7 @@ mod prim_pointer {}
 /// - [`Copy`]
 /// - [`Clone`]
 /// - [`Debug`]
-/// - [`IntoIterator`] (implemented for `&[T; N]` and `&mut [T; N]`)
+/// - [`IntoIterator`] (implemented for `[T; N]`, `&[T; N]` and `&mut [T; N]`)
 /// - [`PartialEq`], [`PartialOrd`], [`Eq`], [`Ord`]
 /// - [`Hash`]
 /// - [`AsRef`], [`AsMut`]
@@ -517,7 +517,8 @@ mod prim_pointer {}
 ///
 /// # Examples
 ///
-/// ```
+#[cfg_attr(bootstrap, doc = "```ignore")]
+#[cfg_attr(not(bootstrap), doc = "```")]
 /// let mut array: [i32; 3] = [0; 3];
 ///
 /// array[1] = 1;
@@ -526,31 +527,16 @@ mod prim_pointer {}
 /// assert_eq!([1, 2], &array[1..]);
 ///
 /// // This loop prints: 0 1 2
-/// for x in &array {
+/// for x in array {
 ///     print!("{} ", x);
 /// }
 /// ```
 ///
-/// An array itself is not iterable:
-///
-/// ```compile_fail,E0277
-/// let array: [i32; 3] = [0; 3];
-///
-/// for x in array { }
-/// // error: the trait bound `[i32; 3]: std::iter::Iterator` is not satisfied
-/// ```
-///
-/// The solution is to coerce the array to a slice by calling a slice method:
+/// You can also iterate over reference to the array's elements:
 ///
 /// ```
-/// # let array: [i32; 3] = [0; 3];
-/// for x in array.iter() { }
-/// ```
-///
-/// You can also use the array reference's [`IntoIterator`] implementation:
+/// let array: [i32; 3] = [0; 3];
 ///
-/// ```
-/// # let array: [i32; 3] = [0; 3];
 /// for x in &array { }
 /// ```
 ///
@@ -564,6 +550,57 @@ mod prim_pointer {}
 /// move_away(roa);
 /// ```
 ///
+/// # Editions
+///
+/// Prior to Rust 1.53, arrays did not implement `IntoIterator` by value, so the method call
+/// `array.into_iter()` auto-referenced into a slice iterator. That behavior is preserved in the
+/// 2015 and 2018 editions of Rust for compatability, ignoring `IntoIterator` by value.
+///
+#[cfg_attr(bootstrap, doc = "```rust,edition2018,ignore")]
+#[cfg_attr(not(bootstrap), doc = "```rust,edition2018")]
+/// # #![allow(array_into_iter)] // override our `deny(warnings)`
+/// let array: [i32; 3] = [0; 3];
+///
+/// // This creates a slice iterator, producing references to each value.
+/// for item in array.into_iter().enumerate() {
+///     let (i, x): (usize, &i32) = item;
+///     println!("array[{}] = {}", i, x);
+/// }
+///
+/// // The `array_into_iter` lint suggests this change for future compatibility:
+/// for item in array.iter().enumerate() {
+///     let (i, x): (usize, &i32) = item;
+///     println!("array[{}] = {}", i, x);
+/// }
+///
+/// // You can explicitly iterate an array by value using
+/// // `IntoIterator::into_iter` or `std::array::IntoIter::new`:
+/// for item in IntoIterator::into_iter(array).enumerate() {
+///     let (i, x): (usize, i32) = item;
+///     println!("array[{}] = {}", i, x);
+/// }
+/// ```
+///
+/// Starting in the 2021 edition, `array.into_iter()` will use `IntoIterator` normally to iterate
+/// by value, and `iter()` should be used to iterate by reference like previous editions.
+///
+/// ```rust,edition2021,ignore
+/// # // FIXME: ignored because 2021 testing is still unstable
+/// let array: [i32; 3] = [0; 3];
+///
+/// // This iterates by reference:
+/// for item in array.iter().enumerate() {
+///     let (i, x): (usize, &i32) = item;
+///     println!("array[{}] = {}", i, x);
+/// }
+///
+/// // This iterates by value:
+/// for item in array.into_iter().enumerate() {
+///     let (i, x): (usize, i32) = item;
+///     println!("array[{}] = {}", i, x);
+/// }
+/// ```
+///
 /// [slice]: prim@slice
 /// [`Debug`]: fmt::Debug
 /// [`Hash`]: hash::Hash
diff --git a/src/test/ui/iterators/array-of-ranges.rs b/src/test/ui/iterators/array-of-ranges.rs
index d2dfc7ec327..037540a3e89 100644
--- a/src/test/ui/iterators/array-of-ranges.rs
+++ b/src/test/ui/iterators/array-of-ranges.rs
@@ -1,23 +1,16 @@
+// check-pass
+
 fn main() {
     for _ in [0..1] {}
-//~^ ERROR is not an iterator
     for _ in [0..=1] {}
-//~^ ERROR is not an iterator
     for _ in [0..] {}
-//~^ ERROR is not an iterator
     for _ in [..1] {}
-//~^ ERROR is not an iterator
     for _ in [..=1] {}
-//~^ ERROR is not an iterator
     let start = 0;
     let end = 0;
     for _ in [start..end] {}
-//~^ ERROR is not an iterator
     let array_of_range = [start..end];
     for _ in array_of_range {}
-//~^ ERROR is not an iterator
     for _ in [0..1, 2..3] {}
-//~^ ERROR is not an iterator
     for _ in [0..=1] {}
-//~^ ERROR is not an iterator
 }
diff --git a/src/test/ui/iterators/array-of-ranges.stderr b/src/test/ui/iterators/array-of-ranges.stderr
deleted file mode 100644
index 7d58eb948ea..00000000000
--- a/src/test/ui/iterators/array-of-ranges.stderr
+++ /dev/null
@@ -1,102 +0,0 @@
-error[E0277]: `[std::ops::Range<{integer}>; 1]` is not an iterator
-  --> $DIR/array-of-ranges.rs:2:14
-   |
-LL |     for _ in [0..1] {}
-   |              ^^^^^^ if you meant to iterate between two values, remove the square brackets
-   |
-   = help: the trait `Iterator` is not implemented for `[std::ops::Range<{integer}>; 1]`
-   = note: `[start..end]` is an array of one `Range`; you might have meant to have a `Range` without the brackets: `start..end`
-   = note: required because of the requirements on the impl of `IntoIterator` for `[std::ops::Range<{integer}>; 1]`
-   = note: required by `into_iter`
-
-error[E0277]: `[RangeInclusive<{integer}>; 1]` is not an iterator
-  --> $DIR/array-of-ranges.rs:4:14
-   |
-LL |     for _ in [0..=1] {}
-   |              ^^^^^^^ if you meant to iterate between two values, remove the square brackets
-   |
-   = help: the trait `Iterator` is not implemented for `[RangeInclusive<{integer}>; 1]`
-   = note: `[start..=end]` is an array of one `RangeInclusive`; you might have meant to have a `RangeInclusive` without the brackets: `start..=end`
-   = note: required because of the requirements on the impl of `IntoIterator` for `[RangeInclusive<{integer}>; 1]`
-   = note: required by `into_iter`
-
-error[E0277]: `[RangeFrom<{integer}>; 1]` is not an iterator
-  --> $DIR/array-of-ranges.rs:6:14
-   |
-LL |     for _ in [0..] {}
-   |              ^^^^^ if you meant to iterate from a value onwards, remove the square brackets
-   |
-   = help: the trait `Iterator` is not implemented for `[RangeFrom<{integer}>; 1]`
-   = note: `[start..]` is an array of one `RangeFrom`; you might have meant to have a `RangeFrom` without the brackets: `start..`, keeping in mind that iterating over an unbounded iterator will run forever unless you `break` or `return` from within the loop
-   = note: required because of the requirements on the impl of `IntoIterator` for `[RangeFrom<{integer}>; 1]`
-   = note: required by `into_iter`
-
-error[E0277]: `[RangeTo<{integer}>; 1]` is not an iterator
-  --> $DIR/array-of-ranges.rs:8:14
-   |
-LL |     for _ in [..1] {}
-   |              ^^^^^ if you meant to iterate until a value, remove the square brackets and add a starting value
-   |
-   = help: the trait `Iterator` is not implemented for `[RangeTo<{integer}>; 1]`
-   = note: `[..end]` is an array of one `RangeTo`; you might have meant to have a bounded `Range` without the brackets: `0..end`
-   = note: required because of the requirements on the impl of `IntoIterator` for `[RangeTo<{integer}>; 1]`
-   = note: required by `into_iter`
-
-error[E0277]: `[RangeToInclusive<{integer}>; 1]` is not an iterator
-  --> $DIR/array-of-ranges.rs:10:14
-   |
-LL |     for _ in [..=1] {}
-   |              ^^^^^^ if you meant to iterate until a value (including it), remove the square brackets and add a starting value
-   |
-   = help: the trait `Iterator` is not implemented for `[RangeToInclusive<{integer}>; 1]`
-   = note: `[..=end]` is an array of one `RangeToInclusive`; you might have meant to have a bounded `RangeInclusive` without the brackets: `0..=end`
-   = note: required because of the requirements on the impl of `IntoIterator` for `[RangeToInclusive<{integer}>; 1]`
-   = note: required by `into_iter`
-
-error[E0277]: `[std::ops::Range<{integer}>; 1]` is not an iterator
-  --> $DIR/array-of-ranges.rs:14:14
-   |
-LL |     for _ in [start..end] {}
-   |              ^^^^^^^^^^^^ if you meant to iterate between two values, remove the square brackets
-   |
-   = help: the trait `Iterator` is not implemented for `[std::ops::Range<{integer}>; 1]`
-   = note: `[start..end]` is an array of one `Range`; you might have meant to have a `Range` without the brackets: `start..end`
-   = note: required because of the requirements on the impl of `IntoIterator` for `[std::ops::Range<{integer}>; 1]`
-   = note: required by `into_iter`
-
-error[E0277]: `[std::ops::Range<{integer}>; 1]` is not an iterator
-  --> $DIR/array-of-ranges.rs:17:14
-   |
-LL |     for _ in array_of_range {}
-   |              ^^^^^^^^^^^^^^ if you meant to iterate between two values, remove the square brackets
-   |
-   = help: the trait `Iterator` is not implemented for `[std::ops::Range<{integer}>; 1]`
-   = note: `[start..end]` is an array of one `Range`; you might have meant to have a `Range` without the brackets: `start..end`
-   = note: required because of the requirements on the impl of `IntoIterator` for `[std::ops::Range<{integer}>; 1]`
-   = note: required by `into_iter`
-
-error[E0277]: `[std::ops::Range<{integer}>; 2]` is not an iterator
-  --> $DIR/array-of-ranges.rs:19:14
-   |
-LL |     for _ in [0..1, 2..3] {}
-   |              ^^^^^^^^^^^^ arrays do not yet implement `IntoIterator`; try using `std::array::IntoIter::new(arr)`
-   |
-   = help: the trait `Iterator` is not implemented for `[std::ops::Range<{integer}>; 2]`
-   = note: see <https://github.com/rust-lang/rust/pull/65819> for more details
-   = note: required because of the requirements on the impl of `IntoIterator` for `[std::ops::Range<{integer}>; 2]`
-   = note: required by `into_iter`
-
-error[E0277]: `[RangeInclusive<{integer}>; 1]` is not an iterator
-  --> $DIR/array-of-ranges.rs:21:14
-   |
-LL |     for _ in [0..=1] {}
-   |              ^^^^^^^ if you meant to iterate between two values, remove the square brackets
-   |
-   = help: the trait `Iterator` is not implemented for `[RangeInclusive<{integer}>; 1]`
-   = note: `[start..=end]` is an array of one `RangeInclusive`; you might have meant to have a `RangeInclusive` without the brackets: `start..=end`
-   = note: required because of the requirements on the impl of `IntoIterator` for `[RangeInclusive<{integer}>; 1]`
-   = note: required by `into_iter`
-
-error: aborting due to 9 previous errors
-
-For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/iterators/array.rs b/src/test/ui/iterators/array.rs
index 33c84f6fa35..5985c74e11f 100644
--- a/src/test/ui/iterators/array.rs
+++ b/src/test/ui/iterators/array.rs
@@ -1,9 +1,8 @@
+// check-pass
+
 fn main() {
     for _ in [1, 2] {}
-//~^ ERROR is not an iterator
     let x = [1, 2];
     for _ in x {}
-//~^ ERROR is not an iterator
     for _ in [1.0, 2.0] {}
-//~^ ERROR is not an iterator
 }
diff --git a/src/test/ui/iterators/array.stderr b/src/test/ui/iterators/array.stderr
deleted file mode 100644
index 7e2b600fb7a..00000000000
--- a/src/test/ui/iterators/array.stderr
+++ /dev/null
@@ -1,36 +0,0 @@
-error[E0277]: `[{integer}; 2]` is not an iterator
-  --> $DIR/array.rs:2:14
-   |
-LL |     for _ in [1, 2] {}
-   |              ^^^^^^ arrays do not yet implement `IntoIterator`; try using `std::array::IntoIter::new(arr)`
-   |
-   = help: the trait `Iterator` is not implemented for `[{integer}; 2]`
-   = note: see <https://github.com/rust-lang/rust/pull/65819> for more details
-   = note: required because of the requirements on the impl of `IntoIterator` for `[{integer}; 2]`
-   = note: required by `into_iter`
-
-error[E0277]: `[{integer}; 2]` is not an iterator
-  --> $DIR/array.rs:5:14
-   |
-LL |     for _ in x {}
-   |              ^ arrays do not yet implement `IntoIterator`; try using `std::array::IntoIter::new(arr)`
-   |
-   = help: the trait `Iterator` is not implemented for `[{integer}; 2]`
-   = note: see <https://github.com/rust-lang/rust/pull/65819> for more details
-   = note: required because of the requirements on the impl of `IntoIterator` for `[{integer}; 2]`
-   = note: required by `into_iter`
-
-error[E0277]: `[{float}; 2]` is not an iterator
-  --> $DIR/array.rs:7:14
-   |
-LL |     for _ in [1.0, 2.0] {}
-   |              ^^^^^^^^^^ arrays do not yet implement `IntoIterator`; try using `std::array::IntoIter::new(arr)`
-   |
-   = help: the trait `Iterator` is not implemented for `[{float}; 2]`
-   = note: see <https://github.com/rust-lang/rust/pull/65819> for more details
-   = note: required because of the requirements on the impl of `IntoIterator` for `[{float}; 2]`
-   = note: required by `into_iter`
-
-error: aborting due to 3 previous errors
-
-For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/iterators/into-iter-on-arrays-2018.rs b/src/test/ui/iterators/into-iter-on-arrays-2018.rs
new file mode 100644
index 00000000000..5661397b3c1
--- /dev/null
+++ b/src/test/ui/iterators/into-iter-on-arrays-2018.rs
@@ -0,0 +1,39 @@
+// check-pass
+// edition:2018
+
+use std::array::IntoIter;
+use std::ops::Deref;
+use std::rc::Rc;
+use std::slice::Iter;
+
+fn main() {
+    let array = [0; 10];
+
+    // Before 2021, the method dispatched to `IntoIterator for &[T; N]`,
+    // which we continue to support for compatibility.
+    let _: Iter<'_, i32> = array.into_iter();
+    //~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter`
+    //~| WARNING this was previously accepted by the compiler but is being phased out
+
+    let _: Iter<'_, i32> = Box::new(array).into_iter();
+    //~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter`
+    //~| WARNING this was previously accepted by the compiler but is being phased out
+
+    // The `array_into_iter` lint doesn't cover other wrappers that deref to an array.
+    let _: Iter<'_, i32> = Rc::new(array).into_iter();
+    let _: Iter<'_, i32> = Array(array).into_iter();
+
+    // But you can always use the trait method explicitly as an array.
+    let _: IntoIter<i32, 10> = IntoIterator::into_iter(array);
+}
+
+/// User type that dereferences to an array.
+struct Array([i32; 10]);
+
+impl Deref for Array {
+    type Target = [i32; 10];
+
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
diff --git a/src/test/ui/iterators/into-iter-on-arrays-2018.stderr b/src/test/ui/iterators/into-iter-on-arrays-2018.stderr
new file mode 100644
index 00000000000..b43338382f2
--- /dev/null
+++ b/src/test/ui/iterators/into-iter-on-arrays-2018.stderr
@@ -0,0 +1,42 @@
+warning: this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter` (due to autoref coercions), but that might change in the future when `IntoIterator` impls for arrays are added.
+  --> $DIR/into-iter-on-arrays-2018.rs:14:34
+   |
+LL |     let _: Iter<'_, i32> = array.into_iter();
+   |                                  ^^^^^^^^^ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity: `iter`
+   |
+   = note: `#[warn(array_into_iter)]` on by default
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #66145 <https://github.com/rust-lang/rust/issues/66145>
+
+warning: this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter` (due to autoref coercions), but that might change in the future when `IntoIterator` impls for arrays are added.
+  --> $DIR/into-iter-on-arrays-2018.rs:18:44
+   |
+LL |     let _: Iter<'_, i32> = Box::new(array).into_iter();
+   |                                            ^^^^^^^^^ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity: `iter`
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #66145 <https://github.com/rust-lang/rust/issues/66145>
+
+warning: 2 warnings emitted
+
+Future incompatibility report: Future breakage date: None, diagnostic:
+warning: this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter` (due to autoref coercions), but that might change in the future when `IntoIterator` impls for arrays are added.
+  --> $DIR/into-iter-on-arrays-2018.rs:14:34
+   |
+LL |     let _: Iter<'_, i32> = array.into_iter();
+   |                                  ^^^^^^^^^ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity: `iter`
+   |
+   = note: `#[warn(array_into_iter)]` on by default
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #66145 <https://github.com/rust-lang/rust/issues/66145>
+
+Future breakage date: None, diagnostic:
+warning: this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter` (due to autoref coercions), but that might change in the future when `IntoIterator` impls for arrays are added.
+  --> $DIR/into-iter-on-arrays-2018.rs:18:44
+   |
+LL |     let _: Iter<'_, i32> = Box::new(array).into_iter();
+   |                                            ^^^^^^^^^ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity: `iter`
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #66145 <https://github.com/rust-lang/rust/issues/66145>
+
diff --git a/src/test/ui/iterators/into-iter-on-arrays-2021.rs b/src/test/ui/iterators/into-iter-on-arrays-2021.rs
new file mode 100644
index 00000000000..ec54ed00517
--- /dev/null
+++ b/src/test/ui/iterators/into-iter-on-arrays-2021.rs
@@ -0,0 +1,33 @@
+// check-pass
+// edition:2021
+// compile-flags: -Zunstable-options
+
+use std::array::IntoIter;
+use std::ops::Deref;
+use std::rc::Rc;
+
+fn main() {
+    let array = [0; 10];
+
+    // In 2021, the method dispatches to `IntoIterator for [T; N]`.
+    let _: IntoIter<i32, 10> = array.into_iter();
+    let _: IntoIter<i32, 10> = Box::new(array).into_iter();
+
+    // The `array_into_iter` lint doesn't cover other wrappers that deref to an array.
+    let _: IntoIter<i32, 10> = Rc::new(array).into_iter();
+    let _: IntoIter<i32, 10> = Array(array).into_iter();
+
+    // You can always use the trait method explicitly as an array.
+    let _: IntoIter<i32, 10> = IntoIterator::into_iter(array);
+}
+
+/// User type that dereferences to an array.
+struct Array([i32; 10]);
+
+impl Deref for Array {
+    type Target = [i32; 10];
+
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}