about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--.editorconfig1
-rw-r--r--compiler/rustc_abi/src/lib.rs10
-rw-r--r--compiler/rustc_builtin_macros/src/test_harness.rs2
-rw-r--r--compiler/rustc_codegen_ssa/src/back/link.rs2
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/operand.rs25
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/rvalue.rs107
-rw-r--r--compiler/rustc_feature/src/active.rs2
-rw-r--r--compiler/rustc_hir_analysis/messages.ftl36
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/mod.rs6
-rw-r--r--compiler/rustc_hir_analysis/src/check/compare_impl_item.rs4
-rw-r--r--compiler/rustc_hir_analysis/src/check/wfcheck.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/collect.rs165
-rw-r--r--compiler/rustc_hir_analysis/src/collect/item_bounds.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/errors.rs124
-rw-r--r--compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs12
-rw-r--r--compiler/rustc_hir_typeck/src/closure.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs29
-rw-r--r--compiler/rustc_hir_typeck/src/intrinsicck.rs7
-rw-r--r--compiler/rustc_hir_typeck/src/method/confirm.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/method/probe.rs30
-rw-r--r--compiler/rustc_hir_typeck/src/pat.rs23
-rw-r--r--compiler/rustc_infer/src/traits/util.rs209
-rw-r--r--compiler/rustc_interface/src/passes.rs4
-rw-r--r--compiler/rustc_lint/src/unused.rs35
-rw-r--r--compiler/rustc_metadata/src/rmeta/decoder.rs2
-rw-r--r--compiler/rustc_metadata/src/rmeta/encoder.rs4
-rw-r--r--compiler/rustc_middle/src/arena.rs1
-rw-r--r--compiler/rustc_middle/src/infer/canonical.rs12
-rw-r--r--compiler/rustc_middle/src/metadata.rs26
-rw-r--r--compiler/rustc_middle/src/query/erase.rs1
-rw-r--r--compiler/rustc_middle/src/query/mod.rs2
-rw-r--r--compiler/rustc_middle/src/traits/solve.rs16
-rw-r--r--compiler/rustc_middle/src/ty/context.rs2
-rw-r--r--compiler/rustc_middle/src/ty/layout.rs95
-rw-r--r--compiler/rustc_mir_transform/src/const_prop.rs5
-rw-r--r--compiler/rustc_mir_transform/src/const_prop_lint.rs5
-rw-r--r--compiler/rustc_passes/src/stability.rs2
-rw-r--r--compiler/rustc_privacy/src/lib.rs16
-rw-r--r--compiler/rustc_resolve/src/build_reduced_graph.rs2
-rw-r--r--compiler/rustc_resolve/src/check_unused.rs2
-rw-r--r--compiler/rustc_resolve/src/imports.rs21
-rw-r--r--compiler/rustc_resolve/src/late.rs4
-rw-r--r--compiler/rustc_resolve/src/late/diagnostics.rs15
-rw-r--r--compiler/rustc_session/src/config.rs2
-rw-r--r--compiler/rustc_session/src/session.rs5
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--compiler/rustc_target/src/spec/aarch64_unknown_nto_qnx_710.rs1
-rw-r--r--compiler/rustc_target/src/spec/i586_pc_nto_qnx700.rs24
-rw-r--r--compiler/rustc_target/src/spec/mod.rs1
-rw-r--r--compiler/rustc_target/src/spec/nto_qnx_base.rs1
-rw-r--r--compiler/rustc_target/src/spec/x86_64_pc_nto_qnx710.rs1
-rw-r--r--compiler/rustc_trait_selection/src/solve/assembly/mod.rs (renamed from compiler/rustc_trait_selection/src/solve/assembly.rs)80
-rw-r--r--compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs (renamed from compiler/rustc_trait_selection/src/solve/trait_goals/structural_traits.rs)18
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt.rs4
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs2
-rw-r--r--compiler/rustc_trait_selection/src/solve/mod.rs91
-rw-r--r--compiler/rustc_trait_selection/src/solve/project_goals.rs3
-rw-r--r--compiler/rustc_trait_selection/src/solve/trait_goals.rs8
-rw-r--r--compiler/rustc_trait_selection/src/traits/auto_trait.rs8
-rw-r--r--compiler/rustc_trait_selection/src/traits/coherence.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs4
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/mod.rs7
-rw-r--r--compiler/rustc_trait_selection/src/traits/object_safety.rs7
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs15
-rw-r--r--compiler/rustc_trait_selection/src/traits/wf.rs4
-rw-r--r--compiler/rustc_ty_utils/messages.ftl10
-rw-r--r--compiler/rustc_ty_utils/src/errors.rs33
-rw-r--r--compiler/rustc_ty_utils/src/instance.rs9
-rw-r--r--compiler/rustc_ty_utils/src/layout.rs39
-rw-r--r--compiler/rustc_ty_utils/src/lib.rs2
-rw-r--r--config.example.toml2
-rw-r--r--library/core/src/iter/range.rs32
-rw-r--r--library/core/src/iter/traits/double_ended.rs11
-rw-r--r--library/core/src/iter/traits/iterator.rs13
-rw-r--r--library/core/src/iter/traits/marker.rs3
-rw-r--r--library/core/src/lib.rs2
-rw-r--r--library/core/src/marker.rs2
-rw-r--r--library/core/src/mem/maybe_uninit.rs16
-rw-r--r--library/core/src/primitive_docs.rs1
-rw-r--r--library/core/tests/iter/consts.rs36
-rw-r--r--library/core/tests/iter/mod.rs2
-rw-r--r--library/core/tests/lib.rs3
-rw-r--r--library/std/src/collections/mod.rs2
-rw-r--r--library/std/src/primitive_docs.rs1
-rw-r--r--src/bootstrap/CHANGELOG.md1
-rw-r--r--src/bootstrap/channel.rs6
-rw-r--r--src/bootstrap/config.rs12
-rw-r--r--src/bootstrap/defaults/config.codegen.toml2
-rw-r--r--src/bootstrap/dist.rs53
-rw-r--r--src/bootstrap/lib.rs16
-rw-r--r--src/bootstrap/tool.rs2
-rw-r--r--src/ci/docker/host-x86_64/x86_64-gnu-distcheck/Dockerfile2
-rw-r--r--src/doc/rustc/src/platform-support.md1
-rw-r--r--src/doc/rustc/src/platform-support/nto-qnx.md15
-rw-r--r--src/doc/unstable-book/src/language-features/lang-items.md16
-rw-r--r--src/doc/unstable-book/src/language-features/plugin.md4
-rw-r--r--src/etc/installer/msi/rust.wxs6
-rw-r--r--src/etc/installer/pkg/Distribution.xml6
-rw-r--r--src/librustdoc/clean/inline.rs3
-rw-r--r--src/librustdoc/clean/mod.rs169
-rw-r--r--src/librustdoc/html/format.rs4
-rw-r--r--src/librustdoc/html/render/mod.rs358
-rw-r--r--src/librustdoc/html/render/print_item.rs680
-rw-r--r--src/librustdoc/html/sources.rs6
-rw-r--r--src/librustdoc/html/templates/item_union.html23
-rw-r--r--src/librustdoc/visit_ast.rs14
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs2
-rw-r--r--src/tools/clippy/clippy_utils/src/lib.rs2
-rw-r--r--src/tools/compiletest/src/runtest.rs5
-rw-r--r--tests/codegen/intrinsics/transmute-x64.rs35
-rw-r--r--tests/codegen/intrinsics/transmute.rs132
-rw-r--r--tests/ui/const-generics/transmute-fail.rs35
-rw-r--r--tests/ui/const-generics/transmute-fail.stderr52
-rw-r--r--tests/ui/const-generics/transmute.rs83
-rw-r--r--tests/ui/const-generics/transmute_no_gate.rs91
-rw-r--r--tests/ui/const-generics/transmute_no_gate.stderr84
-rw-r--r--tests/ui/consts/const-fn-error.rs1
-rw-r--r--tests/ui/consts/const-fn-error.stderr19
-rw-r--r--tests/ui/consts/const-for.rs1
-rw-r--r--tests/ui/consts/const-for.stderr18
-rw-r--r--tests/ui/dyn-star/align.normal.stderr13
-rw-r--r--tests/ui/dyn-star/align.over_aligned.stderr6
-rw-r--r--tests/ui/dyn-star/align.rs3
-rw-r--r--tests/ui/dyn-star/check-size-at-cast-polymorphic-bad.rs2
-rw-r--r--tests/ui/dyn-star/check-size-at-cast-polymorphic-bad.stderr2
-rw-r--r--tests/ui/dyn-star/check-size-at-cast.rs2
-rw-r--r--tests/ui/dyn-star/check-size-at-cast.stderr2
-rw-r--r--tests/ui/dyn-star/upcast.stderr2
-rw-r--r--tests/ui/higher-rank-trait-bounds/issue-95230.new.stderr18
-rw-r--r--tests/ui/higher-rank-trait-bounds/issue-95230.rs6
-rw-r--r--tests/ui/index_message.rs4
-rw-r--r--tests/ui/index_message.stderr2
-rw-r--r--tests/ui/issues/issue-27842.rs5
-rw-r--r--tests/ui/issues/issue-27842.stderr12
-rw-r--r--tests/ui/missing/missing-items/missing-const-parameter.rs24
-rw-r--r--tests/ui/missing/missing-items/missing-const-parameter.stderr64
-rw-r--r--tests/ui/simd/monomorphize-heterogeneous.rs9
-rw-r--r--tests/ui/simd/monomorphize-heterogeneous.stderr9
-rw-r--r--tests/ui/structs/struct-tuple-field-names.rs3
-rw-r--r--tests/ui/structs/struct-tuple-field-names.stderr20
-rw-r--r--tests/ui/traits/new-solver/alias_eq_dont_use_normalizes_to_if_substs_eq.rs2
-rw-r--r--tests/ui/traits/new-solver/pointer-like.rs2
-rw-r--r--tests/ui/traits/new-solver/pointer-like.stderr2
-rw-r--r--tests/ui/traits/new-solver/prefer-candidate-no-constraints.rs22
-rw-r--r--tests/ui/traits/new-solver/prefer-param-env-on-ambiguity.rs10
-rw-r--r--tests/ui/traits/new-solver/recursive-self-normalization-2.stderr13
-rw-r--r--tests/ui/traits/new-solver/recursive-self-normalization.stderr13
-rw-r--r--tests/ui/traits/non_lifetime_binders/method-probe.rs16
-rw-r--r--tests/ui/traits/non_lifetime_binders/method-probe.stderr11
-rw-r--r--tests/ui/typeck/typeck_type_placeholder_item.rs1
-rw-r--r--tests/ui/typeck/typeck_type_placeholder_item.stderr15
152 files changed, 2543 insertions, 1315 deletions
diff --git a/.editorconfig b/.editorconfig
index ec6e107d547..03aab32bfc6 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -11,6 +11,7 @@ trim_trailing_whitespace = true
 insert_final_newline = true
 indent_style = space
 indent_size = 4
+max_line_length = 100
 
 [*.md]
 # double whitespace at end of line
diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs
index da91776ef67..76765c8a0aa 100644
--- a/compiler/rustc_abi/src/lib.rs
+++ b/compiler/rustc_abi/src/lib.rs
@@ -1522,6 +1522,16 @@ impl<'a> Layout<'a> {
     pub fn size(self) -> Size {
         self.0.0.size
     }
+
+    /// Whether the layout is from a type that implements [`std::marker::PointerLike`].
+    ///
+    /// Currently, that means that the type is pointer-sized, pointer-aligned,
+    /// and has a scalar ABI.
+    pub fn is_pointer_like(self, data_layout: &TargetDataLayout) -> bool {
+        self.size() == data_layout.pointer_size
+            && self.align().abi == data_layout.pointer_align.abi
+            && matches!(self.abi(), Abi::Scalar(..))
+    }
 }
 
 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs
index 43ab6c04428..80f497333a6 100644
--- a/compiler/rustc_builtin_macros/src/test_harness.rs
+++ b/compiler/rustc_builtin_macros/src/test_harness.rs
@@ -53,7 +53,7 @@ pub fn inject(krate: &mut ast::Crate, sess: &Session, resolver: &mut dyn Resolve
     // even in non-test builds
     let test_runner = get_test_runner(span_diagnostic, &krate);
 
-    if sess.opts.test {
+    if sess.is_test_crate() {
         let panic_strategy = match (panic_strategy, sess.opts.unstable_opts.panic_abort_tests) {
             (PanicStrategy::Abort, true) => PanicStrategy::Abort,
             (PanicStrategy::Abort, false) => {
diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs
index 6a0d0ca55c2..7a5fa5a370c 100644
--- a/compiler/rustc_codegen_ssa/src/back/link.rs
+++ b/compiler/rustc_codegen_ssa/src/back/link.rs
@@ -2301,7 +2301,7 @@ fn add_native_libs_from_crate(
                         || (whole_archive == None
                             && bundle
                             && cnum == LOCAL_CRATE
-                            && sess.opts.test);
+                            && sess.is_test_crate());
 
                     if bundle && cnum != LOCAL_CRATE {
                         if let Some(filename) = lib.filename {
diff --git a/compiler/rustc_codegen_ssa/src/mir/operand.rs b/compiler/rustc_codegen_ssa/src/mir/operand.rs
index ddef4aaee3b..b37797fef4c 100644
--- a/compiler/rustc_codegen_ssa/src/mir/operand.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/operand.rs
@@ -259,6 +259,31 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
 }
 
 impl<'a, 'tcx, V: CodegenObject> OperandValue<V> {
+    /// Returns an `OperandValue` that's generally UB to use in any way.
+    ///
+    /// Depending on the `layout`, returns an `Immediate` or `Pair` containing
+    /// poison value(s), or a `Ref` containing a poison pointer.
+    ///
+    /// Supports sized types only.
+    pub fn poison<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
+        bx: &mut Bx,
+        layout: TyAndLayout<'tcx>,
+    ) -> OperandValue<V> {
+        assert!(layout.is_sized());
+        if bx.cx().is_backend_immediate(layout) {
+            let ibty = bx.cx().immediate_backend_type(layout);
+            OperandValue::Immediate(bx.const_poison(ibty))
+        } else if bx.cx().is_backend_scalar_pair(layout) {
+            let ibty0 = bx.cx().scalar_pair_element_backend_type(layout, 0, true);
+            let ibty1 = bx.cx().scalar_pair_element_backend_type(layout, 1, true);
+            OperandValue::Pair(bx.const_poison(ibty0), bx.const_poison(ibty1))
+        } else {
+            let bty = bx.cx().backend_type(layout);
+            let ptr_bty = bx.cx().type_ptr_to(bty);
+            OperandValue::Ref(bx.const_poison(ptr_bty), None, layout.align.abi)
+        }
+    }
+
     pub fn store<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
         self,
         bx: &mut Bx,
diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
index 400512fe4e9..d88226f5db0 100644
--- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
@@ -158,17 +158,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
         debug_assert!(src.layout.is_sized());
         debug_assert!(dst.layout.is_sized());
 
-        if src.layout.size != dst.layout.size
-            || src.layout.abi.is_uninhabited()
-            || dst.layout.abi.is_uninhabited()
-        {
-            // In all of these cases it's UB to run this transmute, but that's
-            // known statically so might as well trap for it, rather than just
-            // making it unreachable.
-            bx.abort();
-            return;
-        }
-
         if let Some(val) = self.codegen_transmute_operand(bx, src, dst.layout) {
             val.store(bx, dst);
             return;
@@ -202,8 +191,21 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
         operand: OperandRef<'tcx, Bx::Value>,
         cast: TyAndLayout<'tcx>,
     ) -> Option<OperandValue<Bx::Value>> {
-        // Callers already checked that the layout sizes match
-        debug_assert_eq!(operand.layout.size, cast.size);
+        // Check for transmutes that are always UB.
+        if operand.layout.size != cast.size
+            || operand.layout.abi.is_uninhabited()
+            || cast.abi.is_uninhabited()
+        {
+            if !operand.layout.abi.is_uninhabited() {
+                // Since this is known statically and the input could have existed
+                // without already having hit UB, might as well trap for it.
+                bx.abort();
+            }
+
+            // Because this transmute is UB, return something easy to generate,
+            // since it's fine that later uses of the value are probably UB.
+            return Some(OperandValue::poison(bx, cast));
+        }
 
         let operand_kind = self.value_kind(operand.layout);
         let cast_kind = self.value_kind(cast);
@@ -222,10 +224,20 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                     bug!("Found {operand_kind:?} for operand {operand:?}");
                 };
                 if let OperandValueKind::Immediate(out_scalar) = cast_kind {
-                    let cast_bty = bx.backend_type(cast);
-                    Some(OperandValue::Immediate(Self::transmute_immediate(
-                        bx, imm, in_scalar, out_scalar, cast_bty,
-                    )))
+                    match (in_scalar, out_scalar) {
+                        (ScalarOrZst::Zst, ScalarOrZst::Zst) => {
+                            Some(OperandRef::new_zst(bx, cast).val)
+                        }
+                        (ScalarOrZst::Scalar(in_scalar), ScalarOrZst::Scalar(out_scalar))
+                            if in_scalar.size(self.cx) == out_scalar.size(self.cx) =>
+                        {
+                            let cast_bty = bx.backend_type(cast);
+                            Some(OperandValue::Immediate(
+                                self.transmute_immediate(bx, imm, in_scalar, out_scalar, cast_bty),
+                            ))
+                        }
+                        _ => None,
+                    }
                 } else {
                     None
                 }
@@ -234,12 +246,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 let OperandValueKind::Pair(in_a, in_b) = operand_kind else {
                     bug!("Found {operand_kind:?} for operand {operand:?}");
                 };
-                if let OperandValueKind::Pair(out_a, out_b) = cast_kind {
+                if let OperandValueKind::Pair(out_a, out_b) = cast_kind
+                    && in_a.size(self.cx) == out_a.size(self.cx)
+                    && in_b.size(self.cx) == out_b.size(self.cx)
+                {
                     let out_a_ibty = bx.scalar_pair_element_backend_type(cast, 0, false);
                     let out_b_ibty = bx.scalar_pair_element_backend_type(cast, 1, false);
                     Some(OperandValue::Pair(
-                        Self::transmute_immediate(bx, imm_a, in_a, out_a, out_a_ibty),
-                        Self::transmute_immediate(bx, imm_b, in_b, out_b, out_b_ibty),
+                        self.transmute_immediate(bx, imm_a, in_a, out_a, out_a_ibty),
+                        self.transmute_immediate(bx, imm_b, in_b, out_b, out_b_ibty),
                     ))
                 } else {
                     None
@@ -254,12 +269,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
     /// `to_backend_ty` must be the *non*-immediate backend type (so it will be
     /// `i8`, not `i1`, for `bool`-like types.)
     fn transmute_immediate(
+        &self,
         bx: &mut Bx,
         mut imm: Bx::Value,
         from_scalar: abi::Scalar,
         to_scalar: abi::Scalar,
         to_backend_ty: Bx::Type,
     ) -> Bx::Value {
+        debug_assert_eq!(from_scalar.size(self.cx), to_scalar.size(self.cx));
+
         use abi::Primitive::*;
         imm = bx.from_immediate(imm);
         imm = match (from_scalar.primitive(), to_scalar.primitive()) {
@@ -831,14 +849,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 let operand_ty = operand.ty(self.mir, self.cx.tcx());
                 let cast_layout = self.cx.layout_of(self.monomorphize(cast_ty));
                 let operand_layout = self.cx.layout_of(self.monomorphize(operand_ty));
-                if operand_layout.size != cast_layout.size
-                    || operand_layout.abi.is_uninhabited()
-                    || cast_layout.abi.is_uninhabited()
-                {
-                    // Send UB cases to the full form so the operand version can
-                    // `bitcast` without worrying about malformed IR.
-                    return false;
-                }
 
                 match (self.value_kind(operand_layout), self.value_kind(cast_layout)) {
                     // Can always load from a pointer as needed
@@ -847,9 +857,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                     // Need to generate an `alloc` to get a pointer from an immediate
                     (OperandValueKind::Immediate(..) | OperandValueKind::Pair(..), OperandValueKind::Ref) => false,
 
-                    // When we have scalar immediates, we can convert them as needed
-                    (OperandValueKind::Immediate(..), OperandValueKind::Immediate(..)) |
-                    (OperandValueKind::Pair(..), OperandValueKind::Pair(..)) => true,
+                    // When we have scalar immediates, we can only convert things
+                    // where the sizes match, to avoid endianness questions.
+                    (OperandValueKind::Immediate(a), OperandValueKind::Immediate(b)) =>
+                        a.size(self.cx) == b.size(self.cx),
+                    (OperandValueKind::Pair(a0, a1), OperandValueKind::Pair(b0, b1)) =>
+                        a0.size(self.cx) == b0.size(self.cx) && a1.size(self.cx) == b1.size(self.cx),
 
                     // Send mixings between scalars and pairs through the memory route
                     // FIXME: Maybe this could use insertvalue/extractvalue instead?
@@ -887,13 +900,18 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
         if self.cx.is_backend_immediate(layout) {
             debug_assert!(!self.cx.is_backend_scalar_pair(layout));
             OperandValueKind::Immediate(match layout.abi {
-                abi::Abi::Scalar(s) => s,
-                abi::Abi::Vector { element, .. } => element,
-                x => bug!("Couldn't translate {x:?} as backend immediate"),
+                abi::Abi::Scalar(s) => ScalarOrZst::Scalar(s),
+                abi::Abi::Vector { element, .. } => ScalarOrZst::Scalar(element),
+                _ if layout.is_zst() => ScalarOrZst::Zst,
+                x => span_bug!(self.mir.span, "Couldn't translate {x:?} as backend immediate"),
             })
         } else if self.cx.is_backend_scalar_pair(layout) {
             let abi::Abi::ScalarPair(s1, s2) = layout.abi else {
-                bug!("Couldn't translate {:?} as backend scalar pair", layout.abi)
+                span_bug!(
+                    self.mir.span,
+                    "Couldn't translate {:?} as backend scalar pair",
+                    layout.abi,
+                );
             };
             OperandValueKind::Pair(s1, s2)
         } else {
@@ -902,9 +920,26 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
     }
 }
 
+/// The variants of this match [`OperandValue`], giving details about the
+/// backend values that will be held in that other type.
 #[derive(Debug, Copy, Clone)]
 enum OperandValueKind {
     Ref,
-    Immediate(abi::Scalar),
+    Immediate(ScalarOrZst),
     Pair(abi::Scalar, abi::Scalar),
 }
+
+#[derive(Debug, Copy, Clone)]
+enum ScalarOrZst {
+    Zst,
+    Scalar(abi::Scalar),
+}
+
+impl ScalarOrZst {
+    pub fn size(self, cx: &impl abi::HasDataLayout) -> abi::Size {
+        match self {
+            ScalarOrZst::Zst => abi::Size::ZERO,
+            ScalarOrZst::Scalar(s) => s.size(cx),
+        }
+    }
+}
diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs
index 84114b27f41..426c6727adc 100644
--- a/compiler/rustc_feature/src/active.rs
+++ b/compiler/rustc_feature/src/active.rs
@@ -518,6 +518,8 @@ declare_features! (
     /// Allows dyn upcasting trait objects via supertraits.
     /// Dyn upcasting is casting, e.g., `dyn Foo -> dyn Bar` where `Foo: Bar`.
     (active, trait_upcasting, "1.56.0", Some(65991), None),
+    /// Allows for transmuting between arrays with sizes that contain generic consts.
+    (active, transmute_generic_consts, "CURRENT_RUSTC_VERSION", Some(109929), None),
     /// Allows #[repr(transparent)] on unions (RFC 2645).
     (active, transparent_unions, "1.37.0", Some(60405), None),
     /// Allows inconsistent bounds in where clauses.
diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl
index a57f3987849..1d7965ff5f6 100644
--- a/compiler/rustc_hir_analysis/messages.ftl
+++ b/compiler/rustc_hir_analysis/messages.ftl
@@ -189,3 +189,39 @@ hir_analysis_return_type_notation_equality_bound =
 
 hir_analysis_return_type_notation_missing_method =
     cannot find associated function `{$assoc_name}` in trait `{$trait_name}`
+
+hir_analysis_placeholder_not_allowed_item_signatures = the placeholder `_` is not allowed within types on item signatures for {$kind}
+    .label = not allowed in type signatures
+
+hir_analysis_associated_type_trait_uninferred_generic_params = cannot use the associated type of a trait with uninferred generic parameters
+    .suggestion = use a fully qualified path with inferred lifetimes
+
+hir_analysis_associated_type_trait_uninferred_generic_params_multipart_suggestion = use a fully qualified path with explicit lifetimes
+
+hir_analysis_enum_discriminant_overflowed = enum discriminant overflowed
+    .label = overflowed on value after {$discr}
+    .note = explicitly set `{$item_name} = {$wrapped_discr}` if that is desired outcome
+
+hir_analysis_paren_sugar_attribute = the `#[rustc_paren_sugar]` attribute is a temporary means of controlling which traits can use parenthetical notation
+    .help = add `#![feature(unboxed_closures)]` to the crate attributes to use it
+
+hir_analysis_must_implement_one_of_attribute = the `#[rustc_must_implement_one_of]` attribute must be used with at least 2 args
+
+hir_analysis_must_be_name_of_associated_function = must be a name of an associated function
+
+hir_analysis_function_not_have_default_implementation = function doesn't have a default implementation
+    .note = required by this annotation
+
+hir_analysis_must_implement_not_function = not a function
+
+hir_analysis_must_implement_not_function_span_note = required by this annotation
+
+hir_analysis_must_implement_not_function_note = all `#[rustc_must_implement_one_of]` arguments must be associated function names
+
+hir_analysis_function_not_found_in_trait = function not found in this trait
+
+hir_analysis_functions_names_duplicated = functions names are duplicated
+    .note = all `#[rustc_must_implement_one_of]` arguments must be unique
+
+hir_analysis_simd_ffi_highly_experimental = use of SIMD type{$snip} in FFI is highly experimental and may result in invalid code
+    .help = add `#![feature(simd_ffi)]` to the crate attributes to enable
diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs
index e0408b36af9..acca3fa2641 100644
--- a/compiler/rustc_hir_analysis/src/astconv/mod.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs
@@ -33,9 +33,9 @@ use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKin
 use rustc_middle::middle::stability::AllowUnstable;
 use rustc_middle::ty::fold::FnMutDelegate;
 use rustc_middle::ty::subst::{self, GenericArgKind, InternalSubsts, SubstsRef};
-use rustc_middle::ty::DynKind;
 use rustc_middle::ty::GenericParamDefKind;
 use rustc_middle::ty::{self, Const, IsSuggestable, Ty, TyCtxt, TypeVisitableExt};
+use rustc_middle::ty::{DynKind, ToPredicate};
 use rustc_session::lint::builtin::{AMBIGUOUS_ASSOCIATED_ITEMS, BARE_TRAIT_OBJECTS};
 use rustc_span::edit_distance::find_best_match_for_name;
 use rustc_span::edition::Edition;
@@ -1526,8 +1526,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
 
         for (base_trait_ref, span, constness) in regular_traits_refs_spans {
             assert_eq!(constness, ty::BoundConstness::NotConst);
-
-            for pred in traits::elaborate_trait_ref(tcx, base_trait_ref) {
+            let base_pred: ty::Predicate<'tcx> = base_trait_ref.to_predicate(tcx);
+            for pred in traits::elaborate(tcx, [base_pred]) {
                 debug!("conv_object_ty_poly_trait_ref: observing object predicate `{:?}`", pred);
 
                 let bound_predicate = pred.kind();
diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
index 2d509a114ad..f6c2004c4a6 100644
--- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
+++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
@@ -2034,7 +2034,7 @@ pub(super) fn check_type_bounds<'tcx>(
         ObligationCause::new(impl_ty_span, impl_ty_def_id, code)
     };
 
-    let obligations = tcx
+    let obligations: Vec<_> = tcx
         .bound_explicit_item_bounds(trait_ty.def_id)
         .subst_iter_copied(tcx, rebased_substs)
         .map(|(concrete_ty_bound, span)| {
@@ -2044,7 +2044,7 @@ pub(super) fn check_type_bounds<'tcx>(
         .collect();
     debug!("check_type_bounds: item_bounds={:?}", obligations);
 
-    for mut obligation in util::elaborate_obligations(tcx, obligations) {
+    for mut obligation in util::elaborate(tcx, obligations) {
         let normalized_predicate =
             ocx.normalize(&normalize_cause, normalize_param_env, obligation.predicate);
         debug!("compare_projection_bounds: normalized predicate = {:?}", normalized_predicate);
diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
index 24cc58ee344..c03621fcfb2 100644
--- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs
+++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
@@ -1908,7 +1908,7 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> {
 
         let predicates_with_span = tcx.predicates_of(self.body_def_id).predicates.iter().copied();
         // Check elaborated bounds.
-        let implied_obligations = traits::elaborate_predicates_with_span(tcx, predicates_with_span);
+        let implied_obligations = traits::elaborate(tcx, predicates_with_span);
 
         for (pred, obligation_span) in implied_obligations {
             // We lower empty bounds like `Vec<dyn Copy>:` as
diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs
index c41e96290df..50862e34262 100644
--- a/compiler/rustc_hir_analysis/src/collect.rs
+++ b/compiler/rustc_hir_analysis/src/collect.rs
@@ -20,7 +20,7 @@ use crate::errors;
 use hir::def::DefKind;
 use rustc_data_structures::captures::Captures;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed, StashKey};
+use rustc_errors::{Applicability, DiagnosticBuilder, ErrorGuaranteed, StashKey};
 use rustc_hir as hir;
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_hir::intravisit::{self, Visitor};
@@ -333,17 +333,7 @@ fn bad_placeholder<'tcx>(
     let kind = if kind.ends_with('s') { format!("{}es", kind) } else { format!("{}s", kind) };
 
     spans.sort();
-    let mut err = struct_span_err!(
-        tcx.sess,
-        spans.clone(),
-        E0121,
-        "the placeholder `_` is not allowed within types on item signatures for {}",
-        kind
-    );
-    for span in spans {
-        err.span_label(span, "not allowed in type signatures");
-    }
-    err
+    tcx.sess.create_err(errors::PlaceholderNotAllowedItemSignatures { spans, kind })
 }
 
 impl<'tcx> ItemCtxt<'tcx> {
@@ -419,13 +409,8 @@ impl<'tcx> AstConv<'tcx> for ItemCtxt<'tcx> {
             self.tcx().mk_projection(item_def_id, item_substs)
         } else {
             // There are no late-bound regions; we can just ignore the binder.
-            let mut err = struct_span_err!(
-                self.tcx().sess,
-                span,
-                E0212,
-                "cannot use the associated type of a trait \
-                 with uninferred generic parameters"
-            );
+            let (mut mpart_sugg, mut inferred_sugg) = (None, None);
+            let mut bound = String::new();
 
             match self.node() {
                 hir::Node::Field(_) | hir::Node::Ctor(_) | hir::Node::Variant(_) => {
@@ -444,31 +429,25 @@ impl<'tcx> AstConv<'tcx> for ItemCtxt<'tcx> {
                                     (bound.span.shrink_to_lo(), format!("{}, ", lt_name))
                                 }
                             };
-                            let suggestions = vec![
-                                (lt_sp, sugg),
-                                (
-                                    span.with_hi(item_segment.ident.span.lo()),
-                                    format!(
-                                        "{}::",
-                                        // Replace the existing lifetimes with a new named lifetime.
-                                        self.tcx.replace_late_bound_regions_uncached(
-                                            poly_trait_ref,
-                                            |_| {
-                                                self.tcx.mk_re_early_bound(ty::EarlyBoundRegion {
-                                                    def_id: item_def_id,
-                                                    index: 0,
-                                                    name: Symbol::intern(&lt_name),
-                                                })
-                                            }
-                                        ),
+                            mpart_sugg = Some(errors::AssociatedTypeTraitUninferredGenericParamsMultipartSuggestion {
+                                fspan: lt_sp,
+                                first: sugg,
+                                sspan: span.with_hi(item_segment.ident.span.lo()),
+                                second: format!(
+                                    "{}::",
+                                    // Replace the existing lifetimes with a new named lifetime.
+                                    self.tcx.replace_late_bound_regions_uncached(
+                                        poly_trait_ref,
+                                        |_| {
+                                            self.tcx.mk_re_early_bound(ty::EarlyBoundRegion {
+                                                def_id: item_def_id,
+                                                index: 0,
+                                                name: Symbol::intern(&lt_name),
+                                            })
+                                        }
                                     ),
                                 ),
-                            ];
-                            err.multipart_suggestion(
-                                "use a fully qualified path with explicit lifetimes",
-                                suggestions,
-                                Applicability::MaybeIncorrect,
-                            );
+                            });
                         }
                         _ => {}
                     }
@@ -482,20 +461,23 @@ impl<'tcx> AstConv<'tcx> for ItemCtxt<'tcx> {
                 | hir::Node::ForeignItem(_)
                 | hir::Node::TraitItem(_)
                 | hir::Node::ImplItem(_) => {
-                    err.span_suggestion_verbose(
-                        span.with_hi(item_segment.ident.span.lo()),
-                        "use a fully qualified path with inferred lifetimes",
-                        format!(
-                            "{}::",
-                            // Erase named lt, we want `<A as B<'_>::C`, not `<A as B<'a>::C`.
-                            self.tcx.anonymize_bound_vars(poly_trait_ref).skip_binder(),
-                        ),
-                        Applicability::MaybeIncorrect,
+                    inferred_sugg = Some(span.with_hi(item_segment.ident.span.lo()));
+                    bound = format!(
+                        "{}::",
+                        // Erase named lt, we want `<A as B<'_>::C`, not `<A as B<'a>::C`.
+                        self.tcx.anonymize_bound_vars(poly_trait_ref).skip_binder(),
                     );
                 }
                 _ => {}
             }
-            self.tcx().ty_error(err.emit())
+            self.tcx().ty_error(self.tcx().sess.emit_err(
+                errors::AssociatedTypeTraitUninferredGenericParams {
+                    span,
+                    inferred_sugg,
+                    bound,
+                    mpart_sugg,
+                },
+            ))
         }
     }
 
@@ -763,14 +745,12 @@ fn convert_enum_variant_types(tcx: TyCtxt<'_>, def_id: DefId) {
                 Some(discr)
             } else {
                 let span = tcx.def_span(variant.def_id);
-                struct_span_err!(tcx.sess, span, E0370, "enum discriminant overflowed")
-                    .span_label(span, format!("overflowed on value after {}", prev_discr.unwrap()))
-                    .note(&format!(
-                        "explicitly set `{} = {}` if that is desired outcome",
-                        tcx.item_name(variant.def_id),
-                        wrapped_discr
-                    ))
-                    .emit();
+                tcx.sess.emit_err(errors::EnumDiscriminantOverflowed {
+                    span,
+                    discr: prev_discr.unwrap().to_string(),
+                    item_name: tcx.item_name(variant.def_id),
+                    wrapped_discr: wrapped_discr.to_string(),
+                });
                 None
             }
             .unwrap_or(wrapped_discr),
@@ -915,14 +895,7 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef {
 
     let paren_sugar = tcx.has_attr(def_id, sym::rustc_paren_sugar);
     if paren_sugar && !tcx.features().unboxed_closures {
-        tcx.sess
-            .struct_span_err(
-                item.span,
-                "the `#[rustc_paren_sugar]` attribute is a temporary means of controlling \
-                 which traits can use parenthetical notation",
-            )
-            .help("add `#![feature(unboxed_closures)]` to the crate attributes to use it")
-            .emit();
+        tcx.sess.emit_err(errors::ParenSugarAttribute { span: item.span });
     }
 
     let is_marker = tcx.has_attr(def_id, sym::marker);
@@ -942,13 +915,7 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef {
         // and that they are all identifiers
         .and_then(|attr| match attr.meta_item_list() {
             Some(items) if items.len() < 2 => {
-                tcx.sess
-                    .struct_span_err(
-                        attr.span,
-                        "the `#[rustc_must_implement_one_of]` attribute must be \
-                         used with at least 2 args",
-                    )
-                    .emit();
+                tcx.sess.emit_err(errors::MustImplementOneOfAttribute { span: attr.span });
 
                 None
             }
@@ -957,9 +924,7 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef {
                 .map(|item| item.ident().ok_or(item.span()))
                 .collect::<Result<Box<[_]>, _>>()
                 .map_err(|span| {
-                    tcx.sess
-                        .struct_span_err(span, "must be a name of an associated function")
-                        .emit();
+                    tcx.sess.emit_err(errors::MustBeNameOfAssociatedFunction { span });
                 })
                 .ok()
                 .zip(Some(attr.span)),
@@ -975,13 +940,10 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef {
                 match item {
                     Some(item) if matches!(item.kind, hir::AssocItemKind::Fn { .. }) => {
                         if !tcx.impl_defaultness(item.id.owner_id).has_value() {
-                            tcx.sess
-                                .struct_span_err(
-                                    item.span,
-                                    "function doesn't have a default implementation",
-                                )
-                                .span_note(attr_span, "required by this annotation")
-                                .emit();
+                            tcx.sess.emit_err(errors::FunctionNotHaveDefaultImplementation {
+                                span: item.span,
+                                note_span: attr_span,
+                            });
 
                             return Some(());
                         }
@@ -989,19 +951,14 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef {
                         return None;
                     }
                     Some(item) => {
-                        tcx.sess
-                            .struct_span_err(item.span, "not a function")
-                            .span_note(attr_span, "required by this annotation")
-                            .note(
-                                "all `#[rustc_must_implement_one_of]` arguments must be associated \
-                                 function names",
-                            )
-                            .emit();
+                        tcx.sess.emit_err(errors::MustImplementNotFunction {
+                            span: item.span,
+                            span_note: errors::MustImplementNotFunctionSpanNote { span: attr_span },
+                            note: errors::MustImplementNotFunctionNote {},
+                        });
                     }
                     None => {
-                        tcx.sess
-                            .struct_span_err(ident.span, "function not found in this trait")
-                            .emit();
+                        tcx.sess.emit_err(errors::FunctionNotFoundInTrait { span: ident.span });
                     }
                 }
 
@@ -1018,9 +975,7 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef {
             for ident in &*list {
                 if let Some(dup) = set.insert(ident.name, ident.span) {
                     tcx.sess
-                        .struct_span_err(vec![dup, ident.span], "functions names are duplicated")
-                        .note("all `#[rustc_must_implement_one_of]` arguments must be unique")
-                        .emit();
+                        .emit_err(errors::FunctionNamesDuplicated { spans: vec![dup, ident.span] });
 
                     no_dups = false;
                 }
@@ -1485,17 +1440,7 @@ fn compute_sig_of_foreign_fn_decl<'tcx>(
                     .source_map()
                     .span_to_snippet(ast_ty.span)
                     .map_or_else(|_| String::new(), |s| format!(" `{}`", s));
-                tcx.sess
-                    .struct_span_err(
-                        ast_ty.span,
-                        &format!(
-                            "use of SIMD type{} in FFI is highly experimental and \
-                             may result in invalid code",
-                            snip
-                        ),
-                    )
-                    .help("add `#![feature(simd_ffi)]` to the crate attributes to enable")
-                    .emit();
+                tcx.sess.emit_err(errors::SIMDFFIHighlyExperimental { span: ast_ty.span, snip });
             }
         };
         for (input, ty) in iter::zip(decl.inputs, fty.inputs().skip_binder()) {
diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs
index d2e45c28658..2e56d24638c 100644
--- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs
+++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs
@@ -130,7 +130,7 @@ pub(super) fn item_bounds(
     tcx: TyCtxt<'_>,
     def_id: DefId,
 ) -> ty::EarlyBinder<&'_ ty::List<ty::Predicate<'_>>> {
-    let bounds = tcx.mk_predicates_from_iter(util::elaborate_predicates(
+    let bounds = tcx.mk_predicates_from_iter(util::elaborate(
         tcx,
         tcx.explicit_item_bounds(def_id).iter().map(|&(bound, _span)| bound),
     ));
diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs
index c71ce9a0bc7..2a3a683489d 100644
--- a/compiler/rustc_hir_analysis/src/errors.rs
+++ b/compiler/rustc_hir_analysis/src/errors.rs
@@ -507,3 +507,127 @@ pub(crate) struct ReturnTypeNotationMissingMethod {
     pub trait_name: Symbol,
     pub assoc_name: Symbol,
 }
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_placeholder_not_allowed_item_signatures, code = "E0121")]
+pub(crate) struct PlaceholderNotAllowedItemSignatures {
+    #[primary_span]
+    #[label]
+    pub spans: Vec<Span>,
+    pub kind: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_associated_type_trait_uninferred_generic_params, code = "E0212")]
+pub(crate) struct AssociatedTypeTraitUninferredGenericParams {
+    #[primary_span]
+    pub span: Span,
+    #[suggestion(style = "verbose", applicability = "maybe-incorrect", code = "{bound}")]
+    pub inferred_sugg: Option<Span>,
+    pub bound: String,
+    #[subdiagnostic]
+    pub mpart_sugg: Option<AssociatedTypeTraitUninferredGenericParamsMultipartSuggestion>,
+}
+
+#[derive(Subdiagnostic)]
+#[multipart_suggestion(
+    hir_analysis_associated_type_trait_uninferred_generic_params_multipart_suggestion,
+    applicability = "maybe-incorrect"
+)]
+pub(crate) struct AssociatedTypeTraitUninferredGenericParamsMultipartSuggestion {
+    #[suggestion_part(code = "{first}")]
+    pub fspan: Span,
+    pub first: String,
+    #[suggestion_part(code = "{second}")]
+    pub sspan: Span,
+    pub second: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_enum_discriminant_overflowed, code = "E0370")]
+#[note]
+pub(crate) struct EnumDiscriminantOverflowed {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    pub discr: String,
+    pub item_name: Symbol,
+    pub wrapped_discr: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_paren_sugar_attribute)]
+#[help]
+pub(crate) struct ParenSugarAttribute {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_must_implement_one_of_attribute)]
+pub(crate) struct MustImplementOneOfAttribute {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_must_be_name_of_associated_function)]
+pub(crate) struct MustBeNameOfAssociatedFunction {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_function_not_have_default_implementation)]
+pub(crate) struct FunctionNotHaveDefaultImplementation {
+    #[primary_span]
+    pub span: Span,
+    #[note]
+    pub note_span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_must_implement_not_function)]
+pub(crate) struct MustImplementNotFunction {
+    #[primary_span]
+    pub span: Span,
+    #[subdiagnostic]
+    pub span_note: MustImplementNotFunctionSpanNote,
+    #[subdiagnostic]
+    pub note: MustImplementNotFunctionNote,
+}
+
+#[derive(Subdiagnostic)]
+#[note(hir_analysis_must_implement_not_function_span_note)]
+pub(crate) struct MustImplementNotFunctionSpanNote {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Subdiagnostic)]
+#[note(hir_analysis_must_implement_not_function_note)]
+pub(crate) struct MustImplementNotFunctionNote {}
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_function_not_found_in_trait)]
+pub(crate) struct FunctionNotFoundInTrait {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_functions_names_duplicated)]
+#[note]
+pub(crate) struct FunctionNamesDuplicated {
+    #[primary_span]
+    pub spans: Vec<Span>,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_simd_ffi_highly_experimental)]
+#[help]
+pub(crate) struct SIMDFFIHighlyExperimental {
+    #[primary_span]
+    pub span: Span,
+    pub snip: String,
+}
diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs
index b5bae5788f6..35785e81ff4 100644
--- a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs
+++ b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs
@@ -318,15 +318,14 @@ fn check_predicates<'tcx>(
     span: Span,
 ) {
     let instantiated = tcx.predicates_of(impl1_def_id).instantiate(tcx, impl1_substs);
-    let impl1_predicates: Vec<_> =
-        traits::elaborate_predicates_with_span(tcx, instantiated.into_iter()).collect();
+    let impl1_predicates: Vec<_> = traits::elaborate(tcx, instantiated.into_iter()).collect();
 
     let mut impl2_predicates = if impl2_node.is_from_trait() {
         // Always applicable traits have to be always applicable without any
         // assumptions.
         Vec::new()
     } else {
-        traits::elaborate_predicates(
+        traits::elaborate(
             tcx,
             tcx.predicates_of(impl2_node.def_id())
                 .instantiate(tcx, impl2_substs)
@@ -371,11 +370,10 @@ fn check_predicates<'tcx>(
                 .unwrap();
 
         assert!(!obligations.needs_infer());
-        impl2_predicates.extend(
-            traits::elaborate_obligations(tcx, obligations).map(|obligation| obligation.predicate),
-        )
+        impl2_predicates
+            .extend(traits::elaborate(tcx, obligations).map(|obligation| obligation.predicate))
     }
-    impl2_predicates.extend(traits::elaborate_predicates(tcx, always_applicable_traits));
+    impl2_predicates.extend(traits::elaborate(tcx, always_applicable_traits));
 
     for (predicate, span) in impl1_predicates {
         if !impl2_predicates.iter().any(|pred2| trait_predicates_eq(tcx, predicate, *pred2, span)) {
diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs
index 47a8b080166..15eec42d786 100644
--- a/compiler/rustc_hir_typeck/src/closure.rs
+++ b/compiler/rustc_hir_typeck/src/closure.rs
@@ -204,7 +204,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let mut expected_sig = None;
         let mut expected_kind = None;
 
-        for (pred, span) in traits::elaborate_predicates_with_span(
+        for (pred, span) in traits::elaborate(
             self.tcx,
             // Reverse the obligations here, since `elaborate_*` uses a stack,
             // and we want to keep inference generally in the same order of
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index c17aae22ba5..68e096e3bd0 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -2810,23 +2810,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         "cannot index into a value of type `{base_t}`",
                     );
                     // Try to give some advice about indexing tuples.
-                    if let ty::Tuple(..) = base_t.kind() {
+                    if let ty::Tuple(types) = base_t.kind() {
                         let mut needs_note = true;
                         // If the index is an integer, we can show the actual
                         // fixed expression:
-                        if let ExprKind::Lit(ref lit) = idx.kind {
-                            if let ast::LitKind::Int(i, ast::LitIntType::Unsuffixed) = lit.node {
-                                let snip = self.tcx.sess.source_map().span_to_snippet(base.span);
-                                if let Ok(snip) = snip {
-                                    err.span_suggestion(
-                                        expr.span,
-                                        "to access tuple elements, use",
-                                        format!("{snip}.{i}"),
-                                        Applicability::MachineApplicable,
-                                    );
-                                    needs_note = false;
-                                }
+                        if let ExprKind::Lit(ref lit) = idx.kind
+                            && let ast::LitKind::Int(i, ast::LitIntType::Unsuffixed) = lit.node
+                            && i < types.len().try_into().expect("expected tuple index to be < usize length")
+                        {
+                            let snip = self.tcx.sess.source_map().span_to_snippet(base.span);
+                            if let Ok(snip) = snip {
+                                err.span_suggestion(
+                                    expr.span,
+                                    "to access tuple elements, use",
+                                    format!("{snip}.{i}"),
+                                    Applicability::MachineApplicable,
+                                );
+                                needs_note = false;
                             }
+                        } else if let ExprKind::Path(..) = idx.peel_borrows().kind {
+                            err.span_label(idx.span, "cannot access tuple elements at a variable index");
                         }
                         if needs_note {
                             err.help(
diff --git a/compiler/rustc_hir_typeck/src/intrinsicck.rs b/compiler/rustc_hir_typeck/src/intrinsicck.rs
index 901acffe1c8..106f5bcd755 100644
--- a/compiler/rustc_hir_typeck/src/intrinsicck.rs
+++ b/compiler/rustc_hir_typeck/src/intrinsicck.rs
@@ -84,6 +84,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let skeleton_string = |ty: Ty<'tcx>, sk| match sk {
             Ok(SizeSkeleton::Known(size)) => format!("{} bits", size.bits()),
             Ok(SizeSkeleton::Pointer { tail, .. }) => format!("pointer to `{tail}`"),
+            Ok(SizeSkeleton::Generic(size)) => {
+                if let Some(size) = size.try_eval_target_usize(tcx, self.param_env) {
+                    format!("{size} bytes")
+                } else {
+                    format!("generic size {size}")
+                }
+            }
             Err(LayoutError::Unknown(bad)) => {
                 if bad == ty {
                     "this type does not have a fixed size".to_owned()
diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs
index 2762e778591..9155a3d8daa 100644
--- a/compiler/rustc_hir_typeck/src/method/confirm.rs
+++ b/compiler/rustc_hir_typeck/src/method/confirm.rs
@@ -574,7 +574,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
     ) -> Option<Span> {
         let sized_def_id = self.tcx.lang_items().sized_trait()?;
 
-        traits::elaborate_predicates(self.tcx, predicates.predicates.iter().copied())
+        traits::elaborate(self.tcx, predicates.predicates.iter().copied())
             // We don't care about regions here.
             .filter_map(|pred| match pred.kind().skip_binder() {
                 ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred))
diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs
index dab709e17f0..4258a100d98 100644
--- a/compiler/rustc_hir_typeck/src/method/probe.rs
+++ b/compiler/rustc_hir_typeck/src/method/probe.rs
@@ -793,6 +793,14 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
         // a `&self` method will wind up with an argument type like `&dyn Trait`.
         let trait_ref = principal.with_self_ty(self.tcx, self_ty);
         self.elaborate_bounds(iter::once(trait_ref), |this, new_trait_ref, item| {
+            if new_trait_ref.has_non_region_late_bound() {
+                this.tcx.sess.delay_span_bug(
+                    this.span,
+                    "tried to select method from HRTB with non-lifetime bound vars",
+                );
+                return;
+            }
+
             let new_trait_ref = this.erase_late_bound_regions(new_trait_ref);
 
             let (xform_self_ty, xform_ret_ty) =
@@ -843,18 +851,15 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
         });
 
         self.elaborate_bounds(bounds, |this, poly_trait_ref, item| {
-            let trait_ref = this.erase_late_bound_regions(poly_trait_ref);
+            let trait_ref = this.instantiate_binder_with_fresh_vars(
+                this.span,
+                infer::LateBoundRegionConversionTime::FnCall,
+                poly_trait_ref,
+            );
 
             let (xform_self_ty, xform_ret_ty) =
                 this.xform_self_ty(item, trait_ref.self_ty(), trait_ref.substs);
 
-            // Because this trait derives from a where-clause, it
-            // should not contain any inference variables or other
-            // artifacts. This means it is safe to put into the
-            // `WhereClauseCandidate` and (eventually) into the
-            // `WhereClausePick`.
-            assert!(!trait_ref.substs.needs_infer());
-
             this.push_candidate(
                 Candidate {
                     xform_self_ty,
@@ -964,7 +969,11 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
                             bound_trait_ref.def_id(),
                         ));
                     } else {
-                        let new_trait_ref = self.erase_late_bound_regions(bound_trait_ref);
+                        let new_trait_ref = self.instantiate_binder_with_fresh_vars(
+                            self.span,
+                            infer::LateBoundRegionConversionTime::FnCall,
+                            bound_trait_ref,
+                        );
 
                         let (xform_self_ty, xform_ret_ty) =
                             self.xform_self_ty(item, new_trait_ref.self_ty(), new_trait_ref.substs);
@@ -1555,8 +1564,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
                         if !self.predicate_may_hold(&o) {
                             result = ProbeResult::NoMatch;
                             let parent_o = o.clone();
-                            let implied_obligations =
-                                traits::elaborate_obligations(self.tcx, vec![o]);
+                            let implied_obligations = traits::elaborate(self.tcx, vec![o]);
                             for o in implied_obligations {
                                 let parent = if o == parent_o {
                                     None
diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index 241535b29c5..af0bd26dec5 100644
--- a/compiler/rustc_hir_typeck/src/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -37,6 +37,10 @@ pointers. If you encounter this error you should try to avoid dereferencing the
 You can read more about trait objects in the Trait Objects section of the Reference: \
 https://doc.rust-lang.org/reference/types.html#trait-objects";
 
+fn is_number(text: &str) -> bool {
+    text.chars().all(|c: char| c.is_digit(10))
+}
+
 /// Information about the expected type at the top level of type checking a pattern.
 ///
 /// **NOTE:** This is only for use by diagnostics. Do NOT use for type checking logic!
@@ -1673,7 +1677,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         fields: &'tcx [hir::PatField<'tcx>],
         variant: &ty::VariantDef,
     ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> {
-        if let (Some(CtorKind::Fn), PatKind::Struct(qpath, ..)) = (variant.ctor_kind(), &pat.kind) {
+        if let (Some(CtorKind::Fn), PatKind::Struct(qpath, pattern_fields, ..)) =
+            (variant.ctor_kind(), &pat.kind)
+        {
+            let is_tuple_struct_match = !pattern_fields.is_empty()
+                && pattern_fields.iter().map(|field| field.ident.name.as_str()).all(is_number);
+            if is_tuple_struct_match {
+                return None;
+            }
+
             let path = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| {
                 s.print_qpath(qpath, false)
             });
@@ -1895,7 +1907,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 prefix,
                 unmentioned_fields
                     .iter()
-                    .map(|(_, name)| name.to_string())
+                    .map(|(_, name)| {
+                        let field_name = name.to_string();
+                        if is_number(&field_name) {
+                            format!("{}: _", field_name)
+                        } else {
+                            field_name
+                        }
+                    })
                     .collect::<Vec<_>>()
                     .join(", "),
                 if have_inaccessible_fields { ", .." } else { "" },
diff --git a/compiler/rustc_infer/src/traits/util.rs b/compiler/rustc_infer/src/traits/util.rs
index f3797499866..c7f7ed14940 100644
--- a/compiler/rustc_infer/src/traits/util.rs
+++ b/compiler/rustc_infer/src/traits/util.rs
@@ -1,7 +1,7 @@
 use smallvec::smallvec;
 
 use crate::infer::outlives::components::{push_outlives_components, Component};
-use crate::traits::{self, Obligation, ObligationCause, PredicateObligation};
+use crate::traits::{self, Obligation, PredicateObligation};
 use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
 use rustc_middle::ty::{self, ToPredicate, TyCtxt};
 use rustc_span::symbol::Ident;
@@ -66,99 +66,129 @@ impl<'tcx> Extend<ty::Predicate<'tcx>> for PredicateSet<'tcx> {
 /// if we know that `T: Ord`, the elaborator would deduce that `T: PartialOrd`
 /// holds as well. Similarly, if we have `trait Foo: 'static`, and we know that
 /// `T: Foo`, then we know that `T: 'static`.
-pub struct Elaborator<'tcx> {
-    stack: Vec<PredicateObligation<'tcx>>,
+pub struct Elaborator<'tcx, O> {
+    stack: Vec<O>,
     visited: PredicateSet<'tcx>,
 }
 
-pub fn elaborate_trait_ref<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    trait_ref: ty::PolyTraitRef<'tcx>,
-) -> impl Iterator<Item = ty::Predicate<'tcx>> {
-    elaborate_predicates(tcx, std::iter::once(trait_ref.without_const().to_predicate(tcx)))
+/// Describes how to elaborate an obligation into a sub-obligation.
+///
+/// For [`Obligation`], a sub-obligation is combined with the current obligation's
+/// param-env and cause code. For [`ty::Predicate`], none of this is needed, since
+/// there is no param-env or cause code to copy over.
+pub trait Elaboratable<'tcx> {
+    fn predicate(&self) -> ty::Predicate<'tcx>;
+
+    // Makes a new `Self` but with a different predicate.
+    fn child(&self, predicate: ty::Predicate<'tcx>) -> Self;
+
+    // Makes a new `Self` but with a different predicate and a different cause
+    // code (if `Self` has one).
+    fn child_with_derived_cause(
+        &self,
+        predicate: ty::Predicate<'tcx>,
+        span: Span,
+        parent_trait_pred: ty::PolyTraitPredicate<'tcx>,
+        index: usize,
+    ) -> Self;
 }
 
-pub fn elaborate_trait_refs<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    trait_refs: impl Iterator<Item = ty::PolyTraitRef<'tcx>>,
-) -> impl Iterator<Item = ty::Predicate<'tcx>> {
-    let predicates = trait_refs.map(move |trait_ref| trait_ref.without_const().to_predicate(tcx));
-    elaborate_predicates(tcx, predicates)
+impl<'tcx> Elaboratable<'tcx> for PredicateObligation<'tcx> {
+    fn predicate(&self) -> ty::Predicate<'tcx> {
+        self.predicate
+    }
+
+    fn child(&self, predicate: ty::Predicate<'tcx>) -> Self {
+        Obligation {
+            cause: self.cause.clone(),
+            param_env: self.param_env,
+            recursion_depth: 0,
+            predicate,
+        }
+    }
+
+    fn child_with_derived_cause(
+        &self,
+        predicate: ty::Predicate<'tcx>,
+        span: Span,
+        parent_trait_pred: ty::PolyTraitPredicate<'tcx>,
+        index: usize,
+    ) -> Self {
+        let cause = self.cause.clone().derived_cause(parent_trait_pred, |derived| {
+            traits::ImplDerivedObligation(Box::new(traits::ImplDerivedObligationCause {
+                derived,
+                impl_or_alias_def_id: parent_trait_pred.def_id(),
+                impl_def_predicate_index: Some(index),
+                span,
+            }))
+        });
+        Obligation { cause, param_env: self.param_env, recursion_depth: 0, predicate }
+    }
 }
 
-pub fn elaborate_predicates<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    predicates: impl Iterator<Item = ty::Predicate<'tcx>>,
-) -> impl Iterator<Item = ty::Predicate<'tcx>> {
-    elaborate_obligations(
-        tcx,
-        predicates
-            .map(|predicate| {
-                Obligation::new(
-                    tcx,
-                    // We'll dump the cause/param-env later
-                    ObligationCause::dummy(),
-                    ty::ParamEnv::empty(),
-                    predicate,
-                )
-            })
-            .collect(),
-    )
-    .map(|obl| obl.predicate)
+impl<'tcx> Elaboratable<'tcx> for ty::Predicate<'tcx> {
+    fn predicate(&self) -> ty::Predicate<'tcx> {
+        *self
+    }
+
+    fn child(&self, predicate: ty::Predicate<'tcx>) -> Self {
+        predicate
+    }
+
+    fn child_with_derived_cause(
+        &self,
+        predicate: ty::Predicate<'tcx>,
+        _span: Span,
+        _parent_trait_pred: ty::PolyTraitPredicate<'tcx>,
+        _index: usize,
+    ) -> Self {
+        predicate
+    }
 }
 
-pub fn elaborate_predicates_with_span<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    predicates: impl Iterator<Item = (ty::Predicate<'tcx>, Span)>,
-) -> impl Iterator<Item = (ty::Predicate<'tcx>, Span)> {
-    elaborate_obligations(
-        tcx,
-        predicates
-            .map(|(predicate, span)| {
-                Obligation::new(
-                    tcx,
-                    // We'll dump the cause/param-env later
-                    ObligationCause::dummy_with_span(span),
-                    ty::ParamEnv::empty(),
-                    predicate,
-                )
-            })
-            .collect(),
-    )
-    .map(|obl| (obl.predicate, obl.cause.span))
+impl<'tcx> Elaboratable<'tcx> for (ty::Predicate<'tcx>, Span) {
+    fn predicate(&self) -> ty::Predicate<'tcx> {
+        self.0
+    }
+
+    fn child(&self, predicate: ty::Predicate<'tcx>) -> Self {
+        (predicate, self.1)
+    }
+
+    fn child_with_derived_cause(
+        &self,
+        predicate: ty::Predicate<'tcx>,
+        _span: Span,
+        _parent_trait_pred: ty::PolyTraitPredicate<'tcx>,
+        _index: usize,
+    ) -> Self {
+        (predicate, self.1)
+    }
 }
 
-pub fn elaborate_obligations<'tcx>(
+pub fn elaborate<'tcx, O: Elaboratable<'tcx>>(
     tcx: TyCtxt<'tcx>,
-    obligations: Vec<PredicateObligation<'tcx>>,
-) -> Elaborator<'tcx> {
+    obligations: impl IntoIterator<Item = O>,
+) -> Elaborator<'tcx, O> {
     let mut elaborator = Elaborator { stack: Vec::new(), visited: PredicateSet::new(tcx) };
     elaborator.extend_deduped(obligations);
     elaborator
 }
 
-fn predicate_obligation<'tcx>(
-    predicate: ty::Predicate<'tcx>,
-    param_env: ty::ParamEnv<'tcx>,
-    cause: ObligationCause<'tcx>,
-) -> PredicateObligation<'tcx> {
-    Obligation { cause, param_env, recursion_depth: 0, predicate }
-}
-
-impl<'tcx> Elaborator<'tcx> {
-    fn extend_deduped(&mut self, obligations: impl IntoIterator<Item = PredicateObligation<'tcx>>) {
+impl<'tcx, O: Elaboratable<'tcx>> Elaborator<'tcx, O> {
+    fn extend_deduped(&mut self, obligations: impl IntoIterator<Item = O>) {
         // Only keep those bounds that we haven't already seen.
         // This is necessary to prevent infinite recursion in some
         // cases. One common case is when people define
         // `trait Sized: Sized { }` rather than `trait Sized { }`.
         // let visited = &mut self.visited;
-        self.stack.extend(obligations.into_iter().filter(|o| self.visited.insert(o.predicate)));
+        self.stack.extend(obligations.into_iter().filter(|o| self.visited.insert(o.predicate())));
     }
 
-    fn elaborate(&mut self, obligation: &PredicateObligation<'tcx>) {
+    fn elaborate(&mut self, elaboratable: &O) {
         let tcx = self.visited.tcx;
 
-        let bound_predicate = obligation.predicate.kind();
+        let bound_predicate = elaboratable.predicate().kind();
         match bound_predicate.skip_binder() {
             ty::PredicateKind::Clause(ty::Clause::Trait(data)) => {
                 // Get predicates declared on the trait.
@@ -170,24 +200,11 @@ impl<'tcx> Elaborator<'tcx> {
                         if data.constness == ty::BoundConstness::NotConst {
                             pred = pred.without_const(tcx);
                         }
-
-                        let cause = obligation.cause.clone().derived_cause(
-                            bound_predicate.rebind(data),
-                            |derived| {
-                                traits::ImplDerivedObligation(Box::new(
-                                    traits::ImplDerivedObligationCause {
-                                        derived,
-                                        impl_or_alias_def_id: data.def_id(),
-                                        impl_def_predicate_index: Some(index),
-                                        span,
-                                    },
-                                ))
-                            },
-                        );
-                        predicate_obligation(
+                        elaboratable.child_with_derived_cause(
                             pred.subst_supertrait(tcx, &bound_predicate.rebind(data.trait_ref)),
-                            obligation.param_env,
-                            cause,
+                            span,
+                            bound_predicate.rebind(data),
+                            index,
                         )
                     });
                 debug!(?data, ?obligations, "super_predicates");
@@ -290,13 +307,7 @@ impl<'tcx> Elaborator<'tcx> {
                         .map(|predicate_kind| {
                             bound_predicate.rebind(predicate_kind).to_predicate(tcx)
                         })
-                        .map(|predicate| {
-                            predicate_obligation(
-                                predicate,
-                                obligation.param_env,
-                                obligation.cause.clone(),
-                            )
-                        }),
+                        .map(|predicate| elaboratable.child(predicate)),
                 );
             }
             ty::PredicateKind::TypeWellFormedFromEnv(..) => {
@@ -313,8 +324,8 @@ impl<'tcx> Elaborator<'tcx> {
     }
 }
 
-impl<'tcx> Iterator for Elaborator<'tcx> {
-    type Item = PredicateObligation<'tcx>;
+impl<'tcx, O: Elaboratable<'tcx>> Iterator for Elaborator<'tcx, O> {
+    type Item = O;
 
     fn size_hint(&self) -> (usize, Option<usize>) {
         (self.stack.len(), None)
@@ -339,17 +350,21 @@ pub fn supertraits<'tcx>(
     tcx: TyCtxt<'tcx>,
     trait_ref: ty::PolyTraitRef<'tcx>,
 ) -> impl Iterator<Item = ty::PolyTraitRef<'tcx>> {
-    FilterToTraits::new(elaborate_trait_ref(tcx, trait_ref))
+    let pred: ty::Predicate<'tcx> = trait_ref.to_predicate(tcx);
+    FilterToTraits::new(elaborate(tcx, [pred]))
 }
 
 pub fn transitive_bounds<'tcx>(
     tcx: TyCtxt<'tcx>,
     trait_refs: impl Iterator<Item = ty::PolyTraitRef<'tcx>>,
 ) -> impl Iterator<Item = ty::PolyTraitRef<'tcx>> {
-    FilterToTraits::new(elaborate_trait_refs(tcx, trait_refs))
+    FilterToTraits::new(elaborate(
+        tcx,
+        trait_refs.map(|trait_ref| -> ty::Predicate<'tcx> { trait_ref.to_predicate(tcx) }),
+    ))
 }
 
-/// A specialized variant of `elaborate_trait_refs` that only elaborates trait references that may
+/// A specialized variant of `elaborate` that only elaborates trait references that may
 /// define the given associated type `assoc_name`. It uses the
 /// `super_predicates_that_define_assoc_type` query to avoid enumerating super-predicates that
 /// aren't related to `assoc_item`. This is used when resolving types like `Self::Item` or
diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs
index 8d4892470ac..0e4e20c7cd1 100644
--- a/compiler/rustc_interface/src/passes.rs
+++ b/compiler/rustc_interface/src/passes.rs
@@ -230,7 +230,7 @@ fn configure_and_expand(
             features: Some(features),
             recursion_limit,
             trace_mac: sess.opts.unstable_opts.trace_macros,
-            should_test: sess.opts.test,
+            should_test: sess.is_test_crate(),
             span_debug: sess.opts.unstable_opts.span_debug,
             proc_macro_backtrace: sess.opts.unstable_opts.proc_macro_backtrace,
             ..rustc_expand::expand::ExpansionConfig::default(crate_name.to_string())
@@ -292,7 +292,7 @@ fn configure_and_expand(
     }
 
     sess.time("maybe_create_a_macro_crate", || {
-        let is_test_crate = sess.opts.test;
+        let is_test_crate = sess.is_test_crate();
         rustc_builtin_macros::proc_macro_harness::inject(
             &mut krate,
             sess,
diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs
index ad328006051..35c461f5ace 100644
--- a/compiler/rustc_lint/src/unused.rs
+++ b/compiler/rustc_lint/src/unused.rs
@@ -12,7 +12,7 @@ use rustc_errors::{pluralize, MultiSpan};
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::DefId;
-use rustc_infer::traits::util::elaborate_predicates_with_span;
+use rustc_infer::traits::util::elaborate;
 use rustc_middle::ty::adjustment;
 use rustc_middle::ty::{self, Ty};
 use rustc_span::symbol::Symbol;
@@ -254,24 +254,21 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
                 }
                 ty::Adt(def, _) => is_def_must_use(cx, def.did(), span),
                 ty::Alias(ty::Opaque, ty::AliasTy { def_id: def, .. }) => {
-                    elaborate_predicates_with_span(
-                        cx.tcx,
-                        cx.tcx.explicit_item_bounds(def).iter().cloned(),
-                    )
-                    .find_map(|(pred, _span)| {
-                        // We only look at the `DefId`, so it is safe to skip the binder here.
-                        if let ty::PredicateKind::Clause(ty::Clause::Trait(
-                            ref poly_trait_predicate,
-                        )) = pred.kind().skip_binder()
-                        {
-                            let def_id = poly_trait_predicate.trait_ref.def_id;
-
-                            is_def_must_use(cx, def_id, span)
-                        } else {
-                            None
-                        }
-                    })
-                    .map(|inner| MustUsePath::Opaque(Box::new(inner)))
+                    elaborate(cx.tcx, cx.tcx.explicit_item_bounds(def).iter().cloned())
+                        .find_map(|(pred, _span)| {
+                            // We only look at the `DefId`, so it is safe to skip the binder here.
+                            if let ty::PredicateKind::Clause(ty::Clause::Trait(
+                                ref poly_trait_predicate,
+                            )) = pred.kind().skip_binder()
+                            {
+                                let def_id = poly_trait_predicate.trait_ref.def_id;
+
+                                is_def_must_use(cx, def_id, span)
+                            } else {
+                                None
+                            }
+                        })
+                        .map(|inner| MustUsePath::Opaque(Box::new(inner)))
                 }
                 ty::Dynamic(binders, _, _) => binders.iter().find_map(|predicate| {
                     if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder()
diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs
index 852c6d96469..f9d32ffceef 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder.rs
@@ -991,7 +991,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
             _ => false,
         };
 
-        ModChild { ident, res, vis, span, macro_rules }
+        ModChild { ident, res, vis, span, macro_rules, reexport_chain: Default::default() }
     }
 
     /// Iterates over all named children of the given module,
diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs
index faf0592138b..4291b9aa142 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -1327,8 +1327,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
                 }
             }));
 
-            if let Some(reexports) = tcx.module_reexports(local_def_id) {
-                assert!(!reexports.is_empty());
+            let reexports = tcx.module_reexports(local_def_id);
+            if !reexports.is_empty() {
                 record_array!(self.tables.module_reexports[def_id] <- reexports);
             }
         }
diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs
index 9f16ecbdaa9..dd1e254f405 100644
--- a/compiler/rustc_middle/src/arena.rs
+++ b/compiler/rustc_middle/src/arena.rs
@@ -119,6 +119,7 @@ macro_rules! arena_types {
             [] external_constraints: rustc_middle::traits::solve::ExternalConstraintsData<'tcx>,
             [decode] doc_link_resolutions: rustc_hir::def::DocLinkResMap,
             [] closure_kind_origin: (rustc_span::Span, rustc_middle::hir::place::Place<'tcx>),
+            [] mod_child: rustc_middle::metadata::ModChild,
         ]);
     )
 }
diff --git a/compiler/rustc_middle/src/infer/canonical.rs b/compiler/rustc_middle/src/infer/canonical.rs
index f668c4e77be..b5b712c367d 100644
--- a/compiler/rustc_middle/src/infer/canonical.rs
+++ b/compiler/rustc_middle/src/infer/canonical.rs
@@ -80,6 +80,18 @@ impl CanonicalVarValues<'_> {
             }
         })
     }
+
+    pub fn is_identity_modulo_regions(&self) -> bool {
+        self.var_values.iter().enumerate().all(|(bv, arg)| match arg.unpack() {
+            ty::GenericArgKind::Lifetime(_) => true,
+            ty::GenericArgKind::Type(ty) => {
+                matches!(*ty.kind(), ty::Bound(ty::INNERMOST, bt) if bt.var.as_usize() == bv)
+            }
+            ty::GenericArgKind::Const(ct) => {
+                matches!(ct.kind(), ty::ConstKind::Bound(ty::INNERMOST, bc) if bc.as_usize() == bv)
+            }
+        })
+    }
 }
 
 /// When we canonicalize a value to form a query, we wind up replacing
diff --git a/compiler/rustc_middle/src/metadata.rs b/compiler/rustc_middle/src/metadata.rs
index 5ff014c7815..fabc6bce731 100644
--- a/compiler/rustc_middle/src/metadata.rs
+++ b/compiler/rustc_middle/src/metadata.rs
@@ -5,13 +5,34 @@ use rustc_macros::HashStable;
 use rustc_span::def_id::DefId;
 use rustc_span::symbol::Ident;
 use rustc_span::Span;
+use smallvec::SmallVec;
+
+/// A simplified version of `ImportKind` from resolve.
+/// `DefId`s here correspond to `use` and `extern crate` items themselves, not their targets.
+#[derive(Clone, Copy, Debug, TyEncodable, TyDecodable, HashStable)]
+pub enum Reexport {
+    Single(DefId),
+    Glob(DefId),
+    ExternCrate(DefId),
+    MacroUse,
+    MacroExport,
+}
+
+impl Reexport {
+    pub fn id(self) -> Option<DefId> {
+        match self {
+            Reexport::Single(id) | Reexport::Glob(id) | Reexport::ExternCrate(id) => Some(id),
+            Reexport::MacroUse | Reexport::MacroExport => None,
+        }
+    }
+}
 
 /// This structure is supposed to keep enough data to re-create `NameBinding`s for other crates
 /// during name resolution. Right now the bindings are not recreated entirely precisely so we may
 /// need to add more data in the future to correctly support macros 2.0, for example.
 /// Module child can be either a proper item or a reexport (including private imports).
 /// In case of reexport all the fields describe the reexport item itself, not what it refers to.
-#[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable)]
+#[derive(Debug, TyEncodable, TyDecodable, HashStable)]
 pub struct ModChild {
     /// Name of the item.
     pub ident: Ident,
@@ -24,4 +45,7 @@ pub struct ModChild {
     pub span: Span,
     /// A proper `macro_rules` item (not a reexport).
     pub macro_rules: bool,
+    /// Reexport chain linking this module child to its original reexported item.
+    /// Empty if the module child is a proper item.
+    pub reexport_chain: SmallVec<[Reexport; 2]>,
 }
diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs
index 5462ced16d6..24d98665a7b 100644
--- a/compiler/rustc_middle/src/query/erase.rs
+++ b/compiler/rustc_middle/src/query/erase.rs
@@ -235,7 +235,6 @@ trivial! {
     rustc_hir::OwnerId,
     rustc_hir::Upvar,
     rustc_index::bit_set::FiniteBitSet<u32>,
-    rustc_middle::metadata::ModChild,
     rustc_middle::middle::dependency_format::Linkage,
     rustc_middle::middle::exported_symbols::SymbolExportInfo,
     rustc_middle::middle::resolve_bound_vars::ObjectLifetimeDefault,
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index 96416896756..a0fce4b47ca 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -1510,7 +1510,7 @@ rustc_queries! {
         desc { "getting traits in scope at a block" }
     }
 
-    query module_reexports(def_id: LocalDefId) -> Option<&'tcx [ModChild]> {
+    query module_reexports(def_id: LocalDefId) -> &'tcx [ModChild] {
         desc { |tcx| "looking up reexports of module `{}`", tcx.def_path_str(def_id.to_def_id()) }
     }
 
diff --git a/compiler/rustc_middle/src/traits/solve.rs b/compiler/rustc_middle/src/traits/solve.rs
index 0d6f9813e76..7602bea5a24 100644
--- a/compiler/rustc_middle/src/traits/solve.rs
+++ b/compiler/rustc_middle/src/traits/solve.rs
@@ -56,9 +56,19 @@ pub enum Certainty {
 impl Certainty {
     pub const AMBIGUOUS: Certainty = Certainty::Maybe(MaybeCause::Ambiguity);
 
-    /// When proving multiple goals using **AND**, e.g. nested obligations for an impl,
-    /// use this function to unify the certainty of these goals
-    pub fn unify_and(self, other: Certainty) -> Certainty {
+    /// Use this function to merge the certainty of multiple nested subgoals.
+    ///
+    /// Given an impl like `impl<T: Foo + Bar> Baz for T {}`, we have 2 nested
+    /// subgoals whenever we use the impl as a candidate: `T: Foo` and `T: Bar`.
+    /// If evaluating `T: Foo` results in ambiguity and `T: Bar` results in
+    /// success, we merge these two responses. This results in ambiguity.
+    ///
+    /// If we unify ambiguity with overflow, we return overflow. This doesn't matter
+    /// inside of the solver as we distinguish ambiguity from overflow. It does
+    /// however matter for diagnostics. If `T: Foo` resulted in overflow and `T: Bar`
+    /// in ambiguity without changing the inference state, we still want to tell the
+    /// user that `T: Baz` results in overflow.
+    pub fn unify_with(self, other: Certainty) -> Certainty {
         match (self, other) {
             (Certainty::Yes, Certainty::Yes) => Certainty::Yes,
             (Certainty::Yes, Certainty::Maybe(_)) => other,
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index 299b1bf1d96..2ef6180c4cb 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -2502,7 +2502,7 @@ pub struct DeducedParamAttrs {
 
 pub fn provide(providers: &mut ty::query::Providers) {
     providers.module_reexports =
-        |tcx, id| tcx.resolutions(()).reexport_map.get(&id).map(|v| &v[..]);
+        |tcx, id| tcx.resolutions(()).reexport_map.get(&id).map_or(&[], |v| &v[..]);
     providers.maybe_unused_trait_imports =
         |tcx, ()| &tcx.resolutions(()).maybe_unused_trait_imports;
     providers.names_imported_by_glob_use = |tcx, id| {
diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs
index 8af9acfadde..2328a1324fc 100644
--- a/compiler/rustc_middle/src/ty/layout.rs
+++ b/compiler/rustc_middle/src/ty/layout.rs
@@ -281,6 +281,12 @@ pub enum SizeSkeleton<'tcx> {
     /// Any statically computable Layout.
     Known(Size),
 
+    /// This is a generic const expression (i.e. N * 2), which may contain some parameters.
+    /// It must be of type usize, and represents the size of a type in bytes.
+    /// It is not required to be evaluatable to a concrete value, but can be used to check
+    /// that another SizeSkeleton is of equal size.
+    Generic(ty::Const<'tcx>),
+
     /// A potentially-fat pointer.
     Pointer {
         /// If true, this pointer is never null.
@@ -326,6 +332,37 @@ impl<'tcx> SizeSkeleton<'tcx> {
                     ),
                 }
             }
+            ty::Array(inner, len)
+                if len.ty() == tcx.types.usize && tcx.features().transmute_generic_consts =>
+            {
+                match SizeSkeleton::compute(inner, tcx, param_env)? {
+                    // This may succeed because the multiplication of two types may overflow
+                    // but a single size of a nested array will not.
+                    SizeSkeleton::Known(s) => {
+                        if let Some(c) = len.try_eval_target_usize(tcx, param_env) {
+                            let size = s
+                                .bytes()
+                                .checked_mul(c)
+                                .ok_or_else(|| LayoutError::SizeOverflow(ty))?;
+                            return Ok(SizeSkeleton::Known(Size::from_bytes(size)));
+                        }
+                        let len = tcx.expand_abstract_consts(len);
+                        let prev = ty::Const::from_target_usize(tcx, s.bytes());
+                        let Some(gen_size) = mul_sorted_consts(tcx, param_env, len, prev) else {
+                            return Err(LayoutError::SizeOverflow(ty));
+                        };
+                        Ok(SizeSkeleton::Generic(gen_size))
+                    }
+                    SizeSkeleton::Pointer { .. } => Err(err),
+                    SizeSkeleton::Generic(g) => {
+                        let len = tcx.expand_abstract_consts(len);
+                        let Some(gen_size) = mul_sorted_consts(tcx, param_env, len, g) else {
+                            return Err(LayoutError::SizeOverflow(ty));
+                        };
+                        Ok(SizeSkeleton::Generic(gen_size))
+                    }
+                }
+            }
 
             ty::Adt(def, substs) => {
                 // Only newtypes and enums w/ nullable pointer optimization.
@@ -355,6 +392,9 @@ impl<'tcx> SizeSkeleton<'tcx> {
                                 }
                                 ptr = Some(field);
                             }
+                            SizeSkeleton::Generic(_) => {
+                                return Err(err);
+                            }
                         }
                     }
                     Ok(ptr)
@@ -410,11 +450,66 @@ impl<'tcx> SizeSkeleton<'tcx> {
             (SizeSkeleton::Pointer { tail: a, .. }, SizeSkeleton::Pointer { tail: b, .. }) => {
                 a == b
             }
+            // constants are always pre-normalized into a canonical form so this
+            // only needs to check if their pointers are identical.
+            (SizeSkeleton::Generic(a), SizeSkeleton::Generic(b)) => a == b,
             _ => false,
         }
     }
 }
 
+/// When creating the layout for types with abstract conts in their size (i.e. [usize; 4 * N]),
+/// to ensure that they have a canonical order and can be compared directly we combine all
+/// constants, and sort the other terms. This allows comparison of expressions of sizes,
+/// allowing for things like transmutating between types that depend on generic consts.
+/// This returns `None` if multiplication of constants overflows.
+fn mul_sorted_consts<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    a: ty::Const<'tcx>,
+    b: ty::Const<'tcx>,
+) -> Option<ty::Const<'tcx>> {
+    use crate::mir::BinOp::Mul;
+    use ty::ConstKind::Expr;
+    use ty::Expr::Binop;
+
+    let mut work = vec![a, b];
+    let mut done = vec![];
+    while let Some(n) = work.pop() {
+        if let Expr(Binop(Mul, l, r)) = n.kind() {
+            work.push(l);
+            work.push(r)
+        } else {
+            done.push(n);
+        }
+    }
+    let mut k = 1;
+    let mut overflow = false;
+    done.retain(|c| {
+        let Some(c) = c.try_eval_target_usize(tcx, param_env) else {
+            return true;
+        };
+        let Some(next) = c.checked_mul(k) else {
+            overflow = true;
+            return false;
+        };
+        k = next;
+        false
+    });
+    if overflow {
+        return None;
+    }
+    if k != 1 {
+        done.push(ty::Const::from_target_usize(tcx, k));
+    } else if k == 0 {
+        return Some(ty::Const::from_target_usize(tcx, 0));
+    }
+    done.sort_unstable();
+
+    // create a single tree from the buffer
+    done.into_iter().reduce(|acc, n| tcx.mk_const(Expr(Binop(Mul, n, acc)), n.ty()))
+}
+
 pub trait HasTyCtxt<'tcx>: HasDataLayout {
     fn tcx(&self) -> TyCtxt<'tcx>;
 }
diff --git a/compiler/rustc_mir_transform/src/const_prop.rs b/compiler/rustc_mir_transform/src/const_prop.rs
index f0266051f37..79a9ac7d20c 100644
--- a/compiler/rustc_mir_transform/src/const_prop.rs
+++ b/compiler/rustc_mir_transform/src/const_prop.rs
@@ -115,10 +115,7 @@ impl<'tcx> MirPass<'tcx> for ConstProp {
             .predicates
             .iter()
             .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
-        if traits::impossible_predicates(
-            tcx,
-            traits::elaborate_predicates(tcx, predicates).collect(),
-        ) {
+        if traits::impossible_predicates(tcx, traits::elaborate(tcx, predicates).collect()) {
             trace!("ConstProp skipped for {:?}: found unsatisfiable predicates", def_id);
             return;
         }
diff --git a/compiler/rustc_mir_transform/src/const_prop_lint.rs b/compiler/rustc_mir_transform/src/const_prop_lint.rs
index 1a96fddcd73..699fe44892b 100644
--- a/compiler/rustc_mir_transform/src/const_prop_lint.rs
+++ b/compiler/rustc_mir_transform/src/const_prop_lint.rs
@@ -91,10 +91,7 @@ impl<'tcx> MirLint<'tcx> for ConstProp {
             .predicates
             .iter()
             .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
-        if traits::impossible_predicates(
-            tcx,
-            traits::elaborate_predicates(tcx, predicates).collect(),
-        ) {
+        if traits::impossible_predicates(tcx, traits::elaborate(tcx, predicates).collect()) {
             trace!("ConstProp skipped for {:?}: found unsatisfiable predicates", def_id);
             return;
         }
diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs
index 4a1ba19c920..4a35c679466 100644
--- a/compiler/rustc_passes/src/stability.rs
+++ b/compiler/rustc_passes/src/stability.rs
@@ -530,7 +530,7 @@ struct MissingStabilityAnnotations<'tcx> {
 impl<'tcx> MissingStabilityAnnotations<'tcx> {
     fn check_missing_stability(&self, def_id: LocalDefId, span: Span) {
         let stab = self.tcx.stability().local_stability(def_id);
-        if !self.tcx.sess.opts.test
+        if !self.tcx.sess.is_test_crate()
             && stab.is_none()
             && self.effective_visibilities.is_reachable(def_id)
         {
diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs
index d27505d1ac8..089e043d61c 100644
--- a/compiler/rustc_privacy/src/lib.rs
+++ b/compiler/rustc_privacy/src/lib.rs
@@ -515,16 +515,12 @@ impl<'tcx> EmbargoVisitor<'tcx> {
             let vis = self.tcx.local_visibility(item_id.owner_id.def_id);
             self.update_macro_reachable_def(item_id.owner_id.def_id, def_kind, vis, defining_mod);
         }
-        if let Some(exports) = self.tcx.module_reexports(module_def_id) {
-            for export in exports {
-                if export.vis.is_accessible_from(defining_mod, self.tcx) {
-                    if let Res::Def(def_kind, def_id) = export.res {
-                        if let Some(def_id) = def_id.as_local() {
-                            let vis = self.tcx.local_visibility(def_id);
-                            self.update_macro_reachable_def(def_id, def_kind, vis, defining_mod);
-                        }
-                    }
-                }
+        for export in self.tcx.module_reexports(module_def_id) {
+            if export.vis.is_accessible_from(defining_mod, self.tcx)
+                && let Res::Def(def_kind, def_id) = export.res
+                && let Some(def_id) = def_id.as_local() {
+                let vis = self.tcx.local_visibility(def_id);
+                self.update_macro_reachable_def(def_id, def_kind, vis, defining_mod);
             }
         }
     }
diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs
index 49c41470a15..1f2a90829ec 100644
--- a/compiler/rustc_resolve/src/build_reduced_graph.rs
+++ b/compiler/rustc_resolve/src/build_reduced_graph.rs
@@ -931,7 +931,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> {
     /// Builds the reduced graph for a single item in an external crate.
     fn build_reduced_graph_for_external_crate_res(&mut self, child: ModChild) {
         let parent = self.parent_scope.module;
-        let ModChild { ident, res, vis, span, macro_rules } = child;
+        let ModChild { ident, res, vis, span, macro_rules, .. } = child;
         let res = res.expect_non_local();
         let expansion = self.parent_scope.expansion;
         // Record primary definitions.
diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs
index dbf6cec788b..ae3fd0ede6c 100644
--- a/compiler/rustc_resolve/src/check_unused.rs
+++ b/compiler/rustc_resolve/src/check_unused.rs
@@ -393,7 +393,7 @@ impl Resolver<'_, '_> {
             // If we are in the `--test` mode, suppress a help that adds the `#[cfg(test)]`
             // attribute; however, if not, suggest adding the attribute. There is no way to
             // retrieve attributes here because we do not have a `TyCtxt` yet.
-            let test_module_span = if tcx.sess.opts.test {
+            let test_module_span = if tcx.sess.is_test_crate() {
                 None
             } else {
                 let parent_module = visitor.r.get_nearest_non_block_module(
diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs
index bc17ce571a7..77bfcb659de 100644
--- a/compiler/rustc_resolve/src/imports.rs
+++ b/compiler/rustc_resolve/src/imports.rs
@@ -17,6 +17,7 @@ use rustc_data_structures::intern::Interned;
 use rustc_errors::{pluralize, struct_span_err, Applicability, MultiSpan};
 use rustc_hir::def::{self, DefKind, PartialRes};
 use rustc_middle::metadata::ModChild;
+use rustc_middle::metadata::Reexport;
 use rustc_middle::span_bug;
 use rustc_middle::ty;
 use rustc_session::lint::builtin::{
@@ -27,6 +28,7 @@ use rustc_span::edit_distance::find_best_match_for_name;
 use rustc_span::hygiene::LocalExpnId;
 use rustc_span::symbol::{kw, Ident, Symbol};
 use rustc_span::Span;
+use smallvec::SmallVec;
 
 use std::cell::Cell;
 use std::{mem, ptr};
@@ -190,6 +192,17 @@ impl<'a> Import<'a> {
             ImportKind::MacroUse | ImportKind::MacroExport => None,
         }
     }
+
+    fn simplify(&self, r: &Resolver<'_, '_>) -> Reexport {
+        let to_def_id = |id| r.local_def_id(id).to_def_id();
+        match self.kind {
+            ImportKind::Single { id, .. } => Reexport::Single(to_def_id(id)),
+            ImportKind::Glob { id, .. } => Reexport::Glob(to_def_id(id)),
+            ImportKind::ExternCrate { id, .. } => Reexport::ExternCrate(to_def_id(id)),
+            ImportKind::MacroUse => Reexport::MacroUse,
+            ImportKind::MacroExport => Reexport::MacroExport,
+        }
+    }
 }
 
 /// Records information about the resolution of a name in a namespace of a module.
@@ -1252,12 +1265,20 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
 
             module.for_each_child(self, |this, ident, _, binding| {
                 if let Some(res) = this.is_reexport(binding) {
+                    let mut reexport_chain = SmallVec::new();
+                    let mut next_binding = binding;
+                    while let NameBindingKind::Import { binding, import, .. } = next_binding.kind {
+                        reexport_chain.push(import.simplify(this));
+                        next_binding = binding;
+                    }
+
                     reexports.push(ModChild {
                         ident,
                         res,
                         vis: binding.vis,
                         span: binding.span,
                         macro_rules: false,
+                        reexport_chain,
                     });
                 }
             });
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index b1a696d093e..b3f66c4ba33 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -3467,8 +3467,8 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
                         sugg.to_string(),
                         Applicability::MaybeIncorrect,
                     ))
-                } else if res.is_none() && matches!(source, PathSource::Type) {
-                    this.report_missing_type_error(path)
+                } else if res.is_none() && let PathSource::Type | PathSource::Expr(_) = source {
+                    this.suggest_adding_generic_parameter(path, source)
                 } else {
                     None
                 };
diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs
index df7681dc426..37fbfad2de6 100644
--- a/compiler/rustc_resolve/src/late/diagnostics.rs
+++ b/compiler/rustc_resolve/src/late/diagnostics.rs
@@ -2110,9 +2110,10 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
         }
     }
 
-    pub(crate) fn report_missing_type_error(
+    pub(crate) fn suggest_adding_generic_parameter(
         &self,
         path: &[Segment],
+        source: PathSource<'_>,
     ) -> Option<(Span, &'static str, String, Applicability)> {
         let (ident, span) = match path {
             [segment]
@@ -2148,7 +2149,6 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
             // Without the 2nd `true`, we'd suggest `impl <T>` for `impl T` when a type `T` isn't found
             | (Some(Item { kind: kind @ ItemKind::Impl(..), .. }), true, true)
             | (Some(Item { kind, .. }), false, _) => {
-                // Likely missing type parameter.
                 if let Some(generics) = kind.generics() {
                     if span.overlaps(generics.span) {
                         // Avoid the following:
@@ -2161,7 +2161,12 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
                         //   |           not found in this scope
                         return None;
                     }
-                    let msg = "you might be missing a type parameter";
+
+                    let (msg, sugg) = match source {
+                        PathSource::Type => ("you might be missing a type parameter", ident),
+                        PathSource::Expr(_) => ("you might be missing a const parameter", format!("const {ident}: /* Type */")),
+                        _ => return None,
+                    };
                     let (span, sugg) = if let [.., param] = &generics.params[..] {
                         let span = if let [.., bound] = &param.bounds[..] {
                             bound.span()
@@ -2172,9 +2177,9 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
                         } else {
                             param.ident.span
                         };
-                        (span, format!(", {}", ident))
+                        (span, format!(", {sugg}"))
                     } else {
-                        (generics.span, format!("<{}>", ident))
+                        (generics.span, format!("<{sugg}>"))
                     };
                     // Do not suggest if this is coming from macro expansion.
                     if span.can_be_used_for_suggestions() {
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index de0ddb53fa4..613abaa8e3c 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -1258,7 +1258,7 @@ pub fn build_configuration(sess: &Session, mut user_cfg: CrateConfig) -> CrateCo
     // some default and generated configuration items.
     let default_cfg = default_configuration(sess);
     // If the user wants a test runner, then add the test cfg.
-    if sess.opts.test {
+    if sess.is_test_crate() {
         user_cfg.insert((sym::test, None));
     }
     user_cfg.extend(default_cfg.iter().cloned());
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index 5730df9d5c6..340bb158e17 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -294,6 +294,11 @@ impl Session {
         self.crate_types.get().unwrap().as_slice()
     }
 
+    /// Returns true if the crate is a testing one.
+    pub fn is_test_crate(&self) -> bool {
+        self.opts.test
+    }
+
     pub fn needs_crate_hash(&self) -> bool {
         // Why is the crate hash needed for these configurations?
         // - debug_assertions: for the "fingerprint the result" check in
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 0e55e81143d..7affad9aa01 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -1496,6 +1496,7 @@ symbols! {
         trait_alias,
         trait_upcasting,
         transmute,
+        transmute_generic_consts,
         transmute_opts,
         transmute_trait,
         transparent,
diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_nto_qnx_710.rs b/compiler/rustc_target/src/spec/aarch64_unknown_nto_qnx_710.rs
index 8c1126ae6d1..630642dcd33 100644
--- a/compiler/rustc_target/src/spec/aarch64_unknown_nto_qnx_710.rs
+++ b/compiler/rustc_target/src/spec/aarch64_unknown_nto_qnx_710.rs
@@ -23,6 +23,7 @@ pub fn target() -> Target {
                 LinkerFlavor::Gnu(Cc::Yes, Lld::No),
                 &["-Vgcc_ntoaarch64le_cxx"],
             ),
+            env: "nto71".into(),
             ..nto_qnx_base::opts()
         },
     }
diff --git a/compiler/rustc_target/src/spec/i586_pc_nto_qnx700.rs b/compiler/rustc_target/src/spec/i586_pc_nto_qnx700.rs
new file mode 100644
index 00000000000..68afa7fe401
--- /dev/null
+++ b/compiler/rustc_target/src/spec/i586_pc_nto_qnx700.rs
@@ -0,0 +1,24 @@
+use super::nto_qnx_base;
+use crate::spec::{Cc, LinkerFlavor, Lld, StackProbeType, Target, TargetOptions};
+
+pub fn target() -> Target {
+    Target {
+        llvm_target: "i586-pc-unknown".into(),
+        pointer_width: 32,
+        data_layout: "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-\
+            f64:32:64-f80:32-n8:16:32-S128"
+            .into(),
+        arch: "x86".into(),
+        options: TargetOptions {
+            cpu: "pentium4".into(),
+            max_atomic_width: Some(64),
+            pre_link_args: TargetOptions::link_args(
+                LinkerFlavor::Gnu(Cc::Yes, Lld::No),
+                &["-Vgcc_ntox86_cxx"],
+            ),
+            env: "nto70".into(),
+            stack_probes: StackProbeType::X86,
+            ..nto_qnx_base::opts()
+        },
+    }
+}
diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs
index bb45fb125b2..62c58c204e0 100644
--- a/compiler/rustc_target/src/spec/mod.rs
+++ b/compiler/rustc_target/src/spec/mod.rs
@@ -1261,6 +1261,7 @@ supported_targets! {
 
     ("aarch64-unknown-nto-qnx710", aarch64_unknown_nto_qnx_710),
     ("x86_64-pc-nto-qnx710", x86_64_pc_nto_qnx710),
+    ("i586-pc-nto-qnx700", i586_pc_nto_qnx700),
 
     ("aarch64-unknown-linux-ohos", aarch64_unknown_linux_ohos),
     ("armv7-unknown-linux-ohos", armv7_unknown_linux_ohos),
diff --git a/compiler/rustc_target/src/spec/nto_qnx_base.rs b/compiler/rustc_target/src/spec/nto_qnx_base.rs
index 6fb581ef5ce..f1405e9b446 100644
--- a/compiler/rustc_target/src/spec/nto_qnx_base.rs
+++ b/compiler/rustc_target/src/spec/nto_qnx_base.rs
@@ -4,7 +4,6 @@ pub fn opts() -> TargetOptions {
     TargetOptions {
         crt_static_respected: true,
         dynamic_linking: true,
-        env: "nto71".into(),
         executables: true,
         families: cvs!["unix"],
         has_rpath: true,
diff --git a/compiler/rustc_target/src/spec/x86_64_pc_nto_qnx710.rs b/compiler/rustc_target/src/spec/x86_64_pc_nto_qnx710.rs
index e9b3acee2e7..6fb2dfd807a 100644
--- a/compiler/rustc_target/src/spec/x86_64_pc_nto_qnx710.rs
+++ b/compiler/rustc_target/src/spec/x86_64_pc_nto_qnx710.rs
@@ -15,6 +15,7 @@ pub fn target() -> Target {
                 LinkerFlavor::Gnu(Cc::Yes, Lld::No),
                 &["-Vgcc_ntox86_64_cxx"],
             ),
+            env: "nto71".into(),
             ..nto_qnx_base::opts()
         },
     }
diff --git a/compiler/rustc_trait_selection/src/solve/assembly.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
index f8a54f73a33..12ee80b6722 100644
--- a/compiler/rustc_trait_selection/src/solve/assembly.rs
+++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
@@ -1,21 +1,21 @@
 //! Code shared by trait and projection goals for candidate assembly.
 
 use super::search_graph::OverflowHandler;
-#[cfg(doc)]
-use super::trait_goals::structural_traits::*;
 use super::{EvalCtxt, SolverMode};
+use crate::solve::CanonicalResponseExt;
 use crate::traits::coherence;
-use itertools::Itertools;
 use rustc_data_structures::fx::FxIndexSet;
 use rustc_hir::def_id::DefId;
 use rustc_infer::traits::query::NoSolution;
-use rustc_infer::traits::util::elaborate_predicates;
+use rustc_infer::traits::util::elaborate;
 use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, MaybeCause, QueryResult};
 use rustc_middle::ty::fast_reject::TreatProjections;
 use rustc_middle::ty::TypeFoldable;
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use std::fmt::Debug;
 
+pub(super) mod structural_traits;
+
 /// A candidate is a possible way to prove a goal.
 ///
 /// It consists of both the `source`, which describes how that goal would be proven,
@@ -498,7 +498,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         let tcx = self.tcx();
         let own_bounds: FxIndexSet<_> =
             bounds.iter().map(|bound| bound.with_self_ty(tcx, self_ty)).collect();
-        for assumption in elaborate_predicates(tcx, own_bounds.iter().copied()) {
+        for assumption in elaborate(tcx, own_bounds.iter().copied()) {
             // FIXME: Predicates are fully elaborated in the object type's existential bounds
             // list. We want to only consider these pre-elaborated projections, and not other
             // projection predicates that we reach by elaborating the principal trait ref,
@@ -547,61 +547,41 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         }
     }
 
+    /// If there are multiple ways to prove a trait or projection goal, we have
+    /// to somehow try to merge the candidates into one. If that fails, we return
+    /// ambiguity.
     #[instrument(level = "debug", skip(self), ret)]
     pub(super) fn merge_candidates(
         &mut self,
         mut candidates: Vec<Candidate<'tcx>>,
     ) -> QueryResult<'tcx> {
-        match candidates.len() {
-            0 => return Err(NoSolution),
-            1 => return Ok(candidates.pop().unwrap().result),
-            _ => {}
+        // First try merging all candidates. This is complete and fully sound.
+        let responses = candidates.iter().map(|c| c.result).collect::<Vec<_>>();
+        if let Some(result) = self.try_merge_responses(&responses) {
+            return Ok(result);
         }
 
-        if candidates.len() > 1 {
-            let mut i = 0;
-            'outer: while i < candidates.len() {
-                for j in (0..candidates.len()).filter(|&j| i != j) {
-                    if self.candidate_should_be_dropped_in_favor_of(&candidates[i], &candidates[j])
-                    {
-                        debug!(candidate = ?candidates[i], "Dropping candidate #{}/{}", i, candidates.len());
-                        candidates.swap_remove(i);
-                        continue 'outer;
+        // We then check whether we should prioritize `ParamEnv` candidates.
+        //
+        // Doing so is incomplete and would therefore be unsound during coherence.
+        match self.solver_mode() {
+            SolverMode::Coherence => (),
+            // Prioritize `ParamEnv` candidates only if they do not guide inference.
+            //
+            // This is still incomplete as we may add incorrect region bounds.
+            SolverMode::Normal => {
+                let param_env_responses = candidates
+                    .iter()
+                    .filter(|c| matches!(c.source, CandidateSource::ParamEnv(_)))
+                    .map(|c| c.result)
+                    .collect::<Vec<_>>();
+                if let Some(result) = self.try_merge_responses(&param_env_responses) {
+                    if result.has_only_region_constraints() {
+                        return Ok(result);
                     }
                 }
-
-                debug!(candidate = ?candidates[i], "Retaining candidate #{}/{}", i, candidates.len());
-                i += 1;
             }
-
-            // If there are *STILL* multiple candidates that have *different* response
-            // results, give up and report ambiguity.
-            if candidates.len() > 1 && !candidates.iter().map(|cand| cand.result).all_equal() {
-                let certainty = if candidates.iter().all(|x| {
-                    matches!(x.result.value.certainty, Certainty::Maybe(MaybeCause::Overflow))
-                }) {
-                    Certainty::Maybe(MaybeCause::Overflow)
-                } else {
-                    Certainty::AMBIGUOUS
-                };
-                return self.evaluate_added_goals_and_make_canonical_response(certainty);
-            }
-        }
-
-        Ok(candidates.pop().unwrap().result)
-    }
-
-    fn candidate_should_be_dropped_in_favor_of(
-        &self,
-        candidate: &Candidate<'tcx>,
-        other: &Candidate<'tcx>,
-    ) -> bool {
-        // FIXME: implement this
-        match (candidate.source, other.source) {
-            (CandidateSource::Impl(_), _)
-            | (CandidateSource::ParamEnv(_), _)
-            | (CandidateSource::AliasBound, _)
-            | (CandidateSource::BuiltinImpl, _) => false,
         }
+        self.flounder(&responses)
     }
 }
diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals/structural_traits.rs b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs
index 9e851b788a5..cbec39d8285 100644
--- a/compiler/rustc_trait_selection/src/solve/trait_goals/structural_traits.rs
+++ b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs
@@ -11,7 +11,7 @@ use crate::solve::EvalCtxt;
 //
 // For types with an "existential" binder, i.e. generator witnesses, we also
 // instantiate the binder with placeholders eagerly.
-pub(super) fn instantiate_constituent_tys_for_auto_trait<'tcx>(
+pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait<'tcx>(
     ecx: &EvalCtxt<'_, 'tcx>,
     ty: Ty<'tcx>,
 ) -> Result<Vec<Ty<'tcx>>, NoSolution> {
@@ -87,7 +87,7 @@ pub(super) fn instantiate_constituent_tys_for_auto_trait<'tcx>(
     }
 }
 
-fn replace_erased_lifetimes_with_bound_vars<'tcx>(
+pub(in crate::solve) fn replace_erased_lifetimes_with_bound_vars<'tcx>(
     tcx: TyCtxt<'tcx>,
     ty: Ty<'tcx>,
 ) -> ty::Binder<'tcx, Ty<'tcx>> {
@@ -108,7 +108,7 @@ fn replace_erased_lifetimes_with_bound_vars<'tcx>(
     ty::Binder::bind_with_vars(ty, bound_vars)
 }
 
-pub(super) fn instantiate_constituent_tys_for_sized_trait<'tcx>(
+pub(in crate::solve) fn instantiate_constituent_tys_for_sized_trait<'tcx>(
     ecx: &EvalCtxt<'_, 'tcx>,
     ty: Ty<'tcx>,
 ) -> Result<Vec<Ty<'tcx>>, NoSolution> {
@@ -158,7 +158,7 @@ pub(super) fn instantiate_constituent_tys_for_sized_trait<'tcx>(
     }
 }
 
-pub(super) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>(
+pub(in crate::solve) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>(
     ecx: &EvalCtxt<'_, 'tcx>,
     ty: Ty<'tcx>,
 ) -> Result<Vec<Ty<'tcx>>, NoSolution> {
@@ -224,7 +224,7 @@ pub(super) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>(
 }
 
 // Returns a binder of the tupled inputs types and output type from a builtin callable type.
-pub(crate) fn extract_tupled_inputs_and_output_from_callable<'tcx>(
+pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<'tcx>(
     tcx: TyCtxt<'tcx>,
     self_ty: Ty<'tcx>,
     goal_kind: ty::ClosureKind,
@@ -337,7 +337,13 @@ pub(crate) fn extract_tupled_inputs_and_output_from_callable<'tcx>(
 /// additional step of eagerly folding the associated types in the where
 /// clauses of the impl. In this example, that means replacing
 /// `<Self as Foo>::Bar` with `Ty` in the first impl.
-pub(crate) fn predicates_for_object_candidate<'tcx>(
+///
+// FIXME: This is only necessary as `<Self as Trait>::Assoc: ItemBound`
+// bounds in impls are trivially proven using the item bound candidates.
+// This is unsound in general and once that is fixed, we don't need to
+// normalize eagerly here. See https://github.com/lcnr/solver-woes/issues/9
+// for more details.
+pub(in crate::solve) fn predicates_for_object_candidate<'tcx>(
     ecx: &EvalCtxt<'_, 'tcx>,
     param_env: ty::ParamEnv<'tcx>,
     trait_ref: ty::TraitRef<'tcx>,
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs
index cdbab5bd8d2..28aca76cceb 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs
@@ -357,7 +357,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
                             // deal with `has_changed` in the next iteration.
                             new_goals.normalizes_to_hack_goal =
                                 Some(this.resolve_vars_if_possible(goal));
-                            has_changed = has_changed.map_err(|c| c.unify_and(certainty));
+                            has_changed = has_changed.map_err(|c| c.unify_with(certainty));
                         }
                     }
                 }
@@ -378,7 +378,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
                         Certainty::Yes => {}
                         Certainty::Maybe(_) => {
                             new_goals.goals.push(goal);
-                            has_changed = has_changed.map_err(|c| c.unify_and(certainty));
+                            has_changed = has_changed.map_err(|c| c.unify_with(certainty));
                         }
                     }
                 }
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs
index 714b6dfb717..861fa0a305a 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs
@@ -50,7 +50,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         certainty: Certainty,
     ) -> QueryResult<'tcx> {
         let goals_certainty = self.try_evaluate_added_goals()?;
-        let certainty = certainty.unify_and(goals_certainty);
+        let certainty = certainty.unify_with(goals_certainty);
 
         let external_constraints = self.compute_external_query_constraints()?;
 
diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs
index 9ddae8f8dcd..19bcbd46144 100644
--- a/compiler/rustc_trait_selection/src/solve/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/mod.rs
@@ -46,6 +46,8 @@ enum SolverMode {
 
 trait CanonicalResponseExt {
     fn has_no_inference_or_external_constraints(&self) -> bool;
+
+    fn has_only_region_constraints(&self) -> bool;
 }
 
 impl<'tcx> CanonicalResponseExt for Canonical<'tcx, Response<'tcx>> {
@@ -54,6 +56,11 @@ impl<'tcx> CanonicalResponseExt for Canonical<'tcx, Response<'tcx>> {
             && self.value.var_values.is_identity()
             && self.value.external_constraints.opaque_types.is_empty()
     }
+
+    fn has_only_region_constraints(&self) -> bool {
+        self.value.var_values.is_identity_modulo_regions()
+            && self.value.external_constraints.opaque_types.is_empty()
+    }
 }
 
 impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
@@ -221,12 +228,17 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
             (Some(alias_lhs), Some(alias_rhs)) => {
                 debug!("both sides are aliases");
 
-                let candidates = vec![
-                    // LHS normalizes-to RHS
-                    evaluate_normalizes_to(self, alias_lhs, rhs, direction, Invert::No),
-                    // RHS normalizes-to RHS
-                    evaluate_normalizes_to(self, alias_rhs, lhs, direction, Invert::Yes),
-                    // Relate via substs
+                let mut candidates = Vec::new();
+                // LHS normalizes-to RHS
+                candidates.extend(
+                    evaluate_normalizes_to(self, alias_lhs, rhs, direction, Invert::No).ok(),
+                );
+                // RHS normalizes-to RHS
+                candidates.extend(
+                    evaluate_normalizes_to(self, alias_rhs, lhs, direction, Invert::Yes).ok(),
+                );
+                // Relate via substs
+                candidates.extend(
                     self.probe(|ecx| {
                         let span = tracing::span!(
                             tracing::Level::DEBUG,
@@ -247,11 +259,16 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
                         }
 
                         ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
-                    }),
-                ];
+                    })
+                    .ok(),
+                );
                 debug!(?candidates);
 
-                self.try_merge_responses(candidates.into_iter())
+                if let Some(merged) = self.try_merge_responses(&candidates) {
+                    Ok(merged)
+                } else {
+                    self.flounder(&candidates)
+                }
             }
         }
     }
@@ -289,43 +306,51 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         debug!("added_goals={:?}", &self.nested_goals.goals[current_len..]);
     }
 
-    #[instrument(level = "debug", skip(self, responses))]
+    /// Try to merge multiple possible ways to prove a goal, if that is not possible returns `None`.
+    ///
+    /// In this case we tend to flounder and return ambiguity by calling `[EvalCtxt::flounder]`.
+    #[instrument(level = "debug", skip(self), ret)]
     fn try_merge_responses(
         &mut self,
-        responses: impl Iterator<Item = QueryResult<'tcx>>,
-    ) -> QueryResult<'tcx> {
-        let candidates = responses.into_iter().flatten().collect::<Box<[_]>>();
-
-        if candidates.is_empty() {
-            return Err(NoSolution);
+        responses: &[CanonicalResponse<'tcx>],
+    ) -> Option<CanonicalResponse<'tcx>> {
+        if responses.is_empty() {
+            return None;
         }
 
         // FIXME(-Ztrait-solver=next): We should instead try to find a `Certainty::Yes` response with
         // a subset of the constraints that all the other responses have.
-        let one = candidates[0];
-        if candidates[1..].iter().all(|resp| resp == &one) {
-            return Ok(one);
+        let one = responses[0];
+        if responses[1..].iter().all(|&resp| resp == one) {
+            return Some(one);
         }
 
-        if let Some(response) = candidates.iter().find(|response| {
-            response.value.certainty == Certainty::Yes
-                && response.has_no_inference_or_external_constraints()
-        }) {
-            return Ok(*response);
-        }
+        responses
+            .iter()
+            .find(|response| {
+                response.value.certainty == Certainty::Yes
+                    && response.has_no_inference_or_external_constraints()
+            })
+            .copied()
+    }
 
-        let certainty = candidates.iter().fold(Certainty::AMBIGUOUS, |certainty, response| {
-            certainty.unify_and(response.value.certainty)
+    /// If we fail to merge responses we flounder and return overflow or ambiguity.
+    #[instrument(level = "debug", skip(self), ret)]
+    fn flounder(&mut self, responses: &[CanonicalResponse<'tcx>]) -> QueryResult<'tcx> {
+        if responses.is_empty() {
+            return Err(NoSolution);
+        }
+        let certainty = responses.iter().fold(Certainty::AMBIGUOUS, |certainty, response| {
+            certainty.unify_with(response.value.certainty)
         });
-        // FIXME(-Ztrait-solver=next): We should take the intersection of the constraints on all the
-        // responses and use that for the constraints of this ambiguous response.
-        debug!(">1 response, bailing with {certainty:?}");
+
         let response = self.evaluate_added_goals_and_make_canonical_response(certainty);
-        if let Ok(response) = &response {
+        if let Ok(response) = response {
             assert!(response.has_no_inference_or_external_constraints());
+            Ok(response)
+        } else {
+            bug!("failed to make floundered response: {responses:?}");
         }
-
-        response
     }
 }
 
diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs
index e0a69438dec..2a47da81ec7 100644
--- a/compiler/rustc_trait_selection/src/solve/project_goals.rs
+++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs
@@ -1,7 +1,6 @@
 use crate::traits::specialization_graph;
 
-use super::assembly;
-use super::trait_goals::structural_traits;
+use super::assembly::{self, structural_traits};
 use super::EvalCtxt;
 use rustc_errors::ErrorGuaranteed;
 use rustc_hir::def::DefKind;
diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs
index ce3db83d33d..81f89fd950c 100644
--- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs
+++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs
@@ -1,6 +1,7 @@
 //! Dealing with trait goals, i.e. `T: Trait<'a, U>`.
 
-use super::{assembly, EvalCtxt, SolverMode};
+use super::assembly::{self, structural_traits};
+use super::{EvalCtxt, SolverMode};
 use rustc_hir::def_id::DefId;
 use rustc_hir::LangItem;
 use rustc_infer::traits::query::NoSolution;
@@ -11,8 +12,6 @@ use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt};
 use rustc_middle::ty::{TraitPredicate, TypeVisitableExt};
 use rustc_span::DUMMY_SP;
 
-pub mod structural_traits;
-
 impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     fn self_ty(self) -> Ty<'tcx> {
         self.self_ty()
@@ -221,8 +220,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
         let self_ty = tcx.erase_regions(goal.predicate.self_ty());
 
         if let Ok(layout) = tcx.layout_of(goal.param_env.and(self_ty))
-            && layout.layout.size() == tcx.data_layout.pointer_size
-            && layout.layout.align().abi == tcx.data_layout.pointer_align.abi
+            && layout.layout.is_pointer_like(&tcx.data_layout)
         {
             // FIXME: We could make this faster by making a no-constraints response
             ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs
index 878c502655c..a53d414be9e 100644
--- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs
+++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs
@@ -234,7 +234,7 @@ impl<'tcx> AutoTraitFinder<'tcx> {
     /// constructed once for a given type. As part of the construction process, the `ParamEnv` will
     /// have any supertrait bounds normalized -- e.g., if we have a type `struct Foo<T: Copy>`, the
     /// `ParamEnv` will contain `T: Copy` and `T: Clone`, since `Copy: Clone`. When we construct our
-    /// own `ParamEnv`, we need to do this ourselves, through `traits::elaborate_predicates`, or
+    /// own `ParamEnv`, we need to do this ourselves, through `traits::elaborate`, or
     /// else `SelectionContext` will choke on the missing predicates. However, this should never
     /// show up in the final synthesized generics: we don't want our generated docs page to contain
     /// something like `T: Copy + Clone`, as that's redundant. Therefore, we keep track of a
@@ -346,10 +346,8 @@ impl<'tcx> AutoTraitFinder<'tcx> {
                 _ => panic!("Unexpected error for '{:?}': {:?}", ty, result),
             };
 
-            let normalized_preds = elaborate_predicates(
-                tcx,
-                computed_preds.clone().chain(user_computed_preds.iter().cloned()),
-            );
+            let normalized_preds =
+                elaborate(tcx, computed_preds.clone().chain(user_computed_preds.iter().cloned()));
             new_env = ty::ParamEnv::new(
                 tcx.mk_predicates_from_iter(normalized_preds),
                 param_env.reveal(),
diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs
index d360158fdf8..53d4f95e9e3 100644
--- a/compiler/rustc_trait_selection/src/traits/coherence.rs
+++ b/compiler/rustc_trait_selection/src/traits/coherence.rs
@@ -368,7 +368,7 @@ fn negative_impl_exists<'tcx>(
     }
 
     // Try to prove a negative obligation exists for super predicates
-    for pred in util::elaborate_predicates(infcx.tcx, iter::once(o.predicate)) {
+    for pred in util::elaborate(infcx.tcx, iter::once(o.predicate)) {
         if resolve_negative_obligation(infcx.fork(), &o.with(infcx.tcx, pred), body_def_id) {
             return true;
         }
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs
index 672b3365ff4..0475f24d87c 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs
@@ -1,6 +1,6 @@
 use rustc_hir::def_id::DefId;
 use rustc_infer::infer::{InferCtxt, LateBoundRegionConversionTime};
-use rustc_infer::traits::util::elaborate_predicates_with_span;
+use rustc_infer::traits::util::elaborate;
 use rustc_infer::traits::{Obligation, ObligationCause, TraitObligation};
 use rustc_middle::ty;
 use rustc_span::{Span, DUMMY_SP};
@@ -82,7 +82,7 @@ pub fn recompute_applicable_impls<'tcx>(
 
     let predicates =
         tcx.predicates_of(obligation.cause.body_id.to_def_id()).instantiate_identity(tcx);
-    for (pred, span) in elaborate_predicates_with_span(tcx, predicates.into_iter()) {
+    for (pred, span) in elaborate(tcx, predicates.into_iter()) {
         let kind = pred.kind();
         if let ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)) = kind.skip_binder()
             && param_env_candidate_may_apply(kind.rebind(trait_pred))
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
index c19798213b7..6ebf056f0e8 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
@@ -1624,7 +1624,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
             }
         };
 
-        for pred in super::elaborate_predicates(self.tcx, std::iter::once(cond)) {
+        for pred in super::elaborate(self.tcx, std::iter::once(cond)) {
             let bound_predicate = pred.kind();
             if let ty::PredicateKind::Clause(ty::Clause::Trait(implication)) =
                 bound_predicate.skip_binder()
diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs
index 8d831dca6e3..8a203dec86b 100644
--- a/compiler/rustc_trait_selection/src/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/mod.rs
@@ -58,10 +58,7 @@ pub use self::specialize::{specialization_graph, translate_substs, OverlapError}
 pub use self::structural_match::{
     search_for_adt_const_param_violation, search_for_structural_match_violation,
 };
-pub use self::util::{
-    elaborate_obligations, elaborate_predicates, elaborate_predicates_with_span,
-    elaborate_trait_ref, elaborate_trait_refs,
-};
+pub use self::util::elaborate;
 pub use self::util::{expand_trait_aliases, TraitAliasExpander};
 pub use self::util::{get_vtable_index_of_object_method, impl_item_is_final, upcast_choices};
 pub use self::util::{
@@ -267,7 +264,7 @@ pub fn normalize_param_env_or_error<'tcx>(
     // and errors will get reported then; so outside of type inference we
     // can be sure that no errors should occur.
     let mut predicates: Vec<_> =
-        util::elaborate_predicates(tcx, unnormalized_env.caller_bounds().into_iter()).collect();
+        util::elaborate(tcx, unnormalized_env.caller_bounds().into_iter()).collect();
 
     debug!("normalize_param_env_or_error: elaborated-predicates={:?}", predicates);
 
diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs
index dbf6b78572a..b8ad1925e4e 100644
--- a/compiler/rustc_trait_selection/src/traits/object_safety.rs
+++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs
@@ -8,7 +8,7 @@
 //!   - not reference the erased type `Self` except for in this receiver;
 //!   - not have generic type parameters.
 
-use super::{elaborate_predicates, elaborate_trait_ref};
+use super::elaborate;
 
 use crate::infer::TyCtxtInferExt;
 use crate::traits::query::evaluate_obligation::InferCtxtExt;
@@ -379,7 +379,7 @@ fn generics_require_sized_self(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
     // Search for a predicate like `Self : Sized` amongst the trait bounds.
     let predicates = tcx.predicates_of(def_id);
     let predicates = predicates.instantiate_identity(tcx).predicates;
-    elaborate_predicates(tcx, predicates.into_iter()).any(|pred| match pred.kind().skip_binder() {
+    elaborate(tcx, predicates.into_iter()).any(|pred| match pred.kind().skip_binder() {
         ty::PredicateKind::Clause(ty::Clause::Trait(ref trait_pred)) => {
             trait_pred.def_id() == sized_def_id && trait_pred.self_ty().is_param(0)
         }
@@ -666,7 +666,8 @@ fn object_ty_for_trait<'tcx>(
     });
     debug!(?trait_predicate);
 
-    let mut elaborated_predicates: Vec<_> = elaborate_trait_ref(tcx, trait_ref)
+    let pred: ty::Predicate<'tcx> = trait_ref.to_predicate(tcx);
+    let mut elaborated_predicates: Vec<_> = elaborate(tcx, [pred])
         .filter_map(|pred| {
             debug!(?pred);
             let pred = pred.to_opt_poly_projection_pred()?;
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 090312338e0..a794d20d683 100644
--- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
@@ -97,7 +97,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             } else if lang_items.tuple_trait() == Some(def_id) {
                 self.assemble_candidate_for_tuple(obligation, &mut candidates);
             } else if lang_items.pointer_like() == Some(def_id) {
-                self.assemble_candidate_for_ptr_sized(obligation, &mut candidates);
+                self.assemble_candidate_for_pointer_like(obligation, &mut candidates);
             } else if lang_items.fn_ptr_trait() == Some(def_id) {
                 self.assemble_candidates_for_fn_ptr_trait(obligation, &mut candidates);
             } else {
@@ -942,15 +942,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         }
     }
 
-    fn assemble_candidate_for_ptr_sized(
+    fn assemble_candidate_for_pointer_like(
         &mut self,
         obligation: &TraitObligation<'tcx>,
         candidates: &mut SelectionCandidateSet<'tcx>,
     ) {
         // The regions of a type don't affect the size of the type
-        let self_ty = self
-            .tcx()
-            .erase_regions(self.tcx().erase_late_bound_regions(obligation.predicate.self_ty()));
+        let tcx = self.tcx();
+        let self_ty =
+            tcx.erase_regions(tcx.erase_late_bound_regions(obligation.predicate.self_ty()));
 
         // But if there are inference variables, we have to wait until it's resolved.
         if self_ty.has_non_region_infer() {
@@ -958,9 +958,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             return;
         }
 
-        if let Ok(layout) = self.tcx().layout_of(obligation.param_env.and(self_ty))
-            && layout.layout.size() == self.tcx().data_layout.pointer_size
-            && layout.layout.align().abi == self.tcx().data_layout.pointer_align.abi
+        if let Ok(layout) = tcx.layout_of(obligation.param_env.and(self_ty))
+            && layout.layout.is_pointer_like(&tcx.data_layout)
         {
             candidates.vec.push(BuiltinCandidate { has_nested: false });
         }
diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs
index 156674e33c3..3d026506a5a 100644
--- a/compiler/rustc_trait_selection/src/traits/wf.rs
+++ b/compiler/rustc_trait_selection/src/traits/wf.rs
@@ -364,7 +364,7 @@ impl<'tcx> WfPredicates<'tcx> {
         };
 
         if let Elaborate::All = elaborate {
-            let implied_obligations = traits::util::elaborate_obligations(tcx, obligations);
+            let implied_obligations = traits::util::elaborate(tcx, obligations);
             let implied_obligations = implied_obligations.map(extend);
             self.out.extend(implied_obligations);
         } else {
@@ -920,7 +920,7 @@ pub(crate) fn required_region_bounds<'tcx>(
 ) -> Vec<ty::Region<'tcx>> {
     assert!(!erased_self_ty.has_escaping_bound_vars());
 
-    traits::elaborate_predicates(tcx, predicates)
+    traits::elaborate(tcx, predicates)
         .filter_map(|pred| {
             debug!(?pred);
             match pred.kind().skip_binder() {
diff --git a/compiler/rustc_ty_utils/messages.ftl b/compiler/rustc_ty_utils/messages.ftl
index abe65a0e3fe..a1e97bb95bc 100644
--- a/compiler/rustc_ty_utils/messages.ftl
+++ b/compiler/rustc_ty_utils/messages.ftl
@@ -45,3 +45,13 @@ ty_utils_control_flow_not_supported = control flow is not supported in generic c
 ty_utils_inline_asm_not_supported = assembly is not supported in generic constants
 
 ty_utils_operation_not_supported = unsupported operation in generic constants
+
+ty_utils_unexpected_fnptr_associated_item = `FnPtr` trait with unexpected associated item
+
+ty_utils_zero_length_simd_type = monomorphising SIMD type `{$ty}` of zero length
+
+ty_utils_multiple_array_fields_simd_type = monomorphising SIMD type `{$ty}` with more than one array field
+
+ty_utils_oversized_simd_type = monomorphising SIMD type `{$ty}` of length greater than {$max_lanes}
+
+ty_utils_non_primative_simd_type = monomorphising SIMD type `{$ty}` with a non-primitive-scalar (integer/float/pointer) element type `{$e_ty}`
diff --git a/compiler/rustc_ty_utils/src/errors.rs b/compiler/rustc_ty_utils/src/errors.rs
index ab3e62f0484..3db3c98e9e2 100644
--- a/compiler/rustc_ty_utils/src/errors.rs
+++ b/compiler/rustc_ty_utils/src/errors.rs
@@ -67,3 +67,36 @@ pub enum GenericConstantTooComplexSub {
     #[label(ty_utils_operation_not_supported)]
     OperationNotSupported(#[primary_span] Span),
 }
+
+#[derive(Diagnostic)]
+#[diag(ty_utils_unexpected_fnptr_associated_item)]
+pub struct UnexpectedFnPtrAssociatedItem {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(ty_utils_zero_length_simd_type)]
+pub struct ZeroLengthSimdType<'tcx> {
+    pub ty: Ty<'tcx>,
+}
+
+#[derive(Diagnostic)]
+#[diag(ty_utils_multiple_array_fields_simd_type)]
+pub struct MultipleArrayFieldsSimdType<'tcx> {
+    pub ty: Ty<'tcx>,
+}
+
+#[derive(Diagnostic)]
+#[diag(ty_utils_oversized_simd_type)]
+pub struct OversizedSimdType<'tcx> {
+    pub ty: Ty<'tcx>,
+    pub max_lanes: u64,
+}
+
+#[derive(Diagnostic)]
+#[diag(ty_utils_non_primative_simd_type)]
+pub struct NonPrimitiveSimdType<'tcx> {
+    pub ty: Ty<'tcx>,
+    pub e_ty: Ty<'tcx>,
+}
diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs
index ad70154c98e..0a6c118093e 100644
--- a/compiler/rustc_ty_utils/src/instance.rs
+++ b/compiler/rustc_ty_utils/src/instance.rs
@@ -8,6 +8,8 @@ use rustc_span::sym;
 use rustc_trait_selection::traits;
 use traits::{translate_substs, Reveal};
 
+use crate::errors::UnexpectedFnPtrAssociatedItem;
+
 fn resolve_instance<'tcx>(
     tcx: TyCtxt<'tcx>,
     key: ty::ParamEnvAnd<'tcx, (DefId, SubstsRef<'tcx>)>,
@@ -282,10 +284,9 @@ fn resolve_associated_item<'tcx>(
                         substs: rcvr_substs,
                     })
                 } else {
-                    tcx.sess.span_fatal(
-                        tcx.def_span(trait_item_id),
-                        "`FnPtrAddr` trait with unexpected assoc item",
-                    )
+                    tcx.sess.emit_fatal(UnexpectedFnPtrAssociatedItem {
+                        span: tcx.def_span(trait_item_id),
+                    })
                 }
             } else {
                 None
diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs
index d4420ec88db..63eb34f7d55 100644
--- a/compiler/rustc_ty_utils/src/layout.rs
+++ b/compiler/rustc_ty_utils/src/layout.rs
@@ -17,6 +17,9 @@ use rustc_target::abi::*;
 use std::fmt::Debug;
 use std::iter;
 
+use crate::errors::{
+    MultipleArrayFieldsSimdType, NonPrimitiveSimdType, OversizedSimdType, ZeroLengthSimdType,
+};
 use crate::layout_sanity_check::sanity_check_layout;
 
 pub fn provide(providers: &mut ty::query::Providers) {
@@ -294,6 +297,8 @@ fn layout_of_uncached<'tcx>(
                 return Err(LayoutError::Unknown(ty));
             }
 
+            let fields = &def.non_enum_variant().fields;
+
             // Supported SIMD vectors are homogeneous ADTs with at least one field:
             //
             // * #[repr(simd)] struct S(T, T, T, T);
@@ -304,18 +309,22 @@ fn layout_of_uncached<'tcx>(
 
             // SIMD vectors with zero fields are not supported.
             // (should be caught by typeck)
-            if def.non_enum_variant().fields.is_empty() {
-                tcx.sess.fatal(&format!("monomorphising SIMD type `{}` of zero length", ty));
+            if fields.is_empty() {
+                tcx.sess.emit_fatal(ZeroLengthSimdType { ty })
             }
 
             // Type of the first ADT field:
-            let f0_ty = def.non_enum_variant().fields[FieldIdx::from_u32(0)].ty(tcx, substs);
+            let f0_ty = fields[FieldIdx::from_u32(0)].ty(tcx, substs);
 
             // Heterogeneous SIMD vectors are not supported:
             // (should be caught by typeck)
-            for fi in &def.non_enum_variant().fields {
+            for fi in fields {
                 if fi.ty(tcx, substs) != f0_ty {
-                    tcx.sess.fatal(&format!("monomorphising heterogeneous SIMD type `{}`", ty));
+                    tcx.sess.delay_span_bug(
+                        DUMMY_SP,
+                        "#[repr(simd)] was applied to an ADT with hetrogeneous field type",
+                    );
+                    return Err(LayoutError::Unknown(ty));
                 }
             }
 
@@ -330,12 +339,9 @@ fn layout_of_uncached<'tcx>(
                 // First ADT field is an array:
 
                 // SIMD vectors with multiple array fields are not supported:
-                // (should be caught by typeck)
+                // Can't be caught by typeck with a generic simd type.
                 if def.non_enum_variant().fields.len() != 1 {
-                    tcx.sess.fatal(&format!(
-                        "monomorphising SIMD type `{}` with more than one array field",
-                        ty
-                    ));
+                    tcx.sess.emit_fatal(MultipleArrayFieldsSimdType { ty });
                 }
 
                 // Extract the number of elements from the layout of the array field:
@@ -355,12 +361,9 @@ fn layout_of_uncached<'tcx>(
             //
             // Can't be caught in typeck if the array length is generic.
             if e_len == 0 {
-                tcx.sess.fatal(&format!("monomorphising SIMD type `{}` of zero length", ty));
+                tcx.sess.emit_fatal(ZeroLengthSimdType { ty });
             } else if e_len > MAX_SIMD_LANES {
-                tcx.sess.fatal(&format!(
-                    "monomorphising SIMD type `{}` of length greater than {}",
-                    ty, MAX_SIMD_LANES,
-                ));
+                tcx.sess.emit_fatal(OversizedSimdType { ty, max_lanes: MAX_SIMD_LANES });
             }
 
             // Compute the ABI of the element type:
@@ -368,11 +371,7 @@ fn layout_of_uncached<'tcx>(
             let Abi::Scalar(e_abi) = e_ly.abi else {
                 // This error isn't caught in typeck, e.g., if
                 // the element type of the vector is generic.
-                tcx.sess.fatal(&format!(
-                    "monomorphising SIMD type `{}` with a non-primitive-scalar \
-                    (integer/float/pointer) element type `{}`",
-                    ty, e_ty
-                ))
+                tcx.sess.emit_fatal(NonPrimitiveSimdType { ty, e_ty });
             };
 
             // Compute the size and alignment of the vector:
diff --git a/compiler/rustc_ty_utils/src/lib.rs b/compiler/rustc_ty_utils/src/lib.rs
index 9195964a2f3..2613445f39b 100644
--- a/compiler/rustc_ty_utils/src/lib.rs
+++ b/compiler/rustc_ty_utils/src/lib.rs
@@ -10,6 +10,8 @@
 #![feature(never_type)]
 #![feature(box_patterns)]
 #![recursion_limit = "256"]
+#![deny(rustc::untranslatable_diagnostic)]
+#![deny(rustc::diagnostic_outside_of_impl)]
 
 #[macro_use]
 extern crate rustc_middle;
diff --git a/config.example.toml b/config.example.toml
index 15c6bc0f123..b27a34e61c5 100644
--- a/config.example.toml
+++ b/config.example.toml
@@ -585,7 +585,7 @@ changelog-seen = 2
 # Having the git information can cause a lot of rebuilds during development.
 #
 # FIXME(#76720): this can causes bugs if different compilers reuse the same metadata cache.
-#ignore-git = if rust.channel == "dev" { true } else { false }
+#omit-git-hash = if rust.channel == "dev" { true } else { false }
 
 # Whether to create a source tarball by default when running `x dist`.
 #
diff --git a/library/core/src/iter/range.rs b/library/core/src/iter/range.rs
index 1cd71193bd7..37db074293d 100644
--- a/library/core/src/iter/range.rs
+++ b/library/core/src/iter/range.rs
@@ -1,5 +1,4 @@
 use crate::convert::TryFrom;
-use crate::marker::Destruct;
 use crate::mem;
 use crate::num::NonZeroUsize;
 use crate::ops::{self, Try};
@@ -22,8 +21,7 @@ unsafe_impl_trusted_step![char i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usi
 /// The *successor* operation moves towards values that compare greater.
 /// The *predecessor* operation moves towards values that compare lesser.
 #[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")]
-#[const_trait]
-pub trait Step: ~const Clone + ~const PartialOrd + Sized {
+pub trait Step: Clone + PartialOrd + Sized {
     /// Returns the number of *successor* steps required to get from `start` to `end`.
     ///
     /// Returns `None` if the number of steps would overflow `usize`
@@ -237,8 +235,7 @@ macro_rules! step_integer_impls {
         $(
             #[allow(unreachable_patterns)]
             #[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")]
-            #[rustc_const_unstable(feature = "const_iter", issue = "92476")]
-            impl const Step for $u_narrower {
+            impl Step for $u_narrower {
                 step_identical_methods!();
 
                 #[inline]
@@ -270,8 +267,7 @@ macro_rules! step_integer_impls {
 
             #[allow(unreachable_patterns)]
             #[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")]
-            #[rustc_const_unstable(feature = "const_iter", issue = "92476")]
-            impl const Step for $i_narrower {
+            impl Step for $i_narrower {
                 step_identical_methods!();
 
                 #[inline]
@@ -335,8 +331,7 @@ macro_rules! step_integer_impls {
         $(
             #[allow(unreachable_patterns)]
             #[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")]
-            #[rustc_const_unstable(feature = "const_iter", issue = "92476")]
-            impl const Step for $u_wider {
+            impl Step for $u_wider {
                 step_identical_methods!();
 
                 #[inline]
@@ -361,8 +356,7 @@ macro_rules! step_integer_impls {
 
             #[allow(unreachable_patterns)]
             #[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")]
-            #[rustc_const_unstable(feature = "const_iter", issue = "92476")]
-            impl const Step for $i_wider {
+            impl Step for $i_wider {
                 step_identical_methods!();
 
                 #[inline]
@@ -412,8 +406,7 @@ step_integer_impls! {
 }
 
 #[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")]
-#[rustc_const_unstable(feature = "const_iter", issue = "92476")]
-impl const Step for char {
+impl Step for char {
     #[inline]
     fn steps_between(&start: &char, &end: &char) -> Option<usize> {
         let start = start as u32;
@@ -431,7 +424,6 @@ impl const Step for char {
     }
 
     #[inline]
-    #[rustc_allow_const_fn_unstable(const_try)]
     fn forward_checked(start: char, count: usize) -> Option<char> {
         let start = start as u32;
         let mut res = Step::forward_checked(start, count)?;
@@ -448,7 +440,6 @@ impl const Step for char {
     }
 
     #[inline]
-    #[rustc_allow_const_fn_unstable(const_try)]
     fn backward_checked(start: char, count: usize) -> Option<char> {
         let start = start as u32;
         let mut res = Step::backward_checked(start, count)?;
@@ -524,7 +515,6 @@ macro_rules! range_incl_exact_iter_impl {
 }
 
 /// Specialization implementations for `Range`.
-#[const_trait]
 trait RangeIteratorImpl {
     type Item;
 
@@ -539,7 +529,7 @@ trait RangeIteratorImpl {
     fn spec_advance_back_by(&mut self, n: usize) -> Result<(), NonZeroUsize>;
 }
 
-impl<A: ~const Step + ~const Destruct> const RangeIteratorImpl for ops::Range<A> {
+impl<A: Step> RangeIteratorImpl for ops::Range<A> {
     type Item = A;
 
     #[inline]
@@ -625,7 +615,7 @@ impl<A: ~const Step + ~const Destruct> const RangeIteratorImpl for ops::Range<A>
     }
 }
 
-impl<T: ~const TrustedStep + ~const Destruct> const RangeIteratorImpl for ops::Range<T> {
+impl<T: TrustedStep> RangeIteratorImpl for ops::Range<T> {
     #[inline]
     fn spec_next(&mut self) -> Option<T> {
         if self.start < self.end {
@@ -713,8 +703,7 @@ impl<T: ~const TrustedStep + ~const Destruct> const RangeIteratorImpl for ops::R
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
-#[rustc_const_unstable(feature = "const_iter", issue = "92476")]
-impl<A: ~const Step + ~const Destruct> const Iterator for ops::Range<A> {
+impl<A: Step> Iterator for ops::Range<A> {
     type Item = A;
 
     #[inline]
@@ -824,8 +813,7 @@ range_incl_exact_iter_impl! {
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
-#[rustc_const_unstable(feature = "const_iter", issue = "92476")]
-impl<A: ~const Step + ~const Destruct> const DoubleEndedIterator for ops::Range<A> {
+impl<A: Step> DoubleEndedIterator for ops::Range<A> {
     #[inline]
     fn next_back(&mut self) -> Option<A> {
         self.spec_next_back()
diff --git a/library/core/src/iter/traits/double_ended.rs b/library/core/src/iter/traits/double_ended.rs
index d82ecb698dd..182d9f758ad 100644
--- a/library/core/src/iter/traits/double_ended.rs
+++ b/library/core/src/iter/traits/double_ended.rs
@@ -1,4 +1,3 @@
-use crate::marker::Destruct;
 use crate::num::NonZeroUsize;
 use crate::ops::{ControlFlow, Try};
 
@@ -39,7 +38,6 @@ use crate::ops::{ControlFlow, Try};
 /// ```
 #[stable(feature = "rust1", since = "1.0.0")]
 #[cfg_attr(not(test), rustc_diagnostic_item = "DoubleEndedIterator")]
-#[const_trait]
 pub trait DoubleEndedIterator: Iterator {
     /// Removes and returns an element from the end of the iterator.
     ///
@@ -136,10 +134,7 @@ pub trait DoubleEndedIterator: Iterator {
     /// [`Err(k)`]: Err
     #[inline]
     #[unstable(feature = "iter_advance_by", reason = "recently added", issue = "77404")]
-    fn advance_back_by(&mut self, n: usize) -> Result<(), NonZeroUsize>
-    where
-        Self::Item: ~const Destruct,
-    {
+    fn advance_back_by(&mut self, n: usize) -> Result<(), NonZeroUsize> {
         for i in 0..n {
             if self.next_back().is_none() {
                 // SAFETY: `i` is always less than `n`.
@@ -192,7 +187,6 @@ pub trait DoubleEndedIterator: Iterator {
     /// ```
     #[inline]
     #[stable(feature = "iter_nth_back", since = "1.37.0")]
-    #[rustc_do_not_const_check]
     fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
         if self.advance_back_by(n).is_err() {
             return None;
@@ -232,7 +226,6 @@ pub trait DoubleEndedIterator: Iterator {
     /// ```
     #[inline]
     #[stable(feature = "iterator_try_fold", since = "1.27.0")]
-    #[rustc_do_not_const_check]
     fn try_rfold<B, F, R>(&mut self, init: B, mut f: F) -> R
     where
         Self: Sized,
@@ -304,7 +297,6 @@ pub trait DoubleEndedIterator: Iterator {
     #[doc(alias = "foldr")]
     #[inline]
     #[stable(feature = "iter_rfold", since = "1.27.0")]
-    #[rustc_do_not_const_check]
     fn rfold<B, F>(mut self, init: B, mut f: F) -> B
     where
         Self: Sized,
@@ -360,7 +352,6 @@ pub trait DoubleEndedIterator: Iterator {
     /// ```
     #[inline]
     #[stable(feature = "iter_rfind", since = "1.27.0")]
-    #[rustc_do_not_const_check]
     fn rfind<P>(&mut self, predicate: P) -> Option<Self::Item>
     where
         Self: Sized,
diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs
index 36cf7defd6d..f3d1e45f4fb 100644
--- a/library/core/src/iter/traits/iterator.rs
+++ b/library/core/src/iter/traits/iterator.rs
@@ -1,6 +1,5 @@
 use crate::array;
 use crate::cmp::{self, Ordering};
-use crate::marker::Destruct;
 use crate::num::NonZeroUsize;
 use crate::ops::{ChangeOutputType, ControlFlow, FromResidual, Residual, Try};
 
@@ -340,10 +339,8 @@ pub trait Iterator {
     /// ```
     #[inline]
     #[unstable(feature = "iter_advance_by", reason = "recently added", issue = "77404")]
-    fn advance_by(&mut self, n: usize) -> Result<(), NonZeroUsize>
-    where
-        Self::Item: ~const Destruct,
-    {
+    #[rustc_do_not_const_check]
+    fn advance_by(&mut self, n: usize) -> Result<(), NonZeroUsize> {
         for i in 0..n {
             if self.next().is_none() {
                 // SAFETY: `i` is always less than `n`.
@@ -394,10 +391,8 @@ pub trait Iterator {
     /// ```
     #[inline]
     #[stable(feature = "rust1", since = "1.0.0")]
-    fn nth(&mut self, n: usize) -> Option<Self::Item>
-    where
-        Self::Item: ~const Destruct,
-    {
+    #[rustc_do_not_const_check]
+    fn nth(&mut self, n: usize) -> Option<Self::Item> {
         self.advance_by(n).ok()?;
         self.next()
     }
diff --git a/library/core/src/iter/traits/marker.rs b/library/core/src/iter/traits/marker.rs
index c8f60defff7..af02848233d 100644
--- a/library/core/src/iter/traits/marker.rs
+++ b/library/core/src/iter/traits/marker.rs
@@ -86,5 +86,4 @@ pub unsafe trait InPlaceIterable: Iterator {}
 /// for details. Consumers are free to rely on the invariants in unsafe code.
 #[unstable(feature = "trusted_step", issue = "85731")]
 #[rustc_specialization_trait]
-#[const_trait]
-pub unsafe trait TrustedStep: ~const Step {}
+pub unsafe trait TrustedStep: Step {}
diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs
index a6b9acb576e..4fd5a4bfc65 100644
--- a/library/core/src/lib.rs
+++ b/library/core/src/lib.rs
@@ -123,11 +123,9 @@
 #![feature(const_index_range_slice_index)]
 #![feature(const_inherent_unchecked_arith)]
 #![feature(const_int_unchecked_arith)]
-#![feature(const_intoiterator_identity)]
 #![feature(const_intrinsic_forget)]
 #![feature(const_ipv4)]
 #![feature(const_ipv6)]
-#![feature(const_iter)]
 #![feature(const_likely)]
 #![feature(const_maybe_uninit_uninit_array)]
 #![feature(const_maybe_uninit_as_mut_ptr)]
diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs
index 74e9c55396d..62064f1aa6c 100644
--- a/library/core/src/marker.rs
+++ b/library/core/src/marker.rs
@@ -879,7 +879,7 @@ pub trait Tuple {}
 #[unstable(feature = "pointer_like_trait", issue = "none")]
 #[lang = "pointer_like"]
 #[rustc_on_unimplemented(
-    message = "`{Self}` needs to have the same alignment and size as a pointer",
+    message = "`{Self}` needs to have the same ABI as a pointer",
     label = "`{Self}` needs to be a pointer-like type"
 )]
 pub trait PointerLike {}
diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs
index 3f491836551..9c6d48675a6 100644
--- a/library/core/src/mem/maybe_uninit.rs
+++ b/library/core/src/mem/maybe_uninit.rs
@@ -1241,13 +1241,9 @@ impl<T> MaybeUninit<T> {
     /// ```
     #[unstable(feature = "maybe_uninit_as_bytes", issue = "93092")]
     pub fn slice_as_bytes(this: &[MaybeUninit<T>]) -> &[MaybeUninit<u8>] {
+        let bytes = mem::size_of_val(this);
         // SAFETY: MaybeUninit<u8> is always valid, even for padding bytes
-        unsafe {
-            slice::from_raw_parts(
-                this.as_ptr() as *const MaybeUninit<u8>,
-                this.len() * mem::size_of::<T>(),
-            )
-        }
+        unsafe { slice::from_raw_parts(this.as_ptr() as *const MaybeUninit<u8>, bytes) }
     }
 
     /// Returns the contents of this mutable slice of `MaybeUninit` as a mutable slice of
@@ -1274,13 +1270,9 @@ impl<T> MaybeUninit<T> {
     /// ```
     #[unstable(feature = "maybe_uninit_as_bytes", issue = "93092")]
     pub fn slice_as_bytes_mut(this: &mut [MaybeUninit<T>]) -> &mut [MaybeUninit<u8>] {
+        let bytes = mem::size_of_val(this);
         // SAFETY: MaybeUninit<u8> is always valid, even for padding bytes
-        unsafe {
-            slice::from_raw_parts_mut(
-                this.as_mut_ptr() as *mut MaybeUninit<u8>,
-                this.len() * mem::size_of::<T>(),
-            )
-        }
+        unsafe { slice::from_raw_parts_mut(this.as_mut_ptr() as *mut MaybeUninit<u8>, bytes) }
     }
 }
 
diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs
index bf8339335dd..51e6947a9c2 100644
--- a/library/core/src/primitive_docs.rs
+++ b/library/core/src/primitive_docs.rs
@@ -1362,6 +1362,7 @@ mod prim_usize {}
 /// * [`Hash`]
 /// * [`ToSocketAddrs`]
 /// * [`Send`] \(`&T` references also require <code>T: [Sync]</code>)
+/// * [`Sync`]
 ///
 /// [`std::fmt`]: fmt
 /// [`Hash`]: hash::Hash
diff --git a/library/core/tests/iter/consts.rs b/library/core/tests/iter/consts.rs
deleted file mode 100644
index d56687e48c9..00000000000
--- a/library/core/tests/iter/consts.rs
+++ /dev/null
@@ -1,36 +0,0 @@
-#[test]
-fn const_manual_iter() {
-    struct S(bool);
-
-    impl const Iterator for S {
-        type Item = ();
-
-        fn next(&mut self) -> Option<Self::Item> {
-            if self.0 == false {
-                self.0 = true;
-                Some(())
-            } else {
-                None
-            }
-        }
-    }
-    const {
-        let mut val = S(false);
-        assert!(val.next().is_some());
-        assert!(val.next().is_none());
-        assert!(val.next().is_none());
-    }
-}
-
-#[test]
-fn const_range() {
-    const {
-        let mut arr = [0; 3];
-        for i in 0..arr.len() {
-            arr[i] = i;
-        }
-        assert!(arr[0] == 0);
-        assert!(arr[1] == 1);
-        assert!(arr[2] == 2);
-    }
-}
diff --git a/library/core/tests/iter/mod.rs b/library/core/tests/iter/mod.rs
index cbb18e79e2d..770b6f7601f 100644
--- a/library/core/tests/iter/mod.rs
+++ b/library/core/tests/iter/mod.rs
@@ -20,8 +20,6 @@ mod range;
 mod sources;
 mod traits;
 
-mod consts;
-
 use core::cell::Cell;
 use core::convert::TryFrom;
 use core::iter::*;
diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs
index 344f0b61754..6cdafa411d0 100644
--- a/library/core/tests/lib.rs
+++ b/library/core/tests/lib.rs
@@ -12,11 +12,8 @@
 #![feature(const_caller_location)]
 #![feature(const_cell_into_inner)]
 #![feature(const_convert)]
-#![feature(const_for)]
 #![feature(const_hash)]
 #![feature(const_heap)]
-#![feature(const_intoiterator_identity)]
-#![feature(const_iter)]
 #![feature(const_maybe_uninit_as_mut_ptr)]
 #![feature(const_maybe_uninit_assume_init_read)]
 #![feature(const_nonnull_new)]
diff --git a/library/std/src/collections/mod.rs b/library/std/src/collections/mod.rs
index ae2baba09e6..575f56ff4df 100644
--- a/library/std/src/collections/mod.rs
+++ b/library/std/src/collections/mod.rs
@@ -416,8 +416,10 @@ pub use alloc_crate::collections::{BTreeMap, BTreeSet, BinaryHeap};
 pub use alloc_crate::collections::{LinkedList, VecDeque};
 
 #[stable(feature = "rust1", since = "1.0.0")]
+#[doc(inline)]
 pub use self::hash_map::HashMap;
 #[stable(feature = "rust1", since = "1.0.0")]
+#[doc(inline)]
 pub use self::hash_set::HashSet;
 
 #[stable(feature = "try_reserve", since = "1.57.0")]
diff --git a/library/std/src/primitive_docs.rs b/library/std/src/primitive_docs.rs
index bf8339335dd..51e6947a9c2 100644
--- a/library/std/src/primitive_docs.rs
+++ b/library/std/src/primitive_docs.rs
@@ -1362,6 +1362,7 @@ mod prim_usize {}
 /// * [`Hash`]
 /// * [`ToSocketAddrs`]
 /// * [`Send`] \(`&T` references also require <code>T: [Sync]</code>)
+/// * [`Sync`]
 ///
 /// [`std::fmt`]: fmt
 /// [`Hash`]: hash::Hash
diff --git a/src/bootstrap/CHANGELOG.md b/src/bootstrap/CHANGELOG.md
index 654e03d0c3c..74dd22df9e0 100644
--- a/src/bootstrap/CHANGELOG.md
+++ b/src/bootstrap/CHANGELOG.md
@@ -17,6 +17,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 - `remote-test-server`'s `remote` argument has been removed in favor of the `--bind` flag. Use `--bind 0.0.0.0:12345` to replicate the behavior of the `remote` argument.
 - `x.py fmt` now formats only files modified between the merge-base of HEAD and the last commit in the master branch of the rust-lang repository and the current working directory. To restore old behaviour, use `x.py fmt .`. The check mode is not affected by this change. [#105702](https://github.com/rust-lang/rust/pull/105702)
 - The `llvm.version-check` config option has been removed. Older versions were never supported. If you still need to support older versions (e.g. you are applying custom patches), patch `check_llvm_version` in bootstrap to change the minimum version. [#108619](https://github.com/rust-lang/rust/pull/108619)
+- The `rust.ignore-git` option has been renamed to `rust.omit-git-hash`. [#110059](https://github.com/rust-lang/rust/pull/110059)
 
 ### Non-breaking changes
 
diff --git a/src/bootstrap/channel.rs b/src/bootstrap/channel.rs
index eae81b9fc69..390047f6fdc 100644
--- a/src/bootstrap/channel.rs
+++ b/src/bootstrap/channel.rs
@@ -19,7 +19,7 @@ pub enum GitInfo {
     #[default]
     Absent,
     /// This is a git repository.
-    /// If the info should be used (`ignore_git` is false), this will be
+    /// If the info should be used (`omit_git_hash` is false), this will be
     /// `Some`, otherwise it will be `None`.
     Present(Option<Info>),
     /// This is not a git repostory, but the info can be fetched from the
@@ -35,7 +35,7 @@ pub struct Info {
 }
 
 impl GitInfo {
-    pub fn new(ignore_git: bool, dir: &Path) -> GitInfo {
+    pub fn new(omit_git_hash: bool, dir: &Path) -> GitInfo {
         // See if this even begins to look like a git dir
         if !dir.join(".git").exists() {
             match read_commit_info_file(dir) {
@@ -52,7 +52,7 @@ impl GitInfo {
 
         // If we're ignoring the git info, we don't actually need to collect it, just make sure this
         // was a git repo in the first place.
-        if ignore_git {
+        if omit_git_hash {
             return GitInfo::Present(None);
         }
 
diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs
index f038aceb34c..dd65dc91c0c 100644
--- a/src/bootstrap/config.rs
+++ b/src/bootstrap/config.rs
@@ -77,7 +77,7 @@ pub struct Config {
     pub tools: Option<HashSet<String>>,
     pub sanitizers: bool,
     pub profiler: bool,
-    pub ignore_git: bool,
+    pub omit_git_hash: bool,
     pub exclude: Vec<TaskPath>,
     pub include_default_paths: bool,
     pub rustc_error_format: Option<String>,
@@ -764,7 +764,7 @@ define_config! {
         verbose_tests: Option<bool> = "verbose-tests",
         optimize_tests: Option<bool> = "optimize-tests",
         codegen_tests: Option<bool> = "codegen-tests",
-        ignore_git: Option<bool> = "ignore-git",
+        omit_git_hash: Option<bool> = "omit-git-hash",
         dist_src: Option<bool> = "dist-src",
         save_toolstates: Option<String> = "save-toolstates",
         codegen_backends: Option<Vec<String>> = "codegen-backends",
@@ -1097,7 +1097,7 @@ impl Config {
         let mut debuginfo_level_tools = None;
         let mut debuginfo_level_tests = None;
         let mut optimize = None;
-        let mut ignore_git = None;
+        let mut omit_git_hash = None;
 
         if let Some(rust) = toml.rust {
             debug = rust.debug;
@@ -1118,7 +1118,7 @@ impl Config {
                 .map(|v| v.expect("invalid value for rust.split_debuginfo"))
                 .unwrap_or(SplitDebuginfo::default_for_platform(&config.build.triple));
             optimize = rust.optimize;
-            ignore_git = rust.ignore_git;
+            omit_git_hash = rust.omit_git_hash;
             config.rust_new_symbol_mangling = rust.new_symbol_mangling;
             set(&mut config.rust_optimize_tests, rust.optimize_tests);
             set(&mut config.codegen_tests, rust.codegen_tests);
@@ -1175,8 +1175,8 @@ impl Config {
 
         // rust_info must be set before is_ci_llvm_available() is called.
         let default = config.channel == "dev";
-        config.ignore_git = ignore_git.unwrap_or(default);
-        config.rust_info = GitInfo::new(config.ignore_git, &config.src);
+        config.omit_git_hash = omit_git_hash.unwrap_or(default);
+        config.rust_info = GitInfo::new(config.omit_git_hash, &config.src);
 
         if let Some(llvm) = toml.llvm {
             match llvm.ccache {
diff --git a/src/bootstrap/defaults/config.codegen.toml b/src/bootstrap/defaults/config.codegen.toml
index 20b2699c761..113df88d7c3 100644
--- a/src/bootstrap/defaults/config.codegen.toml
+++ b/src/bootstrap/defaults/config.codegen.toml
@@ -9,6 +9,8 @@ compiler-docs = true
 assertions = true
 # enable warnings during the llvm compilation
 enable-warnings = true
+# build llvm from source
+download-ci-llvm = false
 
 [rust]
 # This enables `RUSTC_LOG=debug`, avoiding confusing situations
diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs
index 2ce54d9a3b4..8ce220c8647 100644
--- a/src/bootstrap/dist.rs
+++ b/src/bootstrap/dist.rs
@@ -1488,7 +1488,7 @@ impl Step for Extended {
 
         let xform = |p: &Path| {
             let mut contents = t!(fs::read_to_string(p));
-            for tool in &["rust-demangler", "miri"] {
+            for tool in &["rust-demangler", "miri", "rust-docs"] {
                 if !built_tools.contains(tool) {
                     contents = filter(&contents, tool);
                 }
@@ -1585,11 +1585,10 @@ impl Step for Extended {
             prepare("rustc");
             prepare("cargo");
             prepare("rust-analysis");
-            prepare("rust-docs");
             prepare("rust-std");
             prepare("clippy");
             prepare("rust-analyzer");
-            for tool in &["rust-demangler", "miri"] {
+            for tool in &["rust-docs", "rust-demangler", "miri"] {
                 if built_tools.contains(tool) {
                     prepare(tool);
                 }
@@ -1624,23 +1623,25 @@ impl Step for Extended {
                     .arg("-out")
                     .arg(exe.join("RustcGroup.wxs")),
             );
-            builder.run(
-                Command::new(&heat)
-                    .current_dir(&exe)
-                    .arg("dir")
-                    .arg("rust-docs")
-                    .args(&heat_flags)
-                    .arg("-cg")
-                    .arg("DocsGroup")
-                    .arg("-dr")
-                    .arg("Docs")
-                    .arg("-var")
-                    .arg("var.DocsDir")
-                    .arg("-out")
-                    .arg(exe.join("DocsGroup.wxs"))
-                    .arg("-t")
-                    .arg(etc.join("msi/squash-components.xsl")),
-            );
+            if built_tools.contains("rust-docs") {
+                builder.run(
+                    Command::new(&heat)
+                        .current_dir(&exe)
+                        .arg("dir")
+                        .arg("rust-docs")
+                        .args(&heat_flags)
+                        .arg("-cg")
+                        .arg("DocsGroup")
+                        .arg("-dr")
+                        .arg("Docs")
+                        .arg("-var")
+                        .arg("var.DocsDir")
+                        .arg("-out")
+                        .arg(exe.join("DocsGroup.wxs"))
+                        .arg("-t")
+                        .arg(etc.join("msi/squash-components.xsl")),
+                );
+            }
             builder.run(
                 Command::new(&heat)
                     .current_dir(&exe)
@@ -1787,7 +1788,6 @@ impl Step for Extended {
                 cmd.current_dir(&exe)
                     .arg("-nologo")
                     .arg("-dRustcDir=rustc")
-                    .arg("-dDocsDir=rust-docs")
                     .arg("-dCargoDir=cargo")
                     .arg("-dStdDir=rust-std")
                     .arg("-dAnalysisDir=rust-analysis")
@@ -1799,6 +1799,9 @@ impl Step for Extended {
                     .arg(&input);
                 add_env(builder, &mut cmd, target);
 
+                if built_tools.contains("rust-docs") {
+                    cmd.arg("-dDocsDir=rust-docs");
+                }
                 if built_tools.contains("rust-demangler") {
                     cmd.arg("-dRustDemanglerDir=rust-demangler");
                 }
@@ -1817,7 +1820,9 @@ impl Step for Extended {
             candle(&etc.join("msi/ui.wxs"));
             candle(&etc.join("msi/rustwelcomedlg.wxs"));
             candle("RustcGroup.wxs".as_ref());
-            candle("DocsGroup.wxs".as_ref());
+            if built_tools.contains("rust-docs") {
+                candle("DocsGroup.wxs".as_ref());
+            }
             candle("CargoGroup.wxs".as_ref());
             candle("StdGroup.wxs".as_ref());
             candle("ClippyGroup.wxs".as_ref());
@@ -1854,7 +1859,6 @@ impl Step for Extended {
                 .arg("ui.wixobj")
                 .arg("rustwelcomedlg.wixobj")
                 .arg("RustcGroup.wixobj")
-                .arg("DocsGroup.wixobj")
                 .arg("CargoGroup.wixobj")
                 .arg("StdGroup.wixobj")
                 .arg("AnalysisGroup.wixobj")
@@ -1870,6 +1874,9 @@ impl Step for Extended {
             if built_tools.contains("rust-demangler") {
                 cmd.arg("RustDemanglerGroup.wixobj");
             }
+            if built_tools.contains("rust-docs") {
+                cmd.arg("DocsGroup.wixobj");
+            }
 
             if target.ends_with("windows-gnu") {
                 cmd.arg("GccGroup.wixobj");
diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs
index e3f3ab5243e..5ee18cf6411 100644
--- a/src/bootstrap/lib.rs
+++ b/src/bootstrap/lib.rs
@@ -358,14 +358,14 @@ impl Build {
         #[cfg(not(unix))]
         let is_sudo = false;
 
-        let ignore_git = config.ignore_git;
-        let rust_info = channel::GitInfo::new(ignore_git, &src);
-        let cargo_info = channel::GitInfo::new(ignore_git, &src.join("src/tools/cargo"));
+        let omit_git_hash = config.omit_git_hash;
+        let rust_info = channel::GitInfo::new(omit_git_hash, &src);
+        let cargo_info = channel::GitInfo::new(omit_git_hash, &src.join("src/tools/cargo"));
         let rust_analyzer_info =
-            channel::GitInfo::new(ignore_git, &src.join("src/tools/rust-analyzer"));
-        let clippy_info = channel::GitInfo::new(ignore_git, &src.join("src/tools/clippy"));
-        let miri_info = channel::GitInfo::new(ignore_git, &src.join("src/tools/miri"));
-        let rustfmt_info = channel::GitInfo::new(ignore_git, &src.join("src/tools/rustfmt"));
+            channel::GitInfo::new(omit_git_hash, &src.join("src/tools/rust-analyzer"));
+        let clippy_info = channel::GitInfo::new(omit_git_hash, &src.join("src/tools/clippy"));
+        let miri_info = channel::GitInfo::new(omit_git_hash, &src.join("src/tools/miri"));
+        let rustfmt_info = channel::GitInfo::new(omit_git_hash, &src.join("src/tools/rustfmt"));
 
         // we always try to use git for LLVM builds
         let in_tree_llvm_info = channel::GitInfo::new(false, &src.join("src/llvm-project"));
@@ -1233,7 +1233,7 @@ impl Build {
         match &self.config.channel[..] {
             "stable" => num.to_string(),
             "beta" => {
-                if self.rust_info().is_managed_git_subrepository() && !self.config.ignore_git {
+                if self.rust_info().is_managed_git_subrepository() && !self.config.omit_git_hash {
                     format!("{}-beta.{}", num, self.beta_prerelease_version())
                 } else {
                     format!("{}-beta", num)
diff --git a/src/bootstrap/tool.rs b/src/bootstrap/tool.rs
index 3c9a154da9a..6a687a7903e 100644
--- a/src/bootstrap/tool.rs
+++ b/src/bootstrap/tool.rs
@@ -320,7 +320,7 @@ pub fn prepare_tool_cargo(
     cargo.env("CFG_RELEASE_NUM", &builder.version);
     cargo.env("DOC_RUST_LANG_ORG_CHANNEL", builder.doc_rust_lang_org_channel());
 
-    let info = GitInfo::new(builder.config.ignore_git, &dir);
+    let info = GitInfo::new(builder.config.omit_git_hash, &dir);
     if let Some(sha) = info.sha() {
         cargo.env("CFG_COMMIT_HASH", sha);
     }
diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-distcheck/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-distcheck/Dockerfile
index 7e640c49f01..2217e6ee704 100644
--- a/src/ci/docker/host-x86_64/x86_64-gnu-distcheck/Dockerfile
+++ b/src/ci/docker/host-x86_64/x86_64-gnu-distcheck/Dockerfile
@@ -24,6 +24,6 @@ RUN sh /scripts/sccache.sh
 # We are disabling CI LLVM since distcheck is an offline build.
 ENV NO_DOWNLOAD_CI_LLVM 1
 
-ENV RUST_CONFIGURE_ARGS --build=x86_64-unknown-linux-gnu --set rust.ignore-git=false
+ENV RUST_CONFIGURE_ARGS --build=x86_64-unknown-linux-gnu --set rust.omit-git-hash=false
 ENV SCRIPT python3 ../x.py --stage 2 test distcheck
 ENV DIST_SRC 1
diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md
index 5c18a38ddab..67fe2a610c2 100644
--- a/src/doc/rustc/src/platform-support.md
+++ b/src/doc/rustc/src/platform-support.md
@@ -257,6 +257,7 @@ target | std | host | notes
 `bpfel-unknown-none` | * |  | BPF (little endian)
 `hexagon-unknown-linux-musl` | ? |  |
 `i386-apple-ios` | ✓ |  | 32-bit x86 iOS
+[`i586-pc-nto-qnx700`](platform-support/nto-qnx.md) | * |  | 32-bit x86 QNX Neutrino 7.0 RTOS |
 `i686-apple-darwin` | ✓ | ✓ | 32-bit macOS (10.7+, Lion+)
 `i686-pc-windows-msvc` | * |  | 32-bit Windows XP support
 `i686-unknown-haiku` | ✓ | ✓ | 32-bit Haiku
diff --git a/src/doc/rustc/src/platform-support/nto-qnx.md b/src/doc/rustc/src/platform-support/nto-qnx.md
index 38198fe6c3a..0d815c9b598 100644
--- a/src/doc/rustc/src/platform-support/nto-qnx.md
+++ b/src/doc/rustc/src/platform-support/nto-qnx.md
@@ -16,10 +16,18 @@ and [Blackberry QNX][BlackBerry].
 
 ## Requirements
 
-Currently, only cross-compilation for QNX Neutrino on AArch64 and x86_64 are supported (little endian).
+Currently, the following QNX Neutrino versions and compilation targets are supported:
+
+| QNX Neutrino Version | Target Architecture | Full support | `no_std` support |
+|----------------------|---------------------|:------------:|:----------------:|
+| 7.1 | AArch64 | ✓ | ✓ |
+| 7.1 | x86_64  | ✓ | ✓ |
+| 7.0 | x86     |   | ✓ |
+
 Adding other architectures that are supported by QNX Neutrino is possible.
 
-The standard library, including `core` and `alloc` (with default allocator) are supported.
+In the table above, 'full support' indicates support for building Rust applications with the full standard library.
+'`no_std` support' indicates that only `core` and `alloc` are available.
 
 For building or using the Rust toolchain for QNX Neutrino, the
 [QNX Software Development Platform (SDP)](https://blackberry.qnx.com/en/products/foundation-software/qnx-software-development-platform)
@@ -70,7 +78,7 @@ fn panic(_panic: &PanicInfo<'_>) -> ! {
 pub extern "C" fn rust_eh_personality() {}
 ```
 
-The QNX Neutrino support of Rust has been tested with QNX Neutrino 7.1.
+The QNX Neutrino support of Rust has been tested with QNX Neutrino 7.0 and 7.1.
 
 There are no further known requirements.
 
@@ -80,6 +88,7 @@ For conditional compilation, following QNX Neutrino specific attributes are defi
 
 - `target_os` = `"nto"`
 - `target_env` = `"nto71"` (for QNX Neutrino 7.1)
+- `target_env` = `"nto70"` (for QNX Neutrino 7.0)
 
 ## Building the target
 
diff --git a/src/doc/unstable-book/src/language-features/lang-items.md b/src/doc/unstable-book/src/language-features/lang-items.md
index 39238dffa10..6adb3506e64 100644
--- a/src/doc/unstable-book/src/language-features/lang-items.md
+++ b/src/doc/unstable-book/src/language-features/lang-items.md
@@ -16,18 +16,26 @@ and one for deallocation. A freestanding program that uses the `Box`
 sugar for dynamic allocations via `malloc` and `free`:
 
 ```rust,ignore (libc-is-finicky)
-#![feature(lang_items, box_syntax, start, libc, core_intrinsics, rustc_private)]
+#![feature(lang_items, start, libc, core_intrinsics, rustc_private, rustc_attrs)]
 #![no_std]
 use core::intrinsics;
 use core::panic::PanicInfo;
+use core::ptr::NonNull;
 
 extern crate libc;
 
-struct Unique<T>(*mut T);
+struct Unique<T>(NonNull<T>);
 
 #[lang = "owned_box"]
 pub struct Box<T>(Unique<T>);
 
+impl<T> Box<T> {
+    pub fn new(x: T) -> Self {
+        #[rustc_box]
+        Box::new(x)
+    }
+}
+
 #[lang = "exchange_malloc"]
 unsafe fn allocate(size: usize, _align: usize) -> *mut u8 {
     let p = libc::malloc(size as libc::size_t) as *mut u8;
@@ -47,13 +55,13 @@ unsafe fn box_free<T: ?Sized>(ptr: *mut T) {
 
 #[start]
 fn main(_argc: isize, _argv: *const *const u8) -> isize {
-    let _x = box 1;
+    let _x = Box::new(1);
 
     0
 }
 
 #[lang = "eh_personality"] extern fn rust_eh_personality() {}
-#[lang = "panic_impl"] extern fn rust_begin_panic(info: &PanicInfo) -> ! { unsafe { intrinsics::abort() } }
+#[lang = "panic_impl"] extern fn rust_begin_panic(_info: &PanicInfo) -> ! { intrinsics::abort() }
 #[no_mangle] pub extern fn rust_eh_register_frames () {}
 #[no_mangle] pub extern fn rust_eh_unregister_frames () {}
 ```
diff --git a/src/doc/unstable-book/src/language-features/plugin.md b/src/doc/unstable-book/src/language-features/plugin.md
index dfbb468d4df..1fade6ce95b 100644
--- a/src/doc/unstable-book/src/language-features/plugin.md
+++ b/src/doc/unstable-book/src/language-features/plugin.md
@@ -37,7 +37,7 @@ additional checks for code style, safety, etc. Now let's write a plugin
 that warns about any item named `lintme`.
 
 ```rust,ignore (requires-stage-2)
-#![feature(box_syntax, rustc_private)]
+#![feature(rustc_private)]
 
 extern crate rustc_ast;
 
@@ -68,7 +68,7 @@ impl EarlyLintPass for Pass {
 #[no_mangle]
 fn __rustc_plugin_registrar(reg: &mut Registry) {
     reg.lint_store.register_lints(&[&TEST_LINT]);
-    reg.lint_store.register_early_pass(|| box Pass);
+    reg.lint_store.register_early_pass(|| Box::new(Pass));
 }
 ```
 
diff --git a/src/etc/installer/msi/rust.wxs b/src/etc/installer/msi/rust.wxs
index 0aa0784e544..9f4e4fd0611 100644
--- a/src/etc/installer/msi/rust.wxs
+++ b/src/etc/installer/msi/rust.wxs
@@ -167,7 +167,9 @@
                     <?if $(env.CFG_MINGW)="1" ?>
                         <Directory Id="Gcc" Name="." />
                     <?endif?>
+                    <!-- tool-rust-docs-start -->
                     <Directory Id="Docs" Name="." />
+                    <!-- tool-rust-docs-end -->
                     <Directory Id="Cargo" Name="." />
                     <Directory Id="Std" Name="." />
                 </Directory>
@@ -209,6 +211,7 @@
                         <RegistryValue Root="HKMU" Key="$(var.BaseRegKey)" Name="RustShell" Type="integer" Value="1" KeyPath="yes" />
                         <RemoveFolder Id="ApplicationProgramsFolder1" On="uninstall" />
                     </Component>
+                    <!-- tool-rust-docs-start -->
                     <Component Id="DocIndexShortcut" Guid="*">
                         <Shortcut Id="RustDocs"
                                   Name="$(var.ProductName) Documentation"
@@ -217,6 +220,7 @@
                         <RegistryValue Root="HKMU" Key="$(var.BaseRegKey)" Name="RustDocs" Type="integer" Value="1" KeyPath="yes" />
                         <RemoveFolder Id="ApplicationProgramsFolder2" On="uninstall" />
                     </Component>
+                    <!-- tool-rust-docs-end -->
                 </Directory>
             </Directory>
 
@@ -256,6 +260,7 @@
                      <ComponentGroupRef Id="GccGroup" />
             </Feature>
         <?endif?>
+        <!-- tool-rust-docs-start -->
         <Feature Id="Docs"
                  Title="HTML documentation"
                  Display="5"
@@ -264,6 +269,7 @@
                  <ComponentGroupRef Id="DocsGroup" />
                  <ComponentRef Id="DocIndexShortcut" />
         </Feature>
+        <!-- tool-rust-docs-end -->
         <Feature Id="Path"
                  Title="Add to PATH"
                  Description="Add Rust to PATH environment variable"
diff --git a/src/etc/installer/pkg/Distribution.xml b/src/etc/installer/pkg/Distribution.xml
index 64f6bab9bb5..1643fc8364b 100644
--- a/src/etc/installer/pkg/Distribution.xml
+++ b/src/etc/installer/pkg/Distribution.xml
@@ -15,7 +15,9 @@
       <line choice="rustc"/>
       <line choice="rust-std"/>
       <line choice="cargo"/>
+      <!-- tool-rust-docs-start -->
       <line choice="rust-docs"/>
+      <!-- tool-rust-docs-end -->
       </line>
       <line choice="uninstall" />
     </choices-outline>
@@ -55,15 +57,19 @@
         >
         <pkg-ref id="org.rust-lang.rust-std"/>
     </choice>
+    <!-- tool-rust-docs-start -->
     <choice id="rust-docs" visible="true"
         title="Documentation" description="HTML documentation."
         selected="(!choices.uninstall.selected &amp;&amp; choices['rust-docs'].selected) || (choices.uninstall.selected &amp;&amp; choices.install.selected)"
         >
         <pkg-ref id="org.rust-lang.rust-docs"/>
     </choice>
+    <!-- tool-rust-docs-end -->
     <pkg-ref id="org.rust-lang.rustc" version="0" onConclusion="none">rustc.pkg</pkg-ref>
     <pkg-ref id="org.rust-lang.cargo" version="0" onConclusion="none">cargo.pkg</pkg-ref>
+    <!-- tool-rust-docs-start -->
     <pkg-ref id="org.rust-lang.rust-docs" version="0" onConclusion="none">rust-docs.pkg</pkg-ref>
+    <!-- tool-rust-docs-end -->
     <pkg-ref id="org.rust-lang.rust-std" version="0" onConclusion="none">rust-std.pkg</pkg-ref>
     <pkg-ref id="org.rust-lang.uninstall" version="0" onConclusion="none">uninstall.pkg</pkg-ref>
     <background file="rust-logo.png" mime-type="image/png"
diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs
index 768f8bb7bc8..9270d1c02e2 100644
--- a/src/librustdoc/clean/inline.rs
+++ b/src/librustdoc/clean/inline.rs
@@ -153,7 +153,6 @@ pub(crate) fn try_inline_glob(
             let reexports = cx
                 .tcx
                 .module_reexports(current_mod)
-                .unwrap_or_default()
                 .iter()
                 .filter_map(|child| child.res.opt_def_id())
                 .collect();
@@ -558,7 +557,7 @@ fn build_module_items(
     // If we're re-exporting a re-export it may actually re-export something in
     // two namespaces, so the target may be listed twice. Make sure we only
     // visit each node at most once.
-    for &item in cx.tcx.module_children(did).iter() {
+    for item in cx.tcx.module_children(did).iter() {
         if item.vis.is_public() {
             let res = item.res.expect_non_local();
             if let Some(def_id) = res.opt_def_id()
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 1c57cd7a946..7f150f38025 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -21,6 +21,7 @@ use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, LocalDefId, LOCAL_CRATE};
 use rustc_hir::PredicateOrigin;
 use rustc_hir_analysis::hir_ty_to_ty;
 use rustc_infer::infer::region_constraints::{Constraint, RegionConstraintData};
+use rustc_middle::metadata::Reexport;
 use rustc_middle::middle::resolve_bound_vars as rbv;
 use rustc_middle::ty::fold::TypeFolder;
 use rustc_middle::ty::InternalSubsts;
@@ -2056,141 +2057,44 @@ fn clean_bare_fn_ty<'tcx>(
     BareFunctionDecl { unsafety: bare_fn.unsafety, abi: bare_fn.abi, decl, generic_params }
 }
 
-/// Get DefId of of an item's user-visible parent.
-///
-/// "User-visible" should account for re-exporting and inlining, which is why this function isn't
-/// just `tcx.parent(def_id)`. If the provided `path` has more than one path element, the `DefId`
-/// of the second-to-last will be given.
-///
-/// ```text
-/// use crate::foo::Bar;
-///            ^^^ DefId of this item will be returned
-/// ```
-///
-/// If the provided path has only one item, `tcx.parent(def_id)` will be returned instead.
-fn get_path_parent_def_id(
-    tcx: TyCtxt<'_>,
-    def_id: DefId,
-    path: &hir::UsePath<'_>,
-) -> Option<DefId> {
-    if let [.., parent_segment, _] = &path.segments {
-        match parent_segment.res {
-            hir::def::Res::Def(_, parent_def_id) => Some(parent_def_id),
-            _ if parent_segment.ident.name == kw::Crate => {
-                // In case the "parent" is the crate, it'll give `Res::Err` so we need to
-                // circumvent it this way.
-                Some(tcx.parent(def_id))
-            }
-            _ => None,
-        }
-    } else {
-        // If the path doesn't have a parent, then the parent is the current module.
-        Some(tcx.parent(def_id))
-    }
-}
-
-/// This visitor is used to find an HIR Item based on its `use` path. This doesn't use the ordinary
-/// name resolver because it does not walk all the way through a chain of re-exports.
-pub(crate) struct OneLevelVisitor<'hir> {
-    map: rustc_middle::hir::map::Map<'hir>,
-    pub(crate) item: Option<&'hir hir::Item<'hir>>,
-    looking_for: Ident,
+pub(crate) fn reexport_chain<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    import_def_id: LocalDefId,
     target_def_id: LocalDefId,
-}
-
-impl<'hir> OneLevelVisitor<'hir> {
-    pub(crate) fn new(map: rustc_middle::hir::map::Map<'hir>, target_def_id: LocalDefId) -> Self {
-        Self { map, item: None, looking_for: Ident::empty(), target_def_id }
-    }
-
-    pub(crate) fn find_target(
-        &mut self,
-        tcx: TyCtxt<'_>,
-        def_id: DefId,
-        path: &hir::UsePath<'_>,
-    ) -> Option<&'hir hir::Item<'hir>> {
-        let parent_def_id = get_path_parent_def_id(tcx, def_id, path)?;
-        let parent = self.map.get_if_local(parent_def_id)?;
-
-        // We get the `Ident` we will be looking for into `item`.
-        self.looking_for = path.segments[path.segments.len() - 1].ident;
-        // We reset the `item`.
-        self.item = None;
-
-        match parent {
-            hir::Node::Item(parent_item) => {
-                hir::intravisit::walk_item(self, parent_item);
-            }
-            hir::Node::Crate(m) => {
-                hir::intravisit::walk_mod(
-                    self,
-                    m,
-                    tcx.local_def_id_to_hir_id(parent_def_id.as_local().unwrap()),
-                );
-            }
-            _ => return None,
-        }
-        self.item
-    }
-}
-
-impl<'hir> hir::intravisit::Visitor<'hir> for OneLevelVisitor<'hir> {
-    type NestedFilter = rustc_middle::hir::nested_filter::All;
-
-    fn nested_visit_map(&mut self) -> Self::Map {
-        self.map
-    }
-
-    fn visit_item(&mut self, item: &'hir hir::Item<'hir>) {
-        if self.item.is_none()
-            && item.ident == self.looking_for
-            && (matches!(item.kind, hir::ItemKind::Use(_, _))
-                || item.owner_id.def_id == self.target_def_id)
+) -> &'tcx [Reexport] {
+    for child in tcx.module_reexports(tcx.local_parent(import_def_id)) {
+        if child.res.opt_def_id() == Some(target_def_id.to_def_id())
+            && child.reexport_chain[0].id() == Some(import_def_id.to_def_id())
         {
-            self.item = Some(item);
+            return &child.reexport_chain;
         }
     }
+    &[]
 }
 
-/// Because a `Use` item directly links to the imported item, we need to manually go through each
-/// import one by one. To do so, we go to the parent item and look for the `Ident` into it. Then,
-/// if we found the "end item" (the imported one), we stop there because we don't need its
-/// documentation. Otherwise, we repeat the same operation until we find the "end item".
+/// Collect attributes from the whole import chain.
 fn get_all_import_attributes<'hir>(
-    mut item: &hir::Item<'hir>,
     cx: &mut DocContext<'hir>,
+    import_def_id: LocalDefId,
     target_def_id: LocalDefId,
     is_inline: bool,
-    mut prev_import: LocalDefId,
 ) -> Vec<(Cow<'hir, ast::Attribute>, Option<DefId>)> {
-    let mut attributes: Vec<(Cow<'hir, ast::Attribute>, Option<DefId>)> = Vec::new();
+    let mut attrs = Vec::new();
     let mut first = true;
-    let hir_map = cx.tcx.hir();
-    let mut visitor = OneLevelVisitor::new(hir_map, target_def_id);
-    let mut visited = FxHashSet::default();
-
-    // If the item is an import and has at least a path with two parts, we go into it.
-    while let hir::ItemKind::Use(path, _) = item.kind && visited.insert(item.hir_id()) {
-        let import_parent = cx.tcx.opt_local_parent(prev_import).map(|def_id| def_id.to_def_id());
+    for def_id in reexport_chain(cx.tcx, import_def_id, target_def_id)
+        .iter()
+        .flat_map(|reexport| reexport.id())
+    {
+        let import_attrs = inline::load_attrs(cx, def_id);
         if first {
             // This is the "original" reexport so we get all its attributes without filtering them.
-            attributes = hir_map.attrs(item.hir_id())
-                .iter()
-                .map(|attr| (Cow::Borrowed(attr), import_parent))
-                .collect::<Vec<_>>();
+            attrs = import_attrs.iter().map(|attr| (Cow::Borrowed(attr), Some(def_id))).collect();
             first = false;
         } else {
-            add_without_unwanted_attributes(&mut attributes, hir_map.attrs(item.hir_id()), is_inline, import_parent);
+            add_without_unwanted_attributes(&mut attrs, import_attrs, is_inline, Some(def_id));
         }
-
-        if let Some(i) = visitor.find_target(cx.tcx, item.owner_id.def_id.to_def_id(), path) {
-            item = i;
-        } else {
-            break;
-        }
-        prev_import = item.owner_id.def_id;
     }
-    attributes
+    attrs
 }
 
 fn filter_tokens_from_list(
@@ -2375,39 +2279,24 @@ fn clean_maybe_renamed_item<'tcx>(
             _ => unreachable!("not yet converted"),
         };
 
-        let attrs = if let Some(import_id) = import_id &&
-            let Some(hir::Node::Item(use_node)) = cx.tcx.hir().find_by_def_id(import_id)
-        {
+        let target_attrs = inline::load_attrs(cx, def_id);
+        let attrs = if let Some(import_id) = import_id {
             let is_inline = inline::load_attrs(cx, import_id.to_def_id())
                 .lists(sym::doc)
                 .get_word_attr(sym::inline)
                 .is_some();
-            // Then we get all the various imports' attributes.
-            let mut attrs = get_all_import_attributes(
-                use_node,
-                cx,
-                item.owner_id.def_id,
-                is_inline,
-                import_id,
-            );
-
-            add_without_unwanted_attributes(
-                &mut attrs,
-                inline::load_attrs(cx, def_id),
-                is_inline,
-                None
-            );
+            let mut attrs =
+                get_all_import_attributes(cx, import_id, item.owner_id.def_id, is_inline);
+            add_without_unwanted_attributes(&mut attrs, target_attrs, is_inline, None);
             attrs
         } else {
             // We only keep the item's attributes.
-            inline::load_attrs(cx, def_id).iter().map(|attr| (Cow::Borrowed(attr), None)).collect::<Vec<_>>()
+            target_attrs.iter().map(|attr| (Cow::Borrowed(attr), None)).collect()
         };
 
         let cfg = attrs.cfg(cx.tcx, &cx.cache.hidden_cfg);
-        let attrs = Attributes::from_ast_iter(attrs.iter().map(|(attr, did)| match attr {
-            Cow::Borrowed(attr) => (*attr, *did),
-            Cow::Owned(attr) => (attr, *did)
-        }), false);
+        let attrs =
+            Attributes::from_ast_iter(attrs.iter().map(|(attr, did)| (&**attr, *did)), false);
 
         let mut item =
             Item::from_def_id_and_attrs_and_parts(def_id, Some(name), kind, Box::new(attrs), cfg);
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index 02b358e863b..7a2449cbe9a 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -136,10 +136,6 @@ impl Buffer {
         self.into_inner()
     }
 
-    pub(crate) fn is_for_html(&self) -> bool {
-        self.for_html
-    }
-
     pub(crate) fn reserve(&mut self, additional: usize) {
         self.buffer.reserve(additional)
     }
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index d75d03071f8..1e3cd266850 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -50,6 +50,7 @@ use std::string::ToString;
 use askama::Template;
 use rustc_ast_pretty::pprust;
 use rustc_attr::{ConstStability, Deprecation, StabilityLevel};
+use rustc_data_structures::captures::Captures;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_hir::def_id::{DefId, DefIdSet};
 use rustc_hir::Mutability;
@@ -69,7 +70,7 @@ use crate::formats::item_type::ItemType;
 use crate::formats::{AssocItemRender, Impl, RenderMode};
 use crate::html::escape::Escape;
 use crate::html::format::{
-    href, join_with_double_colon, print_abi_with_space, print_constness_with_space,
+    display_fn, href, join_with_double_colon, print_abi_with_space, print_constness_with_space,
     print_default_space, print_generic_bounds, print_where_clause, visibility_print_with_space,
     Buffer, Ending, HrefError, PrintWithSpace,
 };
@@ -408,128 +409,134 @@ fn scrape_examples_help(shared: &SharedContext<'_>) -> String {
     )
 }
 
-fn document(
-    w: &mut Buffer,
-    cx: &mut Context<'_>,
-    item: &clean::Item,
-    parent: Option<&clean::Item>,
+fn document<'a, 'cx: 'a>(
+    cx: &'a mut Context<'cx>,
+    item: &'a clean::Item,
+    parent: Option<&'a clean::Item>,
     heading_offset: HeadingOffset,
-) {
+) -> impl fmt::Display + 'a + Captures<'cx> {
     if let Some(ref name) = item.name {
         info!("Documenting {}", name);
     }
-    document_item_info(cx, item, parent).render_into(w).unwrap();
-    if parent.is_none() {
-        document_full_collapsible(w, item, cx, heading_offset);
-    } else {
-        document_full(w, item, cx, heading_offset);
-    }
+
+    display_fn(move |f| {
+        document_item_info(cx, item, parent).render_into(f).unwrap();
+        if parent.is_none() {
+            write!(f, "{}", document_full_collapsible(item, cx, heading_offset))?;
+        } else {
+            write!(f, "{}", document_full(item, cx, heading_offset))?;
+        }
+        Ok(())
+    })
 }
 
 /// Render md_text as markdown.
-fn render_markdown(
-    w: &mut Buffer,
-    cx: &mut Context<'_>,
-    md_text: &str,
+fn render_markdown<'a, 'cx: 'a>(
+    cx: &'a mut Context<'cx>,
+    md_text: &'a str,
     links: Vec<RenderedLink>,
     heading_offset: HeadingOffset,
-) {
-    write!(
-        w,
-        "<div class=\"docblock\">{}</div>",
-        Markdown {
-            content: md_text,
-            links: &links,
-            ids: &mut cx.id_map,
-            error_codes: cx.shared.codes,
-            edition: cx.shared.edition(),
-            playground: &cx.shared.playground,
-            heading_offset,
-        }
-        .into_string()
-    )
+) -> impl fmt::Display + 'a + Captures<'cx> {
+    display_fn(move |f| {
+        write!(
+            f,
+            "<div class=\"docblock\">{}</div>",
+            Markdown {
+                content: md_text,
+                links: &links,
+                ids: &mut cx.id_map,
+                error_codes: cx.shared.codes,
+                edition: cx.shared.edition(),
+                playground: &cx.shared.playground,
+                heading_offset,
+            }
+            .into_string()
+        )
+    })
 }
 
 /// Writes a documentation block containing only the first paragraph of the documentation. If the
 /// docs are longer, a "Read more" link is appended to the end.
-fn document_short(
-    w: &mut Buffer,
-    item: &clean::Item,
-    cx: &mut Context<'_>,
-    link: AssocItemLink<'_>,
-    parent: &clean::Item,
+fn document_short<'a, 'cx: 'a>(
+    item: &'a clean::Item,
+    cx: &'a mut Context<'cx>,
+    link: AssocItemLink<'a>,
+    parent: &'a clean::Item,
     show_def_docs: bool,
-) {
-    document_item_info(cx, item, Some(parent)).render_into(w).unwrap();
-    if !show_def_docs {
-        return;
-    }
-    if let Some(s) = item.doc_value() {
-        let (mut summary_html, has_more_content) =
-            MarkdownSummaryLine(&s, &item.links(cx)).into_string_with_has_more_content();
+) -> impl fmt::Display + 'a + Captures<'cx> {
+    display_fn(move |f| {
+        document_item_info(cx, item, Some(parent)).render_into(f).unwrap();
+        if !show_def_docs {
+            return Ok(());
+        }
+        if let Some(s) = item.doc_value() {
+            let (mut summary_html, has_more_content) =
+                MarkdownSummaryLine(&s, &item.links(cx)).into_string_with_has_more_content();
 
-        if has_more_content {
-            let link = format!(r#" <a{}>Read more</a>"#, assoc_href_attr(item, link, cx));
+            if has_more_content {
+                let link = format!(r#" <a{}>Read more</a>"#, assoc_href_attr(item, link, cx));
 
-            if let Some(idx) = summary_html.rfind("</p>") {
-                summary_html.insert_str(idx, &link);
-            } else {
-                summary_html.push_str(&link);
+                if let Some(idx) = summary_html.rfind("</p>") {
+                    summary_html.insert_str(idx, &link);
+                } else {
+                    summary_html.push_str(&link);
+                }
             }
-        }
 
-        write!(w, "<div class='docblock'>{}</div>", summary_html,);
-    }
+            write!(f, "<div class='docblock'>{}</div>", summary_html)?;
+        }
+        Ok(())
+    })
 }
 
-fn document_full_collapsible(
-    w: &mut Buffer,
-    item: &clean::Item,
-    cx: &mut Context<'_>,
+fn document_full_collapsible<'a, 'cx: 'a>(
+    item: &'a clean::Item,
+    cx: &'a mut Context<'cx>,
     heading_offset: HeadingOffset,
-) {
-    document_full_inner(w, item, cx, true, heading_offset);
+) -> impl fmt::Display + 'a + Captures<'cx> {
+    document_full_inner(item, cx, true, heading_offset)
 }
 
-fn document_full(
-    w: &mut Buffer,
-    item: &clean::Item,
-    cx: &mut Context<'_>,
+fn document_full<'a, 'cx: 'a>(
+    item: &'a clean::Item,
+    cx: &'a mut Context<'cx>,
     heading_offset: HeadingOffset,
-) {
-    document_full_inner(w, item, cx, false, heading_offset);
+) -> impl fmt::Display + 'a + Captures<'cx> {
+    document_full_inner(item, cx, false, heading_offset)
 }
 
-fn document_full_inner(
-    w: &mut Buffer,
-    item: &clean::Item,
-    cx: &mut Context<'_>,
+fn document_full_inner<'a, 'cx: 'a>(
+    item: &'a clean::Item,
+    cx: &'a mut Context<'cx>,
     is_collapsible: bool,
     heading_offset: HeadingOffset,
-) {
-    if let Some(s) = item.collapsed_doc_value() {
-        debug!("Doc block: =====\n{}\n=====", s);
-        if is_collapsible {
-            w.write_str(
-                "<details class=\"toggle top-doc\" open>\
-                <summary class=\"hideme\">\
-                     <span>Expand description</span>\
-                </summary>",
-            );
-            render_markdown(w, cx, &s, item.links(cx), heading_offset);
-            w.write_str("</details>");
-        } else {
-            render_markdown(w, cx, &s, item.links(cx), heading_offset);
+) -> impl fmt::Display + 'a + Captures<'cx> {
+    display_fn(move |f| {
+        if let Some(s) = item.collapsed_doc_value() {
+            debug!("Doc block: =====\n{}\n=====", s);
+            if is_collapsible {
+                write!(
+                    f,
+                    "<details class=\"toggle top-doc\" open>\
+                    <summary class=\"hideme\">\
+                        <span>Expand description</span>\
+                    </summary>{}</details>",
+                    render_markdown(cx, &s, item.links(cx), heading_offset)
+                )?;
+            } else {
+                write!(f, "{}", render_markdown(cx, &s, item.links(cx), heading_offset))?;
+            }
         }
-    }
 
-    let kind = match &*item.kind {
-        clean::ItemKind::StrippedItem(box kind) | kind => kind,
-    };
+        let kind = match &*item.kind {
+            clean::ItemKind::StrippedItem(box kind) | kind => kind,
+        };
 
-    if let clean::ItemKind::FunctionItem(..) | clean::ItemKind::MethodItem(..) = kind {
-        render_call_locations(w, cx, item);
-    }
+        if let clean::ItemKind::FunctionItem(..) | clean::ItemKind::MethodItem(..) = kind {
+            render_call_locations(f, cx, item);
+        }
+        Ok(())
+    })
 }
 
 #[derive(Template)]
@@ -653,7 +660,7 @@ fn short_item_info(
 // "Auto Trait Implementations," "Blanket Trait Implementations" (on struct/enum pages).
 pub(crate) fn render_impls(
     cx: &mut Context<'_>,
-    w: &mut Buffer,
+    mut w: impl Write,
     impls: &[&Impl],
     containing_item: &clean::Item,
     toggle_open_by_default: bool,
@@ -665,7 +672,7 @@ pub(crate) fn render_impls(
             let did = i.trait_did().unwrap();
             let provided_trait_methods = i.inner_impl().provided_trait_methods(tcx);
             let assoc_link = AssocItemLink::GotoSource(did.into(), &provided_trait_methods);
-            let mut buffer = if w.is_for_html() { Buffer::html() } else { Buffer::new() };
+            let mut buffer = Buffer::new();
             render_impl(
                 &mut buffer,
                 cx,
@@ -686,7 +693,7 @@ pub(crate) fn render_impls(
         })
         .collect::<Vec<_>>();
     rendered_impls.sort();
-    w.write_str(&rendered_impls.join(""));
+    w.write_str(&rendered_impls.join("")).unwrap();
 }
 
 /// Build a (possibly empty) `href` attribute (a key-value pair) for the given associated item.
@@ -842,7 +849,7 @@ fn assoc_method(
     let (indent, indent_str, end_newline) = if parent == ItemType::Trait {
         header_len += 4;
         let indent_str = "    ";
-        render_attributes_in_pre(w, meth, indent_str);
+        write!(w, "{}", render_attributes_in_pre(meth, indent_str));
         (4, indent_str, Ending::NoNewline)
     } else {
         render_attributes_in_code(w, meth);
@@ -1038,10 +1045,16 @@ fn attributes(it: &clean::Item) -> Vec<String> {
 
 // When an attribute is rendered inside a `<pre>` tag, it is formatted using
 // a whitespace prefix and newline.
-fn render_attributes_in_pre(w: &mut Buffer, it: &clean::Item, prefix: &str) {
-    for a in attributes(it) {
-        writeln!(w, "{}{}", prefix, a);
-    }
+fn render_attributes_in_pre<'a>(
+    it: &'a clean::Item,
+    prefix: &'a str,
+) -> impl fmt::Display + Captures<'a> {
+    crate::html::format::display_fn(move |f| {
+        for a in attributes(it) {
+            writeln!(f, "{}{}", prefix, a)?;
+        }
+        Ok(())
+    })
 }
 
 // When an attribute is rendered inside a <code> tag, it is formatted using
@@ -1067,61 +1080,68 @@ impl<'a> AssocItemLink<'a> {
     }
 }
 
-fn write_impl_section_heading(w: &mut Buffer, title: &str, id: &str) {
+fn write_impl_section_heading(mut w: impl fmt::Write, title: &str, id: &str) {
     write!(
         w,
         "<h2 id=\"{id}\" class=\"small-section-header\">\
             {title}\
             <a href=\"#{id}\" class=\"anchor\">§</a>\
          </h2>"
-    );
+    )
+    .unwrap();
 }
 
 pub(crate) fn render_all_impls(
-    w: &mut Buffer,
+    mut w: impl Write,
     cx: &mut Context<'_>,
     containing_item: &clean::Item,
     concrete: &[&Impl],
     synthetic: &[&Impl],
     blanket_impl: &[&Impl],
 ) {
-    let mut impls = Buffer::empty_from(w);
+    let mut impls = Buffer::html();
     render_impls(cx, &mut impls, concrete, containing_item, true);
     let impls = impls.into_inner();
     if !impls.is_empty() {
-        write_impl_section_heading(w, "Trait Implementations", "trait-implementations");
-        write!(w, "<div id=\"trait-implementations-list\">{}</div>", impls);
+        write_impl_section_heading(&mut w, "Trait Implementations", "trait-implementations");
+        write!(w, "<div id=\"trait-implementations-list\">{}</div>", impls).unwrap();
     }
 
     if !synthetic.is_empty() {
-        write_impl_section_heading(w, "Auto Trait Implementations", "synthetic-implementations");
-        w.write_str("<div id=\"synthetic-implementations-list\">");
-        render_impls(cx, w, synthetic, containing_item, false);
-        w.write_str("</div>");
+        write_impl_section_heading(
+            &mut w,
+            "Auto Trait Implementations",
+            "synthetic-implementations",
+        );
+        w.write_str("<div id=\"synthetic-implementations-list\">").unwrap();
+        render_impls(cx, &mut w, synthetic, containing_item, false);
+        w.write_str("</div>").unwrap();
     }
 
     if !blanket_impl.is_empty() {
-        write_impl_section_heading(w, "Blanket Implementations", "blanket-implementations");
-        w.write_str("<div id=\"blanket-implementations-list\">");
-        render_impls(cx, w, blanket_impl, containing_item, false);
-        w.write_str("</div>");
+        write_impl_section_heading(&mut w, "Blanket Implementations", "blanket-implementations");
+        w.write_str("<div id=\"blanket-implementations-list\">").unwrap();
+        render_impls(cx, &mut w, blanket_impl, containing_item, false);
+        w.write_str("</div>").unwrap();
     }
 }
 
-fn render_assoc_items(
-    w: &mut Buffer,
-    cx: &mut Context<'_>,
-    containing_item: &clean::Item,
+fn render_assoc_items<'a, 'cx: 'a>(
+    cx: &'a mut Context<'cx>,
+    containing_item: &'a clean::Item,
     it: DefId,
-    what: AssocItemRender<'_>,
-) {
+    what: AssocItemRender<'a>,
+) -> impl fmt::Display + 'a + Captures<'cx> {
     let mut derefs = DefIdSet::default();
     derefs.insert(it);
-    render_assoc_items_inner(w, cx, containing_item, it, what, &mut derefs)
+    display_fn(move |f| {
+        render_assoc_items_inner(f, cx, containing_item, it, what, &mut derefs);
+        Ok(())
+    })
 }
 
 fn render_assoc_items_inner(
-    w: &mut Buffer,
+    mut w: &mut dyn fmt::Write,
     cx: &mut Context<'_>,
     containing_item: &clean::Item,
     it: DefId,
@@ -1134,7 +1154,7 @@ fn render_assoc_items_inner(
     let Some(v) = cache.impls.get(&it) else { return };
     let (non_trait, traits): (Vec<_>, _) = v.iter().partition(|i| i.inner_impl().trait_.is_none());
     if !non_trait.is_empty() {
-        let mut tmp_buf = Buffer::empty_from(w);
+        let mut tmp_buf = Buffer::html();
         let (render_mode, id) = match what {
             AssocItemRender::All => {
                 write_impl_section_heading(&mut tmp_buf, "Implementations", "implementations");
@@ -1158,7 +1178,7 @@ fn render_assoc_items_inner(
                 (RenderMode::ForDeref { mut_: deref_mut_ }, cx.derive_id(id))
             }
         };
-        let mut impls_buf = Buffer::empty_from(w);
+        let mut impls_buf = Buffer::html();
         for i in &non_trait {
             render_impl(
                 &mut impls_buf,
@@ -1178,10 +1198,10 @@ fn render_assoc_items_inner(
             );
         }
         if !impls_buf.is_empty() {
-            w.push_buffer(tmp_buf);
-            write!(w, "<div id=\"{}\">", id);
-            w.push_buffer(impls_buf);
-            w.write_str("</div>");
+            write!(w, "{}", tmp_buf.into_inner()).unwrap();
+            write!(w, "<div id=\"{}\">", id).unwrap();
+            write!(w, "{}", impls_buf.into_inner()).unwrap();
+            w.write_str("</div>").unwrap();
         }
     }
 
@@ -1191,7 +1211,7 @@ fn render_assoc_items_inner(
         if let Some(impl_) = deref_impl {
             let has_deref_mut =
                 traits.iter().any(|t| t.trait_did() == cx.tcx().lang_items().deref_mut_trait());
-            render_deref_methods(w, cx, impl_, containing_item, has_deref_mut, derefs);
+            render_deref_methods(&mut w, cx, impl_, containing_item, has_deref_mut, derefs);
         }
 
         // If we were already one level into rendering deref methods, we don't want to render
@@ -1210,7 +1230,7 @@ fn render_assoc_items_inner(
 }
 
 fn render_deref_methods(
-    w: &mut Buffer,
+    mut w: impl Write,
     cx: &mut Context<'_>,
     impl_: &Impl,
     container_item: &clean::Item,
@@ -1242,10 +1262,10 @@ fn render_deref_methods(
                 return;
             }
         }
-        render_assoc_items_inner(w, cx, container_item, did, what, derefs);
+        render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs);
     } else if let Some(prim) = target.primitive_type() {
         if let Some(&did) = cache.primitive_locations.get(&prim) {
-            render_assoc_items_inner(w, cx, container_item, did, what, derefs);
+            render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs);
         }
     }
 }
@@ -1478,18 +1498,25 @@ fn render_impl(
                             document_item_info(cx, it, Some(parent))
                                 .render_into(&mut info_buffer)
                                 .unwrap();
-                            document_full(&mut doc_buffer, item, cx, HeadingOffset::H5);
+                            write!(
+                                &mut doc_buffer,
+                                "{}",
+                                document_full(item, cx, HeadingOffset::H5)
+                            );
                             short_documented = false;
                         } else {
                             // In case the item isn't documented,
                             // provide short documentation from the trait.
-                            document_short(
+                            write!(
                                 &mut doc_buffer,
-                                it,
-                                cx,
-                                link,
-                                parent,
-                                rendering_params.show_def_docs,
+                                "{}",
+                                document_short(
+                                    it,
+                                    cx,
+                                    link,
+                                    parent,
+                                    rendering_params.show_def_docs,
+                                )
                             );
                         }
                     }
@@ -1498,18 +1525,15 @@ fn render_impl(
                         .render_into(&mut info_buffer)
                         .unwrap();
                     if rendering_params.show_def_docs {
-                        document_full(&mut doc_buffer, item, cx, HeadingOffset::H5);
+                        write!(&mut doc_buffer, "{}", document_full(item, cx, HeadingOffset::H5));
                         short_documented = false;
                     }
                 }
             } else {
-                document_short(
+                write!(
                     &mut doc_buffer,
-                    item,
-                    cx,
-                    link,
-                    parent,
-                    rendering_params.show_def_docs,
+                    "{}",
+                    document_short(item, cx, link, parent, rendering_params.show_def_docs,)
                 );
             }
         }
@@ -2206,7 +2230,7 @@ const MAX_FULL_EXAMPLES: usize = 5;
 const NUM_VISIBLE_LINES: usize = 10;
 
 /// Generates the HTML for example call locations generated via the --scrape-examples flag.
-fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item) {
+fn render_call_locations<W: fmt::Write>(mut w: W, cx: &mut Context<'_>, item: &clean::Item) {
     let tcx = cx.tcx();
     let def_id = item.item_id.expect_def_id();
     let key = tcx.def_path_hash(def_id);
@@ -2215,7 +2239,7 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite
     // Generate a unique ID so users can link to this section for a given method
     let id = cx.id_map.derive("scraped-examples");
     write!(
-        w,
+        &mut w,
         "<div class=\"docblock scraped-example-list\">\
           <span></span>\
           <h5 id=\"{id}\">\
@@ -2224,7 +2248,8 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite
           </h5>",
         root_path = cx.root_path(),
         id = id
-    );
+    )
+    .unwrap();
 
     // Create a URL to a particular location in a reverse-dependency's source file
     let link_to_loc = |call_data: &CallData, loc: &CallLocation| -> (String, String) {
@@ -2242,7 +2267,7 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite
     };
 
     // Generate the HTML for a single example, being the title and code block
-    let write_example = |w: &mut Buffer, (path, call_data): (&PathBuf, &CallData)| -> bool {
+    let write_example = |mut w: &mut W, (path, call_data): (&PathBuf, &CallData)| -> bool {
         let contents = match fs::read_to_string(&path) {
             Ok(contents) => contents,
             Err(err) => {
@@ -2290,7 +2315,7 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite
         let locations_encoded = serde_json::to_string(&line_ranges).unwrap();
 
         write!(
-            w,
+            &mut w,
             "<div class=\"scraped-example {expanded_cls}\" data-locs=\"{locations}\">\
                 <div class=\"scraped-example-title\">\
                    {name} (<a href=\"{url}\">{title}</a>)\
@@ -2303,10 +2328,12 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite
             // The locations are encoded as a data attribute, so they can be read
             // later by the JS for interactions.
             locations = Escape(&locations_encoded)
-        );
+        )
+        .unwrap();
 
         if line_ranges.len() > 1 {
-            write!(w, r#"<button class="prev">&pr;</button> <button class="next">&sc;</button>"#);
+            write!(w, r#"<button class="prev">&pr;</button> <button class="next">&sc;</button>"#)
+                .unwrap();
         }
 
         // Look for the example file in the source map if it exists, otherwise return a dummy span
@@ -2333,7 +2360,7 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite
         decoration_info.insert("highlight", byte_ranges);
 
         sources::print_src(
-            w,
+            &mut w,
             contents_subset,
             file_span,
             cx,
@@ -2341,7 +2368,7 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite
             highlight::DecorationInfo(decoration_info),
             sources::SourceContext::Embedded { offset: line_min, needs_expansion },
         );
-        write!(w, "</div></div>");
+        write!(w, "</div></div>").unwrap();
 
         true
     };
@@ -2375,7 +2402,7 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite
 
     // An example may fail to write if its source can't be read for some reason, so this method
     // continues iterating until a write succeeds
-    let write_and_skip_failure = |w: &mut Buffer, it: &mut Peekable<_>| {
+    let write_and_skip_failure = |w: &mut W, it: &mut Peekable<_>| {
         while let Some(example) = it.next() {
             if write_example(&mut *w, example) {
                 break;
@@ -2384,7 +2411,7 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite
     };
 
     // Write just one example that's visible by default in the method's description.
-    write_and_skip_failure(w, &mut it);
+    write_and_skip_failure(&mut w, &mut it);
 
     // Then add the remaining examples in a hidden section.
     if it.peek().is_some() {
@@ -2397,17 +2424,19 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite
                   <div class=\"hide-more\">Hide additional examples</div>\
                   <div class=\"more-scraped-examples\">\
                     <div class=\"toggle-line\"><div class=\"toggle-line-inner\"></div></div>"
-        );
+        )
+        .unwrap();
 
         // Only generate inline code for MAX_FULL_EXAMPLES number of examples. Otherwise we could
         // make the page arbitrarily huge!
         for _ in 0..MAX_FULL_EXAMPLES {
-            write_and_skip_failure(w, &mut it);
+            write_and_skip_failure(&mut w, &mut it);
         }
 
         // For the remaining examples, generate a <ul> containing links to the source files.
         if it.peek().is_some() {
-            write!(w, r#"<div class="example-links">Additional examples can be found in:<br><ul>"#);
+            write!(w, r#"<div class="example-links">Additional examples can be found in:<br><ul>"#)
+                .unwrap();
             it.for_each(|(_, call_data)| {
                 let (url, _) = link_to_loc(call_data, &call_data.locations[0]);
                 write!(
@@ -2415,13 +2444,14 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite
                     r#"<li><a href="{url}">{name}</a></li>"#,
                     url = url,
                     name = call_data.display_name
-                );
+                )
+                .unwrap();
             });
-            write!(w, "</ul></div>");
+            write!(w, "</ul></div>").unwrap();
         }
 
-        write!(w, "</div></details>");
+        write!(w, "</div></details>").unwrap();
     }
 
-    write!(w, "</div>");
+    write!(w, "</div>").unwrap();
 }
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index 674cd0d62d4..6bce5734004 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -202,7 +202,7 @@ fn should_hide_fields(n_fields: usize) -> bool {
     n_fields > 12
 }
 
-fn toggle_open(w: &mut Buffer, text: impl fmt::Display) {
+fn toggle_open(mut w: impl fmt::Write, text: impl fmt::Display) {
     write!(
         w,
         "<details class=\"toggle type-contents-toggle\">\
@@ -210,15 +210,16 @@ fn toggle_open(w: &mut Buffer, text: impl fmt::Display) {
                 <span>Show {}</span>\
             </summary>",
         text
-    );
+    )
+    .unwrap();
 }
 
-fn toggle_close(w: &mut Buffer) {
-    w.write_str("</details>");
+fn toggle_close(mut w: impl fmt::Write) {
+    w.write_str("</details>").unwrap();
 }
 
 fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items: &[clean::Item]) {
-    document(w, cx, item, None, HeadingOffset::H2);
+    write!(w, "{}", document(cx, item, None, HeadingOffset::H2));
 
     let mut indices = (0..items.len()).filter(|i| !items[*i].is_stripped()).collect::<Vec<usize>>();
 
@@ -544,12 +545,12 @@ fn item_function(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, f: &cle
         f.decl.output.as_return().and_then(|output| notable_traits_button(output, cx));
 
     wrap_item(w, |w| {
-        render_attributes_in_pre(w, it, "");
         w.reserve(header_len);
         write!(
             w,
-            "{vis}{constness}{asyncness}{unsafety}{abi}fn \
+            "{attrs}{vis}{constness}{asyncness}{unsafety}{abi}fn \
                 {name}{generics}{decl}{notable_traits}{where_clause}",
+            attrs = render_attributes_in_pre(it, ""),
             vis = visibility,
             constness = constness,
             asyncness = asyncness,
@@ -562,7 +563,7 @@ fn item_function(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, f: &cle
             notable_traits = notable_traits.unwrap_or_default(),
         );
     });
-    document(w, cx, it, None, HeadingOffset::H2);
+    write!(w, "{}", document(cx, it, None, HeadingOffset::H2));
 }
 
 fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::Trait) {
@@ -580,17 +581,17 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
     let must_implement_one_of_functions = tcx.trait_def(t.def_id).must_implement_one_of.clone();
 
     // Output the trait definition
-    wrap_item(w, |w| {
-        render_attributes_in_pre(w, it, "");
+    wrap_item(w, |mut w| {
         write!(
             w,
-            "{}{}{}trait {}{}{}",
+            "{attrs}{}{}{}trait {}{}{}",
             visibility_print_with_space(it.visibility(tcx), it.item_id, cx),
             t.unsafety(tcx).print_with_space(),
             if t.is_auto(tcx) { "auto " } else { "" },
             it.name.unwrap(),
             t.generics.print(cx),
-            bounds
+            bounds,
+            attrs = render_attributes_in_pre(it, ""),
         );
 
         if !t.generics.where_predicates.is_empty() {
@@ -610,7 +611,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
             if should_hide_fields(count_types) {
                 toggle = true;
                 toggle_open(
-                    w,
+                    &mut w,
                     format_args!("{} associated items", count_types + count_consts + count_methods),
                 );
             }
@@ -634,7 +635,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
             if !toggle && should_hide_fields(count_types + count_consts) {
                 toggle = true;
                 toggle_open(
-                    w,
+                    &mut w,
                     format_args!(
                         "{} associated constant{} and {} method{}",
                         count_consts,
@@ -662,7 +663,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
             }
             if !toggle && should_hide_fields(count_methods) {
                 toggle = true;
-                toggle_open(w, format_args!("{} methods", count_methods));
+                toggle_open(&mut w, format_args!("{} methods", count_methods));
             }
             if count_consts != 0 && count_methods != 0 {
                 w.write_str("\n");
@@ -710,14 +711,14 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
                 }
             }
             if toggle {
-                toggle_close(w);
+                toggle_close(&mut w);
             }
             w.write_str("}");
         }
     });
 
     // Trait documentation
-    document(w, cx, it, None, HeadingOffset::H2);
+    write!(w, "{}", document(cx, it, None, HeadingOffset::H2));
 
     fn write_small_section_header(w: &mut Buffer, id: &str, title: &str, extra_content: &str) {
         write!(
@@ -735,7 +736,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
         let item_type = m.type_();
         let id = cx.derive_id(format!("{}.{}", item_type, name));
         let mut content = Buffer::empty_from(w);
-        document(&mut content, cx, m, Some(t), HeadingOffset::H5);
+        write!(&mut content, "{}", document(cx, m, Some(t), HeadingOffset::H5));
         let toggled = !content.is_empty();
         if toggled {
             let method_toggle_class = if item_type.is_method() { " method-toggle" } else { "" };
@@ -847,7 +848,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
     }
 
     // If there are methods directly on this trait object, render them here.
-    render_assoc_items(w, cx, it, it.item_id.expect_def_id(), AssocItemRender::All);
+    write!(w, "{}", render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All));
 
     let cloned_shared = Rc::clone(&cx.shared);
     let cache = &cloned_shared.cache;
@@ -1057,147 +1058,201 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
 
 fn item_trait_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::TraitAlias) {
     wrap_item(w, |w| {
-        render_attributes_in_pre(w, it, "");
         write!(
             w,
-            "trait {}{}{} = {};",
+            "{attrs}trait {}{}{} = {};",
             it.name.unwrap(),
             t.generics.print(cx),
             print_where_clause(&t.generics, cx, 0, Ending::Newline),
-            bounds(&t.bounds, true, cx)
+            bounds(&t.bounds, true, cx),
+            attrs = render_attributes_in_pre(it, ""),
         );
     });
 
-    document(w, cx, it, None, HeadingOffset::H2);
+    write!(w, "{}", document(cx, it, None, HeadingOffset::H2));
 
     // Render any items associated directly to this alias, as otherwise they
     // won't be visible anywhere in the docs. It would be nice to also show
     // associated items from the aliased type (see discussion in #32077), but
     // we need #14072 to make sense of the generics.
-    render_assoc_items(w, cx, it, it.item_id.expect_def_id(), AssocItemRender::All)
+    write!(w, "{}", render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All))
 }
 
 fn item_opaque_ty(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::OpaqueTy) {
     wrap_item(w, |w| {
-        render_attributes_in_pre(w, it, "");
         write!(
             w,
-            "type {}{}{where_clause} = impl {bounds};",
+            "{attrs}type {}{}{where_clause} = impl {bounds};",
             it.name.unwrap(),
             t.generics.print(cx),
             where_clause = print_where_clause(&t.generics, cx, 0, Ending::Newline),
             bounds = bounds(&t.bounds, false, cx),
+            attrs = render_attributes_in_pre(it, ""),
         );
     });
 
-    document(w, cx, it, None, HeadingOffset::H2);
+    write!(w, "{}", document(cx, it, None, HeadingOffset::H2));
 
     // Render any items associated directly to this alias, as otherwise they
     // won't be visible anywhere in the docs. It would be nice to also show
     // associated items from the aliased type (see discussion in #32077), but
     // we need #14072 to make sense of the generics.
-    render_assoc_items(w, cx, it, it.item_id.expect_def_id(), AssocItemRender::All)
+    write!(w, "{}", render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All))
 }
 
 fn item_typedef(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::Typedef) {
     fn write_content(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Typedef) {
         wrap_item(w, |w| {
-            render_attributes_in_pre(w, it, "");
             write!(
                 w,
-                "{}type {}{}{where_clause} = {type_};",
+                "{attrs}{}type {}{}{where_clause} = {type_};",
                 visibility_print_with_space(it.visibility(cx.tcx()), it.item_id, cx),
                 it.name.unwrap(),
                 t.generics.print(cx),
                 where_clause = print_where_clause(&t.generics, cx, 0, Ending::Newline),
                 type_ = t.type_.print(cx),
+                attrs = render_attributes_in_pre(it, ""),
             );
         });
     }
 
     write_content(w, cx, it, t);
 
-    document(w, cx, it, None, HeadingOffset::H2);
+    write!(w, "{}", document(cx, it, None, HeadingOffset::H2));
 
     let def_id = it.item_id.expect_def_id();
     // Render any items associated directly to this alias, as otherwise they
     // won't be visible anywhere in the docs. It would be nice to also show
     // associated items from the aliased type (see discussion in #32077), but
     // we need #14072 to make sense of the generics.
-    render_assoc_items(w, cx, it, def_id, AssocItemRender::All);
-    document_type_layout(w, cx, def_id);
+    write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All));
+    write!(w, "{}", document_type_layout(cx, def_id));
 }
 
 fn item_union(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Union) {
-    wrap_item(w, |w| {
-        render_attributes_in_pre(w, it, "");
-        render_union(w, it, Some(&s.generics), &s.fields, cx);
-    });
+    #[derive(Template)]
+    #[template(path = "item_union.html")]
+    struct ItemUnion<'a, 'cx> {
+        cx: std::cell::RefCell<&'a mut Context<'cx>>,
+        it: &'a clean::Item,
+        s: &'a clean::Union,
+    }
 
-    document(w, cx, it, None, HeadingOffset::H2);
+    impl<'a, 'cx: 'a> ItemUnion<'a, 'cx> {
+        fn render_assoc_items<'b>(
+            &'b self,
+        ) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> {
+            display_fn(move |f| {
+                let def_id = self.it.item_id.expect_def_id();
+                let mut cx = self.cx.borrow_mut();
+                let v = render_assoc_items(*cx, self.it, def_id, AssocItemRender::All);
+                write!(f, "{v}")
+            })
+        }
+        fn document_type_layout<'b>(
+            &'b self,
+        ) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> {
+            display_fn(move |f| {
+                let def_id = self.it.item_id.expect_def_id();
+                let cx = self.cx.borrow_mut();
+                let v = document_type_layout(*cx, def_id);
+                write!(f, "{v}")
+            })
+        }
+        fn render_union<'b>(&'b self) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> {
+            display_fn(move |f| {
+                let cx = self.cx.borrow_mut();
+                let v = render_union(self.it, Some(&self.s.generics), &self.s.fields, *cx);
+                write!(f, "{v}")
+            })
+        }
+        fn render_attributes_in_pre<'b>(
+            &'b self,
+        ) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> {
+            display_fn(move |f| {
+                let v = render_attributes_in_pre(self.it, "");
+                write!(f, "{v}")
+            })
+        }
+        fn document<'b>(&'b self) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> {
+            display_fn(move |f| {
+                let mut cx = self.cx.borrow_mut();
+                let v = document(*cx, self.it, None, HeadingOffset::H2);
+                write!(f, "{v}")
+            })
+        }
+        fn document_field<'b>(
+            &'b self,
+            field: &'a clean::Item,
+        ) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> {
+            display_fn(move |f| {
+                let mut cx = self.cx.borrow_mut();
+                let v = document(*cx, field, Some(self.it), HeadingOffset::H3);
+                write!(f, "{v}")
+            })
+        }
+        fn stability_field(&self, field: &clean::Item) -> Option<String> {
+            let cx = self.cx.borrow();
+            field.stability_class(cx.tcx())
+        }
+        fn print_ty<'b>(
+            &'b self,
+            ty: &'a clean::Type,
+        ) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> {
+            display_fn(move |f| {
+                let cx = self.cx.borrow();
+                let v = ty.print(*cx);
+                write!(f, "{v}")
+            })
+        }
 
-    let mut fields = s
-        .fields
-        .iter()
-        .filter_map(|f| match *f.kind {
-            clean::StructFieldItem(ref ty) => Some((f, ty)),
-            _ => None,
-        })
-        .peekable();
-    if fields.peek().is_some() {
-        write!(
-            w,
-            "<h2 id=\"fields\" class=\"fields small-section-header\">\
-                Fields<a href=\"#fields\" class=\"anchor\">§</a>\
-            </h2>"
-        );
-        for (field, ty) in fields {
-            let name = field.name.expect("union field name");
-            let id = format!("{}.{}", ItemType::StructField, name);
-            write!(
-                w,
-                "<span id=\"{id}\" class=\"{shortty} small-section-header\">\
-                     <a href=\"#{id}\" class=\"anchor field\">§</a>\
-                     <code>{name}: {ty}</code>\
-                 </span>",
-                shortty = ItemType::StructField,
-                ty = ty.print(cx),
-            );
-            if let Some(stability_class) = field.stability_class(cx.tcx()) {
-                write!(w, "<span class=\"stab {stability_class}\"></span>");
-            }
-            document(w, cx, field, Some(it), HeadingOffset::H3);
+        fn fields_iter(
+            &self,
+        ) -> std::iter::Peekable<impl Iterator<Item = (&'a clean::Item, &'a clean::Type)>> {
+            self.s
+                .fields
+                .iter()
+                .filter_map(|f| match *f.kind {
+                    clean::StructFieldItem(ref ty) => Some((f, ty)),
+                    _ => None,
+                })
+                .peekable()
         }
     }
-    let def_id = it.item_id.expect_def_id();
-    render_assoc_items(w, cx, it, def_id, AssocItemRender::All);
-    document_type_layout(w, cx, def_id);
+
+    ItemUnion { cx: std::cell::RefCell::new(cx), it, s }.render_into(w).unwrap();
 }
 
-fn print_tuple_struct_fields(w: &mut Buffer, cx: &Context<'_>, s: &[clean::Item]) {
-    for (i, ty) in s.iter().enumerate() {
-        if i > 0 {
-            w.write_str(", ");
-        }
-        match *ty.kind {
-            clean::StrippedItem(box clean::StructFieldItem(_)) => w.write_str("_"),
-            clean::StructFieldItem(ref ty) => write!(w, "{}", ty.print(cx)),
-            _ => unreachable!(),
+fn print_tuple_struct_fields<'a, 'cx: 'a>(
+    cx: &'a Context<'cx>,
+    s: &'a [clean::Item],
+) -> impl fmt::Display + 'a + Captures<'cx> {
+    display_fn(|f| {
+        for (i, ty) in s.iter().enumerate() {
+            if i > 0 {
+                f.write_str(", ")?;
+            }
+            match *ty.kind {
+                clean::StrippedItem(box clean::StructFieldItem(_)) => f.write_str("_")?,
+                clean::StructFieldItem(ref ty) => write!(f, "{}", ty.print(cx))?,
+                _ => unreachable!(),
+            }
         }
-    }
+        Ok(())
+    })
 }
 
 fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::Enum) {
     let tcx = cx.tcx();
     let count_variants = e.variants().count();
-    wrap_item(w, |w| {
-        render_attributes_in_pre(w, it, "");
+    wrap_item(w, |mut w| {
         write!(
             w,
-            "{}enum {}{}",
+            "{attrs}{}enum {}{}",
             visibility_print_with_space(it.visibility(tcx), it.item_id, cx),
             it.name.unwrap(),
             e.generics.print(cx),
+            attrs = render_attributes_in_pre(it, ""),
         );
         if !print_where_clause_and_check(w, &e.generics, cx) {
             // If there wasn't a `where` clause, we add a whitespace.
@@ -1211,7 +1266,7 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::
             w.write_str("{\n");
             let toggle = should_hide_fields(count_variants);
             if toggle {
-                toggle_open(w, format_args!("{} variants", count_variants));
+                toggle_open(&mut w, format_args!("{} variants", count_variants));
             }
             for v in e.variants() {
                 w.write_str("    ");
@@ -1221,9 +1276,7 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::
                     clean::VariantItem(ref var) => match var.kind {
                         clean::VariantKind::CLike => write!(w, "{}", name),
                         clean::VariantKind::Tuple(ref s) => {
-                            write!(w, "{}(", name);
-                            print_tuple_struct_fields(w, cx, s);
-                            w.write_str(")");
+                            write!(w, "{name}({})", print_tuple_struct_fields(cx, s),);
                         }
                         clean::VariantKind::Struct(ref s) => {
                             render_struct(w, v, None, None, &s.fields, "    ", false, cx);
@@ -1238,24 +1291,25 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::
                 w.write_str("    // some variants omitted\n");
             }
             if toggle {
-                toggle_close(w);
+                toggle_close(&mut w);
             }
             w.write_str("}");
         }
     });
 
-    document(w, cx, it, None, HeadingOffset::H2);
+    write!(w, "{}", document(cx, it, None, HeadingOffset::H2));
 
     if count_variants != 0 {
         write!(
             w,
             "<h2 id=\"variants\" class=\"variants small-section-header\">\
                 Variants{}<a href=\"#variants\" class=\"anchor\">§</a>\
-            </h2>",
-            document_non_exhaustive_header(it)
+            </h2>\
+            {}\
+            <div class=\"variants\">",
+            document_non_exhaustive_header(it),
+            document_non_exhaustive(it)
         );
-        document_non_exhaustive(w, it);
-        write!(w, "<div class=\"variants\">");
         for variant in e.variants() {
             let id = cx.derive_id(format!("{}.{}", ItemType::Variant, variant.name.unwrap()));
             write!(
@@ -1276,9 +1330,7 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::
             let clean::VariantItem(variant_data) = &*variant.kind else { unreachable!() };
 
             if let clean::VariantKind::Tuple(ref s) = variant_data.kind {
-                w.write_str("(");
-                print_tuple_struct_fields(w, cx, s);
-                w.write_str(")");
+                write!(w, "({})", print_tuple_struct_fields(cx, s),);
             }
             w.write_str("</h3></section>");
 
@@ -1302,9 +1354,10 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::
                 write!(
                     w,
                     "<div class=\"sub-variant\" id=\"{variant_id}\">\
-                        <h4>{heading}</h4>",
+                        <h4>{heading}</h4>\
+                        {}",
+                    document_non_exhaustive(variant)
                 );
-                document_non_exhaustive(w, variant);
                 for field in fields {
                     match *field.kind {
                         clean::StrippedItem(box clean::StructFieldItem(_)) => {}
@@ -1322,10 +1375,13 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::
                                      <code>{f}: {t}</code>\
                                  </span>",
                                 f = field.name.unwrap(),
-                                t = ty.print(cx)
+                                t = ty.print(cx),
+                            );
+                            write!(
+                                w,
+                                "{}</div>",
+                                document(cx, field, Some(variant), HeadingOffset::H5)
                             );
-                            document(w, cx, field, Some(variant), HeadingOffset::H5);
-                            write!(w, "</div>");
                         }
                         _ => unreachable!(),
                     }
@@ -1333,18 +1389,18 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::
                 w.write_str("</div>");
             }
 
-            document(w, cx, variant, Some(it), HeadingOffset::H4);
+            write!(w, "{}", document(cx, variant, Some(it), HeadingOffset::H4));
         }
         write!(w, "</div>");
     }
     let def_id = it.item_id.expect_def_id();
-    render_assoc_items(w, cx, it, def_id, AssocItemRender::All);
-    document_type_layout(w, cx, def_id);
+    write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All));
+    write!(w, "{}", document_type_layout(cx, def_id));
 }
 
 fn item_macro(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::Macro) {
     highlight::render_item_decl_with_highlighting(&t.source, w);
-    document(w, cx, it, None, HeadingOffset::H2)
+    write!(w, "{}", document(cx, it, None, HeadingOffset::H2))
 }
 
 fn item_proc_macro(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, m: &clean::ProcMacro) {
@@ -1370,14 +1426,14 @@ fn item_proc_macro(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, m: &c
             }
         }
     });
-    document(w, cx, it, None, HeadingOffset::H2)
+    write!(w, "{}", document(cx, it, None, HeadingOffset::H2))
 }
 
 fn item_primitive(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item) {
     let def_id = it.item_id.expect_def_id();
-    document(w, cx, it, None, HeadingOffset::H2);
+    write!(w, "{}", document(cx, it, None, HeadingOffset::H2));
     if it.name.map(|n| n.as_str() != "reference").unwrap_or(false) {
-        render_assoc_items(w, cx, it, def_id, AssocItemRender::All);
+        write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All));
     } else {
         // We handle the "reference" primitive type on its own because we only want to list
         // implementations on generic types.
@@ -1433,7 +1489,7 @@ fn item_constant(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, c: &cle
         }
     });
 
-    document(w, cx, it, None, HeadingOffset::H2)
+    write!(w, "{}", document(cx, it, None, HeadingOffset::H2))
 }
 
 fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Struct) {
@@ -1442,7 +1498,7 @@ fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean
         render_struct(w, it, Some(&s.generics), s.ctor_kind, &s.fields, "", true, cx);
     });
 
-    document(w, cx, it, None, HeadingOffset::H2);
+    write!(w, "{}", document(cx, it, None, HeadingOffset::H2));
 
     let mut fields = s
         .fields
@@ -1458,11 +1514,12 @@ fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean
                 w,
                 "<h2 id=\"fields\" class=\"fields small-section-header\">\
                      {}{}<a href=\"#fields\" class=\"anchor\">§</a>\
-                 </h2>",
+                 </h2>\
+                 {}",
                 if s.ctor_kind.is_none() { "Fields" } else { "Tuple Fields" },
-                document_non_exhaustive_header(it)
+                document_non_exhaustive_header(it),
+                document_non_exhaustive(it)
             );
-            document_non_exhaustive(w, it);
             for (index, (field, ty)) in fields.enumerate() {
                 let field_name =
                     field.name.map_or_else(|| index.to_string(), |sym| sym.as_str().to_string());
@@ -1476,13 +1533,13 @@ fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean
                     item_type = ItemType::StructField,
                     ty = ty.print(cx)
                 );
-                document(w, cx, field, Some(it), HeadingOffset::H3);
+                write!(w, "{}", document(cx, field, Some(it), HeadingOffset::H3));
             }
         }
     }
     let def_id = it.item_id.expect_def_id();
-    render_assoc_items(w, cx, it, def_id, AssocItemRender::All);
-    document_type_layout(w, cx, def_id);
+    write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All));
+    write!(w, "{}", document_type_layout(cx, def_id));
 }
 
 fn item_static(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Static) {
@@ -1497,7 +1554,7 @@ fn item_static(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean
             typ = s.type_.print(cx)
         );
     });
-    document(w, cx, it, None, HeadingOffset::H2)
+    write!(w, "{}", document(cx, it, None, HeadingOffset::H2))
 }
 
 fn item_foreign_type(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item) {
@@ -1512,13 +1569,13 @@ fn item_foreign_type(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item) {
         );
     });
 
-    document(w, cx, it, None, HeadingOffset::H2);
+    write!(w, "{}", document(cx, it, None, HeadingOffset::H2));
 
-    render_assoc_items(w, cx, it, it.item_id.expect_def_id(), AssocItemRender::All)
+    write!(w, "{}", render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All))
 }
 
 fn item_keyword(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item) {
-    document(w, cx, it, None, HeadingOffset::H2)
+    write!(w, "{}", document(cx, it, None, HeadingOffset::H2))
 }
 
 /// Compare two strings treating multi-digit numbers as single units (i.e. natural sort order).
@@ -1655,64 +1712,69 @@ fn render_implementor(
     );
 }
 
-fn render_union(
-    w: &mut Buffer,
-    it: &clean::Item,
-    g: Option<&clean::Generics>,
-    fields: &[clean::Item],
-    cx: &Context<'_>,
-) {
-    let tcx = cx.tcx();
-    write!(
-        w,
-        "{}union {}",
-        visibility_print_with_space(it.visibility(tcx), it.item_id, cx),
-        it.name.unwrap(),
-    );
-
-    let where_displayed = g
-        .map(|g| {
-            write!(w, "{}", g.print(cx));
-            print_where_clause_and_check(w, g, cx)
-        })
-        .unwrap_or(false);
+fn render_union<'a, 'cx: 'a>(
+    it: &'a clean::Item,
+    g: Option<&'a clean::Generics>,
+    fields: &'a [clean::Item],
+    cx: &'a Context<'cx>,
+) -> impl fmt::Display + 'a + Captures<'cx> {
+    display_fn(move |mut f| {
+        let tcx = cx.tcx();
+        write!(
+            f,
+            "{}union {}",
+            visibility_print_with_space(it.visibility(tcx), it.item_id, cx),
+            it.name.unwrap(),
+        )?;
+
+        let where_displayed = g
+            .map(|g| {
+                let mut buf = Buffer::html();
+                write!(buf, "{}", g.print(cx));
+                let where_displayed = print_where_clause_and_check(&mut buf, g, cx);
+                write!(f, "{buf}", buf = buf.into_inner()).unwrap();
+                where_displayed
+            })
+            .unwrap_or(false);
 
-    // If there wasn't a `where` clause, we add a whitespace.
-    if !where_displayed {
-        w.write_str(" ");
-    }
+        // If there wasn't a `where` clause, we add a whitespace.
+        if !where_displayed {
+            f.write_str(" ")?;
+        }
 
-    write!(w, "{{\n");
-    let count_fields =
-        fields.iter().filter(|f| matches!(*f.kind, clean::StructFieldItem(..))).count();
-    let toggle = should_hide_fields(count_fields);
-    if toggle {
-        toggle_open(w, format_args!("{} fields", count_fields));
-    }
+        write!(f, "{{\n")?;
+        let count_fields =
+            fields.iter().filter(|field| matches!(*field.kind, clean::StructFieldItem(..))).count();
+        let toggle = should_hide_fields(count_fields);
+        if toggle {
+            toggle_open(&mut f, format_args!("{} fields", count_fields));
+        }
 
-    for field in fields {
-        if let clean::StructFieldItem(ref ty) = *field.kind {
-            write!(
-                w,
-                "    {}{}: {},\n",
-                visibility_print_with_space(field.visibility(tcx), field.item_id, cx),
-                field.name.unwrap(),
-                ty.print(cx)
-            );
+        for field in fields {
+            if let clean::StructFieldItem(ref ty) = *field.kind {
+                write!(
+                    f,
+                    "    {}{}: {},\n",
+                    visibility_print_with_space(field.visibility(tcx), field.item_id, cx),
+                    field.name.unwrap(),
+                    ty.print(cx)
+                )?;
+            }
         }
-    }
 
-    if it.has_stripped_entries().unwrap() {
-        write!(w, "    /* private fields */\n");
-    }
-    if toggle {
-        toggle_close(w);
-    }
-    w.write_str("}");
+        if it.has_stripped_entries().unwrap() {
+            write!(f, "    /* private fields */\n")?;
+        }
+        if toggle {
+            toggle_close(&mut f);
+        }
+        f.write_str("}").unwrap();
+        Ok(())
+    })
 }
 
 fn render_struct(
-    w: &mut Buffer,
+    mut w: &mut Buffer,
     it: &clean::Item,
     g: Option<&clean::Generics>,
     ty: Option<CtorKind>,
@@ -1747,7 +1809,7 @@ fn render_struct(
             let has_visible_fields = count_fields > 0;
             let toggle = should_hide_fields(count_fields);
             if toggle {
-                toggle_open(w, format_args!("{} fields", count_fields));
+                toggle_open(&mut w, format_args!("{} fields", count_fields));
             }
             for field in fields {
                 if let clean::StructFieldItem(ref ty) = *field.kind {
@@ -1771,7 +1833,7 @@ fn render_struct(
                 write!(w, " /* private fields */ ");
             }
             if toggle {
-                toggle_close(w);
+                toggle_close(&mut w);
             }
             w.write_str("}");
         }
@@ -1817,161 +1879,169 @@ fn document_non_exhaustive_header(item: &clean::Item) -> &str {
     if item.is_non_exhaustive() { " (Non-exhaustive)" } else { "" }
 }
 
-fn document_non_exhaustive(w: &mut Buffer, item: &clean::Item) {
-    if item.is_non_exhaustive() {
-        write!(
-            w,
-            "<details class=\"toggle non-exhaustive\">\
-                 <summary class=\"hideme\"><span>{}</span></summary>\
-                 <div class=\"docblock\">",
-            {
-                if item.is_struct() {
-                    "This struct is marked as non-exhaustive"
-                } else if item.is_enum() {
-                    "This enum is marked as non-exhaustive"
-                } else if item.is_variant() {
-                    "This variant is marked as non-exhaustive"
-                } else {
-                    "This type is marked as non-exhaustive"
+fn document_non_exhaustive<'a>(item: &'a clean::Item) -> impl fmt::Display + 'a {
+    display_fn(|f| {
+        if item.is_non_exhaustive() {
+            write!(
+                f,
+                "<details class=\"toggle non-exhaustive\">\
+                    <summary class=\"hideme\"><span>{}</span></summary>\
+                    <div class=\"docblock\">",
+                {
+                    if item.is_struct() {
+                        "This struct is marked as non-exhaustive"
+                    } else if item.is_enum() {
+                        "This enum is marked as non-exhaustive"
+                    } else if item.is_variant() {
+                        "This variant is marked as non-exhaustive"
+                    } else {
+                        "This type is marked as non-exhaustive"
+                    }
                 }
+            )?;
+
+            if item.is_struct() {
+                f.write_str(
+                    "Non-exhaustive structs could have additional fields added in future. \
+                    Therefore, non-exhaustive structs cannot be constructed in external crates \
+                    using the traditional <code>Struct { .. }</code> syntax; cannot be \
+                    matched against without a wildcard <code>..</code>; and \
+                    struct update syntax will not work.",
+                )?;
+            } else if item.is_enum() {
+                f.write_str(
+                    "Non-exhaustive enums could have additional variants added in future. \
+                    Therefore, when matching against variants of non-exhaustive enums, an \
+                    extra wildcard arm must be added to account for any future variants.",
+                )?;
+            } else if item.is_variant() {
+                f.write_str(
+                    "Non-exhaustive enum variants could have additional fields added in future. \
+                    Therefore, non-exhaustive enum variants cannot be constructed in external \
+                    crates and cannot be matched against.",
+                )?;
+            } else {
+                f.write_str(
+                    "This type will require a wildcard arm in any match statements or constructors.",
+                )?;
             }
-        );
 
-        if item.is_struct() {
-            w.write_str(
-                "Non-exhaustive structs could have additional fields added in future. \
-                 Therefore, non-exhaustive structs cannot be constructed in external crates \
-                 using the traditional <code>Struct { .. }</code> syntax; cannot be \
-                 matched against without a wildcard <code>..</code>; and \
-                 struct update syntax will not work.",
-            );
-        } else if item.is_enum() {
-            w.write_str(
-                "Non-exhaustive enums could have additional variants added in future. \
-                 Therefore, when matching against variants of non-exhaustive enums, an \
-                 extra wildcard arm must be added to account for any future variants.",
-            );
-        } else if item.is_variant() {
-            w.write_str(
-                "Non-exhaustive enum variants could have additional fields added in future. \
-                 Therefore, non-exhaustive enum variants cannot be constructed in external \
-                 crates and cannot be matched against.",
-            );
-        } else {
-            w.write_str(
-                "This type will require a wildcard arm in any match statements or constructors.",
-            );
+            f.write_str("</div></details>")?;
         }
-
-        w.write_str("</div></details>");
-    }
+        Ok(())
+    })
 }
 
-fn document_type_layout(w: &mut Buffer, cx: &Context<'_>, ty_def_id: DefId) {
-    fn write_size_of_layout(w: &mut Buffer, layout: &LayoutS, tag_size: u64) {
+fn document_type_layout<'a, 'cx: 'a>(
+    cx: &'a Context<'cx>,
+    ty_def_id: DefId,
+) -> impl fmt::Display + 'a + Captures<'cx> {
+    fn write_size_of_layout(mut w: impl fmt::Write, layout: &LayoutS, tag_size: u64) {
         if layout.abi.is_unsized() {
-            write!(w, "(unsized)");
+            write!(w, "(unsized)").unwrap();
         } else {
             let size = layout.size.bytes() - tag_size;
-            write!(w, "{size} byte{pl}", pl = if size == 1 { "" } else { "s" },);
+            write!(w, "{size} byte{pl}", pl = if size == 1 { "" } else { "s" }).unwrap();
             if layout.abi.is_uninhabited() {
                 write!(
                     w,
                     " (<a href=\"https://doc.rust-lang.org/stable/reference/glossary.html#uninhabited\">uninhabited</a>)"
-                );
+                ).unwrap();
             }
         }
     }
 
-    if !cx.shared.show_type_layout {
-        return;
-    }
-
-    writeln!(
-        w,
-        "<h2 id=\"layout\" class=\"small-section-header\"> \
-        Layout<a href=\"#layout\" class=\"anchor\">§</a></h2>"
-    );
-    writeln!(w, "<div class=\"docblock\">");
-
-    let tcx = cx.tcx();
-    let param_env = tcx.param_env(ty_def_id);
-    let ty = tcx.type_of(ty_def_id).subst_identity();
-    match tcx.layout_of(param_env.and(ty)) {
-        Ok(ty_layout) => {
-            writeln!(
-                w,
-                "<div class=\"warning\"><p><strong>Note:</strong> Most layout information is \
-                 <strong>completely unstable</strong> and may even differ between compilations. \
-                 The only exception is types with certain <code>repr(...)</code> attributes. \
-                 Please see the Rust Reference’s \
-                 <a href=\"https://doc.rust-lang.org/reference/type-layout.html\">“Type Layout”</a> \
-                 chapter for details on type layout guarantees.</p></div>"
-            );
-            w.write_str("<p><strong>Size:</strong> ");
-            write_size_of_layout(w, &ty_layout.layout.0, 0);
-            writeln!(w, "</p>");
-            if let Variants::Multiple { variants, tag, tag_encoding, .. } =
-                &ty_layout.layout.variants()
-            {
-                if !variants.is_empty() {
-                    w.write_str(
-                        "<p><strong>Size for each variant:</strong></p>\
-                            <ul>",
-                    );
-
-                    let Adt(adt, _) = ty_layout.ty.kind() else {
-                        span_bug!(tcx.def_span(ty_def_id), "not an adt")
-                    };
+    display_fn(move |mut f| {
+        if !cx.shared.show_type_layout {
+            return Ok(());
+        }
 
-                    let tag_size = if let TagEncoding::Niche { .. } = tag_encoding {
-                        0
-                    } else if let Primitive::Int(i, _) = tag.primitive() {
-                        i.size().bytes()
-                    } else {
-                        span_bug!(tcx.def_span(ty_def_id), "tag is neither niche nor int")
-                    };
+        writeln!(
+            f,
+            "<h2 id=\"layout\" class=\"small-section-header\"> \
+            Layout<a href=\"#layout\" class=\"anchor\">§</a></h2>"
+        )?;
+        writeln!(f, "<div class=\"docblock\">")?;
 
-                    for (index, layout) in variants.iter_enumerated() {
-                        let name = adt.variant(index).name;
-                        write!(w, "<li><code>{name}</code>: ");
-                        write_size_of_layout(w, layout, tag_size);
-                        writeln!(w, "</li>");
+        let tcx = cx.tcx();
+        let param_env = tcx.param_env(ty_def_id);
+        let ty = tcx.type_of(ty_def_id).subst_identity();
+        match tcx.layout_of(param_env.and(ty)) {
+            Ok(ty_layout) => {
+                writeln!(
+                    f,
+                    "<div class=\"warning\"><p><strong>Note:</strong> Most layout information is \
+                    <strong>completely unstable</strong> and may even differ between compilations. \
+                    The only exception is types with certain <code>repr(...)</code> attributes. \
+                    Please see the Rust Reference’s \
+                    <a href=\"https://doc.rust-lang.org/reference/type-layout.html\">“Type Layout”</a> \
+                    chapter for details on type layout guarantees.</p></div>"
+                )?;
+                f.write_str("<p><strong>Size:</strong> ")?;
+                write_size_of_layout(&mut f, &ty_layout.layout.0, 0);
+                writeln!(f, "</p>")?;
+                if let Variants::Multiple { variants, tag, tag_encoding, .. } =
+                    &ty_layout.layout.variants()
+                {
+                    if !variants.is_empty() {
+                        f.write_str(
+                            "<p><strong>Size for each variant:</strong></p>\
+                                <ul>",
+                        )?;
+
+                        let Adt(adt, _) = ty_layout.ty.kind() else {
+                            span_bug!(tcx.def_span(ty_def_id), "not an adt")
+                        };
+
+                        let tag_size = if let TagEncoding::Niche { .. } = tag_encoding {
+                            0
+                        } else if let Primitive::Int(i, _) = tag.primitive() {
+                            i.size().bytes()
+                        } else {
+                            span_bug!(tcx.def_span(ty_def_id), "tag is neither niche nor int")
+                        };
+
+                        for (index, layout) in variants.iter_enumerated() {
+                            let name = adt.variant(index).name;
+                            write!(&mut f, "<li><code>{name}</code>: ")?;
+                            write_size_of_layout(&mut f, layout, tag_size);
+                            writeln!(&mut f, "</li>")?;
+                        }
+                        f.write_str("</ul>")?;
                     }
-                    w.write_str("</ul>");
                 }
             }
+            // This kind of layout error can occur with valid code, e.g. if you try to
+            // get the layout of a generic type such as `Vec<T>`.
+            Err(LayoutError::Unknown(_)) => {
+                writeln!(
+                    f,
+                    "<p><strong>Note:</strong> Unable to compute type layout, \
+                    possibly due to this type having generic parameters. \
+                    Layout can only be computed for concrete, fully-instantiated types.</p>"
+                )?;
+            }
+            // This kind of error probably can't happen with valid code, but we don't
+            // want to panic and prevent the docs from building, so we just let the
+            // user know that we couldn't compute the layout.
+            Err(LayoutError::SizeOverflow(_)) => {
+                writeln!(
+                    f,
+                    "<p><strong>Note:</strong> Encountered an error during type layout; \
+                    the type was too big.</p>"
+                )?;
+            }
+            Err(LayoutError::NormalizationFailure(_, _)) => {
+                writeln!(
+                    f,
+                    "<p><strong>Note:</strong> Encountered an error during type layout; \
+                    the type failed to be normalized.</p>"
+                )?;
+            }
         }
-        // This kind of layout error can occur with valid code, e.g. if you try to
-        // get the layout of a generic type such as `Vec<T>`.
-        Err(LayoutError::Unknown(_)) => {
-            writeln!(
-                w,
-                "<p><strong>Note:</strong> Unable to compute type layout, \
-                 possibly due to this type having generic parameters. \
-                 Layout can only be computed for concrete, fully-instantiated types.</p>"
-            );
-        }
-        // This kind of error probably can't happen with valid code, but we don't
-        // want to panic and prevent the docs from building, so we just let the
-        // user know that we couldn't compute the layout.
-        Err(LayoutError::SizeOverflow(_)) => {
-            writeln!(
-                w,
-                "<p><strong>Note:</strong> Encountered an error during type layout; \
-                 the type was too big.</p>"
-            );
-        }
-        Err(LayoutError::NormalizationFailure(_, _)) => {
-            writeln!(
-                w,
-                "<p><strong>Note:</strong> Encountered an error during type layout; \
-                the type failed to be normalized.</p>"
-            )
-        }
-    }
 
-    writeln!(w, "</div>");
+        writeln!(f, "</div>")
+    })
 }
 
 fn pluralize(count: usize) -> &'static str {
diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs
index 1d298f52f75..c8397967c87 100644
--- a/src/librustdoc/html/sources.rs
+++ b/src/librustdoc/html/sources.rs
@@ -2,7 +2,6 @@ use crate::clean;
 use crate::docfs::PathError;
 use crate::error::Error;
 use crate::html::format;
-use crate::html::format::Buffer;
 use crate::html::highlight;
 use crate::html::layout;
 use crate::html::render::Context;
@@ -17,6 +16,7 @@ use rustc_span::source_map::FileName;
 
 use std::cell::RefCell;
 use std::ffi::OsStr;
+use std::fmt;
 use std::fs;
 use std::ops::RangeInclusive;
 use std::path::{Component, Path, PathBuf};
@@ -294,7 +294,7 @@ pub(crate) enum SourceContext {
 /// Wrapper struct to render the source code of a file. This will do things like
 /// adding line numbers to the left-hand side.
 pub(crate) fn print_src(
-    buf: &mut Buffer,
+    mut writer: impl fmt::Write,
     s: &str,
     file_span: rustc_span::Span,
     context: &Context<'_>,
@@ -329,5 +329,5 @@ pub(crate) fn print_src(
         );
         Ok(())
     });
-    Source { embedded, needs_expansion, lines, code_html: code }.render_into(buf).unwrap();
+    Source { embedded, needs_expansion, lines, code_html: code }.render_into(&mut writer).unwrap();
 }
diff --git a/src/librustdoc/html/templates/item_union.html b/src/librustdoc/html/templates/item_union.html
new file mode 100644
index 00000000000..a01457971c1
--- /dev/null
+++ b/src/librustdoc/html/templates/item_union.html
@@ -0,0 +1,23 @@
+<pre class="rust item-decl"><code>
+    {{ self.render_attributes_in_pre() | safe }}
+    {{ self.render_union() | safe }}
+</code></pre>
+{{ self.document() | safe }}
+{% if self.fields_iter().peek().is_some() %}
+    <h2 id="fields" class="fields small-section-header">
+        Fields<a href="#fields" class="anchor">§</a>
+    </h2>
+    {% for (field, ty) in self.fields_iter() %}
+        {% let name = field.name.expect("union field name") %}
+        <span id="structfield.{{ name }}" class="{{ ItemType::StructField }} small-section-header">
+            <a href="#structfield.{{ name }}" class="anchor field">§</a>
+            <code>{{ name }}: {{ self.print_ty(ty) | safe }}</code>
+        </span>
+        {% if let Some(stability_class) = self.stability_field(field) %}
+            <span class="stab {{ stability_class }}"></span>
+        {% endif %}
+        {{ self.document_field(field) | safe }}
+    {% endfor %}
+{% endif %}
+{{ self.render_assoc_items() | safe }}
+{{ self.document_type_layout() | safe }}
diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs
index c959bb3701a..393d51fe090 100644
--- a/src/librustdoc/visit_ast.rs
+++ b/src/librustdoc/visit_ast.rs
@@ -13,9 +13,9 @@ use rustc_span::def_id::{CRATE_DEF_ID, LOCAL_CRATE};
 use rustc_span::symbol::{kw, sym, Symbol};
 use rustc_span::Span;
 
-use std::mem;
+use std::{iter, mem};
 
-use crate::clean::{cfg::Cfg, AttributesExt, NestedAttributesExt, OneLevelVisitor};
+use crate::clean::{cfg::Cfg, reexport_chain, AttributesExt, NestedAttributesExt};
 use crate::core;
 
 /// This module is used to store stuff from Rust's AST in a more convenient
@@ -133,7 +133,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
         // is declared but also a reexport of itself producing two exports of the same
         // macro in the same module.
         let mut inserted = FxHashSet::default();
-        for export in self.cx.tcx.module_reexports(CRATE_DEF_ID).unwrap_or(&[]) {
+        for export in self.cx.tcx.module_reexports(CRATE_DEF_ID) {
             if let Res::Def(DefKind::Macro(_), def_id) = export.res &&
                 let Some(local_def_id) = def_id.as_local() &&
                 self.cx.tcx.has_attr(def_id, sym::macro_export) &&
@@ -220,7 +220,6 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
         renamed: Option<Symbol>,
         glob: bool,
         please_inline: bool,
-        path: &hir::UsePath<'_>,
     ) -> bool {
         debug!("maybe_inline_local res: {:?}", res);
 
@@ -266,9 +265,9 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
         }
 
         if !please_inline &&
-            let mut visitor = OneLevelVisitor::new(self.cx.tcx.hir(), res_did) &&
-            let Some(item) = visitor.find_target(self.cx.tcx, def_id.to_def_id(), path) &&
-            let item_def_id = item.owner_id.def_id &&
+            let Some(item_def_id) = reexport_chain(self.cx.tcx, def_id, res_did).iter()
+                .flat_map(|reexport| reexport.id()).map(|id| id.expect_local())
+                .chain(iter::once(res_did)).nth(1) &&
             item_def_id != def_id &&
             self
                 .cx
@@ -383,7 +382,6 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
                             ident,
                             is_glob,
                             please_inline,
-                            path,
                         ) {
                             continue;
                         }
diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
index 327e090d38b..0bb1775aae9 100644
--- a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
@@ -122,7 +122,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
 
         let sized_trait = need!(cx.tcx.lang_items().sized_trait());
 
-        let preds = traits::elaborate_predicates(cx.tcx, cx.param_env.caller_bounds().iter())
+        let preds = traits::elaborate(cx.tcx, cx.param_env.caller_bounds().iter())
             .filter(|p| !p.is_global())
             .filter_map(|pred| {
                 // Note that we do not want to deal with qualified predicates here.
diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs
index 619aa9f4bf6..9051cf51658 100644
--- a/src/tools/clippy/clippy_utils/src/lib.rs
+++ b/src/tools/clippy/clippy_utils/src/lib.rs
@@ -2104,7 +2104,7 @@ pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_>, did: DefId) -> bool {
         .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
     traits::impossible_predicates(
         cx.tcx,
-        traits::elaborate_predicates(cx.tcx, predicates)
+        traits::elaborate(cx.tcx, predicates)
             .collect::<Vec<_>>(),
     )
 }
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index a35284f05b5..e55c82c4b63 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -2135,7 +2135,7 @@ impl<'test> TestCx<'test> {
             if let Some(ref p) = self.config.nodejs {
                 args.push(p.clone());
             } else {
-                self.fatal("no NodeJS binary found (--nodejs)");
+                self.fatal("emscripten target requested and no NodeJS binary found (--nodejs)");
             }
         // If this is otherwise wasm, then run tests under nodejs with our
         // shim
@@ -2143,7 +2143,7 @@ impl<'test> TestCx<'test> {
             if let Some(ref p) = self.config.nodejs {
                 args.push(p.clone());
             } else {
-                self.fatal("no NodeJS binary found (--nodejs)");
+                self.fatal("wasm32 target requested and no NodeJS binary found (--nodejs)");
             }
 
             let src = self
@@ -2999,6 +2999,7 @@ impl<'test> TestCx<'test> {
             || host.contains("freebsd")
             || host.contains("netbsd")
             || host.contains("openbsd")
+            || host.contains("aix")
         {
             "gmake"
         } else {
diff --git a/tests/codegen/intrinsics/transmute-x64.rs b/tests/codegen/intrinsics/transmute-x64.rs
new file mode 100644
index 00000000000..99d258c6204
--- /dev/null
+++ b/tests/codegen/intrinsics/transmute-x64.rs
@@ -0,0 +1,35 @@
+// compile-flags: -O -C no-prepopulate-passes
+// only-x86_64 (it's using arch-specific types)
+// min-llvm-version: 15.0 # this test assumes `ptr`s
+
+#![crate_type = "lib"]
+
+use std::arch::x86_64::{__m128, __m128i, __m256i};
+use std::mem::transmute;
+
+// CHECK-LABEL: @check_sse_float_to_int(
+#[no_mangle]
+pub unsafe fn check_sse_float_to_int(x: __m128) -> __m128i {
+    // CHECK-NOT: alloca
+    // CHECK: %1 = load <4 x float>, ptr %x, align 16
+    // CHECK: store <4 x float> %1, ptr %0, align 16
+    transmute(x)
+}
+
+// CHECK-LABEL: @check_sse_pair_to_avx(
+#[no_mangle]
+pub unsafe fn check_sse_pair_to_avx(x: (__m128i, __m128i)) -> __m256i {
+    // CHECK-NOT: alloca
+    // CHECK: %1 = load <4 x i64>, ptr %x, align 16
+    // CHECK: store <4 x i64> %1, ptr %0, align 32
+    transmute(x)
+}
+
+// CHECK-LABEL: @check_sse_pair_from_avx(
+#[no_mangle]
+pub unsafe fn check_sse_pair_from_avx(x: __m256i) -> (__m128i, __m128i) {
+    // CHECK-NOT: alloca
+    // CHECK: %1 = load <4 x i64>, ptr %x, align 32
+    // CHECK: store <4 x i64> %1, ptr %0, align 16
+    transmute(x)
+}
diff --git a/tests/codegen/intrinsics/transmute.rs b/tests/codegen/intrinsics/transmute.rs
index 7ad0e62213c..57f901c6719 100644
--- a/tests/codegen/intrinsics/transmute.rs
+++ b/tests/codegen/intrinsics/transmute.rs
@@ -8,7 +8,7 @@
 #![feature(inline_const)]
 #![allow(unreachable_code)]
 
-use std::mem::transmute;
+use std::mem::{transmute, MaybeUninit};
 
 // Some of the cases here are statically rejected by `mem::transmute`, so
 // we need to generate custom MIR for those cases to get to codegen.
@@ -54,6 +54,32 @@ pub unsafe fn check_smaller_size(x: u32) -> u16 {
     }
 }
 
+// CHECK-LABEL: @check_smaller_array(
+#[no_mangle]
+#[custom_mir(dialect = "runtime", phase = "initial")]
+pub unsafe fn check_smaller_array(x: [u32; 7]) -> [u32; 3] {
+    // CHECK: call void @llvm.trap
+    mir!{
+        {
+            RET = CastTransmute(x);
+            Return()
+        }
+    }
+}
+
+// CHECK-LABEL: @check_bigger_array(
+#[no_mangle]
+#[custom_mir(dialect = "runtime", phase = "initial")]
+pub unsafe fn check_bigger_array(x: [u32; 3]) -> [u32; 7] {
+    // CHECK: call void @llvm.trap
+    mir!{
+        {
+            RET = CastTransmute(x);
+            Return()
+        }
+    }
+}
+
 // CHECK-LABEL: @check_to_uninhabited(
 #[no_mangle]
 #[custom_mir(dialect = "runtime", phase = "initial")]
@@ -71,7 +97,7 @@ pub unsafe fn check_to_uninhabited(x: u16) -> BigNever {
 #[no_mangle]
 #[custom_mir(dialect = "runtime", phase = "initial")]
 pub unsafe fn check_from_uninhabited(x: BigNever) -> u16 {
-    // CHECK: call void @llvm.trap
+    // CHECK: ret i16 poison
     mir!{
         {
             RET = CastTransmute(x);
@@ -301,3 +327,105 @@ pub unsafe fn check_pair_to_array(x: (i64, u64)) -> [u8; 16] {
     // CHECK: store i64 %x.1, ptr %{{.+}}, align 1
     transmute(x)
 }
+
+// CHECK-LABEL: @check_heterogeneous_integer_pair(
+#[no_mangle]
+pub unsafe fn check_heterogeneous_integer_pair(x: (i32, bool)) -> (bool, u32) {
+    // CHECK: store i32 %x.0
+    // CHECK: %[[WIDER:.+]] = zext i1 %x.1 to i8
+    // CHECK: store i8 %[[WIDER]]
+
+    // CHECK: %[[BYTE:.+]] = load i8
+    // CHECK: trunc i8 %[[BYTE:.+]] to i1
+    // CHECK: load i32
+    transmute(x)
+}
+
+// CHECK-LABEL: @check_heterogeneous_float_pair(
+#[no_mangle]
+pub unsafe fn check_heterogeneous_float_pair(x: (f64, f32)) -> (f32, f64) {
+    // CHECK: store double %x.0
+    // CHECK: store float %x.1
+    // CHECK: %[[A:.+]] = load float
+    // CHECK: %[[B:.+]] = load double
+    // CHECK: %[[P:.+]] = insertvalue { float, double } poison, float %[[A]], 0
+    // CHECK: insertvalue { float, double } %[[P]], double %[[B]], 1
+    transmute(x)
+}
+
+// CHECK-LABEL: @check_issue_110005(
+#[no_mangle]
+pub unsafe fn check_issue_110005(x: (usize, bool)) -> Option<Box<[u8]>> {
+    // CHECK: store i64 %x.0
+    // CHECK: %[[WIDER:.+]] = zext i1 %x.1 to i8
+    // CHECK: store i8 %[[WIDER]]
+    // CHECK: load ptr
+    // CHECK: load i64
+    transmute(x)
+}
+
+// CHECK-LABEL: @check_pair_to_dst_ref(
+#[no_mangle]
+pub unsafe fn check_pair_to_dst_ref<'a>(x: (usize, usize)) -> &'a [u8] {
+    // CHECK: %0 = inttoptr i64 %x.0 to ptr
+    // CHECK: %1 = insertvalue { ptr, i64 } poison, ptr %0, 0
+    // CHECK: %2 = insertvalue { ptr, i64 } %1, i64 %x.1, 1
+    // CHECK: ret { ptr, i64 } %2
+    transmute(x)
+}
+
+// CHECK-LABEL: @check_issue_109992(
+#[no_mangle]
+#[custom_mir(dialect = "runtime", phase = "optimized")]
+pub unsafe fn check_issue_109992(x: ()) -> [(); 1] {
+    // This uses custom MIR to avoid MIR optimizations having removed ZST ops.
+
+    // CHECK: start
+    // CHECK-NEXT: ret void
+    mir!{
+        {
+            RET = CastTransmute(x);
+            Return()
+        }
+    }
+}
+
+// CHECK-LABEL: @check_maybe_uninit_pair(i16 %x.0, i64 %x.1)
+#[no_mangle]
+pub unsafe fn check_maybe_uninit_pair(
+    x: (MaybeUninit<u16>, MaybeUninit<u64>),
+) -> (MaybeUninit<i64>, MaybeUninit<i16>) {
+    // Thanks to `MaybeUninit` this is actually defined behaviour,
+    // unlike the examples above with pairs of primitives.
+
+    // CHECK: store i16 %x.0
+    // CHECK: store i64 %x.1
+    // CHECK: load i64
+    // CHECK-NOT: noundef
+    // CHECK: load i16
+    // CHECK-NOT: noundef
+    // CHECK: ret { i64, i16 }
+    transmute(x)
+}
+
+#[repr(align(8))]
+pub struct HighAlignScalar(u8);
+
+// CHECK-LABEL: @check_to_overalign(
+#[no_mangle]
+pub unsafe fn check_to_overalign(x: u64) -> HighAlignScalar {
+    // CHECK: %0 = alloca %HighAlignScalar, align 8
+    // CHECK: store i64 %x, ptr %0, align 8
+    // CHECK: %1 = load i64, ptr %0, align 8
+    // CHECK: ret i64 %1
+    transmute(x)
+}
+
+// CHECK-LABEL: @check_from_overalign(
+#[no_mangle]
+pub unsafe fn check_from_overalign(x: HighAlignScalar) -> u64 {
+    // CHECK: %x = alloca %HighAlignScalar, align 8
+    // CHECK: %[[VAL:.+]] = load i64, ptr %x, align 8
+    // CHECK: ret i64 %[[VAL]]
+    transmute(x)
+}
diff --git a/tests/ui/const-generics/transmute-fail.rs b/tests/ui/const-generics/transmute-fail.rs
new file mode 100644
index 00000000000..d7bf1b47fb5
--- /dev/null
+++ b/tests/ui/const-generics/transmute-fail.rs
@@ -0,0 +1,35 @@
+#![feature(transmute_generic_consts)]
+#![feature(generic_const_exprs)]
+#![allow(incomplete_features)]
+
+fn foo<const W: usize, const H: usize>(v: [[u32;H+1]; W]) -> [[u32; W+1]; H] {
+  unsafe {
+    std::mem::transmute(v)
+    //~^ ERROR cannot transmute
+  }
+}
+
+fn bar<const W: bool, const H: usize>(v: [[u32; H]; W]) -> [[u32; W]; H] {
+  //~^ ERROR mismatched types
+  //~| ERROR mismatched types
+  unsafe {
+    std::mem::transmute(v)
+    //~^ ERROR cannot transmute between types
+  }
+}
+
+fn baz<const W: usize, const H: usize>(v: [[u32; H]; W]) -> [u32; W * H * H] {
+  unsafe {
+    std::mem::transmute(v)
+    //~^ ERROR cannot transmute
+  }
+}
+
+fn overflow(v: [[[u32; 8888888]; 9999999]; 777777777]) -> [[[u32; 9999999]; 777777777]; 8888888] {
+  unsafe {
+    std::mem::transmute(v)
+    //~^ ERROR cannot transmute
+  }
+}
+
+fn main() {}
diff --git a/tests/ui/const-generics/transmute-fail.stderr b/tests/ui/const-generics/transmute-fail.stderr
new file mode 100644
index 00000000000..41b098135e8
--- /dev/null
+++ b/tests/ui/const-generics/transmute-fail.stderr
@@ -0,0 +1,52 @@
+error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
+  --> $DIR/transmute-fail.rs:7:5
+   |
+LL |     std::mem::transmute(v)
+   |     ^^^^^^^^^^^^^^^^^^^
+   |
+   = note: source type: `[[u32; H+1]; W]` (generic size [const expr])
+   = note: target type: `[[u32; W+1]; H]` (generic size [const expr])
+
+error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
+  --> $DIR/transmute-fail.rs:16:5
+   |
+LL |     std::mem::transmute(v)
+   |     ^^^^^^^^^^^^^^^^^^^
+   |
+   = note: source type: `[[u32; H]; W]` (this type does not have a fixed size)
+   = note: target type: `[[u32; W]; H]` (size can vary because of [u32; W])
+
+error[E0308]: mismatched types
+  --> $DIR/transmute-fail.rs:12:53
+   |
+LL | fn bar<const W: bool, const H: usize>(v: [[u32; H]; W]) -> [[u32; W]; H] {
+   |                                                     ^ expected `usize`, found `bool`
+
+error[E0308]: mismatched types
+  --> $DIR/transmute-fail.rs:12:67
+   |
+LL | fn bar<const W: bool, const H: usize>(v: [[u32; H]; W]) -> [[u32; W]; H] {
+   |                                                                   ^ expected `usize`, found `bool`
+
+error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
+  --> $DIR/transmute-fail.rs:23:5
+   |
+LL |     std::mem::transmute(v)
+   |     ^^^^^^^^^^^^^^^^^^^
+   |
+   = note: source type: `[[u32; H]; W]` (generic size [const expr])
+   = note: target type: `[u32; W * H * H]` (generic size [const expr])
+
+error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
+  --> $DIR/transmute-fail.rs:30:5
+   |
+LL |     std::mem::transmute(v)
+   |     ^^^^^^^^^^^^^^^^^^^
+   |
+   = note: source type: `[[[u32; 8888888]; 9999999]; 777777777]` (values of the type `[[[u32; 8888888]; 9999999]; 777777777]` are too big for the current architecture)
+   = note: target type: `[[[u32; 9999999]; 777777777]; 8888888]` (values of the type `[[[u32; 9999999]; 777777777]; 8888888]` are too big for the current architecture)
+
+error: aborting due to 6 previous errors
+
+Some errors have detailed explanations: E0308, E0512.
+For more information about an error, try `rustc --explain E0308`.
diff --git a/tests/ui/const-generics/transmute.rs b/tests/ui/const-generics/transmute.rs
new file mode 100644
index 00000000000..30560a95b5e
--- /dev/null
+++ b/tests/ui/const-generics/transmute.rs
@@ -0,0 +1,83 @@
+// run-pass
+#![feature(generic_const_exprs)]
+#![feature(transmute_generic_consts)]
+#![allow(incomplete_features)]
+
+fn transpose<const W: usize, const H: usize>(v: [[u32;H]; W]) -> [[u32; W]; H] {
+  unsafe {
+    std::mem::transmute(v)
+  }
+}
+
+fn ident<const W: usize, const H: usize>(v: [[u32; H]; W]) -> [[u32; H]; W] {
+  unsafe {
+    std::mem::transmute(v)
+  }
+}
+
+fn flatten<const W: usize, const H: usize>(v: [[u32; H]; W]) -> [u32; W * H] {
+  unsafe {
+    std::mem::transmute(v)
+  }
+}
+
+fn coagulate<const W: usize, const H: usize>(v: [u32; H*W]) -> [[u32; W];H] {
+  unsafe {
+    std::mem::transmute(v)
+  }
+}
+
+fn flatten_3d<const W: usize, const H: usize, const D: usize>(
+  v: [[[u32; D]; H]; W]
+) -> [u32; D * W * H] {
+  unsafe {
+    std::mem::transmute(v)
+  }
+}
+
+fn flatten_somewhat<const W: usize, const H: usize, const D: usize>(
+  v: [[[u32; D]; H]; W]
+) -> [[u32; D * W]; H] {
+  unsafe {
+    std::mem::transmute(v)
+  }
+}
+
+fn known_size<const L: usize>(v: [u16; L]) -> [u8; L * 2] {
+  unsafe {
+    std::mem::transmute(v)
+  }
+}
+
+fn condense_bytes<const L: usize>(v: [u8; L * 2]) -> [u16; L] {
+  unsafe {
+    std::mem::transmute(v)
+  }
+}
+
+fn singleton_each<const L: usize>(v: [u8; L]) -> [[u8;1]; L] {
+  unsafe {
+    std::mem::transmute(v)
+  }
+}
+
+fn transpose_with_const<const W: usize, const H: usize>(
+  v: [[u32; 2 * H]; W + W]
+) -> [[u32; W + W]; 2 * H] {
+  unsafe {
+    std::mem::transmute(v)
+  }
+}
+
+fn main() {
+  let _ = transpose([[0; 8]; 16]);
+  let _ = transpose_with_const::<8,4>([[0; 8]; 16]);
+  let _ = ident([[0; 8]; 16]);
+  let _ = flatten([[0; 13]; 5]);
+  let _: [[_; 5]; 13] = coagulate([0; 65]);
+  let _ = flatten_3d([[[0; 3]; 13]; 5]);
+  let _ = flatten_somewhat([[[0; 3]; 13]; 5]);
+  let _ = known_size([16; 13]);
+  let _: [u16; 5] = condense_bytes([16u8; 10]);
+  let _ = singleton_each([16; 10]);
+}
diff --git a/tests/ui/const-generics/transmute_no_gate.rs b/tests/ui/const-generics/transmute_no_gate.rs
new file mode 100644
index 00000000000..e1ac44390a5
--- /dev/null
+++ b/tests/ui/const-generics/transmute_no_gate.rs
@@ -0,0 +1,91 @@
+// gate-test-transmute_generic_consts
+#![feature(generic_const_exprs)]
+#![allow(incomplete_features)]
+
+fn transpose<const W: usize, const H: usize>(v: [[u32;H]; W]) -> [[u32; W]; H] {
+  unsafe {
+    std::mem::transmute(v)
+    //~^ ERROR cannot transmute
+  }
+}
+
+fn ident<const W: usize, const H: usize>(v: [[u32; H]; W]) -> [[u32; H]; W] {
+  unsafe {
+    std::mem::transmute(v)
+  }
+}
+
+fn flatten<const W: usize, const H: usize>(v: [[u32; H]; W]) -> [u32; W * H] {
+  unsafe {
+    std::mem::transmute(v)
+    //~^ ERROR cannot transmute
+  }
+}
+
+fn coagulate<const W: usize, const H: usize>(v: [u32; H*W]) -> [[u32; W];H] {
+  unsafe {
+    std::mem::transmute(v)
+    //~^ ERROR cannot transmute
+  }
+}
+
+fn flatten_3d<const W: usize, const H: usize, const D: usize>(
+  v: [[[u32; D]; H]; W]
+) -> [u32; D * W * H] {
+  unsafe {
+    std::mem::transmute(v)
+    //~^ ERROR cannot transmute
+  }
+}
+
+fn flatten_somewhat<const W: usize, const H: usize, const D: usize>(
+  v: [[[u32; D]; H]; W]
+) -> [[u32; D * W]; H] {
+  unsafe {
+    std::mem::transmute(v)
+    //~^ ERROR cannot transmute
+  }
+}
+
+fn known_size<const L: usize>(v: [u16; L]) -> [u8; L * 2] {
+  unsafe {
+    std::mem::transmute(v)
+    //~^ ERROR cannot transmute
+  }
+}
+
+fn condense_bytes<const L: usize>(v: [u8; L * 2]) -> [u16; L] {
+  unsafe {
+    std::mem::transmute(v)
+    //~^ ERROR cannot transmute
+  }
+}
+
+fn singleton_each<const L: usize>(v: [u8; L]) -> [[u8;1]; L] {
+  unsafe {
+    std::mem::transmute(v)
+    //~^ ERROR cannot transmute
+  }
+}
+
+fn transpose_with_const<const W: usize, const H: usize>(
+  v: [[u32; 2 * H]; W + W]
+) -> [[u32; W + W]; 2 * H] {
+  unsafe {
+    std::mem::transmute(v)
+    //~^ ERROR cannot transmute
+  }
+}
+
+fn main() {
+  let _ = transpose([[0; 8]; 16]);
+  let _ = transpose_with_const::<8,4>([[0; 8]; 16]);
+  let _ = ident([[0; 8]; 16]);
+  let _ = flatten([[0; 13]; 5]);
+  let _: [[_; 5]; 13] = coagulate([0; 65]);
+  let _ = flatten_3d([[[0; 3]; 13]; 5]);
+  let _ = flatten_somewhat([[[0; 3]; 13]; 5]);
+  let _ = known_size([16; 13]);
+  let _: [u16; 5] = condense_bytes([16u8; 10]);
+  let _ = singleton_each([16; 10]);
+}
diff --git a/tests/ui/const-generics/transmute_no_gate.stderr b/tests/ui/const-generics/transmute_no_gate.stderr
new file mode 100644
index 00000000000..9c271b34849
--- /dev/null
+++ b/tests/ui/const-generics/transmute_no_gate.stderr
@@ -0,0 +1,84 @@
+error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
+  --> $DIR/transmute_no_gate.rs:7:5
+   |
+LL |     std::mem::transmute(v)
+   |     ^^^^^^^^^^^^^^^^^^^
+   |
+   = note: source type: `[[u32; H]; W]` (this type does not have a fixed size)
+   = note: target type: `[[u32; W]; H]` (this type does not have a fixed size)
+
+error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
+  --> $DIR/transmute_no_gate.rs:20:5
+   |
+LL |     std::mem::transmute(v)
+   |     ^^^^^^^^^^^^^^^^^^^
+   |
+   = note: source type: `[[u32; H]; W]` (this type does not have a fixed size)
+   = note: target type: `[u32; W * H]` (this type does not have a fixed size)
+
+error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
+  --> $DIR/transmute_no_gate.rs:27:5
+   |
+LL |     std::mem::transmute(v)
+   |     ^^^^^^^^^^^^^^^^^^^
+   |
+   = note: source type: `[u32; H*W]` (this type does not have a fixed size)
+   = note: target type: `[[u32; W]; H]` (this type does not have a fixed size)
+
+error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
+  --> $DIR/transmute_no_gate.rs:36:5
+   |
+LL |     std::mem::transmute(v)
+   |     ^^^^^^^^^^^^^^^^^^^
+   |
+   = note: source type: `[[[u32; D]; H]; W]` (this type does not have a fixed size)
+   = note: target type: `[u32; D * W * H]` (this type does not have a fixed size)
+
+error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
+  --> $DIR/transmute_no_gate.rs:45:5
+   |
+LL |     std::mem::transmute(v)
+   |     ^^^^^^^^^^^^^^^^^^^
+   |
+   = note: source type: `[[[u32; D]; H]; W]` (this type does not have a fixed size)
+   = note: target type: `[[u32; D * W]; H]` (this type does not have a fixed size)
+
+error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
+  --> $DIR/transmute_no_gate.rs:52:5
+   |
+LL |     std::mem::transmute(v)
+   |     ^^^^^^^^^^^^^^^^^^^
+   |
+   = note: source type: `[u16; L]` (this type does not have a fixed size)
+   = note: target type: `[u8; L * 2]` (this type does not have a fixed size)
+
+error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
+  --> $DIR/transmute_no_gate.rs:59:5
+   |
+LL |     std::mem::transmute(v)
+   |     ^^^^^^^^^^^^^^^^^^^
+   |
+   = note: source type: `[u8; L * 2]` (this type does not have a fixed size)
+   = note: target type: `[u16; L]` (this type does not have a fixed size)
+
+error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
+  --> $DIR/transmute_no_gate.rs:66:5
+   |
+LL |     std::mem::transmute(v)
+   |     ^^^^^^^^^^^^^^^^^^^
+   |
+   = note: source type: `[u8; L]` (this type does not have a fixed size)
+   = note: target type: `[[u8; 1]; L]` (this type does not have a fixed size)
+
+error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
+  --> $DIR/transmute_no_gate.rs:75:5
+   |
+LL |     std::mem::transmute(v)
+   |     ^^^^^^^^^^^^^^^^^^^
+   |
+   = note: source type: `[[u32; 2 * H]; W + W]` (this type does not have a fixed size)
+   = note: target type: `[[u32; W + W]; 2 * H]` (this type does not have a fixed size)
+
+error: aborting due to 9 previous errors
+
+For more information about this error, try `rustc --explain E0512`.
diff --git a/tests/ui/consts/const-fn-error.rs b/tests/ui/consts/const-fn-error.rs
index 50b7ce1f8c0..dabbd58dbe0 100644
--- a/tests/ui/consts/const-fn-error.rs
+++ b/tests/ui/consts/const-fn-error.rs
@@ -7,6 +7,7 @@ const fn f(x: usize) -> usize {
         //~| ERROR `for` is not allowed in a `const fn`
         //~| ERROR mutable references are not allowed in constant functions
         //~| ERROR cannot call non-const fn
+        //~| ERROR the trait bound
         sum += i;
     }
     sum
diff --git a/tests/ui/consts/const-fn-error.stderr b/tests/ui/consts/const-fn-error.stderr
index f735b3d53ce..73d235d6aec 100644
--- a/tests/ui/consts/const-fn-error.stderr
+++ b/tests/ui/consts/const-fn-error.stderr
@@ -5,7 +5,7 @@ LL | /     for i in 0..x {
 LL | |
 LL | |
 LL | |
-LL | |
+...  |
 LL | |         sum += i;
 LL | |     }
    | |_____^
@@ -33,6 +33,19 @@ LL |     for i in 0..x {
    = note: see issue #57349 <https://github.com/rust-lang/rust/issues/57349> for more information
    = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable
 
+error[E0277]: the trait bound `std::ops::Range<usize>: Iterator` is not satisfied
+  --> $DIR/const-fn-error.rs:5:14
+   |
+LL |     for i in 0..x {
+   |              ^^^^ `std::ops::Range<usize>` is not an iterator
+   |
+   = help: the trait `~const Iterator` is not implemented for `std::ops::Range<usize>`
+note: the trait `Iterator` is implemented for `std::ops::Range<usize>`, but that implementation is not `const`
+  --> $DIR/const-fn-error.rs:5:14
+   |
+LL |     for i in 0..x {
+   |              ^^^^
+
 error[E0015]: cannot call non-const fn `<std::ops::Range<usize> as Iterator>::next` in constant functions
   --> $DIR/const-fn-error.rs:5:14
    |
@@ -42,7 +55,7 @@ LL |     for i in 0..x {
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
    = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
 
-error: aborting due to 4 previous errors
+error: aborting due to 5 previous errors
 
-Some errors have detailed explanations: E0015, E0658.
+Some errors have detailed explanations: E0015, E0277, E0658.
 For more information about an error, try `rustc --explain E0015`.
diff --git a/tests/ui/consts/const-for.rs b/tests/ui/consts/const-for.rs
index 8db24853558..ff9c977f794 100644
--- a/tests/ui/consts/const-for.rs
+++ b/tests/ui/consts/const-for.rs
@@ -5,6 +5,7 @@ const _: () = {
     for _ in 0..5 {}
     //~^ error: cannot call
     //~| error: cannot convert
+    //~| error: the trait bound
 };
 
 fn main() {}
diff --git a/tests/ui/consts/const-for.stderr b/tests/ui/consts/const-for.stderr
index 3fb9787c0d8..64f2f603b94 100644
--- a/tests/ui/consts/const-for.stderr
+++ b/tests/ui/consts/const-for.stderr
@@ -9,6 +9,19 @@ note: impl defined here, but it is not `const`
    = note: calls in constants are limited to constant functions, tuple structs and tuple variants
    = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
 
+error[E0277]: the trait bound `std::ops::Range<i32>: Iterator` is not satisfied
+  --> $DIR/const-for.rs:5:14
+   |
+LL |     for _ in 0..5 {}
+   |              ^^^^ `std::ops::Range<i32>` is not an iterator
+   |
+   = help: the trait `~const Iterator` is not implemented for `std::ops::Range<i32>`
+note: the trait `Iterator` is implemented for `std::ops::Range<i32>`, but that implementation is not `const`
+  --> $DIR/const-for.rs:5:14
+   |
+LL |     for _ in 0..5 {}
+   |              ^^^^
+
 error[E0015]: cannot call non-const fn `<std::ops::Range<i32> as Iterator>::next` in constants
   --> $DIR/const-for.rs:5:14
    |
@@ -18,6 +31,7 @@ LL |     for _ in 0..5 {}
    = note: calls in constants are limited to constant functions, tuple structs and tuple variants
    = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
 
-error: aborting due to 2 previous errors
+error: aborting due to 3 previous errors
 
-For more information about this error, try `rustc --explain E0015`.
+Some errors have detailed explanations: E0015, E0277.
+For more information about an error, try `rustc --explain E0015`.
diff --git a/tests/ui/dyn-star/align.normal.stderr b/tests/ui/dyn-star/align.normal.stderr
index 53c2cbeac32..42fa4fd6f00 100644
--- a/tests/ui/dyn-star/align.normal.stderr
+++ b/tests/ui/dyn-star/align.normal.stderr
@@ -1,5 +1,5 @@
 warning: the feature `dyn_star` is incomplete and may not be safe to use and/or cause compiler crashes
-  --> $DIR/align.rs:4:12
+  --> $DIR/align.rs:3:12
    |
 LL | #![feature(dyn_star)]
    |            ^^^^^^^^
@@ -7,5 +7,14 @@ LL | #![feature(dyn_star)]
    = note: see issue #102425 <https://github.com/rust-lang/rust/issues/102425> for more information
    = note: `#[warn(incomplete_features)]` on by default
 
-warning: 1 warning emitted
+error[E0277]: `AlignedUsize` needs to have the same ABI as a pointer
+  --> $DIR/align.rs:14:13
+   |
+LL |     let x = AlignedUsize(12) as dyn* Debug;
+   |             ^^^^^^^^^^^^^^^^ `AlignedUsize` needs to be a pointer-like type
+   |
+   = help: the trait `PointerLike` is not implemented for `AlignedUsize`
+
+error: aborting due to previous error; 1 warning emitted
 
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/dyn-star/align.over_aligned.stderr b/tests/ui/dyn-star/align.over_aligned.stderr
index 0365d87a6f8..42fa4fd6f00 100644
--- a/tests/ui/dyn-star/align.over_aligned.stderr
+++ b/tests/ui/dyn-star/align.over_aligned.stderr
@@ -1,5 +1,5 @@
 warning: the feature `dyn_star` is incomplete and may not be safe to use and/or cause compiler crashes
-  --> $DIR/align.rs:4:12
+  --> $DIR/align.rs:3:12
    |
 LL | #![feature(dyn_star)]
    |            ^^^^^^^^
@@ -7,8 +7,8 @@ LL | #![feature(dyn_star)]
    = note: see issue #102425 <https://github.com/rust-lang/rust/issues/102425> for more information
    = note: `#[warn(incomplete_features)]` on by default
 
-error[E0277]: `AlignedUsize` needs to have the same alignment and size as a pointer
-  --> $DIR/align.rs:15:13
+error[E0277]: `AlignedUsize` needs to have the same ABI as a pointer
+  --> $DIR/align.rs:14:13
    |
 LL |     let x = AlignedUsize(12) as dyn* Debug;
    |             ^^^^^^^^^^^^^^^^ `AlignedUsize` needs to be a pointer-like type
diff --git a/tests/ui/dyn-star/align.rs b/tests/ui/dyn-star/align.rs
index 6679997a940..79cbaba0c78 100644
--- a/tests/ui/dyn-star/align.rs
+++ b/tests/ui/dyn-star/align.rs
@@ -1,5 +1,4 @@
 // revisions: normal over_aligned
-//[normal] check-pass
 
 #![feature(dyn_star)]
 //~^ WARN the feature `dyn_star` is incomplete and may not be safe to use and/or cause compiler crashes
@@ -13,5 +12,5 @@ struct AlignedUsize(usize);
 
 fn main() {
     let x = AlignedUsize(12) as dyn* Debug;
-    //[over_aligned]~^ ERROR `AlignedUsize` needs to have the same alignment and size as a pointer
+    //~^ ERROR `AlignedUsize` needs to have the same ABI as a pointer
 }
diff --git a/tests/ui/dyn-star/check-size-at-cast-polymorphic-bad.rs b/tests/ui/dyn-star/check-size-at-cast-polymorphic-bad.rs
index 85749aa7b00..913c2faacbd 100644
--- a/tests/ui/dyn-star/check-size-at-cast-polymorphic-bad.rs
+++ b/tests/ui/dyn-star/check-size-at-cast-polymorphic-bad.rs
@@ -9,7 +9,7 @@ fn dyn_debug(_: (dyn* Debug + '_)) {
 
 fn polymorphic<T: Debug + ?Sized>(t: &T) {
     dyn_debug(t);
-    //~^ ERROR `&T` needs to have the same alignment and size as a pointer
+    //~^ ERROR `&T` needs to have the same ABI as a pointer
 }
 
 fn main() {}
diff --git a/tests/ui/dyn-star/check-size-at-cast-polymorphic-bad.stderr b/tests/ui/dyn-star/check-size-at-cast-polymorphic-bad.stderr
index 350630f7941..8726fae79a0 100644
--- a/tests/ui/dyn-star/check-size-at-cast-polymorphic-bad.stderr
+++ b/tests/ui/dyn-star/check-size-at-cast-polymorphic-bad.stderr
@@ -1,4 +1,4 @@
-error[E0277]: `&T` needs to have the same alignment and size as a pointer
+error[E0277]: `&T` needs to have the same ABI as a pointer
   --> $DIR/check-size-at-cast-polymorphic-bad.rs:11:15
    |
 LL |     dyn_debug(t);
diff --git a/tests/ui/dyn-star/check-size-at-cast.rs b/tests/ui/dyn-star/check-size-at-cast.rs
index 17bc4f303bf..e15e090b529 100644
--- a/tests/ui/dyn-star/check-size-at-cast.rs
+++ b/tests/ui/dyn-star/check-size-at-cast.rs
@@ -5,6 +5,6 @@ use std::fmt::Debug;
 
 fn main() {
     let i = [1, 2, 3, 4] as dyn* Debug;
-    //~^ ERROR `[i32; 4]` needs to have the same alignment and size as a pointer
+    //~^ ERROR `[i32; 4]` needs to have the same ABI as a pointer
     dbg!(i);
 }
diff --git a/tests/ui/dyn-star/check-size-at-cast.stderr b/tests/ui/dyn-star/check-size-at-cast.stderr
index 19700b40644..e60b5c56ff0 100644
--- a/tests/ui/dyn-star/check-size-at-cast.stderr
+++ b/tests/ui/dyn-star/check-size-at-cast.stderr
@@ -1,4 +1,4 @@
-error[E0277]: `[i32; 4]` needs to have the same alignment and size as a pointer
+error[E0277]: `[i32; 4]` needs to have the same ABI as a pointer
   --> $DIR/check-size-at-cast.rs:7:13
    |
 LL |     let i = [1, 2, 3, 4] as dyn* Debug;
diff --git a/tests/ui/dyn-star/upcast.stderr b/tests/ui/dyn-star/upcast.stderr
index e60144fea74..8b34c7f8b71 100644
--- a/tests/ui/dyn-star/upcast.stderr
+++ b/tests/ui/dyn-star/upcast.stderr
@@ -7,7 +7,7 @@ LL | #![feature(dyn_star, trait_upcasting)]
    = note: see issue #102425 <https://github.com/rust-lang/rust/issues/102425> for more information
    = note: `#[warn(incomplete_features)]` on by default
 
-error[E0277]: `dyn* Foo` needs to have the same alignment and size as a pointer
+error[E0277]: `dyn* Foo` needs to have the same ABI as a pointer
   --> $DIR/upcast.rs:30:23
    |
 LL |     let w: dyn* Bar = w;
diff --git a/tests/ui/higher-rank-trait-bounds/issue-95230.new.stderr b/tests/ui/higher-rank-trait-bounds/issue-95230.new.stderr
new file mode 100644
index 00000000000..bcb201bf0c3
--- /dev/null
+++ b/tests/ui/higher-rank-trait-bounds/issue-95230.new.stderr
@@ -0,0 +1,18 @@
+error[E0282]: type annotations needed
+  --> $DIR/issue-95230.rs:9:13
+   |
+LL |     for<'a> &'a mut Self:;
+   |             ^^^^^^^^^^^^ cannot infer type for mutable reference `&'a mut Bar`
+   |
+note: required by a bound in `Bar`
+  --> $DIR/issue-95230.rs:9:13
+   |
+LL | pub struct Bar
+   |            --- required by a bound in this struct
+LL | where
+LL |     for<'a> &'a mut Self:;
+   |             ^^^^^^^^^^^^ required by this bound in `Bar`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0282`.
diff --git a/tests/ui/higher-rank-trait-bounds/issue-95230.rs b/tests/ui/higher-rank-trait-bounds/issue-95230.rs
index 92c506eabb7..769b6a92537 100644
--- a/tests/ui/higher-rank-trait-bounds/issue-95230.rs
+++ b/tests/ui/higher-rank-trait-bounds/issue-95230.rs
@@ -1,4 +1,8 @@
-// check-pass
+// revisions: old new
+//[new] compile-flags: -Ztrait-solver=next
+//[old] check-pass
+//[new] known-bug: #109764
+
 
 pub struct Bar
 where
diff --git a/tests/ui/index_message.rs b/tests/ui/index_message.rs
index 87e0cde5919..88b848d6f85 100644
--- a/tests/ui/index_message.rs
+++ b/tests/ui/index_message.rs
@@ -1,4 +1,4 @@
 fn main() {
-    let z = ();
-    let _ = z[0]; //~ ERROR cannot index into a value of type `()`
+    let z = (10,);
+    let _ = z[0]; //~ ERROR cannot index into a value of type `({integer},)`
 }
diff --git a/tests/ui/index_message.stderr b/tests/ui/index_message.stderr
index 6c2b126734b..56d1d70809d 100644
--- a/tests/ui/index_message.stderr
+++ b/tests/ui/index_message.stderr
@@ -1,4 +1,4 @@
-error[E0608]: cannot index into a value of type `()`
+error[E0608]: cannot index into a value of type `({integer},)`
   --> $DIR/index_message.rs:3:13
    |
 LL |     let _ = z[0];
diff --git a/tests/ui/issues/issue-27842.rs b/tests/ui/issues/issue-27842.rs
index 3bcfa133070..060d3b34e09 100644
--- a/tests/ui/issues/issue-27842.rs
+++ b/tests/ui/issues/issue-27842.rs
@@ -8,4 +8,9 @@ fn main() {
     let i = 0_usize;
     let _ = tup[i];
     //~^ ERROR cannot index into a value of type
+
+    // the case where the index is out of bounds
+    let tup = (10,);
+    let _ = tup[3];
+    //~^ ERROR cannot index into a value of type
 }
diff --git a/tests/ui/issues/issue-27842.stderr b/tests/ui/issues/issue-27842.stderr
index 784666a639e..83333aa0c47 100644
--- a/tests/ui/issues/issue-27842.stderr
+++ b/tests/ui/issues/issue-27842.stderr
@@ -8,10 +8,20 @@ error[E0608]: cannot index into a value of type `({integer}, {integer}, {integer
   --> $DIR/issue-27842.rs:9:13
    |
 LL |     let _ = tup[i];
+   |             ^^^^-^
+   |                 |
+   |                 cannot access tuple elements at a variable index
+   |
+   = help: to access tuple elements, use tuple indexing syntax (e.g., `tuple.0`)
+
+error[E0608]: cannot index into a value of type `({integer},)`
+  --> $DIR/issue-27842.rs:14:13
+   |
+LL |     let _ = tup[3];
    |             ^^^^^^
    |
    = help: to access tuple elements, use tuple indexing syntax (e.g., `tuple.0`)
 
-error: aborting due to 2 previous errors
+error: aborting due to 3 previous errors
 
 For more information about this error, try `rustc --explain E0608`.
diff --git a/tests/ui/missing/missing-items/missing-const-parameter.rs b/tests/ui/missing/missing-items/missing-const-parameter.rs
new file mode 100644
index 00000000000..a3af88f2633
--- /dev/null
+++ b/tests/ui/missing/missing-items/missing-const-parameter.rs
@@ -0,0 +1,24 @@
+struct Struct<const N: usize>;
+
+impl Struct<{ N }> {}
+//~^ ERROR cannot find value `N` in this scope
+//~| HELP you might be missing a const parameter
+
+fn func0(_: Struct<{ N }>) {}
+//~^ ERROR cannot find value `N` in this scope
+//~| HELP you might be missing a const parameter
+
+fn func1(_: [u8; N]) {}
+//~^ ERROR cannot find value `N` in this scope
+//~| HELP you might be missing a const parameter
+
+fn func2<T>(_: [T; N]) {}
+//~^ ERROR cannot find value `N` in this scope
+//~| HELP you might be missing a const parameter
+
+struct Image<const R: usize>([[u32; C]; R]);
+//~^ ERROR cannot find value `C` in this scope
+//~| HELP a const parameter with a similar name exists
+//~| HELP you might be missing a const parameter
+
+fn main() {}
diff --git a/tests/ui/missing/missing-items/missing-const-parameter.stderr b/tests/ui/missing/missing-items/missing-const-parameter.stderr
new file mode 100644
index 00000000000..d9fea130651
--- /dev/null
+++ b/tests/ui/missing/missing-items/missing-const-parameter.stderr
@@ -0,0 +1,64 @@
+error[E0425]: cannot find value `N` in this scope
+  --> $DIR/missing-const-parameter.rs:3:15
+   |
+LL | impl Struct<{ N }> {}
+   |               ^ not found in this scope
+   |
+help: you might be missing a const parameter
+   |
+LL | impl<const N: /* Type */> Struct<{ N }> {}
+   |     +++++++++++++++++++++
+
+error[E0425]: cannot find value `N` in this scope
+  --> $DIR/missing-const-parameter.rs:7:22
+   |
+LL | fn func0(_: Struct<{ N }>) {}
+   |                      ^ not found in this scope
+   |
+help: you might be missing a const parameter
+   |
+LL | fn func0<const N: /* Type */>(_: Struct<{ N }>) {}
+   |         +++++++++++++++++++++
+
+error[E0425]: cannot find value `N` in this scope
+  --> $DIR/missing-const-parameter.rs:11:18
+   |
+LL | fn func1(_: [u8; N]) {}
+   |                  ^ not found in this scope
+   |
+help: you might be missing a const parameter
+   |
+LL | fn func1<const N: /* Type */>(_: [u8; N]) {}
+   |         +++++++++++++++++++++
+
+error[E0425]: cannot find value `N` in this scope
+  --> $DIR/missing-const-parameter.rs:15:20
+   |
+LL | fn func2<T>(_: [T; N]) {}
+   |                    ^ not found in this scope
+   |
+help: you might be missing a const parameter
+   |
+LL | fn func2<T, const N: /* Type */>(_: [T; N]) {}
+   |           +++++++++++++++++++++
+
+error[E0425]: cannot find value `C` in this scope
+  --> $DIR/missing-const-parameter.rs:19:37
+   |
+LL | struct Image<const R: usize>([[u32; C]; R]);
+   |                    -                ^
+   |                    |
+   |                    similarly named const parameter `R` defined here
+   |
+help: a const parameter with a similar name exists
+   |
+LL | struct Image<const R: usize>([[u32; R]; R]);
+   |                                     ~
+help: you might be missing a const parameter
+   |
+LL | struct Image<const R: usize, const C: /* Type */>([[u32; C]; R]);
+   |                            +++++++++++++++++++++
+
+error: aborting due to 5 previous errors
+
+For more information about this error, try `rustc --explain E0425`.
diff --git a/tests/ui/simd/monomorphize-heterogeneous.rs b/tests/ui/simd/monomorphize-heterogeneous.rs
new file mode 100644
index 00000000000..42e380dbb77
--- /dev/null
+++ b/tests/ui/simd/monomorphize-heterogeneous.rs
@@ -0,0 +1,9 @@
+#![feature(repr_simd)]
+
+#[repr(simd)]
+struct I64F64(i64, f64);
+//~^ ERROR SIMD vector should be homogeneous
+
+static X: I64F64 = I64F64(1, 2.0);
+
+fn main() {}
diff --git a/tests/ui/simd/monomorphize-heterogeneous.stderr b/tests/ui/simd/monomorphize-heterogeneous.stderr
new file mode 100644
index 00000000000..e7b41cd787c
--- /dev/null
+++ b/tests/ui/simd/monomorphize-heterogeneous.stderr
@@ -0,0 +1,9 @@
+error[E0076]: SIMD vector should be homogeneous
+  --> $DIR/monomorphize-heterogeneous.rs:4:1
+   |
+LL | struct I64F64(i64, f64);
+   | ^^^^^^^^^^^^^ SIMD elements must have the same type
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0076`.
diff --git a/tests/ui/structs/struct-tuple-field-names.rs b/tests/ui/structs/struct-tuple-field-names.rs
index 7bd54af1dbe..33f264aa250 100644
--- a/tests/ui/structs/struct-tuple-field-names.rs
+++ b/tests/ui/structs/struct-tuple-field-names.rs
@@ -12,4 +12,7 @@ fn main() {
     match y {
         S { } => {} //~ ERROR: tuple variant `S` written as struct variant [E0769]
     }
+
+    if let E::S { 0: a } = x { //~ ERROR: pattern does not mention field `1`
+    }
 }
diff --git a/tests/ui/structs/struct-tuple-field-names.stderr b/tests/ui/structs/struct-tuple-field-names.stderr
index 5494c29a6fd..0b837a47a82 100644
--- a/tests/ui/structs/struct-tuple-field-names.stderr
+++ b/tests/ui/structs/struct-tuple-field-names.stderr
@@ -20,6 +20,22 @@ help: use the tuple variant pattern syntax instead
 LL |         S(_, _) => {}
    |          ~~~~~~
 
-error: aborting due to 2 previous errors
+error[E0027]: pattern does not mention field `1`
+  --> $DIR/struct-tuple-field-names.rs:16:12
+   |
+LL |     if let E::S { 0: a } = x {
+   |            ^^^^^^^^^^^^^ missing field `1`
+   |
+help: include the missing field in the pattern
+   |
+LL |     if let E::S { 0: a, 1: _ } = x {
+   |                       ~~~~~~~~
+help: if you don't care about this missing field, you can explicitly ignore it
+   |
+LL |     if let E::S { 0: a, .. } = x {
+   |                       ~~~~~~
+
+error: aborting due to 3 previous errors
 
-For more information about this error, try `rustc --explain E0769`.
+Some errors have detailed explanations: E0027, E0769.
+For more information about an error, try `rustc --explain E0027`.
diff --git a/tests/ui/traits/new-solver/alias_eq_dont_use_normalizes_to_if_substs_eq.rs b/tests/ui/traits/new-solver/alias_eq_dont_use_normalizes_to_if_substs_eq.rs
index fd5d0e3b194..531203d9c64 100644
--- a/tests/ui/traits/new-solver/alias_eq_dont_use_normalizes_to_if_substs_eq.rs
+++ b/tests/ui/traits/new-solver/alias_eq_dont_use_normalizes_to_if_substs_eq.rs
@@ -1,7 +1,7 @@
 // compile-flags: -Ztrait-solver=next
 
 // check that when computing `alias-eq(<() as Foo<u16, T>>::Assoc, <() as Foo<?0, T>>::Assoc)`
-//  we do not infer `?0 = u8` via the `for<STOP> (): Foo<u8, STOP>` impl or `?0 = u16` by
+// we do not infer `?0 = u8` via the `for<STOP> (): Foo<u8, STOP>` impl or `?0 = u16` by
 // relating substs as either could be a valid solution.
 
 trait Foo<T, STOP> {
diff --git a/tests/ui/traits/new-solver/pointer-like.rs b/tests/ui/traits/new-solver/pointer-like.rs
index 3745a075e6a..98630176976 100644
--- a/tests/ui/traits/new-solver/pointer-like.rs
+++ b/tests/ui/traits/new-solver/pointer-like.rs
@@ -9,6 +9,6 @@ fn require_(_: impl PointerLike) {}
 fn main() {
     require_(1usize);
     require_(1u16);
-    //~^ ERROR `u16` needs to have the same alignment and size as a pointer
+    //~^ ERROR `u16` needs to have the same ABI as a pointer
     require_(&1i16);
 }
diff --git a/tests/ui/traits/new-solver/pointer-like.stderr b/tests/ui/traits/new-solver/pointer-like.stderr
index f695e64187d..215a81cc265 100644
--- a/tests/ui/traits/new-solver/pointer-like.stderr
+++ b/tests/ui/traits/new-solver/pointer-like.stderr
@@ -1,4 +1,4 @@
-error[E0277]: `u16` needs to have the same alignment and size as a pointer
+error[E0277]: `u16` needs to have the same ABI as a pointer
   --> $DIR/pointer-like.rs:11:14
    |
 LL |     require_(1u16);
diff --git a/tests/ui/traits/new-solver/prefer-candidate-no-constraints.rs b/tests/ui/traits/new-solver/prefer-candidate-no-constraints.rs
new file mode 100644
index 00000000000..6f8164f3a40
--- /dev/null
+++ b/tests/ui/traits/new-solver/prefer-candidate-no-constraints.rs
@@ -0,0 +1,22 @@
+// compile-flags: -Ztrait-solver=next
+// check-pass
+
+trait Foo {}
+
+impl<T> Foo for T {}
+
+trait Bar {}
+
+struct Wrapper<'a, T>(&'a T);
+
+impl<'a, T> Bar for Wrapper<'a, T> where &'a T: Foo {}
+// We need to satisfy `&'a T: Foo` when checking that this impl is WF
+// that can either be satisfied via the param-env, or via an impl.
+//
+// When satisfied via the param-env, since each lifetime is canonicalized
+// separately, we end up getting extra region constraints.
+//
+// However, when satisfied via the impl, there are no region constraints,
+// and we can short-circuit a response with no external constraints.
+
+fn main() {}
diff --git a/tests/ui/traits/new-solver/prefer-param-env-on-ambiguity.rs b/tests/ui/traits/new-solver/prefer-param-env-on-ambiguity.rs
new file mode 100644
index 00000000000..909b33ec3d5
--- /dev/null
+++ b/tests/ui/traits/new-solver/prefer-param-env-on-ambiguity.rs
@@ -0,0 +1,10 @@
+// compile-flags: -Ztrait-solver=next
+// check-pass
+
+trait Foo<'a> {}
+trait Bar<'a> {}
+
+impl<'a, T: Bar<'a>> Foo<'a> for T {}
+impl<T> Bar<'static> for T {}
+
+fn main() {}
diff --git a/tests/ui/traits/new-solver/recursive-self-normalization-2.stderr b/tests/ui/traits/new-solver/recursive-self-normalization-2.stderr
index 29cfa47a105..e3a92e85e17 100644
--- a/tests/ui/traits/new-solver/recursive-self-normalization-2.stderr
+++ b/tests/ui/traits/new-solver/recursive-self-normalization-2.stderr
@@ -1,9 +1,16 @@
-error[E0282]: type annotations needed
+error[E0283]: type annotations needed: cannot satisfy `<T as Foo1>::Assoc1: Bar`
   --> $DIR/recursive-self-normalization-2.rs:15:5
    |
 LL |     needs_bar::<T::Assoc1>();
-   |     ^^^^^^^^^^^^^^^^^^^^^^ cannot infer type of the type parameter `S` declared on the function `needs_bar`
+   |     ^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: cannot satisfy `<T as Foo1>::Assoc1: Bar`
+note: required by a bound in `needs_bar`
+  --> $DIR/recursive-self-normalization-2.rs:12:17
+   |
+LL | fn needs_bar<S: Bar>() {}
+   |                 ^^^ required by this bound in `needs_bar`
 
 error: aborting due to previous error
 
-For more information about this error, try `rustc --explain E0282`.
+For more information about this error, try `rustc --explain E0283`.
diff --git a/tests/ui/traits/new-solver/recursive-self-normalization.stderr b/tests/ui/traits/new-solver/recursive-self-normalization.stderr
index ba39981893d..773007aebaa 100644
--- a/tests/ui/traits/new-solver/recursive-self-normalization.stderr
+++ b/tests/ui/traits/new-solver/recursive-self-normalization.stderr
@@ -1,9 +1,16 @@
-error[E0282]: type annotations needed
+error[E0283]: type annotations needed: cannot satisfy `<T as Foo>::Assoc: Bar`
   --> $DIR/recursive-self-normalization.rs:11:5
    |
 LL |     needs_bar::<T::Assoc>();
-   |     ^^^^^^^^^^^^^^^^^^^^^ cannot infer type of the type parameter `S` declared on the function `needs_bar`
+   |     ^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: cannot satisfy `<T as Foo>::Assoc: Bar`
+note: required by a bound in `needs_bar`
+  --> $DIR/recursive-self-normalization.rs:8:17
+   |
+LL | fn needs_bar<S: Bar>() {}
+   |                 ^^^ required by this bound in `needs_bar`
 
 error: aborting due to previous error
 
-For more information about this error, try `rustc --explain E0282`.
+For more information about this error, try `rustc --explain E0283`.
diff --git a/tests/ui/traits/non_lifetime_binders/method-probe.rs b/tests/ui/traits/non_lifetime_binders/method-probe.rs
new file mode 100644
index 00000000000..8df240c2082
--- /dev/null
+++ b/tests/ui/traits/non_lifetime_binders/method-probe.rs
@@ -0,0 +1,16 @@
+// check-pass
+
+#![feature(non_lifetime_binders)]
+//~^ WARN the feature `non_lifetime_binders` is incomplete
+
+trait Foo: for<T> Bar<T> {}
+
+trait Bar<T> {
+    fn method() -> T;
+}
+
+fn x<T: Foo>() {
+    let _: i32 = T::method();
+}
+
+fn main() {}
diff --git a/tests/ui/traits/non_lifetime_binders/method-probe.stderr b/tests/ui/traits/non_lifetime_binders/method-probe.stderr
new file mode 100644
index 00000000000..8f61792e6ce
--- /dev/null
+++ b/tests/ui/traits/non_lifetime_binders/method-probe.stderr
@@ -0,0 +1,11 @@
+warning: the feature `non_lifetime_binders` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/method-probe.rs:3:12
+   |
+LL | #![feature(non_lifetime_binders)]
+   |            ^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #108185 <https://github.com/rust-lang/rust/issues/108185> for more information
+   = note: `#[warn(incomplete_features)]` on by default
+
+warning: 1 warning emitted
+
diff --git a/tests/ui/typeck/typeck_type_placeholder_item.rs b/tests/ui/typeck/typeck_type_placeholder_item.rs
index e6f7dc410b6..a450dbb82d1 100644
--- a/tests/ui/typeck/typeck_type_placeholder_item.rs
+++ b/tests/ui/typeck/typeck_type_placeholder_item.rs
@@ -228,4 +228,5 @@ fn evens_squared(n: usize) -> _ {
 
 const _: _ = (1..10).filter(|x| x % 2 == 0).map(|x| x * x);
 //~^ ERROR the trait bound
+//~| ERROR the trait bound
 //~| ERROR the placeholder
diff --git a/tests/ui/typeck/typeck_type_placeholder_item.stderr b/tests/ui/typeck/typeck_type_placeholder_item.stderr
index 9144ab9e3a6..bc6c9fd0779 100644
--- a/tests/ui/typeck/typeck_type_placeholder_item.stderr
+++ b/tests/ui/typeck/typeck_type_placeholder_item.stderr
@@ -437,6 +437,19 @@ LL | fn evens_squared(n: usize) -> _ {
    |                               not allowed in type signatures
    |                               help: replace with an appropriate return type: `impl Iterator<Item = usize>`
 
+error[E0277]: the trait bound `std::ops::Range<{integer}>: Iterator` is not satisfied
+  --> $DIR/typeck_type_placeholder_item.rs:229:22
+   |
+LL | const _: _ = (1..10).filter(|x| x % 2 == 0).map(|x| x * x);
+   |                      ^^^^^^ `std::ops::Range<{integer}>` is not an iterator
+   |
+   = help: the trait `~const Iterator` is not implemented for `std::ops::Range<{integer}>`
+note: the trait `Iterator` is implemented for `std::ops::Range<{integer}>`, but that implementation is not `const`
+  --> $DIR/typeck_type_placeholder_item.rs:229:14
+   |
+LL | const _: _ = (1..10).filter(|x| x % 2 == 0).map(|x| x * x);
+   |              ^^^^^^^
+
 error[E0277]: the trait bound `Filter<std::ops::Range<{integer}>, [closure@$DIR/typeck_type_placeholder_item.rs:229:29: 229:32]>: Iterator` is not satisfied
   --> $DIR/typeck_type_placeholder_item.rs:229:45
    |
@@ -664,7 +677,7 @@ LL |     const D: _ = 42;
    |              not allowed in type signatures
    |              help: replace with the correct type: `i32`
 
-error: aborting due to 72 previous errors
+error: aborting due to 73 previous errors
 
 Some errors have detailed explanations: E0121, E0277, E0282, E0403.
 For more information about an error, try `rustc --explain E0121`.