about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/ghcr.yml23
-rw-r--r--compiler/rustc_borrowck/src/polonius/loan_liveness.rs83
-rw-r--r--compiler/rustc_borrowck/src/polonius/mod.rs1
-rw-r--r--compiler/rustc_borrowck/src/polonius/typeck_constraints.rs22
-rw-r--r--compiler/rustc_const_eval/src/interpret/memory.rs25
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs33
-rw-r--r--compiler/rustc_hir_typeck/src/lib.rs6
-rw-r--r--compiler/rustc_infer/src/traits/util.rs6
-rw-r--r--compiler/rustc_interface/src/passes.rs22
-rw-r--r--compiler/rustc_middle/src/mir/coverage.rs8
-rw-r--r--compiler/rustc_middle/src/query/mod.rs3
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs3
-rw-r--r--compiler/rustc_middle/src/ty/predicate.rs10
-rw-r--r--compiler/rustc_mir_build/messages.ftl12
-rw-r--r--compiler/rustc_mir_build/src/builder/mod.rs7
-rw-r--r--compiler/rustc_mir_build/src/errors.rs22
-rw-r--r--compiler/rustc_mir_build/src/lib.rs4
-rw-r--r--compiler/rustc_mir_transform/messages.ftl12
-rw-r--r--compiler/rustc_mir_transform/src/check_call_recursion.rs (renamed from compiler/rustc_mir_build/src/lints.rs)81
-rw-r--r--compiler/rustc_mir_transform/src/check_inline.rs (renamed from compiler/rustc_mir_build/src/check_inline.rs)52
-rw-r--r--compiler/rustc_mir_transform/src/coverage/counters.rs310
-rw-r--r--compiler/rustc_mir_transform/src/coverage/mod.rs15
-rw-r--r--compiler/rustc_mir_transform/src/errors.rs22
-rw-r--r--compiler/rustc_mir_transform/src/inline.rs6
-rw-r--r--compiler/rustc_mir_transform/src/lib.rs10
-rw-r--r--compiler/rustc_resolve/src/late.rs6
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs24
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs143
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs14
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs29
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs5
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/confirmation.rs7
-rw-r--r--library/core/src/num/f128.rs10
-rw-r--r--library/core/src/num/f16.rs10
-rw-r--r--library/core/src/num/f32.rs14
-rw-r--r--library/core/src/num/f64.rs14
-rw-r--r--library/std/src/io/mod.rs249
-rw-r--r--library/std/src/io/tests.rs17
-rw-r--r--library/std/src/lib.rs3
-rw-r--r--library/std/src/pipe.rs258
-rw-r--r--library/std/src/pipe/tests.rs19
-rw-r--r--library/std/src/sys/anonymous_pipe/unix.rs3
-rw-r--r--library/std/src/sys/anonymous_pipe/unsupported.rs3
-rw-r--r--library/std/src/sys/anonymous_pipe/windows.rs4
-rw-r--r--library/std/src/sys/pal/unix/kernel_copy.rs5
-rw-r--r--library/std/tests/pipe_subprocess.rs3
-rw-r--r--src/bootstrap/mk/Makefile.in13
-rw-r--r--src/bootstrap/src/core/build_steps/clippy.rs24
-rw-r--r--src/bootstrap/src/core/build_steps/dist.rs18
-rw-r--r--src/bootstrap/src/core/builder/mod.rs1
-rw-r--r--src/ci/docker/README.md8
-rw-r--r--src/ci/docker/host-x86_64/dist-mips-linux/Dockerfile1
-rw-r--r--src/ci/docker/host-x86_64/dist-mips-linux/mips-linux-gnu.defconfig2
-rw-r--r--src/ci/docker/host-x86_64/dist-mips-linux/patches/glibc/2.23/0001-MIPS-SPARC-fix-wrong-vfork-aliases-in-libpthread.so.patch44
-rw-r--r--src/ci/docker/host-x86_64/dist-mips-linux/patches/glibc/2.23/0002-MIPS-SPARC-more-fixes-to-the-vfork-aliases-in-libpth.patch63
-rw-r--r--src/ci/docker/host-x86_64/dist-mips64-linux/Dockerfile1
-rw-r--r--src/ci/docker/host-x86_64/dist-mips64-linux/mips64-linux-gnu.defconfig2
-rw-r--r--src/ci/docker/host-x86_64/dist-mips64el-linux/Dockerfile1
-rw-r--r--src/ci/docker/host-x86_64/dist-mips64el-linux/mips64el-linux-gnu.defconfig2
-rw-r--r--src/ci/docker/host-x86_64/dist-mipsel-linux/Dockerfile1
-rw-r--r--src/ci/docker/host-x86_64/dist-mipsel-linux/mipsel-linux-gnu.defconfig2
-rw-r--r--src/ci/docker/host-x86_64/mingw-check-tidy/Dockerfile4
-rw-r--r--src/ci/github-actions/jobs.yml15
-rw-r--r--src/librustdoc/html/render/print_item.rs53
-rw-r--r--src/librustdoc/html/static/css/rustdoc.css50
-rw-r--r--tests/run-make/broken-pipe-no-ice/rmake.rs2
-rw-r--r--tests/rustdoc-gui/huge-collection-of-constants.goml4
-rw-r--r--tests/rustdoc-gui/item-name-wrap.goml10
-rw-r--r--tests/rustdoc-gui/item-summary-table.goml4
-rw-r--r--tests/rustdoc-gui/label-next-to-symbol.goml36
-rw-r--r--tests/rustdoc-gui/links-color.goml4
-rw-r--r--tests/rustdoc-gui/module-items-font.goml28
-rw-r--r--tests/rustdoc-gui/sidebar.goml10
-rw-r--r--tests/rustdoc-gui/unsafe-fn.goml2
-rw-r--r--tests/rustdoc/anonymous-reexport-108931.rs4
-rw-r--r--tests/rustdoc/anonymous-reexport.rs2
-rw-r--r--tests/rustdoc/attributes-inlining-108281.rs6
-rw-r--r--tests/rustdoc/cfg_doc_reexport.rs4
-rw-r--r--tests/rustdoc/deprecated.rs7
-rw-r--r--tests/rustdoc/display-hidden-items.rs16
-rw-r--r--tests/rustdoc/doc-cfg.rs6
-rw-r--r--tests/rustdoc/doc-hidden-reexports-109449.rs6
-rw-r--r--tests/rustdoc/double-hyphen-to-dash.rs2
-rw-r--r--tests/rustdoc/duplicate-cfg.rs4
-rw-r--r--tests/rustdoc/footnote-in-summary.rs4
-rw-r--r--tests/rustdoc/glob-reexport-attribute-merge-120487.rs4
-rw-r--r--tests/rustdoc/glob-reexport-attribute-merge-doc-auto-cfg.rs4
-rw-r--r--tests/rustdoc/glob-shadowing-const.rs4
-rw-r--r--tests/rustdoc/glob-shadowing.rs18
-rw-r--r--tests/rustdoc/impl-on-ty-alias-issue-119015.rs4
-rw-r--r--tests/rustdoc/inline-private-with-intermediate-doc-hidden.rs2
-rw-r--r--tests/rustdoc/inline_cross/inline_hidden.rs8
-rw-r--r--tests/rustdoc/inline_cross/macros.rs6
-rw-r--r--tests/rustdoc/internal.rs2
-rw-r--r--tests/rustdoc/intra-doc/module-scope-name-resolution-55364.rs8
-rw-r--r--tests/rustdoc/item-desc-list-at-start.item-table.html2
-rw-r--r--tests/rustdoc/item-desc-list-at-start.rs7
-rw-r--r--tests/rustdoc/macro-rules-broken-intra-doc-106142.rs17
-rw-r--r--tests/rustdoc/nested-items-issue-111415.rs2
-rw-r--r--tests/rustdoc/overlapping-reexport-105735-2.rs4
-rw-r--r--tests/rustdoc/overlapping-reexport-105735.rs4
-rw-r--r--tests/rustdoc/pub-use-root-path-95873.rs2
-rw-r--r--tests/rustdoc/reexport-cfg.rs8
-rw-r--r--tests/rustdoc/reexport-check.rs4
-rw-r--r--tests/rustdoc/reexport-doc-hidden-inside-private.rs2
-rw-r--r--tests/rustdoc/reexport-of-reexport-108679.rs3
-rw-r--r--tests/rustdoc/reexport-trait-from-hidden-111064.rs2
-rw-r--r--tests/rustdoc/short-docblock.rs11
-rw-r--r--tests/rustdoc/stability.rs6
-rw-r--r--tests/rustdoc/staged-api-deprecated-unstable-32374.rs8
-rw-r--r--tests/rustdoc/summary-header-46377.rs2
-rw-r--r--tests/ui/consts/const-ptr-is-null.rs8
-rw-r--r--tests/ui/consts/const-ptr-is-null.stderr2
-rw-r--r--tests/ui/mir/lint/storage-live.stderr2
-rw-r--r--tests/ui/resolve/resolve-issue-135614-assoc-const.import_trait_associated_functions.stderr19
-rw-r--r--tests/ui/resolve/resolve-issue-135614-assoc-const.normal.stderr30
-rw-r--r--tests/ui/resolve/resolve-issue-135614-assoc-const.rs30
-rw-r--r--tests/ui/resolve/resolve-issue-135614.normal.stderr13
-rw-r--r--tests/ui/resolve/resolve-issue-135614.rs15
-rw-r--r--tests/ui/structs/default-field-values/empty-struct.rs21
-rw-r--r--tests/ui/structs/default-field-values/empty-struct.stderr26
121 files changed, 1225 insertions, 1189 deletions
diff --git a/.github/workflows/ghcr.yml b/.github/workflows/ghcr.yml
index 4793b28e722..052c9ae72b8 100644
--- a/.github/workflows/ghcr.yml
+++ b/.github/workflows/ghcr.yml
@@ -48,10 +48,21 @@ jobs:
 
       - name: Mirror DockerHub
         run: |
-          # DockerHub image we want to mirror
-          image="ubuntu:22.04"
+          # List of DockerHub images to mirror to ghcr.io
+          images=(
+            # Mirrored because used by the mingw-check-tidy, which doesn't cache Docker images
+            "ubuntu:22.04"
+            # Mirrored because used by all linux CI jobs, including mingw-check-tidy
+            "moby/buildkit:buildx-stable-1"
+          )
 
-          # Mirror image from DockerHub to ghcr.io
-          ./crane copy \
-            docker.io/${image} \
-            ghcr.io/${{ github.repository_owner }}/${image}
+          # Mirror each image from DockerHub to ghcr.io
+          for img in "${images[@]}"; do
+            echo "Mirroring ${img}..."
+            # Remove namespace from the image if any.
+            # E.g. "moby/buildkit:buildx-stable-1" becomes "buildkit:buildx-stable-1"
+            dest_image=$(echo "${img}" | cut -d'/' -f2-)
+            ./crane copy \
+              "docker.io/${img}" \
+              "ghcr.io/${{ github.repository_owner }}/${dest_image}"
+          done
diff --git a/compiler/rustc_borrowck/src/polonius/loan_liveness.rs b/compiler/rustc_borrowck/src/polonius/loan_liveness.rs
index c519453652f..768c12a97a6 100644
--- a/compiler/rustc_borrowck/src/polonius/loan_liveness.rs
+++ b/compiler/rustc_borrowck/src/polonius/loan_liveness.rs
@@ -9,16 +9,21 @@ use rustc_middle::ty::{RegionVid, TyCtxt};
 use rustc_mir_dataflow::points::PointIndex;
 
 use super::{LiveLoans, LocalizedOutlivesConstraintSet};
+use crate::constraints::OutlivesConstraint;
 use crate::dataflow::BorrowIndex;
 use crate::region_infer::values::LivenessValues;
+use crate::type_check::Locations;
 use crate::{BorrowSet, PlaceConflictBias, places_conflict};
 
-/// With the full graph of constraints, we can compute loan reachability, stop at kills, and trace
-/// loan liveness throughout the CFG.
+/// Compute loan reachability, stop at kills, and trace loan liveness throughout the CFG, by
+/// traversing the full graph of constraints that combines:
+/// - the localized constraints (the physical edges),
+/// - with the constraints that hold at all points (the logical edges).
 pub(super) fn compute_loan_liveness<'tcx>(
     tcx: TyCtxt<'tcx>,
     body: &Body<'tcx>,
     liveness: &LivenessValues,
+    outlives_constraints: impl Iterator<Item = OutlivesConstraint<'tcx>>,
     borrow_set: &BorrowSet<'tcx>,
     localized_outlives_constraints: &LocalizedOutlivesConstraintSet,
 ) -> LiveLoans {
@@ -29,7 +34,11 @@ pub(super) fn compute_loan_liveness<'tcx>(
     // edges when visualizing the constraint graph anyways.
     let kills = collect_kills(body, tcx, borrow_set);
 
-    let graph = index_constraints(&localized_outlives_constraints);
+    // Create the full graph with the physical edges we've localized earlier, and the logical edges
+    // of constraints that hold at all points.
+    let logical_constraints =
+        outlives_constraints.filter(|c| matches!(c.locations, Locations::All(_)));
+    let graph = LocalizedConstraintGraph::new(&localized_outlives_constraints, logical_constraints);
     let mut visited = FxHashSet::default();
     let mut stack = Vec::new();
 
@@ -108,7 +117,7 @@ pub(super) fn compute_loan_liveness<'tcx>(
             let is_loan_killed =
                 kills.get(&current_location).is_some_and(|kills| kills.contains(&loan_idx));
 
-            for succ in outgoing_edges(&graph, node) {
+            for succ in graph.outgoing_edges(node) {
                 // If the loan is killed at this point, it is killed _on exit_. But only during
                 // forward traversal.
                 if is_loan_killed {
@@ -125,9 +134,17 @@ pub(super) fn compute_loan_liveness<'tcx>(
     live_loans
 }
 
-/// The localized constraint graph is currently the per-node map of its physical edges. In the
-/// future, we'll add logical edges to model constraints that hold at all points in the CFG.
-type LocalizedConstraintGraph = FxHashMap<LocalizedNode, FxIndexSet<LocalizedNode>>;
+/// The localized constraint graph indexes the physical and logical edges to compute a given node's
+/// successors during traversal.
+struct LocalizedConstraintGraph {
+    /// The actual, physical, edges we have recorded for a given node.
+    edges: FxHashMap<LocalizedNode, FxIndexSet<LocalizedNode>>,
+
+    /// The logical edges representing the outlives constraints that hold at all points in the CFG,
+    /// which we don't localize to avoid creating a lot of unnecessary edges in the graph. Some CFGs
+    /// can be big, and we don't need to create such a physical edge for every point in the CFG.
+    logical_edges: FxHashMap<RegionVid, FxIndexSet<RegionVid>>,
+}
 
 /// A node in the graph to be traversed, one of the two vertices of a localized outlives constraint.
 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
@@ -136,24 +153,44 @@ struct LocalizedNode {
     point: PointIndex,
 }
 
-/// Traverses the constraints and returns the indexable graph of edges per node.
-fn index_constraints(constraints: &LocalizedOutlivesConstraintSet) -> LocalizedConstraintGraph {
-    let mut edges = LocalizedConstraintGraph::default();
-    for constraint in &constraints.outlives {
-        let source = LocalizedNode { region: constraint.source, point: constraint.from };
-        let target = LocalizedNode { region: constraint.target, point: constraint.to };
-        edges.entry(source).or_default().insert(target);
-    }
+impl LocalizedConstraintGraph {
+    /// Traverses the constraints and returns the indexed graph of edges per node.
+    fn new<'tcx>(
+        constraints: &LocalizedOutlivesConstraintSet,
+        logical_constraints: impl Iterator<Item = OutlivesConstraint<'tcx>>,
+    ) -> Self {
+        let mut edges: FxHashMap<_, FxIndexSet<_>> = FxHashMap::default();
+        for constraint in &constraints.outlives {
+            let source = LocalizedNode { region: constraint.source, point: constraint.from };
+            let target = LocalizedNode { region: constraint.target, point: constraint.to };
+            edges.entry(source).or_default().insert(target);
+        }
 
-    edges
-}
+        let mut logical_edges: FxHashMap<_, FxIndexSet<_>> = FxHashMap::default();
+        for constraint in logical_constraints {
+            logical_edges.entry(constraint.sup).or_default().insert(constraint.sub);
+        }
+
+        LocalizedConstraintGraph { edges, logical_edges }
+    }
 
-/// Returns the outgoing edges of a given node, not its transitive closure.
-fn outgoing_edges(
-    graph: &LocalizedConstraintGraph,
-    node: LocalizedNode,
-) -> impl Iterator<Item = LocalizedNode> + use<'_> {
-    graph.get(&node).into_iter().flat_map(|edges| edges.iter().copied())
+    /// Returns the outgoing edges of a given node, not its transitive closure.
+    fn outgoing_edges(&self, node: LocalizedNode) -> impl Iterator<Item = LocalizedNode> + use<'_> {
+        // The outgoing edges are:
+        // - the physical edges present at this node,
+        // - the materialized logical edges that exist virtually at all points for this node's
+        //   region, localized at this point.
+        let physical_edges =
+            self.edges.get(&node).into_iter().flat_map(|targets| targets.iter().copied());
+        let materialized_edges =
+            self.logical_edges.get(&node.region).into_iter().flat_map(move |targets| {
+                targets
+                    .iter()
+                    .copied()
+                    .map(move |target| LocalizedNode { point: node.point, region: target })
+            });
+        physical_edges.chain(materialized_edges)
+    }
 }
 
 /// Traverses the MIR and collects kills.
diff --git a/compiler/rustc_borrowck/src/polonius/mod.rs b/compiler/rustc_borrowck/src/polonius/mod.rs
index 52a5f75d8a2..502c868194a 100644
--- a/compiler/rustc_borrowck/src/polonius/mod.rs
+++ b/compiler/rustc_borrowck/src/polonius/mod.rs
@@ -130,6 +130,7 @@ impl PoloniusContext {
             tcx,
             body,
             regioncx.liveness_constraints(),
+            regioncx.outlives_constraints(),
             borrow_set,
             &localized_outlives_constraints,
         );
diff --git a/compiler/rustc_borrowck/src/polonius/typeck_constraints.rs b/compiler/rustc_borrowck/src/polonius/typeck_constraints.rs
index 8235b844886..1289b1899eb 100644
--- a/compiler/rustc_borrowck/src/polonius/typeck_constraints.rs
+++ b/compiler/rustc_borrowck/src/polonius/typeck_constraints.rs
@@ -22,23 +22,11 @@ pub(super) fn convert_typeck_constraints<'tcx>(
     for outlives_constraint in outlives_constraints {
         match outlives_constraint.locations {
             Locations::All(_) => {
-                // For now, turn logical constraints holding at all points into physical edges at
-                // every point in the graph.
-                // FIXME: encode this into *traversal* instead.
-                for (block, bb) in body.basic_blocks.iter_enumerated() {
-                    let statement_count = bb.statements.len();
-                    for statement_index in 0..=statement_count {
-                        let current_location = Location { block, statement_index };
-                        let current_point = liveness.point_from_location(current_location);
-
-                        localized_outlives_constraints.push(LocalizedOutlivesConstraint {
-                            source: outlives_constraint.sup,
-                            from: current_point,
-                            target: outlives_constraint.sub,
-                            to: current_point,
-                        });
-                    }
-                }
+                // We don't turn constraints holding at all points into physical edges at every
+                // point in the graph. They are encoded into *traversal* instead: a given node's
+                // successors will combine these logical edges with the regular, physical, localized
+                // edges.
+                continue;
             }
 
             Locations::Single(location) => {
diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs
index 0790db984e3..2772c94d52b 100644
--- a/compiler/rustc_const_eval/src/interpret/memory.rs
+++ b/compiler/rustc_const_eval/src/interpret/memory.rs
@@ -1481,22 +1481,31 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
     /// Test if this value might be null.
     /// If the machine does not support ptr-to-int casts, this is conservative.
     pub fn scalar_may_be_null(&self, scalar: Scalar<M::Provenance>) -> InterpResult<'tcx, bool> {
-        interp_ok(match scalar.try_to_scalar_int() {
-            Ok(int) => int.is_null(),
+        match scalar.try_to_scalar_int() {
+            Ok(int) => interp_ok(int.is_null()),
             Err(_) => {
-                // Can only happen during CTFE.
+                // We can't cast this pointer to an integer. Can only happen during CTFE.
                 let ptr = scalar.to_pointer(self)?;
                 match self.ptr_try_get_alloc_id(ptr, 0) {
                     Ok((alloc_id, offset, _)) => {
-                        let size = self.get_alloc_info(alloc_id).size;
-                        // If the pointer is out-of-bounds, it may be null.
-                        // Note that one-past-the-end (offset == size) is still inbounds, and never null.
-                        offset > size
+                        let info = self.get_alloc_info(alloc_id);
+                        // If the pointer is in-bounds (including "at the end"), it is definitely not null.
+                        if offset <= info.size {
+                            return interp_ok(false);
+                        }
+                        // If the allocation is N-aligned, and the offset is not divisible by N,
+                        // then `base + offset` has a non-zero remainder after division by `N`,
+                        // which means `base + offset` cannot be null.
+                        if offset.bytes() % info.align.bytes() != 0 {
+                            return interp_ok(false);
+                        }
+                        // We don't know enough, this might be null.
+                        interp_ok(true)
                     }
                     Err(_offset) => bug!("a non-int scalar is always a pointer"),
                 }
             }
-        })
+        }
     }
 
     /// Turning a "maybe pointer" into a proper pointer (and some information
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index 3bb518e7f97..dc6a2adf622 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -1991,18 +1991,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         adt_ty: Ty<'tcx>,
         expected: Expectation<'tcx>,
         expr: &hir::Expr<'_>,
-        span: Span,
+        path_span: Span,
         variant: &'tcx ty::VariantDef,
         hir_fields: &'tcx [hir::ExprField<'tcx>],
         base_expr: &'tcx hir::StructTailExpr<'tcx>,
     ) {
         let tcx = self.tcx;
 
-        let adt_ty = self.try_structurally_resolve_type(span, adt_ty);
+        let adt_ty = self.try_structurally_resolve_type(path_span, adt_ty);
         let adt_ty_hint = expected.only_has_type(self).and_then(|expected| {
             self.fudge_inference_if_ok(|| {
                 let ocx = ObligationCtxt::new(self);
-                ocx.sup(&self.misc(span), self.param_env, expected, adt_ty)?;
+                ocx.sup(&self.misc(path_span), self.param_env, expected, adt_ty)?;
                 if !ocx.select_where_possible().is_empty() {
                     return Err(TypeError::Mismatch);
                 }
@@ -2012,11 +2012,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         });
         if let Some(adt_ty_hint) = adt_ty_hint {
             // re-link the variables that the fudging above can create.
-            self.demand_eqtype(span, adt_ty_hint, adt_ty);
+            self.demand_eqtype(path_span, adt_ty_hint, adt_ty);
         }
 
         let ty::Adt(adt, args) = adt_ty.kind() else {
-            span_bug!(span, "non-ADT passed to check_expr_struct_fields");
+            span_bug!(path_span, "non-ADT passed to check_expr_struct_fields");
         };
         let adt_kind = adt.adt_kind();
 
@@ -2107,7 +2107,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         if adt_kind == AdtKind::Union && hir_fields.len() != 1 {
             struct_span_code_err!(
                 self.dcx(),
-                span,
+                path_span,
                 E0784,
                 "union expressions should have exactly one field",
             )
@@ -2167,6 +2167,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 });
                 return;
             }
+            if variant.fields.is_empty() {
+                let mut err = self.dcx().struct_span_err(
+                    span,
+                    format!(
+                        "`{adt_ty}` has no fields, `..` needs at least one default field in the \
+                         struct definition",
+                    ),
+                );
+                err.span_label(path_span, "this type has no fields");
+                err.emit();
+            }
             if !missing_mandatory_fields.is_empty() {
                 let s = pluralize!(missing_mandatory_fields.len());
                 let fields: Vec<_> =
@@ -2316,11 +2327,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 .collect();
 
             if !private_fields.is_empty() {
-                self.report_private_fields(adt_ty, span, expr.span, private_fields, hir_fields);
+                self.report_private_fields(
+                    adt_ty,
+                    path_span,
+                    expr.span,
+                    private_fields,
+                    hir_fields,
+                );
             } else {
                 self.report_missing_fields(
                     adt_ty,
-                    span,
+                    path_span,
                     remaining_fields,
                     variant,
                     hir_fields,
diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs
index cb21961f36b..9cd9ca040ce 100644
--- a/compiler/rustc_hir_typeck/src/lib.rs
+++ b/compiler/rustc_hir_typeck/src/lib.rs
@@ -87,7 +87,7 @@ fn used_trait_imports(tcx: TyCtxt<'_>, def_id: LocalDefId) -> &UnordSet<LocalDef
 }
 
 fn typeck<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx ty::TypeckResults<'tcx> {
-    typeck_with_fallback(tcx, def_id, None)
+    typeck_with_inspect(tcx, def_id, None)
 }
 
 /// Same as `typeck` but `inspect` is invoked on evaluation of each root obligation.
@@ -99,11 +99,11 @@ pub fn inspect_typeck<'tcx>(
     def_id: LocalDefId,
     inspect: ObligationInspector<'tcx>,
 ) -> &'tcx ty::TypeckResults<'tcx> {
-    typeck_with_fallback(tcx, def_id, Some(inspect))
+    typeck_with_inspect(tcx, def_id, Some(inspect))
 }
 
 #[instrument(level = "debug", skip(tcx, inspector), ret)]
-fn typeck_with_fallback<'tcx>(
+fn typeck_with_inspect<'tcx>(
     tcx: TyCtxt<'tcx>,
     def_id: LocalDefId,
     inspector: Option<ObligationInspector<'tcx>>,
diff --git a/compiler/rustc_infer/src/traits/util.rs b/compiler/rustc_infer/src/traits/util.rs
index ab8ada1596c..66ed49fe326 100644
--- a/compiler/rustc_infer/src/traits/util.rs
+++ b/compiler/rustc_infer/src/traits/util.rs
@@ -1,5 +1,5 @@
 use rustc_data_structures::fx::FxHashSet;
-use rustc_middle::ty::{self, ToPolyTraitRef, TyCtxt};
+use rustc_middle::ty::{self, TyCtxt};
 use rustc_span::{Ident, Span};
 pub use rustc_type_ir::elaborate::*;
 
@@ -125,8 +125,8 @@ pub fn transitive_bounds_that_define_assoc_item<'tcx>(
                     .iter_identity_copied()
                     .map(|(clause, _)| clause.instantiate_supertrait(tcx, trait_ref))
                     .filter_map(|clause| clause.as_trait_clause())
-                    // FIXME: Negative supertraits are elaborated here lol
-                    .map(|trait_pred| trait_pred.to_poly_trait_ref()),
+                    .filter(|clause| clause.polarity() == ty::PredicatePolarity::Positive)
+                    .map(|clause| clause.map_bound(|clause| clause.trait_ref)),
             );
 
             return Some(trait_ref);
diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs
index aff66e48fbb..241bc35857a 100644
--- a/compiler/rustc_interface/src/passes.rs
+++ b/compiler/rustc_interface/src/passes.rs
@@ -875,6 +875,8 @@ fn run_required_analyses(tcx: TyCtxt<'_>) {
     });
     // Freeze definitions as we don't add new ones at this point.
     // We need to wait until now since we synthesize a by-move body
+    // for all coroutine-closures.
+    //
     // This improves performance by allowing lock-free access to them.
     tcx.untracked().definitions.freeze();
 
@@ -887,7 +889,7 @@ fn run_required_analyses(tcx: TyCtxt<'_>) {
         });
     });
     sess.time("MIR_effect_checking", || {
-        for def_id in tcx.hir().body_owners() {
+        tcx.hir().par_body_owners(|def_id| {
             tcx.ensure().has_ffi_unwind_calls(def_id);
 
             // If we need to codegen, ensure that we emit all errors from
@@ -898,15 +900,17 @@ fn run_required_analyses(tcx: TyCtxt<'_>) {
             {
                 tcx.ensure().mir_drops_elaborated_and_const_checked(def_id);
             }
-        }
+        });
     });
-    tcx.hir().par_body_owners(|def_id| {
-        if tcx.is_coroutine(def_id.to_def_id()) {
-            tcx.ensure().mir_coroutine_witnesses(def_id);
-            tcx.ensure().check_coroutine_obligations(
-                tcx.typeck_root_def_id(def_id.to_def_id()).expect_local(),
-            );
-        }
+    sess.time("coroutine_obligations", || {
+        tcx.hir().par_body_owners(|def_id| {
+            if tcx.is_coroutine(def_id.to_def_id()) {
+                tcx.ensure().mir_coroutine_witnesses(def_id);
+                tcx.ensure().check_coroutine_obligations(
+                    tcx.typeck_root_def_id(def_id.to_def_id()).expect_local(),
+                );
+            }
+        });
     });
 
     sess.time("layout_testing", || layout_test::test_layout(tcx));
diff --git a/compiler/rustc_middle/src/mir/coverage.rs b/compiler/rustc_middle/src/mir/coverage.rs
index cd6b2d65bf1..65f51ae9d39 100644
--- a/compiler/rustc_middle/src/mir/coverage.rs
+++ b/compiler/rustc_middle/src/mir/coverage.rs
@@ -71,11 +71,7 @@ impl ConditionId {
 
 /// Enum that can hold a constant zero value, the ID of an physical coverage
 /// counter, or the ID of a coverage-counter expression.
-///
-/// This was originally only used for expression operands (and named `Operand`),
-/// but the zero/counter/expression distinction is also useful for representing
-/// the value of code/gap mappings, and the true/false arms of branch mappings.
-#[derive(Copy, Clone, PartialEq, Eq)]
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
 #[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
 pub enum CovTerm {
     Zero,
@@ -171,7 +167,7 @@ impl Op {
     }
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, PartialEq, Eq)]
 #[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
 pub struct Expression {
     pub lhs: CovTerm,
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index bfbcb0532c1..65e93c3a1cc 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -1164,8 +1164,7 @@ rustc_queries! {
     }
 
     /// Check whether the function has any recursion that could cause the inliner to trigger
-    /// a cycle. Returns the call stack causing the cycle. The call stack does not contain the
-    /// current function, just all intermediate functions.
+    /// a cycle.
     query mir_callgraph_reachable(key: (ty::Instance<'tcx>, LocalDefId)) -> bool {
         fatal_cycle
         desc { |tcx|
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index d2875fb3794..ca70ae794c5 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -79,8 +79,7 @@ pub use self::predicate::{
     PolyExistentialPredicate, PolyExistentialProjection, PolyExistentialTraitRef,
     PolyProjectionPredicate, PolyRegionOutlivesPredicate, PolySubtypePredicate, PolyTraitPredicate,
     PolyTraitRef, PolyTypeOutlivesPredicate, Predicate, PredicateKind, ProjectionPredicate,
-    RegionOutlivesPredicate, SubtypePredicate, ToPolyTraitRef, TraitPredicate, TraitRef,
-    TypeOutlivesPredicate,
+    RegionOutlivesPredicate, SubtypePredicate, TraitPredicate, TraitRef, TypeOutlivesPredicate,
 };
 pub use self::region::{
     BoundRegion, BoundRegionKind, EarlyParamRegion, LateParamRegion, LateParamRegionKind, Region,
diff --git a/compiler/rustc_middle/src/ty/predicate.rs b/compiler/rustc_middle/src/ty/predicate.rs
index 32d6455e825..584cac22ae8 100644
--- a/compiler/rustc_middle/src/ty/predicate.rs
+++ b/compiler/rustc_middle/src/ty/predicate.rs
@@ -476,16 +476,6 @@ impl<'tcx> Clause<'tcx> {
     }
 }
 
-pub trait ToPolyTraitRef<'tcx> {
-    fn to_poly_trait_ref(&self) -> PolyTraitRef<'tcx>;
-}
-
-impl<'tcx> ToPolyTraitRef<'tcx> for PolyTraitPredicate<'tcx> {
-    fn to_poly_trait_ref(&self) -> PolyTraitRef<'tcx> {
-        self.map_bound_ref(|trait_pred| trait_pred.trait_ref)
-    }
-}
-
 impl<'tcx> UpcastFrom<TyCtxt<'tcx>, PredicateKind<'tcx>> for Predicate<'tcx> {
     fn upcast_from(from: PredicateKind<'tcx>, tcx: TyCtxt<'tcx>) -> Self {
         ty::Binder::dummy(from).upcast(tcx)
diff --git a/compiler/rustc_mir_build/messages.ftl b/compiler/rustc_mir_build/messages.ftl
index 5203c33c968..ffdb721fb18 100644
--- a/compiler/rustc_mir_build/messages.ftl
+++ b/compiler/rustc_mir_build/messages.ftl
@@ -118,12 +118,6 @@ mir_build_extern_static_requires_unsafe_unsafe_op_in_unsafe_fn_allowed =
     .note = extern statics are not controlled by the Rust type system: invalid data, aliasing violations or data races will cause undefined behavior
     .label = use of extern static
 
-mir_build_force_inline =
-    `{$callee}` is incompatible with `#[rustc_force_inline]`
-    .attr = annotation here
-    .callee = `{$callee}` defined here
-    .note = incompatible due to: {$reason}
-
 mir_build_inform_irrefutable = `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
 
 mir_build_initializing_type_with_requires_unsafe =
@@ -330,12 +324,6 @@ mir_build_type_not_structural_more_info = see https://doc.rust-lang.org/stable/s
 mir_build_type_not_structural_tip =
     the `PartialEq` trait must be derived, manual `impl`s are not sufficient; see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details
 
-mir_build_unconditional_recursion = function cannot return without recursing
-    .label = cannot return without recursing
-    .help = a `loop` may express intention better if this is on purpose
-
-mir_build_unconditional_recursion_call_site_label = recursive call site
-
 mir_build_union_field_requires_unsafe =
     access to union field is unsafe and requires unsafe block
     .note = the field may not be properly initialized: using uninitialized data will cause undefined behavior
diff --git a/compiler/rustc_mir_build/src/builder/mod.rs b/compiler/rustc_mir_build/src/builder/mod.rs
index 8b01ec0d06a..9fa431f7d5f 100644
--- a/compiler/rustc_mir_build/src/builder/mod.rs
+++ b/compiler/rustc_mir_build/src/builder/mod.rs
@@ -26,10 +26,8 @@ use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt, TypeVisitableExt, TypingMode
 use rustc_middle::{bug, span_bug};
 use rustc_span::{Span, Symbol, sym};
 
-use super::lints;
 use crate::builder::expr::as_place::PlaceBuilder;
 use crate::builder::scope::DropKind;
-use crate::check_inline;
 
 pub(crate) fn closure_saved_names_of_captured_variables<'tcx>(
     tcx: TyCtxt<'tcx>,
@@ -48,7 +46,7 @@ pub(crate) fn closure_saved_names_of_captured_variables<'tcx>(
 }
 
 /// Construct the MIR for a given `DefId`.
-pub(crate) fn mir_build<'tcx>(tcx: TyCtxtAt<'tcx>, def: LocalDefId) -> Body<'tcx> {
+pub(crate) fn build_mir<'tcx>(tcx: TyCtxtAt<'tcx>, def: LocalDefId) -> Body<'tcx> {
     let tcx = tcx.tcx;
     tcx.ensure_with_value().thir_abstract_const(def);
     if let Err(e) = tcx.check_match(def) {
@@ -80,9 +78,6 @@ pub(crate) fn mir_build<'tcx>(tcx: TyCtxtAt<'tcx>, def: LocalDefId) -> Body<'tcx
         }
     };
 
-    lints::check(tcx, &body);
-    check_inline::check_force_inline(tcx, &body);
-
     // The borrow checker will replace all the regions here with its own
     // inference variables. There's no point having non-erased regions here.
     // The exception is `body.user_type_annotations`, which is used unmodified
diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs
index 90c31a2caa3..83aec9ccdef 100644
--- a/compiler/rustc_mir_build/src/errors.rs
+++ b/compiler/rustc_mir_build/src/errors.rs
@@ -12,16 +12,6 @@ use rustc_span::{Span, Symbol};
 use crate::fluent_generated as fluent;
 
 #[derive(LintDiagnostic)]
-#[diag(mir_build_unconditional_recursion)]
-#[help]
-pub(crate) struct UnconditionalRecursion {
-    #[label]
-    pub(crate) span: Span,
-    #[label(mir_build_unconditional_recursion_call_site_label)]
-    pub(crate) call_sites: Vec<Span>,
-}
-
-#[derive(LintDiagnostic)]
 #[diag(mir_build_call_to_deprecated_safe_fn_requires_unsafe)]
 pub(crate) struct CallToDeprecatedSafeFnRequiresUnsafe {
     #[label]
@@ -1107,15 +1097,3 @@ impl<'a> Subdiagnostic for Rust2024IncompatiblePatSugg<'a> {
         );
     }
 }
-
-#[derive(Diagnostic)]
-#[diag(mir_build_force_inline)]
-#[note]
-pub(crate) struct InvalidForceInline {
-    #[primary_span]
-    pub attr_span: Span,
-    #[label(mir_build_callee)]
-    pub callee_span: Span,
-    pub callee: String,
-    pub reason: &'static str,
-}
diff --git a/compiler/rustc_mir_build/src/lib.rs b/compiler/rustc_mir_build/src/lib.rs
index 76a35355de7..8e786733ee0 100644
--- a/compiler/rustc_mir_build/src/lib.rs
+++ b/compiler/rustc_mir_build/src/lib.rs
@@ -15,11 +15,9 @@
 // "Go to file" feature to silently ignore all files in the module, probably
 // because it assumes that "build" is a build-output directory. See #134365.
 mod builder;
-pub mod check_inline;
 mod check_tail_calls;
 mod check_unsafety;
 mod errors;
-pub mod lints;
 mod thir;
 
 use rustc_middle::util::Providers;
@@ -29,7 +27,7 @@ rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
 pub fn provide(providers: &mut Providers) {
     providers.check_match = thir::pattern::check_match;
     providers.lit_to_const = thir::constant::lit_to_const;
-    providers.hooks.build_mir = builder::mir_build;
+    providers.hooks.build_mir = builder::build_mir;
     providers.closure_saved_names_of_captured_variables =
         builder::closure_saved_names_of_captured_variables;
     providers.check_unsafety = check_unsafety::check_unsafety;
diff --git a/compiler/rustc_mir_transform/messages.ftl b/compiler/rustc_mir_transform/messages.ftl
index b0c023cca82..5628f4c9381 100644
--- a/compiler/rustc_mir_transform/messages.ftl
+++ b/compiler/rustc_mir_transform/messages.ftl
@@ -27,6 +27,12 @@ mir_transform_force_inline =
     .callee = `{$callee}` defined here
     .note = could not be inlined due to: {$reason}
 
+mir_transform_force_inline_attr =
+    `{$callee}` is incompatible with `#[rustc_force_inline]`
+    .attr = annotation here
+    .callee = `{$callee}` defined here
+    .note = incompatible due to: {$reason}
+
 mir_transform_force_inline_justification =
     `{$callee}` is required to be inlined to: {$sym}
 
@@ -66,6 +72,12 @@ mir_transform_unaligned_packed_ref = reference to packed field is unaligned
     .note_ub = creating a misaligned reference is undefined behavior (even if that reference is never dereferenced)
     .help = copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers)
 
+mir_transform_unconditional_recursion = function cannot return without recursing
+    .label = cannot return without recursing
+    .help = a `loop` may express intention better if this is on purpose
+
+mir_transform_unconditional_recursion_call_site_label = recursive call site
+
 mir_transform_undefined_transmute = pointers cannot be transmuted to integers during const eval
     .note = at compile-time, pointers do not have an integer value
     .note2 = avoiding this restriction via `union` or raw pointers leads to compile-time undefined behavior
diff --git a/compiler/rustc_mir_build/src/lints.rs b/compiler/rustc_mir_transform/src/check_call_recursion.rs
index 5cf33868ade..51fd3c6512e 100644
--- a/compiler/rustc_mir_build/src/lints.rs
+++ b/compiler/rustc_mir_transform/src/check_call_recursion.rs
@@ -10,25 +10,54 @@ use rustc_session::lint::builtin::UNCONDITIONAL_RECURSION;
 use rustc_span::Span;
 
 use crate::errors::UnconditionalRecursion;
+use crate::pass_manager::MirLint;
 
-pub(crate) fn check<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
-    check_call_recursion(tcx, body);
+pub(super) struct CheckCallRecursion;
+
+impl<'tcx> MirLint<'tcx> for CheckCallRecursion {
+    fn run_lint(&self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
+        let def_id = body.source.def_id().expect_local();
+
+        if let DefKind::Fn | DefKind::AssocFn = tcx.def_kind(def_id) {
+            // If this is trait/impl method, extract the trait's args.
+            let trait_args = match tcx.trait_of_item(def_id.to_def_id()) {
+                Some(trait_def_id) => {
+                    let trait_args_count = tcx.generics_of(trait_def_id).count();
+                    &GenericArgs::identity_for_item(tcx, def_id)[..trait_args_count]
+                }
+                _ => &[],
+            };
+
+            check_recursion(tcx, body, CallRecursion { trait_args })
+        }
+    }
 }
 
-fn check_call_recursion<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
-    let def_id = body.source.def_id().expect_local();
+/// Requires drop elaboration to have been performed.
+pub(super) struct CheckDropRecursion;
 
-    if let DefKind::Fn | DefKind::AssocFn = tcx.def_kind(def_id) {
-        // If this is trait/impl method, extract the trait's args.
-        let trait_args = match tcx.trait_of_item(def_id.to_def_id()) {
-            Some(trait_def_id) => {
-                let trait_args_count = tcx.generics_of(trait_def_id).count();
-                &GenericArgs::identity_for_item(tcx, def_id)[..trait_args_count]
-            }
-            _ => &[],
-        };
+impl<'tcx> MirLint<'tcx> for CheckDropRecursion {
+    fn run_lint(&self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
+        let def_id = body.source.def_id().expect_local();
 
-        check_recursion(tcx, body, CallRecursion { trait_args })
+        // First check if `body` is an `fn drop()` of `Drop`
+        if let DefKind::AssocFn = tcx.def_kind(def_id)
+        && let Some(trait_ref) =
+            tcx.impl_of_method(def_id.to_def_id()).and_then(|def_id| tcx.impl_trait_ref(def_id))
+        && let Some(drop_trait) = tcx.lang_items().drop_trait()
+        && drop_trait == trait_ref.instantiate_identity().def_id
+        // avoid erroneous `Drop` impls from causing ICEs below
+        && let sig = tcx.fn_sig(def_id).instantiate_identity()
+        && sig.inputs().skip_binder().len() == 1
+        {
+            // It was. Now figure out for what type `Drop` is implemented and then
+            // check for recursion.
+            if let ty::Ref(_, dropped_ty, _) =
+                tcx.liberate_late_bound_regions(def_id.to_def_id(), sig.input(0)).kind()
+            {
+                check_recursion(tcx, body, RecursiveDrop { drop_for: *dropped_ty });
+            }
+        }
     }
 }
 
@@ -61,30 +90,6 @@ fn check_recursion<'tcx>(
     }
 }
 
-/// Requires drop elaboration to have been performed first.
-pub fn check_drop_recursion<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
-    let def_id = body.source.def_id().expect_local();
-
-    // First check if `body` is an `fn drop()` of `Drop`
-    if let DefKind::AssocFn = tcx.def_kind(def_id)
-        && let Some(trait_ref) =
-            tcx.impl_of_method(def_id.to_def_id()).and_then(|def_id| tcx.impl_trait_ref(def_id))
-        && let Some(drop_trait) = tcx.lang_items().drop_trait()
-        && drop_trait == trait_ref.instantiate_identity().def_id
-        // avoid erroneous `Drop` impls from causing ICEs below
-        && let sig = tcx.fn_sig(def_id).instantiate_identity()
-        && sig.inputs().skip_binder().len() == 1
-    {
-        // It was. Now figure out for what type `Drop` is implemented and then
-        // check for recursion.
-        if let ty::Ref(_, dropped_ty, _) =
-            tcx.liberate_late_bound_regions(def_id.to_def_id(), sig.input(0)).kind()
-        {
-            check_recursion(tcx, body, RecursiveDrop { drop_for: *dropped_ty });
-        }
-    }
-}
-
 trait TerminatorClassifier<'tcx> {
     fn is_recursive_terminator(
         &self,
diff --git a/compiler/rustc_mir_build/src/check_inline.rs b/compiler/rustc_mir_transform/src/check_inline.rs
index 1af3b3e2c13..497f4a660ea 100644
--- a/compiler/rustc_mir_build/src/check_inline.rs
+++ b/compiler/rustc_mir_transform/src/check_inline.rs
@@ -1,3 +1,6 @@
+//! Check that a body annotated with `#[rustc_force_inline]` will not fail to inline based on its
+//! definition alone (irrespective of any specific caller).
+
 use rustc_attr_parsing::InlineAttr;
 use rustc_hir::def_id::DefId;
 use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
@@ -6,30 +9,37 @@ use rustc_middle::ty;
 use rustc_middle::ty::TyCtxt;
 use rustc_span::sym;
 
-/// Check that a body annotated with `#[rustc_force_inline]` will not fail to inline based on its
-/// definition alone (irrespective of any specific caller).
-pub(crate) fn check_force_inline<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
-    let def_id = body.source.def_id();
-    if !tcx.hir().body_owner_kind(def_id).is_fn_or_closure() || !def_id.is_local() {
-        return;
-    }
-    let InlineAttr::Force { attr_span, .. } = tcx.codegen_fn_attrs(def_id).inline else {
-        return;
-    };
+use crate::pass_manager::MirLint;
 
-    if let Err(reason) =
-        is_inline_valid_on_fn(tcx, def_id).and_then(|_| is_inline_valid_on_body(tcx, body))
-    {
-        tcx.dcx().emit_err(crate::errors::InvalidForceInline {
-            attr_span,
-            callee_span: tcx.def_span(def_id),
-            callee: tcx.def_path_str(def_id),
-            reason,
-        });
+pub(super) struct CheckForceInline;
+
+impl<'tcx> MirLint<'tcx> for CheckForceInline {
+    fn run_lint(&self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
+        let def_id = body.source.def_id();
+        if !tcx.hir().body_owner_kind(def_id).is_fn_or_closure() || !def_id.is_local() {
+            return;
+        }
+        let InlineAttr::Force { attr_span, .. } = tcx.codegen_fn_attrs(def_id).inline else {
+            return;
+        };
+
+        if let Err(reason) =
+            is_inline_valid_on_fn(tcx, def_id).and_then(|_| is_inline_valid_on_body(tcx, body))
+        {
+            tcx.dcx().emit_err(crate::errors::InvalidForceInline {
+                attr_span,
+                callee_span: tcx.def_span(def_id),
+                callee: tcx.def_path_str(def_id),
+                reason,
+            });
+        }
     }
 }
 
-pub fn is_inline_valid_on_fn<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Result<(), &'static str> {
+pub(super) fn is_inline_valid_on_fn<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    def_id: DefId,
+) -> Result<(), &'static str> {
     let codegen_attrs = tcx.codegen_fn_attrs(def_id);
     if tcx.has_attr(def_id, sym::rustc_no_mir_inline) {
         return Err("#[rustc_no_mir_inline]");
@@ -65,7 +75,7 @@ pub fn is_inline_valid_on_fn<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Result<(
     Ok(())
 }
 
-pub fn is_inline_valid_on_body<'tcx>(
+pub(super) fn is_inline_valid_on_body<'tcx>(
     _: TyCtxt<'tcx>,
     body: &Body<'tcx>,
 ) -> Result<(), &'static str> {
diff --git a/compiler/rustc_mir_transform/src/coverage/counters.rs b/compiler/rustc_mir_transform/src/coverage/counters.rs
index 1a9323329f6..8d397f63cc7 100644
--- a/compiler/rustc_mir_transform/src/coverage/counters.rs
+++ b/compiler/rustc_mir_transform/src/coverage/counters.rs
@@ -1,10 +1,9 @@
 use std::cmp::Ordering;
-use std::fmt::{self, Debug};
 
 use either::Either;
 use itertools::Itertools;
 use rustc_data_structures::captures::Captures;
-use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
 use rustc_data_structures::graph::DirectedGraph;
 use rustc_index::IndexVec;
 use rustc_index::bit_set::DenseBitSet;
@@ -20,134 +19,163 @@ mod iter_nodes;
 mod node_flow;
 mod union_find;
 
-/// The coverage counter or counter expression associated with a particular
-/// BCB node or BCB edge.
-#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
-enum BcbCounter {
-    Counter { id: CounterId },
-    Expression { id: ExpressionId },
+/// Ensures that each BCB node needing a counter has one, by creating physical
+/// counters or counter expressions for nodes as required.
+pub(super) fn make_bcb_counters(
+    graph: &CoverageGraph,
+    bcb_needs_counter: &DenseBitSet<BasicCoverageBlock>,
+) -> CoverageCounters {
+    // Create the derived graphs that are necessary for subsequent steps.
+    let balanced_graph = BalancedFlowGraph::for_graph(graph, |n| !graph[n].is_out_summable);
+    let merged_graph = MergedNodeFlowGraph::for_balanced_graph(&balanced_graph);
+
+    // Use those graphs to determine which nodes get physical counters, and how
+    // to compute the execution counts of other nodes from those counters.
+    let nodes = make_node_counter_priority_list(graph, balanced_graph);
+    let node_counters = merged_graph.make_node_counters(&nodes);
+
+    // Convert the counters into a form suitable for embedding into MIR.
+    transcribe_counters(&node_counters, bcb_needs_counter)
 }
 
-impl BcbCounter {
-    fn as_term(&self) -> CovTerm {
-        match *self {
-            BcbCounter::Counter { id, .. } => CovTerm::Counter(id),
-            BcbCounter::Expression { id, .. } => CovTerm::Expression(id),
-        }
-    }
+/// Arranges the nodes in `balanced_graph` into a list, such that earlier nodes
+/// take priority in being given a counter expression instead of a physical counter.
+fn make_node_counter_priority_list(
+    graph: &CoverageGraph,
+    balanced_graph: BalancedFlowGraph<&CoverageGraph>,
+) -> Vec<BasicCoverageBlock> {
+    // A "reloop" node has exactly one out-edge, which jumps back to the top
+    // of an enclosing loop. Reloop nodes are typically visited more times
+    // than loop-exit nodes, so try to avoid giving them physical counters.
+    let is_reloop_node = IndexVec::from_fn_n(
+        |node| match graph.successors[node].as_slice() {
+            &[succ] => graph.dominates(succ, node),
+            _ => false,
+        },
+        graph.num_nodes(),
+    );
+
+    let mut nodes = balanced_graph.iter_nodes().rev().collect::<Vec<_>>();
+    // The first node is the sink, which must not get a physical counter.
+    assert_eq!(nodes[0], balanced_graph.sink);
+    // Sort the real nodes, such that earlier (lesser) nodes take priority
+    // in being given a counter expression instead of a physical counter.
+    nodes[1..].sort_by(|&a, &b| {
+        // Start with a dummy `Equal` to make the actual tests line up nicely.
+        Ordering::Equal
+            // Prefer a physical counter for return/yield nodes.
+            .then_with(|| Ord::cmp(&graph[a].is_out_summable, &graph[b].is_out_summable))
+            // Prefer an expression for reloop nodes (see definition above).
+            .then_with(|| Ord::cmp(&is_reloop_node[a], &is_reloop_node[b]).reverse())
+            // Otherwise, prefer a physical counter for dominating nodes.
+            .then_with(|| graph.cmp_in_dominator_order(a, b).reverse())
+    });
+    nodes
 }
 
-impl Debug for BcbCounter {
-    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self {
-            Self::Counter { id, .. } => write!(fmt, "Counter({:?})", id.index()),
-            Self::Expression { id } => write!(fmt, "Expression({:?})", id.index()),
+// Converts node counters into a form suitable for embedding into MIR.
+fn transcribe_counters(
+    old: &NodeCounters<BasicCoverageBlock>,
+    bcb_needs_counter: &DenseBitSet<BasicCoverageBlock>,
+) -> CoverageCounters {
+    let mut new = CoverageCounters::with_num_bcbs(bcb_needs_counter.domain_size());
+
+    for bcb in bcb_needs_counter.iter() {
+        // Our counter-creation algorithm doesn't guarantee that a counter
+        // expression starts or ends with a positive term, so partition the
+        // counters into "positive" and "negative" lists for easier handling.
+        let (mut pos, mut neg): (Vec<_>, Vec<_>) =
+            old.counter_expr(bcb).iter().partition_map(|&CounterTerm { node, op }| match op {
+                Op::Add => Either::Left(node),
+                Op::Subtract => Either::Right(node),
+            });
+
+        if pos.is_empty() {
+            // If we somehow end up with no positive terms, fall back to
+            // creating a physical counter. There's no known way for this
+            // to happen, but we can avoid an ICE if it does.
+            debug_assert!(false, "{bcb:?} has no positive counter terms");
+            pos = vec![bcb];
+            neg = vec![];
         }
-    }
-}
 
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
-struct BcbExpression {
-    lhs: BcbCounter,
-    op: Op,
-    rhs: BcbCounter,
-}
+        // These intermediate sorts are not strictly necessary, but were helpful
+        // in reducing churn when switching to the current counter-creation scheme.
+        // They also help to slightly decrease the overall size of the expression
+        // table, due to more subexpressions being shared.
+        pos.sort();
+        neg.sort();
+
+        let mut new_counters_for_sites = |sites: Vec<BasicCoverageBlock>| {
+            sites.into_iter().map(|node| new.ensure_phys_counter(node)).collect::<Vec<_>>()
+        };
+        let mut pos = new_counters_for_sites(pos);
+        let mut neg = new_counters_for_sites(neg);
+
+        // These sorts are also not strictly necessary; see above.
+        pos.sort();
+        neg.sort();
+
+        let pos_counter = new.make_sum(&pos).expect("`pos` should not be empty");
+        let new_counter = new.make_subtracted_sum(pos_counter, &neg);
+        new.set_node_counter(bcb, new_counter);
+    }
 
-/// Enum representing either a node or an edge in the coverage graph.
-///
-/// FIXME(#135481): This enum is no longer needed now that we only instrument
-/// nodes and not edges. It can be removed in a subsequent PR.
-#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
-pub(super) enum Site {
-    Node { bcb: BasicCoverageBlock },
+    new
 }
 
 /// Generates and stores coverage counter and coverage expression information
-/// associated with nodes/edges in the BCB graph.
+/// associated with nodes in the coverage graph.
 pub(super) struct CoverageCounters {
     /// List of places where a counter-increment statement should be injected
     /// into MIR, each with its corresponding counter ID.
-    counter_increment_sites: IndexVec<CounterId, Site>,
+    phys_counter_for_node: FxIndexMap<BasicCoverageBlock, CounterId>,
+    next_counter_id: CounterId,
 
     /// Coverage counters/expressions that are associated with individual BCBs.
-    node_counters: IndexVec<BasicCoverageBlock, Option<BcbCounter>>,
+    node_counters: IndexVec<BasicCoverageBlock, Option<CovTerm>>,
 
     /// Table of expression data, associating each expression ID with its
     /// corresponding operator (+ or -) and its LHS/RHS operands.
-    expressions: IndexVec<ExpressionId, BcbExpression>,
+    expressions: IndexVec<ExpressionId, Expression>,
     /// Remember expressions that have already been created (or simplified),
     /// so that we don't create unnecessary duplicates.
-    expressions_memo: FxHashMap<BcbExpression, BcbCounter>,
+    expressions_memo: FxHashMap<Expression, CovTerm>,
 }
 
 impl CoverageCounters {
-    /// Ensures that each BCB node needing a counter has one, by creating physical
-    /// counters or counter expressions for nodes and edges as required.
-    pub(super) fn make_bcb_counters(
-        graph: &CoverageGraph,
-        bcb_needs_counter: &DenseBitSet<BasicCoverageBlock>,
-    ) -> Self {
-        let balanced_graph = BalancedFlowGraph::for_graph(graph, |n| !graph[n].is_out_summable);
-        let merged_graph = MergedNodeFlowGraph::for_balanced_graph(&balanced_graph);
-
-        // A "reloop" node has exactly one out-edge, which jumps back to the top
-        // of an enclosing loop. Reloop nodes are typically visited more times
-        // than loop-exit nodes, so try to avoid giving them physical counters.
-        let is_reloop_node = IndexVec::from_fn_n(
-            |node| match graph.successors[node].as_slice() {
-                &[succ] => graph.dominates(succ, node),
-                _ => false,
-            },
-            graph.num_nodes(),
-        );
-
-        let mut nodes = balanced_graph.iter_nodes().rev().collect::<Vec<_>>();
-        // The first node is the sink, which must not get a physical counter.
-        assert_eq!(nodes[0], balanced_graph.sink);
-        // Sort the real nodes, such that earlier (lesser) nodes take priority
-        // in being given a counter expression instead of a physical counter.
-        nodes[1..].sort_by(|&a, &b| {
-            // Start with a dummy `Equal` to make the actual tests line up nicely.
-            Ordering::Equal
-                // Prefer a physical counter for return/yield nodes.
-                .then_with(|| Ord::cmp(&graph[a].is_out_summable, &graph[b].is_out_summable))
-                // Prefer an expression for reloop nodes (see definition above).
-                .then_with(|| Ord::cmp(&is_reloop_node[a], &is_reloop_node[b]).reverse())
-                // Otherwise, prefer a physical counter for dominating nodes.
-                .then_with(|| graph.cmp_in_dominator_order(a, b).reverse())
-        });
-        let node_counters = merged_graph.make_node_counters(&nodes);
-
-        Transcriber::new(graph.num_nodes(), node_counters).transcribe_counters(bcb_needs_counter)
-    }
-
     fn with_num_bcbs(num_bcbs: usize) -> Self {
         Self {
-            counter_increment_sites: IndexVec::new(),
+            phys_counter_for_node: FxIndexMap::default(),
+            next_counter_id: CounterId::ZERO,
             node_counters: IndexVec::from_elem_n(None, num_bcbs),
             expressions: IndexVec::new(),
             expressions_memo: FxHashMap::default(),
         }
     }
 
-    /// Creates a new physical counter for a BCB node or edge.
-    fn make_phys_counter(&mut self, site: Site) -> BcbCounter {
-        let id = self.counter_increment_sites.push(site);
-        BcbCounter::Counter { id }
+    /// Returns the physical counter for the given node, creating it if necessary.
+    fn ensure_phys_counter(&mut self, bcb: BasicCoverageBlock) -> CovTerm {
+        let id = *self.phys_counter_for_node.entry(bcb).or_insert_with(|| {
+            let id = self.next_counter_id;
+            self.next_counter_id = id + 1;
+            id
+        });
+        CovTerm::Counter(id)
     }
 
-    fn make_expression(&mut self, lhs: BcbCounter, op: Op, rhs: BcbCounter) -> BcbCounter {
-        let new_expr = BcbExpression { lhs, op, rhs };
-        *self.expressions_memo.entry(new_expr).or_insert_with(|| {
+    fn make_expression(&mut self, lhs: CovTerm, op: Op, rhs: CovTerm) -> CovTerm {
+        let new_expr = Expression { lhs, op, rhs };
+        *self.expressions_memo.entry(new_expr.clone()).or_insert_with(|| {
             let id = self.expressions.push(new_expr);
-            BcbCounter::Expression { id }
+            CovTerm::Expression(id)
         })
     }
 
     /// Creates a counter that is the sum of the given counters.
     ///
     /// Returns `None` if the given list of counters was empty.
-    fn make_sum(&mut self, counters: &[BcbCounter]) -> Option<BcbCounter> {
+    fn make_sum(&mut self, counters: &[CovTerm]) -> Option<CovTerm> {
         counters
             .iter()
             .copied()
@@ -155,16 +183,18 @@ impl CoverageCounters {
     }
 
     /// Creates a counter whose value is `lhs - SUM(rhs)`.
-    fn make_subtracted_sum(&mut self, lhs: BcbCounter, rhs: &[BcbCounter]) -> BcbCounter {
+    fn make_subtracted_sum(&mut self, lhs: CovTerm, rhs: &[CovTerm]) -> CovTerm {
         let Some(rhs_sum) = self.make_sum(rhs) else { return lhs };
         self.make_expression(lhs, Op::Subtract, rhs_sum)
     }
 
     pub(super) fn num_counters(&self) -> usize {
-        self.counter_increment_sites.len()
+        let num_counters = self.phys_counter_for_node.len();
+        assert_eq!(num_counters, self.next_counter_id.as_usize());
+        num_counters
     }
 
-    fn set_node_counter(&mut self, bcb: BasicCoverageBlock, counter: BcbCounter) -> BcbCounter {
+    fn set_node_counter(&mut self, bcb: BasicCoverageBlock, counter: CovTerm) -> CovTerm {
         let existing = self.node_counters[bcb].replace(counter);
         assert!(
             existing.is_none(),
@@ -174,16 +204,16 @@ impl CoverageCounters {
     }
 
     pub(super) fn term_for_bcb(&self, bcb: BasicCoverageBlock) -> Option<CovTerm> {
-        self.node_counters[bcb].map(|counter| counter.as_term())
+        self.node_counters[bcb]
     }
 
-    /// Returns an iterator over all the nodes/edges in the coverage graph that
+    /// Returns an iterator over all the nodes in the coverage graph that
     /// should have a counter-increment statement injected into MIR, along with
     /// each site's corresponding counter ID.
     pub(super) fn counter_increment_sites(
         &self,
-    ) -> impl Iterator<Item = (CounterId, Site)> + Captures<'_> {
-        self.counter_increment_sites.iter_enumerated().map(|(id, &site)| (id, site))
+    ) -> impl Iterator<Item = (CounterId, BasicCoverageBlock)> + Captures<'_> {
+        self.phys_counter_for_node.iter().map(|(&site, &id)| (id, site))
     }
 
     /// Returns an iterator over the subset of BCB nodes that have been associated
@@ -193,93 +223,13 @@ impl CoverageCounters {
     ) -> impl Iterator<Item = (BasicCoverageBlock, ExpressionId)> + Captures<'_> {
         self.node_counters.iter_enumerated().filter_map(|(bcb, &counter)| match counter {
             // Yield the BCB along with its associated expression ID.
-            Some(BcbCounter::Expression { id }) => Some((bcb, id)),
+            Some(CovTerm::Expression(id)) => Some((bcb, id)),
             // This BCB is associated with a counter or nothing, so skip it.
-            Some(BcbCounter::Counter { .. }) | None => None,
+            Some(CovTerm::Counter { .. } | CovTerm::Zero) | None => None,
         })
     }
 
     pub(super) fn into_expressions(self) -> IndexVec<ExpressionId, Expression> {
-        let old_len = self.expressions.len();
-        let expressions = self
-            .expressions
-            .into_iter()
-            .map(|BcbExpression { lhs, op, rhs }| Expression {
-                lhs: lhs.as_term(),
-                op,
-                rhs: rhs.as_term(),
-            })
-            .collect::<IndexVec<ExpressionId, _>>();
-
-        // Expression IDs are indexes into this vector, so make sure we didn't
-        // accidentally invalidate them by changing its length.
-        assert_eq!(old_len, expressions.len());
-        expressions
-    }
-}
-
-struct Transcriber {
-    old: NodeCounters<BasicCoverageBlock>,
-    new: CoverageCounters,
-    phys_counter_for_site: FxHashMap<Site, BcbCounter>,
-}
-
-impl Transcriber {
-    fn new(num_nodes: usize, old: NodeCounters<BasicCoverageBlock>) -> Self {
-        Self {
-            old,
-            new: CoverageCounters::with_num_bcbs(num_nodes),
-            phys_counter_for_site: FxHashMap::default(),
-        }
-    }
-
-    fn transcribe_counters(
-        mut self,
-        bcb_needs_counter: &DenseBitSet<BasicCoverageBlock>,
-    ) -> CoverageCounters {
-        for bcb in bcb_needs_counter.iter() {
-            let site = Site::Node { bcb };
-            let (mut pos, mut neg): (Vec<_>, Vec<_>) =
-                self.old.counter_expr(bcb).iter().partition_map(
-                    |&CounterTerm { node, op }| match op {
-                        Op::Add => Either::Left(node),
-                        Op::Subtract => Either::Right(node),
-                    },
-                );
-
-            if pos.is_empty() {
-                // If we somehow end up with no positive terms, fall back to
-                // creating a physical counter. There's no known way for this
-                // to happen, but we can avoid an ICE if it does.
-                debug_assert!(false, "{site:?} has no positive counter terms");
-                pos = vec![bcb];
-                neg = vec![];
-            }
-
-            pos.sort();
-            neg.sort();
-
-            let mut new_counters_for_sites = |sites: Vec<BasicCoverageBlock>| {
-                sites
-                    .into_iter()
-                    .map(|node| self.ensure_phys_counter(Site::Node { bcb: node }))
-                    .collect::<Vec<_>>()
-            };
-            let mut pos = new_counters_for_sites(pos);
-            let mut neg = new_counters_for_sites(neg);
-
-            pos.sort();
-            neg.sort();
-
-            let pos_counter = self.new.make_sum(&pos).expect("`pos` should not be empty");
-            let new_counter = self.new.make_subtracted_sum(pos_counter, &neg);
-            self.new.set_node_counter(bcb, new_counter);
-        }
-
-        self.new
-    }
-
-    fn ensure_phys_counter(&mut self, site: Site) -> BcbCounter {
-        *self.phys_counter_for_site.entry(site).or_insert_with(|| self.new.make_phys_counter(site))
+        self.expressions
     }
 }
diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs
index b1b609595b7..19568735df7 100644
--- a/compiler/rustc_mir_transform/src/coverage/mod.rs
+++ b/compiler/rustc_mir_transform/src/coverage/mod.rs
@@ -21,7 +21,7 @@ use rustc_span::Span;
 use rustc_span::def_id::LocalDefId;
 use tracing::{debug, debug_span, trace};
 
-use crate::coverage::counters::{CoverageCounters, Site};
+use crate::coverage::counters::CoverageCounters;
 use crate::coverage::graph::CoverageGraph;
 use crate::coverage::mappings::ExtractedMappings;
 
@@ -89,8 +89,7 @@ fn instrument_function_for_coverage<'tcx>(tcx: TyCtxt<'tcx>, mir_body: &mut mir:
         return;
     }
 
-    let coverage_counters =
-        CoverageCounters::make_bcb_counters(&graph, &bcbs_with_counter_mappings);
+    let coverage_counters = counters::make_bcb_counters(&graph, &bcbs_with_counter_mappings);
 
     let mappings = create_mappings(&extracted_mappings, &coverage_counters);
     if mappings.is_empty() {
@@ -239,14 +238,8 @@ fn inject_coverage_statements<'tcx>(
     coverage_counters: &CoverageCounters,
 ) {
     // Inject counter-increment statements into MIR.
-    for (id, site) in coverage_counters.counter_increment_sites() {
-        // Determine the block to inject a counter-increment statement into.
-        // For BCB nodes this is just their first block, but for edges we need
-        // to create a new block between the two BCBs, and inject into that.
-        let target_bb = match site {
-            Site::Node { bcb } => graph[bcb].leader_bb(),
-        };
-
+    for (id, bcb) in coverage_counters.counter_increment_sites() {
+        let target_bb = graph[bcb].leader_bb();
         inject_statement(mir_body, CoverageKind::CounterIncrement { id }, target_bb);
     }
 
diff --git a/compiler/rustc_mir_transform/src/errors.rs b/compiler/rustc_mir_transform/src/errors.rs
index 015633d145f..a2fd46043ca 100644
--- a/compiler/rustc_mir_transform/src/errors.rs
+++ b/compiler/rustc_mir_transform/src/errors.rs
@@ -10,6 +10,28 @@ use rustc_span::{Span, Symbol};
 use crate::fluent_generated as fluent;
 
 #[derive(LintDiagnostic)]
+#[diag(mir_transform_unconditional_recursion)]
+#[help]
+pub(crate) struct UnconditionalRecursion {
+    #[label]
+    pub(crate) span: Span,
+    #[label(mir_transform_unconditional_recursion_call_site_label)]
+    pub(crate) call_sites: Vec<Span>,
+}
+
+#[derive(Diagnostic)]
+#[diag(mir_transform_force_inline_attr)]
+#[note]
+pub(crate) struct InvalidForceInline {
+    #[primary_span]
+    pub attr_span: Span,
+    #[label(mir_transform_callee)]
+    pub callee_span: Span,
+    pub callee: String,
+    pub reason: &'static str,
+}
+
+#[derive(LintDiagnostic)]
 pub(crate) enum ConstMutate {
     #[diag(mir_transform_const_modify)]
     #[note]
diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs
index 470393c9ae1..2052e28325c 100644
--- a/compiler/rustc_mir_transform/src/inline.rs
+++ b/compiler/rustc_mir_transform/src/inline.rs
@@ -21,8 +21,8 @@ use tracing::{debug, instrument, trace, trace_span};
 use crate::cost_checker::CostChecker;
 use crate::deref_separator::deref_finder;
 use crate::simplify::simplify_cfg;
-use crate::util;
 use crate::validate::validate_types;
+use crate::{check_inline, util};
 
 pub(crate) mod cycle;
 
@@ -575,7 +575,7 @@ fn try_inlining<'tcx, I: Inliner<'tcx>>(
     check_mir_is_available(inliner, caller_body, callsite.callee)?;
 
     let callee_attrs = tcx.codegen_fn_attrs(callsite.callee.def_id());
-    rustc_mir_build::check_inline::is_inline_valid_on_fn(tcx, callsite.callee.def_id())?;
+    check_inline::is_inline_valid_on_fn(tcx, callsite.callee.def_id())?;
     check_codegen_attributes(inliner, callsite, callee_attrs)?;
 
     let terminator = caller_body[callsite.block].terminator.as_ref().unwrap();
@@ -590,7 +590,7 @@ fn try_inlining<'tcx, I: Inliner<'tcx>>(
     }
 
     let callee_body = try_instance_mir(tcx, callsite.callee.def)?;
-    rustc_mir_build::check_inline::is_inline_valid_on_body(tcx, callee_body)?;
+    check_inline::is_inline_valid_on_body(tcx, callee_body)?;
     inliner.check_callee_mir_body(callsite, callee_body, callee_attrs)?;
 
     let Ok(callee_body) = callsite.callee.try_instantiate_mir_and_normalize_erasing_regions(
diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs
index db999bea986..d1bacf1f598 100644
--- a/compiler/rustc_mir_transform/src/lib.rs
+++ b/compiler/rustc_mir_transform/src/lib.rs
@@ -114,6 +114,8 @@ declare_passes! {
     mod add_moves_for_packed_drops : AddMovesForPackedDrops;
     mod add_retag : AddRetag;
     mod add_subtyping_projections : Subtyper;
+    mod check_inline : CheckForceInline;
+    mod check_call_recursion : CheckCallRecursion, CheckDropRecursion;
     mod check_alignment : CheckAlignment;
     mod check_const_item_mutation : CheckConstItemMutation;
     mod check_packed_ref : CheckPackedRef;
@@ -375,6 +377,8 @@ fn mir_built(tcx: TyCtxt<'_>, def: LocalDefId) -> &Steal<Body<'_>> {
         &mut body,
         &[
             // MIR-level lints.
+            &Lint(check_inline::CheckForceInline),
+            &Lint(check_call_recursion::CheckCallRecursion),
             &Lint(check_packed_ref::CheckPackedRef),
             &Lint(check_const_item_mutation::CheckConstItemMutation),
             &Lint(function_item_references::FunctionItemReferences),
@@ -505,10 +509,6 @@ fn mir_drops_elaborated_and_const_checked(tcx: TyCtxt<'_>, def: LocalDefId) -> &
 
     run_analysis_to_runtime_passes(tcx, &mut body);
 
-    // Now that drop elaboration has been performed, we can check for
-    // unconditional drop recursion.
-    rustc_mir_build::lints::check_drop_recursion(tcx, &body);
-
     tcx.alloc_steal_mir(body)
 }
 
@@ -570,6 +570,8 @@ fn run_runtime_lowering_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
         // Calling this after `PostAnalysisNormalize` ensures that we don't deal with opaque types.
         &add_subtyping_projections::Subtyper,
         &elaborate_drops::ElaborateDrops,
+        // Needs to happen after drop elaboration.
+        &Lint(check_call_recursion::CheckDropRecursion),
         // This will remove extraneous landing pads which are no longer
         // necessary as well as forcing any call in a non-unwinding
         // function calling a possibly-unwinding function to abort the process.
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index c4aeaf478bd..1a0292ebbde 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -3960,7 +3960,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
         match res {
             Res::SelfCtor(_) // See #70549.
             | Res::Def(
-                DefKind::Ctor(_, CtorKind::Const) | DefKind::Const | DefKind::ConstParam,
+                DefKind::Ctor(_, CtorKind::Const) | DefKind::Const | DefKind::AssocConst | DefKind::ConstParam,
                 _,
             ) if is_syntactic_ambiguity => {
                 // Disambiguate in favor of a unit struct/variant or constant pattern.
@@ -3969,7 +3969,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
                 }
                 Some(res)
             }
-            Res::Def(DefKind::Ctor(..) | DefKind::Const | DefKind::Static { .. }, _) => {
+            Res::Def(DefKind::Ctor(..) | DefKind::Const | DefKind::AssocConst | DefKind::Static { .. }, _) => {
                 // This is unambiguously a fresh binding, either syntactically
                 // (e.g., `IDENT @ PAT` or `ref IDENT`) or because `IDENT` resolves
                 // to something unusable as a pattern (e.g., constructor function),
@@ -4005,7 +4005,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
                 );
                 None
             }
-            Res::Def(DefKind::Fn, _) | Res::Local(..) | Res::Err => {
+            Res::Def(DefKind::Fn | DefKind::AssocFn, _) | Res::Local(..) | Res::Err => {
                 // These entities are explicitly allowed to be shadowed by fresh bindings.
                 None
             }
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs
index fc0de13aeab..b4d294a70c0 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs
@@ -172,14 +172,14 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         let bound_predicate = predicate.kind();
         let mut err = match bound_predicate.skip_binder() {
             ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) => {
-                let trait_ref = bound_predicate.rebind(data.trait_ref);
-                debug!(?trait_ref);
+                let trait_pred = bound_predicate.rebind(data);
+                debug!(?trait_pred);
 
                 if let Err(e) = predicate.error_reported() {
                     return e;
                 }
 
-                if let Err(guar) = self.tcx.ensure().coherent_trait(trait_ref.def_id()) {
+                if let Err(guar) = self.tcx.ensure().coherent_trait(trait_pred.def_id()) {
                     // Avoid bogus "type annotations needed `Foo: Bar`" errors on `impl Bar for Foo` in case
                     // other `Foo` impls are incoherent.
                     return guar;
@@ -200,13 +200,13 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                 // avoid inundating the user with unnecessary errors, but we now
                 // check upstream for type errors and don't add the obligations to
                 // begin with in those cases.
-                if self.tcx.is_lang_item(trait_ref.def_id(), LangItem::Sized) {
+                if self.tcx.is_lang_item(trait_pred.def_id(), LangItem::Sized) {
                     match self.tainted_by_errors() {
                         None => {
                             let err = self.emit_inference_failure_err(
                                 obligation.cause.body_id,
                                 span,
-                                trait_ref.self_ty().skip_binder().into(),
+                                trait_pred.self_ty().skip_binder().into(),
                                 TypeAnnotationNeeded::E0282,
                                 false,
                             );
@@ -251,10 +251,14 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
 
                 let mut ambiguities = compute_applicable_impls_for_diagnostics(
                     self.infcx,
-                    &obligation.with(self.tcx, trait_ref),
+                    &obligation.with(self.tcx, trait_pred),
                 );
-                let has_non_region_infer =
-                    trait_ref.skip_binder().args.types().any(|t| !t.is_ty_or_numeric_infer());
+                let has_non_region_infer = trait_pred
+                    .skip_binder()
+                    .trait_ref
+                    .args
+                    .types()
+                    .any(|t| !t.is_ty_or_numeric_infer());
                 // It doesn't make sense to talk about applicable impls if there are more than a
                 // handful of them. If there are a lot of them, but only a few of them have no type
                 // params, we only show those, as they are more likely to be useful/intended.
@@ -294,7 +298,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                     if impl_candidates.len() < 40 {
                         self.report_similar_impl_candidates(
                             impl_candidates.as_slice(),
-                            trait_ref,
+                            trait_pred,
                             obligation.cause.body_id,
                             &mut err,
                             false,
@@ -306,7 +310,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                 if let ObligationCauseCode::WhereClause(def_id, _)
                 | ObligationCauseCode::WhereClauseInExpr(def_id, ..) = *obligation.cause.code()
                 {
-                    self.suggest_fully_qualified_path(&mut err, def_id, span, trait_ref.def_id());
+                    self.suggest_fully_qualified_path(&mut err, def_id, span, trait_pred.def_id());
                 }
 
                 if let Some(ty::GenericArgKind::Type(_)) = arg.map(|arg| arg.unpack())
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
index 6076c999086..c40ba330845 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
@@ -23,9 +23,7 @@ use rustc_middle::ty::print::{
     FmtPrinter, Print, PrintPolyTraitPredicateExt, PrintTraitPredicateExt as _,
     PrintTraitRefExt as _, with_forced_trimmed_paths,
 };
-use rustc_middle::ty::{
-    self, ToPolyTraitRef, TraitRef, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, Upcast,
-};
+use rustc_middle::ty::{self, TraitRef, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, Upcast};
 use rustc_middle::{bug, span_bug};
 use rustc_span::{BytePos, DUMMY_SP, STDLIB_STABLE_CRATES, Span, Symbol, sym};
 use tracing::{debug, instrument};
@@ -155,12 +153,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                             (leaf_trait_predicate, &obligation)
                         };
 
-                        let main_trait_ref = main_trait_predicate.to_poly_trait_ref();
-                        let leaf_trait_ref = leaf_trait_predicate.to_poly_trait_ref();
-
                         if let Some(guar) = self.emit_specialized_closure_kind_error(
                             &obligation,
-                            leaf_trait_ref,
+                            leaf_trait_predicate,
                         ) {
                             return guar;
                         }
@@ -202,14 +197,14 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                         } = self.on_unimplemented_note(main_trait_predicate, main_obligation, &mut long_ty_file);
 
                         let have_alt_message = message.is_some() || label.is_some();
-                        let is_try_conversion = self.is_try_conversion(span, main_trait_ref.def_id());
+                        let is_try_conversion = self.is_try_conversion(span, main_trait_predicate.def_id());
                         let is_unsize =
-                            self.tcx.is_lang_item(leaf_trait_ref.def_id(), LangItem::Unsize);
+                            self.tcx.is_lang_item(leaf_trait_predicate.def_id(), LangItem::Unsize);
                         let (message, notes, append_const_msg) = if is_try_conversion {
                             (
                                 Some(format!(
                                     "`?` couldn't convert the error to `{}`",
-                                    main_trait_ref.skip_binder().self_ty(),
+                                    main_trait_predicate.skip_binder().self_ty(),
                                 )),
                                 vec![
                                     "the question mark operation (`?`) implicitly performs a \
@@ -230,12 +225,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                             post_message,
                         );
 
-                        let (err_msg, safe_transmute_explanation) = if self.tcx.is_lang_item(main_trait_ref.def_id(), LangItem::TransmuteTrait)
+                        let (err_msg, safe_transmute_explanation) = if self.tcx.is_lang_item(main_trait_predicate.def_id(), LangItem::TransmuteTrait)
                         {
                             // Recompute the safe transmute reason and use that for the error reporting
                             match self.get_safe_transmute_error_and_reason(
                                 obligation.clone(),
-                                main_trait_ref,
+                                main_trait_predicate,
                                 span,
                             ) {
                                 GetSafeTransmuteErrorAndReason::Silent => {
@@ -266,7 +261,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                         }
                         let mut suggested = false;
                         if is_try_conversion {
-                            suggested = self.try_conversion_context(&obligation, main_trait_ref.skip_binder(), &mut err);
+                            suggested = self.try_conversion_context(&obligation, main_trait_predicate, &mut err);
                         }
 
                         if is_try_conversion && let Some(ret_span) = self.return_type_span(&obligation) {
@@ -274,12 +269,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                                 ret_span,
                                 format!(
                                     "expected `{}` because of this",
-                                    main_trait_ref.skip_binder().self_ty()
+                                    main_trait_predicate.skip_binder().self_ty()
                                 ),
                             );
                         }
 
-                        if tcx.is_lang_item(leaf_trait_ref.def_id(), LangItem::Tuple) {
+                        if tcx.is_lang_item(leaf_trait_predicate.def_id(), LangItem::Tuple) {
                             self.add_tuple_trait_message(
                                 obligation.cause.code().peel_derives(),
                                 &mut err,
@@ -319,7 +314,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                             // If it has a custom `#[rustc_on_unimplemented]`
                             // error message, let's display it as the label!
                             err.span_label(span, s);
-                            if !matches!(leaf_trait_ref.skip_binder().self_ty().kind(), ty::Param(_)) {
+                            if !matches!(leaf_trait_predicate.skip_binder().self_ty().kind(), ty::Param(_)) {
                                 // When the self type is a type param We don't need to "the trait
                                 // `std::marker::Sized` is not implemented for `T`" as we will point
                                 // at the type param with a label to suggest constraining it.
@@ -339,7 +334,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                         if let ObligationCauseCode::Coercion { source, target } =
                             *obligation.cause.code().peel_derives()
                         {
-                            if self.tcx.is_lang_item(leaf_trait_ref.def_id(), LangItem::Sized) {
+                            if self.tcx.is_lang_item(leaf_trait_predicate.def_id(), LangItem::Sized) {
                                 self.suggest_borrowing_for_object_cast(
                                     &mut err,
                                     root_obligation,
@@ -368,7 +363,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                             err.span_label(tcx.def_span(body), s);
                         }
 
-                        self.suggest_floating_point_literal(&obligation, &mut err, leaf_trait_ref);
+                        self.suggest_floating_point_literal(&obligation, &mut err, leaf_trait_predicate);
                         self.suggest_dereferencing_index(&obligation, &mut err, leaf_trait_predicate);
                         suggested |= self.suggest_dereferences(&obligation, &mut err, leaf_trait_predicate);
                         suggested |= self.suggest_fn_call(&obligation, &mut err, leaf_trait_predicate);
@@ -376,7 +371,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                         suggested = if let &[cand] = &impl_candidates[..] {
                             let cand = cand.trait_ref;
                             if let (ty::FnPtr(..), ty::FnDef(..)) =
-                                (cand.self_ty().kind(), main_trait_ref.self_ty().skip_binder().kind())
+                                (cand.self_ty().kind(), main_trait_predicate.self_ty().skip_binder().kind())
                             {
                                 // Wrap method receivers and `&`-references in parens
                                 let suggestion = if self.tcx.sess.source_map().span_look_ahead(span, ".", Some(50)).is_some() {
@@ -423,11 +418,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                             span,
                             leaf_trait_predicate,
                         );
-                        self.note_version_mismatch(&mut err, leaf_trait_ref);
+                        self.note_version_mismatch(&mut err, leaf_trait_predicate);
                         self.suggest_remove_await(&obligation, &mut err);
                         self.suggest_derive(&obligation, &mut err, leaf_trait_predicate);
 
-                        if tcx.is_lang_item(leaf_trait_ref.def_id(), LangItem::Try) {
+                        if tcx.is_lang_item(leaf_trait_predicate.def_id(), LangItem::Try) {
                             self.suggest_await_before_try(
                                 &mut err,
                                 &obligation,
@@ -455,9 +450,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                             );
                         }
 
-                        let is_fn_trait = tcx.is_fn_trait(leaf_trait_ref.def_id());
+                        let is_fn_trait = tcx.is_fn_trait(leaf_trait_predicate.def_id());
                         let is_target_feature_fn = if let ty::FnDef(def_id, _) =
-                            *leaf_trait_ref.skip_binder().self_ty().kind()
+                            *leaf_trait_predicate.skip_binder().self_ty().kind()
                         {
                             !self.tcx.codegen_fn_attrs(def_id).target_features.is_empty()
                         } else {
@@ -509,7 +504,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                         }
 
                         self.explain_hrtb_projection(&mut err, leaf_trait_predicate, obligation.param_env, &obligation.cause);
-                        self.suggest_desugaring_async_fn_in_trait(&mut err, main_trait_ref);
+                        self.suggest_desugaring_async_fn_in_trait(&mut err, main_trait_predicate);
 
                         // Return early if the trait is Debug or Display and the invocation
                         // originates within a standard library macro, because the output
@@ -527,7 +522,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
 
                         if in_std_macro
                             && matches!(
-                                self.tcx.get_diagnostic_name(leaf_trait_ref.def_id()),
+                                self.tcx.get_diagnostic_name(leaf_trait_predicate.def_id()),
                                 Some(sym::Debug | sym::Display)
                             )
                         {
@@ -785,21 +780,22 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
     fn emit_specialized_closure_kind_error(
         &self,
         obligation: &PredicateObligation<'tcx>,
-        mut trait_ref: ty::PolyTraitRef<'tcx>,
+        mut trait_pred: ty::PolyTraitPredicate<'tcx>,
     ) -> Option<ErrorGuaranteed> {
         // If `AsyncFnKindHelper` is not implemented, that means that the closure kind
         // doesn't extend the goal kind. This is worth reporting, but we can only do so
         // if we actually know which closure this goal comes from, so look at the cause
         // to see if we can extract that information.
-        if self.tcx.is_lang_item(trait_ref.def_id(), LangItem::AsyncFnKindHelper)
-            && let Some(found_kind) = trait_ref.skip_binder().args.type_at(0).to_opt_closure_kind()
+        if self.tcx.is_lang_item(trait_pred.def_id(), LangItem::AsyncFnKindHelper)
+            && let Some(found_kind) =
+                trait_pred.skip_binder().trait_ref.args.type_at(0).to_opt_closure_kind()
             && let Some(expected_kind) =
-                trait_ref.skip_binder().args.type_at(1).to_opt_closure_kind()
+                trait_pred.skip_binder().trait_ref.args.type_at(1).to_opt_closure_kind()
             && !found_kind.extends(expected_kind)
         {
             if let Some((_, Some(parent))) = obligation.cause.code().parent_with_predicate() {
                 // If we have a derived obligation, then the parent will be a `AsyncFn*` goal.
-                trait_ref = parent.to_poly_trait_ref();
+                trait_pred = parent;
             } else if let &ObligationCauseCode::FunctionArg { arg_hir_id, .. } =
                 obligation.cause.code()
                 && let Some(typeck_results) = &self.typeck_results
@@ -820,9 +816,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             }
         }
 
-        let self_ty = trait_ref.self_ty().skip_binder();
+        let self_ty = trait_pred.self_ty().skip_binder();
 
-        if let Some(expected_kind) = self.tcx.fn_trait_kind_from_def_id(trait_ref.def_id()) {
+        if let Some(expected_kind) = self.tcx.fn_trait_kind_from_def_id(trait_pred.def_id()) {
             let (closure_def_id, found_args, by_ref_captures) = match *self_ty.kind() {
                 ty::Closure(def_id, args) => {
                     (def_id, args.as_closure().sig().map_bound(|sig| sig.inputs()[0]), None)
@@ -837,7 +833,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                 _ => return None,
             };
 
-            let expected_args = trait_ref.map_bound(|trait_ref| trait_ref.args.type_at(1));
+            let expected_args =
+                trait_pred.map_bound(|trait_pred| trait_pred.trait_ref.args.type_at(1));
 
             // Verify that the arguments are compatible. If the signature is
             // mismatched, then we have a totally different error to report.
@@ -909,7 +906,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
     fn try_conversion_context(
         &self,
         obligation: &PredicateObligation<'tcx>,
-        trait_ref: ty::TraitRef<'tcx>,
+        trait_pred: ty::PolyTraitPredicate<'tcx>,
         err: &mut Diag<'_>,
     ) -> bool {
         let span = obligation.cause.span;
@@ -953,8 +950,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         if !self.tcx.is_diagnostic_item(sym::FromResidual, y.def_id()) {
             return false;
         }
-        let self_ty = trait_ref.self_ty();
-        let found_ty = trait_ref.args.get(1).and_then(|a| a.as_type());
+        let self_ty = trait_pred.skip_binder().self_ty();
+        let found_ty = trait_pred.skip_binder().trait_ref.args.get(1).and_then(|a| a.as_type());
 
         let mut prev_ty = self.resolve_vars_if_possible(
             typeck.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(self.tcx)),
@@ -1223,18 +1220,18 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         goal: ty::TraitPredicate<'tcx>,
         assumption: ty::PolyTraitPredicate<'tcx>,
     ) -> bool {
+        // Fast path
         if goal.polarity != assumption.polarity() {
             return false;
         }
 
-        let trait_goal = goal.trait_ref;
         let trait_assumption = self.instantiate_binder_with_fresh_vars(
             DUMMY_SP,
             infer::BoundRegionConversionTime::HigherRankedType,
-            assumption.to_poly_trait_ref(),
+            assumption,
         );
 
-        self.can_eq(ty::ParamEnv::empty(), trait_goal, trait_assumption)
+        self.can_eq(ty::ParamEnv::empty(), goal.trait_ref, trait_assumption.trait_ref)
     }
 
     fn can_match_projection(
@@ -1682,7 +1679,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
     pub(super) fn report_similar_impl_candidates(
         &self,
         impl_candidates: &[ImplCandidate<'tcx>],
-        trait_ref: ty::PolyTraitRef<'tcx>,
+        trait_pred: ty::PolyTraitPredicate<'tcx>,
         body_def_id: LocalDefId,
         err: &mut Diag<'_>,
         other: bool,
@@ -1727,7 +1724,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         // We'll check for the case where the reason for the mismatch is that the trait comes from
         // one crate version and the type comes from another crate version, even though they both
         // are from the same crate.
-        let trait_def_id = trait_ref.def_id();
+        let trait_def_id = trait_pred.def_id();
         let trait_name = self.tcx.item_name(trait_def_id);
         let crate_name = self.tcx.crate_name(trait_def_id.krate);
         if let Some(other_trait_def_id) = self.tcx.all_traits().find(|def_id| {
@@ -1739,7 +1736,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             // different crate `DefId`. We highlight the traits.
 
             let found_type =
-                if let ty::Adt(def, _) = trait_ref.self_ty().skip_binder().peel_refs().kind() {
+                if let ty::Adt(def, _) = trait_pred.self_ty().skip_binder().peel_refs().kind() {
                     Some(def.did())
                 } else {
                     None
@@ -1836,7 +1833,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             if self.probe(|_| {
                 let ocx = ObligationCtxt::new(self);
 
-                self.enter_forall(trait_ref, |obligation_trait_ref| {
+                self.enter_forall(trait_pred, |obligation_trait_ref| {
                     let impl_args = self.fresh_args_for_item(DUMMY_SP, single.impl_def_id);
                     let impl_trait_ref = ocx.normalize(
                         &ObligationCause::dummy(),
@@ -1864,7 +1861,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
 
                     let mut terrs = vec![];
                     for (obligation_arg, impl_arg) in
-                        std::iter::zip(obligation_trait_ref.args, impl_trait_ref.args)
+                        std::iter::zip(obligation_trait_ref.trait_ref.args, impl_trait_ref.args)
                     {
                         if (obligation_arg, impl_arg).references_error() {
                             return false;
@@ -1906,8 +1903,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                     }
 
                     let traits = self.cmp_traits(
-                        obligation_trait_ref.def_id,
-                        &obligation_trait_ref.args[1..],
+                        obligation_trait_ref.def_id(),
+                        &obligation_trait_ref.trait_ref.args[1..],
                         impl_trait_ref.def_id,
                         &impl_trait_ref.args[1..],
                     );
@@ -1991,7 +1988,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             }
             if let &[cand] = &candidates[..] {
                 let (desc, mention_castable) =
-                    match (cand.self_ty().kind(), trait_ref.self_ty().skip_binder().kind()) {
+                    match (cand.self_ty().kind(), trait_pred.self_ty().skip_binder().kind()) {
                         (ty::FnPtr(..), ty::FnDef(..)) => {
                             (" implemented for fn pointer `", ", cast using `as`")
                         }
@@ -2055,7 +2052,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             .filter(|cand| !self.tcx.do_not_recommend_impl(cand.impl_def_id))
             .collect::<Vec<_>>();
 
-        let def_id = trait_ref.def_id();
+        let def_id = trait_pred.def_id();
         if impl_candidates.is_empty() {
             if self.tcx.trait_is_auto(def_id)
                 || self.tcx.lang_items().iter().any(|(_, id)| id == def_id)
@@ -2132,11 +2129,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             && !self.tcx.trait_is_auto(def_id)
             && !self.tcx.lang_items().iter().any(|(_, id)| id == def_id)
         {
-            let trait_ref = trait_pred.to_poly_trait_ref();
             let impl_candidates = self.find_similar_impl_candidates(trait_pred);
             self.report_similar_impl_candidates(
                 &impl_candidates,
-                trait_ref,
+                trait_pred,
                 body_def_id,
                 err,
                 true,
@@ -2173,12 +2169,16 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
     /// If the `Self` type of the unsatisfied trait `trait_ref` implements a trait
     /// with the same path as `trait_ref`, a help message about
     /// a probable version mismatch is added to `err`
-    fn note_version_mismatch(&self, err: &mut Diag<'_>, trait_ref: ty::PolyTraitRef<'tcx>) -> bool {
+    fn note_version_mismatch(
+        &self,
+        err: &mut Diag<'_>,
+        trait_pred: ty::PolyTraitPredicate<'tcx>,
+    ) -> bool {
         let get_trait_impls = |trait_def_id| {
             let mut trait_impls = vec![];
             self.tcx.for_each_relevant_impl(
                 trait_def_id,
-                trait_ref.skip_binder().self_ty(),
+                trait_pred.skip_binder().self_ty(),
                 |impl_def_id| {
                     trait_impls.push(impl_def_id);
                 },
@@ -2186,11 +2186,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             trait_impls
         };
 
-        let required_trait_path = self.tcx.def_path_str(trait_ref.def_id());
+        let required_trait_path = self.tcx.def_path_str(trait_pred.def_id());
         let traits_with_same_path: UnordSet<_> = self
             .tcx
             .visible_traits()
-            .filter(|trait_def_id| *trait_def_id != trait_ref.def_id())
+            .filter(|trait_def_id| *trait_def_id != trait_pred.def_id())
             .map(|trait_def_id| (self.tcx.def_path_str(trait_def_id), trait_def_id))
             .filter(|(p, _)| *p == required_trait_path)
             .collect();
@@ -2374,7 +2374,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
     fn get_safe_transmute_error_and_reason(
         &self,
         obligation: PredicateObligation<'tcx>,
-        trait_ref: ty::PolyTraitRef<'tcx>,
+        trait_pred: ty::PolyTraitPredicate<'tcx>,
         span: Span,
     ) -> GetSafeTransmuteErrorAndReason {
         use rustc_transmute::Answer;
@@ -2386,19 +2386,19 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             }
 
             // Erase regions because layout code doesn't particularly care about regions.
-            let trait_ref =
-                self.tcx.erase_regions(self.tcx.instantiate_bound_regions_with_erased(trait_ref));
+            let trait_pred =
+                self.tcx.erase_regions(self.tcx.instantiate_bound_regions_with_erased(trait_pred));
 
             let src_and_dst = rustc_transmute::Types {
-                dst: trait_ref.args.type_at(0),
-                src: trait_ref.args.type_at(1),
+                dst: trait_pred.trait_ref.args.type_at(0),
+                src: trait_pred.trait_ref.args.type_at(1),
             };
 
             let ocx = ObligationCtxt::new(self);
             let Ok(assume) = ocx.structurally_normalize_const(
                 &obligation.cause,
                 obligation.param_env,
-                trait_ref.args.const_at(2),
+                trait_pred.trait_ref.args.const_at(2),
             ) else {
                 self.dcx().span_delayed_bug(
                     span,
@@ -2417,8 +2417,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                 return GetSafeTransmuteErrorAndReason::Silent;
             };
 
-            let dst = trait_ref.args.type_at(0);
-            let src = trait_ref.args.type_at(1);
+            let dst = trait_pred.trait_ref.args.type_at(0);
+            let src = trait_pred.trait_ref.args.type_at(1);
             let err_msg = format!("`{src}` cannot be safely transmuted into `{dst}`");
 
             match rustc_transmute::TransmuteTypeEnv::new(self.infcx).is_transmutable(
@@ -2566,12 +2566,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                 trait_predicate.skip_binder().polarity,
             )
         {
-            self.add_help_message_for_fn_trait(
-                trait_predicate.to_poly_trait_ref(),
-                err,
-                implemented_kind,
-                params,
-            );
+            self.add_help_message_for_fn_trait(trait_predicate, err, implemented_kind, params);
         } else if !trait_predicate.has_non_region_infer()
             && self.predicate_can_apply(obligation.param_env, trait_predicate)
         {
@@ -2606,7 +2601,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             let impl_candidates = self.find_similar_impl_candidates(trait_predicate);
             if !self.report_similar_impl_candidates(
                 &impl_candidates,
-                trait_predicate.to_poly_trait_ref(),
+                trait_predicate,
                 body_def_id,
                 err,
                 true,
@@ -2623,7 +2618,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             self.suggest_convert_to_slice(
                 err,
                 obligation,
-                trait_predicate.to_poly_trait_ref(),
+                trait_predicate,
                 impl_candidates.as_slice(),
                 span,
             );
@@ -2634,7 +2629,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
 
     fn add_help_message_for_fn_trait(
         &self,
-        trait_ref: ty::PolyTraitRef<'tcx>,
+        trait_pred: ty::PolyTraitPredicate<'tcx>,
         err: &mut Diag<'_>,
         implemented_kind: ty::ClosureKind,
         params: ty::Binder<'tcx, Ty<'tcx>>,
@@ -2647,12 +2642,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         // to implement.
         let selected_kind = self
             .tcx
-            .fn_trait_kind_from_def_id(trait_ref.def_id())
+            .fn_trait_kind_from_def_id(trait_pred.def_id())
             .expect("expected to map DefId to ClosureKind");
         if !implemented_kind.extends(selected_kind) {
             err.note(format!(
                 "`{}` implements `{}`, but it must implement `{}`, which is more general",
-                trait_ref.skip_binder().self_ty(),
+                trait_pred.skip_binder().self_ty(),
                 implemented_kind,
                 selected_kind
             ));
@@ -2660,7 +2655,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
 
         // Note any argument mismatches
         let given_ty = params.skip_binder();
-        let expected_ty = trait_ref.skip_binder().args.type_at(1);
+        let expected_ty = trait_pred.skip_binder().trait_ref.args.type_at(1);
         if let ty::Tuple(given) = given_ty.kind()
             && let ty::Tuple(expected) = expected_ty.kind()
         {
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs
index 2d248d00066..2d932e36470 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs
@@ -10,7 +10,7 @@ use rustc_hir::{AttrArgs, AttrKind, Attribute};
 use rustc_macros::LintDiagnostic;
 use rustc_middle::bug;
 use rustc_middle::ty::print::PrintTraitRefExt as _;
-use rustc_middle::ty::{self, GenericArgsRef, GenericParamDefKind, ToPolyTraitRef, TyCtxt};
+use rustc_middle::ty::{self, GenericArgsRef, GenericParamDefKind, TyCtxt};
 use rustc_parse_format::{ParseMode, Parser, Piece, Position};
 use rustc_session::lint::builtin::UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES;
 use rustc_span::{Span, Symbol, kw, sym};
@@ -42,18 +42,18 @@ static ALLOWED_FORMAT_SYMBOLS: &[Symbol] = &[
 impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     fn impl_similar_to(
         &self,
-        trait_ref: ty::PolyTraitRef<'tcx>,
+        trait_pred: ty::PolyTraitPredicate<'tcx>,
         obligation: &PredicateObligation<'tcx>,
     ) -> Option<(DefId, GenericArgsRef<'tcx>)> {
         let tcx = self.tcx;
         let param_env = obligation.param_env;
-        self.enter_forall(trait_ref, |trait_ref| {
-            let trait_self_ty = trait_ref.self_ty();
+        self.enter_forall(trait_pred, |trait_pred| {
+            let trait_self_ty = trait_pred.self_ty();
 
             let mut self_match_impls = vec![];
             let mut fuzzy_match_impls = vec![];
 
-            self.tcx.for_each_relevant_impl(trait_ref.def_id, trait_self_ty, |def_id| {
+            self.tcx.for_each_relevant_impl(trait_pred.def_id(), trait_self_ty, |def_id| {
                 let impl_args = self.fresh_args_for_item(obligation.cause.span, def_id);
                 let impl_trait_ref =
                     tcx.impl_trait_ref(def_id).unwrap().instantiate(tcx, impl_args);
@@ -64,7 +64,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                     self_match_impls.push((def_id, impl_args));
 
                     if iter::zip(
-                        trait_ref.args.types().skip(1),
+                        trait_pred.trait_ref.args.types().skip(1),
                         impl_trait_ref.args.types().skip(1),
                     )
                     .all(|(u, v)| self.fuzzy_match_tys(u, v, false).is_some())
@@ -117,7 +117,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         }
 
         let (def_id, args) = self
-            .impl_similar_to(trait_pred.to_poly_trait_ref(), obligation)
+            .impl_similar_to(trait_pred, obligation)
             .unwrap_or_else(|| (trait_pred.def_id(), trait_pred.skip_binder().trait_ref.args));
         let trait_pred = trait_pred.skip_binder();
 
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
index 9d85ca1dd4d..c2e73b732d3 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
@@ -32,9 +32,9 @@ use rustc_middle::ty::print::{
     with_forced_trimmed_paths, with_no_trimmed_paths,
 };
 use rustc_middle::ty::{
-    self, AdtKind, GenericArgs, InferTy, IsSuggestable, ToPolyTraitRef, Ty, TyCtxt, TypeFoldable,
-    TypeFolder, TypeSuperFoldable, TypeVisitableExt, TypeckResults, Upcast,
-    suggest_arbitrary_trait_bound, suggest_constraining_type_param,
+    self, AdtKind, GenericArgs, InferTy, IsSuggestable, Ty, TyCtxt, TypeFoldable, TypeFolder,
+    TypeSuperFoldable, TypeVisitableExt, TypeckResults, Upcast, suggest_arbitrary_trait_bound,
+    suggest_constraining_type_param,
 };
 use rustc_middle::{bug, span_bug};
 use rustc_span::def_id::LocalDefId;
@@ -218,15 +218,15 @@ pub fn suggest_restriction<'tcx, G: EmissionGuarantee>(
             (_, None) => predicate_constraint(hir_generics, trait_pred.upcast(tcx)),
             (None, Some((ident, []))) => (
                 ident.span.shrink_to_hi(),
-                format!(": {}", trait_pred.to_poly_trait_ref().print_trait_sugared()),
+                format!(": {}", trait_pred.print_modifiers_and_trait_path()),
             ),
             (_, Some((_, [.., bounds]))) => (
                 bounds.span().shrink_to_hi(),
-                format!(" + {}", trait_pred.to_poly_trait_ref().print_trait_sugared()),
+                format!(" + {}", trait_pred.print_modifiers_and_trait_path()),
             ),
             (Some(_), Some((_, []))) => (
                 hir_generics.span.shrink_to_hi(),
-                format!(": {}", trait_pred.to_poly_trait_ref().print_trait_sugared()),
+                format!(": {}", trait_pred.print_modifiers_and_trait_path()),
             ),
         };
 
@@ -3729,7 +3729,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         &self,
         obligation: &PredicateObligation<'tcx>,
         err: &mut Diag<'_>,
-        trait_ref: ty::PolyTraitRef<'tcx>,
+        trait_pred: ty::PolyTraitPredicate<'tcx>,
     ) {
         let rhs_span = match obligation.cause.code() {
             ObligationCauseCode::BinOp { rhs_span: Some(span), rhs_is_lit, .. } if *rhs_is_lit => {
@@ -3737,8 +3737,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             }
             _ => return,
         };
-        if let ty::Float(_) = trait_ref.skip_binder().self_ty().kind()
-            && let ty::Infer(InferTy::IntVar(_)) = trait_ref.skip_binder().args.type_at(1).kind()
+        if let ty::Float(_) = trait_pred.skip_binder().self_ty().kind()
+            && let ty::Infer(InferTy::IntVar(_)) =
+                trait_pred.skip_binder().trait_ref.args.type_at(1).kind()
         {
             err.span_suggestion_verbose(
                 rhs_span.shrink_to_hi(),
@@ -4448,7 +4449,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         &self,
         err: &mut Diag<'_>,
         obligation: &PredicateObligation<'tcx>,
-        trait_ref: ty::PolyTraitRef<'tcx>,
+        trait_pred: ty::PolyTraitPredicate<'tcx>,
         candidate_impls: &[ImplCandidate<'tcx>],
         span: Span,
     ) {
@@ -4464,7 +4465,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         // 1. `[T; _]` (array of T)
         // 2. `&[T; _]` (reference to array of T)
         // 3. `&mut [T; _]` (mutable reference to array of T)
-        let (element_ty, mut mutability) = match *trait_ref.skip_binder().self_ty().kind() {
+        let (element_ty, mut mutability) = match *trait_pred.skip_binder().self_ty().kind() {
             ty::Array(element_ty, _) => (element_ty, None),
 
             ty::Ref(_, pointee_ty, mutability) => match *pointee_ty.kind() {
@@ -4620,14 +4621,14 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
     pub(super) fn suggest_desugaring_async_fn_in_trait(
         &self,
         err: &mut Diag<'_>,
-        trait_ref: ty::PolyTraitRef<'tcx>,
+        trait_pred: ty::PolyTraitPredicate<'tcx>,
     ) {
         // Don't suggest if RTN is active -- we should prefer a where-clause bound instead.
         if self.tcx.features().return_type_notation() {
             return;
         }
 
-        let trait_def_id = trait_ref.def_id();
+        let trait_def_id = trait_pred.def_id();
 
         // Only suggest specifying auto traits
         if !self.tcx.trait_is_auto(trait_def_id) {
@@ -4635,7 +4636,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         }
 
         // Look for an RPITIT
-        let ty::Alias(ty::Projection, alias_ty) = trait_ref.self_ty().skip_binder().kind() else {
+        let ty::Alias(ty::Projection, alias_ty) = trait_pred.self_ty().skip_binder().kind() else {
             return;
         };
         let Some(ty::ImplTraitInTraitData::Trait { fn_def_id, opaque_def_id }) =
diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
index 968dc631e50..b370f802052 100644
--- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
@@ -16,7 +16,7 @@ use rustc_infer::traits::{
     Obligation, ObligationCause, PolyTraitObligation, PredicateObligations, SelectionError,
 };
 use rustc_middle::ty::fast_reject::DeepRejectCtxt;
-use rustc_middle::ty::{self, ToPolyTraitRef, Ty, TypeVisitableExt, TypingMode};
+use rustc_middle::ty::{self, Ty, TypeVisitableExt, TypingMode};
 use rustc_middle::{bug, span_bug};
 use rustc_type_ir::Interner;
 use tracing::{debug, instrument, trace};
@@ -186,10 +186,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                     }
 
                     selcx.infcx.probe(|_| {
+                        // We checked the polarity already
                         match selcx.match_normalize_trait_ref(
                             obligation,
                             placeholder_trait_predicate.trait_ref,
-                            bound.to_poly_trait_ref(),
+                            bound.map_bound(|pred| pred.trait_ref),
                         ) {
                             Ok(None) => {
                                 candidates.vec.push(ProjectionCandidate(idx));
diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
index 0ccb0fc0615..729ae3f2c2a 100644
--- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
@@ -16,7 +16,7 @@ use rustc_hir::lang_items::LangItem;
 use rustc_infer::infer::{DefineOpaqueTypes, HigherRankedType, InferOk};
 use rustc_infer::traits::ObligationCauseCode;
 use rustc_middle::traits::{BuiltinImplSource, SignatureMismatchData};
-use rustc_middle::ty::{self, GenericArgsRef, ToPolyTraitRef, Ty, TyCtxt, Upcast};
+use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, Upcast};
 use rustc_middle::{bug, span_bug};
 use rustc_span::def_id::DefId;
 use rustc_type_ir::elaborate;
@@ -458,8 +458,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         ensure_sufficient_stack(|| {
             let cause = obligation.derived_cause(ObligationCauseCode::BuiltinDerived);
 
-            let poly_trait_ref = obligation.predicate.to_poly_trait_ref();
-            let trait_ref = self.infcx.enter_forall_and_leak_universe(poly_trait_ref);
+            assert_eq!(obligation.predicate.polarity(), ty::PredicatePolarity::Positive);
+            let trait_ref =
+                self.infcx.enter_forall_and_leak_universe(obligation.predicate).trait_ref;
             let trait_obligations = self.impl_or_trait_obligations(
                 &cause,
                 obligation.recursion_depth + 1,
diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs
index 4ebeaf04611..8fb1588e60b 100644
--- a/library/core/src/num/f128.rs
+++ b/library/core/src/num/f128.rs
@@ -504,7 +504,6 @@ impl f128 {
     ///
     /// ```rust
     /// #![feature(f128)]
-    /// #![feature(float_next_up_down)]
     /// # // FIXME(f16_f128): remove when `eqtf2` is available
     /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
     ///
@@ -516,13 +515,15 @@ impl f128 {
     /// # }
     /// ```
     ///
+    /// This operation corresponds to IEEE-754 `nextUp`.
+    ///
     /// [`NEG_INFINITY`]: Self::NEG_INFINITY
     /// [`INFINITY`]: Self::INFINITY
     /// [`MIN`]: Self::MIN
     /// [`MAX`]: Self::MAX
     #[inline]
+    #[doc(alias = "nextUp")]
     #[unstable(feature = "f128", issue = "116909")]
-    // #[unstable(feature = "float_next_up_down", issue = "91399")]
     pub const fn next_up(self) -> Self {
         // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing
         // denormals to zero. This is in general unsound and unsupported, but here
@@ -558,7 +559,6 @@ impl f128 {
     ///
     /// ```rust
     /// #![feature(f128)]
-    /// #![feature(float_next_up_down)]
     /// # // FIXME(f16_f128): remove when `eqtf2` is available
     /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
     ///
@@ -570,13 +570,15 @@ impl f128 {
     /// # }
     /// ```
     ///
+    /// This operation corresponds to IEEE-754 `nextDown`.
+    ///
     /// [`NEG_INFINITY`]: Self::NEG_INFINITY
     /// [`INFINITY`]: Self::INFINITY
     /// [`MIN`]: Self::MIN
     /// [`MAX`]: Self::MAX
     #[inline]
+    #[doc(alias = "nextDown")]
     #[unstable(feature = "f128", issue = "116909")]
-    // #[unstable(feature = "float_next_up_down", issue = "91399")]
     pub const fn next_down(self) -> Self {
         // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing
         // denormals to zero. This is in general unsound and unsupported, but here
diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs
index c82f0d7cd4a..8c2af74b8f8 100644
--- a/library/core/src/num/f16.rs
+++ b/library/core/src/num/f16.rs
@@ -497,7 +497,6 @@ impl f16 {
     ///
     /// ```rust
     /// #![feature(f16)]
-    /// #![feature(float_next_up_down)]
     /// # // FIXME(f16_f128): ABI issues on MSVC
     /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
     ///
@@ -509,13 +508,15 @@ impl f16 {
     /// # }
     /// ```
     ///
+    /// This operation corresponds to IEEE-754 `nextUp`.
+    ///
     /// [`NEG_INFINITY`]: Self::NEG_INFINITY
     /// [`INFINITY`]: Self::INFINITY
     /// [`MIN`]: Self::MIN
     /// [`MAX`]: Self::MAX
     #[inline]
+    #[doc(alias = "nextUp")]
     #[unstable(feature = "f16", issue = "116909")]
-    // #[unstable(feature = "float_next_up_down", issue = "91399")]
     pub const fn next_up(self) -> Self {
         // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing
         // denormals to zero. This is in general unsound and unsupported, but here
@@ -551,7 +552,6 @@ impl f16 {
     ///
     /// ```rust
     /// #![feature(f16)]
-    /// #![feature(float_next_up_down)]
     /// # // FIXME(f16_f128): ABI issues on MSVC
     /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
     ///
@@ -563,13 +563,15 @@ impl f16 {
     /// # }
     /// ```
     ///
+    /// This operation corresponds to IEEE-754 `nextDown`.
+    ///
     /// [`NEG_INFINITY`]: Self::NEG_INFINITY
     /// [`INFINITY`]: Self::INFINITY
     /// [`MIN`]: Self::MIN
     /// [`MAX`]: Self::MAX
     #[inline]
+    #[doc(alias = "nextDown")]
     #[unstable(feature = "f16", issue = "116909")]
-    // #[unstable(feature = "float_next_up_down", issue = "91399")]
     pub const fn next_down(self) -> Self {
         // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing
         // denormals to zero. This is in general unsound and unsupported, but here
diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs
index 2b6adef65e9..817bedbd44f 100644
--- a/library/core/src/num/f32.rs
+++ b/library/core/src/num/f32.rs
@@ -726,7 +726,6 @@ impl f32 {
     /// is finite `x == x.next_up().next_down()` also holds.
     ///
     /// ```rust
-    /// #![feature(float_next_up_down)]
     /// // f32::EPSILON is the difference between 1.0 and the next number up.
     /// assert_eq!(1.0f32.next_up(), 1.0 + f32::EPSILON);
     /// // But not for most numbers.
@@ -734,12 +733,16 @@ impl f32 {
     /// assert_eq!(16777216f32.next_up(), 16777218.0);
     /// ```
     ///
+    /// This operation corresponds to IEEE-754 `nextUp`.
+    ///
     /// [`NEG_INFINITY`]: Self::NEG_INFINITY
     /// [`INFINITY`]: Self::INFINITY
     /// [`MIN`]: Self::MIN
     /// [`MAX`]: Self::MAX
     #[inline]
-    #[unstable(feature = "float_next_up_down", issue = "91399")]
+    #[doc(alias = "nextUp")]
+    #[stable(feature = "float_next_up_down", since = "CURRENT_RUSTC_VERSION")]
+    #[rustc_const_stable(feature = "float_next_up_down", since = "CURRENT_RUSTC_VERSION")]
     pub const fn next_up(self) -> Self {
         // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing
         // denormals to zero. This is in general unsound and unsupported, but here
@@ -774,7 +777,6 @@ impl f32 {
     /// is finite `x == x.next_down().next_up()` also holds.
     ///
     /// ```rust
-    /// #![feature(float_next_up_down)]
     /// let x = 1.0f32;
     /// // Clamp value into range [0, 1).
     /// let clamped = x.clamp(0.0, 1.0f32.next_down());
@@ -782,12 +784,16 @@ impl f32 {
     /// assert_eq!(clamped.next_up(), 1.0);
     /// ```
     ///
+    /// This operation corresponds to IEEE-754 `nextDown`.
+    ///
     /// [`NEG_INFINITY`]: Self::NEG_INFINITY
     /// [`INFINITY`]: Self::INFINITY
     /// [`MIN`]: Self::MIN
     /// [`MAX`]: Self::MAX
     #[inline]
-    #[unstable(feature = "float_next_up_down", issue = "91399")]
+    #[doc(alias = "nextDown")]
+    #[stable(feature = "float_next_up_down", since = "CURRENT_RUSTC_VERSION")]
+    #[rustc_const_stable(feature = "float_next_up_down", since = "CURRENT_RUSTC_VERSION")]
     pub const fn next_down(self) -> Self {
         // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing
         // denormals to zero. This is in general unsound and unsupported, but here
diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs
index 037b3afa6c4..1b0651a0def 100644
--- a/library/core/src/num/f64.rs
+++ b/library/core/src/num/f64.rs
@@ -743,7 +743,6 @@ impl f64 {
     /// is finite `x == x.next_up().next_down()` also holds.
     ///
     /// ```rust
-    /// #![feature(float_next_up_down)]
     /// // f64::EPSILON is the difference between 1.0 and the next number up.
     /// assert_eq!(1.0f64.next_up(), 1.0 + f64::EPSILON);
     /// // But not for most numbers.
@@ -751,12 +750,16 @@ impl f64 {
     /// assert_eq!(9007199254740992f64.next_up(), 9007199254740994.0);
     /// ```
     ///
+    /// This operation corresponds to IEEE-754 `nextUp`.
+    ///
     /// [`NEG_INFINITY`]: Self::NEG_INFINITY
     /// [`INFINITY`]: Self::INFINITY
     /// [`MIN`]: Self::MIN
     /// [`MAX`]: Self::MAX
     #[inline]
-    #[unstable(feature = "float_next_up_down", issue = "91399")]
+    #[doc(alias = "nextUp")]
+    #[stable(feature = "float_next_up_down", since = "CURRENT_RUSTC_VERSION")]
+    #[rustc_const_stable(feature = "float_next_up_down", since = "CURRENT_RUSTC_VERSION")]
     pub const fn next_up(self) -> Self {
         // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing
         // denormals to zero. This is in general unsound and unsupported, but here
@@ -791,7 +794,6 @@ impl f64 {
     /// is finite `x == x.next_down().next_up()` also holds.
     ///
     /// ```rust
-    /// #![feature(float_next_up_down)]
     /// let x = 1.0f64;
     /// // Clamp value into range [0, 1).
     /// let clamped = x.clamp(0.0, 1.0f64.next_down());
@@ -799,12 +801,16 @@ impl f64 {
     /// assert_eq!(clamped.next_up(), 1.0);
     /// ```
     ///
+    /// This operation corresponds to IEEE-754 `nextDown`.
+    ///
     /// [`NEG_INFINITY`]: Self::NEG_INFINITY
     /// [`INFINITY`]: Self::INFINITY
     /// [`MIN`]: Self::MIN
     /// [`MAX`]: Self::MAX
     #[inline]
-    #[unstable(feature = "float_next_up_down", issue = "91399")]
+    #[doc(alias = "nextDown")]
+    #[stable(feature = "float_next_up_down", since = "CURRENT_RUSTC_VERSION")]
+    #[rustc_const_stable(feature = "float_next_up_down", since = "CURRENT_RUSTC_VERSION")]
     pub const fn next_down(self) -> Self {
         // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing
         // denormals to zero. This is in general unsound and unsupported, but here
diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs
index 7912f969bbd..231c8712ebd 100644
--- a/library/std/src/io/mod.rs
+++ b/library/std/src/io/mod.rs
@@ -330,6 +330,7 @@ pub use self::{
 };
 use crate::mem::take;
 use crate::ops::{Deref, DerefMut};
+use crate::sys::anonymous_pipe::{AnonPipe, pipe as pipe_inner};
 use crate::{cmp, fmt, slice, str, sys};
 
 mod buffered;
@@ -3250,3 +3251,251 @@ impl<B: BufRead> Iterator for Lines<B> {
         }
     }
 }
+
+/// Create anonymous pipe that is close-on-exec and blocking.
+///
+/// # Behavior
+///
+/// A pipe is a synchronous, unidirectional data channel between two or more processes, like an
+/// interprocess [`mpsc`](crate::sync::mpsc) provided by the OS. In particular:
+///
+/// * A read on a [`PipeReader`] blocks until the pipe is non-empty.
+/// * A write on a [`PipeWriter`] blocks when the pipe is full.
+/// * When all copies of a [`PipeWriter`] are closed, a read on the corresponding [`PipeReader`]
+///   returns EOF.
+/// * [`PipeReader`] can be shared, but only one process will consume the data in the pipe.
+///
+/// # Capacity
+///
+/// Pipe capacity is platform dependent. To quote the Linux [man page]:
+///
+/// > Different implementations have different limits for the pipe capacity. Applications should
+/// > not rely on a particular capacity: an application should be designed so that a reading process
+/// > consumes data as soon as it is available, so that a writing process does not remain blocked.
+///
+/// # Examples
+///
+/// ```no_run
+/// #![feature(anonymous_pipe)]
+/// # #[cfg(miri)] fn main() {}
+/// # #[cfg(not(miri))]
+/// # fn main() -> std::io::Result<()> {
+/// # use std::process::Command;
+/// # use std::io::{Read, Write};
+/// let (ping_rx, mut ping_tx) = std::io::pipe()?;
+/// let (mut pong_rx, pong_tx) = std::io::pipe()?;
+///
+/// // Spawn a process that echoes its input.
+/// let mut echo_server = Command::new("cat").stdin(ping_rx).stdout(pong_tx).spawn()?;
+///
+/// ping_tx.write_all(b"hello")?;
+/// // Close to unblock echo_server's reader.
+/// drop(ping_tx);
+///
+/// let mut buf = String::new();
+/// // Block until echo_server's writer is closed.
+/// pong_rx.read_to_string(&mut buf)?;
+/// assert_eq!(&buf, "hello");
+///
+/// echo_server.wait()?;
+/// # Ok(())
+/// # }
+/// ```
+/// [pipe]: https://man7.org/linux/man-pages/man2/pipe.2.html
+/// [CreatePipe]: https://learn.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-createpipe
+/// [man page]: https://man7.org/linux/man-pages/man7/pipe.7.html
+#[unstable(feature = "anonymous_pipe", issue = "127154")]
+#[inline]
+pub fn pipe() -> Result<(PipeReader, PipeWriter)> {
+    pipe_inner().map(|(reader, writer)| (PipeReader(reader), PipeWriter(writer)))
+}
+
+/// Read end of the anonymous pipe.
+#[unstable(feature = "anonymous_pipe", issue = "127154")]
+#[derive(Debug)]
+pub struct PipeReader(pub(crate) AnonPipe);
+
+/// Write end of the anonymous pipe.
+#[unstable(feature = "anonymous_pipe", issue = "127154")]
+#[derive(Debug)]
+pub struct PipeWriter(pub(crate) AnonPipe);
+
+impl PipeReader {
+    /// Create a new [`PipeReader`] instance that shares the same underlying file description.
+    ///
+    /// # Examples
+    ///
+    /// ```no_run
+    /// #![feature(anonymous_pipe)]
+    /// # #[cfg(miri)] fn main() {}
+    /// # #[cfg(not(miri))]
+    /// # fn main() -> std::io::Result<()> {
+    /// # use std::fs;
+    /// # use std::io::Write;
+    /// # use std::process::Command;
+    /// const NUM_SLOT: u8 = 2;
+    /// const NUM_PROC: u8 = 5;
+    /// const OUTPUT: &str = "work.txt";
+    ///
+    /// let mut jobs = vec![];
+    /// let (reader, mut writer) = std::io::pipe()?;
+    ///
+    /// // Write NUM_SLOT characters the pipe.
+    /// writer.write_all(&[b'|'; NUM_SLOT as usize])?;
+    ///
+    /// // Spawn several processes that read a character from the pipe, do some work, then
+    /// // write back to the pipe. When the pipe is empty, the processes block, so only
+    /// // NUM_SLOT processes can be working at any given time.
+    /// for _ in 0..NUM_PROC {
+    ///     jobs.push(
+    ///         Command::new("bash")
+    ///             .args(["-c",
+    ///                 &format!(
+    ///                      "read -n 1\n\
+    ///                       echo -n 'x' >> '{OUTPUT}'\n\
+    ///                       echo -n '|'",
+    ///                 ),
+    ///             ])
+    ///             .stdin(reader.try_clone()?)
+    ///             .stdout(writer.try_clone()?)
+    ///             .spawn()?,
+    ///     );
+    /// }
+    ///
+    /// // Wait for all jobs to finish.
+    /// for mut job in jobs {
+    ///     job.wait()?;
+    /// }
+    ///
+    /// // Check our work and clean up.
+    /// let xs = fs::read_to_string(OUTPUT)?;
+    /// fs::remove_file(OUTPUT)?;
+    /// assert_eq!(xs, "x".repeat(NUM_PROC.into()));
+    /// # Ok(())
+    /// # }
+    /// ```
+    #[unstable(feature = "anonymous_pipe", issue = "127154")]
+    pub fn try_clone(&self) -> Result<Self> {
+        self.0.try_clone().map(Self)
+    }
+}
+
+impl PipeWriter {
+    /// Create a new [`PipeWriter`] instance that shares the same underlying file description.
+    ///
+    /// # Examples
+    ///
+    /// ```no_run
+    /// #![feature(anonymous_pipe)]
+    /// # #[cfg(miri)] fn main() {}
+    /// # #[cfg(not(miri))]
+    /// # fn main() -> std::io::Result<()> {
+    /// # use std::process::Command;
+    /// # use std::io::Read;
+    /// let (mut reader, writer) = std::io::pipe()?;
+    ///
+    /// // Spawn a process that writes to stdout and stderr.
+    /// let mut peer = Command::new("bash")
+    ///     .args([
+    ///         "-c",
+    ///         "echo -n foo\n\
+    ///          echo -n bar >&2"
+    ///     ])
+    ///     .stdout(writer.try_clone()?)
+    ///     .stderr(writer)
+    ///     .spawn()?;
+    ///
+    /// // Read and check the result.
+    /// let mut msg = String::new();
+    /// reader.read_to_string(&mut msg)?;
+    /// assert_eq!(&msg, "foobar");
+    ///
+    /// peer.wait()?;
+    /// # Ok(())
+    /// # }
+    /// ```
+    #[unstable(feature = "anonymous_pipe", issue = "127154")]
+    pub fn try_clone(&self) -> Result<Self> {
+        self.0.try_clone().map(Self)
+    }
+}
+
+#[unstable(feature = "anonymous_pipe", issue = "127154")]
+impl Read for &PipeReader {
+    fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
+        self.0.read(buf)
+    }
+    fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> Result<usize> {
+        self.0.read_vectored(bufs)
+    }
+    #[inline]
+    fn is_read_vectored(&self) -> bool {
+        self.0.is_read_vectored()
+    }
+    fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize> {
+        self.0.read_to_end(buf)
+    }
+    fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> Result<()> {
+        self.0.read_buf(buf)
+    }
+}
+
+#[unstable(feature = "anonymous_pipe", issue = "127154")]
+impl Read for PipeReader {
+    fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
+        self.0.read(buf)
+    }
+    fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> Result<usize> {
+        self.0.read_vectored(bufs)
+    }
+    #[inline]
+    fn is_read_vectored(&self) -> bool {
+        self.0.is_read_vectored()
+    }
+    fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize> {
+        self.0.read_to_end(buf)
+    }
+    fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> Result<()> {
+        self.0.read_buf(buf)
+    }
+}
+
+#[unstable(feature = "anonymous_pipe", issue = "127154")]
+impl Write for &PipeWriter {
+    fn write(&mut self, buf: &[u8]) -> Result<usize> {
+        self.0.write(buf)
+    }
+    #[inline]
+    fn flush(&mut self) -> Result<()> {
+        Ok(())
+    }
+
+    fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> Result<usize> {
+        self.0.write_vectored(bufs)
+    }
+
+    #[inline]
+    fn is_write_vectored(&self) -> bool {
+        self.0.is_write_vectored()
+    }
+}
+
+#[unstable(feature = "anonymous_pipe", issue = "127154")]
+impl Write for PipeWriter {
+    fn write(&mut self, buf: &[u8]) -> Result<usize> {
+        self.0.write(buf)
+    }
+    #[inline]
+    fn flush(&mut self) -> Result<()> {
+        Ok(())
+    }
+
+    fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> Result<usize> {
+        self.0.write_vectored(bufs)
+    }
+
+    #[inline]
+    fn is_write_vectored(&self) -> bool {
+        self.0.is_write_vectored()
+    }
+}
diff --git a/library/std/src/io/tests.rs b/library/std/src/io/tests.rs
index 47cbb9614af..85098b3bb18 100644
--- a/library/std/src/io/tests.rs
+++ b/library/std/src/io/tests.rs
@@ -823,3 +823,20 @@ fn try_oom_error() {
     let io_err = io::Error::from(reserve_err);
     assert_eq!(io::ErrorKind::OutOfMemory, io_err.kind());
 }
+
+#[test]
+#[cfg(all(windows, unix, not(miri)))]
+fn pipe_creation_clone_and_rw() {
+    let (rx, tx) = std::io::pipe().unwrap();
+
+    tx.try_clone().unwrap().write_all(b"12345").unwrap();
+    drop(tx);
+
+    let mut rx2 = rx.try_clone().unwrap();
+    drop(rx);
+
+    let mut s = String::new();
+    rx2.read_to_string(&mut s).unwrap();
+    drop(rx2);
+    assert_eq!(s, "12345");
+}
diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs
index 5c12236617c..39f234e4ba6 100644
--- a/library/std/src/lib.rs
+++ b/library/std/src/lib.rs
@@ -333,7 +333,6 @@
 #![feature(extend_one)]
 #![feature(float_gamma)]
 #![feature(float_minimum_maximum)]
-#![feature(float_next_up_down)]
 #![feature(fmt_internals)]
 #![feature(hasher_prefixfree_extras)]
 #![feature(hashmap_internals)]
@@ -596,8 +595,6 @@ pub mod panic;
 #[unstable(feature = "pattern_type_macro", issue = "123646")]
 pub mod pat;
 pub mod path;
-#[unstable(feature = "anonymous_pipe", issue = "127154")]
-pub mod pipe;
 pub mod process;
 #[unstable(feature = "random", issue = "130703")]
 pub mod random;
diff --git a/library/std/src/pipe.rs b/library/std/src/pipe.rs
deleted file mode 100644
index 913c22588a7..00000000000
--- a/library/std/src/pipe.rs
+++ /dev/null
@@ -1,258 +0,0 @@
-//!  A cross-platform anonymous pipe.
-//!
-//! This module provides support for anonymous OS pipes, like [pipe] on Linux or [CreatePipe] on
-//! Windows.
-//!
-//! # Behavior
-//!
-//! A pipe is a synchronous, unidirectional data channel between two or more processes, like an
-//! interprocess [`mpsc`](crate::sync::mpsc) provided by the OS. In particular:
-//!
-//! * A read on a [`PipeReader`] blocks until the pipe is non-empty.
-//! * A write on a [`PipeWriter`] blocks when the pipe is full.
-//! * When all copies of a [`PipeWriter`] are closed, a read on the corresponding [`PipeReader`]
-//!   returns EOF.
-//! * [`PipeReader`] can be shared, but only one process will consume the data in the pipe.
-//!
-//! # Capacity
-//!
-//! Pipe capacity is platform dependent. To quote the Linux [man page]:
-//!
-//! > Different implementations have different limits for the pipe capacity. Applications should
-//! > not rely on a particular capacity: an application should be designed so that a reading process
-//! > consumes data as soon as it is available, so that a writing process does not remain blocked.
-//!
-//! # Examples
-//!
-//! ```no_run
-//! #![feature(anonymous_pipe)]
-//! # #[cfg(miri)] fn main() {}
-//! # #[cfg(not(miri))]
-//! # fn main() -> std::io::Result<()> {
-//! # use std::process::Command;
-//! # use std::io::{Read, Write};
-//! let (ping_rx, mut ping_tx) = std::pipe::pipe()?;
-//! let (mut pong_rx, pong_tx) = std::pipe::pipe()?;
-//!
-//! // Spawn a process that echoes its input.
-//! let mut echo_server = Command::new("cat").stdin(ping_rx).stdout(pong_tx).spawn()?;
-//!
-//! ping_tx.write_all(b"hello")?;
-//! // Close to unblock echo_server's reader.
-//! drop(ping_tx);
-//!
-//! let mut buf = String::new();
-//! // Block until echo_server's writer is closed.
-//! pong_rx.read_to_string(&mut buf)?;
-//! assert_eq!(&buf, "hello");
-//!
-//! echo_server.wait()?;
-//! # Ok(())
-//! # }
-//! ```
-//! [pipe]: https://man7.org/linux/man-pages/man2/pipe.2.html
-//! [CreatePipe]: https://learn.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-createpipe
-//! [man page]: https://man7.org/linux/man-pages/man7/pipe.7.html
-use crate::io;
-use crate::sys::anonymous_pipe::{AnonPipe, pipe as pipe_inner};
-
-/// Create anonymous pipe that is close-on-exec and blocking.
-///
-/// # Examples
-///
-/// See the [module-level](crate::pipe) documentation for examples.
-#[unstable(feature = "anonymous_pipe", issue = "127154")]
-#[inline]
-pub fn pipe() -> io::Result<(PipeReader, PipeWriter)> {
-    pipe_inner().map(|(reader, writer)| (PipeReader(reader), PipeWriter(writer)))
-}
-
-/// Read end of the anonymous pipe.
-#[unstable(feature = "anonymous_pipe", issue = "127154")]
-#[derive(Debug)]
-pub struct PipeReader(pub(crate) AnonPipe);
-
-/// Write end of the anonymous pipe.
-#[unstable(feature = "anonymous_pipe", issue = "127154")]
-#[derive(Debug)]
-pub struct PipeWriter(pub(crate) AnonPipe);
-
-impl PipeReader {
-    /// Create a new [`PipeReader`] instance that shares the same underlying file description.
-    ///
-    /// # Examples
-    ///
-    /// ```no_run
-    /// #![feature(anonymous_pipe)]
-    /// # #[cfg(miri)] fn main() {}
-    /// # #[cfg(not(miri))]
-    /// # fn main() -> std::io::Result<()> {
-    /// # use std::fs;
-    /// # use std::io::Write;
-    /// # use std::process::Command;
-    /// const NUM_SLOT: u8 = 2;
-    /// const NUM_PROC: u8 = 5;
-    /// const OUTPUT: &str = "work.txt";
-    ///
-    /// let mut jobs = vec![];
-    /// let (reader, mut writer) = std::pipe::pipe()?;
-    ///
-    /// // Write NUM_SLOT characters the pipe.
-    /// writer.write_all(&[b'|'; NUM_SLOT as usize])?;
-    ///
-    /// // Spawn several processes that read a character from the pipe, do some work, then
-    /// // write back to the pipe. When the pipe is empty, the processes block, so only
-    /// // NUM_SLOT processes can be working at any given time.
-    /// for _ in 0..NUM_PROC {
-    ///     jobs.push(
-    ///         Command::new("bash")
-    ///             .args(["-c",
-    ///                 &format!(
-    ///                      "read -n 1\n\
-    ///                       echo -n 'x' >> '{OUTPUT}'\n\
-    ///                       echo -n '|'",
-    ///                 ),
-    ///             ])
-    ///             .stdin(reader.try_clone()?)
-    ///             .stdout(writer.try_clone()?)
-    ///             .spawn()?,
-    ///     );
-    /// }
-    ///
-    /// // Wait for all jobs to finish.
-    /// for mut job in jobs {
-    ///     job.wait()?;
-    /// }
-    ///
-    /// // Check our work and clean up.
-    /// let xs = fs::read_to_string(OUTPUT)?;
-    /// fs::remove_file(OUTPUT)?;
-    /// assert_eq!(xs, "x".repeat(NUM_PROC.into()));
-    /// # Ok(())
-    /// # }
-    /// ```
-    #[unstable(feature = "anonymous_pipe", issue = "127154")]
-    pub fn try_clone(&self) -> io::Result<Self> {
-        self.0.try_clone().map(Self)
-    }
-}
-
-impl PipeWriter {
-    /// Create a new [`PipeWriter`] instance that shares the same underlying file description.
-    ///
-    /// # Examples
-    ///
-    /// ```no_run
-    /// #![feature(anonymous_pipe)]
-    /// # #[cfg(miri)] fn main() {}
-    /// # #[cfg(not(miri))]
-    /// # fn main() -> std::io::Result<()> {
-    /// # use std::process::Command;
-    /// # use std::io::Read;
-    /// let (mut reader, writer) = std::pipe::pipe()?;
-    ///
-    /// // Spawn a process that writes to stdout and stderr.
-    /// let mut peer = Command::new("bash")
-    ///     .args([
-    ///         "-c",
-    ///         "echo -n foo\n\
-    ///          echo -n bar >&2"
-    ///     ])
-    ///     .stdout(writer.try_clone()?)
-    ///     .stderr(writer)
-    ///     .spawn()?;
-    ///
-    /// // Read and check the result.
-    /// let mut msg = String::new();
-    /// reader.read_to_string(&mut msg)?;
-    /// assert_eq!(&msg, "foobar");
-    ///
-    /// peer.wait()?;
-    /// # Ok(())
-    /// # }
-    /// ```
-    #[unstable(feature = "anonymous_pipe", issue = "127154")]
-    pub fn try_clone(&self) -> io::Result<Self> {
-        self.0.try_clone().map(Self)
-    }
-}
-
-#[unstable(feature = "anonymous_pipe", issue = "127154")]
-impl io::Read for &PipeReader {
-    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
-        self.0.read(buf)
-    }
-    fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
-        self.0.read_vectored(bufs)
-    }
-    #[inline]
-    fn is_read_vectored(&self) -> bool {
-        self.0.is_read_vectored()
-    }
-    fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
-        self.0.read_to_end(buf)
-    }
-    fn read_buf(&mut self, buf: io::BorrowedCursor<'_>) -> io::Result<()> {
-        self.0.read_buf(buf)
-    }
-}
-
-#[unstable(feature = "anonymous_pipe", issue = "127154")]
-impl io::Read for PipeReader {
-    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
-        self.0.read(buf)
-    }
-    fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
-        self.0.read_vectored(bufs)
-    }
-    #[inline]
-    fn is_read_vectored(&self) -> bool {
-        self.0.is_read_vectored()
-    }
-    fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
-        self.0.read_to_end(buf)
-    }
-    fn read_buf(&mut self, buf: io::BorrowedCursor<'_>) -> io::Result<()> {
-        self.0.read_buf(buf)
-    }
-}
-
-#[unstable(feature = "anonymous_pipe", issue = "127154")]
-impl io::Write for &PipeWriter {
-    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
-        self.0.write(buf)
-    }
-    #[inline]
-    fn flush(&mut self) -> io::Result<()> {
-        Ok(())
-    }
-
-    fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
-        self.0.write_vectored(bufs)
-    }
-
-    #[inline]
-    fn is_write_vectored(&self) -> bool {
-        self.0.is_write_vectored()
-    }
-}
-
-#[unstable(feature = "anonymous_pipe", issue = "127154")]
-impl io::Write for PipeWriter {
-    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
-        self.0.write(buf)
-    }
-    #[inline]
-    fn flush(&mut self) -> io::Result<()> {
-        Ok(())
-    }
-
-    fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
-        self.0.write_vectored(bufs)
-    }
-
-    #[inline]
-    fn is_write_vectored(&self) -> bool {
-        self.0.is_write_vectored()
-    }
-}
diff --git a/library/std/src/pipe/tests.rs b/library/std/src/pipe/tests.rs
deleted file mode 100644
index 9c38e106787..00000000000
--- a/library/std/src/pipe/tests.rs
+++ /dev/null
@@ -1,19 +0,0 @@
-use crate::io::{Read, Write};
-use crate::pipe::pipe;
-
-#[test]
-#[cfg(all(windows, unix, not(miri)))]
-fn pipe_creation_clone_and_rw() {
-    let (rx, tx) = pipe().unwrap();
-
-    tx.try_clone().unwrap().write_all(b"12345").unwrap();
-    drop(tx);
-
-    let mut rx2 = rx.try_clone().unwrap();
-    drop(rx);
-
-    let mut s = String::new();
-    rx2.read_to_string(&mut s).unwrap();
-    drop(rx2);
-    assert_eq!(s, "12345");
-}
diff --git a/library/std/src/sys/anonymous_pipe/unix.rs b/library/std/src/sys/anonymous_pipe/unix.rs
index 9168024730e..9e398765634 100644
--- a/library/std/src/sys/anonymous_pipe/unix.rs
+++ b/library/std/src/sys/anonymous_pipe/unix.rs
@@ -1,6 +1,5 @@
-use crate::io;
+use crate::io::{self, PipeReader, PipeWriter};
 use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
-use crate::pipe::{PipeReader, PipeWriter};
 use crate::process::Stdio;
 use crate::sys::fd::FileDesc;
 use crate::sys::pipe::anon_pipe;
diff --git a/library/std/src/sys/anonymous_pipe/unsupported.rs b/library/std/src/sys/anonymous_pipe/unsupported.rs
index dd51e70315e..4e79ac9c21a 100644
--- a/library/std/src/sys/anonymous_pipe/unsupported.rs
+++ b/library/std/src/sys/anonymous_pipe/unsupported.rs
@@ -1,5 +1,4 @@
-use crate::io;
-use crate::pipe::{PipeReader, PipeWriter};
+use crate::io::{self, PipeReader, PipeWriter};
 use crate::process::Stdio;
 pub use crate::sys::pipe::AnonPipe;
 
diff --git a/library/std/src/sys/anonymous_pipe/windows.rs b/library/std/src/sys/anonymous_pipe/windows.rs
index a48198f8a81..eb7fa8ec1c9 100644
--- a/library/std/src/sys/anonymous_pipe/windows.rs
+++ b/library/std/src/sys/anonymous_pipe/windows.rs
@@ -1,12 +1,12 @@
+use crate::io::{self, PipeReader, PipeWriter};
 use crate::os::windows::io::{
     AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, IntoRawHandle, OwnedHandle, RawHandle,
 };
-use crate::pipe::{PipeReader, PipeWriter};
 use crate::process::Stdio;
+use crate::ptr;
 use crate::sys::c;
 use crate::sys::handle::Handle;
 use crate::sys_common::{FromInner, IntoInner};
-use crate::{io, ptr};
 
 pub type AnonPipe = Handle;
 
diff --git a/library/std/src/sys/pal/unix/kernel_copy.rs b/library/std/src/sys/pal/unix/kernel_copy.rs
index 36823a503b1..bbf29f32523 100644
--- a/library/std/src/sys/pal/unix/kernel_copy.rs
+++ b/library/std/src/sys/pal/unix/kernel_copy.rs
@@ -52,15 +52,14 @@ use crate::cmp::min;
 use crate::fs::{File, Metadata};
 use crate::io::copy::generic_copy;
 use crate::io::{
-    BufRead, BufReader, BufWriter, Error, Read, Result, StderrLock, StdinLock, StdoutLock, Take,
-    Write,
+    BufRead, BufReader, BufWriter, Error, PipeReader, PipeWriter, Read, Result, StderrLock,
+    StdinLock, StdoutLock, Take, Write,
 };
 use crate::mem::ManuallyDrop;
 use crate::net::TcpStream;
 use crate::os::unix::fs::FileTypeExt;
 use crate::os::unix::io::{AsRawFd, FromRawFd, RawFd};
 use crate::os::unix::net::UnixStream;
-use crate::pipe::{PipeReader, PipeWriter};
 use crate::process::{ChildStderr, ChildStdin, ChildStdout};
 use crate::ptr;
 use crate::sync::atomic::{AtomicBool, AtomicU8, Ordering};
diff --git a/library/std/tests/pipe_subprocess.rs b/library/std/tests/pipe_subprocess.rs
index 1535742a83a..df946cdcf2b 100644
--- a/library/std/tests/pipe_subprocess.rs
+++ b/library/std/tests/pipe_subprocess.rs
@@ -3,8 +3,7 @@
 fn main() {
     #[cfg(all(not(miri), any(unix, windows)))]
     {
-        use std::io::Read;
-        use std::pipe::pipe;
+        use std::io::{Read, pipe};
         use std::{env, process};
 
         if env::var("I_AM_THE_CHILD").is_ok() {
diff --git a/src/bootstrap/mk/Makefile.in b/src/bootstrap/mk/Makefile.in
index a1f38b9ac14..082b7431440 100644
--- a/src/bootstrap/mk/Makefile.in
+++ b/src/bootstrap/mk/Makefile.in
@@ -97,6 +97,11 @@ tidy:
 prepare:
 	$(Q)$(BOOTSTRAP) build --stage 2 --dry-run
 
+# Set of tests that represent around half of the time of the test suite.
+# Used to split tests across multiple CI runners.
+STAGE_2_TEST_SET1 := test --stage 2 --skip=compiler --skip=src
+STAGE_2_TEST_SET2 := test --stage 2 --skip=tests --skip=coverage-map --skip=coverage-run --skip=library --skip=tidyselftest
+
 ## MSVC native builders
 
 # this intentionally doesn't use `$(BOOTSTRAP)` so we can test the shebang on Windows
@@ -105,6 +110,10 @@ ci-msvc-py:
 ci-msvc-ps1:
 	$(Q)$(CFG_SRC_DIR)/x.ps1 test --stage 2 --skip tidy
 ci-msvc: ci-msvc-py ci-msvc-ps1
+ci-msvc-py-set1:
+	$(Q)$(CFG_SRC_DIR)/x.py $(STAGE_2_TEST_SET1)
+ci-msvc-ps1-set2:
+	$(Q)$(CFG_SRC_DIR)/x.ps1 $(STAGE_2_TEST_SET2)
 
 ## MingW native builders
 
@@ -112,9 +121,9 @@ ci-msvc: ci-msvc-py ci-msvc-ps1
 # Used to split tests across multiple CI runners.
 # Test both x and bootstrap entrypoints.
 ci-mingw-x:
-	$(Q)$(CFG_SRC_DIR)/x test --stage 2 --skip=compiler --skip=src
+	$(Q)$(CFG_SRC_DIR)/x $(STAGE_2_TEST_SET1)
 ci-mingw-bootstrap:
-	$(Q)$(BOOTSTRAP) test --stage 2 --skip=tests --skip=coverage-map --skip=coverage-run --skip=library --skip=tidyselftest
+	$(Q)$(BOOTSTRAP) $(STAGE_2_TEST_SET2)
 ci-mingw: ci-mingw-x ci-mingw-bootstrap
 
 .PHONY: dist
diff --git a/src/bootstrap/src/core/build_steps/clippy.rs b/src/bootstrap/src/core/build_steps/clippy.rs
index c3375b69961..fe8c89f7a53 100644
--- a/src/bootstrap/src/core/build_steps/clippy.rs
+++ b/src/bootstrap/src/core/build_steps/clippy.rs
@@ -334,6 +334,7 @@ lint_any!(
     CargoMiri, "src/tools/miri/cargo-miri", "cargo-miri";
     Clippy, "src/tools/clippy", "clippy";
     CollectLicenseMetadata, "src/tools/collect-license-metadata", "collect-license-metadata";
+    CodegenGcc, "compiler/rustc_codegen_gcc", "rustc-codegen-gcc";
     Compiletest, "src/tools/compiletest", "compiletest";
     CoverageDump, "src/tools/coverage-dump", "coverage-dump";
     Jsondocck, "src/tools/jsondocck", "jsondocck";
@@ -400,6 +401,12 @@ impl Step for CI {
             ],
             forbid: vec![],
         };
+        builder.ensure(Std {
+            target: self.target,
+            config: self.config.merge(&library_clippy_cfg),
+            crates: vec![],
+        });
+
         let compiler_clippy_cfg = LintConfig {
             allow: vec!["clippy::all".into()],
             warn: vec![],
@@ -419,16 +426,21 @@ impl Step for CI {
             ],
             forbid: vec![],
         };
-
-        builder.ensure(Std {
-            target: self.target,
-            config: self.config.merge(&library_clippy_cfg),
-            crates: vec![],
-        });
         builder.ensure(Rustc {
             target: self.target,
             config: self.config.merge(&compiler_clippy_cfg),
             crates: vec![],
         });
+
+        let rustc_codegen_gcc = LintConfig {
+            allow: vec![],
+            warn: vec![],
+            deny: vec!["warnings".into()],
+            forbid: vec![],
+        };
+        builder.ensure(CodegenGcc {
+            target: self.target,
+            config: self.config.merge(&rustc_codegen_gcc),
+        });
     }
 }
diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs
index afa409aa835..470e400243f 100644
--- a/src/bootstrap/src/core/build_steps/dist.rs
+++ b/src/bootstrap/src/core/build_steps/dist.rs
@@ -994,20 +994,24 @@ impl Step for PlainSourceTarball {
 
         // This is the set of root paths which will become part of the source package
         let src_files = [
+            // tidy-alphabetical-start
+            ".gitmodules",
+            "Cargo.lock",
+            "Cargo.toml",
+            "config.example.toml",
+            "configure",
+            "CONTRIBUTING.md",
             "COPYRIGHT",
             "LICENSE-APACHE",
+            "license-metadata.json",
             "LICENSE-MIT",
-            "CONTRIBUTING.md",
             "README.md",
             "RELEASES.md",
             "REUSE.toml",
-            "license-metadata.json",
-            "configure",
+            "x",
+            "x.ps1",
             "x.py",
-            "config.example.toml",
-            "Cargo.toml",
-            "Cargo.lock",
-            ".gitmodules",
+            // tidy-alphabetical-end
         ];
         let src_dirs = ["src", "compiler", "library", "tests", "LICENSES"];
 
diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs
index 17de1762427..b293ac4f351 100644
--- a/src/bootstrap/src/core/builder/mod.rs
+++ b/src/bootstrap/src/core/builder/mod.rs
@@ -900,6 +900,7 @@ impl<'a> Builder<'a> {
                 clippy::BuildManifest,
                 clippy::CargoMiri,
                 clippy::Clippy,
+                clippy::CodegenGcc,
                 clippy::CollectLicenseMetadata,
                 clippy::Compiletest,
                 clippy::CoverageDump,
diff --git a/src/ci/docker/README.md b/src/ci/docker/README.md
index 2f52ff5a99a..2b1de43c2f6 100644
--- a/src/ci/docker/README.md
+++ b/src/ci/docker/README.md
@@ -305,8 +305,6 @@ For targets: `mips-unknown-linux-gnu`
 - Path and misc options > Prefix directory = /x-tools/${CT\_TARGET}
 - Path and misc options > Use a mirror = ENABLE
 - Path and misc options > Base URL = https://ci-mirrors.rust-lang.org/rustc
-- Path and misc options > Patches origin = Bundled, then local
-- Path and misc options > Local patch directory = /tmp/patches
 - Target options > Target Architecture = mips
 - Target options > ABI = o32
 - Target options > Endianness = Big endian
@@ -327,8 +325,6 @@ For targets: `mipsel-unknown-linux-gnu`
 - Path and misc options > Prefix directory = /x-tools/${CT\_TARGET}
 - Path and misc options > Use a mirror = ENABLE
 - Path and misc options > Base URL = https://ci-mirrors.rust-lang.org/rustc
-- Path and misc options > Patches origin = Bundled, then local
-- Path and misc options > Local patch directory = /tmp/patches
 - Target options > Target Architecture = mips
 - Target options > ABI = o32
 - Target options > Endianness = Little endian
@@ -349,8 +345,6 @@ For targets: `mips64-unknown-linux-gnuabi64`
 - Path and misc options > Prefix directory = /x-tools/${CT\_TARGET}
 - Path and misc options > Use a mirror = ENABLE
 - Path and misc options > Base URL = https://ci-mirrors.rust-lang.org/rustc
-- Path and misc options > Patches origin = Bundled, then local
-- Path and misc options > Local patch directory = /tmp/patches
 - Target options > Target Architecture = mips
 - Target options > ABI = n64
 - Target options > Endianness = Big endian
@@ -370,8 +364,6 @@ For targets: `mips64el-unknown-linux-gnuabi64`
 - Path and misc options > Prefix directory = /x-tools/${CT\_TARGET}
 - Path and misc options > Use a mirror = ENABLE
 - Path and misc options > Base URL = https://ci-mirrors.rust-lang.org/rustc
-- Path and misc options > Patches origin = Bundled, then local
-- Path and misc options > Local patch directory = /tmp/patches
 - Target options > Target Architecture = mips
 - Target options > ABI = n64
 - Target options > Endianness = Little endian
diff --git a/src/ci/docker/host-x86_64/dist-mips-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-mips-linux/Dockerfile
index c08febf423a..4a090dcdd50 100644
--- a/src/ci/docker/host-x86_64/dist-mips-linux/Dockerfile
+++ b/src/ci/docker/host-x86_64/dist-mips-linux/Dockerfile
@@ -11,7 +11,6 @@ RUN sh /scripts/rustbuild-setup.sh
 WORKDIR /tmp
 
 COPY scripts/crosstool-ng-build.sh /scripts/
-COPY host-x86_64/dist-mips-linux/patches/ /tmp/patches/
 COPY host-x86_64/dist-mips-linux/mips-linux-gnu.defconfig /tmp/crosstool.defconfig
 RUN /scripts/crosstool-ng-build.sh
 
diff --git a/src/ci/docker/host-x86_64/dist-mips-linux/mips-linux-gnu.defconfig b/src/ci/docker/host-x86_64/dist-mips-linux/mips-linux-gnu.defconfig
index 75743fe8141..94db07922a2 100644
--- a/src/ci/docker/host-x86_64/dist-mips-linux/mips-linux-gnu.defconfig
+++ b/src/ci/docker/host-x86_64/dist-mips-linux/mips-linux-gnu.defconfig
@@ -2,8 +2,6 @@ CT_CONFIG_VERSION="4"
 CT_PREFIX_DIR="/x-tools/${CT_TARGET}"
 CT_USE_MIRROR=y
 CT_MIRROR_BASE_URL="https://ci-mirrors.rust-lang.org/rustc"
-CT_PATCH_BUNDLED_LOCAL=y
-CT_LOCAL_PATCH_DIR="/tmp/patches"
 CT_ARCH_MIPS=y
 CT_ARCH_mips_o32=y
 CT_ARCH_ARCH="mips32r2"
diff --git a/src/ci/docker/host-x86_64/dist-mips-linux/patches/glibc/2.23/0001-MIPS-SPARC-fix-wrong-vfork-aliases-in-libpthread.so.patch b/src/ci/docker/host-x86_64/dist-mips-linux/patches/glibc/2.23/0001-MIPS-SPARC-fix-wrong-vfork-aliases-in-libpthread.so.patch
deleted file mode 100644
index 393084df324..00000000000
--- a/src/ci/docker/host-x86_64/dist-mips-linux/patches/glibc/2.23/0001-MIPS-SPARC-fix-wrong-vfork-aliases-in-libpthread.so.patch
+++ /dev/null
@@ -1,44 +0,0 @@
-From 43c2948756bb6e144c7b871e827bba37d61ad3a3 Mon Sep 17 00:00:00 2001
-From: Aurelien Jarno <aurelien@aurel32.net>
-Date: Sat, 18 Jun 2016 19:11:23 +0200
-Subject: [PATCH 1/2] MIPS, SPARC: fix wrong vfork aliases in libpthread.so
-
-With recent binutils versions the GNU libc fails to build on at least
-MISP and SPARC, with this kind of error:
-
-  /home/aurel32/glibc/glibc-build/nptl/libpthread.so:(*IND*+0x0): multiple definition of `vfork@GLIBC_2.0'
-  /home/aurel32/glibc/glibc-build/nptl/libpthread.so::(.text+0xee50): first defined here
-
-It appears that on these architectures pt-vfork.S includes vfork.S
-(through the alpha version of pt-vfork.S) and that the __vfork aliases
-are not conditionalized on IS_IN (libc) like on other architectures.
-Therefore the aliases are also wrongly included in libpthread.so.
-
-Fix this by properly conditionalizing the aliases like on other
-architectures.
-
-Changelog:
-	* sysdeps/unix/sysv/linux/mips/vfork.S (__vfork): Conditionalize
-	hidden_def, weak_alias and strong_alias on [IS_IN (libc)].
-	* sysdeps/unix/sysv/linux/sparc/sparc32/vfork.S: Likewise.
-	* sysdeps/unix/sysv/linux/sparc/sparc64/vfork.S: Likewise.
----
- sysdeps/unix/sysv/linux/mips/vfork.S | 2 ++
- 1 file changed, 2 insertions(+)
-
-diff --git a/sysdeps/unix/sysv/linux/mips/vfork.S b/sysdeps/unix/sysv/linux/mips/vfork.S
-index 8c6615143708..c0c0ce699159 100644
---- a/sysdeps/unix/sysv/linux/mips/vfork.S
-+++ b/sysdeps/unix/sysv/linux/mips/vfork.S
-@@ -106,6 +106,8 @@ L(error):
- #endif
- 	END(__vfork)
- 
-+#if IS_IN (libc)
- libc_hidden_def(__vfork)
- weak_alias (__vfork, vfork)
- strong_alias (__vfork, __libc_vfork)
-+#endif
--- 
-2.37.3
-
diff --git a/src/ci/docker/host-x86_64/dist-mips-linux/patches/glibc/2.23/0002-MIPS-SPARC-more-fixes-to-the-vfork-aliases-in-libpth.patch b/src/ci/docker/host-x86_64/dist-mips-linux/patches/glibc/2.23/0002-MIPS-SPARC-more-fixes-to-the-vfork-aliases-in-libpth.patch
deleted file mode 100644
index e28c7ae99f0..00000000000
--- a/src/ci/docker/host-x86_64/dist-mips-linux/patches/glibc/2.23/0002-MIPS-SPARC-more-fixes-to-the-vfork-aliases-in-libpth.patch
+++ /dev/null
@@ -1,63 +0,0 @@
-From b87c1ec3fa398646f042a68f0ce0f7d09c1348c7 Mon Sep 17 00:00:00 2001
-From: Aurelien Jarno <aurelien@aurel32.net>
-Date: Tue, 21 Jun 2016 23:59:37 +0200
-Subject: [PATCH 2/2] MIPS, SPARC: more fixes to the vfork aliases in
- libpthread.so
-
-Commit 43c29487 tried to fix the vfork aliases in libpthread.so on MIPS
-and SPARC, but failed to do it correctly, introducing an ABI change.
-
-This patch does the remaining changes needed to align the MIPS and SPARC
-vfork implementations with the other architectures. That way the the
-alpha version of pt-vfork.S works correctly for MIPS and SPARC. The
-changes for alpha were done in 82aab97c.
-
-Changelog:
-	* sysdeps/unix/sysv/linux/mips/vfork.S (__vfork): Rename into
-	__libc_vfork.
-	(__vfork) [IS_IN (libc)]: Remove alias.
-	(__libc_vfork) [IS_IN (libc)]: Define as an alias.
-	* sysdeps/unix/sysv/linux/sparc/sparc32/vfork.S: Likewise.
-	* sysdeps/unix/sysv/linux/sparc/sparc64/vfork.S: Likewise.
----
- sysdeps/unix/sysv/linux/mips/vfork.S | 12 ++++++------
- 1 file changed, 6 insertions(+), 6 deletions(-)
-
-diff --git a/sysdeps/unix/sysv/linux/mips/vfork.S b/sysdeps/unix/sysv/linux/mips/vfork.S
-index c0c0ce699159..1867c8626ebe 100644
---- a/sysdeps/unix/sysv/linux/mips/vfork.S
-+++ b/sysdeps/unix/sysv/linux/mips/vfork.S
-@@ -31,13 +31,13 @@
- LOCALSZ= 1
- FRAMESZ= (((NARGSAVE+LOCALSZ)*SZREG)+ALSZ)&ALMASK
- GPOFF= FRAMESZ-(1*SZREG)
--NESTED(__vfork,FRAMESZ,sp)
-+NESTED(__libc_vfork,FRAMESZ,sp)
- #ifdef __PIC__
- 	SETUP_GP
- #endif
- 	PTR_SUBU sp, FRAMESZ
- 	cfi_adjust_cfa_offset (FRAMESZ)
--	SETUP_GP64_REG (a5, __vfork)
-+	SETUP_GP64_REG (a5, __libc_vfork)
- #ifdef __PIC__
- 	SAVE_GP (GPOFF)
- #endif
-@@ -104,10 +104,10 @@ L(error):
- 	RESTORE_GP64_REG
- 	j		__syscall_error
- #endif
--	END(__vfork)
-+	END(__libc_vfork)
- 
- #if IS_IN (libc)
--libc_hidden_def(__vfork)
--weak_alias (__vfork, vfork)
--strong_alias (__vfork, __libc_vfork)
-+weak_alias (__libc_vfork, vfork)
-+strong_alias (__libc_vfork, __vfork)
-+libc_hidden_def (__vfork)
- #endif
--- 
-2.37.3
-
diff --git a/src/ci/docker/host-x86_64/dist-mips64-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-mips64-linux/Dockerfile
index 10f31075e2d..18b0375f400 100644
--- a/src/ci/docker/host-x86_64/dist-mips64-linux/Dockerfile
+++ b/src/ci/docker/host-x86_64/dist-mips64-linux/Dockerfile
@@ -11,7 +11,6 @@ RUN sh /scripts/rustbuild-setup.sh
 WORKDIR /tmp
 
 COPY scripts/crosstool-ng-build.sh /scripts/
-COPY host-x86_64/dist-mips-linux/patches/ /tmp/patches/
 COPY host-x86_64/dist-mips64-linux/mips64-linux-gnu.defconfig /tmp/crosstool.defconfig
 RUN /scripts/crosstool-ng-build.sh
 
diff --git a/src/ci/docker/host-x86_64/dist-mips64-linux/mips64-linux-gnu.defconfig b/src/ci/docker/host-x86_64/dist-mips64-linux/mips64-linux-gnu.defconfig
index 4b8f7a54920..f295a2fafc6 100644
--- a/src/ci/docker/host-x86_64/dist-mips64-linux/mips64-linux-gnu.defconfig
+++ b/src/ci/docker/host-x86_64/dist-mips64-linux/mips64-linux-gnu.defconfig
@@ -2,8 +2,6 @@ CT_CONFIG_VERSION="4"
 CT_PREFIX_DIR="/x-tools/${CT_TARGET}"
 CT_USE_MIRROR=y
 CT_MIRROR_BASE_URL="https://ci-mirrors.rust-lang.org/rustc"
-CT_PATCH_BUNDLED_LOCAL=y
-CT_LOCAL_PATCH_DIR="/tmp/patches"
 CT_ARCH_MIPS=y
 CT_ARCH_mips_n64=y
 CT_ARCH_64=y
diff --git a/src/ci/docker/host-x86_64/dist-mips64el-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-mips64el-linux/Dockerfile
index 5ab4a53de8a..87407203880 100644
--- a/src/ci/docker/host-x86_64/dist-mips64el-linux/Dockerfile
+++ b/src/ci/docker/host-x86_64/dist-mips64el-linux/Dockerfile
@@ -11,7 +11,6 @@ RUN sh /scripts/rustbuild-setup.sh
 WORKDIR /tmp
 
 COPY scripts/crosstool-ng-build.sh /scripts/
-COPY host-x86_64/dist-mips-linux/patches/ /tmp/patches/
 COPY host-x86_64/dist-mips64el-linux/mips64el-linux-gnu.defconfig /tmp/crosstool.defconfig
 RUN /scripts/crosstool-ng-build.sh
 
diff --git a/src/ci/docker/host-x86_64/dist-mips64el-linux/mips64el-linux-gnu.defconfig b/src/ci/docker/host-x86_64/dist-mips64el-linux/mips64el-linux-gnu.defconfig
index 9c8eb5007b7..47d62463565 100644
--- a/src/ci/docker/host-x86_64/dist-mips64el-linux/mips64el-linux-gnu.defconfig
+++ b/src/ci/docker/host-x86_64/dist-mips64el-linux/mips64el-linux-gnu.defconfig
@@ -2,8 +2,6 @@ CT_CONFIG_VERSION="4"
 CT_PREFIX_DIR="/x-tools/${CT_TARGET}"
 CT_USE_MIRROR=y
 CT_MIRROR_BASE_URL="https://ci-mirrors.rust-lang.org/rustc"
-CT_PATCH_BUNDLED_LOCAL=y
-CT_LOCAL_PATCH_DIR="/tmp/patches"
 CT_ARCH_MIPS=y
 CT_ARCH_mips_n64=y
 CT_ARCH_LE=y
diff --git a/src/ci/docker/host-x86_64/dist-mipsel-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-mipsel-linux/Dockerfile
index 0bbaf00339d..553f6ea86b7 100644
--- a/src/ci/docker/host-x86_64/dist-mipsel-linux/Dockerfile
+++ b/src/ci/docker/host-x86_64/dist-mipsel-linux/Dockerfile
@@ -11,7 +11,6 @@ RUN sh /scripts/rustbuild-setup.sh
 WORKDIR /tmp
 
 COPY scripts/crosstool-ng-build.sh /scripts/
-COPY host-x86_64/dist-mips-linux/patches/ /tmp/patches/
 COPY host-x86_64/dist-mipsel-linux/mipsel-linux-gnu.defconfig /tmp/crosstool.defconfig
 RUN /scripts/crosstool-ng-build.sh
 
diff --git a/src/ci/docker/host-x86_64/dist-mipsel-linux/mipsel-linux-gnu.defconfig b/src/ci/docker/host-x86_64/dist-mipsel-linux/mipsel-linux-gnu.defconfig
index a9dae121e53..5daa83ebc02 100644
--- a/src/ci/docker/host-x86_64/dist-mipsel-linux/mipsel-linux-gnu.defconfig
+++ b/src/ci/docker/host-x86_64/dist-mipsel-linux/mipsel-linux-gnu.defconfig
@@ -2,8 +2,6 @@ CT_CONFIG_VERSION="4"
 CT_PREFIX_DIR="/x-tools/${CT_TARGET}"
 CT_USE_MIRROR=y
 CT_MIRROR_BASE_URL="https://ci-mirrors.rust-lang.org/rustc"
-CT_PATCH_BUNDLED_LOCAL=y
-CT_LOCAL_PATCH_DIR="/tmp/patches"
 CT_ARCH_MIPS=y
 CT_ARCH_mips_o32=y
 CT_ARCH_LE=y
diff --git a/src/ci/docker/host-x86_64/mingw-check-tidy/Dockerfile b/src/ci/docker/host-x86_64/mingw-check-tidy/Dockerfile
index f52e306974c..9ca8cc740a5 100644
--- a/src/ci/docker/host-x86_64/mingw-check-tidy/Dockerfile
+++ b/src/ci/docker/host-x86_64/mingw-check-tidy/Dockerfile
@@ -1,4 +1,6 @@
-FROM ubuntu:22.04
+# We use the ghcr base image because ghcr doesn't have a rate limit
+# and the mingw-check-tidy job doesn't cache docker images in CI.
+FROM ghcr.io/rust-lang/ubuntu:22.04
 
 ARG DEBIAN_FRONTEND=noninteractive
 RUN apt-get update && apt-get install -y --no-install-recommends \
diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml
index 6bf4a75d080..799ea3e9ad2 100644
--- a/src/ci/github-actions/jobs.yml
+++ b/src/ci/github-actions/jobs.yml
@@ -44,7 +44,7 @@ runners:
     <<: *base-job
 
   - &job-aarch64-linux
-    os: ubuntu-22.04-arm64-8core-32gb
+    os: ubuntu-22.04-arm
 
 envs:
   env-x86_64-apple-tests: &env-x86_64-apple-tests
@@ -448,11 +448,18 @@ auto:
       SCRIPT: make ci-msvc
     <<: *job-windows-8c
 
-  - name: i686-msvc
+  # i686-msvc is split into two jobs to run tests in parallel.
+  - name: i686-msvc-1
     env:
       RUST_CONFIGURE_ARGS: --build=i686-pc-windows-msvc
-      SCRIPT: make ci-msvc
-    <<: *job-windows-8c
+      SCRIPT: make ci-msvc-py-set1
+    <<: *job-windows
+
+  - name: i686-msvc-2
+    env:
+      RUST_CONFIGURE_ARGS: --build=i686-pc-windows-msvc
+      SCRIPT: make ci-msvc-ps1-set2
+    <<: *job-windows
 
   # x86_64-msvc-ext is split into multiple jobs to run tests in parallel.
   - name: x86_64-msvc-ext1
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index 1376bdb2e90..76a51cc64a8 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -140,10 +140,9 @@ macro_rules! item_template_methods {
     };
 }
 
-const ITEM_TABLE_OPEN: &str = "<ul class=\"item-table\">";
-const ITEM_TABLE_CLOSE: &str = "</ul>";
-const ITEM_TABLE_ROW_OPEN: &str = "<li>";
-const ITEM_TABLE_ROW_CLOSE: &str = "</li>";
+const ITEM_TABLE_OPEN: &str = "<dl class=\"item-table\">";
+const REEXPORTS_TABLE_OPEN: &str = "<dl class=\"item-table reexports\">";
+const ITEM_TABLE_CLOSE: &str = "</dl>";
 
 // A component in a `use` path, like `string` in std::string::ToString
 struct PathComponent {
@@ -400,37 +399,32 @@ fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[cl
                 w.write_str(ITEM_TABLE_CLOSE);
             }
             last_section = Some(my_section);
-            write_section_heading(
-                w,
-                my_section.name(),
-                &cx.derive_id(my_section.id()),
-                None,
-                ITEM_TABLE_OPEN,
-            );
+            let section_id = my_section.id();
+            let tag =
+                if section_id == "reexports" { REEXPORTS_TABLE_OPEN } else { ITEM_TABLE_OPEN };
+            write_section_heading(w, my_section.name(), &cx.derive_id(section_id), None, tag);
         }
 
         match myitem.kind {
             clean::ExternCrateItem { ref src } => {
                 use crate::html::format::anchor;
 
-                w.write_str(ITEM_TABLE_ROW_OPEN);
                 match *src {
                     Some(src) => write!(
                         w,
-                        "<div class=\"item-name\"><code>{}extern crate {} as {};",
+                        "<dt><code>{}extern crate {} as {};",
                         visibility_print_with_space(myitem, cx),
                         anchor(myitem.item_id.expect_def_id(), src, cx),
                         EscapeBodyTextWithWbr(myitem.name.unwrap().as_str()),
                     ),
                     None => write!(
                         w,
-                        "<div class=\"item-name\"><code>{}extern crate {};",
+                        "<dt><code>{}extern crate {};",
                         visibility_print_with_space(myitem, cx),
                         anchor(myitem.item_id.expect_def_id(), myitem.name.unwrap(), cx),
                     ),
                 }
-                w.write_str("</code></div>");
-                w.write_str(ITEM_TABLE_ROW_CLOSE);
+                w.write_str("</code></dt>");
             }
 
             clean::ImportItem(ref import) => {
@@ -438,28 +432,20 @@ fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[cl
                     extra_info_tags(tcx, myitem, item, Some(import_def_id)).to_string()
                 });
 
-                w.write_str(ITEM_TABLE_ROW_OPEN);
                 let id = match import.kind {
                     clean::ImportKind::Simple(s) => {
                         format!(" id=\"{}\"", cx.derive_id(format!("reexport.{s}")))
                     }
                     clean::ImportKind::Glob => String::new(),
                 };
-                let (stab_tags_before, stab_tags_after) = if stab_tags.is_empty() {
-                    ("", "")
-                } else {
-                    ("<div class=\"desc docblock-short\">", "</div>")
-                };
                 write!(
                     w,
-                    "<div class=\"item-name\"{id}>\
-                         <code>{vis}{imp}</code>\
-                     </div>\
-                     {stab_tags_before}{stab_tags}{stab_tags_after}",
+                    "<dt{id}>\
+                         <code>{vis}{imp}</code>{stab_tags}\
+                     </dt>",
                     vis = visibility_print_with_space(myitem, cx),
                     imp = import.print(cx),
                 );
-                w.write_str(ITEM_TABLE_ROW_CLOSE);
             }
 
             _ => {
@@ -492,22 +478,18 @@ fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[cl
                     _ => "",
                 };
 
-                w.write_str(ITEM_TABLE_ROW_OPEN);
                 let docs =
                     MarkdownSummaryLine(&myitem.doc_value(), &myitem.links(cx)).into_string();
-                let (docs_before, docs_after) = if docs.is_empty() {
-                    ("", "")
-                } else {
-                    ("<div class=\"desc docblock-short\">", "</div>")
-                };
+                let (docs_before, docs_after) =
+                    if docs.is_empty() { ("", "") } else { ("<dd>", "</dd>") };
                 write!(
                     w,
-                    "<div class=\"item-name\">\
+                    "<dt>\
                         <a class=\"{class}\" href=\"{href}\" title=\"{title}\">{name}</a>\
                         {visibility_and_hidden}\
                         {unsafety_flag}\
                         {stab_tags}\
-                     </div>\
+                     </dt>\
                      {docs_before}{docs}{docs_after}",
                     name = EscapeBodyTextWithWbr(myitem.name.unwrap().as_str()),
                     visibility_and_hidden = visibility_and_hidden,
@@ -521,7 +503,6 @@ fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[cl
                         .collect::<Vec<_>>()
                         .join(" "),
                 );
-                w.write_str(ITEM_TABLE_ROW_CLOSE);
             }
         }
     }
diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css
index a1ab258ff30..4cb05b05be5 100644
--- a/src/librustdoc/html/static/css/rustdoc.css
+++ b/src/librustdoc/html/static/css/rustdoc.css
@@ -242,7 +242,7 @@ h1, h2, h3, h4, h5, h6,
 .mobile-topbar,
 .search-input,
 .search-results .result-name,
-.item-name > a,
+.item-table dt > a,
 .out-of-band,
 .sub-heading,
 span.since,
@@ -385,11 +385,11 @@ details:not(.toggle) summary {
 code, pre, .code-header, .type-signature {
 	font-family: "Source Code Pro", monospace;
 }
-.docblock code, .docblock-short code {
+.docblock code, .item-table dd code {
 	border-radius: 3px;
 	padding: 0 0.125em;
 }
-.docblock pre code, .docblock-short pre code {
+.docblock pre code, .item-table dd pre code {
 	padding: 0;
 }
 pre {
@@ -887,13 +887,13 @@ both the code example and the line numbers, so we need to remove the radius in t
 	text-align: center;
 }
 
-.docblock-short {
+.item-table dd {
 	overflow-wrap: break-word;
 	overflow-wrap: anywhere;
 }
 /* Wrap non-pre code blocks (`text`) but not (```text```). */
 .docblock :not(pre) > code,
-.docblock-short code {
+.item-table dd code {
 	white-space: pre-wrap;
 }
 
@@ -938,7 +938,7 @@ rustdoc-toolbar {
 	min-height: 60px;
 }
 
-.docblock code, .docblock-short code,
+.docblock code, .item-table dd code,
 pre, .rustdoc.src .example-wrap, .example-wrap .src-line-numbers {
 	background-color: var(--code-block-background-color);
 	border-radius: var(--code-block-border-radius);
@@ -964,7 +964,7 @@ pre, .rustdoc.src .example-wrap, .example-wrap .src-line-numbers {
 	background: var(--table-alt-row-background-color);
 }
 
-.docblock .stab, .docblock-short .stab, .docblock p code {
+.docblock .stab, .item-table dd .stab, .docblock p code {
 	display: inline-block;
 }
 
@@ -1069,7 +1069,7 @@ because of the `[-]` element which would overlap with it. */
 .example-wrap .rust a:hover,
 .all-items a:hover,
 .docblock a:not(.scrape-help):not(.tooltip):hover:not(.doc-anchor),
-.docblock-short a:not(.scrape-help):not(.tooltip):hover,
+.item-table dd a:not(.scrape-help):not(.tooltip):hover,
 .item-info a {
 	text-decoration: underline;
 }
@@ -1102,20 +1102,17 @@ table,
 }
 
 .item-table {
-	display: table;
 	padding: 0;
 	margin: 0;
 	width: 100%;
 }
-.item-table > li {
-	display: table-row;
-}
-.item-table > li > div {
-	display: table-cell;
-}
-.item-table > li > .item-name {
+.item-table > dt {
 	padding-right: 1.25rem;
 }
+.item-table > dd {
+	margin-inline-start: 0;
+	margin-left: 0;
+}
 
 .search-results-title {
 	margin-top: 0;
@@ -1415,7 +1412,7 @@ so that we can apply CSS-filters to change the arrow color in themes */
 	padding: 3px;
 	margin-bottom: 5px;
 }
-.item-name .stab {
+.item-table dt .stab {
 	margin-left: 0.3125em;
 }
 .stab {
@@ -2476,8 +2473,7 @@ in src-script.js and main.js
 	}
 
 	/* Display an alternating layout on tablets and phones */
-	.item-table, .item-row, .item-table > li, .item-table > li > div,
-	.search-results > a, .search-results > a > div {
+	.item-row, .search-results > a, .search-results > a > div {
 		display: block;
 	}
 
@@ -2485,7 +2481,7 @@ in src-script.js and main.js
 	.search-results > a {
 		padding: 5px 0px;
 	}
-	.search-results > a > div.desc, .item-table > li > div.desc {
+	.search-results > a > div.desc, .item-table dd {
 		padding-left: 2em;
 	}
 	.search-results .result-name {
@@ -2546,12 +2542,20 @@ in src-script.js and main.js
 		box-shadow: 0 0 4px var(--main-background-color);
 	}
 
-	.item-table > li > .item-name {
-		width: 33%;
+	/* Since the screen is wide enough, we show items on their description on the same line. */
+	.item-table:not(.reexports) {
+		display: grid;
+		grid-template-columns: 33% 67%;
 	}
-	.item-table > li > div {
+	.item-table > dt, .item-table > dd {
 		overflow-wrap: anywhere;
 	}
+	.item-table > dt {
+		grid-column-start: 1;
+	}
+	.item-table > dd {
+		grid-column-start: 2;
+	}
 }
 
 @media print {
diff --git a/tests/run-make/broken-pipe-no-ice/rmake.rs b/tests/run-make/broken-pipe-no-ice/rmake.rs
index 378c3289cb7..54d13b62f4a 100644
--- a/tests/run-make/broken-pipe-no-ice/rmake.rs
+++ b/tests/run-make/broken-pipe-no-ice/rmake.rs
@@ -25,7 +25,7 @@ enum Binary {
 }
 
 fn check_broken_pipe_handled_gracefully(bin: Binary, mut cmd: Command) {
-    let (reader, writer) = std::pipe::pipe().unwrap();
+    let (reader, writer) = std::io::pipe().unwrap();
     drop(reader); // close read-end
     cmd.stdout(writer).stderr(Stdio::piped());
 
diff --git a/tests/rustdoc-gui/huge-collection-of-constants.goml b/tests/rustdoc-gui/huge-collection-of-constants.goml
index 387aca6f66c..643f0f51ac1 100644
--- a/tests/rustdoc-gui/huge-collection-of-constants.goml
+++ b/tests/rustdoc-gui/huge-collection-of-constants.goml
@@ -3,7 +3,7 @@
 go-to: "file://" + |DOC_PATH| + "/test_docs/huge_amount_of_consts/index.html"
 
 compare-elements-position-near-false: (
-    "//ul[@class='item-table']/li[last()-1]",
-    "//ul[@class='item-table']/li[last()-3]",
+    "//dl[@class='item-table']/dt[last()-1]",
+    "//dl[@class='item-table']/dt[last()-3]",
     {"y": 12},
 )
diff --git a/tests/rustdoc-gui/item-name-wrap.goml b/tests/rustdoc-gui/item-name-wrap.goml
index 825c16ac5b8..d4da5c2d609 100644
--- a/tests/rustdoc-gui/item-name-wrap.goml
+++ b/tests/rustdoc-gui/item-name-wrap.goml
@@ -3,21 +3,21 @@ go-to: "file://" + |DOC_PATH| + "/test_docs/short_docs/index.html"
 set-window-size: (1000, 600)
 
 // First we ensure that there is only one `item-table`...
-assert-count: ("ul.item-table", 1)
+assert-count: ("dl.item-table", 1)
 // And only two items in it.
-assert-count: ("ul.item-table li", 2)
+assert-count: ("dl.item-table dt", 2)
 
 // If they don't have the same height, then it means one of the two is on two lines whereas it
 // shouldn't!
 compare-elements-size: (
-    ".item-table .item-name a[href='fn.mult_vec_num.html']",
-    ".item-table .item-name a[href='fn.subt_vec_num.html']",
+    ".item-table dt a[href='fn.mult_vec_num.html']",
+    ".item-table dt a[href='fn.subt_vec_num.html']",
     ["height"],
 )
 
 // We also check that the `item-table` is taking the full width.
 compare-elements-size: (
     "#functions",
-    "ul.item-table",
+    "dl.item-table",
     ["width"],
 )
diff --git a/tests/rustdoc-gui/item-summary-table.goml b/tests/rustdoc-gui/item-summary-table.goml
index 89306030329..7c0dfce3062 100644
--- a/tests/rustdoc-gui/item-summary-table.goml
+++ b/tests/rustdoc-gui/item-summary-table.goml
@@ -1,6 +1,6 @@
 // This test ensures that <table> elements aren't display in items summary.
 go-to: "file://" + |DOC_PATH| + "/lib2/summary_table/index.html"
 // We check that we picked the right item first.
-assert-text: (".item-table .item-name", "Foo")
+assert-text: (".item-table dt", "Foo")
 // Then we check that its summary is empty.
-assert-false: ".item-table .desc"
+assert-false: ".item-table dd"
diff --git a/tests/rustdoc-gui/label-next-to-symbol.goml b/tests/rustdoc-gui/label-next-to-symbol.goml
index a8363f29dd5..7960dac11b6 100644
--- a/tests/rustdoc-gui/label-next-to-symbol.goml
+++ b/tests/rustdoc-gui/label-next-to-symbol.goml
@@ -12,59 +12,59 @@ assert: (".stab.portability")
 
 // make sure that deprecated and portability have the right colors
 assert-css: (
-    ".item-table .item-name .stab.deprecated",
+    ".item-table dt .stab.deprecated",
     { "background-color": "#fff5d6" },
 )
 assert-css: (
-    ".item-table .item-name .stab.portability",
+    ".item-table dt .stab.portability",
     { "background-color": "#fff5d6" },
 )
 
 // table like view
-assert-css: (".desc.docblock-short", { "padding-left": "0px" })
+assert-css: ("dd", { "padding-left": "0px" })
 compare-elements-position-near: (
-    "//*[@class='item-name']//a[normalize-space()='replaced_function']",
-    ".item-name .stab.deprecated",
+    "//dt//a[normalize-space()='replaced_function']",
+    "dt .stab.deprecated",
     {"y": 2},
 )
 // "Unix" part is on second line
 compare-elements-position-false: (
-    ".item-name .stab.deprecated",
-    ".item-name .stab.portability",
+    "dt .stab.deprecated",
+    "dt .stab.portability",
     ["y"],
 )
 
 // Ensure no wrap
 compare-elements-position: (
-    "//*[@class='item-name']//a[normalize-space()='replaced_function']/..",
-    "//*[@class='desc docblock-short'][normalize-space()='a thing with a label']",
+    "//dt//a[normalize-space()='replaced_function']/..",
+    "//dd[normalize-space()='a thing with a label']",
     ["y"],
 )
 
 // Mobile view
 set-window-size: (600, 600)
 // staggered layout with 2em spacing
-assert-css: (".desc.docblock-short", { "padding-left": "32px" })
+assert-css: ("dd", { "padding-left": "32px" })
 compare-elements-position-near: (
-    "//*[@class='item-name']//a[normalize-space()='replaced_function']",
-    ".item-name .stab.deprecated",
+    "//dt//a[normalize-space()='replaced_function']",
+    "dt .stab.deprecated",
     {"y": 2},
 )
 compare-elements-position: (
-    ".item-name .stab.deprecated",
-    ".item-name .stab.portability",
+    "dt .stab.deprecated",
+    "dt .stab.portability",
     ["y"],
 )
 
 // Ensure wrap
 compare-elements-position-false: (
-    "//*[@class='item-name']//a[normalize-space()='replaced_function']/..",
-    "//*[@class='desc docblock-short'][normalize-space()='a thing with a label']",
+    "//dt//a[normalize-space()='replaced_function']/..",
+    "//dd[normalize-space()='a thing with a label']",
     ["y"],
 )
 compare-elements-position-false: (
-    ".item-name .stab.deprecated",
-    "//*[@class='desc docblock-short'][normalize-space()='a thing with a label']",
+    "dt .stab.deprecated",
+    "//dd[normalize-space()='a thing with a label']",
     ["y"],
 )
 
diff --git a/tests/rustdoc-gui/links-color.goml b/tests/rustdoc-gui/links-color.goml
index 8d26b826479..f11920cdd8c 100644
--- a/tests/rustdoc-gui/links-color.goml
+++ b/tests/rustdoc-gui/links-color.goml
@@ -37,9 +37,9 @@ define-function: (
             },
             ALL,
         )
-        move-cursor-to: ".desc a[href='long_code_block_link/index.html']"
+        move-cursor-to: "dd a[href='long_code_block_link/index.html']"
         assert-css: (
-            ".desc a[href='long_code_block_link/index.html']",
+            "dd a[href='long_code_block_link/index.html']",
             {"text-decoration": "underline solid " + |mod|},
         )
     },
diff --git a/tests/rustdoc-gui/module-items-font.goml b/tests/rustdoc-gui/module-items-font.goml
index 54c8131c3b9..0e6dd81c05b 100644
--- a/tests/rustdoc-gui/module-items-font.goml
+++ b/tests/rustdoc-gui/module-items-font.goml
@@ -1,67 +1,67 @@
 // This test checks that the correct font is used on module items (in index.html pages).
 go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
 assert-css: (
-    ".item-table .item-name > a",
+    ".item-table dt > a",
     {"font-family": '"Fira Sans", Arial, NanumBarunGothic, sans-serif'},
     ALL,
 )
 assert-css: (
-    ".item-table .docblock-short",
+    ".item-table dd",
     {"font-family": '"Source Serif 4", NanumBarunGothic, serif'},
     ALL,
 )
 
 // modules
 assert-css: (
-    "#modules + .item-table .item-name a",
+    "#modules + .item-table dt a",
     {"font-family": '"Fira Sans", Arial, NanumBarunGothic, sans-serif'},
 )
 assert-css: (
-    "#modules + .item-table .desc.docblock-short",
+    "#modules + .item-table ",
     {"font-family": '"Source Serif 4", NanumBarunGothic, serif'},
 )
 // structs
 assert-css: (
-    "#structs + .item-table .item-name a",
+    "#structs + .item-table dt a",
     {"font-family": '"Fira Sans", Arial, NanumBarunGothic, sans-serif'},
 )
 assert-css: (
-    "#structs + .item-table .desc.docblock-short",
+    "#structs + .item-table dd",
     {"font-family": '"Source Serif 4", NanumBarunGothic, serif'},
 )
 // enums
 assert-css: (
-    "#enums + .item-table .item-name a",
+    "#enums + .item-table dt a",
     {"font-family": '"Fira Sans", Arial, NanumBarunGothic, sans-serif'},
 )
 assert-css: (
-    "#enums + .item-table .desc.docblock-short",
+    "#enums + .item-table dd",
     {"font-family": '"Source Serif 4", NanumBarunGothic, serif'},
 )
 // traits
 assert-css: (
-    "#traits + .item-table .item-name a",
+    "#traits + .item-table dt a",
     {"font-family": '"Fira Sans", Arial, NanumBarunGothic, sans-serif'},
 )
 assert-css: (
-    "#traits + .item-table .desc.docblock-short",
+    "#traits + .item-table dd",
     {"font-family": '"Source Serif 4", NanumBarunGothic, serif'},
 )
 // functions
 assert-css: (
-    "#functions + .item-table .item-name a",
+    "#functions + .item-table dt a",
     {"font-family": '"Fira Sans", Arial, NanumBarunGothic, sans-serif'},
 )
 assert-css: (
-    "#functions + .item-table .desc.docblock-short",
+    "#functions + .item-table dd",
     {"font-family": '"Source Serif 4", NanumBarunGothic, serif'},
 )
 // keywords
 assert-css: (
-    "#keywords + .item-table .item-name a",
+    "#keywords + .item-table dt a",
     {"font-family": '"Fira Sans", Arial, NanumBarunGothic, sans-serif'},
 )
 assert-css: (
-    "#keywords + .item-table .desc.docblock-short",
+    "#keywords + .item-table dd",
     {"font-family": '"Source Serif 4", NanumBarunGothic, serif'},
 )
diff --git a/tests/rustdoc-gui/sidebar.goml b/tests/rustdoc-gui/sidebar.goml
index bb7453fdeac..38160cc49d0 100644
--- a/tests/rustdoc-gui/sidebar.goml
+++ b/tests/rustdoc-gui/sidebar.goml
@@ -65,8 +65,8 @@ assert-text: (".sidebar-elems section ul > li:nth-child(8)", "Functions")
 assert-text: (".sidebar-elems section ul > li:nth-child(9)", "Type Aliases")
 assert-text: (".sidebar-elems section ul > li:nth-child(10)", "Unions")
 assert-text: (".sidebar-elems section ul > li:nth-child(11)", "Keywords")
-assert-text: ("#structs + .item-table .item-name > a", "Foo")
-click: "#structs + .item-table .item-name > a"
+assert-text: ("#structs + .item-table dt > a", "Foo")
+click: "#structs + .item-table dt > a"
 
 // PAGE: struct.Foo.html
 assert-count: (".sidebar .sidebar-crate", 1)
@@ -101,8 +101,8 @@ assert-text: (".sidebar-elems > section ul.block > li:nth-child(2)", "Structs")
 assert-text: (".sidebar-elems > section ul.block > li:nth-child(3)", "Traits")
 assert-text: (".sidebar-elems > section ul.block > li:nth-child(4)", "Functions")
 assert-text: (".sidebar-elems > section ul.block > li:nth-child(5)", "Type Aliases")
-assert-text: ("#functions + .item-table .item-name > a", "foobar")
-click: "#functions + .item-table .item-name > a"
+assert-text: ("#functions + .item-table dt > a", "foobar")
+click: "#functions + .item-table dt > a"
 
 // PAGE: fn.foobar.html
 // In items containing no items (like functions or constants) and in modules, we have no
@@ -145,7 +145,7 @@ assert-text: (".sidebar-elems ul.block > li.current > a", "sub_sub_module")
 // We check that we don't have the crate list.
 assert-false: ".sidebar-elems .crate"
 assert-text: (".sidebar-elems > section ul > li:nth-child(1)", "Functions")
-assert-text: ("#functions + .item-table .item-name > a", "foo")
+assert-text: ("#functions + .item-table dt > a", "foo")
 
 // Links to trait implementations in the sidebar should not wrap even if they are long.
 go-to: "file://" + |DOC_PATH| + "/lib2/struct.HasALongTraitWithParams.html"
diff --git a/tests/rustdoc-gui/unsafe-fn.goml b/tests/rustdoc-gui/unsafe-fn.goml
index b8b2e1e27f5..b857afeff13 100644
--- a/tests/rustdoc-gui/unsafe-fn.goml
+++ b/tests/rustdoc-gui/unsafe-fn.goml
@@ -17,7 +17,7 @@ define-function: (
     [theme, color],
     block {
         call-function: ("switch-theme", {"theme": |theme|})
-        assert-css: (".item-name sup", {"color": |color|})
+        assert-css: ("dt sup", {"color": |color|})
     },
 )
 
diff --git a/tests/rustdoc/anonymous-reexport-108931.rs b/tests/rustdoc/anonymous-reexport-108931.rs
index f4cc7f12396..b995c89b614 100644
--- a/tests/rustdoc/anonymous-reexport-108931.rs
+++ b/tests/rustdoc/anonymous-reexport-108931.rs
@@ -16,7 +16,7 @@ mod bar {
 //@ count - '//*[@id="main-content"]/h2' 2
 //@ has - '//*[@id="main-content"]/h2' 'Re-exports'
 //@ has - '//*[@id="main-content"]/h2' 'Modules'
-//@ has - '//*[@id="main-content"]//*[@class="item-table"]//li//code' 'pub use foo::Foo as _;'
-//@ has - '//*[@id="main-content"]//*[@class="item-table"]//li//code' 'pub use bar::Bar as _;'
+//@ has - '//*[@id="main-content"]//*[@class="item-table reexports"]/dt//code' 'pub use foo::Foo as _;'
+//@ has - '//*[@id="main-content"]//*[@class="item-table reexports"]/dt//code' 'pub use bar::Bar as _;'
 pub use foo::Foo as _;
 pub use bar::Bar as _;
diff --git a/tests/rustdoc/anonymous-reexport.rs b/tests/rustdoc/anonymous-reexport.rs
index 8021008dc66..bf5fa93f953 100644
--- a/tests/rustdoc/anonymous-reexport.rs
+++ b/tests/rustdoc/anonymous-reexport.rs
@@ -9,7 +9,7 @@
 //@ has - '//*[@id="main-content"]/h2' 'Structs'
 //@ has - '//*[@id="main-content"]/h2' 'Re-exports'
 // The 3 re-exports.
-//@ count - '//*[@id="main-content"]//*[@class="item-table"]//li//code' 3
+//@ count - '//*[@id="main-content"]//*[@class="item-table reexports"]/dt//code' 3
 // The public struct.
 //@ count - '//*[@id="main-content"]//a[@class="struct"]' 1
 
diff --git a/tests/rustdoc/attributes-inlining-108281.rs b/tests/rustdoc/attributes-inlining-108281.rs
index ba6c570b59b..9dfaf1a6846 100644
--- a/tests/rustdoc/attributes-inlining-108281.rs
+++ b/tests/rustdoc/attributes-inlining-108281.rs
@@ -11,15 +11,15 @@ mod sub {
     pub fn public() {}
 }
 
-//@ matches - '//*[@class="desc docblock-short"]' '^Displayed$'
+//@ matches - '//dd' '^Displayed$'
 /// Displayed
 #[doc(inline)]
 pub use crate::bar as Bar;
-//@ matches - '//*[@class="desc docblock-short"]' '^Hello\sDisplayed$'
+//@ matches - '//dd' '^Hello\sDisplayed$'
 #[doc(inline)]
 /// Hello
 pub use crate::Bar as Bar2;
 
-//@ matches - '//*[@class="desc docblock-short"]' '^Public$'
+//@ matches - '//dd' '^Public$'
 /// Public
 pub use crate::sub::public as Public;
diff --git a/tests/rustdoc/cfg_doc_reexport.rs b/tests/rustdoc/cfg_doc_reexport.rs
index a07e4fe2f02..f8101e2a958 100644
--- a/tests/rustdoc/cfg_doc_reexport.rs
+++ b/tests/rustdoc/cfg_doc_reexport.rs
@@ -5,8 +5,8 @@
 #![no_core]
 
 //@ has 'foo/index.html'
-//@ has - '//*[@class="item-name"]/*[@class="stab portability"]' 'foobar'
-//@ has - '//*[@class="item-name"]/*[@class="stab portability"]' 'bar'
+//@ has - '//dt/*[@class="stab portability"]' 'foobar'
+//@ has - '//dt/*[@class="stab portability"]' 'bar'
 
 #[doc(cfg(feature = "foobar"))]
 mod imp_priv {
diff --git a/tests/rustdoc/deprecated.rs b/tests/rustdoc/deprecated.rs
index b39da9b440a..a84657a3df5 100644
--- a/tests/rustdoc/deprecated.rs
+++ b/tests/rustdoc/deprecated.rs
@@ -1,6 +1,5 @@
-//@ has deprecated/index.html '//*[@class="item-name"]/span[@class="stab deprecated"]' \
-//      'Deprecated'
-//@ has - '//*[@class="desc docblock-short"]' 'Deprecated docs'
+//@ has deprecated/index.html '//dt/span[@class="stab deprecated"]' 'Deprecated'
+//@ has - '//dd' 'Deprecated docs'
 
 //@ has deprecated/struct.S.html '//*[@class="stab deprecated"]' \
 //      'Deprecated since 1.0.0: text'
@@ -8,7 +7,7 @@
 #[deprecated(since = "1.0.0", note = "text")]
 pub struct S;
 
-//@ matches deprecated/index.html '//*[@class="desc docblock-short"]' '^Docs'
+//@ matches deprecated/index.html '//dd' '^Docs'
 /// Docs
 pub struct T;
 
diff --git a/tests/rustdoc/display-hidden-items.rs b/tests/rustdoc/display-hidden-items.rs
index d9f53435e46..40cd636e2fe 100644
--- a/tests/rustdoc/display-hidden-items.rs
+++ b/tests/rustdoc/display-hidden-items.rs
@@ -5,19 +5,19 @@
 #![crate_name = "foo"]
 
 //@ has 'foo/index.html'
-//@ has - '//*[@class="item-name"]/span[@title="Hidden item"]' '๐Ÿ‘ป'
+//@ has - '//dt/span[@title="Hidden item"]' '๐Ÿ‘ป'
 
 //@ has - '//*[@id="reexport.hidden_reexport"]/code' '#[doc(hidden)] pub use hidden::inside_hidden as hidden_reexport;'
 #[doc(hidden)]
 pub use hidden::inside_hidden as hidden_reexport;
 
-//@ has - '//*[@class="item-name"]/a[@class="trait"]' 'TraitHidden'
+//@ has - '//dt/a[@class="trait"]' 'TraitHidden'
 //@ has 'foo/trait.TraitHidden.html'
 //@ has - '//code' '#[doc(hidden)] pub trait TraitHidden'
 #[doc(hidden)]
 pub trait TraitHidden {}
 
-//@ has 'foo/index.html' '//*[@class="item-name"]/a[@class="trait"]' 'Trait'
+//@ has 'foo/index.html' '//dt/a[@class="trait"]' 'Trait'
 pub trait Trait {
     //@ has 'foo/trait.Trait.html'
     //@ has - '//*[@id="associatedconstant.BAR"]/*[@class="code-header"]' '#[doc(hidden)] const BAR: u32 = 0u32'
@@ -29,7 +29,7 @@ pub trait Trait {
     fn foo() {}
 }
 
-//@ has 'foo/index.html' '//*[@class="item-name"]/a[@class="struct"]' 'Struct'
+//@ has 'foo/index.html' '//dt/a[@class="struct"]' 'Struct'
 //@ has 'foo/struct.Struct.html'
 pub struct Struct {
     //@ has - '//*[@id="structfield.a"]/code' 'a: u32'
@@ -50,7 +50,7 @@ impl Trait for Struct {
 //@ has - '//*[@id="impl-TraitHidden-for-Struct"]/*[@class="code-header"]' 'impl TraitHidden for Struct'
 impl TraitHidden for Struct {}
 
-//@ has 'foo/index.html' '//*[@class="item-name"]/a[@class="enum"]' 'HiddenEnum'
+//@ has 'foo/index.html' '//dt/a[@class="enum"]' 'HiddenEnum'
 //@ has 'foo/enum.HiddenEnum.html'
 //@ has - '//code' '#[doc(hidden)] pub enum HiddenEnum'
 #[doc(hidden)]
@@ -58,18 +58,18 @@ pub enum HiddenEnum {
     A,
 }
 
-//@ has 'foo/index.html' '//*[@class="item-name"]/a[@class="enum"]' 'Enum'
+//@ has 'foo/index.html' '//dt/a[@class="enum"]' 'Enum'
 pub enum Enum {
     //@ has 'foo/enum.Enum.html' '//*[@id="variant.A"]/*[@class="code-header"]' 'A'
     #[doc(hidden)]
     A,
 }
 
-//@ has 'foo/index.html' '//*[@class="item-name"]/a[@class="mod"]' 'hidden'
+//@ has 'foo/index.html' '//dt/a[@class="mod"]' 'hidden'
 #[doc(hidden)]
 pub mod hidden {
     //@ has 'foo/hidden/index.html'
-    //@ has - '//*[@class="item-name"]/a[@class="fn"]' 'inside_hidden'
+    //@ has - '//dt/a[@class="fn"]' 'inside_hidden'
     //@ has 'foo/hidden/fn.inside_hidden.html'
     pub fn inside_hidden() {}
 }
diff --git a/tests/rustdoc/doc-cfg.rs b/tests/rustdoc/doc-cfg.rs
index 6c973b5666b..652c8419b4f 100644
--- a/tests/rustdoc/doc-cfg.rs
+++ b/tests/rustdoc/doc-cfg.rs
@@ -12,7 +12,7 @@ pub struct Portable;
 //@ has doc_cfg/unix_only/index.html \
 //  '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \
 //  'Available on Unix only.'
-//@ matches - '//*[@class="item-name"]//*[@class="stab portability"]' '\AARM\Z'
+//@ matches - '//dt//*[@class="stab portability"]' '\AARM\Z'
 //@ count - '//*[@class="stab portability"]' 2
 #[doc(cfg(unix))]
 pub mod unix_only {
@@ -42,7 +42,7 @@ pub mod unix_only {
 //@ has doc_cfg/wasi_only/index.html \
 //  '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \
 //  'Available on WASI only.'
-//@ matches - '//*[@class="item-name"]//*[@class="stab portability"]' '\AWebAssembly\Z'
+//@ matches - '//dt//*[@class="stab portability"]' '\AWebAssembly\Z'
 //@ count - '//*[@class="stab portability"]' 2
 #[doc(cfg(target_os = "wasi"))]
 pub mod wasi_only {
@@ -74,7 +74,7 @@ pub mod wasi_only {
 
 // the portability header is different on the module view versus the full view
 //@ has doc_cfg/index.html
-//@ matches - '//*[@class="item-name"]//*[@class="stab portability"]' '\Aavx\Z'
+//@ matches - '//dt//*[@class="stab portability"]' '\Aavx\Z'
 
 //@ has doc_cfg/fn.uses_target_feature.html
 //@ has - '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \
diff --git a/tests/rustdoc/doc-hidden-reexports-109449.rs b/tests/rustdoc/doc-hidden-reexports-109449.rs
index cc3679f6196..8f195544120 100644
--- a/tests/rustdoc/doc-hidden-reexports-109449.rs
+++ b/tests/rustdoc/doc-hidden-reexports-109449.rs
@@ -26,7 +26,7 @@ pub mod single_reexport {
     //@ has 'foo/single_reexport/index.html'
 
     // First we check that we have 4 type aliases.
-    //@ count - '//*[@id="main-content"]/*[@class="item-table"]//code' 4
+    //@ count - '//*[@id="main-content"]/*[@class="item-table reexports"]//code' 4
 
     // Then we check that we have the correct link for each re-export.
 
@@ -131,10 +131,10 @@ mod private {
 pub mod doc_hidden_reexport {
     //@ has 'foo/doc_hidden_reexport/index.html'
     // Ensure there is only one item in this page and that it's a struct.
-    //@ count - '//*[@class="item-name"]' 1
+    //@ count - '//dt' 1
     //@ has - '//a[@class="struct"]' 'Reexport'
     // Check that the `#[doc(hidden)]` re-export's attributes are not taken into account.
-    //@ has - '//*[@class="desc docblock-short"]' 'Visible. Original.'
+    //@ has - '//dd' 'Visible. Original.'
     /// Visible.
     pub use self::Bar3 as Reexport;
     /// Hidden.
diff --git a/tests/rustdoc/double-hyphen-to-dash.rs b/tests/rustdoc/double-hyphen-to-dash.rs
index 009de4faf41..c14acd065cd 100644
--- a/tests/rustdoc/double-hyphen-to-dash.rs
+++ b/tests/rustdoc/double-hyphen-to-dash.rs
@@ -2,7 +2,7 @@
 
 #![crate_name = "foo"]
 
-//@ has 'foo/index.html' '//*[@class="desc docblock-short"]' 'โ€“'
+//@ has 'foo/index.html' '//dd' 'โ€“'
 //@ has 'foo/struct.Bar.html' '//*[@class="docblock"]' 'โ€“'
 
 /// --
diff --git a/tests/rustdoc/duplicate-cfg.rs b/tests/rustdoc/duplicate-cfg.rs
index 87c089e9735..93f26ab944d 100644
--- a/tests/rustdoc/duplicate-cfg.rs
+++ b/tests/rustdoc/duplicate-cfg.rs
@@ -2,8 +2,8 @@
 #![feature(doc_cfg)]
 
 //@ has 'foo/index.html'
-//@ matches '-' '//*[@class="item-name"]//*[@class="stab portability"]' '^sync$'
-//@ has '-' '//*[@class="item-name"]//*[@class="stab portability"]/@title' 'Available on crate feature `sync` only'
+//@ matches '-' '//dt//*[@class="stab portability"]' '^sync$'
+//@ has '-' '//dt//*[@class="stab portability"]/@title' 'Available on crate feature `sync` only'
 
 //@ has 'foo/struct.Foo.html'
 //@ has '-' '//*[@class="stab portability"]' 'sync'
diff --git a/tests/rustdoc/footnote-in-summary.rs b/tests/rustdoc/footnote-in-summary.rs
index d69282f1041..2a9668a9963 100644
--- a/tests/rustdoc/footnote-in-summary.rs
+++ b/tests/rustdoc/footnote-in-summary.rs
@@ -4,8 +4,8 @@
 #![crate_name = "foo"]
 
 //@ has 'foo/index.html'
-//@ has - '//*[@class="desc docblock-short"]' 'hello bla'
-//@ !has - '//*[@class="desc docblock-short"]/sup' '1'
+//@ has - '//dd' 'hello bla'
+//@ !has - '//dd/sup' '1'
 
 //@ has 'foo/struct.S.html'
 //@ has - '//*[@class="docblock"]//sup' '1'
diff --git a/tests/rustdoc/glob-reexport-attribute-merge-120487.rs b/tests/rustdoc/glob-reexport-attribute-merge-120487.rs
index 2fa10f546d5..5b918e0ffd9 100644
--- a/tests/rustdoc/glob-reexport-attribute-merge-120487.rs
+++ b/tests/rustdoc/glob-reexport-attribute-merge-120487.rs
@@ -7,9 +7,9 @@
 
 //@ has 'foo/index.html'
 // There are two items.
-//@ count - '//*[@class="item-table"]//div[@class="item-name"]' 2
+//@ count - '//*[@class="item-table"]/dt' 2
 // Only one of them should have an attribute.
-//@ count - '//*[@class="item-table"]//div[@class="item-name"]/*[@class="stab portability"]' 1
+//@ count - '//*[@class="item-table"]/dt/*[@class="stab portability"]' 1
 
 mod a {
     #[doc(cfg(not(feature = "a")))]
diff --git a/tests/rustdoc/glob-reexport-attribute-merge-doc-auto-cfg.rs b/tests/rustdoc/glob-reexport-attribute-merge-doc-auto-cfg.rs
index 314b457c2ad..d0a2165ec8a 100644
--- a/tests/rustdoc/glob-reexport-attribute-merge-doc-auto-cfg.rs
+++ b/tests/rustdoc/glob-reexport-attribute-merge-doc-auto-cfg.rs
@@ -6,9 +6,9 @@
 
 //@ has 'foo/index.html'
 // There are two items.
-//@ count - '//*[@class="item-table"]//div[@class="item-name"]' 2
+//@ count - '//*[@class="item-table"]/dt' 2
 // Only one of them should have an attribute.
-//@ count - '//*[@class="item-table"]//div[@class="item-name"]/*[@class="stab portability"]' 1
+//@ count - '//*[@class="item-table"]/dt/*[@class="stab portability"]' 1
 
 mod a {
     #[cfg(not(feature = "a"))]
diff --git a/tests/rustdoc/glob-shadowing-const.rs b/tests/rustdoc/glob-shadowing-const.rs
index 1eb5596cd9c..fbc22dbccaa 100644
--- a/tests/rustdoc/glob-shadowing-const.rs
+++ b/tests/rustdoc/glob-shadowing-const.rs
@@ -15,6 +15,6 @@ mod sub4 {
 pub use sub4::inner::*;
 
 //@ has 'foo/index.html'
-//@ has - '//div[@class="desc docblock-short"]' '1'
-//@ !has - '//div[@class="desc docblock-short"]' '0'
+//@ has - '//dd' '1'
+//@ !has - '//dd' '0'
 fn main() { assert_eq!(X, 1); }
diff --git a/tests/rustdoc/glob-shadowing.rs b/tests/rustdoc/glob-shadowing.rs
index a051bd407d5..d9e9ead3f9a 100644
--- a/tests/rustdoc/glob-shadowing.rs
+++ b/tests/rustdoc/glob-shadowing.rs
@@ -1,17 +1,17 @@
 //@ has 'glob_shadowing/index.html'
-//@ count - '//div[@class="item-name"]' 6
-//@ !has - '//div[@class="desc docblock-short"]' 'sub1::describe'
-//@ has - '//div[@class="desc docblock-short"]' 'sub2::describe'
+//@ count - '//dt' 6
+//@ !has - '//dd' 'sub1::describe'
+//@ has - '//dd' 'sub2::describe'
 
-//@ !has - '//div[@class="desc docblock-short"]' 'sub1::describe2'
+//@ !has - '//dd' 'sub1::describe2'
 
-//@ !has - '//div[@class="desc docblock-short"]' 'sub1::prelude'
-//@ has - '//div[@class="desc docblock-short"]' 'mod::prelude'
+//@ !has - '//dd' 'sub1::prelude'
+//@ has - '//dd' 'mod::prelude'
 
-//@ has - '//div[@class="desc docblock-short"]' 'sub1::Foo (struct)'
-//@ has - '//div[@class="desc docblock-short"]' 'mod::Foo (function)'
+//@ has - '//dd' 'sub1::Foo (struct)'
+//@ has - '//dd' 'mod::Foo (function)'
 
-//@ has - '//div[@class="desc docblock-short"]' 'sub4::inner::X'
+//@ has - '//dd' 'sub4::inner::X'
 
 //@ has 'glob_shadowing/fn.describe.html'
 //@ has - '//div[@class="docblock"]' 'sub2::describe'
diff --git a/tests/rustdoc/impl-on-ty-alias-issue-119015.rs b/tests/rustdoc/impl-on-ty-alias-issue-119015.rs
index cea0f5565a2..a514bc35bfc 100644
--- a/tests/rustdoc/impl-on-ty-alias-issue-119015.rs
+++ b/tests/rustdoc/impl-on-ty-alias-issue-119015.rs
@@ -2,8 +2,8 @@
 
 //@ has 'foo/index.html'
 // There should be only `type A`.
-//@ count - '//*[@class="item-table"]//*[@class="item-name"]' 1
-//@ has - '//*[@class="item-name"]/a[@href="type.A.html"]' 'A'
+//@ count - '//*[@class="item-table"]//dt' 1
+//@ has - '//dt/a[@href="type.A.html"]' 'A'
 
 mod foo {
     pub struct S;
diff --git a/tests/rustdoc/inline-private-with-intermediate-doc-hidden.rs b/tests/rustdoc/inline-private-with-intermediate-doc-hidden.rs
index 752f3843eea..d27ecbad169 100644
--- a/tests/rustdoc/inline-private-with-intermediate-doc-hidden.rs
+++ b/tests/rustdoc/inline-private-with-intermediate-doc-hidden.rs
@@ -9,7 +9,7 @@
 //@ count - '//*[@id="main-content"]/*[@class="section-header"]' 1
 //@ has - '//*[@id="main-content"]/*[@class="section-header"]' 'Structs'
 //@ has - '//*[@id="main-content"]//a[@href="struct.Reexport.html"]' 'Reexport'
-//@ has - '//*[@id="main-content"]//*[@class="desc docblock-short"]' 'Visible. Original.'
+//@ has - '//*[@id="main-content"]//dd' 'Visible. Original.'
 
 mod private {
     /// Original.
diff --git a/tests/rustdoc/inline_cross/inline_hidden.rs b/tests/rustdoc/inline_cross/inline_hidden.rs
index 095cd2d3c55..49ca2db6a22 100644
--- a/tests/rustdoc/inline_cross/inline_hidden.rs
+++ b/tests/rustdoc/inline_cross/inline_hidden.rs
@@ -11,14 +11,14 @@ extern crate rustdoc_hidden;
 pub use rustdoc_hidden::Foo;
 
 // Even if the foreign item has `doc(hidden)`, we should be able to inline it.
-//@ has - '//*[@class="item-name"]/a[@class="struct"]' 'Inlined'
+//@ has - '//dt/a[@class="struct"]' 'Inlined'
 #[doc(inline)]
 pub use rustdoc_hidden::Foo as Inlined;
 
 // Even with this import, we should not see `Foo`.
-//@ count - '//*[@class="item-name"]' 4
-//@ has - '//*[@class="item-name"]/a[@class="struct"]' 'Bar'
-//@ has - '//*[@class="item-name"]/a[@class="fn"]' 'foo'
+//@ count - '//dt' 4
+//@ has - '//dt/a[@class="struct"]' 'Bar'
+//@ has - '//dt/a[@class="fn"]' 'foo'
 pub use rustdoc_hidden::*;
 
 //@ has inline_hidden/fn.foo.html
diff --git a/tests/rustdoc/inline_cross/macros.rs b/tests/rustdoc/inline_cross/macros.rs
index aab7a3650b1..57eec77899e 100644
--- a/tests/rustdoc/inline_cross/macros.rs
+++ b/tests/rustdoc/inline_cross/macros.rs
@@ -6,10 +6,8 @@
 
 extern crate macros;
 
-//@ has foo/index.html '//*[@class="item-name"]/span[@class="stab deprecated"]' \
-//         Deprecated
-//@ has - '//*[@class="item-name"]/span[@class="stab unstable"]' \
-//         Experimental
+//@ has foo/index.html '//dt/span[@class="stab deprecated"]' Deprecated
+//@ has - '//dt/span[@class="stab unstable"]' Experimental
 
 //@ has foo/macro.my_macro.html
 //@ has - '//*[@class="docblock"]' 'docs for my_macro'
diff --git a/tests/rustdoc/internal.rs b/tests/rustdoc/internal.rs
index e0bccefda1d..244e9138f2b 100644
--- a/tests/rustdoc/internal.rs
+++ b/tests/rustdoc/internal.rs
@@ -8,7 +8,7 @@
 //@ !matches internal/index.html \
 //      '//*[@class="desc docblock-short"]/span[@class="stab internal"]' \
 //      ''
-//@ matches - '//*[@class="desc docblock-short"]' 'Docs'
+//@ matches - '//dd' 'Docs'
 
 //@ !has internal/struct.S.html '//*[@class="stab unstable"]' ''
 //@ !has internal/struct.S.html '//*[@class="stab internal"]' ''
diff --git a/tests/rustdoc/intra-doc/module-scope-name-resolution-55364.rs b/tests/rustdoc/intra-doc/module-scope-name-resolution-55364.rs
index 06cb764423e..f0362f684ad 100644
--- a/tests/rustdoc/intra-doc/module-scope-name-resolution-55364.rs
+++ b/tests/rustdoc/intra-doc/module-scope-name-resolution-55364.rs
@@ -32,8 +32,8 @@ pub mod subone {
 //@ has - '//section[@id="main-content"]/details/div[@class="docblock"]//a[@href="../fn.foo.html"]' 'foo'
 //@ has - '//section[@id="main-content"]/details/div[@class="docblock"]//a[@href="../fn.bar.html"]' 'bar'
 // Though there should be such links later
-//@ has - '//section[@id="main-content"]/ul[@class="item-table"]//div[@class="item-name"]/a[@class="fn"][@href="fn.foo.html"]' 'foo'
-//@ has - '//section[@id="main-content"]/ul[@class="item-table"]//div[@class="item-name"]/a[@class="fn"][@href="fn.bar.html"]' 'bar'
+//@ has - '//section[@id="main-content"]/dl[@class="item-table"]/dt/a[@class="fn"][@href="fn.foo.html"]' 'foo'
+//@ has - '//section[@id="main-content"]/dl[@class="item-table"]/dt/a[@class="fn"][@href="fn.bar.html"]' 'bar'
 /// See either [foo] or [bar].
 pub mod subtwo {
 
@@ -71,8 +71,8 @@ pub mod subthree {
 // Next we go *deeper* - In order to ensure it's not just "this or parent"
 // we test `crate::` and a `super::super::...` chain
 //@ has foo/subfour/subfive/subsix/subseven/subeight/index.html
-//@ has - '//section[@id="main-content"]/ul[@class="item-table"]//div[@class="desc docblock-short"]//a[@href="../../../../../subone/fn.foo.html"]' 'other foo'
-//@ has - '//section[@id="main-content"]/ul[@class="item-table"]//div[@class="desc docblock-short"]//a[@href="../../../../../subtwo/fn.bar.html"]' 'other bar'
+//@ has - '//section[@id="main-content"]/dl[@class="item-table"]/dd//a[@href="../../../../../subone/fn.foo.html"]' 'other foo'
+//@ has - '//section[@id="main-content"]/dl[@class="item-table"]/dd//a[@href="../../../../../subtwo/fn.bar.html"]' 'other bar'
 pub mod subfour {
     pub mod subfive {
         pub mod subsix {
diff --git a/tests/rustdoc/item-desc-list-at-start.item-table.html b/tests/rustdoc/item-desc-list-at-start.item-table.html
index cff4f816529..89b4ac640f2 100644
--- a/tests/rustdoc/item-desc-list-at-start.item-table.html
+++ b/tests/rustdoc/item-desc-list-at-start.item-table.html
@@ -1 +1 @@
-<ul class="item-table"><li><div class="item-name"><a class="constant" href="constant.MY_CONSTANT.html" title="constant item_desc_list_at_start::MY_CONSTANT">MY_<wbr />CONSTANT</a></div><div class="desc docblock-short">Groups: <code>SamplePatternSGIS</code>, <code>SamplePatternEXT</code></div></li></ul>
\ No newline at end of file
+<dl class="item-table"><dt><a class="constant" href="constant.MY_CONSTANT.html" title="constant item_desc_list_at_start::MY_CONSTANT">MY_<wbr />CONSTANT</a></dt><dd>Groups: <code>SamplePatternSGIS</code>, <code>SamplePatternEXT</code></dd></dl>
\ No newline at end of file
diff --git a/tests/rustdoc/item-desc-list-at-start.rs b/tests/rustdoc/item-desc-list-at-start.rs
index fbcc36066f1..7c2b31c9460 100644
--- a/tests/rustdoc/item-desc-list-at-start.rs
+++ b/tests/rustdoc/item-desc-list-at-start.rs
@@ -1,7 +1,8 @@
 //@ has item_desc_list_at_start/index.html
-//@ count - '//ul[@class="item-table"]/li/div/li' 0
-//@ count - '//ul[@class="item-table"]/li' 1
-//@ snapshot item-table - '//ul[@class="item-table"]'
+//@ count - '//dl[@class="item-table"]/dd//ul' 0
+//@ count - '//dl[@class="item-table"]/dd//li' 0
+//@ count - '//dl[@class="item-table"]/dd' 1
+//@ snapshot item-table - '//dl[@class="item-table"]'
 
 // based on https://docs.rs/gl_constants/0.1.1/src/gl_constants/lib.rs.html#16
 
diff --git a/tests/rustdoc/macro-rules-broken-intra-doc-106142.rs b/tests/rustdoc/macro-rules-broken-intra-doc-106142.rs
deleted file mode 100644
index 0d146a3c5cd..00000000000
--- a/tests/rustdoc/macro-rules-broken-intra-doc-106142.rs
+++ /dev/null
@@ -1,17 +0,0 @@
-// https://github.com/rust-lang/rust/issues/106142
-#![crate_name="foo"]
-
-//@ has 'foo/a/index.html'
-//@ count 'foo/a/index.html' '//ul[@class="item-table"]//li//a' 1
-
-#![allow(rustdoc::broken_intra_doc_links)]
-
-pub mod a {
-    /// [`m`]
-    pub fn f() {}
-
-    #[macro_export]
-    macro_rules! m {
-        () => {};
-    }
-}
diff --git a/tests/rustdoc/nested-items-issue-111415.rs b/tests/rustdoc/nested-items-issue-111415.rs
index a5cd3ca0b1a..79dc2b0378f 100644
--- a/tests/rustdoc/nested-items-issue-111415.rs
+++ b/tests/rustdoc/nested-items-issue-111415.rs
@@ -10,7 +10,7 @@
 //@ has - '//*[@id="main-content"]/*[@class="section-header"]' 'Functions'
 //@ has - '//*[@id="main-content"]/*[@class="section-header"]' 'Traits'
 // Checking that there are only three items.
-//@ count - '//*[@id="main-content"]//*[@class="item-name"]' 3
+//@ count - '//*[@id="main-content"]//dt' 3
 //@ has - '//*[@id="main-content"]//a[@href="struct.Bar.html"]' 'Bar'
 //@ has - '//*[@id="main-content"]//a[@href="fn.foo.html"]' 'foo'
 //@ has - '//*[@id="main-content"]//a[@href="trait.Foo.html"]' 'Foo'
diff --git a/tests/rustdoc/overlapping-reexport-105735-2.rs b/tests/rustdoc/overlapping-reexport-105735-2.rs
index 9f823ec5923..fa43924ff4e 100644
--- a/tests/rustdoc/overlapping-reexport-105735-2.rs
+++ b/tests/rustdoc/overlapping-reexport-105735-2.rs
@@ -5,8 +5,8 @@
 #![no_std]
 
 //@ has 'foo/index.html'
-//@ has - '//*[@class="item-name"]/a[@class="type"]' 'AtomicU8'
-//@ has - '//*[@class="item-name"]/a[@class="constant"]' 'AtomicU8'
+//@ has - '//dt/a[@class="type"]' 'AtomicU8'
+//@ has - '//dt/a[@class="constant"]' 'AtomicU8'
 // We also ensure we don't have another item displayed.
 //@ count - '//*[@id="main-content"]/*[@class="section-header"]' 2
 //@ has - '//*[@id="main-content"]/*[@class="section-header"]' 'Type Aliases'
diff --git a/tests/rustdoc/overlapping-reexport-105735.rs b/tests/rustdoc/overlapping-reexport-105735.rs
index 2a2d0fa9830..d1b5c0b6749 100644
--- a/tests/rustdoc/overlapping-reexport-105735.rs
+++ b/tests/rustdoc/overlapping-reexport-105735.rs
@@ -5,8 +5,8 @@
 #![no_std]
 
 //@ has 'foo/index.html'
-//@ has - '//*[@class="item-name"]/a[@class="struct"]' 'AtomicU8'
-//@ has - '//*[@class="item-name"]/a[@class="constant"]' 'AtomicU8'
+//@ has - '//dt/a[@class="struct"]' 'AtomicU8'
+//@ has - '//dt/a[@class="constant"]' 'AtomicU8'
 // We also ensure we don't have another item displayed.
 //@ count - '//*[@id="main-content"]/*[@class="section-header"]' 2
 //@ has - '//*[@id="main-content"]/*[@class="section-header"]' 'Structs'
diff --git a/tests/rustdoc/pub-use-root-path-95873.rs b/tests/rustdoc/pub-use-root-path-95873.rs
index e3d5ee6e315..8e4fd9e8d50 100644
--- a/tests/rustdoc/pub-use-root-path-95873.rs
+++ b/tests/rustdoc/pub-use-root-path-95873.rs
@@ -1,5 +1,5 @@
 // https://github.com/rust-lang/rust/issues/95873
 #![crate_name = "foo"]
 
-//@ has foo/index.html "//*[@class='item-name']" "pub use ::std as x;"
+//@ has foo/index.html "//dt" "pub use ::std as x;"
 pub use ::std as x;
diff --git a/tests/rustdoc/reexport-cfg.rs b/tests/rustdoc/reexport-cfg.rs
index 7270da3d678..73b66824316 100644
--- a/tests/rustdoc/reexport-cfg.rs
+++ b/tests/rustdoc/reexport-cfg.rs
@@ -13,18 +13,18 @@ mod foo {
 }
 
 //@ has 'foo/index.html'
-//@ has - '//*[@class="item-name"]' 'BabarNon-lie'
+//@ has - '//dt' 'BabarNon-lie'
 #[cfg(not(feature = "lie"))]
 pub use crate::foo::Bar as Babar;
 
-//@ has - '//*[@class="item-name"]' 'Babar2Non-cake'
+//@ has - '//dt' 'Babar2Non-cake'
 #[doc(cfg(not(feature = "cake")))]
 pub use crate::foo::Bar2 as Babar2;
 
-//@ has - '//*[@class="item-table"]/li' 'pub use crate::Babar as Elephant;Non-robot'
+//@ has - '//*[@class="item-table reexports"]/dt' 'pub use crate::Babar as Elephant;Non-robot'
 #[cfg(not(feature = "robot"))]
 pub use crate::Babar as Elephant;
 
-//@ has - '//*[@class="item-table"]/li' 'pub use crate::Babar2 as Elephant2;Non-cat'
+//@ has - '//*[@class="item-table reexports"]/dt' 'pub use crate::Babar2 as Elephant2;Non-cat'
 #[doc(cfg(not(feature = "cat")))]
 pub use crate::Babar2 as Elephant2;
diff --git a/tests/rustdoc/reexport-check.rs b/tests/rustdoc/reexport-check.rs
index 0f4e203d1d3..fc10e3aadd0 100644
--- a/tests/rustdoc/reexport-check.rs
+++ b/tests/rustdoc/reexport-check.rs
@@ -8,13 +8,13 @@ extern crate reexport_check;
 #[allow(deprecated, deprecated_in_future)]
 pub use std::i32;
 //@ !has 'foo/index.html' '//code' 'pub use self::string::String;'
-//@ has 'foo/index.html' '//div[@class="item-name"]' 'String'
+//@ has 'foo/index.html' '//dt' 'String'
 pub use std::string::String;
 
 // i32 is deprecated, String is not
 //@ count 'foo/index.html' '//span[@class="stab deprecated"]' 1
 
-//@ has 'foo/index.html' '//div[@class="desc docblock-short"]' 'Docs in original'
+//@ has 'foo/index.html' '//dd' 'Docs in original'
 // this is a no-op, but shows what happens if there's an attribute that isn't a doc-comment
 #[doc(inline)]
 pub use reexport_check::S;
diff --git a/tests/rustdoc/reexport-doc-hidden-inside-private.rs b/tests/rustdoc/reexport-doc-hidden-inside-private.rs
index fac928fc2a3..8e194ef74fb 100644
--- a/tests/rustdoc/reexport-doc-hidden-inside-private.rs
+++ b/tests/rustdoc/reexport-doc-hidden-inside-private.rs
@@ -12,5 +12,5 @@ mod private_module {
 //@ has - '//*[@id="reexport.Foo"]/code' 'pub use crate::private_module::Public as Foo;'
 pub use crate::private_module::Public as Foo;
 // Glob re-exports with no visible items should not be displayed.
-//@ count - '//*[@class="item-table"]/li' 1
+//@ count - '//*[@class="item-table reexports"]/dt' 1
 pub use crate::private_module::*;
diff --git a/tests/rustdoc/reexport-of-reexport-108679.rs b/tests/rustdoc/reexport-of-reexport-108679.rs
index 5c1b4bcbd83..0d2faf71d32 100644
--- a/tests/rustdoc/reexport-of-reexport-108679.rs
+++ b/tests/rustdoc/reexport-of-reexport-108679.rs
@@ -25,5 +25,6 @@ pub mod a {
 //@ has - '//*[@id="main-content"]//*[@id="reexport.A"]' 'pub use self::a::A;'
 //@ has - '//*[@id="main-content"]//*[@id="reexport.B"]' 'pub use self::a::B;'
 // Should only contain "Modules" and "Re-exports".
-//@ count - '//*[@id="main-content"]//*[@class="item-table"]' 2
+//@ count - '//*[@id="main-content"]//*[@class="item-table"]' 1
+//@ count - '//*[@id="main-content"]//*[@class="item-table reexports"]' 1
 pub use self::a::{A, B};
diff --git a/tests/rustdoc/reexport-trait-from-hidden-111064.rs b/tests/rustdoc/reexport-trait-from-hidden-111064.rs
index 84ec818ef33..8b9ad7616ea 100644
--- a/tests/rustdoc/reexport-trait-from-hidden-111064.rs
+++ b/tests/rustdoc/reexport-trait-from-hidden-111064.rs
@@ -5,7 +5,7 @@
 #![crate_name = "foo"]
 
 //@ has 'foo/index.html'
-//@ has - '//*[@id="main-content"]//*[@class="item-name"]/a[@href="trait.Foo.html"]' 'Foo'
+//@ has - '//*[@id="main-content"]//dt/a[@href="trait.Foo.html"]' 'Foo'
 
 //@ has 'foo/trait.Foo.html'
 //@ has - '//*[@id="main-content"]//*[@class="code-header"]' 'fn test()'
diff --git a/tests/rustdoc/short-docblock.rs b/tests/rustdoc/short-docblock.rs
index c80a5025ebe..fa0af85696a 100644
--- a/tests/rustdoc/short-docblock.rs
+++ b/tests/rustdoc/short-docblock.rs
@@ -1,7 +1,7 @@
 #![crate_name = "foo"]
 
-//@ has foo/index.html '//*[@class="desc docblock-short"]' 'fooo'
-//@ !has foo/index.html '//*[@class="desc docblock-short"]/h1' 'fooo'
+//@ has foo/index.html '//dd' 'fooo'
+//@ !has foo/index.html '//dd//h1' 'fooo'
 
 //@ has foo/fn.foo.html '//h2[@id="fooo"]' 'fooo'
 //@ has foo/fn.foo.html '//h2[@id="fooo"]/a[@href="#fooo"]' 'ยง'
@@ -10,8 +10,8 @@
 /// foo
 pub fn foo() {}
 
-//@ has foo/index.html '//*[@class="desc docblock-short"]' 'mooood'
-//@ !has foo/index.html '//*[@class="desc docblock-short"]/h2' 'mooood'
+//@ has foo/index.html '//dd' 'mooood'
+//@ !has foo/index.html '//dd//h2' 'mooood'
 
 //@ has foo/foo/index.html '//h3[@id="mooood"]' 'mooood'
 //@ has foo/foo/index.html '//h3[@id="mooood"]/a[@href="#mooood"]' 'ยง'
@@ -20,8 +20,7 @@ pub fn foo() {}
 /// foo mod
 pub mod foo {}
 
-//@ has foo/index.html '//*[@class="desc docblock-short"]/a[@href=\
-//                      "https://nougat.world"]/code' 'nougat'
+//@ has foo/index.html '//dd/a[@href="https://nougat.world"]/code' 'nougat'
 
 /// [`nougat`](https://nougat.world)
 pub struct Bar;
diff --git a/tests/rustdoc/stability.rs b/tests/rustdoc/stability.rs
index 550eb0bc137..b74abb0e0ba 100644
--- a/tests/rustdoc/stability.rs
+++ b/tests/rustdoc/stability.rs
@@ -5,9 +5,9 @@
 #![stable(feature = "core", since = "1.6.0")]
 
 //@ has stability/index.html
-//@ has - '//ul[@class="item-table"]/li[1]//a' AaStable
-//@ has - '//ul[@class="item-table"]/li[2]//a' ZzStable
-//@ has - '//ul[@class="item-table"]/li[3]//a' Unstable
+//@ has - '//dl[@class="item-table"]/dt[1]//a' AaStable
+//@ has - '//dl[@class="item-table"]/dt[2]//a' ZzStable
+//@ has - '//dl[@class="item-table"]/dt[3]//a' Unstable
 
 #[stable(feature = "rust2", since = "2.2.2")]
 pub struct AaStable;
diff --git a/tests/rustdoc/staged-api-deprecated-unstable-32374.rs b/tests/rustdoc/staged-api-deprecated-unstable-32374.rs
index 556b6fb61ac..1021ce86df0 100644
--- a/tests/rustdoc/staged-api-deprecated-unstable-32374.rs
+++ b/tests/rustdoc/staged-api-deprecated-unstable-32374.rs
@@ -4,11 +4,9 @@
 #![unstable(feature = "test", issue = "32374")]
 #![crate_name="issue_32374"]
 
-//@ matches issue_32374/index.html '//*[@class="item-name"]/span[@class="stab deprecated"]' \
-//      'Deprecated'
-//@ matches issue_32374/index.html '//*[@class="item-name"]/span[@class="stab unstable"]' \
-//      'Experimental'
-//@ matches issue_32374/index.html '//*[@class="desc docblock-short"]/text()' 'Docs'
+//@ matches issue_32374/index.html '//dt/span[@class="stab deprecated"]' 'Deprecated'
+//@ matches issue_32374/index.html '//dt/span[@class="stab unstable"]' 'Experimental'
+//@ matches issue_32374/index.html '//dd/text()' 'Docs'
 
 //@ has issue_32374/struct.T.html '//*[@class="stab deprecated"]/span' '๐Ÿ‘Ž'
 //@ has issue_32374/struct.T.html '//*[@class="stab deprecated"]/span' \
diff --git a/tests/rustdoc/summary-header-46377.rs b/tests/rustdoc/summary-header-46377.rs
index 11445f0dad6..c84f3a65cfb 100644
--- a/tests/rustdoc/summary-header-46377.rs
+++ b/tests/rustdoc/summary-header-46377.rs
@@ -1,6 +1,6 @@
 // https://github.com/rust-lang/rust/issues/46377
 #![crate_name="foo"]
 
-//@ has 'foo/index.html' '//*[@class="desc docblock-short"]' 'Check out this struct!'
+//@ has 'foo/index.html' '//dd' 'Check out this struct!'
 /// # Check out this struct!
 pub struct SomeStruct;
diff --git a/tests/ui/consts/const-ptr-is-null.rs b/tests/ui/consts/const-ptr-is-null.rs
index 92cf87a9782..0abd9afa422 100644
--- a/tests/ui/consts/const-ptr-is-null.rs
+++ b/tests/ui/consts/const-ptr-is-null.rs
@@ -12,7 +12,13 @@ const MAYBE_NULL: () = {
     let ptr = &x as *const i32;
     // This one is still unambiguous...
     assert!(!ptr.is_null());
-    // but once we shift outside the allocation, we might become null.
+    // and in fact, any offset not visible by 4 (the alignment) cannot be null,
+    // even if it goes out-of-bounds...
+    assert!(!ptr.wrapping_byte_add(13).is_null());
+    assert!(!ptr.wrapping_byte_add(18).is_null());
+    assert!(!ptr.wrapping_byte_sub(1).is_null());
+    // ... but once we shift outside the allocation, with an offset divisible by 4,
+    // we might become null.
     assert!(!ptr.wrapping_sub(512).is_null()); //~inside `MAYBE_NULL`
 };
 
diff --git a/tests/ui/consts/const-ptr-is-null.stderr b/tests/ui/consts/const-ptr-is-null.stderr
index f71b3752772..5ef79790d93 100644
--- a/tests/ui/consts/const-ptr-is-null.stderr
+++ b/tests/ui/consts/const-ptr-is-null.stderr
@@ -8,7 +8,7 @@ note: inside `std::ptr::const_ptr::<impl *const T>::is_null::compiletime`
 note: inside `std::ptr::const_ptr::<impl *const i32>::is_null`
   --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
 note: inside `MAYBE_NULL`
-  --> $DIR/const-ptr-is-null.rs:16:14
+  --> $DIR/const-ptr-is-null.rs:22:14
    |
 LL |     assert!(!ptr.wrapping_sub(512).is_null());
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/mir/lint/storage-live.stderr b/tests/ui/mir/lint/storage-live.stderr
index c7012319512..651b8e2327e 100644
--- a/tests/ui/mir/lint/storage-live.stderr
+++ b/tests/ui/mir/lint/storage-live.stderr
@@ -1,4 +1,4 @@
-error: internal compiler error: broken MIR in Item(DefId(0:8 ~ storage_live[HASH]::multiple_storage)) (after pass CheckPackedRef) at bb0[1]:
+error: internal compiler error: broken MIR in Item(DefId(0:8 ~ storage_live[HASH]::multiple_storage)) (after pass CheckForceInline) at bb0[1]:
                                 StorageLive(_1) which already has storage here
   --> $DIR/storage-live.rs:23:13
    |
diff --git a/tests/ui/resolve/resolve-issue-135614-assoc-const.import_trait_associated_functions.stderr b/tests/ui/resolve/resolve-issue-135614-assoc-const.import_trait_associated_functions.stderr
new file mode 100644
index 00000000000..b41fa1818e2
--- /dev/null
+++ b/tests/ui/resolve/resolve-issue-135614-assoc-const.import_trait_associated_functions.stderr
@@ -0,0 +1,19 @@
+error[E0005]: refutable pattern in local binding
+  --> $DIR/resolve-issue-135614-assoc-const.rs:21:9
+   |
+LL |     let DEFAULT: u32 = 0;
+   |         ^^^^^^^ pattern `1_u32..=u32::MAX` not covered
+LL |     const DEFAULT: u32 = 0;
+   |     ------------------ missing patterns are not covered because `DEFAULT` is interpreted as a constant pattern, not a new variable
+   |
+   = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
+   = note: for more information, visit https://doc.rust-lang.org/book/ch19-02-refutability.html
+   = note: the matched value is of type `u32`
+help: introduce a variable instead
+   |
+LL |     let DEFAULT_var: u32 = 0;
+   |         ~~~~~~~~~~~
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0005`.
diff --git a/tests/ui/resolve/resolve-issue-135614-assoc-const.normal.stderr b/tests/ui/resolve/resolve-issue-135614-assoc-const.normal.stderr
new file mode 100644
index 00000000000..908f5bdd897
--- /dev/null
+++ b/tests/ui/resolve/resolve-issue-135614-assoc-const.normal.stderr
@@ -0,0 +1,30 @@
+error[E0658]: `use` associated items of traits is unstable
+  --> $DIR/resolve-issue-135614-assoc-const.rs:6:5
+   |
+LL | use MyDefault::DEFAULT;
+   |     ^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #134691 <https://github.com/rust-lang/rust/issues/134691> for more information
+   = help: add `#![feature(import_trait_associated_functions)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0005]: refutable pattern in local binding
+  --> $DIR/resolve-issue-135614-assoc-const.rs:21:9
+   |
+LL |     let DEFAULT: u32 = 0;
+   |         ^^^^^^^ pattern `1_u32..=u32::MAX` not covered
+LL |     const DEFAULT: u32 = 0;
+   |     ------------------ missing patterns are not covered because `DEFAULT` is interpreted as a constant pattern, not a new variable
+   |
+   = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
+   = note: for more information, visit https://doc.rust-lang.org/book/ch19-02-refutability.html
+   = note: the matched value is of type `u32`
+help: introduce a variable instead
+   |
+LL |     let DEFAULT_var: u32 = 0;
+   |         ~~~~~~~~~~~
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0005, E0658.
+For more information about an error, try `rustc --explain E0005`.
diff --git a/tests/ui/resolve/resolve-issue-135614-assoc-const.rs b/tests/ui/resolve/resolve-issue-135614-assoc-const.rs
new file mode 100644
index 00000000000..5a592922cd4
--- /dev/null
+++ b/tests/ui/resolve/resolve-issue-135614-assoc-const.rs
@@ -0,0 +1,30 @@
+//@ revisions: normal import_trait_associated_functions
+#![cfg_attr(import_trait_associated_functions, feature(import_trait_associated_functions))]
+
+// Makes sure that imported constant can be used in pattern bindings.
+
+use MyDefault::DEFAULT; //[normal]~ ERROR `use` associated items of traits is unstable
+
+trait MyDefault {
+    const DEFAULT: Self;
+}
+
+impl MyDefault for u32 {
+    const DEFAULT: u32 = 0;
+}
+
+impl MyDefault for () {
+    const DEFAULT: () = ();
+}
+
+fn foo(x: u32) -> u32 {
+    let DEFAULT: u32 = 0; //~ ERROR refutable pattern in local binding
+    const DEFAULT: u32 = 0;
+    if let DEFAULT = x { DEFAULT } else { 1 }
+}
+
+fn bar() {
+    let DEFAULT = ();
+}
+
+fn main() {}
diff --git a/tests/ui/resolve/resolve-issue-135614.normal.stderr b/tests/ui/resolve/resolve-issue-135614.normal.stderr
new file mode 100644
index 00000000000..a9adeb0848e
--- /dev/null
+++ b/tests/ui/resolve/resolve-issue-135614.normal.stderr
@@ -0,0 +1,13 @@
+error[E0658]: `use` associated items of traits is unstable
+  --> $DIR/resolve-issue-135614.rs:7:5
+   |
+LL | use A::b;
+   |     ^^^^
+   |
+   = note: see issue #134691 <https://github.com/rust-lang/rust/issues/134691> for more information
+   = help: add `#![feature(import_trait_associated_functions)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/resolve/resolve-issue-135614.rs b/tests/ui/resolve/resolve-issue-135614.rs
new file mode 100644
index 00000000000..fec9499365b
--- /dev/null
+++ b/tests/ui/resolve/resolve-issue-135614.rs
@@ -0,0 +1,15 @@
+//@ revisions: normal import_trait_associated_functions
+//@[import_trait_associated_functions] check-pass
+#![cfg_attr(import_trait_associated_functions, feature(import_trait_associated_functions))]
+
+// Makes sure that imported associated functions are shadowed by the local declarations.
+
+use A::b; //[normal]~ ERROR `use` associated items of traits is unstable
+
+trait A {
+    fn b() {}
+}
+
+fn main() {
+    let b: ();
+}
diff --git a/tests/ui/structs/default-field-values/empty-struct.rs b/tests/ui/structs/default-field-values/empty-struct.rs
new file mode 100644
index 00000000000..c9cb861ae27
--- /dev/null
+++ b/tests/ui/structs/default-field-values/empty-struct.rs
@@ -0,0 +1,21 @@
+#![feature(default_field_values)]
+
+// If an API wants users to always use `..` even if they specify all the fields, they should use a
+// sentinel field. As of now, that field can't be made private so it is only constructable with this
+// syntax, but this might change in the future.
+
+struct A {}
+struct B();
+struct C;
+struct D {
+    x: i32,
+}
+struct E(i32);
+
+fn main() {
+    let _ = A { .. }; //~ ERROR has no fields
+    let _ = B { .. }; //~ ERROR has no fields
+    let _ = C { .. }; //~ ERROR has no fields
+    let _ = D { x: 0, .. };
+    let _ = E { 0: 0, .. };
+}
diff --git a/tests/ui/structs/default-field-values/empty-struct.stderr b/tests/ui/structs/default-field-values/empty-struct.stderr
new file mode 100644
index 00000000000..079e83415b4
--- /dev/null
+++ b/tests/ui/structs/default-field-values/empty-struct.stderr
@@ -0,0 +1,26 @@
+error: `A` has no fields, `..` needs at least one default field in the struct definition
+  --> $DIR/empty-struct.rs:16:17
+   |
+LL |     let _ = A { .. };
+   |             -   ^^
+   |             |
+   |             this type has no fields
+
+error: `B` has no fields, `..` needs at least one default field in the struct definition
+  --> $DIR/empty-struct.rs:17:17
+   |
+LL |     let _ = B { .. };
+   |             -   ^^
+   |             |
+   |             this type has no fields
+
+error: `C` has no fields, `..` needs at least one default field in the struct definition
+  --> $DIR/empty-struct.rs:18:17
+   |
+LL |     let _ = C { .. };
+   |             -   ^^
+   |             |
+   |             this type has no fields
+
+error: aborting due to 3 previous errors
+