about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--RELEASES.md1
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs3
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/mod.rs4
-rw-r--r--compiler/rustc_borrowck/src/type_check/mod.rs71
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs2
-rw-r--r--compiler/rustc_const_eval/src/interpret/call.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs3
-rw-r--r--compiler/rustc_hir_typeck/src/method/probe.rs3
-rw-r--r--compiler/rustc_lint/src/shadowed_into_iter.rs7
-rw-r--r--compiler/rustc_lint/src/types.rs6
-rw-r--r--compiler/rustc_lint/src/unused.rs5
-rw-r--r--compiler/rustc_middle/src/ty/layout.rs6
-rw-r--r--compiler/rustc_middle/src/ty/sty.rs15
-rw-r--r--compiler/rustc_middle/src/ty/util.rs2
-rw-r--r--compiler/rustc_mir_transform/src/coverage/spans.rs34
-rw-r--r--compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs22
-rw-r--r--compiler/rustc_mir_transform/src/coverage/unexpand.rs15
-rw-r--r--compiler/rustc_mir_transform/src/elaborate_box_derefs.rs12
-rw-r--r--compiler/rustc_monomorphize/src/collector.rs7
-rw-r--r--compiler/rustc_monomorphize/src/lib.rs2
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs3
-rw-r--r--library/std/src/ffi/os_str.rs2
-rw-r--r--library/std/src/path.rs2
-rw-r--r--src/bootstrap/src/core/build_steps/check.rs2
-rw-r--r--src/bootstrap/src/core/build_steps/clippy.rs2
-rw-r--r--src/bootstrap/src/core/build_steps/compile.rs5
-rw-r--r--src/bootstrap/src/core/build_steps/doc.rs2
-rw-r--r--src/bootstrap/src/core/build_steps/test.rs2
-rw-r--r--src/bootstrap/src/core/metadata.rs10
-rw-r--r--src/bootstrap/src/lib.rs19
-rw-r--r--src/tools/clippy/clippy_lints/src/escape.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/mod.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/utils.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs6
-rw-r--r--src/tools/clippy/clippy_utils/src/ty.rs4
-rw-r--r--tests/coverage/async.cov-map48
-rw-r--r--tests/coverage/async.coverage4
-rw-r--r--tests/coverage/await_ready.cov-map25
-rw-r--r--tests/coverage/await_ready.coverage38
-rw-r--r--tests/coverage/await_ready.rs37
-rw-r--r--tests/ui/implied-bounds/implied-bounds-on-nested-references-plus-variance-2.rs13
-rw-r--r--tests/ui/implied-bounds/implied-bounds-on-nested-references-plus-variance-early-bound.rs13
-rw-r--r--tests/ui/implied-bounds/implied-bounds-on-nested-references-plus-variance-early-bound.stderr10
-rw-r--r--tests/ui/implied-bounds/implied-bounds-on-nested-references-plus-variance-unnormalized.rs19
-rw-r--r--tests/ui/implied-bounds/implied-bounds-on-nested-references-plus-variance-unnormalized.stderr14
-rw-r--r--tests/ui/implied-bounds/implied-bounds-on-nested-references-plus-variance.rs7
-rw-r--r--tests/ui/implied-bounds/implied-bounds-on-nested-references-plus-variance.stderr10
47 files changed, 395 insertions, 138 deletions
diff --git a/RELEASES.md b/RELEASES.md
index 1b9bc62ba3a..b49470c3075 100644
--- a/RELEASES.md
+++ b/RELEASES.md
@@ -34,7 +34,6 @@ Compiler
   - [Add Tier 3 `std` Xtensa targets:](https://github.com/rust-lang/rust/pull/126380/) `xtensa-esp32-espidf`, `xtensa-esp32s2-espidf`, `xtensa-esp32s3-espidf`
   - [Add Tier 3 i686 Redox OS target:](https://github.com/rust-lang/rust/pull/126192/) `i686-unknown-redox`
   - [Promote `arm64ec-pc-windows-msvc` to Tier 2.](https://github.com/rust-lang/rust/pull/126039/)
-  - [Promote `wasm32-wasip2` to Tier 2.](https://github.com/rust-lang/rust/pull/126967/)
   - [Promote `loongarch64-unknown-linux-musl` to Tier 2 with host tools.](https://github.com/rust-lang/rust/pull/126298/)
   - [Enable full tools and profiler for LoongArch Linux targets.](https://github.com/rust-lang/rust/pull/127078/)
   - [Unconditionally warn on usage of `wasm32-wasi`.](https://github.com/rust-lang/rust/pull/126662/) (see compatibility note below)
diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
index 91b02a36d00..c720b0928fe 100644
--- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
@@ -662,9 +662,10 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
                                                 // `&dyn Trait`
                                                 ty::Ref(_, ty, _) if ty.is_trait() => true,
                                                 // `Box<dyn Trait>`
-                                                _ if ty.is_box() && ty.boxed_ty().is_trait() => {
+                                                _ if ty.boxed_ty().is_some_and(Ty::is_trait) => {
                                                     true
                                                 }
+
                                                 // `dyn Trait`
                                                 _ if ty.is_trait() => true,
                                                 // Anything else.
diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs
index 5ab66963409..878ce6162e0 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mod.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs
@@ -345,9 +345,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {
         variant_index: Option<VariantIdx>,
         including_tuple_field: IncludingTupleField,
     ) -> Option<String> {
-        if ty.is_box() {
+        if let Some(boxed_ty) = ty.boxed_ty() {
             // If the type is a box, the field is described from the boxed type
-            self.describe_field_from_ty(ty.boxed_ty(), field, variant_index, including_tuple_field)
+            self.describe_field_from_ty(boxed_ty, field, variant_index, including_tuple_field)
         } else {
             match *ty.kind() {
                 ty::Adt(def, _) => {
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index 224f8d5c893..3c9a43883ad 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -1979,19 +1979,76 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
 
                 match cast_kind {
                     CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer) => {
-                        let fn_sig = op.ty(body, tcx).fn_sig(tcx);
+                        let src_sig = op.ty(body, tcx).fn_sig(tcx);
+
+                        // HACK: This shouldn't be necessary... We can remove this when we actually
+                        // get binders with where clauses, then elaborate implied bounds into that
+                        // binder, and implement a higher-ranked subtyping algorithm that actually
+                        // respects these implied bounds.
+                        //
+                        // This protects against the case where we are casting from a higher-ranked
+                        // fn item to a non-higher-ranked fn pointer, where the cast throws away
+                        // implied bounds that would've needed to be checked at the call site. This
+                        // only works when we're casting to a non-higher-ranked fn ptr, since
+                        // placeholders in the target signature could have untracked implied
+                        // bounds, resulting in incorrect errors.
+                        //
+                        // We check that this signature is WF before subtyping the signature with
+                        // the target fn sig.
+                        if src_sig.has_bound_regions()
+                            && let ty::FnPtr(target_fn_tys, target_hdr) = *ty.kind()
+                            && let target_sig = target_fn_tys.with(target_hdr)
+                            && let Some(target_sig) = target_sig.no_bound_vars()
+                        {
+                            let src_sig = self.infcx.instantiate_binder_with_fresh_vars(
+                                span,
+                                BoundRegionConversionTime::HigherRankedType,
+                                src_sig,
+                            );
+                            let src_ty = Ty::new_fn_ptr(self.tcx(), ty::Binder::dummy(src_sig));
+                            self.prove_predicate(
+                                ty::ClauseKind::WellFormed(src_ty.into()),
+                                location.to_locations(),
+                                ConstraintCategory::Cast { unsize_to: None },
+                            );
+
+                            let src_ty = self.normalize(src_ty, location);
+                            if let Err(terr) = self.sub_types(
+                                src_ty,
+                                *ty,
+                                location.to_locations(),
+                                ConstraintCategory::Cast { unsize_to: None },
+                            ) {
+                                span_mirbug!(
+                                    self,
+                                    rvalue,
+                                    "equating {:?} with {:?} yields {:?}",
+                                    target_sig,
+                                    src_sig,
+                                    terr
+                                );
+                            };
+                        }
+
+                        let src_ty = Ty::new_fn_ptr(tcx, src_sig);
+                        // HACK: We want to assert that the signature of the source fn is
+                        // well-formed, because we don't enforce that via the WF of FnDef
+                        // types normally. This should be removed when we improve the tracking
+                        // of implied bounds of fn signatures.
+                        self.prove_predicate(
+                            ty::ClauseKind::WellFormed(src_ty.into()),
+                            location.to_locations(),
+                            ConstraintCategory::Cast { unsize_to: None },
+                        );
 
                         // The type that we see in the fcx is like
                         // `foo::<'a, 'b>`, where `foo` is the path to a
                         // function definition. When we extract the
                         // signature, it comes from the `fn_sig` query,
                         // and hence may contain unnormalized results.
-                        let fn_sig = self.normalize(fn_sig, location);
-
-                        let ty_fn_ptr_from = Ty::new_fn_ptr(tcx, fn_sig);
-
+                        let src_ty = self.normalize(src_ty, location);
                         if let Err(terr) = self.sub_types(
-                            ty_fn_ptr_from,
+                            src_ty,
                             *ty,
                             location.to_locations(),
                             ConstraintCategory::Cast { unsize_to: None },
@@ -2000,7 +2057,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                                 self,
                                 rvalue,
                                 "equating {:?} with {:?} yields {:?}",
-                                ty_fn_ptr_from,
+                                src_ty,
                                 ty,
                                 terr
                             );
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
index 17a9630c655..d231b103964 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
@@ -456,7 +456,7 @@ pub(crate) fn type_di_node<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) ->
             if def.is_box()
                 && args.get(1).map_or(true, |arg| cx.layout_of(arg.expect_ty()).is_1zst()) =>
         {
-            build_pointer_or_reference_di_node(cx, t, t.boxed_ty(), unique_type_id)
+            build_pointer_or_reference_di_node(cx, t, t.expect_boxed_ty(), unique_type_id)
         }
         ty::FnDef(..) | ty::FnPtr(..) => build_subroutine_type_di_node(cx, unique_type_id),
         ty::Closure(..) => build_closure_env_di_node(cx, unique_type_id),
diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs
index 82438eb5e78..568a9a3a637 100644
--- a/compiler/rustc_const_eval/src/interpret/call.rs
+++ b/compiler/rustc_const_eval/src/interpret/call.rs
@@ -189,7 +189,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
                 ty::Ref(_, ty, _) => *ty,
                 ty::RawPtr(ty, _) => *ty,
                 // We only accept `Box` with the default allocator.
-                _ if ty.is_box_global(*self.tcx) => ty.boxed_ty(),
+                _ if ty.is_box_global(*self.tcx) => ty.expect_boxed_ty(),
                 _ => return Ok(None),
             }))
         };
diff --git a/compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs b/compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs
index 0790c6f9a59..ac5e1040803 100644
--- a/compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs
+++ b/compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs
@@ -63,8 +63,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     // 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()
+                } else if self_ty.boxed_ty().is_some_and(Ty::is_slice)
                     && !span.at_least_rust_2024()
                 {
                     // In this case, it wasn't really a prelude addition that was the problem.
diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs
index 2fdba8446bd..c5ffcdd566a 100644
--- a/compiler/rustc_hir_typeck/src/method/probe.rs
+++ b/compiler/rustc_hir_typeck/src/method/probe.rs
@@ -1485,8 +1485,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
 
                         // 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()
+                        if self_ty.boxed_ty().is_some_and(Ty::is_slice)
                             && !method_name.span.at_least_rust_2024()
                         {
                             let trait_def = self.tcx.trait_def(poly_trait_ref.def_id());
diff --git a/compiler/rustc_lint/src/shadowed_into_iter.rs b/compiler/rustc_lint/src/shadowed_into_iter.rs
index bb9c7d85c2e..bb122509d0a 100644
--- a/compiler/rustc_lint/src/shadowed_into_iter.rs
+++ b/compiler/rustc_lint/src/shadowed_into_iter.rs
@@ -94,12 +94,9 @@ impl<'tcx> LateLintPass<'tcx> for ShadowedIntoIter {
         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)
+                pointee_ty.boxed_ty().is_some_and(Ty::is_slice)
             } else {
                 false
             }
@@ -119,7 +116,7 @@ impl<'tcx> LateLintPass<'tcx> for ShadowedIntoIter {
                     .iter()
                     .copied()
                     .take_while(|ty| !is_ref_to_boxed_slice(*ty))
-                    .position(|ty| is_boxed_slice(ty))
+                    .position(|ty| ty.boxed_ty().is_some_and(Ty::is_slice))
             {
                 (BOXED_SLICE_INTO_ITER, "Box<[T]>", "2024", idx == 0)
             } else {
diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs
index f2f7c0eaa4d..b5e501b92f0 100644
--- a/compiler/rustc_lint/src/types.rs
+++ b/compiler/rustc_lint/src/types.rs
@@ -1304,8 +1304,10 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
 
         match *ty.kind() {
             ty::Adt(def, args) => {
-                if def.is_box() && matches!(self.mode, CItemKind::Definition) {
-                    if ty.boxed_ty().is_sized(tcx, self.cx.param_env) {
+                if let Some(boxed) = ty.boxed_ty()
+                    && matches!(self.mode, CItemKind::Definition)
+                {
+                    if boxed.is_sized(tcx, self.cx.param_env) {
                         return FfiSafe;
                     } else {
                         return FfiUnsafe {
diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs
index 761d30bac71..b7f7b782c77 100644
--- a/compiler/rustc_lint/src/unused.rs
+++ b/compiler/rustc_lint/src/unused.rs
@@ -283,9 +283,8 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
             }
 
             match *ty.kind() {
-                ty::Adt(..) if ty.is_box() => {
-                    let boxed_ty = ty.boxed_ty();
-                    is_ty_must_use(cx, boxed_ty, expr, span)
+                ty::Adt(..) if let Some(boxed) = ty.boxed_ty() => {
+                    is_ty_must_use(cx, boxed, expr, span)
                         .map(|inner| MustUsePath::Boxed(Box::new(inner)))
                 }
                 ty::Adt(def, args) if cx.tcx.is_lang_item(def.did(), LangItem::Pin) => {
diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs
index d0a9039441d..8cec8eac189 100644
--- a/compiler/rustc_middle/src/ty/layout.rs
+++ b/compiler/rustc_middle/src/ty/layout.rs
@@ -1075,11 +1075,13 @@ where
                 // the raw pointer, so size and align are set to the boxed type, but `pointee.safe`
                 // will still be `None`.
                 if let Some(ref mut pointee) = result {
-                    if offset.bytes() == 0 && this.ty.is_box() {
+                    if offset.bytes() == 0
+                        && let Some(boxed_ty) = this.ty.boxed_ty()
+                    {
                         debug_assert!(pointee.safe.is_none());
                         let optimize = tcx.sess.opts.optimize != OptLevel::No;
                         pointee.safe = Some(PointerKind::Box {
-                            unpin: optimize && this.ty.boxed_ty().is_unpin(tcx, cx.param_env()),
+                            unpin: optimize && boxed_ty.is_unpin(tcx, cx.param_env()),
                             global: this.ty.is_box_global(tcx),
                         });
                     }
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index 89ef30fa768..1f4f2c62d70 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -1170,14 +1170,19 @@ impl<'tcx> Ty<'tcx> {
         }
     }
 
-    /// Panics if called on any type other than `Box<T>`.
-    pub fn boxed_ty(self) -> Ty<'tcx> {
+    pub fn boxed_ty(self) -> Option<Ty<'tcx>> {
         match self.kind() {
-            Adt(def, args) if def.is_box() => args.type_at(0),
-            _ => bug!("`boxed_ty` is called on non-box type {:?}", self),
+            Adt(def, args) if def.is_box() => Some(args.type_at(0)),
+            _ => None,
         }
     }
 
+    /// Panics if called on any type other than `Box<T>`.
+    pub fn expect_boxed_ty(self) -> Ty<'tcx> {
+        self.boxed_ty()
+            .unwrap_or_else(|| bug!("`expect_boxed_ty` is called on non-box type {:?}", self))
+    }
+
     /// A scalar type is one that denotes an atomic datum, with no sub-components.
     /// (A RawPtr is scalar because it represents a non-managed pointer, so its
     /// contents are abstract to rustc.)
@@ -1323,7 +1328,7 @@ impl<'tcx> Ty<'tcx> {
     /// Some types -- notably unsafe ptrs -- can only be dereferenced explicitly.
     pub fn builtin_deref(self, explicit: bool) -> Option<Ty<'tcx>> {
         match *self.kind() {
-            Adt(def, _) if def.is_box() => Some(self.boxed_ty()),
+            _ if let Some(boxed) = self.boxed_ty() => Some(boxed),
             Ref(_, ty, _) => Some(ty),
             RawPtr(ty, _) if explicit => Some(ty),
             _ => None,
diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs
index efbccca77c1..d70ff8258d0 100644
--- a/compiler/rustc_middle/src/ty/util.rs
+++ b/compiler/rustc_middle/src/ty/util.rs
@@ -1628,7 +1628,7 @@ impl<'tcx> ExplicitSelf<'tcx> {
             _ if is_self_ty(self_arg_ty) => ByValue,
             ty::Ref(region, ty, mutbl) if is_self_ty(ty) => ByReference(region, mutbl),
             ty::RawPtr(ty, mutbl) if is_self_ty(ty) => ByRawPointer(mutbl),
-            ty::Adt(def, _) if def.is_box() && is_self_ty(self_arg_ty.boxed_ty()) => ByBox,
+            _ if self_arg_ty.boxed_ty().is_some_and(is_self_ty) => ByBox,
             _ => Other,
         }
     }
diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs
index fcc774503f4..b904b0d2599 100644
--- a/compiler/rustc_mir_transform/src/coverage/spans.rs
+++ b/compiler/rustc_mir_transform/src/coverage/spans.rs
@@ -3,7 +3,7 @@ use std::collections::VecDeque;
 use rustc_data_structures::captures::Captures;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_middle::mir;
-use rustc_span::Span;
+use rustc_span::{DesugaringKind, ExpnKind, MacroKind, Span};
 use tracing::{debug, debug_span, instrument};
 
 use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph};
@@ -25,7 +25,7 @@ pub(super) fn extract_refined_covspans(
 
     // First, perform the passes that need macro information.
     covspans.sort_by(|a, b| basic_coverage_blocks.cmp_in_dominator_order(a.bcb, b.bcb));
-    remove_unwanted_macro_spans(&mut covspans);
+    remove_unwanted_expansion_spans(&mut covspans);
     split_visible_macro_spans(&mut covspans);
 
     // We no longer need the extra information in `SpanFromMir`, so convert to `Covspan`.
@@ -76,18 +76,24 @@ pub(super) fn extract_refined_covspans(
 /// invocation, which is unhelpful. Keeping only the first such span seems to
 /// give better mappings, so remove the others.
 ///
+/// Similarly, `await` expands to a branch on the discriminant of `Poll`, which
+/// leads to incorrect coverage if the `Future` is immediately ready (#98712).
+///
 /// (The input spans should be sorted in BCB dominator order, so that the
 /// retained "first" span is likely to dominate the others.)
-fn remove_unwanted_macro_spans(covspans: &mut Vec<SpanFromMir>) {
-    let mut seen_macro_spans = FxHashSet::default();
+fn remove_unwanted_expansion_spans(covspans: &mut Vec<SpanFromMir>) {
+    let mut deduplicated_spans = FxHashSet::default();
+
     covspans.retain(|covspan| {
-        // Ignore (retain) non-macro-expansion spans.
-        if covspan.visible_macro.is_none() {
-            return true;
+        match covspan.expn_kind {
+            // Retain only the first await-related or macro-expanded covspan with this span.
+            Some(ExpnKind::Desugaring(kind)) if kind == DesugaringKind::Await => {
+                deduplicated_spans.insert(covspan.span)
+            }
+            Some(ExpnKind::Macro(MacroKind::Bang, _)) => deduplicated_spans.insert(covspan.span),
+            // Ignore (retain) other spans.
+            _ => true,
         }
-
-        // Retain only the first macro-expanded covspan with this span.
-        seen_macro_spans.insert(covspan.span)
     });
 }
 
@@ -99,7 +105,9 @@ fn split_visible_macro_spans(covspans: &mut Vec<SpanFromMir>) {
     let mut extra_spans = vec![];
 
     covspans.retain(|covspan| {
-        let Some(visible_macro) = covspan.visible_macro else { return true };
+        let Some(ExpnKind::Macro(MacroKind::Bang, visible_macro)) = covspan.expn_kind else {
+            return true;
+        };
 
         let split_len = visible_macro.as_str().len() as u32 + 1;
         let (before, after) = covspan.span.split_at(split_len);
@@ -111,8 +119,8 @@ fn split_visible_macro_spans(covspans: &mut Vec<SpanFromMir>) {
             return true;
         }
 
-        extra_spans.push(SpanFromMir::new(before, covspan.visible_macro, covspan.bcb));
-        extra_spans.push(SpanFromMir::new(after, covspan.visible_macro, covspan.bcb));
+        extra_spans.push(SpanFromMir::new(before, covspan.expn_kind.clone(), covspan.bcb));
+        extra_spans.push(SpanFromMir::new(after, covspan.expn_kind.clone(), covspan.bcb));
         false // Discard the original covspan that we just split.
     });
 
diff --git a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs
index 32bd25bf4b9..7f5765c9462 100644
--- a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs
+++ b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs
@@ -3,13 +3,13 @@ use rustc_middle::mir::coverage::CoverageKind;
 use rustc_middle::mir::{
     self, FakeReadCause, Statement, StatementKind, Terminator, TerminatorKind,
 };
-use rustc_span::{Span, Symbol};
+use rustc_span::{ExpnKind, Span};
 
 use crate::coverage::graph::{
     BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph, START_BCB,
 };
 use crate::coverage::spans::Covspan;
-use crate::coverage::unexpand::unexpand_into_body_span_with_visible_macro;
+use crate::coverage::unexpand::unexpand_into_body_span_with_expn_kind;
 use crate::coverage::ExtractedHirInfo;
 
 pub(crate) struct ExtractedCovspans {
@@ -60,7 +60,7 @@ fn bcb_to_initial_coverage_spans<'a, 'tcx>(
         let data = &mir_body[bb];
 
         let unexpand = move |expn_span| {
-            unexpand_into_body_span_with_visible_macro(expn_span, body_span)
+            unexpand_into_body_span_with_expn_kind(expn_span, body_span)
                 // Discard any spans that fill the entire body, because they tend
                 // to represent compiler-inserted code, e.g. implicitly returning `()`.
                 .filter(|(span, _)| !span.source_equal(body_span))
@@ -68,9 +68,9 @@ fn bcb_to_initial_coverage_spans<'a, 'tcx>(
 
         let mut extract_statement_span = |statement| {
             let expn_span = filtered_statement_span(statement)?;
-            let (span, visible_macro) = unexpand(expn_span)?;
+            let (span, expn_kind) = unexpand(expn_span)?;
 
-            initial_covspans.push(SpanFromMir::new(span, visible_macro, bcb));
+            initial_covspans.push(SpanFromMir::new(span, expn_kind, bcb));
             Some(())
         };
         for statement in data.statements.iter() {
@@ -79,9 +79,9 @@ fn bcb_to_initial_coverage_spans<'a, 'tcx>(
 
         let mut extract_terminator_span = |terminator| {
             let expn_span = filtered_terminator_span(terminator)?;
-            let (span, visible_macro) = unexpand(expn_span)?;
+            let (span, expn_kind) = unexpand(expn_span)?;
 
-            initial_covspans.push(SpanFromMir::new(span, visible_macro, bcb));
+            initial_covspans.push(SpanFromMir::new(span, expn_kind, bcb));
             Some(())
         };
         extract_terminator_span(data.terminator());
@@ -214,7 +214,7 @@ pub(crate) struct SpanFromMir {
     /// With the exception of `fn_sig_span`, this should always be contained
     /// within `body_span`.
     pub(crate) span: Span,
-    pub(crate) visible_macro: Option<Symbol>,
+    pub(crate) expn_kind: Option<ExpnKind>,
     pub(crate) bcb: BasicCoverageBlock,
 }
 
@@ -223,12 +223,12 @@ impl SpanFromMir {
         Self::new(fn_sig_span, None, START_BCB)
     }
 
-    pub(crate) fn new(span: Span, visible_macro: Option<Symbol>, bcb: BasicCoverageBlock) -> Self {
-        Self { span, visible_macro, bcb }
+    pub(crate) fn new(span: Span, expn_kind: Option<ExpnKind>, bcb: BasicCoverageBlock) -> Self {
+        Self { span, expn_kind, bcb }
     }
 
     pub(crate) fn into_covspan(self) -> Covspan {
-        let Self { span, visible_macro: _, bcb } = self;
+        let Self { span, expn_kind: _, bcb } = self;
         Covspan { span, bcb }
     }
 }
diff --git a/compiler/rustc_mir_transform/src/coverage/unexpand.rs b/compiler/rustc_mir_transform/src/coverage/unexpand.rs
index 8cde291b907..cb861544736 100644
--- a/compiler/rustc_mir_transform/src/coverage/unexpand.rs
+++ b/compiler/rustc_mir_transform/src/coverage/unexpand.rs
@@ -1,4 +1,4 @@
-use rustc_span::{ExpnKind, MacroKind, Span, Symbol};
+use rustc_span::{ExpnKind, Span};
 
 /// Walks through the expansion ancestors of `original_span` to find a span that
 /// is contained in `body_span` and has the same [syntax context] as `body_span`.
@@ -13,20 +13,15 @@ pub(crate) fn unexpand_into_body_span(original_span: Span, body_span: Span) -> O
 ///
 /// If the returned span represents a bang-macro invocation (e.g. `foo!(..)`),
 /// the returned symbol will be the name of that macro (e.g. `foo`).
-pub(crate) fn unexpand_into_body_span_with_visible_macro(
+pub(crate) fn unexpand_into_body_span_with_expn_kind(
     original_span: Span,
     body_span: Span,
-) -> Option<(Span, Option<Symbol>)> {
+) -> Option<(Span, Option<ExpnKind>)> {
     let (span, prev) = unexpand_into_body_span_with_prev(original_span, body_span)?;
 
-    let visible_macro = prev
-        .map(|prev| match prev.ctxt().outer_expn_data().kind {
-            ExpnKind::Macro(MacroKind::Bang, name) => Some(name),
-            _ => None,
-        })
-        .flatten();
+    let expn_kind = prev.map(|prev| prev.ctxt().outer_expn_data().kind);
 
-    Some((span, visible_macro))
+    Some((span, expn_kind))
 }
 
 /// Walks through the expansion ancestors of `original_span` to find a span that
diff --git a/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs b/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs
index 5dd82f40163..66e49d556e2 100644
--- a/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs
+++ b/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs
@@ -62,11 +62,13 @@ impl<'tcx, 'a> MutVisitor<'tcx> for ElaborateBoxDerefVisitor<'tcx, 'a> {
         let base_ty = self.local_decls[place.local].ty;
 
         // Derefer ensures that derefs are always the first projection
-        if place.projection.first() == Some(&PlaceElem::Deref) && base_ty.is_box() {
+        if let Some(PlaceElem::Deref) = place.projection.first()
+            && let Some(boxed_ty) = base_ty.boxed_ty()
+        {
             let source_info = self.local_decls[place.local].source_info;
 
             let (unique_ty, nonnull_ty, ptr_ty) =
-                build_ptr_tys(tcx, base_ty.boxed_ty(), self.unique_did, self.nonnull_did);
+                build_ptr_tys(tcx, boxed_ty, self.unique_did, self.nonnull_did);
 
             let ptr_local = self.patch.new_temp(ptr_ty, source_info.span);
 
@@ -120,13 +122,15 @@ impl<'tcx> crate::MirPass<'tcx> for ElaborateBoxDerefs {
                     for (base, elem) in place.iter_projections() {
                         let base_ty = base.ty(&body.local_decls, tcx).ty;
 
-                        if elem == PlaceElem::Deref && base_ty.is_box() {
+                        if let PlaceElem::Deref = elem
+                            && let Some(boxed_ty) = base_ty.boxed_ty()
+                        {
                             // Clone the projections before us, since now we need to mutate them.
                             let new_projections =
                                 new_projections.get_or_insert_with(|| base.projection.to_vec());
 
                             let (unique_ty, nonnull_ty, ptr_ty) =
-                                build_ptr_tys(tcx, base_ty.boxed_ty(), unique_did, nonnull_did);
+                                build_ptr_tys(tcx, boxed_ty, unique_did, nonnull_did);
 
                             new_projections.extend_from_slice(&build_projection(
                                 unique_ty, nonnull_ty, ptr_ty,
diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs
index 8515ab45de2..093697a290c 100644
--- a/compiler/rustc_monomorphize/src/collector.rs
+++ b/compiler/rustc_monomorphize/src/collector.rs
@@ -1041,8 +1041,11 @@ fn find_vtable_types_for_unsizing<'tcx>(
     match (source_ty.kind(), target_ty.kind()) {
         (&ty::Ref(_, a, _), &ty::Ref(_, b, _) | &ty::RawPtr(b, _))
         | (&ty::RawPtr(a, _), &ty::RawPtr(b, _)) => ptr_vtable(a, b),
-        (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) if def_a.is_box() && def_b.is_box() => {
-            ptr_vtable(source_ty.boxed_ty(), target_ty.boxed_ty())
+        (_, _)
+            if let Some(source_boxed) = source_ty.boxed_ty()
+                && let Some(target_boxed) = target_ty.boxed_ty() =>
+        {
+            ptr_vtable(source_boxed, target_boxed)
         }
 
         // T as dyn* Trait
diff --git a/compiler/rustc_monomorphize/src/lib.rs b/compiler/rustc_monomorphize/src/lib.rs
index b22e8e30465..91101ddd590 100644
--- a/compiler/rustc_monomorphize/src/lib.rs
+++ b/compiler/rustc_monomorphize/src/lib.rs
@@ -1,5 +1,7 @@
 // tidy-alphabetical-start
 #![feature(array_windows)]
+#![feature(if_let_guard)]
+#![feature(let_chains)]
 #![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs
index bff2a184b19..65d21518491 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs
@@ -348,8 +348,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                     }
                 }
                 if let Some(ty::error::ExpectedFound { found, .. }) = exp_found
-                    && ty.is_box()
-                    && ty.boxed_ty() == found
+                    && ty.boxed_ty() == Some(found)
                     && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span)
                 {
                     err.span_suggestion(
diff --git a/library/std/src/ffi/os_str.rs b/library/std/src/ffi/os_str.rs
index 918eec2d0d8..99bea676e12 100644
--- a/library/std/src/ffi/os_str.rs
+++ b/library/std/src/ffi/os_str.rs
@@ -852,7 +852,7 @@ impl OsStr {
 
     /// Converts an `OsStr` to a <code>[Cow]<[str]></code>.
     ///
-    /// Any non-Unicode sequences are replaced with
+    /// Any non-UTF-8 sequences are replaced with
     /// [`U+FFFD REPLACEMENT CHARACTER`][U+FFFD].
     ///
     /// [U+FFFD]: crate::char::REPLACEMENT_CHARACTER
diff --git a/library/std/src/path.rs b/library/std/src/path.rs
index 9eaa0e01c2c..506ad445b6b 100644
--- a/library/std/src/path.rs
+++ b/library/std/src/path.rs
@@ -2200,7 +2200,7 @@ impl Path {
 
     /// Converts a `Path` to a [`Cow<str>`].
     ///
-    /// Any non-Unicode sequences are replaced with
+    /// Any non-UTF-8 sequences are replaced with
     /// [`U+FFFD REPLACEMENT CHARACTER`][U+FFFD].
     ///
     /// [U+FFFD]: super::char::REPLACEMENT_CHARACTER
diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs
index 7f7faf077d0..ba12e64c4a2 100644
--- a/src/bootstrap/src/core/build_steps/check.rs
+++ b/src/bootstrap/src/core/build_steps/check.rs
@@ -228,7 +228,7 @@ impl Step for Rustc {
             self.override_build_kind.unwrap_or(builder.kind),
         );
 
-        rustc_cargo(builder, &mut cargo, target, &compiler);
+        rustc_cargo(builder, &mut cargo, target, &compiler, &self.crates);
 
         // For ./x.py clippy, don't run with --all-targets because
         // linting tests and benchmarks can produce very noisy results
diff --git a/src/bootstrap/src/core/build_steps/clippy.rs b/src/bootstrap/src/core/build_steps/clippy.rs
index a2bb03cd5ac..a0992350722 100644
--- a/src/bootstrap/src/core/build_steps/clippy.rs
+++ b/src/bootstrap/src/core/build_steps/clippy.rs
@@ -197,7 +197,7 @@ impl Step for Rustc {
             Kind::Clippy,
         );
 
-        rustc_cargo(builder, &mut cargo, target, &compiler);
+        rustc_cargo(builder, &mut cargo, target, &compiler, &self.crates);
 
         // Explicitly pass -p for all compiler crates -- this will force cargo
         // to also lint the tests/benches/examples for these crates, rather
diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs
index 71e217bbbfc..de4fbebe4d3 100644
--- a/src/bootstrap/src/core/build_steps/compile.rs
+++ b/src/bootstrap/src/core/build_steps/compile.rs
@@ -988,7 +988,7 @@ impl Step for Rustc {
             Kind::Build,
         );
 
-        rustc_cargo(builder, &mut cargo, target, &compiler);
+        rustc_cargo(builder, &mut cargo, target, &compiler, &self.crates);
 
         // NB: all RUSTFLAGS should be added to `rustc_cargo()` so they will be
         // consistently applied by check/doc/test modes too.
@@ -1047,10 +1047,11 @@ pub fn rustc_cargo(
     cargo: &mut Cargo,
     target: TargetSelection,
     compiler: &Compiler,
+    crates: &[String],
 ) {
     cargo
         .arg("--features")
-        .arg(builder.rustc_features(builder.kind, target))
+        .arg(builder.rustc_features(builder.kind, target, crates))
         .arg("--manifest-path")
         .arg(builder.src.join("compiler/rustc/Cargo.toml"));
 
diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs
index ac68bbf8050..73d9e3f6793 100644
--- a/src/bootstrap/src/core/build_steps/doc.rs
+++ b/src/bootstrap/src/core/build_steps/doc.rs
@@ -826,7 +826,7 @@ impl Step for Rustc {
         // see https://github.com/rust-lang/rust/pull/122066#issuecomment-1983049222
         // cargo.rustdocflag("--generate-link-to-definition");
 
-        compile::rustc_cargo(builder, &mut cargo, target, &compiler);
+        compile::rustc_cargo(builder, &mut cargo, target, &compiler, &self.crates);
         cargo.arg("-Zskip-rustdoc-fingerprint");
 
         // Only include compiler crates, no dependencies of those, such as `libc`.
diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs
index 2d68ccd631b..83f65615c8d 100644
--- a/src/bootstrap/src/core/build_steps/test.rs
+++ b/src/bootstrap/src/core/build_steps/test.rs
@@ -2690,7 +2690,7 @@ impl Step for Crate {
                 }
             }
             Mode::Rustc => {
-                compile::rustc_cargo(builder, &mut cargo, target, &compiler);
+                compile::rustc_cargo(builder, &mut cargo, target, &compiler, &self.crates);
             }
             _ => panic!("can only test libraries"),
         };
diff --git a/src/bootstrap/src/core/metadata.rs b/src/bootstrap/src/core/metadata.rs
index 1016607fc83..d665544f593 100644
--- a/src/bootstrap/src/core/metadata.rs
+++ b/src/bootstrap/src/core/metadata.rs
@@ -1,3 +1,4 @@
+use std::collections::BTreeMap;
 use std::path::PathBuf;
 
 use serde_derive::Deserialize;
@@ -21,6 +22,7 @@ struct Package {
     manifest_path: String,
     dependencies: Vec<Dependency>,
     targets: Vec<Target>,
+    features: BTreeMap<String, Vec<String>>,
 }
 
 /// For more information, see the output of
@@ -51,7 +53,13 @@ pub fn build(build: &mut Build) {
                 .map(|dep| dep.name)
                 .collect();
             let has_lib = package.targets.iter().any(|t| t.kind.iter().any(|k| k == "lib"));
-            let krate = Crate { name: name.clone(), deps, path, has_lib };
+            let krate = Crate {
+                name: name.clone(),
+                deps,
+                path,
+                has_lib,
+                features: package.features.keys().cloned().collect(),
+            };
             let relative_path = krate.local_path(build);
             build.crates.insert(name.clone(), krate);
             let existing_path = build.crate_paths.insert(relative_path, name);
diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs
index 07befa983c5..fc12b359e0a 100644
--- a/src/bootstrap/src/lib.rs
+++ b/src/bootstrap/src/lib.rs
@@ -183,6 +183,7 @@ struct Crate {
     deps: HashSet<String>,
     path: PathBuf,
     has_lib: bool,
+    features: Vec<String>,
 }
 
 impl Crate {
@@ -672,16 +673,24 @@ impl Build {
     }
 
     /// Gets the space-separated set of activated features for the compiler.
-    fn rustc_features(&self, kind: Kind, target: TargetSelection) -> String {
+    fn rustc_features(&self, kind: Kind, target: TargetSelection, crates: &[String]) -> String {
+        let possible_features_by_crates: HashSet<_> = crates
+            .iter()
+            .flat_map(|krate| &self.crates[krate].features)
+            .map(std::ops::Deref::deref)
+            .collect();
+        let check = |feature: &str| -> bool {
+            crates.is_empty() || possible_features_by_crates.contains(feature)
+        };
         let mut features = vec![];
-        if self.config.jemalloc {
+        if self.config.jemalloc && check("jemalloc") {
             features.push("jemalloc");
         }
-        if self.config.llvm_enabled(target) || kind == Kind::Check {
+        if (self.config.llvm_enabled(target) || kind == Kind::Check) && check("llvm") {
             features.push("llvm");
         }
         // keep in sync with `bootstrap/compile.rs:rustc_cargo_env`
-        if self.config.rustc_parallel {
+        if self.config.rustc_parallel && check("rustc_use_parallel_compiler") {
             features.push("rustc_use_parallel_compiler");
         }
         if self.config.rust_randomize_layout {
@@ -693,7 +702,7 @@ impl Build {
         // which is everything (including debug/trace/etc.)
         // if its unset, if debug_assertions is on, then debug_logging will also be on
         // as well as tracing *ignoring* this feature when debug_assertions is on
-        if !self.config.rust_debug_logging {
+        if !self.config.rust_debug_logging && check("max_level_info") {
             features.push("max_level_info");
         }
 
diff --git a/src/tools/clippy/clippy_lints/src/escape.rs b/src/tools/clippy/clippy_lints/src/escape.rs
index a5da52b0be5..80360697941 100644
--- a/src/tools/clippy/clippy_lints/src/escape.rs
+++ b/src/tools/clippy/clippy_lints/src/escape.rs
@@ -50,7 +50,7 @@ declare_clippy_lint! {
 }
 
 fn is_non_trait_box(ty: Ty<'_>) -> bool {
-    ty.is_box() && !ty.boxed_ty().is_trait()
+    ty.boxed_ty().is_some_and(|boxed| !boxed.is_trait())
 }
 
 struct EscapeDelegate<'a, 'tcx> {
@@ -191,8 +191,8 @@ impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> {
 impl<'a, 'tcx> EscapeDelegate<'a, 'tcx> {
     fn is_large_box(&self, ty: Ty<'tcx>) -> bool {
         // Large types need to be boxed to avoid stack overflows.
-        if ty.is_box() {
-            self.cx.layout_of(ty.boxed_ty()).map_or(0, |l| l.size.bytes()) > self.too_large_for_stack
+        if let Some(boxed_ty) = ty.boxed_ty() {
+            self.cx.layout_of(boxed_ty).map_or(0, |l| l.size.bytes()) > self.too_large_for_stack
         } else {
             false
         }
diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs
index d7126990edb..f61bb3a6bf4 100644
--- a/src/tools/clippy/clippy_lints/src/methods/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs
@@ -5187,8 +5187,8 @@ impl SelfKind {
         fn matches_value<'a>(cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool {
             if ty == parent_ty {
                 true
-            } else if ty.is_box() {
-                ty.boxed_ty() == parent_ty
+            } else if let Some(boxed_ty) = ty.boxed_ty() {
+                boxed_ty == parent_ty
             } else if is_type_diagnostic_item(cx, ty, sym::Rc) || is_type_diagnostic_item(cx, ty, sym::Arc) {
                 if let ty::Adt(_, args) = ty.kind() {
                     args.types().next().map_or(false, |t| t == parent_ty)
diff --git a/src/tools/clippy/clippy_lints/src/methods/utils.rs b/src/tools/clippy/clippy_lints/src/methods/utils.rs
index 0d2b0a31317..fe860e5ae26 100644
--- a/src/tools/clippy/clippy_lints/src/methods/utils.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/utils.rs
@@ -16,7 +16,7 @@ pub(super) fn derefs_to_slice<'tcx>(
     fn may_slice<'a>(cx: &LateContext<'a>, ty: Ty<'a>) -> bool {
         match ty.kind() {
             ty::Slice(_) => true,
-            ty::Adt(def, _) if def.is_box() => may_slice(cx, ty.boxed_ty()),
+            ty::Adt(..) if let Some(boxed) = ty.boxed_ty() => may_slice(cx, boxed),
             ty::Adt(..) => is_type_diagnostic_item(cx, ty, sym::Vec),
             ty::Array(_, size) => size.try_eval_target_usize(cx.tcx, cx.param_env).is_some(),
             ty::Ref(_, inner, _) => may_slice(cx, *inner),
@@ -33,7 +33,7 @@ pub(super) fn derefs_to_slice<'tcx>(
     } else {
         match ty.kind() {
             ty::Slice(_) => Some(expr),
-            ty::Adt(def, _) if def.is_box() && may_slice(cx, ty.boxed_ty()) => Some(expr),
+            _ if ty.boxed_ty().is_some_and(|boxed| may_slice(cx, boxed)) => Some(expr),
             ty::Ref(_, inner, _) => {
                 if may_slice(cx, *inner) {
                     Some(expr)
diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs b/src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs
index 3f130bf5a67..14f4aa6676b 100644
--- a/src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs
+++ b/src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs
@@ -75,11 +75,9 @@ impl UnnecessaryBoxReturns {
             .instantiate_bound_regions_with_erased(cx.tcx.fn_sig(def_id).skip_binder())
             .output();
 
-        if !return_ty.is_box() {
+        let Some(boxed_ty) = return_ty.boxed_ty() else {
             return;
-        }
-
-        let boxed_ty = return_ty.boxed_ty();
+        };
 
         // It's sometimes useful to return Box<T> if T is unsized, so don't lint those.
         // Also, don't lint if we know that T is very large, in which case returning
diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs
index f80981c11af..585134209ca 100644
--- a/src/tools/clippy/clippy_utils/src/ty.rs
+++ b/src/tools/clippy/clippy_utils/src/ty.rs
@@ -704,8 +704,8 @@ pub fn expr_sig<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option<ExprFnS
 
 /// If the type is function like, get the signature for it.
 pub fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<ExprFnSig<'tcx>> {
-    if ty.is_box() {
-        return ty_sig(cx, ty.boxed_ty());
+    if let Some(boxed_ty) = ty.boxed_ty() {
+        return ty_sig(cx, boxed_ty);
     }
     match *ty.kind() {
         ty::Closure(id, subs) => {
diff --git a/tests/coverage/async.cov-map b/tests/coverage/async.cov-map
index 9e5a4bdc60f..1ba165f1e49 100644
--- a/tests/coverage/async.cov-map
+++ b/tests/coverage/async.cov-map
@@ -92,20 +92,18 @@ Number of file 0 mappings: 1
 - Code(Counter(0)) at (prev + 25, 1) to (start + 0, 23)
 
 Function name: async::g::{closure#0} (unused)
-Raw bytes (69): 0x[01, 01, 00, 0d, 00, 19, 17, 01, 0c, 00, 02, 09, 00, 0a, 00, 00, 0e, 00, 11, 00, 00, 12, 00, 17, 00, 00, 1b, 00, 1c, 00, 00, 20, 00, 22, 00, 01, 09, 00, 0a, 00, 00, 0e, 00, 11, 00, 00, 12, 00, 17, 00, 00, 1b, 00, 1c, 00, 00, 20, 00, 22, 00, 01, 0e, 00, 10, 00, 02, 01, 00, 02]
+Raw bytes (59): 0x[01, 01, 00, 0b, 00, 19, 17, 01, 0c, 00, 02, 09, 00, 0a, 00, 00, 0e, 00, 17, 00, 00, 1b, 00, 1c, 00, 00, 20, 00, 22, 00, 01, 09, 00, 0a, 00, 00, 0e, 00, 17, 00, 00, 1b, 00, 1c, 00, 00, 20, 00, 22, 00, 01, 0e, 00, 10, 00, 02, 01, 00, 02]
 Number of files: 1
 - file 0 => global file 1
 Number of expressions: 0
-Number of file 0 mappings: 13
+Number of file 0 mappings: 11
 - Code(Zero) at (prev + 25, 23) to (start + 1, 12)
 - Code(Zero) at (prev + 2, 9) to (start + 0, 10)
-- Code(Zero) at (prev + 0, 14) to (start + 0, 17)
-- Code(Zero) at (prev + 0, 18) to (start + 0, 23)
+- Code(Zero) at (prev + 0, 14) to (start + 0, 23)
 - Code(Zero) at (prev + 0, 27) to (start + 0, 28)
 - Code(Zero) at (prev + 0, 32) to (start + 0, 34)
 - Code(Zero) at (prev + 1, 9) to (start + 0, 10)
-- Code(Zero) at (prev + 0, 14) to (start + 0, 17)
-- Code(Zero) at (prev + 0, 18) to (start + 0, 23)
+- Code(Zero) at (prev + 0, 14) to (start + 0, 23)
 - Code(Zero) at (prev + 0, 27) to (start + 0, 28)
 - Code(Zero) at (prev + 0, 32) to (start + 0, 34)
 - Code(Zero) at (prev + 1, 14) to (start + 0, 16)
@@ -120,15 +118,14 @@ Number of file 0 mappings: 1
 - Code(Counter(0)) at (prev + 33, 1) to (start + 0, 22)
 
 Function name: async::h::{closure#0} (unused)
-Raw bytes (44): 0x[01, 01, 00, 08, 00, 21, 16, 03, 0c, 00, 04, 09, 00, 0a, 00, 00, 0e, 00, 13, 00, 00, 14, 00, 19, 00, 00, 1a, 00, 1b, 00, 00, 20, 00, 22, 00, 01, 0e, 00, 10, 00, 02, 01, 00, 02]
+Raw bytes (39): 0x[01, 01, 00, 07, 00, 21, 16, 03, 0c, 00, 04, 09, 00, 0a, 00, 00, 0e, 00, 19, 00, 00, 1a, 00, 1b, 00, 00, 20, 00, 22, 00, 01, 0e, 00, 10, 00, 02, 01, 00, 02]
 Number of files: 1
 - file 0 => global file 1
 Number of expressions: 0
-Number of file 0 mappings: 8
+Number of file 0 mappings: 7
 - Code(Zero) at (prev + 33, 22) to (start + 3, 12)
 - Code(Zero) at (prev + 4, 9) to (start + 0, 10)
-- Code(Zero) at (prev + 0, 14) to (start + 0, 19)
-- Code(Zero) at (prev + 0, 20) to (start + 0, 25)
+- Code(Zero) at (prev + 0, 14) to (start + 0, 25)
 - Code(Zero) at (prev + 0, 26) to (start + 0, 27)
 - Code(Zero) at (prev + 0, 32) to (start + 0, 34)
 - Code(Zero) at (prev + 1, 14) to (start + 0, 16)
@@ -143,28 +140,25 @@ Number of file 0 mappings: 1
 - Code(Counter(0)) at (prev + 42, 1) to (start + 0, 19)
 
 Function name: async::i::{closure#0}
-Raw bytes (78): 0x[01, 01, 02, 07, 21, 19, 1d, 0e, 01, 2a, 13, 04, 0c, 0d, 05, 09, 00, 0a, 01, 00, 0e, 00, 12, 05, 00, 13, 00, 18, 09, 00, 1c, 00, 21, 0d, 00, 27, 00, 2a, 15, 00, 2b, 00, 30, 1d, 01, 09, 00, 0a, 11, 00, 0e, 00, 11, 25, 00, 12, 00, 17, 29, 00, 1b, 00, 20, 1d, 00, 24, 00, 26, 21, 01, 0e, 00, 10, 03, 02, 01, 00, 02]
+Raw bytes (63): 0x[01, 01, 02, 07, 19, 11, 15, 0b, 01, 2a, 13, 04, 0c, 09, 05, 09, 00, 0a, 01, 00, 0e, 00, 18, 05, 00, 1c, 00, 21, 09, 00, 27, 00, 30, 15, 01, 09, 00, 0a, 0d, 00, 0e, 00, 17, 1d, 00, 1b, 00, 20, 15, 00, 24, 00, 26, 19, 01, 0e, 00, 10, 03, 02, 01, 00, 02]
 Number of files: 1
 - file 0 => global file 1
 Number of expressions: 2
-- expression 0 operands: lhs = Expression(1, Add), rhs = Counter(8)
-- expression 1 operands: lhs = Counter(6), rhs = Counter(7)
-Number of file 0 mappings: 14
+- expression 0 operands: lhs = Expression(1, Add), rhs = Counter(6)
+- expression 1 operands: lhs = Counter(4), rhs = Counter(5)
+Number of file 0 mappings: 11
 - Code(Counter(0)) at (prev + 42, 19) to (start + 4, 12)
-- Code(Counter(3)) at (prev + 5, 9) to (start + 0, 10)
-- Code(Counter(0)) at (prev + 0, 14) to (start + 0, 18)
-- Code(Counter(1)) at (prev + 0, 19) to (start + 0, 24)
-- Code(Counter(2)) at (prev + 0, 28) to (start + 0, 33)
-- Code(Counter(3)) at (prev + 0, 39) to (start + 0, 42)
-- Code(Counter(5)) at (prev + 0, 43) to (start + 0, 48)
-- Code(Counter(7)) at (prev + 1, 9) to (start + 0, 10)
-- Code(Counter(4)) at (prev + 0, 14) to (start + 0, 17)
-- Code(Counter(9)) at (prev + 0, 18) to (start + 0, 23)
-- Code(Counter(10)) at (prev + 0, 27) to (start + 0, 32)
-- Code(Counter(7)) at (prev + 0, 36) to (start + 0, 38)
-- Code(Counter(8)) at (prev + 1, 14) to (start + 0, 16)
+- Code(Counter(2)) at (prev + 5, 9) to (start + 0, 10)
+- Code(Counter(0)) at (prev + 0, 14) to (start + 0, 24)
+- Code(Counter(1)) at (prev + 0, 28) to (start + 0, 33)
+- Code(Counter(2)) at (prev + 0, 39) to (start + 0, 48)
+- Code(Counter(5)) at (prev + 1, 9) to (start + 0, 10)
+- Code(Counter(3)) at (prev + 0, 14) to (start + 0, 23)
+- Code(Counter(7)) at (prev + 0, 27) to (start + 0, 32)
+- Code(Counter(5)) at (prev + 0, 36) to (start + 0, 38)
+- Code(Counter(6)) at (prev + 1, 14) to (start + 0, 16)
 - Code(Expression(0, Add)) at (prev + 2, 1) to (start + 0, 2)
-    = ((c6 + c7) + c8)
+    = ((c4 + c5) + c6)
 
 Function name: async::j
 Raw bytes (58): 0x[01, 01, 02, 07, 0d, 05, 09, 0a, 01, 35, 01, 00, 0d, 01, 0b, 0b, 00, 0c, 05, 01, 09, 00, 0a, 01, 00, 0e, 00, 1b, 05, 00, 1f, 00, 27, 09, 01, 09, 00, 0a, 11, 00, 0e, 00, 1a, 09, 00, 1e, 00, 20, 0d, 01, 0e, 00, 10, 03, 02, 01, 00, 02]
diff --git a/tests/coverage/async.coverage b/tests/coverage/async.coverage
index f5473829b02..995674257c4 100644
--- a/tests/coverage/async.coverage
+++ b/tests/coverage/async.coverage
@@ -45,9 +45,9 @@
    LL|      1|                    // executed asynchronously.
    LL|      1|    match x {
    LL|      1|        y if c(x).await == y + 1 => { d().await; }
-                      ^0        ^0                  ^0  ^0
+                      ^0                            ^0
    LL|      1|        y if f().await == y + 1 => (),
-                      ^0       ^0                ^0
+                      ^0                         ^0
    LL|      1|        _ => (),
    LL|       |    }
    LL|      1|}
diff --git a/tests/coverage/await_ready.cov-map b/tests/coverage/await_ready.cov-map
new file mode 100644
index 00000000000..0c9f2ae29a8
--- /dev/null
+++ b/tests/coverage/await_ready.cov-map
@@ -0,0 +1,25 @@
+Function name: await_ready::await_ready
+Raw bytes (9): 0x[01, 01, 00, 01, 01, 0a, 01, 00, 1e]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 0
+Number of file 0 mappings: 1
+- Code(Counter(0)) at (prev + 10, 1) to (start + 0, 30)
+
+Function name: await_ready::await_ready::{closure#0}
+Raw bytes (14): 0x[01, 01, 00, 02, 01, 0a, 1e, 03, 0f, 05, 04, 01, 00, 02]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 0
+Number of file 0 mappings: 2
+- Code(Counter(0)) at (prev + 10, 30) to (start + 3, 15)
+- Code(Counter(1)) at (prev + 4, 1) to (start + 0, 2)
+
+Function name: await_ready::main
+Raw bytes (9): 0x[01, 01, 00, 01, 01, 10, 01, 03, 02]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 0
+Number of file 0 mappings: 1
+- Code(Counter(0)) at (prev + 16, 1) to (start + 3, 2)
+
diff --git a/tests/coverage/await_ready.coverage b/tests/coverage/await_ready.coverage
new file mode 100644
index 00000000000..0075f09426e
--- /dev/null
+++ b/tests/coverage/await_ready.coverage
@@ -0,0 +1,38 @@
+   LL|       |#![feature(coverage_attribute)]
+   LL|       |#![feature(custom_inner_attributes)] // for #![rustfmt::skip]
+   LL|       |#![feature(noop_waker)]
+   LL|       |#![rustfmt::skip]
+   LL|       |//@ edition: 2021
+   LL|       |
+   LL|       |#[coverage(off)]
+   LL|       |async fn ready() -> u8 { 1 }
+   LL|       |
+   LL|      1|async fn await_ready() -> u8 {
+   LL|      1|    // await should be covered even if the function never yields
+   LL|      1|    ready()
+   LL|      1|        .await
+   LL|      1|}
+   LL|       |
+   LL|      1|fn main() {
+   LL|      1|    let mut future = Box::pin(await_ready());
+   LL|      1|    executor::block_on(future.as_mut());
+   LL|      1|}
+   LL|       |
+   LL|       |mod executor {
+   LL|       |    use core::future::Future;
+   LL|       |    use core::pin::pin;
+   LL|       |    use core::task::{Context, Poll, Waker};
+   LL|       |
+   LL|       |    #[coverage(off)]
+   LL|       |    pub fn block_on<F: Future>(mut future: F) -> F::Output {
+   LL|       |        let mut future = pin!(future);
+   LL|       |        let mut context = Context::from_waker(Waker::noop());
+   LL|       |
+   LL|       |        loop {
+   LL|       |            if let Poll::Ready(val) = future.as_mut().poll(&mut context) {
+   LL|       |                break val;
+   LL|       |            }
+   LL|       |        }
+   LL|       |    }
+   LL|       |}
+
diff --git a/tests/coverage/await_ready.rs b/tests/coverage/await_ready.rs
new file mode 100644
index 00000000000..9212a4ba705
--- /dev/null
+++ b/tests/coverage/await_ready.rs
@@ -0,0 +1,37 @@
+#![feature(coverage_attribute)]
+#![feature(custom_inner_attributes)] // for #![rustfmt::skip]
+#![feature(noop_waker)]
+#![rustfmt::skip]
+//@ edition: 2021
+
+#[coverage(off)]
+async fn ready() -> u8 { 1 }
+
+async fn await_ready() -> u8 {
+    // await should be covered even if the function never yields
+    ready()
+        .await
+}
+
+fn main() {
+    let mut future = Box::pin(await_ready());
+    executor::block_on(future.as_mut());
+}
+
+mod executor {
+    use core::future::Future;
+    use core::pin::pin;
+    use core::task::{Context, Poll, Waker};
+
+    #[coverage(off)]
+    pub fn block_on<F: Future>(mut future: F) -> F::Output {
+        let mut future = pin!(future);
+        let mut context = Context::from_waker(Waker::noop());
+
+        loop {
+            if let Poll::Ready(val) = future.as_mut().poll(&mut context) {
+                break val;
+            }
+        }
+    }
+}
diff --git a/tests/ui/implied-bounds/implied-bounds-on-nested-references-plus-variance-2.rs b/tests/ui/implied-bounds/implied-bounds-on-nested-references-plus-variance-2.rs
new file mode 100644
index 00000000000..ca8b8b7e4d9
--- /dev/null
+++ b/tests/ui/implied-bounds/implied-bounds-on-nested-references-plus-variance-2.rs
@@ -0,0 +1,13 @@
+//@ check-pass
+//@ known-bug: #25860
+
+static UNIT: &'static &'static () = &&();
+
+fn foo<'a, 'b, T>(_: &'a &'b (), v: &'b T, _: &()) -> &'a T { v }
+
+fn bad<'a, T>(x: &'a T) -> &'static T {
+    let f: fn(_, &'a T, &()) -> &'static T = foo;
+    f(UNIT, x, &())
+}
+
+fn main() {}
diff --git a/tests/ui/implied-bounds/implied-bounds-on-nested-references-plus-variance-early-bound.rs b/tests/ui/implied-bounds/implied-bounds-on-nested-references-plus-variance-early-bound.rs
new file mode 100644
index 00000000000..226a6fa3016
--- /dev/null
+++ b/tests/ui/implied-bounds/implied-bounds-on-nested-references-plus-variance-early-bound.rs
@@ -0,0 +1,13 @@
+// Regression test for #129021.
+
+static UNIT: &'static &'static () = &&();
+
+fn foo<'a: 'a, 'b: 'b, T>(_: &'a &'b (), v: &'b T) -> &'a T { v }
+
+fn bad<'a, T>(x: &'a T) -> &'static T {
+    let f: fn(_, &'a T) -> &'static T = foo;
+    //~^ ERROR lifetime may not live long enough
+    f(UNIT, x)
+}
+
+fn main() {}
diff --git a/tests/ui/implied-bounds/implied-bounds-on-nested-references-plus-variance-early-bound.stderr b/tests/ui/implied-bounds/implied-bounds-on-nested-references-plus-variance-early-bound.stderr
new file mode 100644
index 00000000000..84d2a6d2b6a
--- /dev/null
+++ b/tests/ui/implied-bounds/implied-bounds-on-nested-references-plus-variance-early-bound.stderr
@@ -0,0 +1,10 @@
+error: lifetime may not live long enough
+  --> $DIR/implied-bounds-on-nested-references-plus-variance-early-bound.rs:8:12
+   |
+LL | fn bad<'a, T>(x: &'a T) -> &'static T {
+   |        -- lifetime `'a` defined here
+LL |     let f: fn(_, &'a T) -> &'static T = foo;
+   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static`
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/implied-bounds/implied-bounds-on-nested-references-plus-variance-unnormalized.rs b/tests/ui/implied-bounds/implied-bounds-on-nested-references-plus-variance-unnormalized.rs
new file mode 100644
index 00000000000..f3068990189
--- /dev/null
+++ b/tests/ui/implied-bounds/implied-bounds-on-nested-references-plus-variance-unnormalized.rs
@@ -0,0 +1,19 @@
+// Regression test for #129021.
+
+trait ToArg<T> {
+    type Arg;
+}
+impl<T, U> ToArg<T> for U {
+    type Arg = T;
+}
+
+fn extend_inner<'a, 'b>(x: &'a str) -> <&'b &'a () as ToArg<&'b str>>::Arg { x }
+fn extend<'a, 'b>(x: &'a str) -> &'b str {
+    (extend_inner as fn(_) -> _)(x)
+    //~^ ERROR lifetime may not live long enough
+}
+
+fn main() {
+    let y = extend(&String::from("Hello World"));
+    println!("{}", y);
+}
diff --git a/tests/ui/implied-bounds/implied-bounds-on-nested-references-plus-variance-unnormalized.stderr b/tests/ui/implied-bounds/implied-bounds-on-nested-references-plus-variance-unnormalized.stderr
new file mode 100644
index 00000000000..4cdb959786a
--- /dev/null
+++ b/tests/ui/implied-bounds/implied-bounds-on-nested-references-plus-variance-unnormalized.stderr
@@ -0,0 +1,14 @@
+error: lifetime may not live long enough
+  --> $DIR/implied-bounds-on-nested-references-plus-variance-unnormalized.rs:12:5
+   |
+LL | fn extend<'a, 'b>(x: &'a str) -> &'b str {
+   |           --  -- lifetime `'b` defined here
+   |           |
+   |           lifetime `'a` defined here
+LL |     (extend_inner as fn(_) -> _)(x)
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a`
+   |
+   = help: consider adding the following bound: `'a: 'b`
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/implied-bounds/implied-bounds-on-nested-references-plus-variance.rs b/tests/ui/implied-bounds/implied-bounds-on-nested-references-plus-variance.rs
index f3401f34eec..6de81cba728 100644
--- a/tests/ui/implied-bounds/implied-bounds-on-nested-references-plus-variance.rs
+++ b/tests/ui/implied-bounds/implied-bounds-on-nested-references-plus-variance.rs
@@ -1,8 +1,4 @@
-//@ check-pass
-//@ known-bug: #25860
-
-// Should fail. The combination of variance and implied bounds for nested
-// references allows us to infer a longer lifetime than we can prove.
+// Regression test for #129021.
 
 static UNIT: &'static &'static () = &&();
 
@@ -10,6 +6,7 @@ fn foo<'a, 'b, T>(_: &'a &'b (), v: &'b T) -> &'a T { v }
 
 fn bad<'a, T>(x: &'a T) -> &'static T {
     let f: fn(_, &'a T) -> &'static T = foo;
+    //~^ ERROR lifetime may not live long enough
     f(UNIT, x)
 }
 
diff --git a/tests/ui/implied-bounds/implied-bounds-on-nested-references-plus-variance.stderr b/tests/ui/implied-bounds/implied-bounds-on-nested-references-plus-variance.stderr
new file mode 100644
index 00000000000..c96fa92937b
--- /dev/null
+++ b/tests/ui/implied-bounds/implied-bounds-on-nested-references-plus-variance.stderr
@@ -0,0 +1,10 @@
+error: lifetime may not live long enough
+  --> $DIR/implied-bounds-on-nested-references-plus-variance.rs:8:12
+   |
+LL | fn bad<'a, T>(x: &'a T) -> &'static T {
+   |        -- lifetime `'a` defined here
+LL |     let f: fn(_, &'a T) -> &'static T = foo;
+   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static`
+
+error: aborting due to 1 previous error
+