Auto merge of #124097 - compiler-errors:box-into-iter, r=WaffleLapkin

Add `IntoIterator` for `Box<[T]>` + edition 2024-specific lints

* Adds a similar method probe opt-out mechanism to the `[T;N]: IntoIterator` implementation for edition 2021.
* Adjusts the relevant lints (shadowed `.into_iter()` calls, new source of method ambiguity).
* Adds some tests.
* Took the liberty to rework the logic in the `ARRAY_INTO_ITER` lint, since it was kind of confusing.

Based mostly off of #116607.

ACP: rust-lang/libs-team#263
References #59878
Tracking for Rust 2024: https://github.com/rust-lang/rust/issues/123759

Crater run was done here: https://github.com/rust-lang/rust/pull/116607#issuecomment-1770293013
Consensus afaict was that there is too much breakage, so let's do this in an edition-dependent way much like `[T; N]: IntoIterator`.
diff --git a/compiler/rustc_ast/src/ptr.rs b/compiler/rustc_ast/src/ptr.rs
index e22a523..34c539e 100644
--- a/compiler/rustc_ast/src/ptr.rs
+++ b/compiler/rustc_ast/src/ptr.rs
@@ -184,7 +184,7 @@
     type Item = &'a T;
     type IntoIter = slice::Iter<'a, T>;
     fn into_iter(self) -> Self::IntoIter {
-        self.ptr.into_iter()
+        self.ptr.iter()
     }
 }
 
diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs
index dbb88e4..ebaa9f8 100644
--- a/compiler/rustc_feature/src/builtin_attrs.rs
+++ b/compiler/rustc_feature/src/builtin_attrs.rs
@@ -899,10 +899,11 @@
         "the `#[rustc_main]` attribute is used internally to specify test entry point function",
     ),
     rustc_attr!(
-        rustc_skip_array_during_method_dispatch, Normal, template!(Word),
-        WarnFollowing, EncodeCrossCrate::No,
-        "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."
+        rustc_skip_during_method_dispatch, Normal, template!(List: "array, boxed_slice"), WarnFollowing,
+        EncodeCrossCrate::No,
+        "the `#[rustc_skip_during_method_dispatch]` attribute is used to exclude a trait \
+        from method dispatch when the receiver is of the following type, for compatibility in \
+        editions < 2021 (array) or editions < 2024 (boxed_slice)."
     ),
     rustc_attr!(
         rustc_must_implement_one_of, Normal, template!(List: "function1, function2, ..."),
diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs
index aa28b2c..b760b86 100644
--- a/compiler/rustc_hir_analysis/src/collect.rs
+++ b/compiler/rustc_hir_analysis/src/collect.rs
@@ -1117,8 +1117,24 @@
 
     let is_marker = tcx.has_attr(def_id, sym::marker);
     let rustc_coinductive = tcx.has_attr(def_id, sym::rustc_coinductive);
-    let skip_array_during_method_dispatch =
-        tcx.has_attr(def_id, sym::rustc_skip_array_during_method_dispatch);
+
+    // FIXME: We could probably do way better attribute validation here.
+    let mut skip_array_during_method_dispatch = false;
+    let mut skip_boxed_slice_during_method_dispatch = false;
+    for attr in tcx.get_attrs(def_id, sym::rustc_skip_during_method_dispatch) {
+        if let Some(lst) = attr.meta_item_list() {
+            for item in lst {
+                if let Some(ident) = item.ident() {
+                    match ident.as_str() {
+                        "array" => skip_array_during_method_dispatch = true,
+                        "boxed_slice" => skip_boxed_slice_during_method_dispatch = true,
+                        _ => (),
+                    }
+                }
+            }
+        }
+    }
+
     let specialization_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) {
@@ -1253,6 +1269,7 @@
         is_marker,
         is_coinductive: rustc_coinductive || is_auto,
         skip_array_during_method_dispatch,
+        skip_boxed_slice_during_method_dispatch,
         specialization_kind,
         must_implement_one_of,
         implement_via_object,
diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs
index a40fa60..4165ccb 100644
--- a/compiler/rustc_hir_typeck/src/method/mod.rs
+++ b/compiler/rustc_hir_typeck/src/method/mod.rs
@@ -3,7 +3,7 @@
 //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/method-lookup.html
 
 mod confirm;
-mod prelude2021;
+mod prelude_edition_lints;
 pub mod probe;
 mod suggest;
 
@@ -186,7 +186,9 @@
         let pick =
             self.lookup_probe(segment.ident, self_ty, call_expr, ProbeScope::TraitsInScope)?;
 
-        self.lint_dot_call_from_2018(self_ty, segment, span, call_expr, self_expr, &pick, args);
+        self.lint_edition_dependent_dot_call(
+            self_ty, segment, span, call_expr, self_expr, &pick, args,
+        );
 
         for &import_id in &pick.import_ids {
             debug!("used_trait_import: {:?}", import_id);
diff --git a/compiler/rustc_hir_typeck/src/method/prelude2021.rs b/compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs
similarity index 90%
rename from compiler/rustc_hir_typeck/src/method/prelude2021.rs
rename to compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs
index eca3268..e9eab69 100644
--- a/compiler/rustc_hir_typeck/src/method/prelude2021.rs
+++ b/compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs
@@ -1,12 +1,12 @@
-use crate::{
-    method::probe::{self, Pick},
-    FnCtxt,
-};
+use crate::method::probe::{self, Pick};
+use crate::FnCtxt;
+
 use hir::def_id::DefId;
 use hir::HirId;
 use hir::ItemKind;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
+use rustc_lint::{ARRAY_INTO_ITER, BOXED_SLICE_INTO_ITER};
 use rustc_middle::span_bug;
 use rustc_middle::ty::{self, Ty};
 use rustc_session::lint::builtin::RUST_2021_PRELUDE_COLLISIONS;
@@ -17,7 +17,7 @@
 use std::fmt::Write;
 
 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
-    pub(super) fn lint_dot_call_from_2018(
+    pub(super) fn lint_edition_dependent_dot_call(
         &self,
         self_ty: Ty<'tcx>,
         segment: &hir::PathSegment<'_>,
@@ -32,22 +32,32 @@
             segment.ident, self_ty, call_expr, self_expr
         );
 
-        // Rust 2021 and later is already using the new prelude
-        if span.at_least_rust_2021() {
-            return;
-        }
-
-        let prelude_or_array_lint = match segment.ident.name {
+        let (prelude_or_array_lint, edition) = match segment.ident.name {
             // `try_into` was added to the prelude in Rust 2021.
-            sym::try_into => RUST_2021_PRELUDE_COLLISIONS,
+            sym::try_into if !span.at_least_rust_2021() => (RUST_2021_PRELUDE_COLLISIONS, "2021"),
             // `into_iter` wasn't added to the prelude,
             // but `[T; N].into_iter()` doesn't resolve to IntoIterator::into_iter
             // before Rust 2021, which results in the same problem.
             // It is only a problem for arrays.
-            sym::into_iter if let ty::Array(..) = self_ty.kind() => {
-                // In this case, it wasn't really a prelude addition that was the problem.
-                // Instead, the problem is that the array-into_iter hack will no longer apply in Rust 2021.
-                rustc_lint::ARRAY_INTO_ITER
+            sym::into_iter => {
+                if let ty::Array(..) = self_ty.kind()
+                    && !span.at_least_rust_2021()
+                {
+                    // In this case, it wasn't really a prelude addition that was the problem.
+                    // Instead, the problem is that the array-into_iter hack will no longer
+                    // apply in Rust 2021.
+                    (ARRAY_INTO_ITER, "2021")
+                } else if self_ty.is_box()
+                    && self_ty.boxed_ty().is_slice()
+                    && !span.at_least_rust_2024()
+                {
+                    // In this case, it wasn't really a prelude addition that was the problem.
+                    // Instead, the problem is that the boxed-slice-into_iter hack will no
+                    // longer apply in Rust 2024.
+                    (BOXED_SLICE_INTO_ITER, "2024")
+                } else {
+                    return;
+                }
             }
             _ => return,
         };
@@ -81,7 +91,10 @@
                 prelude_or_array_lint,
                 self_expr.hir_id,
                 self_expr.span,
-                format!("trait method `{}` will become ambiguous in Rust 2021", segment.ident.name),
+                format!(
+                    "trait method `{}` will become ambiguous in Rust {edition}",
+                    segment.ident.name
+                ),
                 |lint| {
                     let sp = self_expr.span;
 
@@ -131,7 +144,10 @@
                 prelude_or_array_lint,
                 call_expr.hir_id,
                 call_expr.span,
-                format!("trait method `{}` will become ambiguous in Rust 2021", segment.ident.name),
+                format!(
+                    "trait method `{}` will become ambiguous in Rust {edition}",
+                    segment.ident.name
+                ),
                 |lint| {
                     let sp = call_expr.span;
                     let trait_name = self.trait_path_or_bare_name(
diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs
index 60a63d7..e0a6033 100644
--- a/compiler/rustc_hir_typeck/src/method/probe.rs
+++ b/compiler/rustc_hir_typeck/src/method/probe.rs
@@ -1444,6 +1444,18 @@
                                 return ProbeResult::NoMatch;
                             }
                         }
+
+                        // Some trait methods are excluded for boxed slices before 2024.
+                        // (`boxed_slice.into_iter()` wants a slice iterator for compatibility.)
+                        if self_ty.is_box()
+                            && self_ty.boxed_ty().is_slice()
+                            && !method_name.span.at_least_rust_2024()
+                        {
+                            let trait_def = self.tcx.trait_def(poly_trait_ref.def_id());
+                            if trait_def.skip_boxed_slice_during_method_dispatch {
+                                return ProbeResult::NoMatch;
+                            }
+                        }
                     }
 
                     let trait_ref = self.instantiate_binder_with_fresh_vars(
diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl
index 5180fce..cf9d089 100644
--- a/compiler/rustc_lint/messages.ftl
+++ b/compiler/rustc_lint/messages.ftl
@@ -2,13 +2,6 @@
     .addr_metadata_suggestion = use explicit `std::ptr::eq` method to compare metadata and addresses
     .addr_suggestion = use `std::ptr::addr_eq` or untyped pointers to only compare their addresses
 
-lint_array_into_iter =
-    this method call resolves to `<&{$target} as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to <{$target} as IntoIterator>::into_iter in Rust 2021
-    .use_iter_suggestion = use `.iter()` instead of `.into_iter()` to avoid ambiguity
-    .remove_into_iter_suggestion = or remove `.into_iter()` to iterate by value
-    .use_explicit_into_iter_suggestion =
-        or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value
-
 lint_async_fn_in_trait = use of `async fn` in public traits is discouraged as auto trait bounds cannot be specified
     .note = you can suppress this lint if you plan to use the trait only in your own code, or do not care about auto traits like `Send` on the `Future`
     .suggestion = you can alternatively desugar to a normal `fn` that returns `impl Future` and add any desired bounds such as `Send`, but these cannot be relaxed without a breaking API change
@@ -565,6 +558,13 @@
 
 lint_requested_level = requested on the command line with `{$level} {$lint_name}`
 
+lint_shadowed_into_iter =
+    this method call resolves to `<&{$target} as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<{$target} as IntoIterator>::into_iter` in Rust {$edition}
+    .use_iter_suggestion = use `.iter()` instead of `.into_iter()` to avoid ambiguity
+    .remove_into_iter_suggestion = or remove `.into_iter()` to iterate by value
+    .use_explicit_into_iter_suggestion =
+        or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value
+
 lint_span_use_eq_ctxt = use `.eq_ctxt()` instead of `.ctxt() == .ctxt()`
 
 lint_supertrait_as_deref_target = this `Deref` implementation is covered by an implicit supertrait coercion
diff --git a/compiler/rustc_lint/src/array_into_iter.rs b/compiler/rustc_lint/src/array_into_iter.rs
deleted file mode 100644
index 8f4bae3..0000000
--- a/compiler/rustc_lint/src/array_into_iter.rs
+++ /dev/null
@@ -1,144 +0,0 @@
-use crate::{
-    lints::{ArrayIntoIterDiag, ArrayIntoIterDiagSub},
-    LateContext, LateLintPass, LintContext,
-};
-use rustc_hir as hir;
-use rustc_middle::bug;
-use rustc_middle::ty;
-use rustc_middle::ty::adjustment::{Adjust, Adjustment};
-use rustc_session::lint::FutureIncompatibilityReason;
-use rustc_session::{declare_lint, impl_lint_pass};
-use rustc_span::edition::Edition;
-use rustc_span::symbol::sym;
-use rustc_span::Span;
-
-declare_lint! {
-    /// The `array_into_iter` lint detects calling `into_iter` on arrays.
-    ///
-    /// ### Example
-    ///
-    /// ```rust,edition2018
-    /// # #![allow(unused)]
-    /// [1, 2, 3].into_iter().for_each(|n| { *n; });
-    /// ```
-    ///
-    /// {{produces}}
-    ///
-    /// ### Explanation
-    ///
-    /// Since Rust 1.53, arrays implement `IntoIterator`. However, to avoid
-    /// breakage, `array.into_iter()` in Rust 2015 and 2018 code will still
-    /// behave as `(&array).into_iter()`, returning an iterator over
-    /// references, just like in Rust 1.52 and earlier.
-    /// This only applies to the method call syntax `array.into_iter()`, not to
-    /// any other syntax such as `for _ in array` or `IntoIterator::into_iter(array)`.
-    pub ARRAY_INTO_ITER,
-    Warn,
-    "detects calling `into_iter` on arrays in Rust 2015 and 2018",
-    @future_incompatible = FutureIncompatibleInfo {
-        reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2021),
-        reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2021/IntoIterator-for-arrays.html>",
-    };
-}
-
-#[derive(Copy, Clone, Default)]
-pub struct ArrayIntoIter {
-    for_expr_span: Span,
-}
-
-impl_lint_pass!(ArrayIntoIter => [ARRAY_INTO_ITER]);
-
-impl<'tcx> LateLintPass<'tcx> for ArrayIntoIter {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
-        // Save the span of expressions in `for _ in expr` syntax,
-        // so we can give a better suggestion for those later.
-        if let hir::ExprKind::Match(arg, [_], hir::MatchSource::ForLoopDesugar) = &expr.kind {
-            if let hir::ExprKind::Call(path, [arg]) = &arg.kind {
-                if let hir::ExprKind::Path(hir::QPath::LangItem(
-                    hir::LangItem::IntoIterIntoIter,
-                    ..,
-                )) = &path.kind
-                {
-                    self.for_expr_span = arg.span;
-                }
-            }
-        }
-
-        // We only care about method call expressions.
-        if let hir::ExprKind::MethodCall(call, receiver_arg, ..) = &expr.kind {
-            if call.ident.name != sym::into_iter {
-                return;
-            }
-
-            // Check if the method call actually calls the libcore
-            // `IntoIterator::into_iter`.
-            let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap();
-            match cx.tcx.trait_of_item(def_id) {
-                Some(trait_id) if cx.tcx.is_diagnostic_item(sym::IntoIterator, trait_id) => {}
-                _ => return,
-            };
-
-            // As this is a method call expression, we have at least one argument.
-            let receiver_ty = cx.typeck_results().expr_ty(receiver_arg);
-            let adjustments = cx.typeck_results().expr_adjustments(receiver_arg);
-
-            let Some(Adjustment { kind: Adjust::Borrow(_), target }) = adjustments.last() else {
-                return;
-            };
-
-            let types =
-                std::iter::once(receiver_ty).chain(adjustments.iter().map(|adj| adj.target));
-
-            let mut found_array = false;
-
-            for ty in types {
-                match ty.kind() {
-                    // If we run into a &[T; N] or &[T] first, there's nothing to warn about.
-                    // It'll resolve to the reference version.
-                    ty::Ref(_, inner_ty, _) if inner_ty.is_array() => return,
-                    ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), ty::Slice(..)) => return,
-                    // Found an actual array type without matching a &[T; N] first.
-                    // This is the problematic case.
-                    ty::Array(..) => {
-                        found_array = true;
-                        break;
-                    }
-                    _ => {}
-                }
-            }
-
-            if !found_array {
-                return;
-            }
-
-            // Emit lint diagnostic.
-            let target = match *target.kind() {
-                ty::Ref(_, inner_ty, _) if inner_ty.is_array() => "[T; N]",
-                ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), ty::Slice(..)) => "[T]",
-                // We know the original first argument type is an array type,
-                // we know that the first adjustment was an autoref coercion
-                // and we know that `IntoIterator` is the trait involved. The
-                // array cannot be coerced to something other than a reference
-                // to an array or to a slice.
-                _ => bug!("array type coerced to something other than array or slice"),
-            };
-            let sub = if self.for_expr_span == expr.span {
-                Some(ArrayIntoIterDiagSub::RemoveIntoIter {
-                    span: receiver_arg.span.shrink_to_hi().to(expr.span.shrink_to_hi()),
-                })
-            } else if receiver_ty.is_array() {
-                Some(ArrayIntoIterDiagSub::UseExplicitIntoIter {
-                    start_span: expr.span.shrink_to_lo(),
-                    end_span: receiver_arg.span.shrink_to_hi().to(expr.span.shrink_to_hi()),
-                })
-            } else {
-                None
-            };
-            cx.emit_span_lint(
-                ARRAY_INTO_ITER,
-                call.ident.span,
-                ArrayIntoIterDiag { target, suggestion: call.ident.span, sub },
-            );
-        }
-    }
-}
diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs
index d93edad..64fcc7e 100644
--- a/compiler/rustc_lint/src/lib.rs
+++ b/compiler/rustc_lint/src/lib.rs
@@ -42,7 +42,6 @@
 #[macro_use]
 extern crate tracing;
 
-mod array_into_iter;
 mod async_fn_in_trait;
 pub mod builtin;
 mod context;
@@ -76,18 +75,18 @@
 mod ptr_nulls;
 mod redundant_semicolon;
 mod reference_casting;
+mod shadowed_into_iter;
 mod traits;
 mod types;
 mod unit_bindings;
 mod unused;
 
-pub use array_into_iter::ARRAY_INTO_ITER;
+pub use shadowed_into_iter::{ARRAY_INTO_ITER, BOXED_SLICE_INTO_ITER};
 
 use rustc_hir::def_id::LocalModDefId;
 use rustc_middle::query::Providers;
 use rustc_middle::ty::TyCtxt;
 
-use array_into_iter::ArrayIntoIter;
 use async_fn_in_trait::AsyncFnInTrait;
 use builtin::*;
 use deref_into_dyn_supertrait::*;
@@ -112,6 +111,7 @@
 use ptr_nulls::*;
 use redundant_semicolon::*;
 use reference_casting::*;
+use shadowed_into_iter::ShadowedIntoIter;
 use traits::*;
 use types::*;
 use unit_bindings::*;
@@ -215,7 +215,7 @@
             DerefNullPtr: DerefNullPtr,
             UnstableFeatures: UnstableFeatures,
             UngatedAsyncFnTrackCaller: UngatedAsyncFnTrackCaller,
-            ArrayIntoIter: ArrayIntoIter::default(),
+            ShadowedIntoIter: ShadowedIntoIter,
             DropTraitConstraints: DropTraitConstraints,
             TemporaryCStringAsPtr: TemporaryCStringAsPtr,
             NonPanicFmt: NonPanicFmt,
diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs
index 7efa524..bc0c8cf 100644
--- a/compiler/rustc_lint/src/lints.rs
+++ b/compiler/rustc_lint/src/lints.rs
@@ -22,17 +22,18 @@
 
 // array_into_iter.rs
 #[derive(LintDiagnostic)]
-#[diag(lint_array_into_iter)]
-pub struct ArrayIntoIterDiag<'a> {
-    pub target: &'a str,
+#[diag(lint_shadowed_into_iter)]
+pub struct ShadowedIntoIterDiag {
+    pub target: &'static str,
+    pub edition: &'static str,
     #[suggestion(lint_use_iter_suggestion, code = "iter", applicability = "machine-applicable")]
     pub suggestion: Span,
     #[subdiagnostic]
-    pub sub: Option<ArrayIntoIterDiagSub>,
+    pub sub: Option<ShadowedIntoIterDiagSub>,
 }
 
 #[derive(Subdiagnostic)]
-pub enum ArrayIntoIterDiagSub {
+pub enum ShadowedIntoIterDiagSub {
     #[suggestion(lint_remove_into_iter_suggestion, code = "", applicability = "maybe-incorrect")]
     RemoveIntoIter {
         #[primary_span]
diff --git a/compiler/rustc_lint/src/shadowed_into_iter.rs b/compiler/rustc_lint/src/shadowed_into_iter.rs
new file mode 100644
index 0000000..41ec84f
--- /dev/null
+++ b/compiler/rustc_lint/src/shadowed_into_iter.rs
@@ -0,0 +1,157 @@
+use crate::lints::{ShadowedIntoIterDiag, ShadowedIntoIterDiagSub};
+use crate::{LateContext, LateLintPass, LintContext};
+use rustc_hir as hir;
+use rustc_middle::ty::{self, Ty};
+use rustc_session::lint::FutureIncompatibilityReason;
+use rustc_session::{declare_lint, impl_lint_pass};
+use rustc_span::edition::Edition;
+
+declare_lint! {
+    /// The `array_into_iter` lint detects calling `into_iter` on arrays.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,edition2018
+    /// # #![allow(unused)]
+    /// [1, 2, 3].into_iter().for_each(|n| { *n; });
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Since Rust 1.53, arrays implement `IntoIterator`. However, to avoid
+    /// breakage, `array.into_iter()` in Rust 2015 and 2018 code will still
+    /// behave as `(&array).into_iter()`, returning an iterator over
+    /// references, just like in Rust 1.52 and earlier.
+    /// This only applies to the method call syntax `array.into_iter()`, not to
+    /// any other syntax such as `for _ in array` or `IntoIterator::into_iter(array)`.
+    pub ARRAY_INTO_ITER,
+    Warn,
+    "detects calling `into_iter` on arrays in Rust 2015 and 2018",
+    @future_incompatible = FutureIncompatibleInfo {
+        reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2021),
+        reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2021/IntoIterator-for-arrays.html>",
+    };
+}
+
+declare_lint! {
+    /// The `boxed_slice_into_iter` lint detects calling `into_iter` on boxed slices.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,edition2021
+    /// # #![allow(unused)]
+    /// vec![1, 2, 3].into_boxed_slice().into_iter().for_each(|n| { *n; });
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Since Rust CURRENT_RUSTC_VERSION, boxed slices implement `IntoIterator`. However, to avoid
+    /// breakage, `boxed_slice.into_iter()` in Rust 2015, 2018, and 2021 code will still
+    /// behave as `(&boxed_slice).into_iter()`, returning an iterator over
+    /// references, just like in Rust CURRENT_RUSTC_VERSION and earlier.
+    /// This only applies to the method call syntax `boxed_slice.into_iter()`, not to
+    /// any other syntax such as `for _ in boxed_slice` or `IntoIterator::into_iter(boxed_slice)`.
+    pub BOXED_SLICE_INTO_ITER,
+    Warn,
+    "detects calling `into_iter` on boxed slices in Rust 2015, 2018, and 2021",
+    @future_incompatible = FutureIncompatibleInfo {
+        reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024),
+    };
+}
+
+#[derive(Copy, Clone)]
+pub struct ShadowedIntoIter;
+
+impl_lint_pass!(ShadowedIntoIter => [ARRAY_INTO_ITER, BOXED_SLICE_INTO_ITER]);
+
+impl<'tcx> LateLintPass<'tcx> for ShadowedIntoIter {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
+        let hir::ExprKind::MethodCall(call, receiver_arg, ..) = &expr.kind else {
+            return;
+        };
+
+        // Check if the method call actually calls the libcore
+        // `IntoIterator::into_iter`.
+        let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) else {
+            return;
+        };
+        if Some(method_def_id) != cx.tcx.lang_items().into_iter_fn() {
+            return;
+        }
+
+        // As this is a method call expression, we have at least one argument.
+        let receiver_ty = cx.typeck_results().expr_ty(receiver_arg);
+        let adjustments = cx.typeck_results().expr_adjustments(receiver_arg);
+
+        let adjusted_receiver_tys: Vec<_> =
+            [receiver_ty].into_iter().chain(adjustments.iter().map(|adj| adj.target)).collect();
+
+        fn is_ref_to_array(ty: Ty<'_>) -> bool {
+            if let ty::Ref(_, pointee_ty, _) = *ty.kind() { pointee_ty.is_array() } else { false }
+        }
+        fn is_boxed_slice(ty: Ty<'_>) -> bool {
+            ty.is_box() && ty.boxed_ty().is_slice()
+        }
+        fn is_ref_to_boxed_slice(ty: Ty<'_>) -> bool {
+            if let ty::Ref(_, pointee_ty, _) = *ty.kind() {
+                is_boxed_slice(pointee_ty)
+            } else {
+                false
+            }
+        }
+
+        let (lint, target, edition, can_suggest_ufcs) =
+            if is_ref_to_array(*adjusted_receiver_tys.last().unwrap())
+                && let Some(idx) = adjusted_receiver_tys
+                    .iter()
+                    .copied()
+                    .take_while(|ty| !is_ref_to_array(*ty))
+                    .position(|ty| ty.is_array())
+            {
+                (ARRAY_INTO_ITER, "[T; N]", "2021", idx == 0)
+            } else if is_ref_to_boxed_slice(*adjusted_receiver_tys.last().unwrap())
+                && let Some(idx) = adjusted_receiver_tys
+                    .iter()
+                    .copied()
+                    .take_while(|ty| !is_ref_to_boxed_slice(*ty))
+                    .position(|ty| is_boxed_slice(ty))
+            {
+                (BOXED_SLICE_INTO_ITER, "Box<[T]>", "2024", idx == 0)
+            } else {
+                return;
+            };
+
+        // If this expression comes from the `IntoIter::into_iter` inside of a for loop,
+        // we should just suggest removing the `.into_iter()` or changing it to `.iter()`
+        // to disambiguate if we want to iterate by-value or by-ref.
+        let sub = if let Some((_, hir::Node::Expr(parent_expr))) =
+            cx.tcx.hir().parent_iter(expr.hir_id).nth(1)
+            && let hir::ExprKind::Match(arg, [_], hir::MatchSource::ForLoopDesugar) =
+                &parent_expr.kind
+            && let hir::ExprKind::Call(path, [_]) = &arg.kind
+            && let hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::IntoIterIntoIter, ..)) =
+                &path.kind
+        {
+            Some(ShadowedIntoIterDiagSub::RemoveIntoIter {
+                span: receiver_arg.span.shrink_to_hi().to(expr.span.shrink_to_hi()),
+            })
+        } else if can_suggest_ufcs {
+            Some(ShadowedIntoIterDiagSub::UseExplicitIntoIter {
+                start_span: expr.span.shrink_to_lo(),
+                end_span: receiver_arg.span.shrink_to_hi().to(expr.span.shrink_to_hi()),
+            })
+        } else {
+            None
+        };
+
+        cx.emit_span_lint(
+            lint,
+            call.ident.span,
+            ShadowedIntoIterDiag { target, edition, suggestion: call.ident.span, sub },
+        );
+    }
+}
diff --git a/compiler/rustc_middle/src/ty/trait_def.rs b/compiler/rustc_middle/src/ty/trait_def.rs
index c5b3de1..cf1cbb9 100644
--- a/compiler/rustc_middle/src/ty/trait_def.rs
+++ b/compiler/rustc_middle/src/ty/trait_def.rs
@@ -39,11 +39,16 @@
     /// also have already switched to the new trait solver.
     pub is_coinductive: bool,
 
-    /// If `true`, then this trait has the `#[rustc_skip_array_during_method_dispatch]`
+    /// If `true`, then this trait has the `#[rustc_skip_during_method_dispatch(array)]`
     /// 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,
 
+    /// If `true`, then this trait has the `#[rustc_skip_during_method_dispatch(boxed_slice)]`
+    /// attribute, indicating that editions before 2024 should not consider this trait
+    /// during method dispatch if the receiver is a boxed slice.
+    pub skip_boxed_slice_during_method_dispatch: bool,
+
     /// Used to determine whether the standard library is allowed to specialize
     /// on this trait.
     pub specialization_kind: TraitSpecializationKind,
diff --git a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
index d59318b..1544798 100644
--- a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
+++ b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
@@ -505,6 +505,7 @@
             is_marker: self.is_marker,
             is_coinductive: self.is_coinductive,
             skip_array_during_method_dispatch: self.skip_array_during_method_dispatch,
+            skip_boxed_slice_during_method_dispatch: self.skip_boxed_slice_during_method_dispatch,
             specialization_kind: self.specialization_kind.stable(tables),
             must_implement_one_of: self
                 .must_implement_one_of
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index e453c03..1869daf 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -1633,7 +1633,7 @@
         rustc_reservation_impl,
         rustc_safe_intrinsic,
         rustc_serialize,
-        rustc_skip_array_during_method_dispatch,
+        rustc_skip_during_method_dispatch,
         rustc_specialization_trait,
         rustc_std_internal_symbol,
         rustc_strict_coherence,
diff --git a/compiler/stable_mir/src/ty.rs b/compiler/stable_mir/src/ty.rs
index 759e3f1..321c56b 100644
--- a/compiler/stable_mir/src/ty.rs
+++ b/compiler/stable_mir/src/ty.rs
@@ -1205,6 +1205,7 @@
     pub is_marker: bool,
     pub is_coinductive: bool,
     pub skip_array_during_method_dispatch: bool,
+    pub skip_boxed_slice_during_method_dispatch: bool,
     pub specialization_kind: TraitSpecializationKind,
     pub must_implement_one_of: Option<Vec<Ident>>,
     pub implement_via_object: bool,
diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs
index f1a6df9..21d0050 100644
--- a/library/alloc/src/boxed.rs
+++ b/library/alloc/src/boxed.rs
@@ -135,6 +135,45 @@
 //! is not allowed. For more guidance on working with box from unsafe code, see
 //! [rust-lang/unsafe-code-guidelines#326][ucg#326].
 //!
+//! # Editions
+//!
+//! A special case exists for the implementation of `IntoIterator` for arrays on the Rust 2021
+//! edition, as documented [here][array]. Unfortunately, it was later found that a similar
+//! workaround should be added for boxed slices, and this was applied in the 2024 edition.
+//!
+//! Specifically, `IntoIterator` is implemented for `Box<[T]>` on all editions, but specific calls
+//! to `into_iter()` for boxed slices will defer to the slice implementation on editions before
+//! 2024:
+//!
+#![cfg_attr(bootstrap, doc = "```rust,edition2021,ignore")]
+#![cfg_attr(not(bootstrap), doc = "```rust,edition2021")]
+//! // Rust 2015, 2018, and 2021:
+//!
+//! # #![allow(boxed_slice_into_iter)] // override our `deny(warnings)`
+//! let boxed_slice: Box<[i32]> = vec![0; 3].into_boxed_slice();
+//!
+//! // This creates a slice iterator, producing references to each value.
+//! for item in boxed_slice.into_iter().enumerate() {
+//!     let (i, x): (usize, &i32) = item;
+//!     println!("boxed_slice[{i}] = {x}");
+//! }
+//!
+//! // The `boxed_slice_into_iter` lint suggests this change for future compatibility:
+//! for item in boxed_slice.iter().enumerate() {
+//!     let (i, x): (usize, &i32) = item;
+//!     println!("boxed_slice[{i}] = {x}");
+//! }
+//!
+//! // You can explicitly iterate a boxed slice by value using `IntoIterator::into_iter`
+//! for item in IntoIterator::into_iter(boxed_slice).enumerate() {
+//!     let (i, x): (usize, i32) = item;
+//!     println!("boxed_slice[{i}] = {x}");
+//! }
+//! ```
+//!
+//! Similar to the array implementation, this may be modified in the future to remove this override,
+//! and it's best to avoid relying on this edition-dependent behavior if you wish to preserve
+//! compatibility with future versions of the compiler.
 //!
 //! [ucg#198]: https://github.com/rust-lang/unsafe-code-guidelines/issues/198
 //! [ucg#326]: https://github.com/rust-lang/unsafe-code-guidelines/issues/326
@@ -165,6 +204,7 @@
 };
 use core::pin::Pin;
 use core::ptr::{self, addr_of_mut, NonNull, Unique};
+use core::slice;
 use core::task::{Context, Poll};
 
 #[cfg(not(no_global_oom_handling))]
@@ -177,6 +217,7 @@
 use crate::str::from_boxed_utf8_unchecked;
 #[cfg(not(no_global_oom_handling))]
 use crate::string::String;
+use crate::vec;
 #[cfg(not(no_global_oom_handling))]
 use crate::vec::Vec;
 
@@ -2080,6 +2121,51 @@
     }
 }
 
+/// This implementation is required to make sure that the `Box<[I]>: IntoIterator`
+/// implementation doesn't overlap with `IntoIterator for T where T: Iterator` blanket.
+#[stable(feature = "boxed_slice_into_iter", since = "CURRENT_RUSTC_VERSION")]
+impl<I, A: Allocator> !Iterator for Box<[I], A> {}
+
+/// This implementation is required to make sure that the `&Box<[I]>: IntoIterator`
+/// implementation doesn't overlap with `IntoIterator for T where T: Iterator` blanket.
+#[stable(feature = "boxed_slice_into_iter", since = "CURRENT_RUSTC_VERSION")]
+impl<'a, I, A: Allocator> !Iterator for &'a Box<[I], A> {}
+
+/// This implementation is required to make sure that the `&mut Box<[I]>: IntoIterator`
+/// implementation doesn't overlap with `IntoIterator for T where T: Iterator` blanket.
+#[stable(feature = "boxed_slice_into_iter", since = "CURRENT_RUSTC_VERSION")]
+impl<'a, I, A: Allocator> !Iterator for &'a mut Box<[I], A> {}
+
+// Note: the `#[rustc_skip_during_method_dispatch(boxed_slice)]` on `trait IntoIterator`
+// hides this implementation from explicit `.into_iter()` calls on editions < 2024,
+// so those calls will still resolve to the slice implementation, by reference.
+#[stable(feature = "boxed_slice_into_iter", since = "CURRENT_RUSTC_VERSION")]
+impl<I, A: Allocator> IntoIterator for Box<[I], A> {
+    type IntoIter = vec::IntoIter<I, A>;
+    type Item = I;
+    fn into_iter(self) -> vec::IntoIter<I, A> {
+        self.into_vec().into_iter()
+    }
+}
+
+#[stable(feature = "boxed_slice_into_iter", since = "CURRENT_RUSTC_VERSION")]
+impl<'a, I, A: Allocator> IntoIterator for &'a Box<[I], A> {
+    type IntoIter = slice::Iter<'a, I>;
+    type Item = &'a I;
+    fn into_iter(self) -> slice::Iter<'a, I> {
+        self.iter()
+    }
+}
+
+#[stable(feature = "boxed_slice_into_iter", since = "CURRENT_RUSTC_VERSION")]
+impl<'a, I, A: Allocator> IntoIterator for &'a mut Box<[I], A> {
+    type IntoIter = slice::IterMut<'a, I>;
+    type Item = &'a mut I;
+    fn into_iter(self) -> slice::IterMut<'a, I> {
+        self.iter_mut()
+    }
+}
+
 #[cfg(not(no_global_oom_handling))]
 #[stable(feature = "boxed_str_from_iter", since = "CURRENT_RUSTC_VERSION")]
 impl FromIterator<char> for Box<str> {
diff --git a/library/core/src/array/iter.rs b/library/core/src/array/iter.rs
index e3d2cd2..b314d05 100644
--- a/library/core/src/array/iter.rs
+++ b/library/core/src/array/iter.rs
@@ -38,7 +38,7 @@
     alive: IndexRange,
 }
 
-// Note: the `#[rustc_skip_array_during_method_dispatch]` on `trait IntoIterator`
+// Note: the `#[rustc_skip_during_method_dispatch(array)]` 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.
 #[stable(feature = "array_into_iter_impl", since = "1.53.0")]
diff --git a/library/core/src/iter/traits/collect.rs b/library/core/src/iter/traits/collect.rs
index 5637812..d9d860c 100644
--- a/library/core/src/iter/traits/collect.rs
+++ b/library/core/src/iter/traits/collect.rs
@@ -268,7 +268,6 @@
 /// }
 /// ```
 #[rustc_diagnostic_item = "IntoIterator"]
-#[rustc_skip_array_during_method_dispatch]
 #[rustc_on_unimplemented(
     on(
         _Self = "core::ops::range::RangeTo<Idx>",
@@ -312,6 +311,8 @@
     label = "`{Self}` is not an iterator",
     message = "`{Self}` is not an iterator"
 )]
+#[cfg_attr(bootstrap, rustc_skip_array_during_method_dispatch)]
+#[cfg_attr(not(bootstrap), rustc_skip_during_method_dispatch(array, boxed_slice))]
 #[stable(feature = "rust1", since = "1.0.0")]
 pub trait IntoIterator {
     /// The type of the elements being iterated over.
diff --git a/library/core/src/slice/iter.rs b/library/core/src/slice/iter.rs
index d7d4f90..96fc87a 100644
--- a/library/core/src/slice/iter.rs
+++ b/library/core/src/slice/iter.rs
@@ -16,6 +16,9 @@
 
 use super::{from_raw_parts, from_raw_parts_mut};
 
+#[stable(feature = "boxed_slice_into_iter", since = "CURRENT_RUSTC_VERSION")]
+impl<T> !Iterator for [T] {}
+
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<'a, T> IntoIterator for &'a [T] {
     type Item = &'a T;
diff --git a/src/tools/clippy/tests/ui/into_iter_on_ref.fixed b/src/tools/clippy/tests/ui/into_iter_on_ref.fixed
index c03d91c..72b39c9 100644
--- a/src/tools/clippy/tests/ui/into_iter_on_ref.fixed
+++ b/src/tools/clippy/tests/ui/into_iter_on_ref.fixed
@@ -11,7 +11,6 @@
 
     let _ = vec![1, 2, 3].into_iter();
     let _ = (&vec![1, 2, 3]).iter(); //~ ERROR: equivalent to `.iter()
-    let _ = vec![1, 2, 3].into_boxed_slice().iter(); //~ ERROR: equivalent to `.iter()
     let _ = std::rc::Rc::from(&[X][..]).iter(); //~ ERROR: equivalent to `.iter()
     let _ = std::sync::Arc::from(&[X][..]).iter(); //~ ERROR: equivalent to `.iter()
 
diff --git a/src/tools/clippy/tests/ui/into_iter_on_ref.rs b/src/tools/clippy/tests/ui/into_iter_on_ref.rs
index 93c732f..5ba2247 100644
--- a/src/tools/clippy/tests/ui/into_iter_on_ref.rs
+++ b/src/tools/clippy/tests/ui/into_iter_on_ref.rs
@@ -11,7 +11,6 @@
 
     let _ = vec![1, 2, 3].into_iter();
     let _ = (&vec![1, 2, 3]).into_iter(); //~ ERROR: equivalent to `.iter()
-    let _ = vec![1, 2, 3].into_boxed_slice().into_iter(); //~ ERROR: equivalent to `.iter()
     let _ = std::rc::Rc::from(&[X][..]).into_iter(); //~ ERROR: equivalent to `.iter()
     let _ = std::sync::Arc::from(&[X][..]).into_iter(); //~ ERROR: equivalent to `.iter()
 
diff --git a/src/tools/clippy/tests/ui/into_iter_on_ref.stderr b/src/tools/clippy/tests/ui/into_iter_on_ref.stderr
index 0e9d485..64d8140 100644
--- a/src/tools/clippy/tests/ui/into_iter_on_ref.stderr
+++ b/src/tools/clippy/tests/ui/into_iter_on_ref.stderr
@@ -8,160 +8,154 @@
    = help: to override `-D warnings` add `#[allow(clippy::into_iter_on_ref)]`
 
 error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `slice`
-  --> tests/ui/into_iter_on_ref.rs:14:46
-   |
-LL |     let _ = vec![1, 2, 3].into_boxed_slice().into_iter();
-   |                                              ^^^^^^^^^ help: call directly: `iter`
-
-error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `slice`
-  --> tests/ui/into_iter_on_ref.rs:15:41
+  --> tests/ui/into_iter_on_ref.rs:14:41
    |
 LL |     let _ = std::rc::Rc::from(&[X][..]).into_iter();
    |                                         ^^^^^^^^^ help: call directly: `iter`
 
 error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `slice`
-  --> tests/ui/into_iter_on_ref.rs:16:44
+  --> tests/ui/into_iter_on_ref.rs:15:44
    |
 LL |     let _ = std::sync::Arc::from(&[X][..]).into_iter();
    |                                            ^^^^^^^^^ help: call directly: `iter`
 
 error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `array`
-  --> tests/ui/into_iter_on_ref.rs:18:32
+  --> tests/ui/into_iter_on_ref.rs:17:32
    |
 LL |     let _ = (&&&&&&&[1, 2, 3]).into_iter();
    |                                ^^^^^^^^^ help: call directly: `iter`
 
 error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `array`
-  --> tests/ui/into_iter_on_ref.rs:19:36
+  --> tests/ui/into_iter_on_ref.rs:18:36
    |
 LL |     let _ = (&&&&mut &&&[1, 2, 3]).into_iter();
    |                                    ^^^^^^^^^ help: call directly: `iter`
 
 error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `array`
-  --> tests/ui/into_iter_on_ref.rs:20:40
+  --> tests/ui/into_iter_on_ref.rs:19:40
    |
 LL |     let _ = (&mut &mut &mut [1, 2, 3]).into_iter();
    |                                        ^^^^^^^^^ help: call directly: `iter_mut`
 
 error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `Option`
-  --> tests/ui/into_iter_on_ref.rs:22:24
+  --> tests/ui/into_iter_on_ref.rs:21:24
    |
 LL |     let _ = (&Some(4)).into_iter();
    |                        ^^^^^^^^^ help: call directly: `iter`
 
 error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `Option`
-  --> tests/ui/into_iter_on_ref.rs:23:28
+  --> tests/ui/into_iter_on_ref.rs:22:28
    |
 LL |     let _ = (&mut Some(5)).into_iter();
    |                            ^^^^^^^^^ help: call directly: `iter_mut`
 
 error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `Result`
-  --> tests/ui/into_iter_on_ref.rs:24:32
+  --> tests/ui/into_iter_on_ref.rs:23:32
    |
 LL |     let _ = (&Ok::<_, i32>(6)).into_iter();
    |                                ^^^^^^^^^ help: call directly: `iter`
 
 error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `Result`
-  --> tests/ui/into_iter_on_ref.rs:25:37
+  --> tests/ui/into_iter_on_ref.rs:24:37
    |
 LL |     let _ = (&mut Err::<i32, _>(7)).into_iter();
    |                                     ^^^^^^^^^ help: call directly: `iter_mut`
 
 error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `Vec`
-  --> tests/ui/into_iter_on_ref.rs:26:34
+  --> tests/ui/into_iter_on_ref.rs:25:34
    |
 LL |     let _ = (&Vec::<i32>::new()).into_iter();
    |                                  ^^^^^^^^^ help: call directly: `iter`
 
 error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `Vec`
-  --> tests/ui/into_iter_on_ref.rs:27:38
+  --> tests/ui/into_iter_on_ref.rs:26:38
    |
 LL |     let _ = (&mut Vec::<i32>::new()).into_iter();
    |                                      ^^^^^^^^^ help: call directly: `iter_mut`
 
 error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `BTreeMap`
-  --> tests/ui/into_iter_on_ref.rs:28:44
+  --> tests/ui/into_iter_on_ref.rs:27:44
    |
 LL |     let _ = (&BTreeMap::<i32, u64>::new()).into_iter();
    |                                            ^^^^^^^^^ help: call directly: `iter`
 
 error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `BTreeMap`
-  --> tests/ui/into_iter_on_ref.rs:29:48
+  --> tests/ui/into_iter_on_ref.rs:28:48
    |
 LL |     let _ = (&mut BTreeMap::<i32, u64>::new()).into_iter();
    |                                                ^^^^^^^^^ help: call directly: `iter_mut`
 
 error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `VecDeque`
-  --> tests/ui/into_iter_on_ref.rs:30:39
+  --> tests/ui/into_iter_on_ref.rs:29:39
    |
 LL |     let _ = (&VecDeque::<i32>::new()).into_iter();
    |                                       ^^^^^^^^^ help: call directly: `iter`
 
 error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `VecDeque`
-  --> tests/ui/into_iter_on_ref.rs:31:43
+  --> tests/ui/into_iter_on_ref.rs:30:43
    |
 LL |     let _ = (&mut VecDeque::<i32>::new()).into_iter();
    |                                           ^^^^^^^^^ help: call directly: `iter_mut`
 
 error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `LinkedList`
-  --> tests/ui/into_iter_on_ref.rs:32:41
+  --> tests/ui/into_iter_on_ref.rs:31:41
    |
 LL |     let _ = (&LinkedList::<i32>::new()).into_iter();
    |                                         ^^^^^^^^^ help: call directly: `iter`
 
 error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `LinkedList`
-  --> tests/ui/into_iter_on_ref.rs:33:45
+  --> tests/ui/into_iter_on_ref.rs:32:45
    |
 LL |     let _ = (&mut LinkedList::<i32>::new()).into_iter();
    |                                             ^^^^^^^^^ help: call directly: `iter_mut`
 
 error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `HashMap`
-  --> tests/ui/into_iter_on_ref.rs:34:43
+  --> tests/ui/into_iter_on_ref.rs:33:43
    |
 LL |     let _ = (&HashMap::<i32, u64>::new()).into_iter();
    |                                           ^^^^^^^^^ help: call directly: `iter`
 
 error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `HashMap`
-  --> tests/ui/into_iter_on_ref.rs:35:47
+  --> tests/ui/into_iter_on_ref.rs:34:47
    |
 LL |     let _ = (&mut HashMap::<i32, u64>::new()).into_iter();
    |                                               ^^^^^^^^^ help: call directly: `iter_mut`
 
 error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `BTreeSet`
-  --> tests/ui/into_iter_on_ref.rs:37:39
+  --> tests/ui/into_iter_on_ref.rs:36:39
    |
 LL |     let _ = (&BTreeSet::<i32>::new()).into_iter();
    |                                       ^^^^^^^^^ help: call directly: `iter`
 
 error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `BinaryHeap`
-  --> tests/ui/into_iter_on_ref.rs:38:41
+  --> tests/ui/into_iter_on_ref.rs:37:41
    |
 LL |     let _ = (&BinaryHeap::<i32>::new()).into_iter();
    |                                         ^^^^^^^^^ help: call directly: `iter`
 
 error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `HashSet`
-  --> tests/ui/into_iter_on_ref.rs:39:38
+  --> tests/ui/into_iter_on_ref.rs:38:38
    |
 LL |     let _ = (&HashSet::<i32>::new()).into_iter();
    |                                      ^^^^^^^^^ help: call directly: `iter`
 
 error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `Path`
-  --> tests/ui/into_iter_on_ref.rs:40:43
+  --> tests/ui/into_iter_on_ref.rs:39:43
    |
 LL |     let _ = std::path::Path::new("12/34").into_iter();
    |                                           ^^^^^^^^^ help: call directly: `iter`
 
 error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `PathBuf`
-  --> tests/ui/into_iter_on_ref.rs:41:47
+  --> tests/ui/into_iter_on_ref.rs:40:47
    |
 LL |     let _ = std::path::PathBuf::from("12/34").into_iter();
    |                                               ^^^^^^^^^ help: call directly: `iter`
 
 error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `array`
-  --> tests/ui/into_iter_on_ref.rs:43:26
+  --> tests/ui/into_iter_on_ref.rs:42:26
    |
 LL |     let _ = (&[1, 2, 3]).into_iter().next();
    |                          ^^^^^^^^^ help: call directly: `iter`
 
-error: aborting due to 27 previous errors
+error: aborting due to 26 previous errors
 
diff --git a/src/tools/miri/tests/fail/shims/backtrace/bad-backtrace-decl.rs b/src/tools/miri/tests/fail/shims/backtrace/bad-backtrace-decl.rs
index 97a7010..a20539e 100644
--- a/src/tools/miri/tests/fail/shims/backtrace/bad-backtrace-decl.rs
+++ b/src/tools/miri/tests/fail/shims/backtrace/bad-backtrace-decl.rs
@@ -5,7 +5,7 @@
 
 fn main() {
     let frames = unsafe { miri_get_backtrace(0) };
-    for frame in frames.into_iter() {
+    for frame in frames.iter() {
         unsafe {
             miri_resolve_frame(*frame, 0); //~ ERROR: Undefined Behavior: bad declaration of miri_resolve_frame - should return a struct with 5 fields
         }
diff --git a/src/tools/miri/tests/pass/backtrace/backtrace-api-v0.rs b/src/tools/miri/tests/pass/backtrace/backtrace-api-v0.rs
index 8d3173d..3fff792 100644
--- a/src/tools/miri/tests/pass/backtrace/backtrace-api-v0.rs
+++ b/src/tools/miri/tests/pass/backtrace/backtrace-api-v0.rs
@@ -27,7 +27,7 @@
 fn main() {
     let mut seen_main = false;
     let frames = func_a();
-    for frame in frames.into_iter() {
+    for frame in frames.iter() {
         let miri_frame = unsafe { miri_resolve_frame(*frame, 0) };
         let name = String::from_utf8(miri_frame.name.into()).unwrap();
         let filename = String::from_utf8(miri_frame.filename.into()).unwrap();
diff --git a/src/tools/miri/tests/pass/backtrace/backtrace-api-v1.rs b/src/tools/miri/tests/pass/backtrace/backtrace-api-v1.rs
index ad05271..a3060ab 100644
--- a/src/tools/miri/tests/pass/backtrace/backtrace-api-v1.rs
+++ b/src/tools/miri/tests/pass/backtrace/backtrace-api-v1.rs
@@ -32,7 +32,7 @@
 fn main() {
     let mut seen_main = false;
     let frames = func_a();
-    for frame in frames.into_iter() {
+    for frame in frames.iter() {
         let miri_frame = unsafe { miri_resolve_frame(*frame, 1) };
 
         let mut name = vec![0; miri_frame.name_len];
diff --git a/tests/ui/iterators/into-iter-on-arrays-2018.stderr b/tests/ui/iterators/into-iter-on-arrays-2018.stderr
index 2378476..9d6bbf0 100644
--- a/tests/ui/iterators/into-iter-on-arrays-2018.stderr
+++ b/tests/ui/iterators/into-iter-on-arrays-2018.stderr
@@ -1,4 +1,4 @@
-warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to <[T; N] as IntoIterator>::into_iter in Rust 2021
+warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<[T; N] as IntoIterator>::into_iter` in Rust 2021
   --> $DIR/into-iter-on-arrays-2018.rs:14:34
    |
 LL |     let _: Iter<'_, i32> = array.into_iter();
@@ -16,7 +16,7 @@
 LL |     let _: Iter<'_, i32> = IntoIterator::into_iter(array);
    |                            ++++++++++++++++++++++++     ~
 
-warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to <[T; N] as IntoIterator>::into_iter in Rust 2021
+warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<[T; N] as IntoIterator>::into_iter` in Rust 2021
   --> $DIR/into-iter-on-arrays-2018.rs:18:44
    |
 LL |     let _: Iter<'_, i32> = Box::new(array).into_iter();
@@ -25,7 +25,7 @@
    = warning: this changes meaning in Rust 2021
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/IntoIterator-for-arrays.html>
 
-warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to <[T; N] as IntoIterator>::into_iter in Rust 2021
+warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<[T; N] as IntoIterator>::into_iter` in Rust 2021
   --> $DIR/into-iter-on-arrays-2018.rs:22:43
    |
 LL |     let _: Iter<'_, i32> = Rc::new(array).into_iter();
@@ -34,7 +34,7 @@
    = warning: this changes meaning in Rust 2021
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/IntoIterator-for-arrays.html>
 
-warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to <[T; N] as IntoIterator>::into_iter in Rust 2021
+warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<[T; N] as IntoIterator>::into_iter` in Rust 2021
   --> $DIR/into-iter-on-arrays-2018.rs:25:41
    |
 LL |     let _: Iter<'_, i32> = Array(array).into_iter();
@@ -43,7 +43,7 @@
    = warning: this changes meaning in Rust 2021
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/IntoIterator-for-arrays.html>
 
-warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to <[T; N] as IntoIterator>::into_iter in Rust 2021
+warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<[T; N] as IntoIterator>::into_iter` in Rust 2021
   --> $DIR/into-iter-on-arrays-2018.rs:32:24
    |
 LL |     for _ in [1, 2, 3].into_iter() {}
diff --git a/tests/ui/iterators/into-iter-on-arrays-lint.stderr b/tests/ui/iterators/into-iter-on-arrays-lint.stderr
index 2fde276..da388d6 100644
--- a/tests/ui/iterators/into-iter-on-arrays-lint.stderr
+++ b/tests/ui/iterators/into-iter-on-arrays-lint.stderr
@@ -1,4 +1,4 @@
-warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to <[T; N] as IntoIterator>::into_iter in Rust 2021
+warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<[T; N] as IntoIterator>::into_iter` in Rust 2021
   --> $DIR/into-iter-on-arrays-lint.rs:11:11
    |
 LL |     small.into_iter();
@@ -16,7 +16,7 @@
 LL |     IntoIterator::into_iter(small);
    |     ++++++++++++++++++++++++     ~
 
-warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to <[T; N] as IntoIterator>::into_iter in Rust 2021
+warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<[T; N] as IntoIterator>::into_iter` in Rust 2021
   --> $DIR/into-iter-on-arrays-lint.rs:14:12
    |
 LL |     [1, 2].into_iter();
@@ -33,7 +33,7 @@
 LL |     IntoIterator::into_iter([1, 2]);
    |     ++++++++++++++++++++++++      ~
 
-warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to <[T; N] as IntoIterator>::into_iter in Rust 2021
+warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<[T; N] as IntoIterator>::into_iter` in Rust 2021
   --> $DIR/into-iter-on-arrays-lint.rs:17:9
    |
 LL |     big.into_iter();
@@ -50,7 +50,7 @@
 LL |     IntoIterator::into_iter(big);
    |     ++++++++++++++++++++++++   ~
 
-warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to <[T; N] as IntoIterator>::into_iter in Rust 2021
+warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<[T; N] as IntoIterator>::into_iter` in Rust 2021
   --> $DIR/into-iter-on-arrays-lint.rs:20:15
    |
 LL |     [0u8; 33].into_iter();
@@ -67,7 +67,7 @@
 LL |     IntoIterator::into_iter([0u8; 33]);
    |     ++++++++++++++++++++++++         ~
 
-warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to <[T; N] as IntoIterator>::into_iter in Rust 2021
+warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<[T; N] as IntoIterator>::into_iter` in Rust 2021
   --> $DIR/into-iter-on-arrays-lint.rs:24:21
    |
 LL |     Box::new(small).into_iter();
@@ -76,7 +76,7 @@
    = warning: this changes meaning in Rust 2021
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/IntoIterator-for-arrays.html>
 
-warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to <[T; N] as IntoIterator>::into_iter in Rust 2021
+warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<[T; N] as IntoIterator>::into_iter` in Rust 2021
   --> $DIR/into-iter-on-arrays-lint.rs:27:22
    |
 LL |     Box::new([1, 2]).into_iter();
@@ -85,7 +85,7 @@
    = warning: this changes meaning in Rust 2021
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/IntoIterator-for-arrays.html>
 
-warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to <[T; N] as IntoIterator>::into_iter in Rust 2021
+warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<[T; N] as IntoIterator>::into_iter` in Rust 2021
   --> $DIR/into-iter-on-arrays-lint.rs:30:19
    |
 LL |     Box::new(big).into_iter();
@@ -94,7 +94,7 @@
    = warning: this changes meaning in Rust 2021
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/IntoIterator-for-arrays.html>
 
-warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to <[T; N] as IntoIterator>::into_iter in Rust 2021
+warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<[T; N] as IntoIterator>::into_iter` in Rust 2021
   --> $DIR/into-iter-on-arrays-lint.rs:33:25
    |
 LL |     Box::new([0u8; 33]).into_iter();
@@ -103,7 +103,7 @@
    = warning: this changes meaning in Rust 2021
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/IntoIterator-for-arrays.html>
 
-warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to <[T; N] as IntoIterator>::into_iter in Rust 2021
+warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<[T; N] as IntoIterator>::into_iter` in Rust 2021
   --> $DIR/into-iter-on-arrays-lint.rs:37:31
    |
 LL |     Box::new(Box::new(small)).into_iter();
@@ -112,7 +112,7 @@
    = warning: this changes meaning in Rust 2021
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/IntoIterator-for-arrays.html>
 
-warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to <[T; N] as IntoIterator>::into_iter in Rust 2021
+warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<[T; N] as IntoIterator>::into_iter` in Rust 2021
   --> $DIR/into-iter-on-arrays-lint.rs:40:32
    |
 LL |     Box::new(Box::new([1, 2])).into_iter();
@@ -121,7 +121,7 @@
    = warning: this changes meaning in Rust 2021
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/IntoIterator-for-arrays.html>
 
-warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to <[T; N] as IntoIterator>::into_iter in Rust 2021
+warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<[T; N] as IntoIterator>::into_iter` in Rust 2021
   --> $DIR/into-iter-on-arrays-lint.rs:43:29
    |
 LL |     Box::new(Box::new(big)).into_iter();
@@ -130,7 +130,7 @@
    = warning: this changes meaning in Rust 2021
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/IntoIterator-for-arrays.html>
 
-warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to <[T; N] as IntoIterator>::into_iter in Rust 2021
+warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<[T; N] as IntoIterator>::into_iter` in Rust 2021
   --> $DIR/into-iter-on-arrays-lint.rs:46:35
    |
 LL |     Box::new(Box::new([0u8; 33])).into_iter();
diff --git a/tests/ui/iterators/into-iter-on-boxed-slices-2021.rs b/tests/ui/iterators/into-iter-on-boxed-slices-2021.rs
new file mode 100644
index 0000000..3d9d131
--- /dev/null
+++ b/tests/ui/iterators/into-iter-on-boxed-slices-2021.rs
@@ -0,0 +1,46 @@
+//@ check-pass
+//@ edition:2018
+
+use std::ops::Deref;
+use std::rc::Rc;
+use std::slice::Iter;
+use std::vec::IntoIter;
+
+fn main() {
+    let boxed_slice = vec![0; 10].into_boxed_slice();
+
+    // Before 2024, the method dispatched to `IntoIterator for Box<[T]>`,
+    // which we continue to support for compatibility.
+    let _: Iter<'_, i32> = boxed_slice.into_iter();
+    //~^ WARNING this method call resolves to `<&Box<[T]> as IntoIterator>::into_iter`
+    //~| WARNING this changes meaning
+
+    let _: Iter<'_, i32> = Box::new(boxed_slice.clone()).into_iter();
+    //~^ WARNING this method call resolves to `<&Box<[T]> as IntoIterator>::into_iter`
+    //~| WARNING this changes meaning
+
+    let _: Iter<'_, i32> = Rc::new(boxed_slice.clone()).into_iter();
+    //~^ WARNING this method call resolves to `<&Box<[T]> as IntoIterator>::into_iter`
+    //~| WARNING this changes meaning
+    let _: Iter<'_, i32> = Array(boxed_slice.clone()).into_iter();
+    //~^ WARNING this method call resolves to `<&Box<[T]> as IntoIterator>::into_iter`
+    //~| WARNING this changes meaning
+
+    // But you can always use the trait method explicitly as an boxed_slice.
+    let _: IntoIter<i32> = IntoIterator::into_iter(boxed_slice);
+
+    for _ in (Box::new([1, 2, 3]) as Box<[_]>).into_iter() {}
+    //~^ WARNING this method call resolves to `<&Box<[T]> as IntoIterator>::into_iter`
+    //~| WARNING this changes meaning
+}
+
+/// User type that dereferences to a boxed slice.
+struct Array(Box<[i32]>);
+
+impl Deref for Array {
+    type Target = Box<[i32]>;
+
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
diff --git a/tests/ui/iterators/into-iter-on-boxed-slices-2021.stderr b/tests/ui/iterators/into-iter-on-boxed-slices-2021.stderr
new file mode 100644
index 0000000..d7f38a2
--- /dev/null
+++ b/tests/ui/iterators/into-iter-on-boxed-slices-2021.stderr
@@ -0,0 +1,60 @@
+warning: this method call resolves to `<&Box<[T]> as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<Box<[T]> as IntoIterator>::into_iter` in Rust 2024
+  --> $DIR/into-iter-on-boxed-slices-2021.rs:14:40
+   |
+LL |     let _: Iter<'_, i32> = boxed_slice.into_iter();
+   |                                        ^^^^^^^^^
+   |
+   = warning: this changes meaning in Rust 2024
+   = note: `#[warn(boxed_slice_into_iter)]` on by default
+help: use `.iter()` instead of `.into_iter()` to avoid ambiguity
+   |
+LL |     let _: Iter<'_, i32> = boxed_slice.iter();
+   |                                        ~~~~
+help: or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value
+   |
+LL |     let _: Iter<'_, i32> = IntoIterator::into_iter(boxed_slice);
+   |                            ++++++++++++++++++++++++           ~
+
+warning: this method call resolves to `<&Box<[T]> as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<Box<[T]> as IntoIterator>::into_iter` in Rust 2024
+  --> $DIR/into-iter-on-boxed-slices-2021.rs:18:58
+   |
+LL |     let _: Iter<'_, i32> = Box::new(boxed_slice.clone()).into_iter();
+   |                                                          ^^^^^^^^^ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity: `iter`
+   |
+   = warning: this changes meaning in Rust 2024
+
+warning: this method call resolves to `<&Box<[T]> as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<Box<[T]> as IntoIterator>::into_iter` in Rust 2024
+  --> $DIR/into-iter-on-boxed-slices-2021.rs:22:57
+   |
+LL |     let _: Iter<'_, i32> = Rc::new(boxed_slice.clone()).into_iter();
+   |                                                         ^^^^^^^^^ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity: `iter`
+   |
+   = warning: this changes meaning in Rust 2024
+
+warning: this method call resolves to `<&Box<[T]> as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<Box<[T]> as IntoIterator>::into_iter` in Rust 2024
+  --> $DIR/into-iter-on-boxed-slices-2021.rs:25:55
+   |
+LL |     let _: Iter<'_, i32> = Array(boxed_slice.clone()).into_iter();
+   |                                                       ^^^^^^^^^ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity: `iter`
+   |
+   = warning: this changes meaning in Rust 2024
+
+warning: this method call resolves to `<&Box<[T]> as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<Box<[T]> as IntoIterator>::into_iter` in Rust 2024
+  --> $DIR/into-iter-on-boxed-slices-2021.rs:32:48
+   |
+LL |     for _ in (Box::new([1, 2, 3]) as Box<[_]>).into_iter() {}
+   |                                                ^^^^^^^^^
+   |
+   = warning: this changes meaning in Rust 2024
+help: use `.iter()` instead of `.into_iter()` to avoid ambiguity
+   |
+LL |     for _ in (Box::new([1, 2, 3]) as Box<[_]>).iter() {}
+   |                                                ~~~~
+help: or remove `.into_iter()` to iterate by value
+   |
+LL -     for _ in (Box::new([1, 2, 3]) as Box<[_]>).into_iter() {}
+LL +     for _ in (Box::new([1, 2, 3]) as Box<[_]>) {}
+   |
+
+warning: 5 warnings emitted
+
diff --git a/tests/ui/iterators/into-iter-on-boxed-slices-2024.rs b/tests/ui/iterators/into-iter-on-boxed-slices-2024.rs
new file mode 100644
index 0000000..ffd6f02
--- /dev/null
+++ b/tests/ui/iterators/into-iter-on-boxed-slices-2024.rs
@@ -0,0 +1,20 @@
+//@ check-pass
+//@ edition:2024
+//@ compile-flags: -Zunstable-options
+
+use std::ops::Deref;
+use std::rc::Rc;
+use std::vec::IntoIter;
+
+fn main() {
+    let boxed_slice = vec![0; 10].into_boxed_slice();
+
+    // In 2021, the method dispatches to `IntoIterator for [T; N]`.
+    let _: IntoIter<i32> = boxed_slice.clone().into_iter();
+
+    // And through other boxes.
+    let _: IntoIter<i32> = Box::new(boxed_slice.clone()).into_iter();
+
+    // You can always use the trait method explicitly as a boxed_slice.
+    let _: IntoIter<i32> = IntoIterator::into_iter(boxed_slice.clone());
+}
diff --git a/tests/ui/iterators/into-iter-on-boxed-slices-lint.fixed b/tests/ui/iterators/into-iter-on-boxed-slices-lint.fixed
new file mode 100644
index 0000000..265a6c7
--- /dev/null
+++ b/tests/ui/iterators/into-iter-on-boxed-slices-lint.fixed
@@ -0,0 +1,30 @@
+//@ run-pass
+//@ run-rustfix
+//@ rustfix-only-machine-applicable
+
+#[allow(unused_must_use, unused_allocation)]
+fn main() {
+    let boxed = vec![1, 2].into_boxed_slice();
+
+    // Expressions that should trigger the lint
+    boxed.iter();
+    //~^ WARNING this method call resolves to `<&Box<[T]> as IntoIterator>::into_iter`
+    //~| WARNING this changes meaning
+    Box::new(boxed.clone()).iter();
+    //~^ WARNING this method call resolves to `<&Box<[T]> as IntoIterator>::into_iter`
+    //~| WARNING this changes meaning
+    Box::new(Box::new(boxed.clone())).iter();
+    //~^ WARNING this method call resolves to `<&Box<[T]> as IntoIterator>::into_iter`
+    //~| WARNING this changes meaning
+
+    // Expressions that should not
+    (&boxed).into_iter();
+
+    for _ in &boxed {}
+    (&boxed as &[_]).into_iter();
+    boxed[..].into_iter();
+    std::iter::IntoIterator::into_iter(&boxed);
+
+    #[allow(boxed_slice_into_iter)]
+    boxed.into_iter();
+}
diff --git a/tests/ui/iterators/into-iter-on-boxed-slices-lint.rs b/tests/ui/iterators/into-iter-on-boxed-slices-lint.rs
new file mode 100644
index 0000000..dd78e48
--- /dev/null
+++ b/tests/ui/iterators/into-iter-on-boxed-slices-lint.rs
@@ -0,0 +1,30 @@
+//@ run-pass
+//@ run-rustfix
+//@ rustfix-only-machine-applicable
+
+#[allow(unused_must_use, unused_allocation)]
+fn main() {
+    let boxed = vec![1, 2].into_boxed_slice();
+
+    // Expressions that should trigger the lint
+    boxed.into_iter();
+    //~^ WARNING this method call resolves to `<&Box<[T]> as IntoIterator>::into_iter`
+    //~| WARNING this changes meaning
+    Box::new(boxed.clone()).into_iter();
+    //~^ WARNING this method call resolves to `<&Box<[T]> as IntoIterator>::into_iter`
+    //~| WARNING this changes meaning
+    Box::new(Box::new(boxed.clone())).into_iter();
+    //~^ WARNING this method call resolves to `<&Box<[T]> as IntoIterator>::into_iter`
+    //~| WARNING this changes meaning
+
+    // Expressions that should not
+    (&boxed).into_iter();
+
+    for _ in &boxed {}
+    (&boxed as &[_]).into_iter();
+    boxed[..].into_iter();
+    std::iter::IntoIterator::into_iter(&boxed);
+
+    #[allow(boxed_slice_into_iter)]
+    boxed.into_iter();
+}
diff --git a/tests/ui/iterators/into-iter-on-boxed-slices-lint.stderr b/tests/ui/iterators/into-iter-on-boxed-slices-lint.stderr
new file mode 100644
index 0000000..b73faf0
--- /dev/null
+++ b/tests/ui/iterators/into-iter-on-boxed-slices-lint.stderr
@@ -0,0 +1,35 @@
+warning: this method call resolves to `<&Box<[T]> as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<Box<[T]> as IntoIterator>::into_iter` in Rust 2024
+  --> $DIR/into-iter-on-boxed-slices-lint.rs:10:11
+   |
+LL |     boxed.into_iter();
+   |           ^^^^^^^^^
+   |
+   = warning: this changes meaning in Rust 2024
+   = note: `#[warn(boxed_slice_into_iter)]` on by default
+help: use `.iter()` instead of `.into_iter()` to avoid ambiguity
+   |
+LL |     boxed.iter();
+   |           ~~~~
+help: or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value
+   |
+LL |     IntoIterator::into_iter(boxed);
+   |     ++++++++++++++++++++++++     ~
+
+warning: this method call resolves to `<&Box<[T]> as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<Box<[T]> as IntoIterator>::into_iter` in Rust 2024
+  --> $DIR/into-iter-on-boxed-slices-lint.rs:13:29
+   |
+LL |     Box::new(boxed.clone()).into_iter();
+   |                             ^^^^^^^^^ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity: `iter`
+   |
+   = warning: this changes meaning in Rust 2024
+
+warning: this method call resolves to `<&Box<[T]> as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<Box<[T]> as IntoIterator>::into_iter` in Rust 2024
+  --> $DIR/into-iter-on-boxed-slices-lint.rs:16:39
+   |
+LL |     Box::new(Box::new(boxed.clone())).into_iter();
+   |                                       ^^^^^^^^^ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity: `iter`
+   |
+   = warning: this changes meaning in Rust 2024
+
+warning: 3 warnings emitted
+
diff --git a/tests/ui/rust-2024/box-slice-into-iter-ambiguous.fixed b/tests/ui/rust-2024/box-slice-into-iter-ambiguous.fixed
new file mode 100644
index 0000000..d49fee5
--- /dev/null
+++ b/tests/ui/rust-2024/box-slice-into-iter-ambiguous.fixed
@@ -0,0 +1,27 @@
+// See https://github.com/rust-lang/rust/issues/88475
+//@ run-rustfix
+//@ edition:2021
+//@ check-pass
+#![warn(boxed_slice_into_iter)]
+#![allow(unused)]
+
+struct FooIter;
+
+trait MyIntoIter {
+    fn into_iter(self) -> FooIter;
+}
+
+impl<T> MyIntoIter for Box<[T]> {
+    fn into_iter(self) -> FooIter {
+        FooIter
+    }
+}
+
+struct Point;
+
+pub fn main() {
+    let points: Box<[_]> = vec![Point].into_boxed_slice();
+    let y = MyIntoIter::into_iter(points);
+    //~^ WARNING trait method `into_iter` will become ambiguous in Rust 2024
+    //~| WARNING this changes meaning in Rust 2024
+}
diff --git a/tests/ui/rust-2024/box-slice-into-iter-ambiguous.rs b/tests/ui/rust-2024/box-slice-into-iter-ambiguous.rs
new file mode 100644
index 0000000..e78f550
--- /dev/null
+++ b/tests/ui/rust-2024/box-slice-into-iter-ambiguous.rs
@@ -0,0 +1,27 @@
+// See https://github.com/rust-lang/rust/issues/88475
+//@ run-rustfix
+//@ edition:2021
+//@ check-pass
+#![warn(boxed_slice_into_iter)]
+#![allow(unused)]
+
+struct FooIter;
+
+trait MyIntoIter {
+    fn into_iter(self) -> FooIter;
+}
+
+impl<T> MyIntoIter for Box<[T]> {
+    fn into_iter(self) -> FooIter {
+        FooIter
+    }
+}
+
+struct Point;
+
+pub fn main() {
+    let points: Box<[_]> = vec![Point].into_boxed_slice();
+    let y = points.into_iter();
+    //~^ WARNING trait method `into_iter` will become ambiguous in Rust 2024
+    //~| WARNING this changes meaning in Rust 2024
+}
diff --git a/tests/ui/rust-2024/box-slice-into-iter-ambiguous.stderr b/tests/ui/rust-2024/box-slice-into-iter-ambiguous.stderr
new file mode 100644
index 0000000..9cc79a7
--- /dev/null
+++ b/tests/ui/rust-2024/box-slice-into-iter-ambiguous.stderr
@@ -0,0 +1,15 @@
+warning: trait method `into_iter` will become ambiguous in Rust 2024
+  --> $DIR/box-slice-into-iter-ambiguous.rs:24:13
+   |
+LL |     let y = points.into_iter();
+   |             ^^^^^^^^^^^^^^^^^^ help: disambiguate the associated function: `MyIntoIter::into_iter(points)`
+   |
+   = warning: this changes meaning in Rust 2024
+note: the lint level is defined here
+  --> $DIR/box-slice-into-iter-ambiguous.rs:5:9
+   |
+LL | #![warn(boxed_slice_into_iter)]
+   |         ^^^^^^^^^^^^^^^^^^^^^
+
+warning: 1 warning emitted
+
diff --git a/tests/ui/traits/method-on-unbounded-type-param.stderr b/tests/ui/traits/method-on-unbounded-type-param.stderr
index 4d968e7..0b1f83a 100644
--- a/tests/ui/traits/method-on-unbounded-type-param.stderr
+++ b/tests/ui/traits/method-on-unbounded-type-param.stderr
@@ -76,8 +76,8 @@
            which is required by `&mut dyn T: Iterator`
    = help: items from traits can only be used if the trait is implemented and in scope
    = note: the following traits define an item `cmp`, perhaps you need to implement one of them:
-           candidate #1: `Iterator`
-           candidate #2: `Ord`
+           candidate #1: `Ord`
+   = note: the trait `Iterator` defines an item `cmp`, but is explicitly unimplemented
 
 error: aborting due to 4 previous errors