about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_abi/src/lib.rs8
-rw-r--r--compiler/rustc_ast/src/ast.rs3
-rw-r--r--compiler/rustc_ast/src/util/parser.rs45
-rw-r--r--compiler/rustc_ast_lowering/messages.ftl3
-rw-r--r--compiler/rustc_ast_lowering/src/delegation.rs107
-rw-r--r--compiler/rustc_ast_lowering/src/errors.rs8
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs20
-rw-r--r--compiler/rustc_ast_passes/src/ast_validation.rs2
-rw-r--r--compiler/rustc_ast_passes/src/errors.rs2
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state/expr.rs14
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/region_errors.rs6
-rw-r--r--compiler/rustc_codegen_ssa/src/back/link.rs7
-rw-r--r--compiler/rustc_data_structures/src/stable_hasher.rs71
-rw-r--r--compiler/rustc_hir/src/hir_id.rs8
-rw-r--r--compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs8
-rw-r--r--compiler/rustc_hir_analysis/src/check/mod.rs17
-rw-r--r--compiler/rustc_hir_pretty/src/lib.rs8
-rw-r--r--compiler/rustc_hir_typeck/src/callee.rs4
-rw-r--r--compiler/rustc_hir_typeck/src/cast.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs8
-rw-r--r--compiler/rustc_hir_typeck/src/method/suggest.rs102
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/suggest.rs6
-rw-r--r--compiler/rustc_middle/src/mir/tcx.rs14
-rw-r--r--compiler/rustc_middle/src/ty/sty.rs28
-rw-r--r--compiler/rustc_mir_build/src/build/matches/mod.rs40
-rw-r--r--compiler/rustc_mir_build/src/build/matches/util.rs2
-rw-r--r--compiler/rustc_mir_transform/src/cost_checker.rs29
-rw-r--r--compiler/rustc_mir_transform/src/gvn.rs114
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/alias_relate.rs6
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs85
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs112
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs22
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/inspect/build.rs4
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/mod.rs12
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs18
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs199
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs6
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/normalizes_to/weak_types.rs10
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/project_goals.rs6
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/search_graph.rs60
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/trait_goals.rs140
-rw-r--r--compiler/rustc_parse/src/parser/ty.rs2
-rw-r--r--compiler/rustc_passes/src/dead.rs66
-rw-r--r--compiler/rustc_passes/src/reachable.rs12
-rw-r--r--compiler/rustc_query_system/src/dep_graph/dep_node.rs5
-rw-r--r--compiler/rustc_resolve/src/effective_visibilities.rs2
-rw-r--r--compiler/rustc_resolve/src/imports.rs74
-rw-r--r--compiler/rustc_resolve/src/late.rs2
-rw-r--r--compiler/rustc_resolve/src/lib.rs14
-rw-r--r--compiler/rustc_session/src/config.rs6
-rw-r--r--compiler/rustc_span/src/def_id.rs6
-rw-r--r--library/alloc/src/sync/tests.rs2
-rw-r--r--library/core/src/default.rs1
-rw-r--r--library/core/src/ffi/mod.rs9
-rw-r--r--library/core/src/intrinsics.rs2
-rw-r--r--library/core/src/iter/adapters/filter.rs90
-rw-r--r--library/core/src/num/f128.rs674
-rw-r--r--library/core/src/num/f16.rs646
-rw-r--r--library/core/src/num/f32.rs55
-rw-r--r--library/core/src/num/f64.rs61
-rw-r--r--library/core/tests/iter/adapters/filter.rs13
-rw-r--r--library/std/build.rs62
-rw-r--r--library/std/src/f128.rs30
-rw-r--r--library/std/src/f128/tests.rs527
-rw-r--r--library/std/src/f16.rs29
-rw-r--r--library/std/src/f16/tests.rs526
-rw-r--r--library/std/src/f32/tests.rs82
-rw-r--r--library/std/src/f64/tests.rs83
-rw-r--r--library/std/src/ffi/os_str.rs16
-rw-r--r--library/std/src/macros.rs11
-rw-r--r--library/std/src/path.rs31
-rw-r--r--library/std/src/sys/os_str/bytes.rs16
-rw-r--r--library/std/src/sys/os_str/wtf8.rs16
-rw-r--r--library/std/src/sys_common/wtf8.rs12
-rw-r--r--src/bootstrap/src/core/build_steps/tool.rs15
-rw-r--r--src/ci/docker/host-x86_64/disabled/riscv64gc-gnu/Dockerfile4
m---------src/tools/cargo0
-rw-r--r--src/tools/clippy/clippy_lints/src/dereference.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/manual_utils.rs4
-rw-r--r--src/tools/miri/tests/pass/tls/win_tls_callback.rs16
-rw-r--r--src/tools/miri/tests/pass/tls/win_tls_callback.stderr1
-rw-r--r--src/tools/tidy/config/requirements.in4
-rw-r--r--src/tools/tidy/config/requirements.txt91
-rw-r--r--src/tools/tidy/config/ruff.toml23
-rw-r--r--src/tools/tidy/src/allowed_run_make_makefiles.txt3
-rw-r--r--src/tools/tidy/src/ext_tool_checks.rs3
-rw-r--r--tests/coverage/closure_macro.cov-map8
-rw-r--r--tests/coverage/closure_macro_async.cov-map8
-rw-r--r--tests/mir-opt/gvn.cast_pointer_eq.GVN.panic-abort.diff134
-rw-r--r--tests/mir-opt/gvn.cast_pointer_eq.GVN.panic-unwind.diff134
-rw-r--r--tests/mir-opt/gvn.cast_pointer_then_transmute.GVN.panic-abort.diff47
-rw-r--r--tests/mir-opt/gvn.cast_pointer_then_transmute.GVN.panic-unwind.diff47
-rw-r--r--tests/mir-opt/gvn.rs45
-rw-r--r--tests/mir-opt/lower_array_len.array_bound.GVN.panic-abort.diff24
-rw-r--r--tests/mir-opt/lower_array_len.array_bound.GVN.panic-unwind.diff24
-rw-r--r--tests/mir-opt/lower_array_len.array_bound_mut.GVN.panic-abort.diff24
-rw-r--r--tests/mir-opt/lower_array_len.array_bound_mut.GVN.panic-unwind.diff24
-rw-r--r--tests/mir-opt/lower_array_len.rs12
-rw-r--r--tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir231
-rw-r--r--tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir237
-rw-r--r--tests/mir-opt/pre-codegen/slice_iter.rs7
-rw-r--r--tests/mir-opt/pre-codegen/slice_iter.slice_iter_generic_is_empty.PreCodegen.after.panic-abort.mir76
-rw-r--r--tests/mir-opt/pre-codegen/slice_iter.slice_iter_generic_is_empty.PreCodegen.after.panic-unwind.mir76
-rw-r--r--tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-abort.mir195
-rw-r--r--tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-unwind.mir195
-rw-r--r--tests/run-make/invalid-so/Makefile7
-rw-r--r--tests/run-make/invalid-so/rmake.rs17
-rw-r--r--tests/run-make/issue-20626/Makefile9
-rw-r--r--tests/run-make/lto-empty/Makefile13
-rw-r--r--tests/run-make/lto-empty/rmake.rs17
-rw-r--r--tests/run-make/print-native-static-libs/bar.rs6
-rw-r--r--tests/run-make/raw-fn-pointer-opt-undefined-behavior/foo.rs (renamed from tests/run-make/issue-20626/foo.rs)0
-rw-r--r--tests/run-make/raw-fn-pointer-opt-undefined-behavior/rmake.rs16
-rw-r--r--tests/ui/array-slice-vec/vec-res-add.stderr5
-rw-r--r--tests/ui/async-await/suggest-missing-await.rs7
-rw-r--r--tests/ui/async-await/suggest-missing-await.stderr14
-rw-r--r--tests/ui/autoderef-full-lval.stderr12
-rw-r--r--tests/ui/binop/binary-op-not-allowed-issue-125631.rs16
-rw-r--r--tests/ui/binop/binary-op-not-allowed-issue-125631.stderr75
-rw-r--r--tests/ui/binop/binop-bitxor-str.stderr5
-rw-r--r--tests/ui/const-generics/cross_crate_complex.rs1
-rw-r--r--tests/ui/delegation/explicit-paths.stderr8
-rw-r--r--tests/ui/deriving/deriving-default-enum.rs2
-rw-r--r--tests/ui/error-codes/E0067.stderr6
-rw-r--r--tests/ui/generic-associated-types/missing-bounds.fixed4
-rw-r--r--tests/ui/generic-associated-types/missing-bounds.rs4
-rw-r--r--tests/ui/generic-associated-types/missing-bounds.stderr18
-rw-r--r--tests/ui/impl-trait/in-trait/refine-resolution-errors.rs23
-rw-r--r--tests/ui/impl-trait/in-trait/refine-resolution-errors.stderr16
-rw-r--r--tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.rs1
-rw-r--r--tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.stderr10
-rw-r--r--tests/ui/impl-trait/precise-capturing/redundant.normal.stderr20
-rw-r--r--tests/ui/impl-trait/precise-capturing/redundant.rpitit.stderr18
-rw-r--r--tests/ui/impl-trait/precise-capturing/redundant.rs13
-rw-r--r--tests/ui/impl-trait/precise-capturing/redundant.stderr36
-rw-r--r--tests/ui/impl-trait/precise-capturing/rpitit.rs21
-rw-r--r--tests/ui/impl-trait/precise-capturing/rpitit.stderr50
-rw-r--r--tests/ui/impl-trait/precise-capturing/self-capture.rs3
-rw-r--r--tests/ui/impl-trait/precise-capturing/self-capture.stderr10
-rw-r--r--tests/ui/issues/issue-14915.stderr6
-rw-r--r--tests/ui/issues/issue-68696-catch-during-unwind.rs1
-rw-r--r--tests/ui/lint/dead-code/unused-struct-derive-default.rs25
-rw-r--r--tests/ui/lint/dead-code/unused-struct-derive-default.stderr24
-rw-r--r--tests/ui/lint/dead-code/unused-trait-with-assoc-ty.rs11
-rw-r--r--tests/ui/lint/dead-code/unused-trait-with-assoc-ty.stderr20
-rw-r--r--tests/ui/minus-string.stderr5
-rw-r--r--tests/ui/parser/fn-header-semantic-fail.stderr16
-rw-r--r--tests/ui/parser/no-const-fn-in-extern-block.stderr8
-rw-r--r--tests/ui/parser/unsafe-foreign-mod-2.stderr8
-rw-r--r--tests/ui/pattern/issue-22546.rs2
-rw-r--r--tests/ui/pattern/issue-22546.stderr10
-rw-r--r--tests/ui/pattern/pattern-tyvar-2.stderr5
-rw-r--r--tests/ui/regions/account-for-lifetimes-in-closure-suggestion.rs19
-rw-r--r--tests/ui/regions/account-for-lifetimes-in-closure-suggestion.stderr17
-rw-r--r--tests/ui/regions/lifetime-not-long-enough-suggestion-regression-test-124563.rs (renamed from tests/crashes/124563.rs)11
-rw-r--r--tests/ui/regions/lifetime-not-long-enough-suggestion-regression-test-124563.stderr49
-rw-r--r--tests/ui/regions/regions-escape-method.fixed17
-rw-r--r--tests/ui/regions/regions-escape-method.rs1
-rw-r--r--tests/ui/regions/regions-escape-method.stderr7
-rw-r--r--tests/ui/rust-2024/safe-outside-extern.gated.stderr2
-rw-r--r--tests/ui/rust-2024/safe-outside-extern.ungated.stderr2
-rw-r--r--tests/ui/rust-2024/unsafe-extern-blocks/safe-unsafe-on-unadorned-extern-block.edition2021.stderr16
-rw-r--r--tests/ui/rust-2024/unsafe-extern-blocks/safe-unsafe-on-unadorned-extern-block.edition2024.stderr16
-rw-r--r--tests/ui/rust-2024/unsafe-extern-blocks/unsafe-on-extern-block-issue-126756.fixed10
-rw-r--r--tests/ui/rust-2024/unsafe-extern-blocks/unsafe-on-extern-block-issue-126756.rs10
-rw-r--r--tests/ui/rust-2024/unsafe-extern-blocks/unsafe-on-extern-block-issue-126756.stderr13
-rw-r--r--tests/ui/suggestions/missing-impl-trait-block-but-not-ascii.rs13
-rw-r--r--tests/ui/suggestions/missing-impl-trait-block-but-not-ascii.stderr31
-rw-r--r--tests/ui/typeck/assign-non-lval-derefmut.stderr8
169 files changed, 5922 insertions, 1211 deletions
diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs
index a6662d4e0e4..31c66a56bea 100644
--- a/compiler/rustc_abi/src/lib.rs
+++ b/compiler/rustc_abi/src/lib.rs
@@ -427,11 +427,13 @@ pub struct Size {
     raw: u64,
 }
 
-// Safety: Ord is implement as just comparing numerical values and numerical values
-// are not changed by (de-)serialization.
 #[cfg(feature = "nightly")]
-unsafe impl StableOrd for Size {
+impl StableOrd for Size {
     const CAN_USE_UNSTABLE_SORT: bool = true;
+
+    // `Ord` is implemented as just comparing numerical values and numerical values
+    // are not changed by (de-)serialization.
+    const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();
 }
 
 // This is debug-printed a lot in larger structs, don't waste too much space there
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 30c54ef2d3c..4a3ce0e0c30 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -2126,7 +2126,8 @@ pub struct BareFnTy {
     pub ext: Extern,
     pub generic_params: ThinVec<GenericParam>,
     pub decl: P<FnDecl>,
-    /// Span of the `fn(...) -> ...` part.
+    /// Span of the `[unsafe] [extern] fn(...) -> ...` part, i.e. everything
+    /// after the generic params (if there are any, e.g. `for<'a>`).
     pub decl_span: Span,
 }
 
diff --git a/compiler/rustc_ast/src/util/parser.rs b/compiler/rustc_ast/src/util/parser.rs
index 373c0ebcc5c..ad92bf2cd40 100644
--- a/compiler/rustc_ast/src/util/parser.rs
+++ b/compiler/rustc_ast/src/util/parser.rs
@@ -233,8 +233,7 @@ pub const PREC_JUMP: i8 = -30;
 pub const PREC_RANGE: i8 = -10;
 // The range 2..=14 is reserved for AssocOp binary operator precedences.
 pub const PREC_PREFIX: i8 = 50;
-pub const PREC_POSTFIX: i8 = 60;
-pub const PREC_PAREN: i8 = 99;
+pub const PREC_UNAMBIGUOUS: i8 = 60;
 pub const PREC_FORCE_PAREN: i8 = 100;
 
 #[derive(Debug, Clone, Copy)]
@@ -325,37 +324,35 @@ impl ExprPrecedence {
             | ExprPrecedence::Let
             | ExprPrecedence::Unary => PREC_PREFIX,
 
-            // Unary, postfix
-            ExprPrecedence::Await
+            // Never need parens
+            ExprPrecedence::Array
+            | ExprPrecedence::Await
+            | ExprPrecedence::Block
             | ExprPrecedence::Call
-            | ExprPrecedence::MethodCall
+            | ExprPrecedence::ConstBlock
             | ExprPrecedence::Field
+            | ExprPrecedence::ForLoop
+            | ExprPrecedence::FormatArgs
+            | ExprPrecedence::Gen
+            | ExprPrecedence::If
             | ExprPrecedence::Index
-            | ExprPrecedence::Try
             | ExprPrecedence::InlineAsm
+            | ExprPrecedence::Lit
+            | ExprPrecedence::Loop
             | ExprPrecedence::Mac
-            | ExprPrecedence::FormatArgs
+            | ExprPrecedence::Match
+            | ExprPrecedence::MethodCall
             | ExprPrecedence::OffsetOf
-            | ExprPrecedence::PostfixMatch => PREC_POSTFIX,
-
-            // Never need parens
-            ExprPrecedence::Array
+            | ExprPrecedence::Paren
+            | ExprPrecedence::Path
+            | ExprPrecedence::PostfixMatch
             | ExprPrecedence::Repeat
+            | ExprPrecedence::Struct
+            | ExprPrecedence::Try
+            | ExprPrecedence::TryBlock
             | ExprPrecedence::Tup
-            | ExprPrecedence::Lit
-            | ExprPrecedence::Path
-            | ExprPrecedence::Paren
-            | ExprPrecedence::If
             | ExprPrecedence::While
-            | ExprPrecedence::ForLoop
-            | ExprPrecedence::Loop
-            | ExprPrecedence::Match
-            | ExprPrecedence::ConstBlock
-            | ExprPrecedence::Block
-            | ExprPrecedence::TryBlock
-            | ExprPrecedence::Gen
-            | ExprPrecedence::Struct
-            | ExprPrecedence::Err => PREC_PAREN,
+            | ExprPrecedence::Err => PREC_UNAMBIGUOUS,
         }
     }
 }
diff --git a/compiler/rustc_ast_lowering/messages.ftl b/compiler/rustc_ast_lowering/messages.ftl
index 52164d6ef16..58f65f1257f 100644
--- a/compiler/rustc_ast_lowering/messages.ftl
+++ b/compiler/rustc_ast_lowering/messages.ftl
@@ -130,6 +130,9 @@ ast_lowering_never_pattern_with_guard =
 
 ast_lowering_no_precise_captures_on_apit = `use<...>` precise capturing syntax not allowed in argument-position `impl Trait`
 
+ast_lowering_no_precise_captures_on_rpitit = `use<...>` precise capturing syntax is currently not allowed in return-position `impl Trait` in traits
+    .note = currently, return-position `impl Trait` in traits and trait implementations capture all lifetimes in scope
+
 ast_lowering_previously_used_here = previously used here
 
 ast_lowering_register1 = register `{$reg1_name}`
diff --git a/compiler/rustc_ast_lowering/src/delegation.rs b/compiler/rustc_ast_lowering/src/delegation.rs
index d9dd0b3bca5..678cac210f4 100644
--- a/compiler/rustc_ast_lowering/src/delegation.rs
+++ b/compiler/rustc_ast_lowering/src/delegation.rs
@@ -66,14 +66,18 @@ impl<'hir> LoweringContext<'_, 'hir> {
         let Ok(sig_id) = sig_id else {
             return false;
         };
-        if let Some(local_sig_id) = sig_id.as_local() {
+        self.has_self(sig_id, span)
+    }
+
+    fn has_self(&self, def_id: DefId, span: Span) -> bool {
+        if let Some(local_sig_id) = def_id.as_local() {
             // The value may be missing due to recursive delegation.
             // Error will be emmited later during HIR ty lowering.
             self.resolver.delegation_fn_sigs.get(&local_sig_id).map_or(false, |sig| sig.has_self)
         } else {
-            match self.tcx.def_kind(sig_id) {
+            match self.tcx.def_kind(def_id) {
                 DefKind::Fn => false,
-                DefKind::AssocFn => self.tcx.associated_item(sig_id).fn_has_self_parameter,
+                DefKind::AssocFn => self.tcx.associated_item(def_id).fn_has_self_parameter,
                 _ => span_bug!(span, "unexpected DefKind for delegation item"),
             }
         }
@@ -107,12 +111,17 @@ impl<'hir> LoweringContext<'_, 'hir> {
         span: Span,
     ) -> Result<DefId, ErrorGuaranteed> {
         let sig_id = if self.is_in_trait_impl { item_id } else { path_id };
-        let sig_id =
-            self.resolver.get_partial_res(sig_id).and_then(|r| r.expect_full_res().opt_def_id());
-        sig_id.ok_or_else(|| {
-            self.tcx
-                .dcx()
-                .span_delayed_bug(span, "LoweringContext: couldn't resolve delegation item")
+        self.get_resolution_id(sig_id, span)
+    }
+
+    fn get_resolution_id(&self, node_id: NodeId, span: Span) -> Result<DefId, ErrorGuaranteed> {
+        let def_id =
+            self.resolver.get_partial_res(node_id).and_then(|r| r.expect_full_res().opt_def_id());
+        def_id.ok_or_else(|| {
+            self.tcx.dcx().span_delayed_bug(
+                span,
+                format!("LoweringContext: couldn't resolve node {:?} in delegation item", node_id),
+            )
         })
     }
 
@@ -122,7 +131,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
             predicates: &[],
             has_where_clause_predicates: false,
             where_clause_span: span,
-            span: span,
+            span,
         })
     }
 
@@ -222,12 +231,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
         }));
 
         let path = self.arena.alloc(hir::Path { span, res: Res::Local(param_id), segments });
-
-        hir::Expr {
-            hir_id: self.next_id(),
-            kind: hir::ExprKind::Path(hir::QPath::Resolved(None, path)),
-            span,
-        }
+        self.mk_expr(hir::ExprKind::Path(hir::QPath::Resolved(None, path)), span)
     }
 
     fn lower_delegation_body(
@@ -236,19 +240,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
         param_count: usize,
         span: Span,
     ) -> BodyId {
-        let path = self.lower_qpath(
-            delegation.id,
-            &delegation.qself,
-            &delegation.path,
-            ParamMode::Optional,
-            ImplTraitContext::Disallowed(ImplTraitPosition::Path),
-            None,
-        );
         let block = delegation.body.as_deref();
 
         self.lower_body(|this| {
-            let mut parameters: Vec<hir::Param<'_>> = Vec::new();
-            let mut args: Vec<hir::Expr<'hir>> = Vec::new();
+            let mut parameters: Vec<hir::Param<'_>> = Vec::with_capacity(param_count);
+            let mut args: Vec<hir::Expr<'_>> = Vec::with_capacity(param_count);
 
             for idx in 0..param_count {
                 let (param, pat_node_id) = this.generate_param(span);
@@ -264,11 +260,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     };
                     self_resolver.visit_block(block);
                     let block = this.lower_block(block, false);
-                    hir::Expr {
-                        hir_id: this.next_id(),
-                        kind: hir::ExprKind::Block(block, None),
-                        span: block.span,
-                    }
+                    this.mk_expr(hir::ExprKind::Block(block, None), block.span)
                 } else {
                     let pat_hir_id = this.lower_node_id(pat_node_id);
                     this.generate_arg(pat_hir_id, span)
@@ -276,43 +268,41 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 args.push(arg);
             }
 
-            let args = self.arena.alloc_from_iter(args);
-            let final_expr = this.generate_call(path, args);
+            let final_expr = this.finalize_body_lowering(delegation, args, span);
             (this.arena.alloc_from_iter(parameters), final_expr)
         })
     }
 
-    fn generate_call(
+    // Generates fully qualified call for the resulting body.
+    fn finalize_body_lowering(
         &mut self,
-        path: hir::QPath<'hir>,
-        args: &'hir [hir::Expr<'hir>],
+        delegation: &Delegation,
+        args: Vec<hir::Expr<'hir>>,
+        span: Span,
     ) -> hir::Expr<'hir> {
-        let callee = self.arena.alloc(hir::Expr {
-            hir_id: self.next_id(),
-            kind: hir::ExprKind::Path(path),
-            span: path.span(),
-        });
+        let path = self.lower_qpath(
+            delegation.id,
+            &delegation.qself,
+            &delegation.path,
+            ParamMode::Optional,
+            ImplTraitContext::Disallowed(ImplTraitPosition::Path),
+            None,
+        );
 
-        let expr = self.arena.alloc(hir::Expr {
-            hir_id: self.next_id(),
-            kind: hir::ExprKind::Call(callee, args),
-            span: path.span(),
-        });
+        let args = self.arena.alloc_from_iter(args);
+        let path_expr = self.arena.alloc(self.mk_expr(hir::ExprKind::Path(path), span));
+        let call = self.arena.alloc(self.mk_expr(hir::ExprKind::Call(path_expr, args), span));
 
         let block = self.arena.alloc(hir::Block {
             stmts: &[],
-            expr: Some(expr),
+            expr: Some(call),
             hir_id: self.next_id(),
             rules: hir::BlockCheckMode::DefaultBlock,
-            span: path.span(),
+            span,
             targeted_by_break: false,
         });
 
-        hir::Expr {
-            hir_id: self.next_id(),
-            kind: hir::ExprKind::Block(block, None),
-            span: path.span(),
-        }
+        self.mk_expr(hir::ExprKind::Block(block, None), span)
     }
 
     fn generate_delegation_error(
@@ -333,11 +323,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
         let header = self.generate_header_error();
         let sig = hir::FnSig { decl, header, span };
 
-        let body_id = self.lower_body(|this| {
-            let expr =
-                hir::Expr { hir_id: this.next_id(), kind: hir::ExprKind::Err(err), span: span };
-            (&[], expr)
-        });
+        let body_id = self.lower_body(|this| (&[], this.mk_expr(hir::ExprKind::Err(err), span)));
         DelegationResults { generics, body_id, sig }
     }
 
@@ -349,6 +335,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
             abi: abi::Abi::Rust,
         }
     }
+
+    #[inline]
+    fn mk_expr(&mut self, kind: hir::ExprKind<'hir>, span: Span) -> hir::Expr<'hir> {
+        hir::Expr { hir_id: self.next_id(), kind, span }
+    }
 }
 
 struct SelfResolver<'a> {
diff --git a/compiler/rustc_ast_lowering/src/errors.rs b/compiler/rustc_ast_lowering/src/errors.rs
index 02744d16b42..3d4b6a1f033 100644
--- a/compiler/rustc_ast_lowering/src/errors.rs
+++ b/compiler/rustc_ast_lowering/src/errors.rs
@@ -425,6 +425,14 @@ pub(crate) struct NoPreciseCapturesOnApit {
 }
 
 #[derive(Diagnostic)]
+#[diag(ast_lowering_no_precise_captures_on_rpitit)]
+#[note]
+pub(crate) struct NoPreciseCapturesOnRpitit {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
 #[diag(ast_lowering_yield_in_closure)]
 pub(crate) struct YieldInClosure {
     #[primary_span]
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index da8682d3d09..0a06304fcec 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -1594,6 +1594,26 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         };
         debug!(?captured_lifetimes_to_duplicate);
 
+        match fn_kind {
+            // Deny `use<>` on RPITIT in trait/trait-impl for now.
+            Some(FnDeclKind::Trait | FnDeclKind::Impl) => {
+                if let Some(span) = bounds.iter().find_map(|bound| match *bound {
+                    ast::GenericBound::Use(_, span) => Some(span),
+                    _ => None,
+                }) {
+                    self.tcx.dcx().emit_err(errors::NoPreciseCapturesOnRpitit { span });
+                }
+            }
+            None
+            | Some(
+                FnDeclKind::Fn
+                | FnDeclKind::Inherent
+                | FnDeclKind::ExternFn
+                | FnDeclKind::Closure
+                | FnDeclKind::Pointer,
+            ) => {}
+        }
+
         self.lower_opaque_inner(
             opaque_ty_node_id,
             origin,
diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs
index 941bb78c0dd..ba4b6130b60 100644
--- a/compiler/rustc_ast_passes/src/ast_validation.rs
+++ b/compiler/rustc_ast_passes/src/ast_validation.rs
@@ -464,7 +464,7 @@ impl<'a> AstValidator<'a> {
                 {
                     self.dcx().emit_err(errors::InvalidSafetyOnExtern {
                         item_span: span,
-                        block: self.current_extern_span(),
+                        block: self.current_extern_span().shrink_to_lo(),
                     });
                 }
             }
diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs
index 96c476b271c..965d8fac712 100644
--- a/compiler/rustc_ast_passes/src/errors.rs
+++ b/compiler/rustc_ast_passes/src/errors.rs
@@ -221,7 +221,7 @@ pub enum ExternBlockSuggestion {
 pub struct InvalidSafetyOnExtern {
     #[primary_span]
     pub item_span: Span,
-    #[suggestion(code = "", applicability = "maybe-incorrect")]
+    #[suggestion(code = "unsafe ", applicability = "machine-applicable", style = "verbose")]
     pub block: Span,
 }
 
diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
index 1e117c46b6e..f2f6594e686 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
@@ -217,7 +217,7 @@ impl<'a> State<'a> {
     fn print_expr_call(&mut self, func: &ast::Expr, args: &[P<ast::Expr>], fixup: FixupContext) {
         let prec = match func.kind {
             ast::ExprKind::Field(..) => parser::PREC_FORCE_PAREN,
-            _ => parser::PREC_POSTFIX,
+            _ => parser::PREC_UNAMBIGUOUS,
         };
 
         // Independent of parenthesization related to precedence, we must
@@ -257,7 +257,7 @@ impl<'a> State<'a> {
         // boundaries, `$receiver.method()` can be parsed back as a statement
         // containing an expression if and only if `$receiver` can be parsed as
         // a statement containing an expression.
-        self.print_expr_maybe_paren(receiver, parser::PREC_POSTFIX, fixup);
+        self.print_expr_maybe_paren(receiver, parser::PREC_UNAMBIGUOUS, fixup);
 
         self.word(".");
         self.print_ident(segment.ident);
@@ -489,7 +489,7 @@ impl<'a> State<'a> {
                         self.space();
                     }
                     MatchKind::Postfix => {
-                        self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX, fixup);
+                        self.print_expr_maybe_paren(expr, parser::PREC_UNAMBIGUOUS, fixup);
                         self.word_nbsp(".match");
                     }
                 }
@@ -549,7 +549,7 @@ impl<'a> State<'a> {
                 self.print_block_with_attrs(blk, attrs);
             }
             ast::ExprKind::Await(expr, _) => {
-                self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX, fixup);
+                self.print_expr_maybe_paren(expr, parser::PREC_UNAMBIGUOUS, fixup);
                 self.word(".await");
             }
             ast::ExprKind::Assign(lhs, rhs, _) => {
@@ -568,14 +568,14 @@ impl<'a> State<'a> {
                 self.print_expr_maybe_paren(rhs, prec, fixup.subsequent_subexpression());
             }
             ast::ExprKind::Field(expr, ident) => {
-                self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX, fixup);
+                self.print_expr_maybe_paren(expr, parser::PREC_UNAMBIGUOUS, fixup);
                 self.word(".");
                 self.print_ident(*ident);
             }
             ast::ExprKind::Index(expr, index, _) => {
                 self.print_expr_maybe_paren(
                     expr,
-                    parser::PREC_POSTFIX,
+                    parser::PREC_UNAMBIGUOUS,
                     fixup.leftmost_subexpression(),
                 );
                 self.word("[");
@@ -713,7 +713,7 @@ impl<'a> State<'a> {
                 }
             }
             ast::ExprKind::Try(e) => {
-                self.print_expr_maybe_paren(e, parser::PREC_POSTFIX, fixup);
+                self.print_expr_maybe_paren(e, parser::PREC_UNAMBIGUOUS, fixup);
                 self.word("?")
             }
             ast::ExprKind::TryBlock(blk) => {
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
index 245ce790e49..db78edc45b9 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
@@ -1151,7 +1151,9 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
         // Get the arguments for the found method, only specifying that `Self` is the receiver type.
         let Some(possible_rcvr_ty) = typeck_results.node_type_opt(rcvr.hir_id) else { return };
         let args = GenericArgs::for_item(tcx, method_def_id, |param, _| {
-            if param.index == 0 {
+            if let ty::GenericParamDefKind::Lifetime = param.kind {
+                tcx.lifetimes.re_erased.into()
+            } else if param.index == 0 && param.name == kw::SelfUpper {
                 possible_rcvr_ty.into()
             } else if param.index == closure_param.index {
                 closure_ty.into()
@@ -1168,7 +1170,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
             Obligation::misc(tcx, span, self.mir_def_id(), self.param_env, pred)
         }));
 
-        if ocx.select_all_or_error().is_empty() {
+        if ocx.select_all_or_error().is_empty() && count > 0 {
             diag.span_suggestion_verbose(
                 tcx.hir().body(*body).value.peel_blocks().span.shrink_to_lo(),
                 "dereference the return value",
diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs
index c352100b01b..d509e4ce56d 100644
--- a/compiler/rustc_codegen_ssa/src/back/link.rs
+++ b/compiler/rustc_codegen_ssa/src/back/link.rs
@@ -1490,11 +1490,6 @@ fn print_native_static_libs(
     let mut lib_args: Vec<_> = all_native_libs
         .iter()
         .filter(|l| relevant_lib(sess, l))
-        // Deduplication of successive repeated libraries, see rust-lang/rust#113209
-        //
-        // note: we don't use PartialEq/Eq because NativeLib transitively depends on local
-        // elements like spans, which we don't care about and would make the deduplication impossible
-        .dedup_by(|l1, l2| l1.name == l2.name && l1.kind == l2.kind && l1.verbatim == l2.verbatim)
         .filter_map(|lib| {
             let name = lib.name;
             match lib.kind {
@@ -1521,6 +1516,8 @@ fn print_native_static_libs(
                 | NativeLibKind::RawDylib => None,
             }
         })
+        // deduplication of consecutive repeated libraries, see rust-lang/rust#113209
+        .dedup()
         .collect();
     for path in all_rust_dylibs {
         // FIXME deduplicate with add_dynamic_crate
diff --git a/compiler/rustc_data_structures/src/stable_hasher.rs b/compiler/rustc_data_structures/src/stable_hasher.rs
index b5bdf2e1790..a57f5067dd8 100644
--- a/compiler/rustc_data_structures/src/stable_hasher.rs
+++ b/compiler/rustc_data_structures/src/stable_hasher.rs
@@ -238,12 +238,21 @@ pub trait ToStableHashKey<HCX> {
 /// The associated constant `CAN_USE_UNSTABLE_SORT` denotes whether
 /// unstable sorting can be used for this type. Set to true if and
 /// only if `a == b` implies `a` and `b` are fully indistinguishable.
-pub unsafe trait StableOrd: Ord {
+pub trait StableOrd: Ord {
     const CAN_USE_UNSTABLE_SORT: bool;
+
+    /// Marker to ensure that implementors have carefully considered
+    /// whether their `Ord` implementation obeys this trait's contract.
+    const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: ();
 }
 
-unsafe impl<T: StableOrd> StableOrd for &T {
+impl<T: StableOrd> StableOrd for &T {
     const CAN_USE_UNSTABLE_SORT: bool = T::CAN_USE_UNSTABLE_SORT;
+
+    // Ordering of a reference is exactly that of the referent, and since
+    // the ordering of the referet is stable so must be the ordering of the
+    // reference.
+    const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();
 }
 
 /// This is a companion trait to `StableOrd`. Some types like `Symbol` can be
@@ -290,8 +299,12 @@ macro_rules! impl_stable_traits_for_trivial_type {
             }
         }
 
-        unsafe impl $crate::stable_hasher::StableOrd for $t {
+        impl $crate::stable_hasher::StableOrd for $t {
             const CAN_USE_UNSTABLE_SORT: bool = true;
+
+            // Encoding and decoding doesn't change the bytes of trivial types
+            // and `Ord::cmp` depends only on those bytes.
+            const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();
         }
     };
 }
@@ -327,8 +340,12 @@ impl<CTX> HashStable<CTX> for Hash128 {
     }
 }
 
-unsafe impl StableOrd for Hash128 {
+impl StableOrd for Hash128 {
     const CAN_USE_UNSTABLE_SORT: bool = true;
+
+    // Encoding and decoding doesn't change the bytes of `Hash128`
+    // and `Ord::cmp` depends only on those bytes.
+    const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();
 }
 
 impl<CTX> HashStable<CTX> for ! {
@@ -392,8 +409,12 @@ impl<T1: HashStable<CTX>, T2: HashStable<CTX>, CTX> HashStable<CTX> for (T1, T2)
     }
 }
 
-unsafe impl<T1: StableOrd, T2: StableOrd> StableOrd for (T1, T2) {
+impl<T1: StableOrd, T2: StableOrd> StableOrd for (T1, T2) {
     const CAN_USE_UNSTABLE_SORT: bool = T1::CAN_USE_UNSTABLE_SORT && T2::CAN_USE_UNSTABLE_SORT;
+
+    // Ordering of tuples is a pure function of their elements' ordering, and since
+    // the ordering of each element is stable so must be the ordering of the tuple.
+    const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();
 }
 
 impl<T1, T2, T3, CTX> HashStable<CTX> for (T1, T2, T3)
@@ -410,9 +431,13 @@ where
     }
 }
 
-unsafe impl<T1: StableOrd, T2: StableOrd, T3: StableOrd> StableOrd for (T1, T2, T3) {
+impl<T1: StableOrd, T2: StableOrd, T3: StableOrd> StableOrd for (T1, T2, T3) {
     const CAN_USE_UNSTABLE_SORT: bool =
         T1::CAN_USE_UNSTABLE_SORT && T2::CAN_USE_UNSTABLE_SORT && T3::CAN_USE_UNSTABLE_SORT;
+
+    // Ordering of tuples is a pure function of their elements' ordering, and since
+    // the ordering of each element is stable so must be the ordering of the tuple.
+    const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();
 }
 
 impl<T1, T2, T3, T4, CTX> HashStable<CTX> for (T1, T2, T3, T4)
@@ -431,13 +456,15 @@ where
     }
 }
 
-unsafe impl<T1: StableOrd, T2: StableOrd, T3: StableOrd, T4: StableOrd> StableOrd
-    for (T1, T2, T3, T4)
-{
+impl<T1: StableOrd, T2: StableOrd, T3: StableOrd, T4: StableOrd> StableOrd for (T1, T2, T3, T4) {
     const CAN_USE_UNSTABLE_SORT: bool = T1::CAN_USE_UNSTABLE_SORT
         && T2::CAN_USE_UNSTABLE_SORT
         && T3::CAN_USE_UNSTABLE_SORT
         && T4::CAN_USE_UNSTABLE_SORT;
+
+    // Ordering of tuples is a pure function of their elements' ordering, and since
+    // the ordering of each element is stable so must be the ordering of the tuple.
+    const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();
 }
 
 impl<T: HashStable<CTX>, CTX> HashStable<CTX> for [T] {
@@ -530,8 +557,12 @@ impl<CTX> HashStable<CTX> for str {
     }
 }
 
-unsafe impl StableOrd for &str {
+impl StableOrd for &str {
     const CAN_USE_UNSTABLE_SORT: bool = true;
+
+    // Encoding and decoding doesn't change the bytes of string slices
+    // and `Ord::cmp` depends only on those bytes.
+    const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();
 }
 
 impl<CTX> HashStable<CTX> for String {
@@ -541,10 +572,12 @@ impl<CTX> HashStable<CTX> for String {
     }
 }
 
-// Safety: String comparison only depends on their contents and the
-// contents are not changed by (de-)serialization.
-unsafe impl StableOrd for String {
+impl StableOrd for String {
     const CAN_USE_UNSTABLE_SORT: bool = true;
+
+    // String comparison only depends on their contents and the
+    // contents are not changed by (de-)serialization.
+    const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();
 }
 
 impl<HCX> ToStableHashKey<HCX> for String {
@@ -570,9 +603,11 @@ impl<CTX> HashStable<CTX> for bool {
     }
 }
 
-// Safety: sort order of bools is not changed by (de-)serialization.
-unsafe impl StableOrd for bool {
+impl StableOrd for bool {
     const CAN_USE_UNSTABLE_SORT: bool = true;
+
+    // sort order of bools is not changed by (de-)serialization.
+    const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();
 }
 
 impl<T, CTX> HashStable<CTX> for Option<T>
@@ -590,9 +625,11 @@ where
     }
 }
 
-// Safety: the Option wrapper does not add instability to comparison.
-unsafe impl<T: StableOrd> StableOrd for Option<T> {
+impl<T: StableOrd> StableOrd for Option<T> {
     const CAN_USE_UNSTABLE_SORT: bool = T::CAN_USE_UNSTABLE_SORT;
+
+    // the Option wrapper does not add instability to comparison.
+    const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();
 }
 
 impl<T1, T2, CTX> HashStable<CTX> for Result<T1, T2>
diff --git a/compiler/rustc_hir/src/hir_id.rs b/compiler/rustc_hir/src/hir_id.rs
index ac487469507..c0ca1a8017e 100644
--- a/compiler/rustc_hir/src/hir_id.rs
+++ b/compiler/rustc_hir/src/hir_id.rs
@@ -165,10 +165,12 @@ impl ItemLocalId {
     pub const INVALID: ItemLocalId = ItemLocalId::MAX;
 }
 
-// Safety: Ord is implement as just comparing the ItemLocalId's numerical
-// values and these are not changed by (de-)serialization.
-unsafe impl StableOrd for ItemLocalId {
+impl StableOrd for ItemLocalId {
     const CAN_USE_UNSTABLE_SORT: bool = true;
+
+    // `Ord` is implemented as just comparing the ItemLocalId's numerical
+    // values and these are not changed by (de-)serialization.
+    const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();
 }
 
 /// The `HirId` corresponding to `CRATE_NODE_ID` and `CRATE_DEF_ID`.
diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs
index 6cdbd692f73..ad3324f79e2 100644
--- a/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs
+++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs
@@ -171,10 +171,10 @@ pub(super) fn check_refining_return_position_impl_trait_in_trait<'tcx>(
     }
     // Resolve any lifetime variables that may have been introduced during normalization.
     let Ok((trait_bounds, impl_bounds)) = infcx.fully_resolve((trait_bounds, impl_bounds)) else {
-        // This code path is not reached in any tests, but may be reachable. If
-        // this is triggered, it should be converted to `delayed_bug` and the
-        // triggering case turned into a test.
-        tcx.dcx().bug("encountered errors when checking RPITIT refinement (resolution)");
+        // If resolution didn't fully complete, we cannot continue checking RPITIT refinement, and
+        // delay a bug as the original code contains load-bearing errors.
+        tcx.dcx().delayed_bug("encountered errors when checking RPITIT refinement (resolution)");
+        return;
     };
 
     // For quicker lookup, use an `IndexSet` (we don't use one earlier because
diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs
index 4d1b96d9c1b..8469cbbbc7d 100644
--- a/compiler/rustc_hir_analysis/src/check/mod.rs
+++ b/compiler/rustc_hir_analysis/src/check/mod.rs
@@ -211,11 +211,18 @@ fn missing_items_err(
         .collect::<Vec<_>>()
         .join("`, `");
 
-    // `Span` before impl block closing brace.
-    let hi = full_impl_span.hi() - BytePos(1);
-    // Point at the place right before the closing brace of the relevant `impl` to suggest
-    // adding the associated item at the end of its body.
-    let sugg_sp = full_impl_span.with_lo(hi).with_hi(hi);
+    let sugg_sp = if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(full_impl_span)
+        && snippet.ends_with("}")
+    {
+        // `Span` before impl block closing brace.
+        let hi = full_impl_span.hi() - BytePos(1);
+        // Point at the place right before the closing brace of the relevant `impl` to suggest
+        // adding the associated item at the end of its body.
+        full_impl_span.with_lo(hi).with_hi(hi)
+    } else {
+        full_impl_span.shrink_to_hi()
+    };
+
     // Obtain the level of indentation ending in `sugg_sp`.
     let padding =
         tcx.sess.source_map().indentation_before(sugg_sp).unwrap_or_else(|| String::new());
diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs
index b21f1eadfb7..25b0cbdc026 100644
--- a/compiler/rustc_hir_pretty/src/lib.rs
+++ b/compiler/rustc_hir_pretty/src/lib.rs
@@ -1120,7 +1120,7 @@ impl<'a> State<'a> {
     fn print_expr_call(&mut self, func: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
         let prec = match func.kind {
             hir::ExprKind::Field(..) => parser::PREC_FORCE_PAREN,
-            _ => parser::PREC_POSTFIX,
+            _ => parser::PREC_UNAMBIGUOUS,
         };
 
         self.print_expr_maybe_paren(func, prec);
@@ -1134,7 +1134,7 @@ impl<'a> State<'a> {
         args: &[hir::Expr<'_>],
     ) {
         let base_args = args;
-        self.print_expr_maybe_paren(receiver, parser::PREC_POSTFIX);
+        self.print_expr_maybe_paren(receiver, parser::PREC_UNAMBIGUOUS);
         self.word(".");
         self.print_ident(segment.ident);
 
@@ -1478,12 +1478,12 @@ impl<'a> State<'a> {
                 self.print_expr_maybe_paren(rhs, prec);
             }
             hir::ExprKind::Field(expr, ident) => {
-                self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX);
+                self.print_expr_maybe_paren(expr, parser::PREC_UNAMBIGUOUS);
                 self.word(".");
                 self.print_ident(ident);
             }
             hir::ExprKind::Index(expr, index, _) => {
-                self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX);
+                self.print_expr_maybe_paren(expr, parser::PREC_UNAMBIGUOUS);
                 self.word("[");
                 self.print_expr(index);
                 self.word("]");
diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs
index 46c85515575..3b199b7e3c2 100644
--- a/compiler/rustc_hir_typeck/src/callee.rs
+++ b/compiler/rustc_hir_typeck/src/callee.rs
@@ -3,7 +3,7 @@ use super::method::MethodCallee;
 use super::{Expectation, FnCtxt, TupleArgumentsFlag};
 
 use crate::errors;
-use rustc_ast::util::parser::PREC_POSTFIX;
+use rustc_ast::util::parser::PREC_UNAMBIGUOUS;
 use rustc_errors::{Applicability, Diag, ErrorGuaranteed, StashKey};
 use rustc_hir::def::{self, CtorKind, Namespace, Res};
 use rustc_hir::def_id::DefId;
@@ -656,7 +656,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             };
 
             if let Ok(rest_snippet) = rest_snippet {
-                let sugg = if callee_expr.precedence().order() >= PREC_POSTFIX {
+                let sugg = if callee_expr.precedence().order() >= PREC_UNAMBIGUOUS {
                     vec![
                         (up_to_rcvr_span, "".to_string()),
                         (rest_span, format!(".{}({rest_snippet}", segment.ident)),
diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs
index 58708510282..92f2d3254bb 100644
--- a/compiler/rustc_hir_typeck/src/cast.rs
+++ b/compiler/rustc_hir_typeck/src/cast.rs
@@ -946,7 +946,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
 
     fn lossy_provenance_ptr2int_lint(&self, fcx: &FnCtxt<'a, 'tcx>, t_c: ty::cast::IntTy) {
         let expr_prec = self.expr.precedence().order();
-        let needs_parens = expr_prec < rustc_ast::util::parser::PREC_POSTFIX;
+        let needs_parens = expr_prec < rustc_ast::util::parser::PREC_UNAMBIGUOUS;
 
         let needs_cast = !matches!(t_c, ty::cast::IntTy::U(ty::UintTy::Usize));
         let cast_span = self.expr_span.shrink_to_hi().to(self.cast_span);
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
index b3ebc0621cb..8d380caf916 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
@@ -9,7 +9,7 @@ use crate::method::probe::{IsSuggestion, Mode, ProbeScope};
 use core::cmp::min;
 use core::iter;
 use hir::def_id::LocalDefId;
-use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX};
+use rustc_ast::util::parser::{ExprPrecedence, PREC_UNAMBIGUOUS};
 use rustc_data_structures::packed::Pu128;
 use rustc_errors::{Applicability, Diag, MultiSpan};
 use rustc_hir as hir;
@@ -1329,7 +1329,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         {
             let span = expr.span.find_oldest_ancestor_in_same_ctxt();
 
-            let mut sugg = if expr.precedence().order() >= PREC_POSTFIX {
+            let mut sugg = if expr.precedence().order() >= PREC_UNAMBIGUOUS {
                 vec![(span.shrink_to_hi(), ".into()".to_owned())]
             } else {
                 vec![
@@ -2868,7 +2868,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             "change the type of the numeric literal from `{checked_ty}` to `{expected_ty}`",
         );
 
-        let close_paren = if expr.precedence().order() < PREC_POSTFIX {
+        let close_paren = if expr.precedence().order() < PREC_UNAMBIGUOUS {
             sugg.push((expr.span.shrink_to_lo(), "(".to_string()));
             ")"
         } else {
@@ -2893,7 +2893,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 let len = src.trim_end_matches(&checked_ty.to_string()).len();
                 expr.span.with_lo(expr.span.lo() + BytePos(len as u32))
             },
-            if expr.precedence().order() < PREC_POSTFIX {
+            if expr.precedence().order() < PREC_UNAMBIGUOUS {
                 // Readd `)`
                 format!("{expected_ty})")
             } else {
diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs
index 4f46320fa02..a385bc70e35 100644
--- a/compiler/rustc_hir_typeck/src/method/suggest.rs
+++ b/compiler/rustc_hir_typeck/src/method/suggest.rs
@@ -2831,32 +2831,38 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         errors: Vec<FulfillmentError<'tcx>>,
         suggest_derive: bool,
     ) {
-        let all_local_types_needing_impls =
-            errors.iter().all(|e| match e.obligation.predicate.kind().skip_binder() {
+        let preds: Vec<_> = errors
+            .iter()
+            .filter_map(|e| match e.obligation.predicate.kind().skip_binder() {
                 ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => {
                     match pred.self_ty().kind() {
-                        ty::Adt(def, _) => def.did().is_local(),
-                        _ => false,
+                        ty::Adt(_, _) => Some(pred),
+                        _ => None,
                     }
                 }
-                _ => false,
-            });
-        let mut preds: Vec<_> = errors
-            .iter()
-            .filter_map(|e| match e.obligation.predicate.kind().skip_binder() {
-                ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => Some(pred),
                 _ => None,
             })
             .collect();
-        preds.sort_by_key(|pred| pred.trait_ref.to_string());
-        let def_ids = preds
+
+        // Note for local items and foreign items respectively.
+        let (mut local_preds, mut foreign_preds): (Vec<_>, Vec<_>) =
+            preds.iter().partition(|&pred| {
+                if let ty::Adt(def, _) = pred.self_ty().kind() {
+                    def.did().is_local()
+                } else {
+                    false
+                }
+            });
+
+        local_preds.sort_by_key(|pred: &&ty::TraitPredicate<'_>| pred.trait_ref.to_string());
+        let local_def_ids = local_preds
             .iter()
             .filter_map(|pred| match pred.self_ty().kind() {
                 ty::Adt(def, _) => Some(def.did()),
                 _ => None,
             })
             .collect::<FxIndexSet<_>>();
-        let mut spans: MultiSpan = def_ids
+        let mut local_spans: MultiSpan = local_def_ids
             .iter()
             .filter_map(|def_id| {
                 let span = self.tcx.def_span(*def_id);
@@ -2864,11 +2870,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             })
             .collect::<Vec<_>>()
             .into();
-
-        for pred in &preds {
+        for pred in &local_preds {
             match pred.self_ty().kind() {
-                ty::Adt(def, _) if def.did().is_local() => {
-                    spans.push_span_label(
+                ty::Adt(def, _) => {
+                    local_spans.push_span_label(
                         self.tcx.def_span(def.did()),
                         format!("must implement `{}`", pred.trait_ref.print_trait_sugared()),
                     );
@@ -2876,24 +2881,69 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 _ => {}
             }
         }
-
-        if all_local_types_needing_impls && spans.primary_span().is_some() {
-            let msg = if preds.len() == 1 {
+        if local_spans.primary_span().is_some() {
+            let msg = if local_preds.len() == 1 {
                 format!(
                     "an implementation of `{}` might be missing for `{}`",
-                    preds[0].trait_ref.print_trait_sugared(),
-                    preds[0].self_ty()
+                    local_preds[0].trait_ref.print_trait_sugared(),
+                    local_preds[0].self_ty()
                 )
             } else {
                 format!(
                     "the following type{} would have to `impl` {} required trait{} for this \
                      operation to be valid",
-                    pluralize!(def_ids.len()),
-                    if def_ids.len() == 1 { "its" } else { "their" },
-                    pluralize!(preds.len()),
+                    pluralize!(local_def_ids.len()),
+                    if local_def_ids.len() == 1 { "its" } else { "their" },
+                    pluralize!(local_preds.len()),
+                )
+            };
+            err.span_note(local_spans, msg);
+        }
+
+        foreign_preds.sort_by_key(|pred: &&ty::TraitPredicate<'_>| pred.trait_ref.to_string());
+        let foreign_def_ids = foreign_preds
+            .iter()
+            .filter_map(|pred| match pred.self_ty().kind() {
+                ty::Adt(def, _) => Some(def.did()),
+                _ => None,
+            })
+            .collect::<FxIndexSet<_>>();
+        let mut foreign_spans: MultiSpan = foreign_def_ids
+            .iter()
+            .filter_map(|def_id| {
+                let span = self.tcx.def_span(*def_id);
+                if span.is_dummy() { None } else { Some(span) }
+            })
+            .collect::<Vec<_>>()
+            .into();
+        for pred in &foreign_preds {
+            match pred.self_ty().kind() {
+                ty::Adt(def, _) => {
+                    foreign_spans.push_span_label(
+                        self.tcx.def_span(def.did()),
+                        format!("not implement `{}`", pred.trait_ref.print_trait_sugared()),
+                    );
+                }
+                _ => {}
+            }
+        }
+        if foreign_spans.primary_span().is_some() {
+            let msg = if foreign_preds.len() == 1 {
+                format!(
+                    "the foreign item type `{}` doesn't implement `{}`",
+                    foreign_preds[0].self_ty(),
+                    foreign_preds[0].trait_ref.print_trait_sugared()
+                )
+            } else {
+                format!(
+                    "the foreign item type{} {} implement required trait{} for this \
+                     operation to be valid",
+                    pluralize!(foreign_def_ids.len()),
+                    if foreign_def_ids.len() > 1 { "don't" } else { "doesn't" },
+                    pluralize!(foreign_preds.len()),
                 )
             };
-            err.span_note(spans, msg);
+            err.span_note(foreign_spans, msg);
         }
 
         let preds: Vec<_> = errors
diff --git a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs
index 74c65e93616..13b145296a7 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs
@@ -209,8 +209,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
             }
             (Some(ty), _) if self.same_type_modulo_infer(ty, exp_found.found) => match cause.code()
             {
-                ObligationCauseCode::Pattern { span: Some(then_span), .. } => {
-                    Some(ConsiderAddingAwait::FutureSugg { span: then_span.shrink_to_hi() })
+                ObligationCauseCode::Pattern { span: Some(then_span), origin_expr, .. } => {
+                    origin_expr.then_some(ConsiderAddingAwait::FutureSugg {
+                        span: then_span.shrink_to_hi(),
+                    })
                 }
                 ObligationCauseCode::IfExpression(box IfExpressionCause { then_id, .. }) => {
                     let then_span = self.find_block_span_from_hir_id(*then_id);
diff --git a/compiler/rustc_middle/src/mir/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs
index 126387db1d9..412cfc1fc7a 100644
--- a/compiler/rustc_middle/src/mir/tcx.rs
+++ b/compiler/rustc_middle/src/mir/tcx.rs
@@ -289,19 +289,7 @@ impl<'tcx> UnOp {
     pub fn ty(&self, tcx: TyCtxt<'tcx>, arg_ty: Ty<'tcx>) -> Ty<'tcx> {
         match self {
             UnOp::Not | UnOp::Neg => arg_ty,
-            UnOp::PtrMetadata => {
-                let pointee_ty = arg_ty
-                    .builtin_deref(true)
-                    .unwrap_or_else(|| bug!("PtrMetadata of non-dereferenceable ty {arg_ty:?}"));
-                if pointee_ty.is_trivially_sized(tcx) {
-                    tcx.types.unit
-                } else {
-                    let Some(metadata_def_id) = tcx.lang_items().metadata_type() else {
-                        bug!("No metadata_type lang item while looking at {arg_ty:?}")
-                    };
-                    Ty::new_projection(tcx, metadata_def_id, [pointee_ty])
-                }
-            }
+            UnOp::PtrMetadata => arg_ty.pointee_metadata_ty_or_projection(tcx),
         }
     }
 }
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index a3cb84d561d..ff40a726fbc 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -1647,6 +1647,34 @@ impl<'tcx> Ty<'tcx> {
         }
     }
 
+    /// Given a pointer or reference type, returns the type of the *pointee*'s
+    /// metadata. If it can't be determined exactly (perhaps due to still
+    /// being generic) then a projection through `ptr::Pointee` will be returned.
+    ///
+    /// This is particularly useful for getting the type of the result of
+    /// [`UnOp::PtrMetadata`](crate::mir::UnOp::PtrMetadata).
+    ///
+    /// Panics if `self` is not dereferencable.
+    #[track_caller]
+    pub fn pointee_metadata_ty_or_projection(self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
+        let Some(pointee_ty) = self.builtin_deref(true) else {
+            bug!("Type {self:?} is not a pointer or reference type")
+        };
+        if pointee_ty.is_trivially_sized(tcx) {
+            tcx.types.unit
+        } else {
+            match pointee_ty.ptr_metadata_ty_or_tail(tcx, |x| x) {
+                Ok(metadata_ty) => metadata_ty,
+                Err(tail_ty) => {
+                    let Some(metadata_def_id) = tcx.lang_items().metadata_type() else {
+                        bug!("No metadata_type lang item while looking at {self:?}")
+                    };
+                    Ty::new_projection(tcx, metadata_def_id, [tail_ty])
+                }
+            }
+        }
+    }
+
     /// When we create a closure, we record its kind (i.e., what trait
     /// it implements, constrained by how it uses its borrows) into its
     /// [`ty::ClosureArgs`] or [`ty::CoroutineClosureArgs`] using a type
diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs
index 68244136d1a..932406fd1aa 100644
--- a/compiler/rustc_mir_build/src/build/matches/mod.rs
+++ b/compiler/rustc_mir_build/src/build/matches/mod.rs
@@ -358,8 +358,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
     where
         'a: 'pat,
     {
-        // Assemble a list of candidates: there is one candidate per pattern,
-        // which means there may be more than one candidate *per arm*.
+        // Assemble the initial list of candidates. These top-level candidates
+        // are 1:1 with the original match arms, but other parts of match
+        // lowering also introduce subcandidates (for subpatterns), and will
+        // also flatten candidates in some cases. So in general a list of
+        // candidates does _not_ necessarily correspond to a list of arms.
         arms.iter()
             .copied()
             .map(|arm| {
@@ -1031,6 +1034,12 @@ impl<'tcx> PatternExtraData<'tcx> {
 }
 
 /// A pattern in a form suitable for generating code.
+///
+/// Here, "flat" indicates that the pattern's match pairs have been recursively
+/// simplified by [`Builder::simplify_match_pairs`]. They are not necessarily
+/// flat in an absolute sense.
+///
+/// Will typically be incorporated into a [`Candidate`].
 #[derive(Debug, Clone)]
 struct FlatPat<'pat, 'tcx> {
     /// To match the pattern, all of these must be satisfied...
@@ -1042,23 +1051,25 @@ struct FlatPat<'pat, 'tcx> {
 }
 
 impl<'tcx, 'pat> FlatPat<'pat, 'tcx> {
+    /// Creates a `FlatPat` containing a simplified [`MatchPair`] list/forest
+    /// for the given pattern.
     fn new(
         place: PlaceBuilder<'tcx>,
         pattern: &'pat Pat<'tcx>,
         cx: &mut Builder<'_, 'tcx>,
     ) -> Self {
-        let is_never = pattern.is_never_pattern();
-        let mut flat_pat = FlatPat {
-            match_pairs: vec![MatchPair::new(place, pattern, cx)],
-            extra_data: PatternExtraData {
-                span: pattern.span,
-                bindings: Vec::new(),
-                ascriptions: Vec::new(),
-                is_never,
-            },
+        // First, recursively build a tree of match pairs for the given pattern.
+        let mut match_pairs = vec![MatchPair::new(place, pattern, cx)];
+        let mut extra_data = PatternExtraData {
+            span: pattern.span,
+            bindings: Vec::new(),
+            ascriptions: Vec::new(),
+            is_never: pattern.is_never_pattern(),
         };
-        cx.simplify_match_pairs(&mut flat_pat.match_pairs, &mut flat_pat.extra_data);
-        flat_pat
+        // Partly-flatten and sort the match pairs, while recording extra data.
+        cx.simplify_match_pairs(&mut match_pairs, &mut extra_data);
+
+        Self { match_pairs, extra_data }
     }
 }
 
@@ -1104,9 +1115,12 @@ impl<'tcx, 'pat> Candidate<'pat, 'tcx> {
         has_guard: bool,
         cx: &mut Builder<'_, 'tcx>,
     ) -> Self {
+        // Use `FlatPat` to build simplified match pairs, then immediately
+        // incorporate them into a new candidate.
         Self::from_flat_pat(FlatPat::new(place, pattern, cx), has_guard)
     }
 
+    /// Incorporates an already-simplified [`FlatPat`] into a new candidate.
     fn from_flat_pat(flat_pat: FlatPat<'pat, 'tcx>, has_guard: bool) -> Self {
         Candidate {
             match_pairs: flat_pat.match_pairs,
diff --git a/compiler/rustc_mir_build/src/build/matches/util.rs b/compiler/rustc_mir_build/src/build/matches/util.rs
index 50f4ca2d819..630d0b9438d 100644
--- a/compiler/rustc_mir_build/src/build/matches/util.rs
+++ b/compiler/rustc_mir_build/src/build/matches/util.rs
@@ -95,6 +95,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
 }
 
 impl<'pat, 'tcx> MatchPair<'pat, 'tcx> {
+    /// Recursively builds a `MatchPair` tree for the given pattern and its
+    /// subpatterns.
     pub(in crate::build) fn new(
         mut place_builder: PlaceBuilder<'tcx>,
         pattern: &'pat Pat<'tcx>,
diff --git a/compiler/rustc_mir_transform/src/cost_checker.rs b/compiler/rustc_mir_transform/src/cost_checker.rs
index 32c0d27f635..7e401b5482f 100644
--- a/compiler/rustc_mir_transform/src/cost_checker.rs
+++ b/compiler/rustc_mir_transform/src/cost_checker.rs
@@ -60,7 +60,15 @@ impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> {
 
     fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, _location: Location) {
         match rvalue {
-            Rvalue::NullaryOp(NullOp::UbChecks, ..) if !self.tcx.sess.ub_checks() => {
+            Rvalue::NullaryOp(NullOp::UbChecks, ..)
+                if !self
+                    .tcx
+                    .sess
+                    .opts
+                    .unstable_opts
+                    .inline_mir_preserve_debug
+                    .unwrap_or(self.tcx.sess.ub_checks()) =>
+            {
                 // If this is in optimized MIR it's because it's used later,
                 // so if we don't need UB checks this session, give a bonus
                 // here to offset the cost of the call later.
@@ -111,12 +119,19 @@ impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> {
                 }
             }
             TerminatorKind::Assert { unwind, msg, .. } => {
-                self.penalty +=
-                    if msg.is_optional_overflow_check() && !self.tcx.sess.overflow_checks() {
-                        INSTR_COST
-                    } else {
-                        CALL_PENALTY
-                    };
+                self.penalty += if msg.is_optional_overflow_check()
+                    && !self
+                        .tcx
+                        .sess
+                        .opts
+                        .unstable_opts
+                        .inline_mir_preserve_debug
+                        .unwrap_or(self.tcx.sess.overflow_checks())
+                {
+                    INSTR_COST
+                } else {
+                    CALL_PENALTY
+                };
                 if let UnwindAction::Cleanup(_) = unwind {
                     self.penalty += LANDINGPAD_PENALTY;
                 }
diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs
index bfdefd5a7d6..936a7e2d9de 100644
--- a/compiler/rustc_mir_transform/src/gvn.rs
+++ b/compiler/rustc_mir_transform/src/gvn.rs
@@ -823,18 +823,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
                 return self.simplify_cast(kind, value, to, location);
             }
             Rvalue::BinaryOp(op, box (ref mut lhs, ref mut rhs)) => {
-                let ty = lhs.ty(self.local_decls, self.tcx);
-                let lhs = self.simplify_operand(lhs, location);
-                let rhs = self.simplify_operand(rhs, location);
-                // Only short-circuit options after we called `simplify_operand`
-                // on both operands for side effect.
-                let lhs = lhs?;
-                let rhs = rhs?;
-
-                if let Some(value) = self.simplify_binary(op, ty, lhs, rhs) {
-                    return Some(value);
-                }
-                Value::BinaryOp(op, lhs, rhs)
+                return self.simplify_binary(op, lhs, rhs, location);
             }
             Rvalue::UnaryOp(op, ref mut arg_op) => {
                 return self.simplify_unary(op, arg_op, location);
@@ -987,23 +976,10 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
                     // `*const [T]` -> `*const T` which remove metadata.
                     // We run on potentially-generic MIR, though, so unlike codegen
                     // we can't always know exactly what the metadata are.
-                    // Thankfully, equality on `ptr_metadata_ty_or_tail` gives us
-                    // what we need: `Ok(meta_ty)` if the metadata is known, or
-                    // `Err(tail_ty)` if not. Matching metadata is ok, but if
-                    // that's not known, then matching tail types is also ok,
-                    // allowing things like `*mut (?A, ?T)` <-> `*mut (?B, ?T)`.
-                    // FIXME: Would it be worth trying to normalize, rather than
-                    // passing the identity closure?  Or are the types in the
-                    // Cast realistically about as normalized as we can get anyway?
+                    // To allow things like `*mut (?A, ?T)` <-> `*mut (?B, ?T)`,
+                    // it's fine to get a projection as the type.
                     Value::Cast { kind: CastKind::PtrToPtr, value: inner, from, to }
-                        if from
-                            .builtin_deref(true)
-                            .unwrap()
-                            .ptr_metadata_ty_or_tail(self.tcx, |t| t)
-                            == to
-                                .builtin_deref(true)
-                                .unwrap()
-                                .ptr_metadata_ty_or_tail(self.tcx, |t| t) =>
+                        if self.pointers_have_same_metadata(*from, *to) =>
                     {
                         arg_index = *inner;
                         was_updated = true;
@@ -1070,6 +1046,52 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
     fn simplify_binary(
         &mut self,
         op: BinOp,
+        lhs_operand: &mut Operand<'tcx>,
+        rhs_operand: &mut Operand<'tcx>,
+        location: Location,
+    ) -> Option<VnIndex> {
+        let lhs = self.simplify_operand(lhs_operand, location);
+        let rhs = self.simplify_operand(rhs_operand, location);
+        // Only short-circuit options after we called `simplify_operand`
+        // on both operands for side effect.
+        let mut lhs = lhs?;
+        let mut rhs = rhs?;
+
+        let lhs_ty = lhs_operand.ty(self.local_decls, self.tcx);
+
+        // If we're comparing pointers, remove `PtrToPtr` casts if the from
+        // types of both casts and the metadata all match.
+        if let BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge = op
+            && lhs_ty.is_any_ptr()
+            && let Value::Cast {
+                kind: CastKind::PtrToPtr, value: lhs_value, from: lhs_from, ..
+            } = self.get(lhs)
+            && let Value::Cast {
+                kind: CastKind::PtrToPtr, value: rhs_value, from: rhs_from, ..
+            } = self.get(rhs)
+            && lhs_from == rhs_from
+            && self.pointers_have_same_metadata(*lhs_from, lhs_ty)
+        {
+            lhs = *lhs_value;
+            rhs = *rhs_value;
+            if let Some(op) = self.try_as_operand(lhs, location) {
+                *lhs_operand = op;
+            }
+            if let Some(op) = self.try_as_operand(rhs, location) {
+                *rhs_operand = op;
+            }
+        }
+
+        if let Some(value) = self.simplify_binary_inner(op, lhs_ty, lhs, rhs) {
+            return Some(value);
+        }
+        let value = Value::BinaryOp(op, lhs, rhs);
+        Some(self.insert(value))
+    }
+
+    fn simplify_binary_inner(
+        &mut self,
+        op: BinOp,
         lhs_ty: Ty<'tcx>,
         lhs: VnIndex,
         rhs: VnIndex,
@@ -1228,6 +1250,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
             }
         }
 
+        // PtrToPtr-then-PtrToPtr can skip the intermediate step
         if let PtrToPtr = kind
             && let Value::Cast { kind: inner_kind, value: inner_value, from: inner_from, to: _ } =
                 *self.get(value)
@@ -1235,7 +1258,25 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
         {
             from = inner_from;
             value = inner_value;
-            *kind = PtrToPtr;
+            was_updated = true;
+            if inner_from == to {
+                return Some(inner_value);
+            }
+        }
+
+        // PtrToPtr-then-Transmute can just transmute the original, so long as the
+        // PtrToPtr didn't change metadata (and thus the size of the pointer)
+        if let Transmute = kind
+            && let Value::Cast {
+                kind: PtrToPtr,
+                value: inner_value,
+                from: inner_from,
+                to: inner_to,
+            } = *self.get(value)
+            && self.pointers_have_same_metadata(inner_from, inner_to)
+        {
+            from = inner_from;
+            value = inner_value;
             was_updated = true;
             if inner_from == to {
                 return Some(inner_value);
@@ -1289,6 +1330,21 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
         // Fallback: a symbolic `Len`.
         Some(self.insert(Value::Len(inner)))
     }
+
+    fn pointers_have_same_metadata(&self, left_ptr_ty: Ty<'tcx>, right_ptr_ty: Ty<'tcx>) -> bool {
+        let left_meta_ty = left_ptr_ty.pointee_metadata_ty_or_projection(self.tcx);
+        let right_meta_ty = right_ptr_ty.pointee_metadata_ty_or_projection(self.tcx);
+        if left_meta_ty == right_meta_ty {
+            true
+        } else if let Ok(left) =
+            self.tcx.try_normalize_erasing_regions(self.param_env, left_meta_ty)
+            && let Ok(right) = self.tcx.try_normalize_erasing_regions(self.param_env, right_meta_ty)
+        {
+            left == right
+        } else {
+            false
+        }
+    }
 }
 
 fn op_to_prop_const<'tcx>(
diff --git a/compiler/rustc_next_trait_solver/src/solve/alias_relate.rs b/compiler/rustc_next_trait_solver/src/solve/alias_relate.rs
index 5a95f4edf19..d8c1dc8b4e9 100644
--- a/compiler/rustc_next_trait_solver/src/solve/alias_relate.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/alias_relate.rs
@@ -32,14 +32,14 @@ where
         &mut self,
         goal: Goal<I, (I::Term, I::Term, ty::AliasRelationDirection)>,
     ) -> QueryResult<I> {
-        let tcx = self.cx();
+        let cx = self.cx();
         let Goal { param_env, predicate: (lhs, rhs, direction) } = goal;
         debug_assert!(lhs.to_alias_term().is_some() || rhs.to_alias_term().is_some());
 
         // Structurally normalize the lhs.
         let lhs = if let Some(alias) = lhs.to_alias_term() {
             let term = self.next_term_infer_of_kind(lhs);
-            self.add_normalizes_to_goal(goal.with(tcx, ty::NormalizesTo { alias, term }));
+            self.add_normalizes_to_goal(goal.with(cx, ty::NormalizesTo { alias, term }));
             term
         } else {
             lhs
@@ -48,7 +48,7 @@ where
         // Structurally normalize the rhs.
         let rhs = if let Some(alias) = rhs.to_alias_term() {
             let term = self.next_term_infer_of_kind(rhs);
-            self.add_normalizes_to_goal(goal.with(tcx, ty::NormalizesTo { alias, term }));
+            self.add_normalizes_to_goal(goal.with(cx, ty::NormalizesTo { alias, term }));
             term
         } else {
             rhs
diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs
index ee7279a43b2..21439530c08 100644
--- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs
@@ -36,11 +36,11 @@ where
 {
     fn self_ty(self) -> I::Ty;
 
-    fn trait_ref(self, tcx: I) -> ty::TraitRef<I>;
+    fn trait_ref(self, cx: I) -> ty::TraitRef<I>;
 
-    fn with_self_ty(self, tcx: I, self_ty: I::Ty) -> Self;
+    fn with_self_ty(self, cx: I, self_ty: I::Ty) -> Self;
 
-    fn trait_def_id(self, tcx: I) -> I::DefId;
+    fn trait_def_id(self, cx: I) -> I::DefId;
 
     /// Try equating an assumption predicate against a goal's predicate. If it
     /// holds, then execute the `then` callback, which should do any additional
@@ -82,7 +82,7 @@ where
         assumption: I::Clause,
     ) -> Result<Candidate<I>, NoSolution> {
         Self::probe_and_match_goal_against_assumption(ecx, source, goal, assumption, |ecx| {
-            let tcx = ecx.cx();
+            let cx = ecx.cx();
             let ty::Dynamic(bounds, _, _) = goal.predicate.self_ty().kind() else {
                 panic!("expected object type in `probe_and_consider_object_bound_candidate`");
             };
@@ -91,7 +91,7 @@ where
                 structural_traits::predicates_for_object_candidate(
                     ecx,
                     goal.param_env,
-                    goal.predicate.trait_ref(tcx),
+                    goal.predicate.trait_ref(cx),
                     bounds,
                 ),
             );
@@ -340,15 +340,15 @@ where
         goal: Goal<I, G>,
         candidates: &mut Vec<Candidate<I>>,
     ) {
-        let tcx = self.cx();
-        tcx.for_each_relevant_impl(
-            goal.predicate.trait_def_id(tcx),
+        let cx = self.cx();
+        cx.for_each_relevant_impl(
+            goal.predicate.trait_def_id(cx),
             goal.predicate.self_ty(),
             |impl_def_id| {
                 // For every `default impl`, there's always a non-default `impl`
                 // that will *also* apply. There's no reason to register a candidate
                 // for this impl, since it is *not* proof that the trait goal holds.
-                if tcx.impl_is_default(impl_def_id) {
+                if cx.impl_is_default(impl_def_id) {
                     return;
                 }
 
@@ -366,8 +366,8 @@ where
         goal: Goal<I, G>,
         candidates: &mut Vec<Candidate<I>>,
     ) {
-        let tcx = self.cx();
-        let trait_def_id = goal.predicate.trait_def_id(tcx);
+        let cx = self.cx();
+        let trait_def_id = goal.predicate.trait_def_id(cx);
 
         // N.B. When assembling built-in candidates for lang items that are also
         // `auto` traits, then the auto trait candidate that is assembled in
@@ -378,47 +378,47 @@ where
         // `solve::trait_goals` instead.
         let result = if let Err(guar) = goal.predicate.error_reported() {
             G::consider_error_guaranteed_candidate(self, guar)
-        } else if tcx.trait_is_auto(trait_def_id) {
+        } else if cx.trait_is_auto(trait_def_id) {
             G::consider_auto_trait_candidate(self, goal)
-        } else if tcx.trait_is_alias(trait_def_id) {
+        } else if cx.trait_is_alias(trait_def_id) {
             G::consider_trait_alias_candidate(self, goal)
-        } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::Sized) {
+        } else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::Sized) {
             G::consider_builtin_sized_candidate(self, goal)
-        } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::Copy)
-            || tcx.is_lang_item(trait_def_id, TraitSolverLangItem::Clone)
+        } else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::Copy)
+            || cx.is_lang_item(trait_def_id, TraitSolverLangItem::Clone)
         {
             G::consider_builtin_copy_clone_candidate(self, goal)
-        } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::PointerLike) {
+        } else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::PointerLike) {
             G::consider_builtin_pointer_like_candidate(self, goal)
-        } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::FnPtrTrait) {
+        } else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::FnPtrTrait) {
             G::consider_builtin_fn_ptr_trait_candidate(self, goal)
         } else if let Some(kind) = self.cx().fn_trait_kind_from_def_id(trait_def_id) {
             G::consider_builtin_fn_trait_candidates(self, goal, kind)
         } else if let Some(kind) = self.cx().async_fn_trait_kind_from_def_id(trait_def_id) {
             G::consider_builtin_async_fn_trait_candidates(self, goal, kind)
-        } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::AsyncFnKindHelper) {
+        } else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::AsyncFnKindHelper) {
             G::consider_builtin_async_fn_kind_helper_candidate(self, goal)
-        } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::Tuple) {
+        } else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::Tuple) {
             G::consider_builtin_tuple_candidate(self, goal)
-        } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::PointeeTrait) {
+        } else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::PointeeTrait) {
             G::consider_builtin_pointee_candidate(self, goal)
-        } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::Future) {
+        } else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::Future) {
             G::consider_builtin_future_candidate(self, goal)
-        } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::Iterator) {
+        } else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::Iterator) {
             G::consider_builtin_iterator_candidate(self, goal)
-        } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::FusedIterator) {
+        } else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::FusedIterator) {
             G::consider_builtin_fused_iterator_candidate(self, goal)
-        } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::AsyncIterator) {
+        } else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::AsyncIterator) {
             G::consider_builtin_async_iterator_candidate(self, goal)
-        } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::Coroutine) {
+        } else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::Coroutine) {
             G::consider_builtin_coroutine_candidate(self, goal)
-        } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::DiscriminantKind) {
+        } else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::DiscriminantKind) {
             G::consider_builtin_discriminant_kind_candidate(self, goal)
-        } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::AsyncDestruct) {
+        } else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::AsyncDestruct) {
             G::consider_builtin_async_destruct_candidate(self, goal)
-        } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::Destruct) {
+        } else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::Destruct) {
             G::consider_builtin_destruct_candidate(self, goal)
-        } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::TransmuteTrait) {
+        } else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::TransmuteTrait) {
             G::consider_builtin_transmute_candidate(self, goal)
         } else {
             Err(NoSolution)
@@ -428,7 +428,7 @@ where
 
         // There may be multiple unsize candidates for a trait with several supertraits:
         // `trait Foo: Bar<A> + Bar<B>` and `dyn Foo: Unsize<dyn Bar<_>>`
-        if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::Unsize) {
+        if cx.is_lang_item(trait_def_id, TraitSolverLangItem::Unsize) {
             candidates.extend(G::consider_structural_builtin_unsize_candidates(self, goal));
         }
     }
@@ -557,8 +557,8 @@ where
         goal: Goal<I, G>,
         candidates: &mut Vec<Candidate<I>>,
     ) {
-        let tcx = self.cx();
-        if !tcx.trait_may_be_implemented_via_object(goal.predicate.trait_def_id(tcx)) {
+        let cx = self.cx();
+        if !cx.trait_may_be_implemented_via_object(goal.predicate.trait_def_id(cx)) {
             return;
         }
 
@@ -596,7 +596,7 @@ where
         };
 
         // Do not consider built-in object impls for non-object-safe types.
-        if bounds.principal_def_id().is_some_and(|def_id| !tcx.trait_is_object_safe(def_id)) {
+        if bounds.principal_def_id().is_some_and(|def_id| !cx.trait_is_object_safe(def_id)) {
             return;
         }
 
@@ -614,7 +614,7 @@ where
                         self,
                         CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
                         goal,
-                        bound.with_self_ty(tcx, self_ty),
+                        bound.with_self_ty(cx, self_ty),
                     ));
                 }
             }
@@ -624,14 +624,13 @@ where
         // since we don't need to look at any supertrait or anything if we are doing
         // a projection goal.
         if let Some(principal) = bounds.principal() {
-            let principal_trait_ref = principal.with_self_ty(tcx, self_ty);
-            for (idx, assumption) in D::elaborate_supertraits(tcx, principal_trait_ref).enumerate()
-            {
+            let principal_trait_ref = principal.with_self_ty(cx, self_ty);
+            for (idx, assumption) in D::elaborate_supertraits(cx, principal_trait_ref).enumerate() {
                 candidates.extend(G::probe_and_consider_object_bound_candidate(
                     self,
                     CandidateSource::BuiltinImpl(BuiltinImplSource::Object(idx)),
                     goal,
-                    assumption.upcast(tcx),
+                    assumption.upcast(cx),
                 ));
             }
         }
@@ -649,11 +648,11 @@ where
         goal: Goal<I, G>,
         candidates: &mut Vec<Candidate<I>>,
     ) {
-        let tcx = self.cx();
+        let cx = self.cx();
 
         candidates.extend(self.probe_trait_candidate(CandidateSource::CoherenceUnknowable).enter(
             |ecx| {
-                let trait_ref = goal.predicate.trait_ref(tcx);
+                let trait_ref = goal.predicate.trait_ref(cx);
                 if ecx.trait_ref_is_knowable(goal.param_env, trait_ref)? {
                     Err(NoSolution)
                 } else {
@@ -678,9 +677,9 @@ where
         goal: Goal<I, G>,
         candidates: &mut Vec<Candidate<I>>,
     ) {
-        let tcx = self.cx();
+        let cx = self.cx();
         let trait_goal: Goal<I, ty::TraitPredicate<I>> =
-            goal.with(tcx, goal.predicate.trait_ref(tcx));
+            goal.with(cx, goal.predicate.trait_ref(cx));
 
         let mut trait_candidates_from_env = vec![];
         self.probe(|_| ProbeKind::ShadowedEnvProbing).enter(|ecx| {
diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs
index 2df039c766c..0cef8d9f4bc 100644
--- a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs
@@ -23,7 +23,7 @@ where
     D: SolverDelegate<Interner = I>,
     I: Interner,
 {
-    let tcx = ecx.cx();
+    let cx = ecx.cx();
     match ty.kind() {
         ty::Uint(_)
         | ty::Int(_)
@@ -36,7 +36,7 @@ where
         | ty::Char => Ok(vec![]),
 
         // Treat `str` like it's defined as `struct str([u8]);`
-        ty::Str => Ok(vec![ty::Binder::dummy(Ty::new_slice(tcx, Ty::new_u8(tcx)))]),
+        ty::Str => Ok(vec![ty::Binder::dummy(Ty::new_slice(cx, Ty::new_u8(cx)))]),
 
         ty::Dynamic(..)
         | ty::Param(..)
@@ -79,21 +79,21 @@ where
             .cx()
             .bound_coroutine_hidden_types(def_id)
             .into_iter()
-            .map(|bty| bty.instantiate(tcx, args))
+            .map(|bty| bty.instantiate(cx, args))
             .collect()),
 
         // For `PhantomData<T>`, we pass `T`.
         ty::Adt(def, args) if def.is_phantom_data() => Ok(vec![ty::Binder::dummy(args.type_at(0))]),
 
         ty::Adt(def, args) => {
-            Ok(def.all_field_tys(tcx).iter_instantiated(tcx, args).map(ty::Binder::dummy).collect())
+            Ok(def.all_field_tys(cx).iter_instantiated(cx, args).map(ty::Binder::dummy).collect())
         }
 
         ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => {
             // We can resolve the `impl Trait` to its concrete type,
             // which enforces a DAG between the functions requiring
             // the auto trait bounds in question.
-            Ok(vec![ty::Binder::dummy(tcx.type_of(def_id).instantiate(tcx, args))])
+            Ok(vec![ty::Binder::dummy(cx.type_of(def_id).instantiate(cx, args))])
         }
     }
 }
@@ -247,18 +247,18 @@ where
 
 // Returns a binder of the tupled inputs types and output type from a builtin callable type.
 pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<I: Interner>(
-    tcx: I,
+    cx: I,
     self_ty: I::Ty,
     goal_kind: ty::ClosureKind,
 ) -> Result<Option<ty::Binder<I, (I::Ty, I::Ty)>>, NoSolution> {
     match self_ty.kind() {
         // keep this in sync with assemble_fn_pointer_candidates until the old solver is removed.
         ty::FnDef(def_id, args) => {
-            let sig = tcx.fn_sig(def_id);
-            if sig.skip_binder().is_fn_trait_compatible() && !tcx.has_target_features(def_id) {
+            let sig = cx.fn_sig(def_id);
+            if sig.skip_binder().is_fn_trait_compatible() && !cx.has_target_features(def_id) {
                 Ok(Some(
-                    sig.instantiate(tcx, args)
-                        .map_bound(|sig| (Ty::new_tup(tcx, sig.inputs().as_slice()), sig.output())),
+                    sig.instantiate(cx, args)
+                        .map_bound(|sig| (Ty::new_tup(cx, sig.inputs().as_slice()), sig.output())),
                 ))
             } else {
                 Err(NoSolution)
@@ -268,7 +268,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<I: Intern
         ty::FnPtr(sig) => {
             if sig.is_fn_trait_compatible() {
                 Ok(Some(
-                    sig.map_bound(|sig| (Ty::new_tup(tcx, sig.inputs().as_slice()), sig.output())),
+                    sig.map_bound(|sig| (Ty::new_tup(cx, sig.inputs().as_slice()), sig.output())),
                 ))
             } else {
                 Err(NoSolution)
@@ -323,10 +323,10 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<I: Intern
                 }
 
                 coroutine_closure_to_certain_coroutine(
-                    tcx,
+                    cx,
                     goal_kind,
                     // No captures by ref, so this doesn't matter.
-                    Region::new_static(tcx),
+                    Region::new_static(cx),
                     def_id,
                     args,
                     sig,
@@ -339,9 +339,9 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<I: Intern
                 }
 
                 coroutine_closure_to_ambiguous_coroutine(
-                    tcx,
+                    cx,
                     goal_kind, // No captures by ref, so this doesn't matter.
-                    Region::new_static(tcx),
+                    Region::new_static(cx),
                     def_id,
                     args,
                     sig,
@@ -403,7 +403,7 @@ pub(in crate::solve) struct AsyncCallableRelevantTypes<I: Interner> {
 // which enforces the closure is actually callable with the given trait. When we
 // know the kind already, we can short-circuit this check.
 pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<I: Interner>(
-    tcx: I,
+    cx: I,
     self_ty: I::Ty,
     goal_kind: ty::ClosureKind,
     env_region: I::Region,
@@ -422,9 +422,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<I:
                     return Err(NoSolution);
                 }
 
-                coroutine_closure_to_certain_coroutine(
-                    tcx, goal_kind, env_region, def_id, args, sig,
-                )
+                coroutine_closure_to_certain_coroutine(cx, goal_kind, env_region, def_id, args, sig)
             } else {
                 // When we don't know the closure kind (and therefore also the closure's upvars,
                 // which are computed at the same time), we must delay the computation of the
@@ -435,15 +433,15 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<I:
                 // coroutine upvars respecting the closure kind.
                 nested.push(
                     ty::TraitRef::new(
-                        tcx,
-                        tcx.require_lang_item(TraitSolverLangItem::AsyncFnKindHelper),
-                        [kind_ty, Ty::from_closure_kind(tcx, goal_kind)],
+                        cx,
+                        cx.require_lang_item(TraitSolverLangItem::AsyncFnKindHelper),
+                        [kind_ty, Ty::from_closure_kind(cx, goal_kind)],
                     )
-                    .upcast(tcx),
+                    .upcast(cx),
                 );
 
                 coroutine_closure_to_ambiguous_coroutine(
-                    tcx, goal_kind, env_region, def_id, args, sig,
+                    cx, goal_kind, env_region, def_id, args, sig,
                 )
             };
 
@@ -458,21 +456,21 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<I:
         }
 
         ty::FnDef(..) | ty::FnPtr(..) => {
-            let bound_sig = self_ty.fn_sig(tcx);
+            let bound_sig = self_ty.fn_sig(cx);
             let sig = bound_sig.skip_binder();
-            let future_trait_def_id = tcx.require_lang_item(TraitSolverLangItem::Future);
+            let future_trait_def_id = cx.require_lang_item(TraitSolverLangItem::Future);
             // `FnDef` and `FnPtr` only implement `AsyncFn*` when their
             // return type implements `Future`.
             let nested = vec![
                 bound_sig
-                    .rebind(ty::TraitRef::new(tcx, future_trait_def_id, [sig.output()]))
-                    .upcast(tcx),
+                    .rebind(ty::TraitRef::new(cx, future_trait_def_id, [sig.output()]))
+                    .upcast(cx),
             ];
-            let future_output_def_id = tcx.require_lang_item(TraitSolverLangItem::FutureOutput);
-            let future_output_ty = Ty::new_projection(tcx, future_output_def_id, [sig.output()]);
+            let future_output_def_id = cx.require_lang_item(TraitSolverLangItem::FutureOutput);
+            let future_output_ty = Ty::new_projection(cx, future_output_def_id, [sig.output()]);
             Ok((
                 bound_sig.rebind(AsyncCallableRelevantTypes {
-                    tupled_inputs_ty: Ty::new_tup(tcx, sig.inputs().as_slice()),
+                    tupled_inputs_ty: Ty::new_tup(cx, sig.inputs().as_slice()),
                     output_coroutine_ty: sig.output(),
                     coroutine_return_ty: future_output_ty,
                 }),
@@ -483,13 +481,13 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<I:
             let args = args.as_closure();
             let bound_sig = args.sig();
             let sig = bound_sig.skip_binder();
-            let future_trait_def_id = tcx.require_lang_item(TraitSolverLangItem::Future);
+            let future_trait_def_id = cx.require_lang_item(TraitSolverLangItem::Future);
             // `Closure`s only implement `AsyncFn*` when their return type
             // implements `Future`.
             let mut nested = vec![
                 bound_sig
-                    .rebind(ty::TraitRef::new(tcx, future_trait_def_id, [sig.output()]))
-                    .upcast(tcx),
+                    .rebind(ty::TraitRef::new(cx, future_trait_def_id, [sig.output()]))
+                    .upcast(cx),
             ];
 
             // Additionally, we need to check that the closure kind
@@ -501,7 +499,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<I:
                 }
             } else {
                 let async_fn_kind_trait_def_id =
-                    tcx.require_lang_item(TraitSolverLangItem::AsyncFnKindHelper);
+                    cx.require_lang_item(TraitSolverLangItem::AsyncFnKindHelper);
                 // When we don't know the closure kind (and therefore also the closure's upvars,
                 // which are computed at the same time), we must delay the computation of the
                 // generator's upvars. We do this using the `AsyncFnKindHelper`, which as a trait
@@ -511,16 +509,16 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<I:
                 // coroutine upvars respecting the closure kind.
                 nested.push(
                     ty::TraitRef::new(
-                        tcx,
+                        cx,
                         async_fn_kind_trait_def_id,
-                        [kind_ty, Ty::from_closure_kind(tcx, goal_kind)],
+                        [kind_ty, Ty::from_closure_kind(cx, goal_kind)],
                     )
-                    .upcast(tcx),
+                    .upcast(cx),
                 );
             }
 
-            let future_output_def_id = tcx.require_lang_item(TraitSolverLangItem::FutureOutput);
-            let future_output_ty = Ty::new_projection(tcx, future_output_def_id, [sig.output()]);
+            let future_output_def_id = cx.require_lang_item(TraitSolverLangItem::FutureOutput);
+            let future_output_ty = Ty::new_projection(cx, future_output_def_id, [sig.output()]);
             Ok((
                 bound_sig.rebind(AsyncCallableRelevantTypes {
                     tupled_inputs_ty: sig.inputs().get(0).unwrap(),
@@ -565,7 +563,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<I:
 /// Given a coroutine-closure, project to its returned coroutine when we are *certain*
 /// that the closure's kind is compatible with the goal.
 fn coroutine_closure_to_certain_coroutine<I: Interner>(
-    tcx: I,
+    cx: I,
     goal_kind: ty::ClosureKind,
     goal_region: I::Region,
     def_id: I::DefId,
@@ -573,9 +571,9 @@ fn coroutine_closure_to_certain_coroutine<I: Interner>(
     sig: ty::CoroutineClosureSignature<I>,
 ) -> I::Ty {
     sig.to_coroutine_given_kind_and_upvars(
-        tcx,
+        cx,
         args.parent_args(),
-        tcx.coroutine_for_closure(def_id),
+        cx.coroutine_for_closure(def_id),
         goal_kind,
         goal_region,
         args.tupled_upvars_ty(),
@@ -589,20 +587,20 @@ fn coroutine_closure_to_certain_coroutine<I: Interner>(
 ///
 /// Note that we do not also push a `AsyncFnKindHelper` goal here.
 fn coroutine_closure_to_ambiguous_coroutine<I: Interner>(
-    tcx: I,
+    cx: I,
     goal_kind: ty::ClosureKind,
     goal_region: I::Region,
     def_id: I::DefId,
     args: ty::CoroutineClosureArgs<I>,
     sig: ty::CoroutineClosureSignature<I>,
 ) -> I::Ty {
-    let upvars_projection_def_id = tcx.require_lang_item(TraitSolverLangItem::AsyncFnKindUpvars);
+    let upvars_projection_def_id = cx.require_lang_item(TraitSolverLangItem::AsyncFnKindUpvars);
     let tupled_upvars_ty = Ty::new_projection(
-        tcx,
+        cx,
         upvars_projection_def_id,
         [
             I::GenericArg::from(args.kind_ty()),
-            Ty::from_closure_kind(tcx, goal_kind).into(),
+            Ty::from_closure_kind(cx, goal_kind).into(),
             goal_region.into(),
             sig.tupled_inputs_ty.into(),
             args.tupled_upvars_ty().into(),
@@ -610,10 +608,10 @@ fn coroutine_closure_to_ambiguous_coroutine<I: Interner>(
         ],
     );
     sig.to_coroutine(
-        tcx,
+        cx,
         args.parent_args(),
-        Ty::from_closure_kind(tcx, goal_kind),
-        tcx.coroutine_for_closure(def_id),
+        Ty::from_closure_kind(cx, goal_kind),
+        cx.coroutine_for_closure(def_id),
         tupled_upvars_ty,
     )
 }
@@ -668,28 +666,28 @@ where
     D: SolverDelegate<Interner = I>,
     I: Interner,
 {
-    let tcx = ecx.cx();
+    let cx = ecx.cx();
     let mut requirements = vec![];
     requirements
-        .extend(tcx.super_predicates_of(trait_ref.def_id).iter_instantiated(tcx, trait_ref.args));
+        .extend(cx.super_predicates_of(trait_ref.def_id).iter_instantiated(cx, trait_ref.args));
 
     // FIXME(associated_const_equality): Also add associated consts to
     // the requirements here.
-    for associated_type_def_id in tcx.associated_type_def_ids(trait_ref.def_id) {
+    for associated_type_def_id in cx.associated_type_def_ids(trait_ref.def_id) {
         // associated types that require `Self: Sized` do not show up in the built-in
         // implementation of `Trait for dyn Trait`, and can be dropped here.
-        if tcx.generics_require_sized_self(associated_type_def_id) {
+        if cx.generics_require_sized_self(associated_type_def_id) {
             continue;
         }
 
         requirements
-            .extend(tcx.item_bounds(associated_type_def_id).iter_instantiated(tcx, trait_ref.args));
+            .extend(cx.item_bounds(associated_type_def_id).iter_instantiated(cx, trait_ref.args));
     }
 
     let mut replace_projection_with = HashMap::default();
     for bound in object_bounds.iter() {
         if let ty::ExistentialPredicate::Projection(proj) = bound.skip_binder() {
-            let proj = proj.with_self_ty(tcx, trait_ref.self_ty());
+            let proj = proj.with_self_ty(cx, trait_ref.self_ty());
             let old_ty = replace_projection_with.insert(proj.def_id(), bound.rebind(proj));
             assert_eq!(
                 old_ty,
@@ -709,7 +707,7 @@ where
     folder
         .nested
         .into_iter()
-        .chain(folded_requirements.into_iter().map(|clause| Goal::new(tcx, param_env, clause)))
+        .chain(folded_requirements.into_iter().map(|clause| Goal::new(cx, param_env, clause)))
         .collect()
 }
 
diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
index 04dce2780b0..87342eefb33 100644
--- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
@@ -239,14 +239,14 @@ where
     /// This function takes care of setting up the inference context, setting the anchor,
     /// and registering opaques from the canonicalized input.
     fn enter_canonical<R>(
-        tcx: I,
+        cx: I,
         search_graph: &'a mut search_graph::SearchGraph<I>,
         canonical_input: CanonicalInput<I>,
         canonical_goal_evaluation: &mut ProofTreeBuilder<D>,
         f: impl FnOnce(&mut EvalCtxt<'_, D>, Goal<I, I::Predicate>) -> R,
     ) -> R {
         let (ref delegate, input, var_values) =
-            SolverDelegate::build_with_canonical(tcx, search_graph.solver_mode(), &canonical_input);
+            SolverDelegate::build_with_canonical(cx, search_graph.solver_mode(), &canonical_input);
 
         let mut ecx = EvalCtxt {
             delegate,
@@ -292,9 +292,9 @@ where
     /// Instead of calling this function directly, use either [EvalCtxt::evaluate_goal]
     /// if you're inside of the solver or [SolverDelegateEvalExt::evaluate_root_goal] if you're
     /// outside of it.
-    #[instrument(level = "debug", skip(tcx, search_graph, goal_evaluation), ret)]
+    #[instrument(level = "debug", skip(cx, search_graph, goal_evaluation), ret)]
     fn evaluate_canonical_goal(
-        tcx: I,
+        cx: I,
         search_graph: &'a mut search_graph::SearchGraph<I>,
         canonical_input: CanonicalInput<I>,
         goal_evaluation: &mut ProofTreeBuilder<D>,
@@ -307,12 +307,12 @@ where
         // The actual solver logic happens in `ecx.compute_goal`.
         let result = ensure_sufficient_stack(|| {
             search_graph.with_new_goal(
-                tcx,
+                cx,
                 canonical_input,
                 &mut canonical_goal_evaluation,
                 |search_graph, canonical_goal_evaluation| {
                     EvalCtxt::enter_canonical(
-                        tcx,
+                        cx,
                         search_graph,
                         canonical_input,
                         canonical_goal_evaluation,
@@ -506,7 +506,7 @@ where
     ///
     /// Goals for the next step get directly added to the nested goals of the `EvalCtxt`.
     fn evaluate_added_goals_step(&mut self) -> Result<Option<Certainty>, NoSolution> {
-        let tcx = self.cx();
+        let cx = self.cx();
         let mut goals = core::mem::take(&mut self.nested_goals);
 
         // If this loop did not result in any progress, what's our final certainty.
@@ -516,7 +516,7 @@ where
             // RHS does not affect projection candidate assembly.
             let unconstrained_rhs = self.next_term_infer_of_kind(goal.predicate.term);
             let unconstrained_goal = goal.with(
-                tcx,
+                cx,
                 ty::NormalizesTo { alias: goal.predicate.alias, term: unconstrained_rhs },
             );
 
@@ -777,7 +777,7 @@ where
         // NOTE: this check is purely an optimization, the structural eq would
         // always fail if the term is not an inference variable.
         if term.is_infer() {
-            let tcx = self.cx();
+            let cx = self.cx();
             // We need to relate `alias` to `term` treating only the outermost
             // constructor as rigid, relating any contained generic arguments as
             // normal. We do this by first structurally equating the `term`
@@ -787,8 +787,8 @@ where
             // Alternatively we could modify `Equate` for this case by adding another
             // variant to `StructurallyRelateAliases`.
             let identity_args = self.fresh_args_for_item(alias.def_id);
-            let rigid_ctor = ty::AliasTerm::new_from_args(tcx, alias.def_id, identity_args);
-            let ctor_term = rigid_ctor.to_term(tcx);
+            let rigid_ctor = ty::AliasTerm::new_from_args(cx, alias.def_id, identity_args);
+            let ctor_term = rigid_ctor.to_term(cx);
             let obligations =
                 self.delegate.eq_structurally_relating_aliases(param_env, term, ctor_term)?;
             debug_assert!(obligations.is_empty());
diff --git a/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs b/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs
index 4fc58e06d67..b50676e8d53 100644
--- a/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs
@@ -323,13 +323,13 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> ProofTreeBuilder<D> {
 
     pub fn finalize_canonical_goal_evaluation(
         &mut self,
-        tcx: I,
+        cx: I,
     ) -> Option<I::CanonicalGoalEvaluationStepRef> {
         self.as_mut().map(|this| match this {
             DebugSolver::CanonicalGoalEvaluation(evaluation) => {
                 let final_revision = mem::take(&mut evaluation.final_revision).unwrap();
                 let final_revision =
-                    tcx.intern_canonical_goal_evaluation_step(final_revision.finalize());
+                    cx.intern_canonical_goal_evaluation_step(final_revision.finalize());
                 let kind = WipCanonicalGoalEvaluationKind::Interned { final_revision };
                 assert_eq!(evaluation.kind.replace(kind), None);
                 final_revision
diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs
index e29ae7ac0a2..24055d6cd83 100644
--- a/compiler/rustc_next_trait_solver/src/solve/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs
@@ -34,7 +34,7 @@ use crate::delegate::SolverDelegate;
 /// How many fixpoint iterations we should attempt inside of the solver before bailing
 /// with overflow.
 ///
-/// We previously used  `tcx.recursion_limit().0.checked_ilog2().unwrap_or(0)` for this.
+/// We previously used  `cx.recursion_limit().0.checked_ilog2().unwrap_or(0)` for this.
 /// However, it feels unlikely that uncreasing the recursion limit by a power of two
 /// to get one more itereation is every useful or desirable. We now instead used a constant
 /// here. If there ever ends up some use-cases where a bigger number of fixpoint iterations
@@ -285,7 +285,7 @@ where
 }
 
 fn response_no_constraints_raw<I: Interner>(
-    tcx: I,
+    cx: I,
     max_universe: ty::UniverseIndex,
     variables: I::CanonicalVars,
     certainty: Certainty,
@@ -294,10 +294,10 @@ fn response_no_constraints_raw<I: Interner>(
         max_universe,
         variables,
         value: Response {
-            var_values: ty::CanonicalVarValues::make_identity(tcx, variables),
-            // FIXME: maybe we should store the "no response" version in tcx, like
-            // we do for tcx.types and stuff.
-            external_constraints: tcx.mk_external_constraints(ExternalConstraintsData::default()),
+            var_values: ty::CanonicalVarValues::make_identity(cx, variables),
+            // FIXME: maybe we should store the "no response" version in cx, like
+            // we do for cx.types and stuff.
+            external_constraints: cx.mk_external_constraints(ExternalConstraintsData::default()),
             certainty,
         },
         defining_opaque_types: Default::default(),
diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs
index 004ecf2d2c4..25e8708a332 100644
--- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs
@@ -19,21 +19,21 @@ where
         &mut self,
         goal: Goal<I, ty::NormalizesTo<I>>,
     ) -> QueryResult<I> {
-        let tcx = self.cx();
-        let inherent = goal.predicate.alias.expect_ty(tcx);
+        let cx = self.cx();
+        let inherent = goal.predicate.alias.expect_ty(cx);
 
-        let impl_def_id = tcx.parent(inherent.def_id);
+        let impl_def_id = cx.parent(inherent.def_id);
         let impl_args = self.fresh_args_for_item(impl_def_id);
 
         // Equate impl header and add impl where clauses
         self.eq(
             goal.param_env,
             inherent.self_ty(),
-            tcx.type_of(impl_def_id).instantiate(tcx, impl_args),
+            cx.type_of(impl_def_id).instantiate(cx, impl_args),
         )?;
 
         // Equate IAT with the RHS of the project goal
-        let inherent_args = inherent.rebase_inherent_args_onto_impl(impl_args, tcx);
+        let inherent_args = inherent.rebase_inherent_args_onto_impl(impl_args, cx);
 
         // Check both where clauses on the impl and IAT
         //
@@ -43,12 +43,12 @@ where
         // and I don't think the assoc item where-bounds are allowed to be coinductive.
         self.add_goals(
             GoalSource::Misc,
-            tcx.predicates_of(inherent.def_id)
-                .iter_instantiated(tcx, inherent_args)
-                .map(|pred| goal.with(tcx, pred)),
+            cx.predicates_of(inherent.def_id)
+                .iter_instantiated(cx, inherent_args)
+                .map(|pred| goal.with(cx, pred)),
         );
 
-        let normalized = tcx.type_of(inherent.def_id).instantiate(tcx, inherent_args);
+        let normalized = cx.type_of(inherent.def_id).instantiate(cx, inherent_args);
         self.instantiate_normalizes_to_term(goal, normalized.into());
         self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
     }
diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs
index bc5233c4887..4e8cb4384f4 100644
--- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs
@@ -84,16 +84,16 @@ where
         self.self_ty()
     }
 
-    fn trait_ref(self, tcx: I) -> ty::TraitRef<I> {
-        self.alias.trait_ref(tcx)
+    fn trait_ref(self, cx: I) -> ty::TraitRef<I> {
+        self.alias.trait_ref(cx)
     }
 
-    fn with_self_ty(self, tcx: I, self_ty: I::Ty) -> Self {
-        self.with_self_ty(tcx, self_ty)
+    fn with_self_ty(self, cx: I, self_ty: I::Ty) -> Self {
+        self.with_self_ty(cx, self_ty)
     }
 
-    fn trait_def_id(self, tcx: I) -> I::DefId {
-        self.trait_def_id(tcx)
+    fn trait_def_id(self, cx: I) -> I::DefId {
+        self.trait_def_id(cx)
     }
 
     fn probe_and_match_goal_against_assumption(
@@ -105,7 +105,7 @@ where
     ) -> Result<Candidate<I>, NoSolution> {
         if let Some(projection_pred) = assumption.as_projection_clause() {
             if projection_pred.projection_def_id() == goal.predicate.def_id() {
-                let tcx = ecx.cx();
+                let cx = ecx.cx();
                 ecx.probe_trait_candidate(source).enter(|ecx| {
                     let assumption_projection_pred =
                         ecx.instantiate_binder_with_infer(projection_pred);
@@ -120,9 +120,9 @@ where
                     // Add GAT where clauses from the trait's definition
                     ecx.add_goals(
                         GoalSource::Misc,
-                        tcx.own_predicates_of(goal.predicate.def_id())
-                            .iter_instantiated(tcx, goal.predicate.alias.args)
-                            .map(|pred| goal.with(tcx, pred)),
+                        cx.own_predicates_of(goal.predicate.def_id())
+                            .iter_instantiated(cx, goal.predicate.alias.args)
+                            .map(|pred| goal.with(cx, pred)),
                     );
 
                     then(ecx)
@@ -140,19 +140,19 @@ where
         goal: Goal<I, NormalizesTo<I>>,
         impl_def_id: I::DefId,
     ) -> Result<Candidate<I>, NoSolution> {
-        let tcx = ecx.cx();
+        let cx = ecx.cx();
 
-        let goal_trait_ref = goal.predicate.alias.trait_ref(tcx);
-        let impl_trait_ref = tcx.impl_trait_ref(impl_def_id);
+        let goal_trait_ref = goal.predicate.alias.trait_ref(cx);
+        let impl_trait_ref = cx.impl_trait_ref(impl_def_id);
         if !ecx.cx().args_may_unify_deep(
-            goal.predicate.alias.trait_ref(tcx).args,
+            goal.predicate.alias.trait_ref(cx).args,
             impl_trait_ref.skip_binder().args,
         ) {
             return Err(NoSolution);
         }
 
         // We have to ignore negative impls when projecting.
-        let impl_polarity = tcx.impl_polarity(impl_def_id);
+        let impl_polarity = cx.impl_polarity(impl_def_id);
         match impl_polarity {
             ty::ImplPolarity::Negative => return Err(NoSolution),
             ty::ImplPolarity::Reservation => {
@@ -163,22 +163,22 @@ where
 
         ecx.probe_trait_candidate(CandidateSource::Impl(impl_def_id)).enter(|ecx| {
             let impl_args = ecx.fresh_args_for_item(impl_def_id);
-            let impl_trait_ref = impl_trait_ref.instantiate(tcx, impl_args);
+            let impl_trait_ref = impl_trait_ref.instantiate(cx, impl_args);
 
             ecx.eq(goal.param_env, goal_trait_ref, impl_trait_ref)?;
 
-            let where_clause_bounds = tcx
+            let where_clause_bounds = cx
                 .predicates_of(impl_def_id)
-                .iter_instantiated(tcx, impl_args)
-                .map(|pred| goal.with(tcx, pred));
+                .iter_instantiated(cx, impl_args)
+                .map(|pred| goal.with(cx, pred));
             ecx.add_goals(GoalSource::ImplWhereBound, where_clause_bounds);
 
             // Add GAT where clauses from the trait's definition
             ecx.add_goals(
                 GoalSource::Misc,
-                tcx.own_predicates_of(goal.predicate.def_id())
-                    .iter_instantiated(tcx, goal.predicate.alias.args)
-                    .map(|pred| goal.with(tcx, pred)),
+                cx.own_predicates_of(goal.predicate.def_id())
+                    .iter_instantiated(cx, goal.predicate.alias.args)
+                    .map(|pred| goal.with(cx, pred)),
             );
 
             // In case the associated item is hidden due to specialization, we have to
@@ -195,21 +195,21 @@ where
             };
 
             let error_response = |ecx: &mut EvalCtxt<'_, D>, msg: &str| {
-                let guar = tcx.delay_bug(msg);
-                let error_term = match goal.predicate.alias.kind(tcx) {
-                    ty::AliasTermKind::ProjectionTy => Ty::new_error(tcx, guar).into(),
-                    ty::AliasTermKind::ProjectionConst => Const::new_error(tcx, guar).into(),
+                let guar = cx.delay_bug(msg);
+                let error_term = match goal.predicate.alias.kind(cx) {
+                    ty::AliasTermKind::ProjectionTy => Ty::new_error(cx, guar).into(),
+                    ty::AliasTermKind::ProjectionConst => Const::new_error(cx, guar).into(),
                     kind => panic!("expected projection, found {kind:?}"),
                 };
                 ecx.instantiate_normalizes_to_term(goal, error_term);
                 ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
             };
 
-            if !tcx.has_item_definition(target_item_def_id) {
+            if !cx.has_item_definition(target_item_def_id) {
                 return error_response(ecx, "missing item");
             }
 
-            let target_container_def_id = tcx.parent(target_item_def_id);
+            let target_container_def_id = cx.parent(target_item_def_id);
 
             // Getting the right args here is complex, e.g. given:
             // - a goal `<Vec<u32> as Trait<i32>>::Assoc<u64>`
@@ -229,22 +229,22 @@ where
                 target_container_def_id,
             )?;
 
-            if !tcx.check_args_compatible(target_item_def_id, target_args) {
+            if !cx.check_args_compatible(target_item_def_id, target_args) {
                 return error_response(ecx, "associated item has mismatched arguments");
             }
 
             // Finally we construct the actual value of the associated type.
-            let term = match goal.predicate.alias.kind(tcx) {
+            let term = match goal.predicate.alias.kind(cx) {
                 ty::AliasTermKind::ProjectionTy => {
-                    tcx.type_of(target_item_def_id).map_bound(|ty| ty.into())
+                    cx.type_of(target_item_def_id).map_bound(|ty| ty.into())
                 }
                 ty::AliasTermKind::ProjectionConst => {
-                    if tcx.features().associated_const_equality() {
+                    if cx.features().associated_const_equality() {
                         panic!("associated const projection is not supported yet")
                     } else {
                         ty::EarlyBinder::bind(
                             Const::new_error_with_message(
-                                tcx,
+                                cx,
                                 "associated const projection is not supported yet",
                             )
                             .into(),
@@ -254,7 +254,7 @@ where
                 kind => panic!("expected projection, found {kind:?}"),
             };
 
-            ecx.instantiate_normalizes_to_term(goal, term.instantiate(tcx, target_args));
+            ecx.instantiate_normalizes_to_term(goal, term.instantiate(cx, target_args));
             ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
         })
     }
@@ -316,10 +316,10 @@ where
         goal: Goal<I, Self>,
         goal_kind: ty::ClosureKind,
     ) -> Result<Candidate<I>, NoSolution> {
-        let tcx = ecx.cx();
+        let cx = ecx.cx();
         let tupled_inputs_and_output =
             match structural_traits::extract_tupled_inputs_and_output_from_callable(
-                tcx,
+                cx,
                 goal.predicate.self_ty(),
                 goal_kind,
             )? {
@@ -329,19 +329,19 @@ where
                 }
             };
         let output_is_sized_pred = tupled_inputs_and_output.map_bound(|(_, output)| {
-            ty::TraitRef::new(tcx, tcx.require_lang_item(TraitSolverLangItem::Sized), [output])
+            ty::TraitRef::new(cx, cx.require_lang_item(TraitSolverLangItem::Sized), [output])
         });
 
         let pred = tupled_inputs_and_output
             .map_bound(|(inputs, output)| ty::ProjectionPredicate {
                 projection_term: ty::AliasTerm::new(
-                    tcx,
+                    cx,
                     goal.predicate.def_id(),
                     [goal.predicate.self_ty(), inputs],
                 ),
                 term: output.into(),
             })
-            .upcast(tcx);
+            .upcast(cx);
 
         // A built-in `Fn` impl only holds if the output is sized.
         // (FIXME: technically we only need to check this if the type is a fn ptr...)
@@ -350,7 +350,7 @@ where
             CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
             goal,
             pred,
-            [(GoalSource::ImplWhereBound, goal.with(tcx, output_is_sized_pred))],
+            [(GoalSource::ImplWhereBound, goal.with(cx, output_is_sized_pred))],
         )
     }
 
@@ -359,27 +359,23 @@ where
         goal: Goal<I, Self>,
         goal_kind: ty::ClosureKind,
     ) -> Result<Candidate<I>, NoSolution> {
-        let tcx = ecx.cx();
+        let cx = ecx.cx();
 
         let env_region = match goal_kind {
             ty::ClosureKind::Fn | ty::ClosureKind::FnMut => goal.predicate.alias.args.region_at(2),
             // Doesn't matter what this region is
-            ty::ClosureKind::FnOnce => Region::new_static(tcx),
+            ty::ClosureKind::FnOnce => Region::new_static(cx),
         };
         let (tupled_inputs_and_output_and_coroutine, nested_preds) =
             structural_traits::extract_tupled_inputs_and_output_from_async_callable(
-                tcx,
+                cx,
                 goal.predicate.self_ty(),
                 goal_kind,
                 env_region,
             )?;
         let output_is_sized_pred = tupled_inputs_and_output_and_coroutine.map_bound(
             |AsyncCallableRelevantTypes { output_coroutine_ty: output_ty, .. }| {
-                ty::TraitRef::new(
-                    tcx,
-                    tcx.require_lang_item(TraitSolverLangItem::Sized),
-                    [output_ty],
-                )
+                ty::TraitRef::new(cx, cx.require_lang_item(TraitSolverLangItem::Sized), [output_ty])
             },
         );
 
@@ -390,23 +386,23 @@ where
                      output_coroutine_ty,
                      coroutine_return_ty,
                  }| {
-                    let (projection_term, term) = if tcx
+                    let (projection_term, term) = if cx
                         .is_lang_item(goal.predicate.def_id(), TraitSolverLangItem::CallOnceFuture)
                     {
                         (
                             ty::AliasTerm::new(
-                                tcx,
+                                cx,
                                 goal.predicate.def_id(),
                                 [goal.predicate.self_ty(), tupled_inputs_ty],
                             ),
                             output_coroutine_ty.into(),
                         )
-                    } else if tcx
+                    } else if cx
                         .is_lang_item(goal.predicate.def_id(), TraitSolverLangItem::CallRefFuture)
                     {
                         (
                             ty::AliasTerm::new(
-                                tcx,
+                                cx,
                                 goal.predicate.def_id(),
                                 [
                                     I::GenericArg::from(goal.predicate.self_ty()),
@@ -416,13 +412,13 @@ where
                             ),
                             output_coroutine_ty.into(),
                         )
-                    } else if tcx.is_lang_item(
+                    } else if cx.is_lang_item(
                         goal.predicate.def_id(),
                         TraitSolverLangItem::AsyncFnOnceOutput,
                     ) {
                         (
                             ty::AliasTerm::new(
-                                tcx,
+                                cx,
                                 goal.predicate.def_id(),
                                 [
                                     I::GenericArg::from(goal.predicate.self_ty()),
@@ -440,7 +436,7 @@ where
                     ty::ProjectionPredicate { projection_term, term }
                 },
             )
-            .upcast(tcx);
+            .upcast(cx);
 
         // A built-in `AsyncFn` impl only holds if the output is sized.
         // (FIXME: technically we only need to check this if the type is a fn ptr...)
@@ -449,9 +445,9 @@ where
             CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
             goal,
             pred,
-            [goal.with(tcx, output_is_sized_pred)]
+            [goal.with(cx, output_is_sized_pred)]
                 .into_iter()
-                .chain(nested_preds.into_iter().map(|pred| goal.with(tcx, pred)))
+                .chain(nested_preds.into_iter().map(|pred| goal.with(cx, pred)))
                 .map(|goal| (GoalSource::ImplWhereBound, goal)),
         )
     }
@@ -514,8 +510,8 @@ where
         ecx: &mut EvalCtxt<'_, D>,
         goal: Goal<I, Self>,
     ) -> Result<Candidate<I>, NoSolution> {
-        let tcx = ecx.cx();
-        let metadata_def_id = tcx.require_lang_item(TraitSolverLangItem::Metadata);
+        let cx = ecx.cx();
+        let metadata_def_id = cx.require_lang_item(TraitSolverLangItem::Metadata);
         assert_eq!(metadata_def_id, goal.predicate.def_id());
         ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
             let metadata_ty = match goal.predicate.self_ty().kind() {
@@ -537,16 +533,16 @@ where
                 | ty::CoroutineWitness(..)
                 | ty::Never
                 | ty::Foreign(..)
-                | ty::Dynamic(_, _, ty::DynStar) => Ty::new_unit(tcx),
+                | ty::Dynamic(_, _, ty::DynStar) => Ty::new_unit(cx),
 
-                ty::Error(e) => Ty::new_error(tcx, e),
+                ty::Error(e) => Ty::new_error(cx, e),
 
-                ty::Str | ty::Slice(_) => Ty::new_usize(tcx),
+                ty::Str | ty::Slice(_) => Ty::new_usize(cx),
 
                 ty::Dynamic(_, _, ty::Dyn) => {
-                    let dyn_metadata = tcx.require_lang_item(TraitSolverLangItem::DynMetadata);
-                    tcx.type_of(dyn_metadata)
-                        .instantiate(tcx, &[I::GenericArg::from(goal.predicate.self_ty())])
+                    let dyn_metadata = cx.require_lang_item(TraitSolverLangItem::DynMetadata);
+                    cx.type_of(dyn_metadata)
+                        .instantiate(cx, &[I::GenericArg::from(goal.predicate.self_ty())])
                 }
 
                 ty::Alias(_, _) | ty::Param(_) | ty::Placeholder(..) => {
@@ -555,26 +551,26 @@ where
                     // FIXME(ptr_metadata): This impl overlaps with the other impls and shouldn't
                     // exist. Instead, `Pointee<Metadata = ()>` should be a supertrait of `Sized`.
                     let sized_predicate = ty::TraitRef::new(
-                        tcx,
-                        tcx.require_lang_item(TraitSolverLangItem::Sized),
+                        cx,
+                        cx.require_lang_item(TraitSolverLangItem::Sized),
                         [I::GenericArg::from(goal.predicate.self_ty())],
                     );
                     // FIXME(-Znext-solver=coinductive): Should this be `GoalSource::ImplWhereBound`?
-                    ecx.add_goal(GoalSource::Misc, goal.with(tcx, sized_predicate));
-                    Ty::new_unit(tcx)
+                    ecx.add_goal(GoalSource::Misc, goal.with(cx, sized_predicate));
+                    Ty::new_unit(cx)
                 }
 
-                ty::Adt(def, args) if def.is_struct() => match def.struct_tail_ty(tcx) {
-                    None => Ty::new_unit(tcx),
+                ty::Adt(def, args) if def.is_struct() => match def.struct_tail_ty(cx) {
+                    None => Ty::new_unit(cx),
                     Some(tail_ty) => {
-                        Ty::new_projection(tcx, metadata_def_id, [tail_ty.instantiate(tcx, args)])
+                        Ty::new_projection(cx, metadata_def_id, [tail_ty.instantiate(cx, args)])
                     }
                 },
-                ty::Adt(_, _) => Ty::new_unit(tcx),
+                ty::Adt(_, _) => Ty::new_unit(cx),
 
                 ty::Tuple(elements) => match elements.last() {
-                    None => Ty::new_unit(tcx),
-                    Some(tail_ty) => Ty::new_projection(tcx, metadata_def_id, [tail_ty]),
+                    None => Ty::new_unit(cx),
+                    Some(tail_ty) => Ty::new_projection(cx, metadata_def_id, [tail_ty]),
                 },
 
                 ty::Infer(
@@ -601,8 +597,8 @@ where
         };
 
         // Coroutines are not futures unless they come from `async` desugaring
-        let tcx = ecx.cx();
-        if !tcx.coroutine_is_async(def_id) {
+        let cx = ecx.cx();
+        if !cx.coroutine_is_async(def_id) {
             return Err(NoSolution);
         }
 
@@ -616,7 +612,7 @@ where
                 projection_term: ty::AliasTerm::new(ecx.cx(), goal.predicate.def_id(), [self_ty]),
                 term,
             }
-            .upcast(tcx),
+            .upcast(cx),
             // Technically, we need to check that the future type is Sized,
             // but that's already proven by the coroutine being WF.
             [],
@@ -633,8 +629,8 @@ where
         };
 
         // Coroutines are not Iterators unless they come from `gen` desugaring
-        let tcx = ecx.cx();
-        if !tcx.coroutine_is_gen(def_id) {
+        let cx = ecx.cx();
+        if !cx.coroutine_is_gen(def_id) {
             return Err(NoSolution);
         }
 
@@ -648,7 +644,7 @@ where
                 projection_term: ty::AliasTerm::new(ecx.cx(), goal.predicate.def_id(), [self_ty]),
                 term,
             }
-            .upcast(tcx),
+            .upcast(cx),
             // Technically, we need to check that the iterator type is Sized,
             // but that's already proven by the generator being WF.
             [],
@@ -672,8 +668,8 @@ where
         };
 
         // Coroutines are not AsyncIterators unless they come from `gen` desugaring
-        let tcx = ecx.cx();
-        if !tcx.coroutine_is_async_gen(def_id) {
+        let cx = ecx.cx();
+        if !cx.coroutine_is_async_gen(def_id) {
             return Err(NoSolution);
         }
 
@@ -682,12 +678,12 @@ where
             // Take `AsyncIterator<Item = I>` and turn it into the corresponding
             // coroutine yield ty `Poll<Option<I>>`.
             let wrapped_expected_ty = Ty::new_adt(
-                tcx,
-                tcx.adt_def(tcx.require_lang_item(TraitSolverLangItem::Poll)),
-                tcx.mk_args(&[Ty::new_adt(
-                    tcx,
-                    tcx.adt_def(tcx.require_lang_item(TraitSolverLangItem::Option)),
-                    tcx.mk_args(&[expected_ty.into()]),
+                cx,
+                cx.adt_def(cx.require_lang_item(TraitSolverLangItem::Poll)),
+                cx.mk_args(&[Ty::new_adt(
+                    cx,
+                    cx.adt_def(cx.require_lang_item(TraitSolverLangItem::Option)),
+                    cx.mk_args(&[expected_ty.into()]),
                 )
                 .into()]),
             );
@@ -708,18 +704,17 @@ where
         };
 
         // `async`-desugared coroutines do not implement the coroutine trait
-        let tcx = ecx.cx();
-        if !tcx.is_general_coroutine(def_id) {
+        let cx = ecx.cx();
+        if !cx.is_general_coroutine(def_id) {
             return Err(NoSolution);
         }
 
         let coroutine = args.as_coroutine();
 
-        let term = if tcx
-            .is_lang_item(goal.predicate.def_id(), TraitSolverLangItem::CoroutineReturn)
+        let term = if cx.is_lang_item(goal.predicate.def_id(), TraitSolverLangItem::CoroutineReturn)
         {
             coroutine.return_ty().into()
-        } else if tcx.is_lang_item(goal.predicate.def_id(), TraitSolverLangItem::CoroutineYield) {
+        } else if cx.is_lang_item(goal.predicate.def_id(), TraitSolverLangItem::CoroutineYield) {
             coroutine.yield_ty().into()
         } else {
             panic!("unexpected associated item `{:?}` for `{self_ty:?}`", goal.predicate.def_id())
@@ -737,7 +732,7 @@ where
                 ),
                 term,
             }
-            .upcast(tcx),
+            .upcast(cx),
             // Technically, we need to check that the coroutine type is Sized,
             // but that's already proven by the coroutine being WF.
             [],
@@ -884,29 +879,29 @@ where
         impl_trait_ref: rustc_type_ir::TraitRef<I>,
         target_container_def_id: I::DefId,
     ) -> Result<I::GenericArgs, NoSolution> {
-        let tcx = self.cx();
+        let cx = self.cx();
         Ok(if target_container_def_id == impl_trait_ref.def_id {
             // Default value from the trait definition. No need to rebase.
             goal.predicate.alias.args
         } else if target_container_def_id == impl_def_id {
             // Same impl, no need to fully translate, just a rebase from
             // the trait is sufficient.
-            goal.predicate.alias.args.rebase_onto(tcx, impl_trait_ref.def_id, impl_args)
+            goal.predicate.alias.args.rebase_onto(cx, impl_trait_ref.def_id, impl_args)
         } else {
             let target_args = self.fresh_args_for_item(target_container_def_id);
             let target_trait_ref =
-                tcx.impl_trait_ref(target_container_def_id).instantiate(tcx, target_args);
+                cx.impl_trait_ref(target_container_def_id).instantiate(cx, target_args);
             // Relate source impl to target impl by equating trait refs.
             self.eq(goal.param_env, impl_trait_ref, target_trait_ref)?;
             // Also add predicates since they may be needed to constrain the
             // target impl's params.
             self.add_goals(
                 GoalSource::Misc,
-                tcx.predicates_of(target_container_def_id)
-                    .iter_instantiated(tcx, target_args)
-                    .map(|pred| goal.with(tcx, pred)),
+                cx.predicates_of(target_container_def_id)
+                    .iter_instantiated(cx, target_args)
+                    .map(|pred| goal.with(cx, pred)),
             );
-            goal.predicate.alias.args.rebase_onto(tcx, impl_trait_ref.def_id, target_args)
+            goal.predicate.alias.args.rebase_onto(cx, impl_trait_ref.def_id, target_args)
         })
     }
 }
diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs
index a16f9e64f2f..120f96d24bd 100644
--- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs
@@ -18,7 +18,7 @@ where
         &mut self,
         goal: Goal<I, ty::NormalizesTo<I>>,
     ) -> QueryResult<I> {
-        let tcx = self.cx();
+        let cx = self.cx();
         let opaque_ty = goal.predicate.alias;
         let expected = goal.predicate.term.as_type().expect("no such thing as an opaque const");
 
@@ -86,7 +86,7 @@ where
             }
             (Reveal::All, _) => {
                 // FIXME: Add an assertion that opaque type storage is empty.
-                let actual = tcx.type_of(opaque_ty.def_id).instantiate(tcx, opaque_ty.args);
+                let actual = cx.type_of(opaque_ty.def_id).instantiate(cx, opaque_ty.args);
                 self.eq(goal.param_env, expected, actual)?;
                 self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
             }
@@ -98,7 +98,7 @@ where
 ///
 /// FIXME: Interner argument is needed to constrain the `I` parameter.
 pub fn uses_unique_placeholders_ignoring_regions<I: Interner>(
-    _interner: I,
+    _cx: I,
     args: I::GenericArgs,
 ) -> Result<(), NotUniqueParam<I>> {
     let mut seen = GrowableBitSet::default();
diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/weak_types.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/weak_types.rs
index ca90bc17cc7..14e68dd52b6 100644
--- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/weak_types.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/weak_types.rs
@@ -18,18 +18,18 @@ where
         &mut self,
         goal: Goal<I, ty::NormalizesTo<I>>,
     ) -> QueryResult<I> {
-        let tcx = self.cx();
+        let cx = self.cx();
         let weak_ty = goal.predicate.alias;
 
         // Check where clauses
         self.add_goals(
             GoalSource::Misc,
-            tcx.predicates_of(weak_ty.def_id)
-                .iter_instantiated(tcx, weak_ty.args)
-                .map(|pred| goal.with(tcx, pred)),
+            cx.predicates_of(weak_ty.def_id)
+                .iter_instantiated(cx, weak_ty.args)
+                .map(|pred| goal.with(cx, pred)),
         );
 
-        let actual = tcx.type_of(weak_ty.def_id).instantiate(tcx, weak_ty.args);
+        let actual = cx.type_of(weak_ty.def_id).instantiate(cx, weak_ty.args);
         self.instantiate_normalizes_to_term(goal, actual.into());
 
         self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
diff --git a/compiler/rustc_next_trait_solver/src/solve/project_goals.rs b/compiler/rustc_next_trait_solver/src/solve/project_goals.rs
index a430dbb408c..d9452880071 100644
--- a/compiler/rustc_next_trait_solver/src/solve/project_goals.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/project_goals.rs
@@ -14,10 +14,10 @@ where
         &mut self,
         goal: Goal<I, ProjectionPredicate<I>>,
     ) -> QueryResult<I> {
-        let tcx = self.cx();
-        let projection_term = goal.predicate.projection_term.to_term(tcx);
+        let cx = self.cx();
+        let projection_term = goal.predicate.projection_term.to_term(cx);
         let goal = goal.with(
-            tcx,
+            cx,
             ty::PredicateKind::AliasRelate(
                 projection_term,
                 goal.predicate.term,
diff --git a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs
index d3ad55d6491..2cd3b10f56a 100644
--- a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs
@@ -164,7 +164,7 @@ impl<I: Interner> SearchGraph<I> {
     /// the remaining depth of all nested goals to prevent hangs
     /// in case there is exponential blowup.
     fn allowed_depth_for_nested(
-        tcx: I,
+        cx: I,
         stack: &IndexVec<StackDepth, StackEntry<I>>,
     ) -> Option<SolverLimit> {
         if let Some(last) = stack.raw.last() {
@@ -178,18 +178,18 @@ impl<I: Interner> SearchGraph<I> {
                 SolverLimit(last.available_depth.0 - 1)
             })
         } else {
-            Some(SolverLimit(tcx.recursion_limit()))
+            Some(SolverLimit(cx.recursion_limit()))
         }
     }
 
     fn stack_coinductive_from(
-        tcx: I,
+        cx: I,
         stack: &IndexVec<StackDepth, StackEntry<I>>,
         head: StackDepth,
     ) -> bool {
         stack.raw[head.index()..]
             .iter()
-            .all(|entry| entry.input.value.goal.predicate.is_coinductive(tcx))
+            .all(|entry| entry.input.value.goal.predicate.is_coinductive(cx))
     }
 
     // When encountering a solver cycle, the result of the current goal
@@ -247,8 +247,8 @@ impl<I: Interner> SearchGraph<I> {
     /// so we use a separate cache. Alternatively we could use
     /// a single cache and share it between coherence and ordinary
     /// trait solving.
-    pub(super) fn global_cache(&self, tcx: I) -> I::EvaluationCache {
-        tcx.evaluation_cache(self.mode)
+    pub(super) fn global_cache(&self, cx: I) -> I::EvaluationCache {
+        cx.evaluation_cache(self.mode)
     }
 
     /// Probably the most involved method of the whole solver.
@@ -257,24 +257,24 @@ impl<I: Interner> SearchGraph<I> {
     /// handles caching, overflow, and coinductive cycles.
     pub(super) fn with_new_goal<D: SolverDelegate<Interner = I>>(
         &mut self,
-        tcx: I,
+        cx: I,
         input: CanonicalInput<I>,
         inspect: &mut ProofTreeBuilder<D>,
         mut prove_goal: impl FnMut(&mut Self, &mut ProofTreeBuilder<D>) -> QueryResult<I>,
     ) -> QueryResult<I> {
         self.check_invariants();
         // Check for overflow.
-        let Some(available_depth) = Self::allowed_depth_for_nested(tcx, &self.stack) else {
+        let Some(available_depth) = Self::allowed_depth_for_nested(cx, &self.stack) else {
             if let Some(last) = self.stack.raw.last_mut() {
                 last.encountered_overflow = true;
             }
 
             inspect
                 .canonical_goal_evaluation_kind(inspect::WipCanonicalGoalEvaluationKind::Overflow);
-            return Self::response_no_constraints(tcx, input, Certainty::overflow(true));
+            return Self::response_no_constraints(cx, input, Certainty::overflow(true));
         };
 
-        if let Some(result) = self.lookup_global_cache(tcx, input, available_depth, inspect) {
+        if let Some(result) = self.lookup_global_cache(cx, input, available_depth, inspect) {
             debug!("global cache hit");
             return result;
         }
@@ -287,12 +287,12 @@ impl<I: Interner> SearchGraph<I> {
         if let Some(entry) = cache_entry
             .with_coinductive_stack
             .as_ref()
-            .filter(|p| Self::stack_coinductive_from(tcx, &self.stack, p.head))
+            .filter(|p| Self::stack_coinductive_from(cx, &self.stack, p.head))
             .or_else(|| {
                 cache_entry
                     .with_inductive_stack
                     .as_ref()
-                    .filter(|p| !Self::stack_coinductive_from(tcx, &self.stack, p.head))
+                    .filter(|p| !Self::stack_coinductive_from(cx, &self.stack, p.head))
             })
         {
             debug!("provisional cache hit");
@@ -315,7 +315,7 @@ impl<I: Interner> SearchGraph<I> {
             inspect.canonical_goal_evaluation_kind(
                 inspect::WipCanonicalGoalEvaluationKind::CycleInStack,
             );
-            let is_coinductive_cycle = Self::stack_coinductive_from(tcx, &self.stack, stack_depth);
+            let is_coinductive_cycle = Self::stack_coinductive_from(cx, &self.stack, stack_depth);
             let usage_kind = if is_coinductive_cycle {
                 HasBeenUsed::COINDUCTIVE_CYCLE
             } else {
@@ -328,9 +328,9 @@ impl<I: Interner> SearchGraph<I> {
             return if let Some(result) = self.stack[stack_depth].provisional_result {
                 result
             } else if is_coinductive_cycle {
-                Self::response_no_constraints(tcx, input, Certainty::Yes)
+                Self::response_no_constraints(cx, input, Certainty::Yes)
             } else {
-                Self::response_no_constraints(tcx, input, Certainty::overflow(false))
+                Self::response_no_constraints(cx, input, Certainty::overflow(false))
             };
         } else {
             // No entry, we push this goal on the stack and try to prove it.
@@ -355,9 +355,9 @@ impl<I: Interner> SearchGraph<I> {
         // not tracked by the cache key and from outside of this anon task, it
         // must not be added to the global cache. Notably, this is the case for
         // trait solver cycles participants.
-        let ((final_entry, result), dep_node) = tcx.with_cached_task(|| {
+        let ((final_entry, result), dep_node) = cx.with_cached_task(|| {
             for _ in 0..FIXPOINT_STEP_LIMIT {
-                match self.fixpoint_step_in_task(tcx, input, inspect, &mut prove_goal) {
+                match self.fixpoint_step_in_task(cx, input, inspect, &mut prove_goal) {
                     StepResult::Done(final_entry, result) => return (final_entry, result),
                     StepResult::HasChanged => debug!("fixpoint changed provisional results"),
                 }
@@ -366,17 +366,17 @@ impl<I: Interner> SearchGraph<I> {
             debug!("canonical cycle overflow");
             let current_entry = self.pop_stack();
             debug_assert!(current_entry.has_been_used.is_empty());
-            let result = Self::response_no_constraints(tcx, input, Certainty::overflow(false));
+            let result = Self::response_no_constraints(cx, input, Certainty::overflow(false));
             (current_entry, result)
         });
 
-        let proof_tree = inspect.finalize_canonical_goal_evaluation(tcx);
+        let proof_tree = inspect.finalize_canonical_goal_evaluation(cx);
 
         // We're now done with this goal. In case this goal is involved in a larger cycle
         // do not remove it from the provisional cache and update its provisional result.
         // We only add the root of cycles to the global cache.
         if let Some(head) = final_entry.non_root_cycle_participant {
-            let coinductive_stack = Self::stack_coinductive_from(tcx, &self.stack, head);
+            let coinductive_stack = Self::stack_coinductive_from(cx, &self.stack, head);
 
             let entry = self.provisional_cache.get_mut(&input).unwrap();
             entry.stack_depth = None;
@@ -396,8 +396,8 @@ impl<I: Interner> SearchGraph<I> {
             // participant is on the stack. This is necessary to prevent unstable
             // results. See the comment of `StackEntry::cycle_participants` for
             // more details.
-            self.global_cache(tcx).insert(
-                tcx,
+            self.global_cache(cx).insert(
+                cx,
                 input,
                 proof_tree,
                 reached_depth,
@@ -418,15 +418,15 @@ impl<I: Interner> SearchGraph<I> {
     /// this goal.
     fn lookup_global_cache<D: SolverDelegate<Interner = I>>(
         &mut self,
-        tcx: I,
+        cx: I,
         input: CanonicalInput<I>,
         available_depth: SolverLimit,
         inspect: &mut ProofTreeBuilder<D>,
     ) -> Option<QueryResult<I>> {
         let CacheData { result, proof_tree, additional_depth, encountered_overflow } = self
-            .global_cache(tcx)
+            .global_cache(cx)
             // FIXME: Awkward `Limit -> usize -> Limit`.
-            .get(tcx, input, self.stack.iter().map(|e| e.input), available_depth.0)?;
+            .get(cx, input, self.stack.iter().map(|e| e.input), available_depth.0)?;
 
         // If we're building a proof tree and the current cache entry does not
         // contain a proof tree, we do not use the entry but instead recompute
@@ -467,7 +467,7 @@ impl<I: Interner> SearchGraph<I> {
     /// point we are done.
     fn fixpoint_step_in_task<D, F>(
         &mut self,
-        tcx: I,
+        cx: I,
         input: CanonicalInput<I>,
         inspect: &mut ProofTreeBuilder<D>,
         prove_goal: &mut F,
@@ -506,9 +506,9 @@ impl<I: Interner> SearchGraph<I> {
         let reached_fixpoint = if let Some(r) = stack_entry.provisional_result {
             r == result
         } else if stack_entry.has_been_used == HasBeenUsed::COINDUCTIVE_CYCLE {
-            Self::response_no_constraints(tcx, input, Certainty::Yes) == result
+            Self::response_no_constraints(cx, input, Certainty::Yes) == result
         } else if stack_entry.has_been_used == HasBeenUsed::INDUCTIVE_CYCLE {
-            Self::response_no_constraints(tcx, input, Certainty::overflow(false)) == result
+            Self::response_no_constraints(cx, input, Certainty::overflow(false)) == result
         } else {
             false
         };
@@ -528,11 +528,11 @@ impl<I: Interner> SearchGraph<I> {
     }
 
     fn response_no_constraints(
-        tcx: I,
+        cx: I,
         goal: CanonicalInput<I>,
         certainty: Certainty,
     ) -> QueryResult<I> {
-        Ok(super::response_no_constraints_raw(tcx, goal.max_universe, goal.variables, certainty))
+        Ok(super::response_no_constraints_raw(cx, goal.max_universe, goal.variables, certainty))
     }
 
     #[allow(rustc::potential_query_instability)]
diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
index 9746c836aff..2bc9d35c2b0 100644
--- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
@@ -30,8 +30,8 @@ where
         self.trait_ref
     }
 
-    fn with_self_ty(self, tcx: I, self_ty: I::Ty) -> Self {
-        self.with_self_ty(tcx, self_ty)
+    fn with_self_ty(self, cx: I, self_ty: I::Ty) -> Self {
+        self.with_self_ty(cx, self_ty)
     }
 
     fn trait_def_id(self, _: I) -> I::DefId {
@@ -43,18 +43,17 @@ where
         goal: Goal<I, TraitPredicate<I>>,
         impl_def_id: I::DefId,
     ) -> Result<Candidate<I>, NoSolution> {
-        let tcx = ecx.cx();
+        let cx = ecx.cx();
 
-        let impl_trait_ref = tcx.impl_trait_ref(impl_def_id);
-        if !tcx
-            .args_may_unify_deep(goal.predicate.trait_ref.args, impl_trait_ref.skip_binder().args)
+        let impl_trait_ref = cx.impl_trait_ref(impl_def_id);
+        if !cx.args_may_unify_deep(goal.predicate.trait_ref.args, impl_trait_ref.skip_binder().args)
         {
             return Err(NoSolution);
         }
 
         // An upper bound of the certainty of this goal, used to lower the certainty
         // of reservation impl to ambiguous during coherence.
-        let impl_polarity = tcx.impl_polarity(impl_def_id);
+        let impl_polarity = cx.impl_polarity(impl_def_id);
         let maximal_certainty = match (impl_polarity, goal.predicate.polarity) {
             // In intercrate mode, this is ambiguous. But outside of intercrate,
             // it's not a real impl.
@@ -77,13 +76,13 @@ where
         ecx.probe_trait_candidate(CandidateSource::Impl(impl_def_id)).enter(|ecx| {
             let impl_args = ecx.fresh_args_for_item(impl_def_id);
             ecx.record_impl_args(impl_args);
-            let impl_trait_ref = impl_trait_ref.instantiate(tcx, impl_args);
+            let impl_trait_ref = impl_trait_ref.instantiate(cx, impl_args);
 
             ecx.eq(goal.param_env, goal.predicate.trait_ref, impl_trait_ref)?;
-            let where_clause_bounds = tcx
+            let where_clause_bounds = cx
                 .predicates_of(impl_def_id)
-                .iter_instantiated(tcx, impl_args)
-                .map(|pred| goal.with(tcx, pred));
+                .iter_instantiated(cx, impl_args)
+                .map(|pred| goal.with(cx, pred));
             ecx.add_goals(GoalSource::ImplWhereBound, where_clause_bounds);
 
             ecx.evaluate_added_goals_and_make_canonical_response(maximal_certainty)
@@ -181,13 +180,13 @@ where
             return Err(NoSolution);
         }
 
-        let tcx = ecx.cx();
+        let cx = ecx.cx();
 
         ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
-            let nested_obligations = tcx
+            let nested_obligations = cx
                 .predicates_of(goal.predicate.def_id())
-                .iter_instantiated(tcx, goal.predicate.trait_ref.args)
-                .map(|p| goal.with(tcx, p));
+                .iter_instantiated(cx, goal.predicate.trait_ref.args)
+                .map(|p| goal.with(cx, p));
             // FIXME(-Znext-solver=coinductive): Should this be `GoalSource::ImplWhereBound`?
             ecx.add_goals(GoalSource::Misc, nested_obligations);
             ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
@@ -232,13 +231,13 @@ where
             return Err(NoSolution);
         }
 
-        let tcx = ecx.cx();
+        let cx = ecx.cx();
         // But if there are inference variables, we have to wait until it's resolved.
         if (goal.param_env, goal.predicate.self_ty()).has_non_region_infer() {
             return ecx.forced_ambiguity(MaybeCause::Ambiguity);
         }
 
-        if tcx.layout_is_pointer_like(goal.param_env, goal.predicate.self_ty()) {
+        if cx.layout_is_pointer_like(goal.param_env, goal.predicate.self_ty()) {
             ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc)
                 .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
         } else {
@@ -286,10 +285,10 @@ where
             return Err(NoSolution);
         }
 
-        let tcx = ecx.cx();
+        let cx = ecx.cx();
         let tupled_inputs_and_output =
             match structural_traits::extract_tupled_inputs_and_output_from_callable(
-                tcx,
+                cx,
                 goal.predicate.self_ty(),
                 goal_kind,
             )? {
@@ -299,14 +298,14 @@ where
                 }
             };
         let output_is_sized_pred = tupled_inputs_and_output.map_bound(|(_, output)| {
-            ty::TraitRef::new(tcx, tcx.require_lang_item(TraitSolverLangItem::Sized), [output])
+            ty::TraitRef::new(cx, cx.require_lang_item(TraitSolverLangItem::Sized), [output])
         });
 
         let pred = tupled_inputs_and_output
             .map_bound(|(inputs, _)| {
-                ty::TraitRef::new(tcx, goal.predicate.def_id(), [goal.predicate.self_ty(), inputs])
+                ty::TraitRef::new(cx, goal.predicate.def_id(), [goal.predicate.self_ty(), inputs])
             })
-            .upcast(tcx);
+            .upcast(cx);
         // A built-in `Fn` impl only holds if the output is sized.
         // (FIXME: technically we only need to check this if the type is a fn ptr...)
         Self::probe_and_consider_implied_clause(
@@ -314,7 +313,7 @@ where
             CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
             goal,
             pred,
-            [(GoalSource::ImplWhereBound, goal.with(tcx, output_is_sized_pred))],
+            [(GoalSource::ImplWhereBound, goal.with(cx, output_is_sized_pred))],
         )
     }
 
@@ -327,20 +326,20 @@ where
             return Err(NoSolution);
         }
 
-        let tcx = ecx.cx();
+        let cx = ecx.cx();
         let (tupled_inputs_and_output_and_coroutine, nested_preds) =
             structural_traits::extract_tupled_inputs_and_output_from_async_callable(
-                tcx,
+                cx,
                 goal.predicate.self_ty(),
                 goal_kind,
                 // This region doesn't matter because we're throwing away the coroutine type
-                Region::new_static(tcx),
+                Region::new_static(cx),
             )?;
         let output_is_sized_pred = tupled_inputs_and_output_and_coroutine.map_bound(
             |AsyncCallableRelevantTypes { output_coroutine_ty, .. }| {
                 ty::TraitRef::new(
-                    tcx,
-                    tcx.require_lang_item(TraitSolverLangItem::Sized),
+                    cx,
+                    cx.require_lang_item(TraitSolverLangItem::Sized),
                     [output_coroutine_ty],
                 )
             },
@@ -349,12 +348,12 @@ where
         let pred = tupled_inputs_and_output_and_coroutine
             .map_bound(|AsyncCallableRelevantTypes { tupled_inputs_ty, .. }| {
                 ty::TraitRef::new(
-                    tcx,
+                    cx,
                     goal.predicate.def_id(),
                     [goal.predicate.self_ty(), tupled_inputs_ty],
                 )
             })
-            .upcast(tcx);
+            .upcast(cx);
         // A built-in `AsyncFn` impl only holds if the output is sized.
         // (FIXME: technically we only need to check this if the type is a fn ptr...)
         Self::probe_and_consider_implied_clause(
@@ -362,9 +361,9 @@ where
             CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
             goal,
             pred,
-            [goal.with(tcx, output_is_sized_pred)]
+            [goal.with(cx, output_is_sized_pred)]
                 .into_iter()
-                .chain(nested_preds.into_iter().map(|pred| goal.with(tcx, pred)))
+                .chain(nested_preds.into_iter().map(|pred| goal.with(cx, pred)))
                 .map(|goal| (GoalSource::ImplWhereBound, goal)),
         )
     }
@@ -437,8 +436,8 @@ where
         };
 
         // Coroutines are not futures unless they come from `async` desugaring
-        let tcx = ecx.cx();
-        if !tcx.coroutine_is_async(def_id) {
+        let cx = ecx.cx();
+        if !cx.coroutine_is_async(def_id) {
             return Err(NoSolution);
         }
 
@@ -463,8 +462,8 @@ where
         };
 
         // Coroutines are not iterators unless they come from `gen` desugaring
-        let tcx = ecx.cx();
-        if !tcx.coroutine_is_gen(def_id) {
+        let cx = ecx.cx();
+        if !cx.coroutine_is_gen(def_id) {
             return Err(NoSolution);
         }
 
@@ -489,8 +488,8 @@ where
         };
 
         // Coroutines are not iterators unless they come from `gen` desugaring
-        let tcx = ecx.cx();
-        if !tcx.coroutine_is_gen(def_id) {
+        let cx = ecx.cx();
+        if !cx.coroutine_is_gen(def_id) {
             return Err(NoSolution);
         }
 
@@ -513,8 +512,8 @@ where
         };
 
         // Coroutines are not iterators unless they come from `gen` desugaring
-        let tcx = ecx.cx();
-        if !tcx.coroutine_is_async_gen(def_id) {
+        let cx = ecx.cx();
+        if !cx.coroutine_is_async_gen(def_id) {
             return Err(NoSolution);
         }
 
@@ -540,8 +539,8 @@ where
         };
 
         // `async`-desugared coroutines do not implement the coroutine trait
-        let tcx = ecx.cx();
-        if !tcx.is_general_coroutine(def_id) {
+        let cx = ecx.cx();
+        if !cx.is_general_coroutine(def_id) {
             return Err(NoSolution);
         }
 
@@ -550,8 +549,8 @@ where
             ecx,
             CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
             goal,
-            ty::TraitRef::new(tcx, goal.predicate.def_id(), [self_ty, coroutine.resume_ty()])
-                .upcast(tcx),
+            ty::TraitRef::new(cx, goal.predicate.def_id(), [self_ty, coroutine.resume_ty()])
+                .upcast(cx),
             // Technically, we need to check that the coroutine types are Sized,
             // but that's already proven by the coroutine being WF.
             [],
@@ -727,7 +726,7 @@ where
         b_data: I::BoundExistentialPredicates,
         b_region: I::Region,
     ) -> Vec<Candidate<I>> {
-        let tcx = self.cx();
+        let cx = self.cx();
         let Goal { predicate: (a_ty, _b_ty), .. } = goal;
 
         let mut responses = vec![];
@@ -745,7 +744,7 @@ where
             ));
         } else if let Some(a_principal) = a_data.principal() {
             for new_a_principal in
-                D::elaborate_supertraits(self.cx(), a_principal.with_self_ty(tcx, a_ty)).skip(1)
+                D::elaborate_supertraits(self.cx(), a_principal.with_self_ty(cx, a_ty)).skip(1)
             {
                 responses.extend(self.consider_builtin_upcast_to_principal(
                     goal,
@@ -755,7 +754,7 @@ where
                     b_data,
                     b_region,
                     Some(new_a_principal.map_bound(|trait_ref| {
-                        ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)
+                        ty::ExistentialTraitRef::erase_self_ty(cx, trait_ref)
                     })),
                 ));
             }
@@ -770,11 +769,11 @@ where
         b_data: I::BoundExistentialPredicates,
         b_region: I::Region,
     ) -> Result<Candidate<I>, NoSolution> {
-        let tcx = self.cx();
+        let cx = self.cx();
         let Goal { predicate: (a_ty, _), .. } = goal;
 
         // Can only unsize to an object-safe trait.
-        if b_data.principal_def_id().is_some_and(|def_id| !tcx.trait_is_object_safe(def_id)) {
+        if b_data.principal_def_id().is_some_and(|def_id| !cx.trait_is_object_safe(def_id)) {
             return Err(NoSolution);
         }
 
@@ -783,24 +782,20 @@ where
             // (i.e. the principal, all of the associated types match, and any auto traits)
             ecx.add_goals(
                 GoalSource::ImplWhereBound,
-                b_data.iter().map(|pred| goal.with(tcx, pred.with_self_ty(tcx, a_ty))),
+                b_data.iter().map(|pred| goal.with(cx, pred.with_self_ty(cx, a_ty))),
             );
 
             // The type must be `Sized` to be unsized.
             ecx.add_goal(
                 GoalSource::ImplWhereBound,
                 goal.with(
-                    tcx,
-                    ty::TraitRef::new(
-                        tcx,
-                        tcx.require_lang_item(TraitSolverLangItem::Sized),
-                        [a_ty],
-                    ),
+                    cx,
+                    ty::TraitRef::new(cx, cx.require_lang_item(TraitSolverLangItem::Sized), [a_ty]),
                 ),
             );
 
             // The type must outlive the lifetime of the `dyn` we're unsizing into.
-            ecx.add_goal(GoalSource::Misc, goal.with(tcx, ty::OutlivesPredicate(a_ty, b_region)));
+            ecx.add_goal(GoalSource::Misc, goal.with(cx, ty::OutlivesPredicate(a_ty, b_region)));
             ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
         })
     }
@@ -941,28 +936,28 @@ where
         a_args: I::GenericArgs,
         b_args: I::GenericArgs,
     ) -> Result<Candidate<I>, NoSolution> {
-        let tcx = self.cx();
+        let cx = self.cx();
         let Goal { predicate: (_a_ty, b_ty), .. } = goal;
 
-        let unsizing_params = tcx.unsizing_params_for_adt(def.def_id());
+        let unsizing_params = cx.unsizing_params_for_adt(def.def_id());
         // We must be unsizing some type parameters. This also implies
         // that the struct has a tail field.
         if unsizing_params.is_empty() {
             return Err(NoSolution);
         }
 
-        let tail_field_ty = def.struct_tail_ty(tcx).unwrap();
+        let tail_field_ty = def.struct_tail_ty(cx).unwrap();
 
-        let a_tail_ty = tail_field_ty.instantiate(tcx, a_args);
-        let b_tail_ty = tail_field_ty.instantiate(tcx, b_args);
+        let a_tail_ty = tail_field_ty.instantiate(cx, a_args);
+        let b_tail_ty = tail_field_ty.instantiate(cx, b_args);
 
         // Instantiate just the unsizing params from B into A. The type after
         // this instantiation must be equal to B. This is so we don't unsize
         // unrelated type parameters.
-        let new_a_args = tcx.mk_args_from_iter(a_args.iter().enumerate().map(|(i, a)| {
+        let new_a_args = cx.mk_args_from_iter(a_args.iter().enumerate().map(|(i, a)| {
             if unsizing_params.contains(i as u32) { b_args.get(i).unwrap() } else { a }
         }));
-        let unsized_a_ty = Ty::new_adt(tcx, def, new_a_args);
+        let unsized_a_ty = Ty::new_adt(cx, def, new_a_args);
 
         // Finally, we require that `TailA: Unsize<TailB>` for the tail field
         // types.
@@ -970,10 +965,10 @@ where
         self.add_goal(
             GoalSource::ImplWhereBound,
             goal.with(
-                tcx,
+                cx,
                 ty::TraitRef::new(
-                    tcx,
-                    tcx.require_lang_item(TraitSolverLangItem::Unsize),
+                    cx,
+                    cx.require_lang_item(TraitSolverLangItem::Unsize),
                     [a_tail_ty, b_tail_ty],
                 ),
             ),
@@ -998,25 +993,24 @@ where
         a_tys: I::Tys,
         b_tys: I::Tys,
     ) -> Result<Candidate<I>, NoSolution> {
-        let tcx = self.cx();
+        let cx = self.cx();
         let Goal { predicate: (_a_ty, b_ty), .. } = goal;
 
         let (&a_last_ty, a_rest_tys) = a_tys.split_last().unwrap();
         let b_last_ty = b_tys.last().unwrap();
 
         // Instantiate just the tail field of B., and require that they're equal.
-        let unsized_a_ty =
-            Ty::new_tup_from_iter(tcx, a_rest_tys.iter().copied().chain([b_last_ty]));
+        let unsized_a_ty = Ty::new_tup_from_iter(cx, a_rest_tys.iter().copied().chain([b_last_ty]));
         self.eq(goal.param_env, unsized_a_ty, b_ty)?;
 
         // Similar to ADTs, require that we can unsize the tail.
         self.add_goal(
             GoalSource::ImplWhereBound,
             goal.with(
-                tcx,
+                cx,
                 ty::TraitRef::new(
-                    tcx,
-                    tcx.require_lang_item(TraitSolverLangItem::Unsize),
+                    cx,
+                    cx.require_lang_item(TraitSolverLangItem::Unsize),
                     [a_last_ty, b_last_ty],
                 ),
             ),
diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs
index fcd623b477f..d2043c353fe 100644
--- a/compiler/rustc_parse/src/parser/ty.rs
+++ b/compiler/rustc_parse/src/parser/ty.rs
@@ -608,7 +608,7 @@ impl<'a> Parser<'a> {
             self.dcx().emit_err(FnPointerCannotBeAsync { span: whole_span, qualifier: span });
         }
         // FIXME(gen_blocks): emit a similar error for `gen fn()`
-        let decl_span = span_start.to(self.token.span);
+        let decl_span = span_start.to(self.prev_token.span);
         Ok(TyKind::BareFn(P(BareFnTy { ext, safety, generic_params: params, decl, decl_span })))
     }
 
diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs
index 7c7700dd859..bbd586386dd 100644
--- a/compiler/rustc_passes/src/dead.rs
+++ b/compiler/rustc_passes/src/dead.rs
@@ -155,7 +155,10 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
 
     fn handle_res(&mut self, res: Res) {
         match res {
-            Res::Def(DefKind::Const | DefKind::AssocConst | DefKind::TyAlias, def_id) => {
+            Res::Def(
+                DefKind::Const | DefKind::AssocConst | DefKind::AssocTy | DefKind::TyAlias,
+                def_id,
+            ) => {
                 self.check_def_id(def_id);
             }
             _ if self.in_pat => {}
@@ -399,6 +402,31 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
                 return false;
             }
 
+            // don't ignore impls for Enums and pub Structs whose methods don't have self receiver,
+            // cause external crate may call such methods to construct values of these types
+            if let Some(local_impl_of) = impl_of.as_local()
+                && let Some(local_def_id) = def_id.as_local()
+                && let Some(fn_sig) =
+                    self.tcx.hir().fn_sig_by_hir_id(self.tcx.local_def_id_to_hir_id(local_def_id))
+                && matches!(fn_sig.decl.implicit_self, hir::ImplicitSelfKind::None)
+                && let TyKind::Path(hir::QPath::Resolved(_, path)) =
+                    self.tcx.hir().expect_item(local_impl_of).expect_impl().self_ty.kind
+                && let Res::Def(def_kind, did) = path.res
+            {
+                match def_kind {
+                    // for example, #[derive(Default)] pub struct T(i32);
+                    // external crate can call T::default() to construct T,
+                    // so that don't ignore impl Default for pub Enum and Structs
+                    DefKind::Struct | DefKind::Union if self.tcx.visibility(did).is_public() => {
+                        return false;
+                    }
+                    // don't ignore impl Default for Enums,
+                    // cause we don't know which variant is constructed
+                    DefKind::Enum => return false,
+                    _ => (),
+                };
+            }
+
             if let Some(trait_of) = self.tcx.trait_id_of_impl(impl_of)
                 && self.tcx.has_attr(trait_of, sym::rustc_trivial_field_reads)
             {
@@ -441,7 +469,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
                     intravisit::walk_item(self, item)
                 }
                 hir::ItemKind::ForeignMod { .. } => {}
-                hir::ItemKind::Trait(..) => {
+                hir::ItemKind::Trait(_, _, _, _, trait_item_refs) => {
                     for impl_def_id in self.tcx.all_impls(item.owner_id.to_def_id()) {
                         if let Some(local_def_id) = impl_def_id.as_local()
                             && let ItemKind::Impl(impl_ref) =
@@ -454,7 +482,12 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
                             intravisit::walk_path(self, impl_ref.of_trait.unwrap().path);
                         }
                     }
-
+                    // mark assoc ty live if the trait is live
+                    for trait_item in trait_item_refs {
+                        if let hir::AssocItemKind::Type = trait_item.kind {
+                            self.check_def_id(trait_item.id.owner_id.to_def_id());
+                        }
+                    }
                     intravisit::walk_item(self, item)
                 }
                 _ => intravisit::walk_item(self, item),
@@ -471,9 +504,8 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
                             && let ItemKind::Impl(impl_ref) =
                                 self.tcx.hir().expect_item(local_impl_id).kind
                         {
-                            if !matches!(trait_item.kind, hir::TraitItemKind::Type(..))
-                                && !ty_ref_to_pub_struct(self.tcx, impl_ref.self_ty)
-                                    .ty_and_all_fields_are_public
+                            if !ty_ref_to_pub_struct(self.tcx, impl_ref.self_ty)
+                                .ty_and_all_fields_are_public
                             {
                                 // skip impl-items of non pure pub ty,
                                 // cause we don't know the ty is constructed or not,
@@ -812,9 +844,8 @@ fn check_item<'tcx>(
                 // for trait impl blocks,
                 // mark the method live if the self_ty is public,
                 // or the method is public and may construct self
-                if of_trait && matches!(tcx.def_kind(local_def_id), DefKind::AssocTy)
-                    || tcx.visibility(local_def_id).is_public()
-                        && (ty_and_all_fields_are_public || may_construct_self)
+                if tcx.visibility(local_def_id).is_public()
+                    && (ty_and_all_fields_are_public || may_construct_self)
                 {
                     // if the impl item is public,
                     // and the ty may be constructed or can be constructed in foreign crates,
@@ -851,10 +882,13 @@ fn check_trait_item(
     worklist: &mut Vec<(LocalDefId, ComesFromAllowExpect)>,
     id: hir::TraitItemId,
 ) {
-    use hir::TraitItemKind::{Const, Fn};
-    if matches!(tcx.def_kind(id.owner_id), DefKind::AssocConst | DefKind::AssocFn) {
+    use hir::TraitItemKind::{Const, Fn, Type};
+    if matches!(
+        tcx.def_kind(id.owner_id),
+        DefKind::AssocConst | DefKind::AssocTy | DefKind::AssocFn
+    ) {
         let trait_item = tcx.hir().trait_item(id);
-        if matches!(trait_item.kind, Const(_, Some(_)) | Fn(..))
+        if matches!(trait_item.kind, Const(_, Some(_)) | Type(_, Some(_)) | Fn(..))
             && let Some(comes_from_allow) =
                 has_allow_dead_code_or_lang_attr(tcx, trait_item.owner_id.def_id)
         {
@@ -896,7 +930,7 @@ fn create_and_seed_worklist(
                     // checks impls, impl-items and pub structs with all public fields later
                     match tcx.def_kind(id) {
                         DefKind::Impl { .. } => false,
-                        DefKind::AssocConst | DefKind::AssocFn => !matches!(tcx.associated_item(id).container, AssocItemContainer::ImplContainer),
+                        DefKind::AssocConst | DefKind::AssocTy | DefKind::AssocFn => !matches!(tcx.associated_item(id).container, AssocItemContainer::ImplContainer),
                         DefKind::Struct => struct_all_fields_are_public(tcx, id.to_def_id()) || has_allow_dead_code_or_lang_attr(tcx, id).is_some(),
                         _ => true
                     })
@@ -1183,6 +1217,7 @@ impl<'tcx> DeadVisitor<'tcx> {
         }
         match self.tcx.def_kind(def_id) {
             DefKind::AssocConst
+            | DefKind::AssocTy
             | DefKind::AssocFn
             | DefKind::Fn
             | DefKind::Static { .. }
@@ -1224,15 +1259,14 @@ fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) {
             || (def_kind == DefKind::Trait && live_symbols.contains(&item.owner_id.def_id))
         {
             for &def_id in tcx.associated_item_def_ids(item.owner_id.def_id) {
-                // We have diagnosed unused assoc consts and fns in traits
+                // We have diagnosed unused assocs in traits
                 if matches!(def_kind, DefKind::Impl { of_trait: true })
-                    && matches!(tcx.def_kind(def_id), DefKind::AssocConst | DefKind::AssocFn)
+                    && matches!(tcx.def_kind(def_id), DefKind::AssocConst | DefKind::AssocTy | DefKind::AssocFn)
                     // skip unused public inherent methods,
                     // cause we have diagnosed unconstructed struct
                     || matches!(def_kind, DefKind::Impl { of_trait: false })
                         && tcx.visibility(def_id).is_public()
                         && ty_ref_to_pub_struct(tcx, tcx.hir().item(item).expect_impl().self_ty).ty_is_public
-                    || def_kind == DefKind::Trait && tcx.def_kind(def_id) == DefKind::AssocTy
                 {
                     continue;
                 }
diff --git a/compiler/rustc_passes/src/reachable.rs b/compiler/rustc_passes/src/reachable.rs
index 6dd8eaf7e67..da4435ebebe 100644
--- a/compiler/rustc_passes/src/reachable.rs
+++ b/compiler/rustc_passes/src/reachable.rs
@@ -30,7 +30,7 @@ use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_hir::intravisit::{self, Visitor};
 use rustc_hir::Node;
 use rustc_middle::bug;
-use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
+use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
 use rustc_middle::middle::privacy::{self, Level};
 use rustc_middle::mir::interpret::{ConstAllocation, ErrorHandled, GlobalAlloc};
 use rustc_middle::query::Providers;
@@ -178,15 +178,7 @@ impl<'tcx> ReachableContext<'tcx> {
         if !self.any_library {
             // If we are building an executable, only explicitly extern
             // types need to be exported.
-            let codegen_attrs = if self.tcx.def_kind(search_item).has_codegen_attrs() {
-                self.tcx.codegen_fn_attrs(search_item)
-            } else {
-                CodegenFnAttrs::EMPTY
-            };
-            let is_extern = codegen_attrs.contains_extern_indicator();
-            let std_internal =
-                codegen_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL);
-            if is_extern || std_internal {
+            if has_custom_linkage(self.tcx, search_item) {
                 self.reachable_symbols.insert(search_item);
             }
         } else {
diff --git a/compiler/rustc_query_system/src/dep_graph/dep_node.rs b/compiler/rustc_query_system/src/dep_graph/dep_node.rs
index 5f1a03502a7..f2a68e35671 100644
--- a/compiler/rustc_query_system/src/dep_graph/dep_node.rs
+++ b/compiler/rustc_query_system/src/dep_graph/dep_node.rs
@@ -301,9 +301,12 @@ impl<HCX> ToStableHashKey<HCX> for WorkProductId {
         self.hash
     }
 }
-unsafe impl StableOrd for WorkProductId {
+impl StableOrd for WorkProductId {
     // Fingerprint can use unstable (just a tuple of `u64`s), so WorkProductId can as well
     const CAN_USE_UNSTABLE_SORT: bool = true;
+
+    // `WorkProductId` sort order is not affected by (de)serialization.
+    const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();
 }
 
 // Some types are used a lot. Make sure they don't unintentionally get bigger.
diff --git a/compiler/rustc_resolve/src/effective_visibilities.rs b/compiler/rustc_resolve/src/effective_visibilities.rs
index aab4a3366da..dabed238838 100644
--- a/compiler/rustc_resolve/src/effective_visibilities.rs
+++ b/compiler/rustc_resolve/src/effective_visibilities.rs
@@ -99,7 +99,7 @@ impl<'r, 'a, 'tcx> EffectiveVisibilitiesVisitor<'r, 'a, 'tcx> {
         // is the maximum value among visibilities of bindings corresponding to that def id.
         for (binding, eff_vis) in visitor.import_effective_visibilities.iter() {
             let NameBindingKind::Import { import, .. } = binding.kind else { unreachable!() };
-            if !binding.is_ambiguity() {
+            if !binding.is_ambiguity_recursive() {
                 if let Some(node_id) = import.id() {
                     r.effective_visibilities.update_eff_vis(r.local_def_id(node_id), eff_vis, r.tcx)
                 }
diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs
index 96a4647b942..3896fe4c4fa 100644
--- a/compiler/rustc_resolve/src/imports.rs
+++ b/compiler/rustc_resolve/src/imports.rs
@@ -325,8 +325,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                 }
                 match (old_binding.is_glob_import(), binding.is_glob_import()) {
                     (true, true) => {
-                        // FIXME: remove `!binding.is_ambiguity()` after delete the warning ambiguity.
-                        if !binding.is_ambiguity()
+                        // FIXME: remove `!binding.is_ambiguity_recursive()` after delete the warning ambiguity.
+                        if !binding.is_ambiguity_recursive()
                             && let NameBindingKind::Import { import: old_import, .. } =
                                 old_binding.kind
                             && let NameBindingKind::Import { import, .. } = binding.kind
@@ -337,21 +337,17 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                             // imported from the same glob-import statement.
                             resolution.binding = Some(binding);
                         } else if res != old_binding.res() {
-                            let binding = if warn_ambiguity {
-                                this.warn_ambiguity(AmbiguityKind::GlobVsGlob, old_binding, binding)
-                            } else {
-                                this.ambiguity(AmbiguityKind::GlobVsGlob, old_binding, binding)
-                            };
-                            resolution.binding = Some(binding);
+                            resolution.binding = Some(this.new_ambiguity_binding(
+                                AmbiguityKind::GlobVsGlob,
+                                old_binding,
+                                binding,
+                                warn_ambiguity,
+                            ));
                         } else if !old_binding.vis.is_at_least(binding.vis, this.tcx) {
                             // We are glob-importing the same item but with greater visibility.
                             resolution.binding = Some(binding);
-                        } else if binding.is_ambiguity() {
-                            resolution.binding =
-                                Some(self.arenas.alloc_name_binding(NameBindingData {
-                                    warn_ambiguity: true,
-                                    ..(*binding).clone()
-                                }));
+                        } else if binding.is_ambiguity_recursive() {
+                            resolution.binding = Some(this.new_warn_ambiguity_binding(binding));
                         }
                     }
                     (old_glob @ true, false) | (old_glob @ false, true) => {
@@ -361,24 +357,26 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                             && nonglob_binding.expansion != LocalExpnId::ROOT
                             && glob_binding.res() != nonglob_binding.res()
                         {
-                            resolution.binding = Some(this.ambiguity(
+                            resolution.binding = Some(this.new_ambiguity_binding(
                                 AmbiguityKind::GlobVsExpanded,
                                 nonglob_binding,
                                 glob_binding,
+                                false,
                             ));
                         } else {
                             resolution.binding = Some(nonglob_binding);
                         }
 
-                        if let Some(old_binding) = resolution.shadowed_glob {
-                            assert!(old_binding.is_glob_import());
-                            if glob_binding.res() != old_binding.res() {
-                                resolution.shadowed_glob = Some(this.ambiguity(
+                        if let Some(old_shadowed_glob) = resolution.shadowed_glob {
+                            assert!(old_shadowed_glob.is_glob_import());
+                            if glob_binding.res() != old_shadowed_glob.res() {
+                                resolution.shadowed_glob = Some(this.new_ambiguity_binding(
                                     AmbiguityKind::GlobVsGlob,
-                                    old_binding,
+                                    old_shadowed_glob,
                                     glob_binding,
+                                    false,
                                 ));
-                            } else if !old_binding.vis.is_at_least(binding.vis, this.tcx) {
+                            } else if !old_shadowed_glob.vis.is_at_least(binding.vis, this.tcx) {
                                 resolution.shadowed_glob = Some(glob_binding);
                             }
                         } else {
@@ -397,29 +395,21 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         })
     }
 
-    fn ambiguity(
+    fn new_ambiguity_binding(
         &self,
-        kind: AmbiguityKind,
+        ambiguity_kind: AmbiguityKind,
         primary_binding: NameBinding<'a>,
         secondary_binding: NameBinding<'a>,
+        warn_ambiguity: bool,
     ) -> NameBinding<'a> {
-        self.arenas.alloc_name_binding(NameBindingData {
-            ambiguity: Some((secondary_binding, kind)),
-            ..(*primary_binding).clone()
-        })
+        let ambiguity = Some((secondary_binding, ambiguity_kind));
+        let data = NameBindingData { ambiguity, warn_ambiguity, ..*primary_binding };
+        self.arenas.alloc_name_binding(data)
     }
 
-    fn warn_ambiguity(
-        &self,
-        kind: AmbiguityKind,
-        primary_binding: NameBinding<'a>,
-        secondary_binding: NameBinding<'a>,
-    ) -> NameBinding<'a> {
-        self.arenas.alloc_name_binding(NameBindingData {
-            ambiguity: Some((secondary_binding, kind)),
-            warn_ambiguity: true,
-            ..(*primary_binding).clone()
-        })
+    fn new_warn_ambiguity_binding(&self, binding: NameBinding<'a>) -> NameBinding<'a> {
+        assert!(binding.is_ambiguity_recursive());
+        self.arenas.alloc_name_binding(NameBindingData { warn_ambiguity: true, ..*binding })
     }
 
     // Use `f` to mutate the resolution of the name in the module.
@@ -1381,8 +1371,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                     target_bindings[ns].get(),
                 ) {
                     Ok(other_binding) => {
-                        is_redundant =
-                            binding.res() == other_binding.res() && !other_binding.is_ambiguity();
+                        is_redundant = binding.res() == other_binding.res()
+                            && !other_binding.is_ambiguity_recursive();
                         if is_redundant {
                             redundant_span[ns] =
                                 Some((other_binding.span, other_binding.is_import()));
@@ -1455,7 +1445,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                     .resolution(import.parent_scope.module, key)
                     .borrow()
                     .binding()
-                    .is_some_and(|binding| binding.is_warn_ambiguity());
+                    .is_some_and(|binding| binding.warn_ambiguity_recursive());
                 let _ = self.try_define(
                     import.parent_scope.module,
                     key,
@@ -1480,7 +1470,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
 
             module.for_each_child(self, |this, ident, _, binding| {
                 let res = binding.res().expect_non_local();
-                let error_ambiguity = binding.is_ambiguity() && !binding.warn_ambiguity;
+                let error_ambiguity = binding.is_ambiguity_recursive() && !binding.warn_ambiguity;
                 if res != def::Res::Err && !error_ambiguity {
                     let mut reexport_chain = SmallVec::new();
                     let mut next_binding = binding;
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index 5ab6ba23a7d..66a1c05289b 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -3730,7 +3730,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
         let ls_binding = self.maybe_resolve_ident_in_lexical_scope(ident, ValueNS)?;
         let (res, binding) = match ls_binding {
             LexicalScopeBinding::Item(binding)
-                if is_syntactic_ambiguity && binding.is_ambiguity() =>
+                if is_syntactic_ambiguity && binding.is_ambiguity_recursive() =>
             {
                 // For ambiguous bindings we don't know all their definitions and cannot check
                 // whether they can be shadowed by fresh bindings or not, so force an error.
diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs
index 3a831a7f19e..94cdce1025f 100644
--- a/compiler/rustc_resolve/src/lib.rs
+++ b/compiler/rustc_resolve/src/lib.rs
@@ -694,10 +694,12 @@ impl<'a> fmt::Debug for Module<'a> {
 }
 
 /// Records a possibly-private value, type, or module definition.
-#[derive(Clone, Debug)]
+#[derive(Clone, Copy, Debug)]
 struct NameBindingData<'a> {
     kind: NameBindingKind<'a>,
     ambiguity: Option<(NameBinding<'a>, AmbiguityKind)>,
+    /// Produce a warning instead of an error when reporting ambiguities inside this binding.
+    /// May apply to indirect ambiguities under imports, so `ambiguity.is_some()` is not required.
     warn_ambiguity: bool,
     expansion: LocalExpnId,
     span: Span,
@@ -718,7 +720,7 @@ impl<'a> ToNameBinding<'a> for NameBinding<'a> {
     }
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Copy, Debug)]
 enum NameBindingKind<'a> {
     Res(Res),
     Module(Module<'a>),
@@ -830,18 +832,18 @@ impl<'a> NameBindingData<'a> {
         }
     }
 
-    fn is_ambiguity(&self) -> bool {
+    fn is_ambiguity_recursive(&self) -> bool {
         self.ambiguity.is_some()
             || match self.kind {
-                NameBindingKind::Import { binding, .. } => binding.is_ambiguity(),
+                NameBindingKind::Import { binding, .. } => binding.is_ambiguity_recursive(),
                 _ => false,
             }
     }
 
-    fn is_warn_ambiguity(&self) -> bool {
+    fn warn_ambiguity_recursive(&self) -> bool {
         self.warn_ambiguity
             || match self.kind {
-                NameBindingKind::Import { binding, .. } => binding.is_warn_ambiguity(),
+                NameBindingKind::Import { binding, .. } => binding.warn_ambiguity_recursive(),
                 _ => false,
             }
     }
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index 839cc51efce..2d38ad37133 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -498,9 +498,11 @@ pub enum OutputType {
     DepInfo,
 }
 
-// Safety: Trivial C-Style enums have a stable sort order across compilation sessions.
-unsafe impl StableOrd for OutputType {
+impl StableOrd for OutputType {
     const CAN_USE_UNSTABLE_SORT: bool = true;
+
+    // Trivial C-Style enums have a stable sort order across compilation sessions.
+    const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();
 }
 
 impl<HCX: HashStableContext> ToStableHashKey<HCX> for OutputType {
diff --git a/compiler/rustc_span/src/def_id.rs b/compiler/rustc_span/src/def_id.rs
index 1ac3a817bba..5456303b36f 100644
--- a/compiler/rustc_span/src/def_id.rs
+++ b/compiler/rustc_span/src/def_id.rs
@@ -120,9 +120,11 @@ impl Default for DefPathHash {
     }
 }
 
-// Safety: `DefPathHash` sort order is not affected (de)serialization.
-unsafe impl StableOrd for DefPathHash {
+impl StableOrd for DefPathHash {
     const CAN_USE_UNSTABLE_SORT: bool = true;
+
+    // `DefPathHash` sort order is not affected by (de)serialization.
+    const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();
 }
 
 /// A [`StableCrateId`] is a 64-bit hash of a crate name, together with all
diff --git a/library/alloc/src/sync/tests.rs b/library/alloc/src/sync/tests.rs
index 49eae718c16..1b123aa58f2 100644
--- a/library/alloc/src/sync/tests.rs
+++ b/library/alloc/src/sync/tests.rs
@@ -396,7 +396,7 @@ fn show_arc() {
 
 // Make sure deriving works with Arc<T>
 #[derive(Eq, Ord, PartialEq, PartialOrd, Clone, Debug, Default)]
-struct Foo {
+struct _Foo {
     inner: Arc<i32>,
 }
 
diff --git a/library/core/src/default.rs b/library/core/src/default.rs
index 4524b352ec8..5cacedcb241 100644
--- a/library/core/src/default.rs
+++ b/library/core/src/default.rs
@@ -103,6 +103,7 @@ use crate::ascii::Char as AsciiChar;
 /// ```
 #[cfg_attr(not(test), rustc_diagnostic_item = "Default")]
 #[stable(feature = "rust1", since = "1.0.0")]
+#[cfg_attr(not(bootstrap), rustc_trivial_field_reads)]
 pub trait Default: Sized {
     /// Returns the "default value" for a type.
     ///
diff --git a/library/core/src/ffi/mod.rs b/library/core/src/ffi/mod.rs
index 618897b3aba..6d1f10f5211 100644
--- a/library/core/src/ffi/mod.rs
+++ b/library/core/src/ffi/mod.rs
@@ -484,7 +484,7 @@ mod sealed_trait {
                   all supported platforms",
         issue = "44930"
     )]
-    pub trait VaArgSafe {}
+    pub unsafe trait VaArgSafe {}
 }
 
 macro_rules! impl_va_arg_safe {
@@ -494,7 +494,7 @@ macro_rules! impl_va_arg_safe {
                        reason = "the `c_variadic` feature has not been properly tested on \
                                  all supported platforms",
                        issue = "44930")]
-            impl sealed_trait::VaArgSafe for $t {}
+            unsafe impl sealed_trait::VaArgSafe for $t {}
         )+
     }
 }
@@ -509,14 +509,15 @@ impl_va_arg_safe! {f64}
               all supported platforms",
     issue = "44930"
 )]
-impl<T> sealed_trait::VaArgSafe for *mut T {}
+unsafe impl<T> sealed_trait::VaArgSafe for *mut T {}
+
 #[unstable(
     feature = "c_variadic",
     reason = "the `c_variadic` feature has not been properly tested on \
               all supported platforms",
     issue = "44930"
 )]
-impl<T> sealed_trait::VaArgSafe for *const T {}
+unsafe impl<T> sealed_trait::VaArgSafe for *const T {}
 
 #[unstable(
     feature = "c_variadic",
diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs
index 6b5054a9f06..9ba1c6a4154 100644
--- a/library/core/src/intrinsics.rs
+++ b/library/core/src/intrinsics.rs
@@ -2579,7 +2579,7 @@ extern "rust-intrinsic" {
 ///     fn runtime() -> i32 { 1 }
 ///     const fn compiletime() -> i32 { 2 }
 ///
-//      // ⚠ This code violates the required equivalence of `compiletime`
+///     // ⚠ This code violates the required equivalence of `compiletime`
 ///     // and `runtime`.
 ///     const_eval_select((), compiletime, runtime)
 /// }
diff --git a/library/core/src/iter/adapters/filter.rs b/library/core/src/iter/adapters/filter.rs
index ca23d1b13a8..ba49070329c 100644
--- a/library/core/src/iter/adapters/filter.rs
+++ b/library/core/src/iter/adapters/filter.rs
@@ -3,7 +3,7 @@ use crate::iter::{adapters::SourceIter, FusedIterator, InPlaceIterable, TrustedF
 use crate::num::NonZero;
 use crate::ops::Try;
 use core::array;
-use core::mem::{ManuallyDrop, MaybeUninit};
+use core::mem::MaybeUninit;
 use core::ops::ControlFlow;
 
 /// An iterator that filters the elements of `iter` with `predicate`.
@@ -27,6 +27,42 @@ impl<I, P> Filter<I, P> {
     }
 }
 
+impl<I, P> Filter<I, P>
+where
+    I: Iterator,
+    P: FnMut(&I::Item) -> bool,
+{
+    #[inline]
+    fn next_chunk_dropless<const N: usize>(
+        &mut self,
+    ) -> Result<[I::Item; N], array::IntoIter<I::Item, N>> {
+        let mut array: [MaybeUninit<I::Item>; N] = [const { MaybeUninit::uninit() }; N];
+        let mut initialized = 0;
+
+        let result = self.iter.try_for_each(|element| {
+            let idx = initialized;
+            // branchless index update combined with unconditionally copying the value even when
+            // it is filtered reduces branching and dependencies in the loop.
+            initialized = idx + (self.predicate)(&element) as usize;
+            // SAFETY: Loop conditions ensure the index is in bounds.
+            unsafe { array.get_unchecked_mut(idx) }.write(element);
+
+            if initialized < N { ControlFlow::Continue(()) } else { ControlFlow::Break(()) }
+        });
+
+        match result {
+            ControlFlow::Break(()) => {
+                // SAFETY: The loop above is only explicitly broken when the array has been fully initialized
+                Ok(unsafe { MaybeUninit::array_assume_init(array) })
+            }
+            ControlFlow::Continue(()) => {
+                // SAFETY: The range is in bounds since the loop breaks when reaching N elements.
+                Err(unsafe { array::IntoIter::new_unchecked(array, 0..initialized) })
+            }
+        }
+    }
+}
+
 #[stable(feature = "core_impl_debug", since = "1.9.0")]
 impl<I: fmt::Debug, P> fmt::Debug for Filter<I, P> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@@ -64,52 +100,16 @@ where
     fn next_chunk<const N: usize>(
         &mut self,
     ) -> Result<[Self::Item; N], array::IntoIter<Self::Item, N>> {
-        let mut array: [MaybeUninit<Self::Item>; N] = [const { MaybeUninit::uninit() }; N];
-
-        struct Guard<'a, T> {
-            array: &'a mut [MaybeUninit<T>],
-            initialized: usize,
-        }
-
-        impl<T> Drop for Guard<'_, T> {
-            #[inline]
-            fn drop(&mut self) {
-                if const { crate::mem::needs_drop::<T>() } {
-                    // SAFETY: self.initialized is always <= N, which also is the length of the array.
-                    unsafe {
-                        core::ptr::drop_in_place(MaybeUninit::slice_assume_init_mut(
-                            self.array.get_unchecked_mut(..self.initialized),
-                        ));
-                    }
-                }
+        // avoid codegen for the dead branch
+        let fun = const {
+            if crate::mem::needs_drop::<I::Item>() {
+                array::iter_next_chunk::<I::Item, N>
+            } else {
+                Self::next_chunk_dropless::<N>
             }
-        }
-
-        let mut guard = Guard { array: &mut array, initialized: 0 };
-
-        let result = self.iter.try_for_each(|element| {
-            let idx = guard.initialized;
-            guard.initialized = idx + (self.predicate)(&element) as usize;
-
-            // SAFETY: Loop conditions ensure the index is in bounds.
-            unsafe { guard.array.get_unchecked_mut(idx) }.write(element);
-
-            if guard.initialized < N { ControlFlow::Continue(()) } else { ControlFlow::Break(()) }
-        });
+        };
 
-        let guard = ManuallyDrop::new(guard);
-
-        match result {
-            ControlFlow::Break(()) => {
-                // SAFETY: The loop above is only explicitly broken when the array has been fully initialized
-                Ok(unsafe { MaybeUninit::array_assume_init(array) })
-            }
-            ControlFlow::Continue(()) => {
-                let initialized = guard.initialized;
-                // SAFETY: The range is in bounds since the loop breaks when reaching N elements.
-                Err(unsafe { array::IntoIter::new_unchecked(array, 0..initialized) })
-            }
-        }
+        fun(self)
     }
 
     #[inline]
diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs
index 129f62fb43d..58ed98c888c 100644
--- a/library/core/src/num/f128.rs
+++ b/library/core/src/num/f128.rs
@@ -11,6 +11,7 @@
 
 #![unstable(feature = "f128", issue = "116909")]
 
+use crate::convert::FloatToInt;
 use crate::mem;
 
 /// Basic mathematical constants.
@@ -220,21 +221,145 @@ impl f128 {
     #[unstable(feature = "f128", issue = "116909")]
     pub const MAX_10_EXP: i32 = 4_932;
 
+    /// Not a Number (NaN).
+    ///
+    /// Note that IEEE 754 doesn't define just a single NaN value;
+    /// a plethora of bit patterns are considered to be NaN.
+    /// Furthermore, the standard makes a difference
+    /// between a "signaling" and a "quiet" NaN,
+    /// and allows inspecting its "payload" (the unspecified bits in the bit pattern).
+    /// This constant isn't guaranteed to equal to any specific NaN bitpattern,
+    /// and the stability of its representation over Rust versions
+    /// and target platforms isn't guaranteed.
+    #[cfg(not(bootstrap))]
+    #[allow(clippy::eq_op)]
+    #[rustc_diagnostic_item = "f128_nan"]
+    #[unstable(feature = "f128", issue = "116909")]
+    pub const NAN: f128 = 0.0_f128 / 0.0_f128;
+
+    /// Infinity (∞).
+    #[cfg(not(bootstrap))]
+    #[unstable(feature = "f128", issue = "116909")]
+    pub const INFINITY: f128 = 1.0_f128 / 0.0_f128;
+
+    /// Negative infinity (−∞).
+    #[cfg(not(bootstrap))]
+    #[unstable(feature = "f128", issue = "116909")]
+    pub const NEG_INFINITY: f128 = -1.0_f128 / 0.0_f128;
+
+    /// Sign bit
+    #[cfg(not(bootstrap))]
+    pub(crate) const SIGN_MASK: u128 = 0x8000_0000_0000_0000_0000_0000_0000_0000;
+
+    /// Minimum representable positive value (min subnormal)
+    #[cfg(not(bootstrap))]
+    const TINY_BITS: u128 = 0x1;
+
+    /// Minimum representable negative value (min negative subnormal)
+    #[cfg(not(bootstrap))]
+    const NEG_TINY_BITS: u128 = Self::TINY_BITS | Self::SIGN_MASK;
+
     /// Returns `true` if this value is NaN.
+    ///
+    /// ```
+    /// #![feature(f128)]
+    /// # // FIXME(f16_f128): remove when `unordtf2` is available
+    /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
+    ///
+    /// let nan = f128::NAN;
+    /// let f = 7.0_f128;
+    ///
+    /// assert!(nan.is_nan());
+    /// assert!(!f.is_nan());
+    /// # }
+    /// ```
     #[inline]
     #[must_use]
+    #[cfg(not(bootstrap))]
     #[unstable(feature = "f128", issue = "116909")]
     #[allow(clippy::eq_op)] // > if you intended to check if the operand is NaN, use `.is_nan()` instead :)
     pub const fn is_nan(self) -> bool {
         self != self
     }
 
+    // FIXME(#50145): `abs` is publicly unavailable in core due to
+    // concerns about portability, so this implementation is for
+    // private use internally.
+    #[inline]
+    #[cfg(not(bootstrap))]
+    #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
+    pub(crate) const fn abs_private(self) -> f128 {
+        // SAFETY: This transmutation is fine. Probably. For the reasons std is using it.
+        unsafe {
+            mem::transmute::<u128, f128>(mem::transmute::<f128, u128>(self) & !Self::SIGN_MASK)
+        }
+    }
+
+    /// Returns `true` if this value is positive infinity or negative infinity, and
+    /// `false` otherwise.
+    ///
+    /// ```
+    /// #![feature(f128)]
+    /// # // FIXME(f16_f128): remove when `eqtf2` is available
+    /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
+    ///
+    /// let f = 7.0f128;
+    /// let inf = f128::INFINITY;
+    /// let neg_inf = f128::NEG_INFINITY;
+    /// let nan = f128::NAN;
+    ///
+    /// assert!(!f.is_infinite());
+    /// assert!(!nan.is_infinite());
+    ///
+    /// assert!(inf.is_infinite());
+    /// assert!(neg_inf.is_infinite());
+    /// # }
+    /// ```
+    #[inline]
+    #[must_use]
+    #[cfg(not(bootstrap))]
+    #[unstable(feature = "f128", issue = "116909")]
+    #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
+    pub const fn is_infinite(self) -> bool {
+        (self == f128::INFINITY) | (self == f128::NEG_INFINITY)
+    }
+
+    /// Returns `true` if this number is neither infinite nor NaN.
+    ///
+    /// ```
+    /// #![feature(f128)]
+    /// # // FIXME(f16_f128): remove when `lttf2` is available
+    /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
+    ///
+    /// let f = 7.0f128;
+    /// let inf: f128 = f128::INFINITY;
+    /// let neg_inf: f128 = f128::NEG_INFINITY;
+    /// let nan: f128 = f128::NAN;
+    ///
+    /// assert!(f.is_finite());
+    ///
+    /// assert!(!nan.is_finite());
+    /// assert!(!inf.is_finite());
+    /// assert!(!neg_inf.is_finite());
+    /// # }
+    /// ```
+    #[inline]
+    #[must_use]
+    #[cfg(not(bootstrap))]
+    #[unstable(feature = "f128", issue = "116909")]
+    #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
+    pub const fn is_finite(self) -> bool {
+        // There's no need to handle NaN separately: if self is NaN,
+        // the comparison is not true, exactly as desired.
+        self.abs_private() < Self::INFINITY
+    }
+
     /// Returns `true` if `self` has a positive sign, including `+0.0`, NaNs with
     /// positive sign bit and positive infinity. Note that IEEE 754 doesn't assign any
     /// meaning to the sign bit in case of a NaN, and as Rust doesn't guarantee that
     /// the bit pattern of NaNs are conserved over arithmetic operations, the result of
     /// `is_sign_positive` on a NaN might produce an unexpected result in some cases.
-    /// See [explanation of NaN as a special value](f32) for more info.
+    /// See [explanation of NaN as a special value](f128) for more info.
     ///
     /// ```
     /// #![feature(f128)]
@@ -257,7 +382,7 @@ impl f128 {
     /// meaning to the sign bit in case of a NaN, and as Rust doesn't guarantee that
     /// the bit pattern of NaNs are conserved over arithmetic operations, the result of
     /// `is_sign_negative` on a NaN might produce an unexpected result in some cases.
-    /// See [explanation of NaN as a special value](f32) for more info.
+    /// See [explanation of NaN as a special value](f128) for more info.
     ///
     /// ```
     /// #![feature(f128)]
@@ -278,6 +403,222 @@ impl f128 {
         (self.to_bits() & (1 << 127)) != 0
     }
 
+    /// Returns the least number greater than `self`.
+    ///
+    /// Let `TINY` be the smallest representable positive `f128`. Then,
+    ///  - if `self.is_nan()`, this returns `self`;
+    ///  - if `self` is [`NEG_INFINITY`], this returns [`MIN`];
+    ///  - if `self` is `-TINY`, this returns -0.0;
+    ///  - if `self` is -0.0 or +0.0, this returns `TINY`;
+    ///  - if `self` is [`MAX`] or [`INFINITY`], this returns [`INFINITY`];
+    ///  - otherwise the unique least value greater than `self` is returned.
+    ///
+    /// The identity `x.next_up() == -(-x).next_down()` holds for all non-NaN `x`. When `x`
+    /// is finite `x == x.next_up().next_down()` also holds.
+    ///
+    /// ```rust
+    /// #![feature(f128)]
+    /// #![feature(float_next_up_down)]
+    /// # // FIXME(f16_f128): remove when `eqtf2` is available
+    /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
+    ///
+    /// // f128::EPSILON is the difference between 1.0 and the next number up.
+    /// assert_eq!(1.0f128.next_up(), 1.0 + f128::EPSILON);
+    /// // But not for most numbers.
+    /// assert!(0.1f128.next_up() < 0.1 + f128::EPSILON);
+    /// assert_eq!(4611686018427387904f128.next_up(), 4611686018427387904.000000000000001);
+    /// # }
+    /// ```
+    ///
+    /// [`NEG_INFINITY`]: Self::NEG_INFINITY
+    /// [`INFINITY`]: Self::INFINITY
+    /// [`MIN`]: Self::MIN
+    /// [`MAX`]: Self::MAX
+    #[inline]
+    #[cfg(not(bootstrap))]
+    #[unstable(feature = "f128", issue = "116909")]
+    // #[unstable(feature = "float_next_up_down", issue = "91399")]
+    pub fn next_up(self) -> Self {
+        // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing
+        // denormals to zero. This is in general unsound and unsupported, but here
+        // we do our best to still produce the correct result on such targets.
+        let bits = self.to_bits();
+        if self.is_nan() || bits == Self::INFINITY.to_bits() {
+            return self;
+        }
+
+        let abs = bits & !Self::SIGN_MASK;
+        let next_bits = if abs == 0 {
+            Self::TINY_BITS
+        } else if bits == abs {
+            bits + 1
+        } else {
+            bits - 1
+        };
+        Self::from_bits(next_bits)
+    }
+
+    /// Returns the greatest number less than `self`.
+    ///
+    /// Let `TINY` be the smallest representable positive `f128`. Then,
+    ///  - if `self.is_nan()`, this returns `self`;
+    ///  - if `self` is [`INFINITY`], this returns [`MAX`];
+    ///  - if `self` is `TINY`, this returns 0.0;
+    ///  - if `self` is -0.0 or +0.0, this returns `-TINY`;
+    ///  - if `self` is [`MIN`] or [`NEG_INFINITY`], this returns [`NEG_INFINITY`];
+    ///  - otherwise the unique greatest value less than `self` is returned.
+    ///
+    /// The identity `x.next_down() == -(-x).next_up()` holds for all non-NaN `x`. When `x`
+    /// is finite `x == x.next_down().next_up()` also holds.
+    ///
+    /// ```rust
+    /// #![feature(f128)]
+    /// #![feature(float_next_up_down)]
+    /// # // FIXME(f16_f128): remove when `eqtf2` is available
+    /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
+    ///
+    /// let x = 1.0f128;
+    /// // Clamp value into range [0, 1).
+    /// let clamped = x.clamp(0.0, 1.0f128.next_down());
+    /// assert!(clamped < 1.0);
+    /// assert_eq!(clamped.next_up(), 1.0);
+    /// # }
+    /// ```
+    ///
+    /// [`NEG_INFINITY`]: Self::NEG_INFINITY
+    /// [`INFINITY`]: Self::INFINITY
+    /// [`MIN`]: Self::MIN
+    /// [`MAX`]: Self::MAX
+    #[inline]
+    #[cfg(not(bootstrap))]
+    #[unstable(feature = "f128", issue = "116909")]
+    // #[unstable(feature = "float_next_up_down", issue = "91399")]
+    pub fn next_down(self) -> Self {
+        // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing
+        // denormals to zero. This is in general unsound and unsupported, but here
+        // we do our best to still produce the correct result on such targets.
+        let bits = self.to_bits();
+        if self.is_nan() || bits == Self::NEG_INFINITY.to_bits() {
+            return self;
+        }
+
+        let abs = bits & !Self::SIGN_MASK;
+        let next_bits = if abs == 0 {
+            Self::NEG_TINY_BITS
+        } else if bits == abs {
+            bits - 1
+        } else {
+            bits + 1
+        };
+        Self::from_bits(next_bits)
+    }
+
+    /// Takes the reciprocal (inverse) of a number, `1/x`.
+    ///
+    /// ```
+    /// #![feature(f128)]
+    /// # // FIXME(f16_f128): remove when `eqtf2` is available
+    /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
+    ///
+    /// let x = 2.0_f128;
+    /// let abs_difference = (x.recip() - (1.0 / x)).abs();
+    ///
+    /// assert!(abs_difference <= f128::EPSILON);
+    /// # }
+    /// ```
+    #[inline]
+    #[cfg(not(bootstrap))]
+    #[unstable(feature = "f128", issue = "116909")]
+    #[must_use = "this returns the result of the operation, without modifying the original"]
+    pub fn recip(self) -> Self {
+        1.0 / self
+    }
+
+    /// Converts radians to degrees.
+    ///
+    /// ```
+    /// #![feature(f128)]
+    /// # // FIXME(f16_f128): remove when `eqtf2` is available
+    /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
+    ///
+    /// let angle = std::f128::consts::PI;
+    ///
+    /// let abs_difference = (angle.to_degrees() - 180.0).abs();
+    /// assert!(abs_difference <= f128::EPSILON);
+    /// # }
+    /// ```
+    #[inline]
+    #[cfg(not(bootstrap))]
+    #[unstable(feature = "f128", issue = "116909")]
+    #[must_use = "this returns the result of the operation, without modifying the original"]
+    pub fn to_degrees(self) -> Self {
+        // Use a literal for better precision.
+        const PIS_IN_180: f128 = 57.2957795130823208767981548141051703324054724665643215491602_f128;
+        self * PIS_IN_180
+    }
+
+    /// Converts degrees to radians.
+    ///
+    /// ```
+    /// #![feature(f128)]
+    /// # // FIXME(f16_f128): remove when `eqtf2` is available
+    /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
+    ///
+    /// let angle = 180.0f128;
+    ///
+    /// let abs_difference = (angle.to_radians() - std::f128::consts::PI).abs();
+    ///
+    /// assert!(abs_difference <= 1e-30);
+    /// # }
+    /// ```
+    #[inline]
+    #[cfg(not(bootstrap))]
+    #[unstable(feature = "f128", issue = "116909")]
+    #[must_use = "this returns the result of the operation, without modifying the original"]
+    pub fn to_radians(self) -> f128 {
+        // Use a literal for better precision.
+        const RADS_PER_DEG: f128 =
+            0.0174532925199432957692369076848861271344287188854172545609719_f128;
+        self * RADS_PER_DEG
+    }
+
+    /// Rounds toward zero and converts to any primitive integer type,
+    /// assuming that the value is finite and fits in that type.
+    ///
+    /// ```
+    /// #![feature(f128)]
+    /// # // FIXME(f16_f128): remove when `float*itf` is available
+    /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
+    ///
+    /// let value = 4.6_f128;
+    /// let rounded = unsafe { value.to_int_unchecked::<u16>() };
+    /// assert_eq!(rounded, 4);
+    ///
+    /// let value = -128.9_f128;
+    /// let rounded = unsafe { value.to_int_unchecked::<i8>() };
+    /// assert_eq!(rounded, i8::MIN);
+    /// # }
+    /// ```
+    ///
+    /// # Safety
+    ///
+    /// The value must:
+    ///
+    /// * Not be `NaN`
+    /// * Not be infinite
+    /// * Be representable in the return type `Int`, after truncating off its fractional part
+    #[inline]
+    #[unstable(feature = "f128", issue = "116909")]
+    #[must_use = "this returns the result of the operation, without modifying the original"]
+    pub unsafe fn to_int_unchecked<Int>(self) -> Int
+    where
+        Self: FloatToInt<Int>,
+    {
+        // SAFETY: the caller must uphold the safety contract for
+        // `FloatToInt::to_int_unchecked`.
+        unsafe { FloatToInt::<Int>::to_int_unchecked(self) }
+    }
+
     /// Raw transmutation to `u128`.
     ///
     /// This is currently identical to `transmute::<f128, u128>(self)` on all platforms.
@@ -287,6 +628,14 @@ impl f128 {
     ///
     /// Note that this function is distinct from `as` casting, which attempts to
     /// preserve the *numeric* value, and not the bitwise value.
+    ///
+    /// ```
+    /// #![feature(f128)]
+    ///
+    /// # // FIXME(f16_f128): enable this once const casting works
+    /// # // assert_ne!((1f128).to_bits(), 1f128 as u128); // to_bits() is not casting!
+    /// assert_eq!((12.5f128).to_bits(), 0x40029000000000000000000000000000);
+    /// ```
     #[inline]
     #[unstable(feature = "f128", issue = "116909")]
     #[must_use = "this returns the result of the operation, without modifying the original"]
@@ -326,6 +675,16 @@ impl f128 {
     ///
     /// Note that this function is distinct from `as` casting, which attempts to
     /// preserve the *numeric* value, and not the bitwise value.
+    ///
+    /// ```
+    /// #![feature(f128)]
+    /// #  // FIXME(f16_f128): remove when `eqtf2` is available
+    /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
+    ///
+    /// let v = f128::from_bits(0x40029000000000000000000000000000);
+    /// assert_eq!(v, 12.5);
+    /// # }
+    /// ```
     #[inline]
     #[must_use]
     #[unstable(feature = "f128", issue = "116909")]
@@ -335,4 +694,315 @@ impl f128 {
         // Stability concerns.
         unsafe { mem::transmute(v) }
     }
+
+    /// Return the memory representation of this floating point number as a byte array in
+    /// big-endian (network) byte order.
+    ///
+    /// See [`from_bits`](Self::from_bits) for some discussion of the
+    /// portability of this operation (there are almost no issues).
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(f128)]
+    ///
+    /// let bytes = 12.5f128.to_be_bytes();
+    /// assert_eq!(
+    ///     bytes,
+    ///     [0x40, 0x02, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00,
+    ///      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
+    /// );
+    /// ```
+    #[inline]
+    #[unstable(feature = "f128", issue = "116909")]
+    #[must_use = "this returns the result of the operation, without modifying the original"]
+    pub fn to_be_bytes(self) -> [u8; 16] {
+        self.to_bits().to_be_bytes()
+    }
+
+    /// Return the memory representation of this floating point number as a byte array in
+    /// little-endian byte order.
+    ///
+    /// See [`from_bits`](Self::from_bits) for some discussion of the
+    /// portability of this operation (there are almost no issues).
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(f128)]
+    ///
+    /// let bytes = 12.5f128.to_le_bytes();
+    /// assert_eq!(
+    ///     bytes,
+    ///     [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    ///      0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x02, 0x40]
+    /// );
+    /// ```
+    #[inline]
+    #[unstable(feature = "f128", issue = "116909")]
+    #[must_use = "this returns the result of the operation, without modifying the original"]
+    pub fn to_le_bytes(self) -> [u8; 16] {
+        self.to_bits().to_le_bytes()
+    }
+
+    /// Return the memory representation of this floating point number as a byte array in
+    /// native byte order.
+    ///
+    /// As the target platform's native endianness is used, portable code
+    /// should use [`to_be_bytes`] or [`to_le_bytes`], as appropriate, instead.
+    ///
+    /// [`to_be_bytes`]: f128::to_be_bytes
+    /// [`to_le_bytes`]: f128::to_le_bytes
+    ///
+    /// See [`from_bits`](Self::from_bits) for some discussion of the
+    /// portability of this operation (there are almost no issues).
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(f128)]
+    ///
+    /// let bytes = 12.5f128.to_ne_bytes();
+    /// assert_eq!(
+    ///     bytes,
+    ///     if cfg!(target_endian = "big") {
+    ///         [0x40, 0x02, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00,
+    ///          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
+    ///     } else {
+    ///         [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    ///          0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x02, 0x40]
+    ///     }
+    /// );
+    /// ```
+    #[inline]
+    #[unstable(feature = "f128", issue = "116909")]
+    #[must_use = "this returns the result of the operation, without modifying the original"]
+    pub fn to_ne_bytes(self) -> [u8; 16] {
+        self.to_bits().to_ne_bytes()
+    }
+
+    /// Create a floating point value from its representation as a byte array in big endian.
+    ///
+    /// See [`from_bits`](Self::from_bits) for some discussion of the
+    /// portability of this operation (there are almost no issues).
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(f128)]
+    /// # // FIXME(f16_f128): remove when `eqtf2` is available
+    /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
+    ///
+    /// let value = f128::from_be_bytes(
+    ///     [0x40, 0x02, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00,
+    ///      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
+    /// );
+    /// assert_eq!(value, 12.5);
+    /// # }
+    /// ```
+    #[inline]
+    #[must_use]
+    #[unstable(feature = "f128", issue = "116909")]
+    pub fn from_be_bytes(bytes: [u8; 16]) -> Self {
+        Self::from_bits(u128::from_be_bytes(bytes))
+    }
+
+    /// Create a floating point value from its representation as a byte array in little endian.
+    ///
+    /// See [`from_bits`](Self::from_bits) for some discussion of the
+    /// portability of this operation (there are almost no issues).
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(f128)]
+    /// # // FIXME(f16_f128): remove when `eqtf2` is available
+    /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
+    ///
+    /// let value = f128::from_le_bytes(
+    ///     [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    ///      0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x02, 0x40]
+    /// );
+    /// assert_eq!(value, 12.5);
+    /// # }
+    /// ```
+    #[inline]
+    #[must_use]
+    #[unstable(feature = "f128", issue = "116909")]
+    pub fn from_le_bytes(bytes: [u8; 16]) -> Self {
+        Self::from_bits(u128::from_le_bytes(bytes))
+    }
+
+    /// Create a floating point value from its representation as a byte array in native endian.
+    ///
+    /// As the target platform's native endianness is used, portable code
+    /// likely wants to use [`from_be_bytes`] or [`from_le_bytes`], as
+    /// appropriate instead.
+    ///
+    /// [`from_be_bytes`]: f128::from_be_bytes
+    /// [`from_le_bytes`]: f128::from_le_bytes
+    ///
+    /// See [`from_bits`](Self::from_bits) for some discussion of the
+    /// portability of this operation (there are almost no issues).
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(f128)]
+    /// # // FIXME(f16_f128): remove when `eqtf2` is available
+    /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
+    ///
+    /// let value = f128::from_ne_bytes(if cfg!(target_endian = "big") {
+    ///     [0x40, 0x02, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00,
+    ///      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
+    /// } else {
+    ///     [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    ///      0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x02, 0x40]
+    /// });
+    /// assert_eq!(value, 12.5);
+    /// # }
+    /// ```
+    #[inline]
+    #[must_use]
+    #[unstable(feature = "f128", issue = "116909")]
+    pub fn from_ne_bytes(bytes: [u8; 16]) -> Self {
+        Self::from_bits(u128::from_ne_bytes(bytes))
+    }
+
+    /// Return the ordering between `self` and `other`.
+    ///
+    /// Unlike the standard partial comparison between floating point numbers,
+    /// this comparison always produces an ordering in accordance to
+    /// the `totalOrder` predicate as defined in the IEEE 754 (2008 revision)
+    /// floating point standard. The values are ordered in the following sequence:
+    ///
+    /// - negative quiet NaN
+    /// - negative signaling NaN
+    /// - negative infinity
+    /// - negative numbers
+    /// - negative subnormal numbers
+    /// - negative zero
+    /// - positive zero
+    /// - positive subnormal numbers
+    /// - positive numbers
+    /// - positive infinity
+    /// - positive signaling NaN
+    /// - positive quiet NaN.
+    ///
+    /// The ordering established by this function does not always agree with the
+    /// [`PartialOrd`] and [`PartialEq`] implementations of `f128`. For example,
+    /// they consider negative and positive zero equal, while `total_cmp`
+    /// doesn't.
+    ///
+    /// The interpretation of the signaling NaN bit follows the definition in
+    /// the IEEE 754 standard, which may not match the interpretation by some of
+    /// the older, non-conformant (e.g. MIPS) hardware implementations.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// #![feature(f128)]
+    ///
+    /// struct GoodBoy {
+    ///     name: &'static str,
+    ///     weight: f128,
+    /// }
+    ///
+    /// let mut bois = vec![
+    ///     GoodBoy { name: "Pucci", weight: 0.1 },
+    ///     GoodBoy { name: "Woofer", weight: 99.0 },
+    ///     GoodBoy { name: "Yapper", weight: 10.0 },
+    ///     GoodBoy { name: "Chonk", weight: f128::INFINITY },
+    ///     GoodBoy { name: "Abs. Unit", weight: f128::NAN },
+    ///     GoodBoy { name: "Floaty", weight: -5.0 },
+    /// ];
+    ///
+    /// bois.sort_by(|a, b| a.weight.total_cmp(&b.weight));
+    ///
+    /// // `f128::NAN` could be positive or negative, which will affect the sort order.
+    /// if f128::NAN.is_sign_negative() {
+    ///     bois.into_iter().map(|b| b.weight)
+    ///         .zip([f128::NAN, -5.0, 0.1, 10.0, 99.0, f128::INFINITY].iter())
+    ///         .for_each(|(a, b)| assert_eq!(a.to_bits(), b.to_bits()))
+    /// } else {
+    ///     bois.into_iter().map(|b| b.weight)
+    ///         .zip([-5.0, 0.1, 10.0, 99.0, f128::INFINITY, f128::NAN].iter())
+    ///         .for_each(|(a, b)| assert_eq!(a.to_bits(), b.to_bits()))
+    /// }
+    /// ```
+    #[inline]
+    #[must_use]
+    #[cfg(not(bootstrap))]
+    #[unstable(feature = "f128", issue = "116909")]
+    pub fn total_cmp(&self, other: &Self) -> crate::cmp::Ordering {
+        let mut left = self.to_bits() as i128;
+        let mut right = other.to_bits() as i128;
+
+        // In case of negatives, flip all the bits except the sign
+        // to achieve a similar layout as two's complement integers
+        //
+        // Why does this work? IEEE 754 floats consist of three fields:
+        // Sign bit, exponent and mantissa. The set of exponent and mantissa
+        // fields as a whole have the property that their bitwise order is
+        // equal to the numeric magnitude where the magnitude is defined.
+        // The magnitude is not normally defined on NaN values, but
+        // IEEE 754 totalOrder defines the NaN values also to follow the
+        // bitwise order. This leads to order explained in the doc comment.
+        // However, the representation of magnitude is the same for negative
+        // and positive numbers – only the sign bit is different.
+        // To easily compare the floats as signed integers, we need to
+        // flip the exponent and mantissa bits in case of negative numbers.
+        // We effectively convert the numbers to "two's complement" form.
+        //
+        // To do the flipping, we construct a mask and XOR against it.
+        // We branchlessly calculate an "all-ones except for the sign bit"
+        // mask from negative-signed values: right shifting sign-extends
+        // the integer, so we "fill" the mask with sign bits, and then
+        // convert to unsigned to push one more zero bit.
+        // On positive values, the mask is all zeros, so it's a no-op.
+        left ^= (((left >> 127) as u128) >> 1) as i128;
+        right ^= (((right >> 127) as u128) >> 1) as i128;
+
+        left.cmp(&right)
+    }
+
+    /// Restrict a value to a certain interval unless it is NaN.
+    ///
+    /// Returns `max` if `self` is greater than `max`, and `min` if `self` is
+    /// less than `min`. Otherwise this returns `self`.
+    ///
+    /// Note that this function returns NaN if the initial value was NaN as
+    /// well.
+    ///
+    /// # Panics
+    ///
+    /// Panics if `min > max`, `min` is NaN, or `max` is NaN.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(f128)]
+    /// # // FIXME(f16_f128): remove when `{eq,gt,unord}tf` are available
+    /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
+    ///
+    /// assert!((-3.0f128).clamp(-2.0, 1.0) == -2.0);
+    /// assert!((0.0f128).clamp(-2.0, 1.0) == 0.0);
+    /// assert!((2.0f128).clamp(-2.0, 1.0) == 1.0);
+    /// assert!((f128::NAN).clamp(-2.0, 1.0).is_nan());
+    /// # }
+    /// ```
+    #[inline]
+    #[cfg(not(bootstrap))]
+    #[unstable(feature = "f128", issue = "116909")]
+    #[must_use = "method returns a new number and does not mutate the original value"]
+    pub fn clamp(mut self, min: f128, max: f128) -> f128 {
+        assert!(min <= max, "min > max, or either was NaN. min = {min:?}, max = {max:?}");
+        if self < min {
+            self = min;
+        }
+        if self > max {
+            self = max;
+        }
+        self
+    }
 }
diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs
index 7a488cd6bf6..3c58b0af9c2 100644
--- a/library/core/src/num/f16.rs
+++ b/library/core/src/num/f16.rs
@@ -11,6 +11,7 @@
 
 #![unstable(feature = "f16", issue = "116909")]
 
+use crate::convert::FloatToInt;
 use crate::mem;
 
 /// Basic mathematical constants.
@@ -215,21 +216,140 @@ impl f16 {
     #[unstable(feature = "f16", issue = "116909")]
     pub const MAX_10_EXP: i32 = 4;
 
+    /// Not a Number (NaN).
+    ///
+    /// Note that IEEE 754 doesn't define just a single NaN value;
+    /// a plethora of bit patterns are considered to be NaN.
+    /// Furthermore, the standard makes a difference
+    /// between a "signaling" and a "quiet" NaN,
+    /// and allows inspecting its "payload" (the unspecified bits in the bit pattern).
+    /// This constant isn't guaranteed to equal to any specific NaN bitpattern,
+    /// and the stability of its representation over Rust versions
+    /// and target platforms isn't guaranteed.
+    #[cfg(not(bootstrap))]
+    #[allow(clippy::eq_op)]
+    #[rustc_diagnostic_item = "f16_nan"]
+    #[unstable(feature = "f16", issue = "116909")]
+    pub const NAN: f16 = 0.0_f16 / 0.0_f16;
+
+    /// Infinity (∞).
+    #[cfg(not(bootstrap))]
+    #[unstable(feature = "f16", issue = "116909")]
+    pub const INFINITY: f16 = 1.0_f16 / 0.0_f16;
+
+    /// Negative infinity (−∞).
+    #[cfg(not(bootstrap))]
+    #[unstable(feature = "f16", issue = "116909")]
+    pub const NEG_INFINITY: f16 = -1.0_f16 / 0.0_f16;
+
+    /// Sign bit
+    #[cfg(not(bootstrap))]
+    const SIGN_MASK: u16 = 0x8000;
+
+    /// Minimum representable positive value (min subnormal)
+    #[cfg(not(bootstrap))]
+    const TINY_BITS: u16 = 0x1;
+
+    /// Minimum representable negative value (min negative subnormal)
+    #[cfg(not(bootstrap))]
+    const NEG_TINY_BITS: u16 = Self::TINY_BITS | Self::SIGN_MASK;
+
     /// Returns `true` if this value is NaN.
+    ///
+    /// ```
+    /// #![feature(f16)]
+    /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885
+    ///
+    /// let nan = f16::NAN;
+    /// let f = 7.0_f16;
+    ///
+    /// assert!(nan.is_nan());
+    /// assert!(!f.is_nan());
+    /// # }
+    /// ```
     #[inline]
     #[must_use]
+    #[cfg(not(bootstrap))]
     #[unstable(feature = "f16", issue = "116909")]
     #[allow(clippy::eq_op)] // > if you intended to check if the operand is NaN, use `.is_nan()` instead :)
     pub const fn is_nan(self) -> bool {
         self != self
     }
 
+    // FIXMxE(#50145): `abs` is publicly unavailable in core due to
+    // concerns about portability, so this implementation is for
+    // private use internally.
+    #[inline]
+    #[cfg(not(bootstrap))]
+    #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
+    pub(crate) const fn abs_private(self) -> f16 {
+        // SAFETY: This transmutation is fine. Probably. For the reasons std is using it.
+        unsafe { mem::transmute::<u16, f16>(mem::transmute::<f16, u16>(self) & !Self::SIGN_MASK) }
+    }
+
+    /// Returns `true` if this value is positive infinity or negative infinity, and
+    /// `false` otherwise.
+    ///
+    /// ```
+    /// #![feature(f16)]
+    /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885
+    ///
+    /// let f = 7.0f16;
+    /// let inf = f16::INFINITY;
+    /// let neg_inf = f16::NEG_INFINITY;
+    /// let nan = f16::NAN;
+    ///
+    /// assert!(!f.is_infinite());
+    /// assert!(!nan.is_infinite());
+    ///
+    /// assert!(inf.is_infinite());
+    /// assert!(neg_inf.is_infinite());
+    /// # }
+    /// ```
+    #[inline]
+    #[must_use]
+    #[cfg(not(bootstrap))]
+    #[unstable(feature = "f16", issue = "116909")]
+    #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
+    pub const fn is_infinite(self) -> bool {
+        (self == f16::INFINITY) | (self == f16::NEG_INFINITY)
+    }
+
+    /// Returns `true` if this number is neither infinite nor NaN.
+    ///
+    /// ```
+    /// #![feature(f16)]
+    /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885
+    ///
+    /// let f = 7.0f16;
+    /// let inf: f16 = f16::INFINITY;
+    /// let neg_inf: f16 = f16::NEG_INFINITY;
+    /// let nan: f16 = f16::NAN;
+    ///
+    /// assert!(f.is_finite());
+    ///
+    /// assert!(!nan.is_finite());
+    /// assert!(!inf.is_finite());
+    /// assert!(!neg_inf.is_finite());
+    /// # }
+    /// ```
+    #[inline]
+    #[must_use]
+    #[cfg(not(bootstrap))]
+    #[unstable(feature = "f16", issue = "116909")]
+    #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
+    pub const fn is_finite(self) -> bool {
+        // There's no need to handle NaN separately: if self is NaN,
+        // the comparison is not true, exactly as desired.
+        self.abs_private() < Self::INFINITY
+    }
+
     /// Returns `true` if `self` has a positive sign, including `+0.0`, NaNs with
     /// positive sign bit and positive infinity. Note that IEEE 754 doesn't assign any
     /// meaning to the sign bit in case of a NaN, and as Rust doesn't guarantee that
     /// the bit pattern of NaNs are conserved over arithmetic operations, the result of
     /// `is_sign_positive` on a NaN might produce an unexpected result in some cases.
-    /// See [explanation of NaN as a special value](f32) for more info.
+    /// See [explanation of NaN as a special value](f16) for more info.
     ///
     /// ```
     /// #![feature(f16)]
@@ -252,7 +372,7 @@ impl f16 {
     /// meaning to the sign bit in case of a NaN, and as Rust doesn't guarantee that
     /// the bit pattern of NaNs are conserved over arithmetic operations, the result of
     /// `is_sign_negative` on a NaN might produce an unexpected result in some cases.
-    /// See [explanation of NaN as a special value](f32) for more info.
+    /// See [explanation of NaN as a special value](f16) for more info.
     ///
     /// ```
     /// #![feature(f16)]
@@ -273,6 +393,220 @@ impl f16 {
         (self.to_bits() & (1 << 15)) != 0
     }
 
+    /// Returns the least number greater than `self`.
+    ///
+    /// Let `TINY` be the smallest representable positive `f16`. Then,
+    ///  - if `self.is_nan()`, this returns `self`;
+    ///  - if `self` is [`NEG_INFINITY`], this returns [`MIN`];
+    ///  - if `self` is `-TINY`, this returns -0.0;
+    ///  - if `self` is -0.0 or +0.0, this returns `TINY`;
+    ///  - if `self` is [`MAX`] or [`INFINITY`], this returns [`INFINITY`];
+    ///  - otherwise the unique least value greater than `self` is returned.
+    ///
+    /// The identity `x.next_up() == -(-x).next_down()` holds for all non-NaN `x`. When `x`
+    /// is finite `x == x.next_up().next_down()` also holds.
+    ///
+    /// ```rust
+    /// #![feature(f16)]
+    /// #![feature(float_next_up_down)]
+    /// # // FIXME(f16_f128): ABI issues on MSVC
+    /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
+    ///
+    /// // f16::EPSILON is the difference between 1.0 and the next number up.
+    /// assert_eq!(1.0f16.next_up(), 1.0 + f16::EPSILON);
+    /// // But not for most numbers.
+    /// assert!(0.1f16.next_up() < 0.1 + f16::EPSILON);
+    /// assert_eq!(4356f16.next_up(), 4360.0);
+    /// # }
+    /// ```
+    ///
+    /// [`NEG_INFINITY`]: Self::NEG_INFINITY
+    /// [`INFINITY`]: Self::INFINITY
+    /// [`MIN`]: Self::MIN
+    /// [`MAX`]: Self::MAX
+    #[inline]
+    #[cfg(not(bootstrap))]
+    #[unstable(feature = "f16", issue = "116909")]
+    // #[unstable(feature = "float_next_up_down", issue = "91399")]
+    pub fn next_up(self) -> Self {
+        // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing
+        // denormals to zero. This is in general unsound and unsupported, but here
+        // we do our best to still produce the correct result on such targets.
+        let bits = self.to_bits();
+        if self.is_nan() || bits == Self::INFINITY.to_bits() {
+            return self;
+        }
+
+        let abs = bits & !Self::SIGN_MASK;
+        let next_bits = if abs == 0 {
+            Self::TINY_BITS
+        } else if bits == abs {
+            bits + 1
+        } else {
+            bits - 1
+        };
+        Self::from_bits(next_bits)
+    }
+
+    /// Returns the greatest number less than `self`.
+    ///
+    /// Let `TINY` be the smallest representable positive `f16`. Then,
+    ///  - if `self.is_nan()`, this returns `self`;
+    ///  - if `self` is [`INFINITY`], this returns [`MAX`];
+    ///  - if `self` is `TINY`, this returns 0.0;
+    ///  - if `self` is -0.0 or +0.0, this returns `-TINY`;
+    ///  - if `self` is [`MIN`] or [`NEG_INFINITY`], this returns [`NEG_INFINITY`];
+    ///  - otherwise the unique greatest value less than `self` is returned.
+    ///
+    /// The identity `x.next_down() == -(-x).next_up()` holds for all non-NaN `x`. When `x`
+    /// is finite `x == x.next_down().next_up()` also holds.
+    ///
+    /// ```rust
+    /// #![feature(f16)]
+    /// #![feature(float_next_up_down)]
+    /// # // FIXME(f16_f128): ABI issues on MSVC
+    /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
+    ///
+    /// let x = 1.0f16;
+    /// // Clamp value into range [0, 1).
+    /// let clamped = x.clamp(0.0, 1.0f16.next_down());
+    /// assert!(clamped < 1.0);
+    /// assert_eq!(clamped.next_up(), 1.0);
+    /// # }
+    /// ```
+    ///
+    /// [`NEG_INFINITY`]: Self::NEG_INFINITY
+    /// [`INFINITY`]: Self::INFINITY
+    /// [`MIN`]: Self::MIN
+    /// [`MAX`]: Self::MAX
+    #[inline]
+    #[cfg(not(bootstrap))]
+    #[unstable(feature = "f16", issue = "116909")]
+    // #[unstable(feature = "float_next_up_down", issue = "91399")]
+    pub fn next_down(self) -> Self {
+        // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing
+        // denormals to zero. This is in general unsound and unsupported, but here
+        // we do our best to still produce the correct result on such targets.
+        let bits = self.to_bits();
+        if self.is_nan() || bits == Self::NEG_INFINITY.to_bits() {
+            return self;
+        }
+
+        let abs = bits & !Self::SIGN_MASK;
+        let next_bits = if abs == 0 {
+            Self::NEG_TINY_BITS
+        } else if bits == abs {
+            bits - 1
+        } else {
+            bits + 1
+        };
+        Self::from_bits(next_bits)
+    }
+
+    /// Takes the reciprocal (inverse) of a number, `1/x`.
+    ///
+    /// ```
+    /// #![feature(f16)]
+    /// # // FIXME(f16_f128): remove when `extendhfsf2` and `truncsfhf2` are available
+    /// # #[cfg(target_os = "linux")] {
+    ///
+    /// let x = 2.0_f16;
+    /// let abs_difference = (x.recip() - (1.0 / x)).abs();
+    ///
+    /// assert!(abs_difference <= f16::EPSILON);
+    /// # }
+    /// ```
+    #[inline]
+    #[cfg(not(bootstrap))]
+    #[unstable(feature = "f16", issue = "116909")]
+    #[must_use = "this returns the result of the operation, without modifying the original"]
+    pub fn recip(self) -> Self {
+        1.0 / self
+    }
+
+    /// Converts radians to degrees.
+    ///
+    /// ```
+    /// #![feature(f16)]
+    /// # // FIXME(f16_f128): remove when `extendhfsf2` and `truncsfhf2` are available
+    /// # #[cfg(target_os = "linux")] {
+    ///
+    /// let angle = std::f16::consts::PI;
+    ///
+    /// let abs_difference = (angle.to_degrees() - 180.0).abs();
+    /// assert!(abs_difference <= 0.5);
+    /// # }
+    /// ```
+    #[inline]
+    #[cfg(not(bootstrap))]
+    #[unstable(feature = "f16", issue = "116909")]
+    #[must_use = "this returns the result of the operation, without modifying the original"]
+    pub fn to_degrees(self) -> Self {
+        // Use a literal for better precision.
+        const PIS_IN_180: f16 = 57.2957795130823208767981548141051703_f16;
+        self * PIS_IN_180
+    }
+
+    /// Converts degrees to radians.
+    ///
+    /// ```
+    /// #![feature(f16)]
+    /// # // FIXME(f16_f128): remove when `extendhfsf2` and `truncsfhf2` are available
+    /// # #[cfg(target_os = "linux")] {
+    ///
+    /// let angle = 180.0f16;
+    ///
+    /// let abs_difference = (angle.to_radians() - std::f16::consts::PI).abs();
+    ///
+    /// assert!(abs_difference <= 0.01);
+    /// # }
+    /// ```
+    #[inline]
+    #[cfg(not(bootstrap))]
+    #[unstable(feature = "f16", issue = "116909")]
+    #[must_use = "this returns the result of the operation, without modifying the original"]
+    pub fn to_radians(self) -> f16 {
+        // Use a literal for better precision.
+        const RADS_PER_DEG: f16 = 0.017453292519943295769236907684886_f16;
+        self * RADS_PER_DEG
+    }
+
+    /// Rounds toward zero and converts to any primitive integer type,
+    /// assuming that the value is finite and fits in that type.
+    ///
+    /// ```
+    /// #![feature(f16)]
+    /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885
+    ///
+    /// let value = 4.6_f16;
+    /// let rounded = unsafe { value.to_int_unchecked::<u16>() };
+    /// assert_eq!(rounded, 4);
+    ///
+    /// let value = -128.9_f16;
+    /// let rounded = unsafe { value.to_int_unchecked::<i8>() };
+    /// assert_eq!(rounded, i8::MIN);
+    /// # }
+    /// ```
+    ///
+    /// # Safety
+    ///
+    /// The value must:
+    ///
+    /// * Not be `NaN`
+    /// * Not be infinite
+    /// * Be representable in the return type `Int`, after truncating off its fractional part
+    #[inline]
+    #[unstable(feature = "f16", issue = "116909")]
+    #[must_use = "this returns the result of the operation, without modifying the original"]
+    pub unsafe fn to_int_unchecked<Int>(self) -> Int
+    where
+        Self: FloatToInt<Int>,
+    {
+        // SAFETY: the caller must uphold the safety contract for
+        // `FloatToInt::to_int_unchecked`.
+        unsafe { FloatToInt::<Int>::to_int_unchecked(self) }
+    }
+
     /// Raw transmutation to `u16`.
     ///
     /// This is currently identical to `transmute::<f16, u16>(self)` on all platforms.
@@ -282,6 +616,16 @@ impl f16 {
     ///
     /// Note that this function is distinct from `as` casting, which attempts to
     /// preserve the *numeric* value, and not the bitwise value.
+    ///
+    /// ```
+    /// #![feature(f16)]
+    /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885
+    ///
+    /// # // FIXME(f16_f128): enable this once const casting works
+    /// # // assert_ne!((1f16).to_bits(), 1f16 as u128); // to_bits() is not casting!
+    /// assert_eq!((12.5f16).to_bits(), 0x4a40);
+    /// # }
+    /// ```
     #[inline]
     #[unstable(feature = "f16", issue = "116909")]
     #[must_use = "this returns the result of the operation, without modifying the original"]
@@ -321,6 +665,15 @@ impl f16 {
     ///
     /// Note that this function is distinct from `as` casting, which attempts to
     /// preserve the *numeric* value, and not the bitwise value.
+    ///
+    /// ```
+    /// #![feature(f16)]
+    /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885
+    ///
+    /// let v = f16::from_bits(0x4a40);
+    /// assert_eq!(v, 12.5);
+    /// # }
+    /// ```
     #[inline]
     #[must_use]
     #[unstable(feature = "f16", issue = "116909")]
@@ -330,4 +683,293 @@ impl f16 {
         // Stability concerns.
         unsafe { mem::transmute(v) }
     }
+
+    /// Return the memory representation of this floating point number as a byte array in
+    /// big-endian (network) byte order.
+    ///
+    /// See [`from_bits`](Self::from_bits) for some discussion of the
+    /// portability of this operation (there are almost no issues).
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(f16)]
+    ///
+    /// let bytes = 12.5f16.to_be_bytes();
+    /// assert_eq!(bytes, [0x4a, 0x40]);
+    /// ```
+    #[inline]
+    #[unstable(feature = "f16", issue = "116909")]
+    #[must_use = "this returns the result of the operation, without modifying the original"]
+    pub fn to_be_bytes(self) -> [u8; 2] {
+        self.to_bits().to_be_bytes()
+    }
+
+    /// Return the memory representation of this floating point number as a byte array in
+    /// little-endian byte order.
+    ///
+    /// See [`from_bits`](Self::from_bits) for some discussion of the
+    /// portability of this operation (there are almost no issues).
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(f16)]
+    ///
+    /// let bytes = 12.5f16.to_le_bytes();
+    /// assert_eq!(bytes, [0x40, 0x4a]);
+    /// ```
+    #[inline]
+    #[unstable(feature = "f16", issue = "116909")]
+    #[must_use = "this returns the result of the operation, without modifying the original"]
+    pub fn to_le_bytes(self) -> [u8; 2] {
+        self.to_bits().to_le_bytes()
+    }
+
+    /// Return the memory representation of this floating point number as a byte array in
+    /// native byte order.
+    ///
+    /// As the target platform's native endianness is used, portable code
+    /// should use [`to_be_bytes`] or [`to_le_bytes`], as appropriate, instead.
+    ///
+    /// [`to_be_bytes`]: f16::to_be_bytes
+    /// [`to_le_bytes`]: f16::to_le_bytes
+    ///
+    /// See [`from_bits`](Self::from_bits) for some discussion of the
+    /// portability of this operation (there are almost no issues).
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(f16)]
+    ///
+    /// let bytes = 12.5f16.to_ne_bytes();
+    /// assert_eq!(
+    ///     bytes,
+    ///     if cfg!(target_endian = "big") {
+    ///         [0x4a, 0x40]
+    ///     } else {
+    ///         [0x40, 0x4a]
+    ///     }
+    /// );
+    /// ```
+    #[inline]
+    #[unstable(feature = "f16", issue = "116909")]
+    #[must_use = "this returns the result of the operation, without modifying the original"]
+    pub fn to_ne_bytes(self) -> [u8; 2] {
+        self.to_bits().to_ne_bytes()
+    }
+
+    /// Create a floating point value from its representation as a byte array in big endian.
+    ///
+    /// See [`from_bits`](Self::from_bits) for some discussion of the
+    /// portability of this operation (there are almost no issues).
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(f16)]
+    /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885
+    ///
+    /// let value = f16::from_be_bytes([0x4a, 0x40]);
+    /// assert_eq!(value, 12.5);
+    /// # }
+    /// ```
+    #[inline]
+    #[must_use]
+    #[unstable(feature = "f16", issue = "116909")]
+    pub fn from_be_bytes(bytes: [u8; 2]) -> Self {
+        Self::from_bits(u16::from_be_bytes(bytes))
+    }
+
+    /// Create a floating point value from its representation as a byte array in little endian.
+    ///
+    /// See [`from_bits`](Self::from_bits) for some discussion of the
+    /// portability of this operation (there are almost no issues).
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(f16)]
+    /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885
+    ///
+    /// let value = f16::from_le_bytes([0x40, 0x4a]);
+    /// assert_eq!(value, 12.5);
+    /// # }
+    /// ```
+    #[inline]
+    #[must_use]
+    #[unstable(feature = "f16", issue = "116909")]
+    pub fn from_le_bytes(bytes: [u8; 2]) -> Self {
+        Self::from_bits(u16::from_le_bytes(bytes))
+    }
+
+    /// Create a floating point value from its representation as a byte array in native endian.
+    ///
+    /// As the target platform's native endianness is used, portable code
+    /// likely wants to use [`from_be_bytes`] or [`from_le_bytes`], as
+    /// appropriate instead.
+    ///
+    /// [`from_be_bytes`]: f16::from_be_bytes
+    /// [`from_le_bytes`]: f16::from_le_bytes
+    ///
+    /// See [`from_bits`](Self::from_bits) for some discussion of the
+    /// portability of this operation (there are almost no issues).
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(f16)]
+    /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885
+    ///
+    /// let value = f16::from_ne_bytes(if cfg!(target_endian = "big") {
+    ///     [0x4a, 0x40]
+    /// } else {
+    ///     [0x40, 0x4a]
+    /// });
+    /// assert_eq!(value, 12.5);
+    /// # }
+    /// ```
+    #[inline]
+    #[must_use]
+    #[unstable(feature = "f16", issue = "116909")]
+    pub fn from_ne_bytes(bytes: [u8; 2]) -> Self {
+        Self::from_bits(u16::from_ne_bytes(bytes))
+    }
+
+    /// Return the ordering between `self` and `other`.
+    ///
+    /// Unlike the standard partial comparison between floating point numbers,
+    /// this comparison always produces an ordering in accordance to
+    /// the `totalOrder` predicate as defined in the IEEE 754 (2008 revision)
+    /// floating point standard. The values are ordered in the following sequence:
+    ///
+    /// - negative quiet NaN
+    /// - negative signaling NaN
+    /// - negative infinity
+    /// - negative numbers
+    /// - negative subnormal numbers
+    /// - negative zero
+    /// - positive zero
+    /// - positive subnormal numbers
+    /// - positive numbers
+    /// - positive infinity
+    /// - positive signaling NaN
+    /// - positive quiet NaN.
+    ///
+    /// The ordering established by this function does not always agree with the
+    /// [`PartialOrd`] and [`PartialEq`] implementations of `f16`. For example,
+    /// they consider negative and positive zero equal, while `total_cmp`
+    /// doesn't.
+    ///
+    /// The interpretation of the signaling NaN bit follows the definition in
+    /// the IEEE 754 standard, which may not match the interpretation by some of
+    /// the older, non-conformant (e.g. MIPS) hardware implementations.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// #![feature(f16)]
+    ///
+    /// struct GoodBoy {
+    ///     name: &'static str,
+    ///     weight: f16,
+    /// }
+    ///
+    /// let mut bois = vec![
+    ///     GoodBoy { name: "Pucci", weight: 0.1 },
+    ///     GoodBoy { name: "Woofer", weight: 99.0 },
+    ///     GoodBoy { name: "Yapper", weight: 10.0 },
+    ///     GoodBoy { name: "Chonk", weight: f16::INFINITY },
+    ///     GoodBoy { name: "Abs. Unit", weight: f16::NAN },
+    ///     GoodBoy { name: "Floaty", weight: -5.0 },
+    /// ];
+    ///
+    /// bois.sort_by(|a, b| a.weight.total_cmp(&b.weight));
+    ///
+    /// // `f16::NAN` could be positive or negative, which will affect the sort order.
+    /// if f16::NAN.is_sign_negative() {
+    ///     bois.into_iter().map(|b| b.weight)
+    ///         .zip([f16::NAN, -5.0, 0.1, 10.0, 99.0, f16::INFINITY].iter())
+    ///         .for_each(|(a, b)| assert_eq!(a.to_bits(), b.to_bits()))
+    /// } else {
+    ///     bois.into_iter().map(|b| b.weight)
+    ///         .zip([-5.0, 0.1, 10.0, 99.0, f16::INFINITY, f16::NAN].iter())
+    ///         .for_each(|(a, b)| assert_eq!(a.to_bits(), b.to_bits()))
+    /// }
+    /// ```
+    #[inline]
+    #[must_use]
+    #[cfg(not(bootstrap))]
+    #[unstable(feature = "f16", issue = "116909")]
+    pub fn total_cmp(&self, other: &Self) -> crate::cmp::Ordering {
+        let mut left = self.to_bits() as i16;
+        let mut right = other.to_bits() as i16;
+
+        // In case of negatives, flip all the bits except the sign
+        // to achieve a similar layout as two's complement integers
+        //
+        // Why does this work? IEEE 754 floats consist of three fields:
+        // Sign bit, exponent and mantissa. The set of exponent and mantissa
+        // fields as a whole have the property that their bitwise order is
+        // equal to the numeric magnitude where the magnitude is defined.
+        // The magnitude is not normally defined on NaN values, but
+        // IEEE 754 totalOrder defines the NaN values also to follow the
+        // bitwise order. This leads to order explained in the doc comment.
+        // However, the representation of magnitude is the same for negative
+        // and positive numbers – only the sign bit is different.
+        // To easily compare the floats as signed integers, we need to
+        // flip the exponent and mantissa bits in case of negative numbers.
+        // We effectively convert the numbers to "two's complement" form.
+        //
+        // To do the flipping, we construct a mask and XOR against it.
+        // We branchlessly calculate an "all-ones except for the sign bit"
+        // mask from negative-signed values: right shifting sign-extends
+        // the integer, so we "fill" the mask with sign bits, and then
+        // convert to unsigned to push one more zero bit.
+        // On positive values, the mask is all zeros, so it's a no-op.
+        left ^= (((left >> 15) as u16) >> 1) as i16;
+        right ^= (((right >> 15) as u16) >> 1) as i16;
+
+        left.cmp(&right)
+    }
+
+    /// Restrict a value to a certain interval unless it is NaN.
+    ///
+    /// Returns `max` if `self` is greater than `max`, and `min` if `self` is
+    /// less than `min`. Otherwise this returns `self`.
+    ///
+    /// Note that this function returns NaN if the initial value was NaN as
+    /// well.
+    ///
+    /// # Panics
+    ///
+    /// Panics if `min > max`, `min` is NaN, or `max` is NaN.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(f16)]
+    /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885
+    ///
+    /// assert!((-3.0f16).clamp(-2.0, 1.0) == -2.0);
+    /// assert!((0.0f16).clamp(-2.0, 1.0) == 0.0);
+    /// assert!((2.0f16).clamp(-2.0, 1.0) == 1.0);
+    /// assert!((f16::NAN).clamp(-2.0, 1.0).is_nan());
+    /// # }
+    /// ```
+    #[inline]
+    #[cfg(not(bootstrap))]
+    #[unstable(feature = "f16", issue = "116909")]
+    #[must_use = "method returns a new number and does not mutate the original value"]
+    pub fn clamp(mut self, min: f16, max: f16) -> f16 {
+        assert!(min <= max, "min > max, or either was NaN. min = {min:?}, max = {max:?}");
+        if self < min {
+            self = min;
+        }
+        if self > max {
+            self = max;
+        }
+        self
+    }
 }
diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs
index 9d34d3da9e9..b9c84a66ed1 100644
--- a/library/core/src/num/f32.rs
+++ b/library/core/src/num/f32.rs
@@ -490,6 +490,21 @@ impl f32 {
     #[stable(feature = "assoc_int_consts", since = "1.43.0")]
     pub const NEG_INFINITY: f32 = -1.0_f32 / 0.0_f32;
 
+    /// Sign bit
+    const SIGN_MASK: u32 = 0x8000_0000;
+
+    /// Exponent mask
+    const EXP_MASK: u32 = 0x7f80_0000;
+
+    /// Mantissa mask
+    const MAN_MASK: u32 = 0x007f_ffff;
+
+    /// Minimum representable positive value (min subnormal)
+    const TINY_BITS: u32 = 0x1;
+
+    /// Minimum representable negative value (min negative subnormal)
+    const NEG_TINY_BITS: u32 = Self::TINY_BITS | Self::SIGN_MASK;
+
     /// Returns `true` if this value is NaN.
     ///
     /// ```
@@ -515,7 +530,7 @@ impl f32 {
     #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
     pub(crate) const fn abs_private(self) -> f32 {
         // SAFETY: This transmutation is fine. Probably. For the reasons std is using it.
-        unsafe { mem::transmute::<u32, f32>(mem::transmute::<f32, u32>(self) & 0x7fff_ffff) }
+        unsafe { mem::transmute::<u32, f32>(mem::transmute::<f32, u32>(self) & !Self::SIGN_MASK) }
     }
 
     /// Returns `true` if this value is positive infinity or negative infinity, and
@@ -682,12 +697,9 @@ impl f32 {
     // runtime-deviating logic which may or may not be acceptable.
     #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
     const unsafe fn partial_classify(self) -> FpCategory {
-        const EXP_MASK: u32 = 0x7f800000;
-        const MAN_MASK: u32 = 0x007fffff;
-
         // SAFETY: The caller is not asking questions for which this will tell lies.
         let b = unsafe { mem::transmute::<f32, u32>(self) };
-        match (b & MAN_MASK, b & EXP_MASK) {
+        match (b & Self::MAN_MASK, b & Self::EXP_MASK) {
             (0, 0) => FpCategory::Zero,
             (_, 0) => FpCategory::Subnormal,
             _ => FpCategory::Normal,
@@ -699,12 +711,9 @@ impl f32 {
     // plus a transmute. We do not live in a just world, but we can make it more so.
     #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
     const fn classify_bits(b: u32) -> FpCategory {
-        const EXP_MASK: u32 = 0x7f800000;
-        const MAN_MASK: u32 = 0x007fffff;
-
-        match (b & MAN_MASK, b & EXP_MASK) {
-            (0, EXP_MASK) => FpCategory::Infinite,
-            (_, EXP_MASK) => FpCategory::Nan,
+        match (b & Self::MAN_MASK, b & Self::EXP_MASK) {
+            (0, Self::EXP_MASK) => FpCategory::Infinite,
+            (_, Self::EXP_MASK) => FpCategory::Nan,
             (0, 0) => FpCategory::Zero,
             (_, 0) => FpCategory::Subnormal,
             _ => FpCategory::Normal,
@@ -787,19 +796,17 @@ impl f32 {
     #[unstable(feature = "float_next_up_down", issue = "91399")]
     #[rustc_const_unstable(feature = "float_next_up_down", issue = "91399")]
     pub const fn next_up(self) -> Self {
-        // We must use strictly integer arithmetic to prevent denormals from
-        // flushing to zero after an arithmetic operation on some platforms.
-        const TINY_BITS: u32 = 0x1; // Smallest positive f32.
-        const CLEAR_SIGN_MASK: u32 = 0x7fff_ffff;
-
+        // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing
+        // denormals to zero. This is in general unsound and unsupported, but here
+        // we do our best to still produce the correct result on such targets.
         let bits = self.to_bits();
         if self.is_nan() || bits == Self::INFINITY.to_bits() {
             return self;
         }
 
-        let abs = bits & CLEAR_SIGN_MASK;
+        let abs = bits & !Self::SIGN_MASK;
         let next_bits = if abs == 0 {
-            TINY_BITS
+            Self::TINY_BITS
         } else if bits == abs {
             bits + 1
         } else {
@@ -837,19 +844,17 @@ impl f32 {
     #[unstable(feature = "float_next_up_down", issue = "91399")]
     #[rustc_const_unstable(feature = "float_next_up_down", issue = "91399")]
     pub const fn next_down(self) -> Self {
-        // We must use strictly integer arithmetic to prevent denormals from
-        // flushing to zero after an arithmetic operation on some platforms.
-        const NEG_TINY_BITS: u32 = 0x8000_0001; // Smallest (in magnitude) negative f32.
-        const CLEAR_SIGN_MASK: u32 = 0x7fff_ffff;
-
+        // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing
+        // denormals to zero. This is in general unsound and unsupported, but here
+        // we do our best to still produce the correct result on such targets.
         let bits = self.to_bits();
         if self.is_nan() || bits == Self::NEG_INFINITY.to_bits() {
             return self;
         }
 
-        let abs = bits & CLEAR_SIGN_MASK;
+        let abs = bits & !Self::SIGN_MASK;
         let next_bits = if abs == 0 {
-            NEG_TINY_BITS
+            Self::NEG_TINY_BITS
         } else if bits == abs {
             bits - 1
         } else {
diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs
index 95f021b2541..f8e4555fc44 100644
--- a/library/core/src/num/f64.rs
+++ b/library/core/src/num/f64.rs
@@ -489,6 +489,21 @@ impl f64 {
     #[stable(feature = "assoc_int_consts", since = "1.43.0")]
     pub const NEG_INFINITY: f64 = -1.0_f64 / 0.0_f64;
 
+    /// Sign bit
+    const SIGN_MASK: u64 = 0x8000_0000_0000_0000;
+
+    /// Exponent mask
+    const EXP_MASK: u64 = 0x7ff0_0000_0000_0000;
+
+    /// Mantissa mask
+    const MAN_MASK: u64 = 0x000f_ffff_ffff_ffff;
+
+    /// Minimum representable positive value (min subnormal)
+    const TINY_BITS: u64 = 0x1;
+
+    /// Minimum representable negative value (min negative subnormal)
+    const NEG_TINY_BITS: u64 = Self::TINY_BITS | Self::SIGN_MASK;
+
     /// Returns `true` if this value is NaN.
     ///
     /// ```
@@ -514,9 +529,7 @@ impl f64 {
     #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
     pub(crate) const fn abs_private(self) -> f64 {
         // SAFETY: This transmutation is fine. Probably. For the reasons std is using it.
-        unsafe {
-            mem::transmute::<u64, f64>(mem::transmute::<f64, u64>(self) & 0x7fff_ffff_ffff_ffff)
-        }
+        unsafe { mem::transmute::<u64, f64>(mem::transmute::<f64, u64>(self) & !Self::SIGN_MASK) }
     }
 
     /// Returns `true` if this value is positive infinity or negative infinity, and
@@ -673,13 +686,10 @@ impl f64 {
     // and some normal floating point numbers truncated from an x87 FPU.
     #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
     const unsafe fn partial_classify(self) -> FpCategory {
-        const EXP_MASK: u64 = 0x7ff0000000000000;
-        const MAN_MASK: u64 = 0x000fffffffffffff;
-
         // SAFETY: The caller is not asking questions for which this will tell lies.
         let b = unsafe { mem::transmute::<f64, u64>(self) };
-        match (b & MAN_MASK, b & EXP_MASK) {
-            (0, EXP_MASK) => FpCategory::Infinite,
+        match (b & Self::MAN_MASK, b & Self::EXP_MASK) {
+            (0, Self::EXP_MASK) => FpCategory::Infinite,
             (0, 0) => FpCategory::Zero,
             (_, 0) => FpCategory::Subnormal,
             _ => FpCategory::Normal,
@@ -691,12 +701,9 @@ impl f64 {
     // plus a transmute. We do not live in a just world, but we can make it more so.
     #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
     const fn classify_bits(b: u64) -> FpCategory {
-        const EXP_MASK: u64 = 0x7ff0000000000000;
-        const MAN_MASK: u64 = 0x000fffffffffffff;
-
-        match (b & MAN_MASK, b & EXP_MASK) {
-            (0, EXP_MASK) => FpCategory::Infinite,
-            (_, EXP_MASK) => FpCategory::Nan,
+        match (b & Self::MAN_MASK, b & Self::EXP_MASK) {
+            (0, Self::EXP_MASK) => FpCategory::Infinite,
+            (_, Self::EXP_MASK) => FpCategory::Nan,
             (0, 0) => FpCategory::Zero,
             (_, 0) => FpCategory::Subnormal,
             _ => FpCategory::Normal,
@@ -756,7 +763,7 @@ impl f64 {
         // IEEE754 says: isSignMinus(x) is true if and only if x has negative sign. isSignMinus
         // applies to zeros and NaNs as well.
         // SAFETY: This is just transmuting to get the sign bit, it's fine.
-        unsafe { mem::transmute::<f64, u64>(self) & 0x8000_0000_0000_0000 != 0 }
+        unsafe { mem::transmute::<f64, u64>(self) & Self::SIGN_MASK != 0 }
     }
 
     #[must_use]
@@ -797,19 +804,17 @@ impl f64 {
     #[unstable(feature = "float_next_up_down", issue = "91399")]
     #[rustc_const_unstable(feature = "float_next_up_down", issue = "91399")]
     pub const fn next_up(self) -> Self {
-        // We must use strictly integer arithmetic to prevent denormals from
-        // flushing to zero after an arithmetic operation on some platforms.
-        const TINY_BITS: u64 = 0x1; // Smallest positive f64.
-        const CLEAR_SIGN_MASK: u64 = 0x7fff_ffff_ffff_ffff;
-
+        // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing
+        // denormals to zero. This is in general unsound and unsupported, but here
+        // we do our best to still produce the correct result on such targets.
         let bits = self.to_bits();
         if self.is_nan() || bits == Self::INFINITY.to_bits() {
             return self;
         }
 
-        let abs = bits & CLEAR_SIGN_MASK;
+        let abs = bits & !Self::SIGN_MASK;
         let next_bits = if abs == 0 {
-            TINY_BITS
+            Self::TINY_BITS
         } else if bits == abs {
             bits + 1
         } else {
@@ -847,19 +852,17 @@ impl f64 {
     #[unstable(feature = "float_next_up_down", issue = "91399")]
     #[rustc_const_unstable(feature = "float_next_up_down", issue = "91399")]
     pub const fn next_down(self) -> Self {
-        // We must use strictly integer arithmetic to prevent denormals from
-        // flushing to zero after an arithmetic operation on some platforms.
-        const NEG_TINY_BITS: u64 = 0x8000_0000_0000_0001; // Smallest (in magnitude) negative f64.
-        const CLEAR_SIGN_MASK: u64 = 0x7fff_ffff_ffff_ffff;
-
+        // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing
+        // denormals to zero. This is in general unsound and unsupported, but here
+        // we do our best to still produce the correct result on such targets.
         let bits = self.to_bits();
         if self.is_nan() || bits == Self::NEG_INFINITY.to_bits() {
             return self;
         }
 
-        let abs = bits & CLEAR_SIGN_MASK;
+        let abs = bits & !Self::SIGN_MASK;
         let next_bits = if abs == 0 {
-            NEG_TINY_BITS
+            Self::NEG_TINY_BITS
         } else if bits == abs {
             bits - 1
         } else {
diff --git a/library/core/tests/iter/adapters/filter.rs b/library/core/tests/iter/adapters/filter.rs
index a2050d89d85..167851e3333 100644
--- a/library/core/tests/iter/adapters/filter.rs
+++ b/library/core/tests/iter/adapters/filter.rs
@@ -1,4 +1,5 @@
 use core::iter::*;
+use std::rc::Rc;
 
 #[test]
 fn test_iterator_filter_count() {
@@ -50,3 +51,15 @@ fn test_double_ended_filter() {
     assert_eq!(it.next().unwrap(), &2);
     assert_eq!(it.next_back(), None);
 }
+
+#[test]
+fn test_next_chunk_does_not_leak() {
+    let drop_witness: [_; 5] = std::array::from_fn(|_| Rc::new(()));
+
+    let v = (0..5).map(|i| drop_witness[i].clone()).collect::<Vec<_>>();
+    let _ = v.into_iter().filter(|_| false).next_chunk::<1>();
+
+    for ref w in drop_witness {
+        assert_eq!(Rc::strong_count(w), 1);
+    }
+}
diff --git a/library/std/build.rs b/library/std/build.rs
index 7d975df545e..55388648a14 100644
--- a/library/std/build.rs
+++ b/library/std/build.rs
@@ -7,6 +7,10 @@ fn main() {
     let target_vendor =
         env::var("CARGO_CFG_TARGET_VENDOR").expect("CARGO_CFG_TARGET_VENDOR was not set");
     let target_env = env::var("CARGO_CFG_TARGET_ENV").expect("CARGO_CFG_TARGET_ENV was not set");
+    let target_pointer_width: u32 = env::var("CARGO_CFG_TARGET_POINTER_WIDTH")
+        .expect("CARGO_CFG_TARGET_POINTER_WIDTH was not set")
+        .parse()
+        .unwrap();
 
     println!("cargo:rustc-check-cfg=cfg(netbsd10)");
     if target_os == "netbsd" && env::var("RUSTC_STD_NETBSD10").is_ok() {
@@ -70,4 +74,62 @@ fn main() {
     println!("cargo:rustc-cfg=backtrace_in_libstd");
 
     println!("cargo:rustc-env=STD_ENV_ARCH={}", env::var("CARGO_CFG_TARGET_ARCH").unwrap());
+
+    // Emit these on platforms that have no known ABI bugs, LLVM selection bugs, lowering bugs,
+    // missing symbols, or other problems, to determine when tests get run.
+    // If more broken platforms are found, please update the tracking issue at
+    // <https://github.com/rust-lang/rust/issues/116909>
+    //
+    // Some of these match arms are redundant; the goal is to separate reasons that the type is
+    // unreliable, even when multiple reasons might fail the same platform.
+    println!("cargo:rustc-check-cfg=cfg(reliable_f16)");
+    println!("cargo:rustc-check-cfg=cfg(reliable_f128)");
+
+    let has_reliable_f16 = match (target_arch.as_str(), target_os.as_str()) {
+        // Selection failure until recent LLVM <https://github.com/llvm/llvm-project/issues/93894>
+        // FIXME(llvm19): can probably be removed at the version bump
+        ("loongarch64", _) => false,
+        // Selection failure <https://github.com/llvm/llvm-project/issues/50374>
+        ("s390x", _) => false,
+        // Unsupported <https://github.com/llvm/llvm-project/issues/94434>
+        ("arm64ec", _) => false,
+        // MinGW ABI bugs <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=115054>
+        ("x86", "windows") => false,
+        // x86 has ABI bugs that show up with optimizations. This should be partially fixed with
+        // the compiler-builtins update. <https://github.com/rust-lang/rust/issues/123885>
+        ("x86" | "x86_64", _) => false,
+        // Missing `__gnu_h2f_ieee` and `__gnu_f2h_ieee`
+        ("powerpc" | "powerpc64" | "powerpc64le", _) => false,
+        // Missing `__extendhfsf` and `__truncsfhf`
+        ("riscv32" | "riscv64", _) => false,
+        // Most OSs are missing `__extendhfsf` and `__truncsfhf`
+        (_, "linux" | "macos") => true,
+        // Almost all OSs besides Linux and MacOS are missing symbols until compiler-builtins can
+        // be updated. <https://github.com/rust-lang/rust/pull/125016> will get some of these, the
+        // next CB update should get the rest.
+        _ => false,
+    };
+
+    let has_reliable_f128 = match (target_arch.as_str(), target_os.as_str()) {
+        // Unsupported <https://github.com/llvm/llvm-project/issues/94434>
+        ("arm64ec", _) => false,
+        // ABI and precision bugs <https://github.com/rust-lang/rust/issues/125109>
+        // <https://github.com/rust-lang/rust/issues/125102>
+        ("powerpc" | "powerpc64", _) => false,
+        // Selection bug <https://github.com/llvm/llvm-project/issues/95471>
+        ("nvptx64", _) => false,
+        // ABI unsupported  <https://github.com/llvm/llvm-project/issues/41838>
+        ("sparc", _) => false,
+        // 64-bit Linux is about the only platform to have f128 symbols by default
+        (_, "linux") if target_pointer_width == 64 => true,
+        // Same as for f16, except MacOS is also missing f128 symbols.
+        _ => false,
+    };
+
+    if has_reliable_f16 {
+        println!("cargo:rustc-cfg=reliable_f16");
+    }
+    if has_reliable_f128 {
+        println!("cargo:rustc-cfg=reliable_f128");
+    }
 }
diff --git a/library/std/src/f128.rs b/library/std/src/f128.rs
index 491235a872e..0591c6f517b 100644
--- a/library/std/src/f128.rs
+++ b/library/std/src/f128.rs
@@ -32,4 +32,34 @@ impl f128 {
     pub fn powi(self, n: i32) -> f128 {
         unsafe { intrinsics::powif128(self, n) }
     }
+
+    /// Computes the absolute value of `self`.
+    ///
+    /// This function always returns the precise result.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(f128)]
+    /// # #[cfg(reliable_f128)] { // FIXME(f16_f128): reliable_f128
+    ///
+    /// let x = 3.5_f128;
+    /// let y = -3.5_f128;
+    ///
+    /// assert_eq!(x.abs(), x);
+    /// assert_eq!(y.abs(), -y);
+    ///
+    /// assert!(f128::NAN.abs().is_nan());
+    /// # }
+    /// ```
+    #[inline]
+    #[cfg(not(bootstrap))]
+    #[rustc_allow_incoherent_impl]
+    #[unstable(feature = "f128", issue = "116909")]
+    #[must_use = "method returns a new number and does not mutate the original value"]
+    pub fn abs(self) -> Self {
+        // FIXME(f16_f128): replace with `intrinsics::fabsf128` when available
+        // We don't do this now because LLVM has lowering bugs for f128 math.
+        Self::from_bits(self.to_bits() & !(1 << 127))
+    }
 }
diff --git a/library/std/src/f128/tests.rs b/library/std/src/f128/tests.rs
index b64c7f856a1..bd7a921c502 100644
--- a/library/std/src/f128/tests.rs
+++ b/library/std/src/f128/tests.rs
@@ -1,29 +1,31 @@
-#![allow(dead_code)] // FIXME(f16_f128): remove once constants are used
+#![cfg(not(bootstrap))]
+// FIXME(f16_f128): only tested on platforms that have symbols and aren't buggy
+#![cfg(reliable_f128)]
+
+use crate::f128::consts;
+use crate::num::*;
 
 /// Smallest number
 const TINY_BITS: u128 = 0x1;
+
 /// Next smallest number
 const TINY_UP_BITS: u128 = 0x2;
+
 /// Exponent = 0b11...10, Sifnificand 0b1111..10. Min val > 0
-const MAX_DOWN_BITS: u128 = 0x7ffeffffffffffffffffffffffffffff;
+const MAX_DOWN_BITS: u128 = 0x7ffefffffffffffffffffffffffffffe;
+
 /// Zeroed exponent, full significant
 const LARGEST_SUBNORMAL_BITS: u128 = 0x0000ffffffffffffffffffffffffffff;
+
 /// Exponent = 0b1, zeroed significand
 const SMALLEST_NORMAL_BITS: u128 = 0x00010000000000000000000000000000;
+
 /// First pattern over the mantissa
 const NAN_MASK1: u128 = 0x0000aaaaaaaaaaaaaaaaaaaaaaaaaaaa;
+
 /// Second pattern over the mantissa
 const NAN_MASK2: u128 = 0x00005555555555555555555555555555;
 
-/// Compare by value
-#[allow(unused_macros)]
-macro_rules! assert_f128_eq {
-    ($a:expr, $b:expr) => {
-        let (l, r): (&f128, &f128) = (&$a, &$b);
-        assert_eq!(*l, *r, "\na: {:#0130x}\nb: {:#0130x}", l.to_bits(), r.to_bits())
-    };
-}
-
 /// Compare by representation
 #[allow(unused_macros)]
 macro_rules! assert_f128_biteq {
@@ -31,10 +33,503 @@ macro_rules! assert_f128_biteq {
         let (l, r): (&f128, &f128) = (&$a, &$b);
         let lb = l.to_bits();
         let rb = r.to_bits();
-        assert_eq!(
-            lb, rb,
-            "float {:?} is not bitequal to {:?}.\na: {:#0130x}\nb: {:#0130x}",
-            *l, *r, lb, rb
-        );
+        assert_eq!(lb, rb, "float {l:?} is not bitequal to {r:?}.\na: {lb:#034x}\nb: {rb:#034x}");
     };
 }
+
+#[test]
+fn test_num_f128() {
+    test_num(10f128, 2f128);
+}
+
+// FIXME(f16_f128): add min and max tests when available
+
+#[test]
+fn test_nan() {
+    let nan: f128 = f128::NAN;
+    assert!(nan.is_nan());
+    assert!(!nan.is_infinite());
+    assert!(!nan.is_finite());
+    assert!(nan.is_sign_positive());
+    assert!(!nan.is_sign_negative());
+    // FIXME(f16_f128): classify
+    // assert!(!nan.is_normal());
+    // assert_eq!(Fp::Nan, nan.classify());
+}
+
+#[test]
+fn test_infinity() {
+    let inf: f128 = f128::INFINITY;
+    assert!(inf.is_infinite());
+    assert!(!inf.is_finite());
+    assert!(inf.is_sign_positive());
+    assert!(!inf.is_sign_negative());
+    assert!(!inf.is_nan());
+    // FIXME(f16_f128): classify
+    // assert!(!inf.is_normal());
+    // assert_eq!(Fp::Infinite, inf.classify());
+}
+
+#[test]
+fn test_neg_infinity() {
+    let neg_inf: f128 = f128::NEG_INFINITY;
+    assert!(neg_inf.is_infinite());
+    assert!(!neg_inf.is_finite());
+    assert!(!neg_inf.is_sign_positive());
+    assert!(neg_inf.is_sign_negative());
+    assert!(!neg_inf.is_nan());
+    // FIXME(f16_f128): classify
+    // assert!(!neg_inf.is_normal());
+    // assert_eq!(Fp::Infinite, neg_inf.classify());
+}
+
+#[test]
+fn test_zero() {
+    let zero: f128 = 0.0f128;
+    assert_eq!(0.0, zero);
+    assert!(!zero.is_infinite());
+    assert!(zero.is_finite());
+    assert!(zero.is_sign_positive());
+    assert!(!zero.is_sign_negative());
+    assert!(!zero.is_nan());
+    // FIXME(f16_f128): classify
+    // assert!(!zero.is_normal());
+    // assert_eq!(Fp::Zero, zero.classify());
+}
+
+#[test]
+fn test_neg_zero() {
+    let neg_zero: f128 = -0.0;
+    assert_eq!(0.0, neg_zero);
+    assert!(!neg_zero.is_infinite());
+    assert!(neg_zero.is_finite());
+    assert!(!neg_zero.is_sign_positive());
+    assert!(neg_zero.is_sign_negative());
+    assert!(!neg_zero.is_nan());
+    // FIXME(f16_f128): classify
+    // assert!(!neg_zero.is_normal());
+    // assert_eq!(Fp::Zero, neg_zero.classify());
+}
+
+#[test]
+fn test_one() {
+    let one: f128 = 1.0f128;
+    assert_eq!(1.0, one);
+    assert!(!one.is_infinite());
+    assert!(one.is_finite());
+    assert!(one.is_sign_positive());
+    assert!(!one.is_sign_negative());
+    assert!(!one.is_nan());
+    // FIXME(f16_f128): classify
+    // assert!(one.is_normal());
+    // assert_eq!(Fp::Normal, one.classify());
+}
+
+#[test]
+fn test_is_nan() {
+    let nan: f128 = f128::NAN;
+    let inf: f128 = f128::INFINITY;
+    let neg_inf: f128 = f128::NEG_INFINITY;
+    assert!(nan.is_nan());
+    assert!(!0.0f128.is_nan());
+    assert!(!5.3f128.is_nan());
+    assert!(!(-10.732f128).is_nan());
+    assert!(!inf.is_nan());
+    assert!(!neg_inf.is_nan());
+}
+
+#[test]
+fn test_is_infinite() {
+    let nan: f128 = f128::NAN;
+    let inf: f128 = f128::INFINITY;
+    let neg_inf: f128 = f128::NEG_INFINITY;
+    assert!(!nan.is_infinite());
+    assert!(inf.is_infinite());
+    assert!(neg_inf.is_infinite());
+    assert!(!0.0f128.is_infinite());
+    assert!(!42.8f128.is_infinite());
+    assert!(!(-109.2f128).is_infinite());
+}
+
+#[test]
+fn test_is_finite() {
+    let nan: f128 = f128::NAN;
+    let inf: f128 = f128::INFINITY;
+    let neg_inf: f128 = f128::NEG_INFINITY;
+    assert!(!nan.is_finite());
+    assert!(!inf.is_finite());
+    assert!(!neg_inf.is_finite());
+    assert!(0.0f128.is_finite());
+    assert!(42.8f128.is_finite());
+    assert!((-109.2f128).is_finite());
+}
+
+// FIXME(f16_f128): add `test_is_normal` and `test_classify` when classify is working
+// FIXME(f16_f128): add missing math functions when available
+
+#[test]
+fn test_abs() {
+    assert_eq!(f128::INFINITY.abs(), f128::INFINITY);
+    assert_eq!(1f128.abs(), 1f128);
+    assert_eq!(0f128.abs(), 0f128);
+    assert_eq!((-0f128).abs(), 0f128);
+    assert_eq!((-1f128).abs(), 1f128);
+    assert_eq!(f128::NEG_INFINITY.abs(), f128::INFINITY);
+    assert_eq!((1f128 / f128::NEG_INFINITY).abs(), 0f128);
+    assert!(f128::NAN.abs().is_nan());
+}
+
+#[test]
+fn test_is_sign_positive() {
+    assert!(f128::INFINITY.is_sign_positive());
+    assert!(1f128.is_sign_positive());
+    assert!(0f128.is_sign_positive());
+    assert!(!(-0f128).is_sign_positive());
+    assert!(!(-1f128).is_sign_positive());
+    assert!(!f128::NEG_INFINITY.is_sign_positive());
+    assert!(!(1f128 / f128::NEG_INFINITY).is_sign_positive());
+    assert!(f128::NAN.is_sign_positive());
+    assert!(!(-f128::NAN).is_sign_positive());
+}
+
+#[test]
+fn test_is_sign_negative() {
+    assert!(!f128::INFINITY.is_sign_negative());
+    assert!(!1f128.is_sign_negative());
+    assert!(!0f128.is_sign_negative());
+    assert!((-0f128).is_sign_negative());
+    assert!((-1f128).is_sign_negative());
+    assert!(f128::NEG_INFINITY.is_sign_negative());
+    assert!((1f128 / f128::NEG_INFINITY).is_sign_negative());
+    assert!(!f128::NAN.is_sign_negative());
+    assert!((-f128::NAN).is_sign_negative());
+}
+
+#[test]
+fn test_next_up() {
+    let tiny = f128::from_bits(TINY_BITS);
+    let tiny_up = f128::from_bits(TINY_UP_BITS);
+    let max_down = f128::from_bits(MAX_DOWN_BITS);
+    let largest_subnormal = f128::from_bits(LARGEST_SUBNORMAL_BITS);
+    let smallest_normal = f128::from_bits(SMALLEST_NORMAL_BITS);
+    assert_f128_biteq!(f128::NEG_INFINITY.next_up(), f128::MIN);
+    assert_f128_biteq!(f128::MIN.next_up(), -max_down);
+    assert_f128_biteq!((-1.0 - f128::EPSILON).next_up(), -1.0);
+    assert_f128_biteq!((-smallest_normal).next_up(), -largest_subnormal);
+    assert_f128_biteq!((-tiny_up).next_up(), -tiny);
+    assert_f128_biteq!((-tiny).next_up(), -0.0f128);
+    assert_f128_biteq!((-0.0f128).next_up(), tiny);
+    assert_f128_biteq!(0.0f128.next_up(), tiny);
+    assert_f128_biteq!(tiny.next_up(), tiny_up);
+    assert_f128_biteq!(largest_subnormal.next_up(), smallest_normal);
+    assert_f128_biteq!(1.0f128.next_up(), 1.0 + f128::EPSILON);
+    assert_f128_biteq!(f128::MAX.next_up(), f128::INFINITY);
+    assert_f128_biteq!(f128::INFINITY.next_up(), f128::INFINITY);
+
+    // Check that NaNs roundtrip.
+    let nan0 = f128::NAN;
+    let nan1 = f128::from_bits(f128::NAN.to_bits() ^ 0x002a_aaaa);
+    let nan2 = f128::from_bits(f128::NAN.to_bits() ^ 0x0055_5555);
+    assert_f128_biteq!(nan0.next_up(), nan0);
+    assert_f128_biteq!(nan1.next_up(), nan1);
+    assert_f128_biteq!(nan2.next_up(), nan2);
+}
+
+#[test]
+fn test_next_down() {
+    let tiny = f128::from_bits(TINY_BITS);
+    let tiny_up = f128::from_bits(TINY_UP_BITS);
+    let max_down = f128::from_bits(MAX_DOWN_BITS);
+    let largest_subnormal = f128::from_bits(LARGEST_SUBNORMAL_BITS);
+    let smallest_normal = f128::from_bits(SMALLEST_NORMAL_BITS);
+    assert_f128_biteq!(f128::NEG_INFINITY.next_down(), f128::NEG_INFINITY);
+    assert_f128_biteq!(f128::MIN.next_down(), f128::NEG_INFINITY);
+    assert_f128_biteq!((-max_down).next_down(), f128::MIN);
+    assert_f128_biteq!((-1.0f128).next_down(), -1.0 - f128::EPSILON);
+    assert_f128_biteq!((-largest_subnormal).next_down(), -smallest_normal);
+    assert_f128_biteq!((-tiny).next_down(), -tiny_up);
+    assert_f128_biteq!((-0.0f128).next_down(), -tiny);
+    assert_f128_biteq!((0.0f128).next_down(), -tiny);
+    assert_f128_biteq!(tiny.next_down(), 0.0f128);
+    assert_f128_biteq!(tiny_up.next_down(), tiny);
+    assert_f128_biteq!(smallest_normal.next_down(), largest_subnormal);
+    assert_f128_biteq!((1.0 + f128::EPSILON).next_down(), 1.0f128);
+    assert_f128_biteq!(f128::MAX.next_down(), max_down);
+    assert_f128_biteq!(f128::INFINITY.next_down(), f128::MAX);
+
+    // Check that NaNs roundtrip.
+    let nan0 = f128::NAN;
+    let nan1 = f128::from_bits(f128::NAN.to_bits() ^ 0x002a_aaaa);
+    let nan2 = f128::from_bits(f128::NAN.to_bits() ^ 0x0055_5555);
+    assert_f128_biteq!(nan0.next_down(), nan0);
+    assert_f128_biteq!(nan1.next_down(), nan1);
+    assert_f128_biteq!(nan2.next_down(), nan2);
+}
+
+#[test]
+fn test_recip() {
+    let nan: f128 = f128::NAN;
+    let inf: f128 = f128::INFINITY;
+    let neg_inf: f128 = f128::NEG_INFINITY;
+    assert_eq!(1.0f128.recip(), 1.0);
+    assert_eq!(2.0f128.recip(), 0.5);
+    assert_eq!((-0.4f128).recip(), -2.5);
+    assert_eq!(0.0f128.recip(), inf);
+    assert!(nan.recip().is_nan());
+    assert_eq!(inf.recip(), 0.0);
+    assert_eq!(neg_inf.recip(), 0.0);
+}
+
+#[test]
+fn test_to_degrees() {
+    let pi: f128 = consts::PI;
+    let nan: f128 = f128::NAN;
+    let inf: f128 = f128::INFINITY;
+    let neg_inf: f128 = f128::NEG_INFINITY;
+    assert_eq!(0.0f128.to_degrees(), 0.0);
+    assert_approx_eq!((-5.8f128).to_degrees(), -332.315521);
+    assert_eq!(pi.to_degrees(), 180.0);
+    assert!(nan.to_degrees().is_nan());
+    assert_eq!(inf.to_degrees(), inf);
+    assert_eq!(neg_inf.to_degrees(), neg_inf);
+    assert_eq!(1_f128.to_degrees(), 57.2957795130823208767981548141051703);
+}
+
+#[test]
+fn test_to_radians() {
+    let pi: f128 = consts::PI;
+    let nan: f128 = f128::NAN;
+    let inf: f128 = f128::INFINITY;
+    let neg_inf: f128 = f128::NEG_INFINITY;
+    assert_eq!(0.0f128.to_radians(), 0.0);
+    assert_approx_eq!(154.6f128.to_radians(), 2.698279);
+    assert_approx_eq!((-332.31f128).to_radians(), -5.799903);
+    // check approx rather than exact because round trip for pi doesn't fall on an exactly
+    // representable value (unlike `f32` and `f64`).
+    assert_approx_eq!(180.0f128.to_radians(), pi);
+    assert!(nan.to_radians().is_nan());
+    assert_eq!(inf.to_radians(), inf);
+    assert_eq!(neg_inf.to_radians(), neg_inf);
+}
+
+#[test]
+fn test_real_consts() {
+    // FIXME(f16_f128): add math tests when available
+    use super::consts;
+
+    let pi: f128 = consts::PI;
+    let frac_pi_2: f128 = consts::FRAC_PI_2;
+    let frac_pi_3: f128 = consts::FRAC_PI_3;
+    let frac_pi_4: f128 = consts::FRAC_PI_4;
+    let frac_pi_6: f128 = consts::FRAC_PI_6;
+    let frac_pi_8: f128 = consts::FRAC_PI_8;
+    let frac_1_pi: f128 = consts::FRAC_1_PI;
+    let frac_2_pi: f128 = consts::FRAC_2_PI;
+    // let frac_2_sqrtpi: f128 = consts::FRAC_2_SQRT_PI;
+    // let sqrt2: f128 = consts::SQRT_2;
+    // let frac_1_sqrt2: f128 = consts::FRAC_1_SQRT_2;
+    // let e: f128 = consts::E;
+    // let log2_e: f128 = consts::LOG2_E;
+    // let log10_e: f128 = consts::LOG10_E;
+    // let ln_2: f128 = consts::LN_2;
+    // let ln_10: f128 = consts::LN_10;
+
+    assert_approx_eq!(frac_pi_2, pi / 2f128);
+    assert_approx_eq!(frac_pi_3, pi / 3f128);
+    assert_approx_eq!(frac_pi_4, pi / 4f128);
+    assert_approx_eq!(frac_pi_6, pi / 6f128);
+    assert_approx_eq!(frac_pi_8, pi / 8f128);
+    assert_approx_eq!(frac_1_pi, 1f128 / pi);
+    assert_approx_eq!(frac_2_pi, 2f128 / pi);
+    // assert_approx_eq!(frac_2_sqrtpi, 2f128 / pi.sqrt());
+    // assert_approx_eq!(sqrt2, 2f128.sqrt());
+    // assert_approx_eq!(frac_1_sqrt2, 1f128 / 2f128.sqrt());
+    // assert_approx_eq!(log2_e, e.log2());
+    // assert_approx_eq!(log10_e, e.log10());
+    // assert_approx_eq!(ln_2, 2f128.ln());
+    // assert_approx_eq!(ln_10, 10f128.ln());
+}
+
+#[test]
+fn test_float_bits_conv() {
+    assert_eq!((1f128).to_bits(), 0x3fff0000000000000000000000000000);
+    assert_eq!((12.5f128).to_bits(), 0x40029000000000000000000000000000);
+    assert_eq!((1337f128).to_bits(), 0x40094e40000000000000000000000000);
+    assert_eq!((-14.25f128).to_bits(), 0xc002c800000000000000000000000000);
+    assert_approx_eq!(f128::from_bits(0x3fff0000000000000000000000000000), 1.0);
+    assert_approx_eq!(f128::from_bits(0x40029000000000000000000000000000), 12.5);
+    assert_approx_eq!(f128::from_bits(0x40094e40000000000000000000000000), 1337.0);
+    assert_approx_eq!(f128::from_bits(0xc002c800000000000000000000000000), -14.25);
+
+    // Check that NaNs roundtrip their bits regardless of signaling-ness
+    // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits
+    let masked_nan1 = f128::NAN.to_bits() ^ NAN_MASK1;
+    let masked_nan2 = f128::NAN.to_bits() ^ NAN_MASK2;
+    assert!(f128::from_bits(masked_nan1).is_nan());
+    assert!(f128::from_bits(masked_nan2).is_nan());
+
+    assert_eq!(f128::from_bits(masked_nan1).to_bits(), masked_nan1);
+    assert_eq!(f128::from_bits(masked_nan2).to_bits(), masked_nan2);
+}
+
+#[test]
+#[should_panic]
+fn test_clamp_min_greater_than_max() {
+    let _ = 1.0f128.clamp(3.0, 1.0);
+}
+
+#[test]
+#[should_panic]
+fn test_clamp_min_is_nan() {
+    let _ = 1.0f128.clamp(f128::NAN, 1.0);
+}
+
+#[test]
+#[should_panic]
+fn test_clamp_max_is_nan() {
+    let _ = 1.0f128.clamp(3.0, f128::NAN);
+}
+
+#[test]
+fn test_total_cmp() {
+    use core::cmp::Ordering;
+
+    fn quiet_bit_mask() -> u128 {
+        1 << (f128::MANTISSA_DIGITS - 2)
+    }
+
+    // FIXME(f16_f128): test subnormals when powf is available
+    // fn min_subnorm() -> f128 {
+    //     f128::MIN_POSITIVE / f128::powf(2.0, f128::MANTISSA_DIGITS as f128 - 1.0)
+    // }
+
+    // fn max_subnorm() -> f128 {
+    //     f128::MIN_POSITIVE - min_subnorm()
+    // }
+
+    fn q_nan() -> f128 {
+        f128::from_bits(f128::NAN.to_bits() | quiet_bit_mask())
+    }
+
+    fn s_nan() -> f128 {
+        f128::from_bits((f128::NAN.to_bits() & !quiet_bit_mask()) + 42)
+    }
+
+    assert_eq!(Ordering::Equal, (-q_nan()).total_cmp(&-q_nan()));
+    assert_eq!(Ordering::Equal, (-s_nan()).total_cmp(&-s_nan()));
+    assert_eq!(Ordering::Equal, (-f128::INFINITY).total_cmp(&-f128::INFINITY));
+    assert_eq!(Ordering::Equal, (-f128::MAX).total_cmp(&-f128::MAX));
+    assert_eq!(Ordering::Equal, (-2.5_f128).total_cmp(&-2.5));
+    assert_eq!(Ordering::Equal, (-1.0_f128).total_cmp(&-1.0));
+    assert_eq!(Ordering::Equal, (-1.5_f128).total_cmp(&-1.5));
+    assert_eq!(Ordering::Equal, (-0.5_f128).total_cmp(&-0.5));
+    assert_eq!(Ordering::Equal, (-f128::MIN_POSITIVE).total_cmp(&-f128::MIN_POSITIVE));
+    // assert_eq!(Ordering::Equal, (-max_subnorm()).total_cmp(&-max_subnorm()));
+    // assert_eq!(Ordering::Equal, (-min_subnorm()).total_cmp(&-min_subnorm()));
+    assert_eq!(Ordering::Equal, (-0.0_f128).total_cmp(&-0.0));
+    assert_eq!(Ordering::Equal, 0.0_f128.total_cmp(&0.0));
+    // assert_eq!(Ordering::Equal, min_subnorm().total_cmp(&min_subnorm()));
+    // assert_eq!(Ordering::Equal, max_subnorm().total_cmp(&max_subnorm()));
+    assert_eq!(Ordering::Equal, f128::MIN_POSITIVE.total_cmp(&f128::MIN_POSITIVE));
+    assert_eq!(Ordering::Equal, 0.5_f128.total_cmp(&0.5));
+    assert_eq!(Ordering::Equal, 1.0_f128.total_cmp(&1.0));
+    assert_eq!(Ordering::Equal, 1.5_f128.total_cmp(&1.5));
+    assert_eq!(Ordering::Equal, 2.5_f128.total_cmp(&2.5));
+    assert_eq!(Ordering::Equal, f128::MAX.total_cmp(&f128::MAX));
+    assert_eq!(Ordering::Equal, f128::INFINITY.total_cmp(&f128::INFINITY));
+    assert_eq!(Ordering::Equal, s_nan().total_cmp(&s_nan()));
+    assert_eq!(Ordering::Equal, q_nan().total_cmp(&q_nan()));
+
+    assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan()));
+    assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f128::INFINITY));
+    assert_eq!(Ordering::Less, (-f128::INFINITY).total_cmp(&-f128::MAX));
+    assert_eq!(Ordering::Less, (-f128::MAX).total_cmp(&-2.5));
+    assert_eq!(Ordering::Less, (-2.5_f128).total_cmp(&-1.5));
+    assert_eq!(Ordering::Less, (-1.5_f128).total_cmp(&-1.0));
+    assert_eq!(Ordering::Less, (-1.0_f128).total_cmp(&-0.5));
+    assert_eq!(Ordering::Less, (-0.5_f128).total_cmp(&-f128::MIN_POSITIVE));
+    // assert_eq!(Ordering::Less, (-f128::MIN_POSITIVE).total_cmp(&-max_subnorm()));
+    // assert_eq!(Ordering::Less, (-max_subnorm()).total_cmp(&-min_subnorm()));
+    // assert_eq!(Ordering::Less, (-min_subnorm()).total_cmp(&-0.0));
+    assert_eq!(Ordering::Less, (-0.0_f128).total_cmp(&0.0));
+    // assert_eq!(Ordering::Less, 0.0_f128.total_cmp(&min_subnorm()));
+    // assert_eq!(Ordering::Less, min_subnorm().total_cmp(&max_subnorm()));
+    // assert_eq!(Ordering::Less, max_subnorm().total_cmp(&f128::MIN_POSITIVE));
+    assert_eq!(Ordering::Less, f128::MIN_POSITIVE.total_cmp(&0.5));
+    assert_eq!(Ordering::Less, 0.5_f128.total_cmp(&1.0));
+    assert_eq!(Ordering::Less, 1.0_f128.total_cmp(&1.5));
+    assert_eq!(Ordering::Less, 1.5_f128.total_cmp(&2.5));
+    assert_eq!(Ordering::Less, 2.5_f128.total_cmp(&f128::MAX));
+    assert_eq!(Ordering::Less, f128::MAX.total_cmp(&f128::INFINITY));
+    assert_eq!(Ordering::Less, f128::INFINITY.total_cmp(&s_nan()));
+    assert_eq!(Ordering::Less, s_nan().total_cmp(&q_nan()));
+
+    assert_eq!(Ordering::Greater, (-s_nan()).total_cmp(&-q_nan()));
+    assert_eq!(Ordering::Greater, (-f128::INFINITY).total_cmp(&-s_nan()));
+    assert_eq!(Ordering::Greater, (-f128::MAX).total_cmp(&-f128::INFINITY));
+    assert_eq!(Ordering::Greater, (-2.5_f128).total_cmp(&-f128::MAX));
+    assert_eq!(Ordering::Greater, (-1.5_f128).total_cmp(&-2.5));
+    assert_eq!(Ordering::Greater, (-1.0_f128).total_cmp(&-1.5));
+    assert_eq!(Ordering::Greater, (-0.5_f128).total_cmp(&-1.0));
+    assert_eq!(Ordering::Greater, (-f128::MIN_POSITIVE).total_cmp(&-0.5));
+    // assert_eq!(Ordering::Greater, (-max_subnorm()).total_cmp(&-f128::MIN_POSITIVE));
+    // assert_eq!(Ordering::Greater, (-min_subnorm()).total_cmp(&-max_subnorm()));
+    // assert_eq!(Ordering::Greater, (-0.0_f128).total_cmp(&-min_subnorm()));
+    assert_eq!(Ordering::Greater, 0.0_f128.total_cmp(&-0.0));
+    // assert_eq!(Ordering::Greater, min_subnorm().total_cmp(&0.0));
+    // assert_eq!(Ordering::Greater, max_subnorm().total_cmp(&min_subnorm()));
+    // assert_eq!(Ordering::Greater, f128::MIN_POSITIVE.total_cmp(&max_subnorm()));
+    assert_eq!(Ordering::Greater, 0.5_f128.total_cmp(&f128::MIN_POSITIVE));
+    assert_eq!(Ordering::Greater, 1.0_f128.total_cmp(&0.5));
+    assert_eq!(Ordering::Greater, 1.5_f128.total_cmp(&1.0));
+    assert_eq!(Ordering::Greater, 2.5_f128.total_cmp(&1.5));
+    assert_eq!(Ordering::Greater, f128::MAX.total_cmp(&2.5));
+    assert_eq!(Ordering::Greater, f128::INFINITY.total_cmp(&f128::MAX));
+    assert_eq!(Ordering::Greater, s_nan().total_cmp(&f128::INFINITY));
+    assert_eq!(Ordering::Greater, q_nan().total_cmp(&s_nan()));
+
+    assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan()));
+    assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f128::INFINITY));
+    assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f128::MAX));
+    assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-2.5));
+    assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.5));
+    assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.0));
+    assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.5));
+    assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f128::MIN_POSITIVE));
+    // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-max_subnorm()));
+    // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-min_subnorm()));
+    assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.0));
+    assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.0));
+    // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&min_subnorm()));
+    // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&max_subnorm()));
+    assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f128::MIN_POSITIVE));
+    assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.5));
+    assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.0));
+    assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.5));
+    assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&2.5));
+    assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f128::MAX));
+    assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f128::INFINITY));
+    assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&s_nan()));
+
+    assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f128::INFINITY));
+    assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f128::MAX));
+    assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-2.5));
+    assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.5));
+    assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.0));
+    assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.5));
+    assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f128::MIN_POSITIVE));
+    // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-max_subnorm()));
+    // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-min_subnorm()));
+    assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.0));
+    assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.0));
+    // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&min_subnorm()));
+    // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&max_subnorm()));
+    assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f128::MIN_POSITIVE));
+    assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.5));
+    assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.0));
+    assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.5));
+    assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&2.5));
+    assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f128::MAX));
+    assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f128::INFINITY));
+    assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&s_nan()));
+}
diff --git a/library/std/src/f16.rs b/library/std/src/f16.rs
index 1cb655ffabd..d4851862299 100644
--- a/library/std/src/f16.rs
+++ b/library/std/src/f16.rs
@@ -32,4 +32,33 @@ impl f16 {
     pub fn powi(self, n: i32) -> f16 {
         unsafe { intrinsics::powif16(self, n) }
     }
+
+    /// Computes the absolute value of `self`.
+    ///
+    /// This function always returns the precise result.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(f16)]
+    /// # #[cfg(reliable_f16)] {
+    ///
+    /// let x = 3.5_f16;
+    /// let y = -3.5_f16;
+    ///
+    /// assert_eq!(x.abs(), x);
+    /// assert_eq!(y.abs(), -y);
+    ///
+    /// assert!(f16::NAN.abs().is_nan());
+    /// # }
+    /// ```
+    #[inline]
+    #[cfg(not(bootstrap))]
+    #[rustc_allow_incoherent_impl]
+    #[unstable(feature = "f16", issue = "116909")]
+    #[must_use = "method returns a new number and does not mutate the original value"]
+    pub fn abs(self) -> Self {
+        // FIXME(f16_f128): replace with `intrinsics::fabsf16` when available
+        Self::from_bits(self.to_bits() & !(1 << 15))
+    }
 }
diff --git a/library/std/src/f16/tests.rs b/library/std/src/f16/tests.rs
index d65c43eca4b..bb6a811529e 100644
--- a/library/std/src/f16/tests.rs
+++ b/library/std/src/f16/tests.rs
@@ -1,35 +1,37 @@
-#![allow(dead_code)] // FIXME(f16_f128): remove once constants are used
+#![cfg(not(bootstrap))]
+// FIXME(f16_f128): only tested on platforms that have symbols and aren't buggy
+#![cfg(reliable_f16)]
+
+use crate::f16::consts;
+use crate::num::*;
 
 // We run out of precision pretty quickly with f16
-const F16_APPROX_L1: f16 = 0.001;
+// const F16_APPROX_L1: f16 = 0.001;
 const F16_APPROX_L2: f16 = 0.01;
-const F16_APPROX_L3: f16 = 0.1;
+// const F16_APPROX_L3: f16 = 0.1;
 const F16_APPROX_L4: f16 = 0.5;
 
 /// Smallest number
 const TINY_BITS: u16 = 0x1;
+
 /// Next smallest number
 const TINY_UP_BITS: u16 = 0x2;
+
 /// Exponent = 0b11...10, Sifnificand 0b1111..10. Min val > 0
 const MAX_DOWN_BITS: u16 = 0x7bfe;
+
 /// Zeroed exponent, full significant
 const LARGEST_SUBNORMAL_BITS: u16 = 0x03ff;
+
 /// Exponent = 0b1, zeroed significand
 const SMALLEST_NORMAL_BITS: u16 = 0x0400;
+
 /// First pattern over the mantissa
 const NAN_MASK1: u16 = 0x02aa;
+
 /// Second pattern over the mantissa
 const NAN_MASK2: u16 = 0x0155;
 
-/// Compare by value
-#[allow(unused_macros)]
-macro_rules! assert_f16_eq {
-    ($a:expr, $b:expr) => {
-        let (l, r): (&f16, &f16) = (&$a, &$b);
-        assert_eq!(*l, *r, "\na: {:#018x}\nb: {:#018x}", l.to_bits(), r.to_bits())
-    };
-}
-
 /// Compare by representation
 #[allow(unused_macros)]
 macro_rules! assert_f16_biteq {
@@ -37,10 +39,500 @@ macro_rules! assert_f16_biteq {
         let (l, r): (&f16, &f16) = (&$a, &$b);
         let lb = l.to_bits();
         let rb = r.to_bits();
-        assert_eq!(
-            lb, rb,
-            "float {:?} is not bitequal to {:?}.\na: {:#018x}\nb: {:#018x}",
-            *l, *r, lb, rb
-        );
+        assert_eq!(lb, rb, "float {l:?} ({lb:#04x}) is not bitequal to {r:?} ({rb:#04x})");
     };
 }
+
+#[test]
+fn test_num_f16() {
+    test_num(10f16, 2f16);
+}
+
+// FIXME(f16_f128): add min and max tests when available
+
+#[test]
+fn test_nan() {
+    let nan: f16 = f16::NAN;
+    assert!(nan.is_nan());
+    assert!(!nan.is_infinite());
+    assert!(!nan.is_finite());
+    assert!(nan.is_sign_positive());
+    assert!(!nan.is_sign_negative());
+    // FIXME(f16_f128): classify
+    // assert!(!nan.is_normal());
+    // assert_eq!(Fp::Nan, nan.classify());
+}
+
+#[test]
+fn test_infinity() {
+    let inf: f16 = f16::INFINITY;
+    assert!(inf.is_infinite());
+    assert!(!inf.is_finite());
+    assert!(inf.is_sign_positive());
+    assert!(!inf.is_sign_negative());
+    assert!(!inf.is_nan());
+    // FIXME(f16_f128): classify
+    // assert!(!inf.is_normal());
+    // assert_eq!(Fp::Infinite, inf.classify());
+}
+
+#[test]
+fn test_neg_infinity() {
+    let neg_inf: f16 = f16::NEG_INFINITY;
+    assert!(neg_inf.is_infinite());
+    assert!(!neg_inf.is_finite());
+    assert!(!neg_inf.is_sign_positive());
+    assert!(neg_inf.is_sign_negative());
+    assert!(!neg_inf.is_nan());
+    // FIXME(f16_f128): classify
+    // assert!(!neg_inf.is_normal());
+    // assert_eq!(Fp::Infinite, neg_inf.classify());
+}
+
+#[test]
+fn test_zero() {
+    let zero: f16 = 0.0f16;
+    assert_eq!(0.0, zero);
+    assert!(!zero.is_infinite());
+    assert!(zero.is_finite());
+    assert!(zero.is_sign_positive());
+    assert!(!zero.is_sign_negative());
+    assert!(!zero.is_nan());
+    // FIXME(f16_f128): classify
+    // assert!(!zero.is_normal());
+    // assert_eq!(Fp::Zero, zero.classify());
+}
+
+#[test]
+fn test_neg_zero() {
+    let neg_zero: f16 = -0.0;
+    assert_eq!(0.0, neg_zero);
+    assert!(!neg_zero.is_infinite());
+    assert!(neg_zero.is_finite());
+    assert!(!neg_zero.is_sign_positive());
+    assert!(neg_zero.is_sign_negative());
+    assert!(!neg_zero.is_nan());
+    // FIXME(f16_f128): classify
+    // assert!(!neg_zero.is_normal());
+    // assert_eq!(Fp::Zero, neg_zero.classify());
+}
+
+#[test]
+fn test_one() {
+    let one: f16 = 1.0f16;
+    assert_eq!(1.0, one);
+    assert!(!one.is_infinite());
+    assert!(one.is_finite());
+    assert!(one.is_sign_positive());
+    assert!(!one.is_sign_negative());
+    assert!(!one.is_nan());
+    // FIXME(f16_f128): classify
+    // assert!(one.is_normal());
+    // assert_eq!(Fp::Normal, one.classify());
+}
+
+#[test]
+fn test_is_nan() {
+    let nan: f16 = f16::NAN;
+    let inf: f16 = f16::INFINITY;
+    let neg_inf: f16 = f16::NEG_INFINITY;
+    assert!(nan.is_nan());
+    assert!(!0.0f16.is_nan());
+    assert!(!5.3f16.is_nan());
+    assert!(!(-10.732f16).is_nan());
+    assert!(!inf.is_nan());
+    assert!(!neg_inf.is_nan());
+}
+
+#[test]
+fn test_is_infinite() {
+    let nan: f16 = f16::NAN;
+    let inf: f16 = f16::INFINITY;
+    let neg_inf: f16 = f16::NEG_INFINITY;
+    assert!(!nan.is_infinite());
+    assert!(inf.is_infinite());
+    assert!(neg_inf.is_infinite());
+    assert!(!0.0f16.is_infinite());
+    assert!(!42.8f16.is_infinite());
+    assert!(!(-109.2f16).is_infinite());
+}
+
+#[test]
+fn test_is_finite() {
+    let nan: f16 = f16::NAN;
+    let inf: f16 = f16::INFINITY;
+    let neg_inf: f16 = f16::NEG_INFINITY;
+    assert!(!nan.is_finite());
+    assert!(!inf.is_finite());
+    assert!(!neg_inf.is_finite());
+    assert!(0.0f16.is_finite());
+    assert!(42.8f16.is_finite());
+    assert!((-109.2f16).is_finite());
+}
+
+// FIXME(f16_f128): add `test_is_normal` and `test_classify` when classify is working
+// FIXME(f16_f128): add missing math functions when available
+
+#[test]
+fn test_abs() {
+    assert_eq!(f16::INFINITY.abs(), f16::INFINITY);
+    assert_eq!(1f16.abs(), 1f16);
+    assert_eq!(0f16.abs(), 0f16);
+    assert_eq!((-0f16).abs(), 0f16);
+    assert_eq!((-1f16).abs(), 1f16);
+    assert_eq!(f16::NEG_INFINITY.abs(), f16::INFINITY);
+    assert_eq!((1f16 / f16::NEG_INFINITY).abs(), 0f16);
+    assert!(f16::NAN.abs().is_nan());
+}
+
+#[test]
+fn test_is_sign_positive() {
+    assert!(f16::INFINITY.is_sign_positive());
+    assert!(1f16.is_sign_positive());
+    assert!(0f16.is_sign_positive());
+    assert!(!(-0f16).is_sign_positive());
+    assert!(!(-1f16).is_sign_positive());
+    assert!(!f16::NEG_INFINITY.is_sign_positive());
+    assert!(!(1f16 / f16::NEG_INFINITY).is_sign_positive());
+    assert!(f16::NAN.is_sign_positive());
+    assert!(!(-f16::NAN).is_sign_positive());
+}
+
+#[test]
+fn test_is_sign_negative() {
+    assert!(!f16::INFINITY.is_sign_negative());
+    assert!(!1f16.is_sign_negative());
+    assert!(!0f16.is_sign_negative());
+    assert!((-0f16).is_sign_negative());
+    assert!((-1f16).is_sign_negative());
+    assert!(f16::NEG_INFINITY.is_sign_negative());
+    assert!((1f16 / f16::NEG_INFINITY).is_sign_negative());
+    assert!(!f16::NAN.is_sign_negative());
+    assert!((-f16::NAN).is_sign_negative());
+}
+
+#[test]
+fn test_next_up() {
+    let tiny = f16::from_bits(TINY_BITS);
+    let tiny_up = f16::from_bits(TINY_UP_BITS);
+    let max_down = f16::from_bits(MAX_DOWN_BITS);
+    let largest_subnormal = f16::from_bits(LARGEST_SUBNORMAL_BITS);
+    let smallest_normal = f16::from_bits(SMALLEST_NORMAL_BITS);
+    assert_f16_biteq!(f16::NEG_INFINITY.next_up(), f16::MIN);
+    assert_f16_biteq!(f16::MIN.next_up(), -max_down);
+    assert_f16_biteq!((-1.0 - f16::EPSILON).next_up(), -1.0);
+    assert_f16_biteq!((-smallest_normal).next_up(), -largest_subnormal);
+    assert_f16_biteq!((-tiny_up).next_up(), -tiny);
+    assert_f16_biteq!((-tiny).next_up(), -0.0f16);
+    assert_f16_biteq!((-0.0f16).next_up(), tiny);
+    assert_f16_biteq!(0.0f16.next_up(), tiny);
+    assert_f16_biteq!(tiny.next_up(), tiny_up);
+    assert_f16_biteq!(largest_subnormal.next_up(), smallest_normal);
+    assert_f16_biteq!(1.0f16.next_up(), 1.0 + f16::EPSILON);
+    assert_f16_biteq!(f16::MAX.next_up(), f16::INFINITY);
+    assert_f16_biteq!(f16::INFINITY.next_up(), f16::INFINITY);
+
+    // Check that NaNs roundtrip.
+    let nan0 = f16::NAN;
+    let nan1 = f16::from_bits(f16::NAN.to_bits() ^ NAN_MASK1);
+    let nan2 = f16::from_bits(f16::NAN.to_bits() ^ NAN_MASK2);
+    assert_f16_biteq!(nan0.next_up(), nan0);
+    assert_f16_biteq!(nan1.next_up(), nan1);
+    assert_f16_biteq!(nan2.next_up(), nan2);
+}
+
+#[test]
+fn test_next_down() {
+    let tiny = f16::from_bits(TINY_BITS);
+    let tiny_up = f16::from_bits(TINY_UP_BITS);
+    let max_down = f16::from_bits(MAX_DOWN_BITS);
+    let largest_subnormal = f16::from_bits(LARGEST_SUBNORMAL_BITS);
+    let smallest_normal = f16::from_bits(SMALLEST_NORMAL_BITS);
+    assert_f16_biteq!(f16::NEG_INFINITY.next_down(), f16::NEG_INFINITY);
+    assert_f16_biteq!(f16::MIN.next_down(), f16::NEG_INFINITY);
+    assert_f16_biteq!((-max_down).next_down(), f16::MIN);
+    assert_f16_biteq!((-1.0f16).next_down(), -1.0 - f16::EPSILON);
+    assert_f16_biteq!((-largest_subnormal).next_down(), -smallest_normal);
+    assert_f16_biteq!((-tiny).next_down(), -tiny_up);
+    assert_f16_biteq!((-0.0f16).next_down(), -tiny);
+    assert_f16_biteq!((0.0f16).next_down(), -tiny);
+    assert_f16_biteq!(tiny.next_down(), 0.0f16);
+    assert_f16_biteq!(tiny_up.next_down(), tiny);
+    assert_f16_biteq!(smallest_normal.next_down(), largest_subnormal);
+    assert_f16_biteq!((1.0 + f16::EPSILON).next_down(), 1.0f16);
+    assert_f16_biteq!(f16::MAX.next_down(), max_down);
+    assert_f16_biteq!(f16::INFINITY.next_down(), f16::MAX);
+
+    // Check that NaNs roundtrip.
+    let nan0 = f16::NAN;
+    let nan1 = f16::from_bits(f16::NAN.to_bits() ^ NAN_MASK1);
+    let nan2 = f16::from_bits(f16::NAN.to_bits() ^ NAN_MASK2);
+    assert_f16_biteq!(nan0.next_down(), nan0);
+    assert_f16_biteq!(nan1.next_down(), nan1);
+    assert_f16_biteq!(nan2.next_down(), nan2);
+}
+
+#[test]
+fn test_recip() {
+    let nan: f16 = f16::NAN;
+    let inf: f16 = f16::INFINITY;
+    let neg_inf: f16 = f16::NEG_INFINITY;
+    assert_eq!(1.0f16.recip(), 1.0);
+    assert_eq!(2.0f16.recip(), 0.5);
+    assert_eq!((-0.4f16).recip(), -2.5);
+    assert_eq!(0.0f16.recip(), inf);
+    assert!(nan.recip().is_nan());
+    assert_eq!(inf.recip(), 0.0);
+    assert_eq!(neg_inf.recip(), 0.0);
+}
+
+#[test]
+fn test_to_degrees() {
+    let pi: f16 = consts::PI;
+    let nan: f16 = f16::NAN;
+    let inf: f16 = f16::INFINITY;
+    let neg_inf: f16 = f16::NEG_INFINITY;
+    assert_eq!(0.0f16.to_degrees(), 0.0);
+    assert_approx_eq!((-5.8f16).to_degrees(), -332.315521);
+    assert_approx_eq!(pi.to_degrees(), 180.0, F16_APPROX_L4);
+    assert!(nan.to_degrees().is_nan());
+    assert_eq!(inf.to_degrees(), inf);
+    assert_eq!(neg_inf.to_degrees(), neg_inf);
+    assert_eq!(1_f16.to_degrees(), 57.2957795130823208767981548141051703);
+}
+
+#[test]
+fn test_to_radians() {
+    let pi: f16 = consts::PI;
+    let nan: f16 = f16::NAN;
+    let inf: f16 = f16::INFINITY;
+    let neg_inf: f16 = f16::NEG_INFINITY;
+    assert_eq!(0.0f16.to_radians(), 0.0);
+    assert_approx_eq!(154.6f16.to_radians(), 2.698279);
+    assert_approx_eq!((-332.31f16).to_radians(), -5.799903);
+    assert_approx_eq!(180.0f16.to_radians(), pi, F16_APPROX_L2);
+    assert!(nan.to_radians().is_nan());
+    assert_eq!(inf.to_radians(), inf);
+    assert_eq!(neg_inf.to_radians(), neg_inf);
+}
+
+#[test]
+fn test_real_consts() {
+    // FIXME(f16_f128): add math tests when available
+    use super::consts;
+
+    let pi: f16 = consts::PI;
+    let frac_pi_2: f16 = consts::FRAC_PI_2;
+    let frac_pi_3: f16 = consts::FRAC_PI_3;
+    let frac_pi_4: f16 = consts::FRAC_PI_4;
+    let frac_pi_6: f16 = consts::FRAC_PI_6;
+    let frac_pi_8: f16 = consts::FRAC_PI_8;
+    let frac_1_pi: f16 = consts::FRAC_1_PI;
+    let frac_2_pi: f16 = consts::FRAC_2_PI;
+    // let frac_2_sqrtpi: f16 = consts::FRAC_2_SQRT_PI;
+    // let sqrt2: f16 = consts::SQRT_2;
+    // let frac_1_sqrt2: f16 = consts::FRAC_1_SQRT_2;
+    // let e: f16 = consts::E;
+    // let log2_e: f16 = consts::LOG2_E;
+    // let log10_e: f16 = consts::LOG10_E;
+    // let ln_2: f16 = consts::LN_2;
+    // let ln_10: f16 = consts::LN_10;
+
+    assert_approx_eq!(frac_pi_2, pi / 2f16);
+    assert_approx_eq!(frac_pi_3, pi / 3f16);
+    assert_approx_eq!(frac_pi_4, pi / 4f16);
+    assert_approx_eq!(frac_pi_6, pi / 6f16);
+    assert_approx_eq!(frac_pi_8, pi / 8f16);
+    assert_approx_eq!(frac_1_pi, 1f16 / pi);
+    assert_approx_eq!(frac_2_pi, 2f16 / pi);
+    // assert_approx_eq!(frac_2_sqrtpi, 2f16 / pi.sqrt());
+    // assert_approx_eq!(sqrt2, 2f16.sqrt());
+    // assert_approx_eq!(frac_1_sqrt2, 1f16 / 2f16.sqrt());
+    // assert_approx_eq!(log2_e, e.log2());
+    // assert_approx_eq!(log10_e, e.log10());
+    // assert_approx_eq!(ln_2, 2f16.ln());
+    // assert_approx_eq!(ln_10, 10f16.ln());
+}
+
+#[test]
+fn test_float_bits_conv() {
+    assert_eq!((1f16).to_bits(), 0x3c00);
+    assert_eq!((12.5f16).to_bits(), 0x4a40);
+    assert_eq!((1337f16).to_bits(), 0x6539);
+    assert_eq!((-14.25f16).to_bits(), 0xcb20);
+    assert_approx_eq!(f16::from_bits(0x3c00), 1.0);
+    assert_approx_eq!(f16::from_bits(0x4a40), 12.5);
+    assert_approx_eq!(f16::from_bits(0x6539), 1337.0);
+    assert_approx_eq!(f16::from_bits(0xcb20), -14.25);
+
+    // Check that NaNs roundtrip their bits regardless of signaling-ness
+    let masked_nan1 = f16::NAN.to_bits() ^ NAN_MASK1;
+    let masked_nan2 = f16::NAN.to_bits() ^ NAN_MASK2;
+    assert!(f16::from_bits(masked_nan1).is_nan());
+    assert!(f16::from_bits(masked_nan2).is_nan());
+
+    assert_eq!(f16::from_bits(masked_nan1).to_bits(), masked_nan1);
+    assert_eq!(f16::from_bits(masked_nan2).to_bits(), masked_nan2);
+}
+
+#[test]
+#[should_panic]
+fn test_clamp_min_greater_than_max() {
+    let _ = 1.0f16.clamp(3.0, 1.0);
+}
+
+#[test]
+#[should_panic]
+fn test_clamp_min_is_nan() {
+    let _ = 1.0f16.clamp(f16::NAN, 1.0);
+}
+
+#[test]
+#[should_panic]
+fn test_clamp_max_is_nan() {
+    let _ = 1.0f16.clamp(3.0, f16::NAN);
+}
+
+#[test]
+fn test_total_cmp() {
+    use core::cmp::Ordering;
+
+    fn quiet_bit_mask() -> u16 {
+        1 << (f16::MANTISSA_DIGITS - 2)
+    }
+
+    // FIXME(f16_f128): test subnormals when powf is available
+    // fn min_subnorm() -> f16 {
+    //     f16::MIN_POSITIVE / f16::powf(2.0, f16::MANTISSA_DIGITS as f16 - 1.0)
+    // }
+
+    // fn max_subnorm() -> f16 {
+    //     f16::MIN_POSITIVE - min_subnorm()
+    // }
+
+    fn q_nan() -> f16 {
+        f16::from_bits(f16::NAN.to_bits() | quiet_bit_mask())
+    }
+
+    fn s_nan() -> f16 {
+        f16::from_bits((f16::NAN.to_bits() & !quiet_bit_mask()) + 42)
+    }
+
+    assert_eq!(Ordering::Equal, (-q_nan()).total_cmp(&-q_nan()));
+    assert_eq!(Ordering::Equal, (-s_nan()).total_cmp(&-s_nan()));
+    assert_eq!(Ordering::Equal, (-f16::INFINITY).total_cmp(&-f16::INFINITY));
+    assert_eq!(Ordering::Equal, (-f16::MAX).total_cmp(&-f16::MAX));
+    assert_eq!(Ordering::Equal, (-2.5_f16).total_cmp(&-2.5));
+    assert_eq!(Ordering::Equal, (-1.0_f16).total_cmp(&-1.0));
+    assert_eq!(Ordering::Equal, (-1.5_f16).total_cmp(&-1.5));
+    assert_eq!(Ordering::Equal, (-0.5_f16).total_cmp(&-0.5));
+    assert_eq!(Ordering::Equal, (-f16::MIN_POSITIVE).total_cmp(&-f16::MIN_POSITIVE));
+    // assert_eq!(Ordering::Equal, (-max_subnorm()).total_cmp(&-max_subnorm()));
+    // assert_eq!(Ordering::Equal, (-min_subnorm()).total_cmp(&-min_subnorm()));
+    assert_eq!(Ordering::Equal, (-0.0_f16).total_cmp(&-0.0));
+    assert_eq!(Ordering::Equal, 0.0_f16.total_cmp(&0.0));
+    // assert_eq!(Ordering::Equal, min_subnorm().total_cmp(&min_subnorm()));
+    // assert_eq!(Ordering::Equal, max_subnorm().total_cmp(&max_subnorm()));
+    assert_eq!(Ordering::Equal, f16::MIN_POSITIVE.total_cmp(&f16::MIN_POSITIVE));
+    assert_eq!(Ordering::Equal, 0.5_f16.total_cmp(&0.5));
+    assert_eq!(Ordering::Equal, 1.0_f16.total_cmp(&1.0));
+    assert_eq!(Ordering::Equal, 1.5_f16.total_cmp(&1.5));
+    assert_eq!(Ordering::Equal, 2.5_f16.total_cmp(&2.5));
+    assert_eq!(Ordering::Equal, f16::MAX.total_cmp(&f16::MAX));
+    assert_eq!(Ordering::Equal, f16::INFINITY.total_cmp(&f16::INFINITY));
+    assert_eq!(Ordering::Equal, s_nan().total_cmp(&s_nan()));
+    assert_eq!(Ordering::Equal, q_nan().total_cmp(&q_nan()));
+
+    assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan()));
+    assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f16::INFINITY));
+    assert_eq!(Ordering::Less, (-f16::INFINITY).total_cmp(&-f16::MAX));
+    assert_eq!(Ordering::Less, (-f16::MAX).total_cmp(&-2.5));
+    assert_eq!(Ordering::Less, (-2.5_f16).total_cmp(&-1.5));
+    assert_eq!(Ordering::Less, (-1.5_f16).total_cmp(&-1.0));
+    assert_eq!(Ordering::Less, (-1.0_f16).total_cmp(&-0.5));
+    assert_eq!(Ordering::Less, (-0.5_f16).total_cmp(&-f16::MIN_POSITIVE));
+    // assert_eq!(Ordering::Less, (-f16::MIN_POSITIVE).total_cmp(&-max_subnorm()));
+    // assert_eq!(Ordering::Less, (-max_subnorm()).total_cmp(&-min_subnorm()));
+    // assert_eq!(Ordering::Less, (-min_subnorm()).total_cmp(&-0.0));
+    assert_eq!(Ordering::Less, (-0.0_f16).total_cmp(&0.0));
+    // assert_eq!(Ordering::Less, 0.0_f16.total_cmp(&min_subnorm()));
+    // assert_eq!(Ordering::Less, min_subnorm().total_cmp(&max_subnorm()));
+    // assert_eq!(Ordering::Less, max_subnorm().total_cmp(&f16::MIN_POSITIVE));
+    assert_eq!(Ordering::Less, f16::MIN_POSITIVE.total_cmp(&0.5));
+    assert_eq!(Ordering::Less, 0.5_f16.total_cmp(&1.0));
+    assert_eq!(Ordering::Less, 1.0_f16.total_cmp(&1.5));
+    assert_eq!(Ordering::Less, 1.5_f16.total_cmp(&2.5));
+    assert_eq!(Ordering::Less, 2.5_f16.total_cmp(&f16::MAX));
+    assert_eq!(Ordering::Less, f16::MAX.total_cmp(&f16::INFINITY));
+    assert_eq!(Ordering::Less, f16::INFINITY.total_cmp(&s_nan()));
+    assert_eq!(Ordering::Less, s_nan().total_cmp(&q_nan()));
+
+    assert_eq!(Ordering::Greater, (-s_nan()).total_cmp(&-q_nan()));
+    assert_eq!(Ordering::Greater, (-f16::INFINITY).total_cmp(&-s_nan()));
+    assert_eq!(Ordering::Greater, (-f16::MAX).total_cmp(&-f16::INFINITY));
+    assert_eq!(Ordering::Greater, (-2.5_f16).total_cmp(&-f16::MAX));
+    assert_eq!(Ordering::Greater, (-1.5_f16).total_cmp(&-2.5));
+    assert_eq!(Ordering::Greater, (-1.0_f16).total_cmp(&-1.5));
+    assert_eq!(Ordering::Greater, (-0.5_f16).total_cmp(&-1.0));
+    assert_eq!(Ordering::Greater, (-f16::MIN_POSITIVE).total_cmp(&-0.5));
+    // assert_eq!(Ordering::Greater, (-max_subnorm()).total_cmp(&-f16::MIN_POSITIVE));
+    // assert_eq!(Ordering::Greater, (-min_subnorm()).total_cmp(&-max_subnorm()));
+    // assert_eq!(Ordering::Greater, (-0.0_f16).total_cmp(&-min_subnorm()));
+    assert_eq!(Ordering::Greater, 0.0_f16.total_cmp(&-0.0));
+    // assert_eq!(Ordering::Greater, min_subnorm().total_cmp(&0.0));
+    // assert_eq!(Ordering::Greater, max_subnorm().total_cmp(&min_subnorm()));
+    // assert_eq!(Ordering::Greater, f16::MIN_POSITIVE.total_cmp(&max_subnorm()));
+    assert_eq!(Ordering::Greater, 0.5_f16.total_cmp(&f16::MIN_POSITIVE));
+    assert_eq!(Ordering::Greater, 1.0_f16.total_cmp(&0.5));
+    assert_eq!(Ordering::Greater, 1.5_f16.total_cmp(&1.0));
+    assert_eq!(Ordering::Greater, 2.5_f16.total_cmp(&1.5));
+    assert_eq!(Ordering::Greater, f16::MAX.total_cmp(&2.5));
+    assert_eq!(Ordering::Greater, f16::INFINITY.total_cmp(&f16::MAX));
+    assert_eq!(Ordering::Greater, s_nan().total_cmp(&f16::INFINITY));
+    assert_eq!(Ordering::Greater, q_nan().total_cmp(&s_nan()));
+
+    assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan()));
+    assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f16::INFINITY));
+    assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f16::MAX));
+    assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-2.5));
+    assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.5));
+    assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.0));
+    assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.5));
+    assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f16::MIN_POSITIVE));
+    // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-max_subnorm()));
+    // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-min_subnorm()));
+    assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.0));
+    assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.0));
+    // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&min_subnorm()));
+    // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&max_subnorm()));
+    assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f16::MIN_POSITIVE));
+    assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.5));
+    assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.0));
+    assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.5));
+    assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&2.5));
+    assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f16::MAX));
+    assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f16::INFINITY));
+    assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&s_nan()));
+
+    assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f16::INFINITY));
+    assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f16::MAX));
+    assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-2.5));
+    assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.5));
+    assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.0));
+    assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.5));
+    assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f16::MIN_POSITIVE));
+    // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-max_subnorm()));
+    // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-min_subnorm()));
+    assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.0));
+    assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.0));
+    // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&min_subnorm()));
+    // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&max_subnorm()));
+    assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f16::MIN_POSITIVE));
+    assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.5));
+    assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.0));
+    assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.5));
+    assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&2.5));
+    assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f16::MAX));
+    assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f16::INFINITY));
+    assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&s_nan()));
+}
diff --git a/library/std/src/f32/tests.rs b/library/std/src/f32/tests.rs
index 9ca4e8f2f45..63e65698374 100644
--- a/library/std/src/f32/tests.rs
+++ b/library/std/src/f32/tests.rs
@@ -2,6 +2,45 @@ use crate::f32::consts;
 use crate::num::FpCategory as Fp;
 use crate::num::*;
 
+/// Smallest number
+#[allow(dead_code)] // unused on x86
+const TINY_BITS: u32 = 0x1;
+
+/// Next smallest number
+#[allow(dead_code)] // unused on x86
+const TINY_UP_BITS: u32 = 0x2;
+
+/// Exponent = 0b11...10, Sifnificand 0b1111..10. Min val > 0
+#[allow(dead_code)] // unused on x86
+const MAX_DOWN_BITS: u32 = 0x7f7f_fffe;
+
+/// Zeroed exponent, full significant
+#[allow(dead_code)] // unused on x86
+const LARGEST_SUBNORMAL_BITS: u32 = 0x007f_ffff;
+
+/// Exponent = 0b1, zeroed significand
+#[allow(dead_code)] // unused on x86
+const SMALLEST_NORMAL_BITS: u32 = 0x0080_0000;
+
+/// First pattern over the mantissa
+#[allow(dead_code)] // unused on x86
+const NAN_MASK1: u32 = 0x002a_aaaa;
+
+/// Second pattern over the mantissa
+#[allow(dead_code)] // unused on x86
+const NAN_MASK2: u32 = 0x0055_5555;
+
+#[allow(unused_macros)]
+macro_rules! assert_f32_biteq {
+    ($left : expr, $right : expr) => {
+        let l: &f32 = &$left;
+        let r: &f32 = &$right;
+        let lb = l.to_bits();
+        let rb = r.to_bits();
+        assert_eq!(lb, rb, "float {l} ({lb:#010x}) is not bitequal to {r} ({rb:#010x})");
+    };
+}
+
 #[test]
 fn test_num_f32() {
     test_num(10f32, 2f32);
@@ -315,27 +354,16 @@ fn test_is_sign_negative() {
     assert!((-f32::NAN).is_sign_negative());
 }
 
-#[allow(unused_macros)]
-macro_rules! assert_f32_biteq {
-    ($left : expr, $right : expr) => {
-        let l: &f32 = &$left;
-        let r: &f32 = &$right;
-        let lb = l.to_bits();
-        let rb = r.to_bits();
-        assert_eq!(lb, rb, "float {} ({:#x}) is not equal to {} ({:#x})", *l, lb, *r, rb);
-    };
-}
-
 // Ignore test on x87 floating point, these platforms do not guarantee NaN
 // payloads are preserved and flush denormals to zero, failing the tests.
 #[cfg(not(target_arch = "x86"))]
 #[test]
 fn test_next_up() {
-    let tiny = f32::from_bits(1);
-    let tiny_up = f32::from_bits(2);
-    let max_down = f32::from_bits(0x7f7f_fffe);
-    let largest_subnormal = f32::from_bits(0x007f_ffff);
-    let smallest_normal = f32::from_bits(0x0080_0000);
+    let tiny = f32::from_bits(TINY_BITS);
+    let tiny_up = f32::from_bits(TINY_UP_BITS);
+    let max_down = f32::from_bits(MAX_DOWN_BITS);
+    let largest_subnormal = f32::from_bits(LARGEST_SUBNORMAL_BITS);
+    let smallest_normal = f32::from_bits(SMALLEST_NORMAL_BITS);
     assert_f32_biteq!(f32::NEG_INFINITY.next_up(), f32::MIN);
     assert_f32_biteq!(f32::MIN.next_up(), -max_down);
     assert_f32_biteq!((-1.0 - f32::EPSILON).next_up(), -1.0);
@@ -352,8 +380,8 @@ fn test_next_up() {
 
     // Check that NaNs roundtrip.
     let nan0 = f32::NAN;
-    let nan1 = f32::from_bits(f32::NAN.to_bits() ^ 0x002a_aaaa);
-    let nan2 = f32::from_bits(f32::NAN.to_bits() ^ 0x0055_5555);
+    let nan1 = f32::from_bits(f32::NAN.to_bits() ^ NAN_MASK1);
+    let nan2 = f32::from_bits(f32::NAN.to_bits() ^ NAN_MASK2);
     assert_f32_biteq!(nan0.next_up(), nan0);
     assert_f32_biteq!(nan1.next_up(), nan1);
     assert_f32_biteq!(nan2.next_up(), nan2);
@@ -364,11 +392,11 @@ fn test_next_up() {
 #[cfg(not(target_arch = "x86"))]
 #[test]
 fn test_next_down() {
-    let tiny = f32::from_bits(1);
-    let tiny_up = f32::from_bits(2);
-    let max_down = f32::from_bits(0x7f7f_fffe);
-    let largest_subnormal = f32::from_bits(0x007f_ffff);
-    let smallest_normal = f32::from_bits(0x0080_0000);
+    let tiny = f32::from_bits(TINY_BITS);
+    let tiny_up = f32::from_bits(TINY_UP_BITS);
+    let max_down = f32::from_bits(MAX_DOWN_BITS);
+    let largest_subnormal = f32::from_bits(LARGEST_SUBNORMAL_BITS);
+    let smallest_normal = f32::from_bits(SMALLEST_NORMAL_BITS);
     assert_f32_biteq!(f32::NEG_INFINITY.next_down(), f32::NEG_INFINITY);
     assert_f32_biteq!(f32::MIN.next_down(), f32::NEG_INFINITY);
     assert_f32_biteq!((-max_down).next_down(), f32::MIN);
@@ -386,8 +414,8 @@ fn test_next_down() {
 
     // Check that NaNs roundtrip.
     let nan0 = f32::NAN;
-    let nan1 = f32::from_bits(f32::NAN.to_bits() ^ 0x002a_aaaa);
-    let nan2 = f32::from_bits(f32::NAN.to_bits() ^ 0x0055_5555);
+    let nan1 = f32::from_bits(f32::NAN.to_bits() ^ NAN_MASK1);
+    let nan2 = f32::from_bits(f32::NAN.to_bits() ^ NAN_MASK2);
     assert_f32_biteq!(nan0.next_down(), nan0);
     assert_f32_biteq!(nan1.next_down(), nan1);
     assert_f32_biteq!(nan2.next_down(), nan2);
@@ -734,8 +762,8 @@ fn test_float_bits_conv() {
 
     // Check that NaNs roundtrip their bits regardless of signaling-ness
     // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits
-    let masked_nan1 = f32::NAN.to_bits() ^ 0x002A_AAAA;
-    let masked_nan2 = f32::NAN.to_bits() ^ 0x0055_5555;
+    let masked_nan1 = f32::NAN.to_bits() ^ NAN_MASK1;
+    let masked_nan2 = f32::NAN.to_bits() ^ NAN_MASK2;
     assert!(f32::from_bits(masked_nan1).is_nan());
     assert!(f32::from_bits(masked_nan2).is_nan());
 
diff --git a/library/std/src/f64/tests.rs b/library/std/src/f64/tests.rs
index f88d01593b5..d9e17fd601d 100644
--- a/library/std/src/f64/tests.rs
+++ b/library/std/src/f64/tests.rs
@@ -2,6 +2,45 @@ use crate::f64::consts;
 use crate::num::FpCategory as Fp;
 use crate::num::*;
 
+/// Smallest number
+#[allow(dead_code)] // unused on x86
+const TINY_BITS: u64 = 0x1;
+
+/// Next smallest number
+#[allow(dead_code)] // unused on x86
+const TINY_UP_BITS: u64 = 0x2;
+
+/// Exponent = 0b11...10, Sifnificand 0b1111..10. Min val > 0
+#[allow(dead_code)] // unused on x86
+const MAX_DOWN_BITS: u64 = 0x7fef_ffff_ffff_fffe;
+
+/// Zeroed exponent, full significant
+#[allow(dead_code)] // unused on x86
+const LARGEST_SUBNORMAL_BITS: u64 = 0x000f_ffff_ffff_ffff;
+
+/// Exponent = 0b1, zeroed significand
+#[allow(dead_code)] // unused on x86
+const SMALLEST_NORMAL_BITS: u64 = 0x0010_0000_0000_0000;
+
+/// First pattern over the mantissa
+#[allow(dead_code)] // unused on x86
+const NAN_MASK1: u64 = 0x000a_aaaa_aaaa_aaaa;
+
+/// Second pattern over the mantissa
+#[allow(dead_code)] // unused on x86
+const NAN_MASK2: u64 = 0x0005_5555_5555_5555;
+
+#[allow(unused_macros)]
+macro_rules! assert_f64_biteq {
+    ($left : expr, $right : expr) => {
+        let l: &f64 = &$left;
+        let r: &f64 = &$right;
+        let lb = l.to_bits();
+        let rb = r.to_bits();
+        assert_eq!(lb, rb, "float {l} ({lb:#018x}) is not bitequal to {r} ({rb:#018x})");
+    };
+}
+
 #[test]
 fn test_num_f64() {
     test_num(10f64, 2f64);
@@ -305,27 +344,16 @@ fn test_is_sign_negative() {
     assert!((-f64::NAN).is_sign_negative());
 }
 
-#[allow(unused_macros)]
-macro_rules! assert_f64_biteq {
-    ($left : expr, $right : expr) => {
-        let l: &f64 = &$left;
-        let r: &f64 = &$right;
-        let lb = l.to_bits();
-        let rb = r.to_bits();
-        assert_eq!(lb, rb, "float {} ({:#x}) is not equal to {} ({:#x})", *l, lb, *r, rb);
-    };
-}
-
 // Ignore test on x87 floating point, these platforms do not guarantee NaN
 // payloads are preserved and flush denormals to zero, failing the tests.
 #[cfg(not(target_arch = "x86"))]
 #[test]
 fn test_next_up() {
-    let tiny = f64::from_bits(1);
-    let tiny_up = f64::from_bits(2);
-    let max_down = f64::from_bits(0x7fef_ffff_ffff_fffe);
-    let largest_subnormal = f64::from_bits(0x000f_ffff_ffff_ffff);
-    let smallest_normal = f64::from_bits(0x0010_0000_0000_0000);
+    let tiny = f64::from_bits(TINY_BITS);
+    let tiny_up = f64::from_bits(TINY_UP_BITS);
+    let max_down = f64::from_bits(MAX_DOWN_BITS);
+    let largest_subnormal = f64::from_bits(LARGEST_SUBNORMAL_BITS);
+    let smallest_normal = f64::from_bits(SMALLEST_NORMAL_BITS);
     assert_f64_biteq!(f64::NEG_INFINITY.next_up(), f64::MIN);
     assert_f64_biteq!(f64::MIN.next_up(), -max_down);
     assert_f64_biteq!((-1.0 - f64::EPSILON).next_up(), -1.0);
@@ -341,8 +369,8 @@ fn test_next_up() {
     assert_f64_biteq!(f64::INFINITY.next_up(), f64::INFINITY);
 
     let nan0 = f64::NAN;
-    let nan1 = f64::from_bits(f64::NAN.to_bits() ^ 0x000a_aaaa_aaaa_aaaa);
-    let nan2 = f64::from_bits(f64::NAN.to_bits() ^ 0x0005_5555_5555_5555);
+    let nan1 = f64::from_bits(f64::NAN.to_bits() ^ NAN_MASK1);
+    let nan2 = f64::from_bits(f64::NAN.to_bits() ^ NAN_MASK2);
     assert_f64_biteq!(nan0.next_up(), nan0);
     assert_f64_biteq!(nan1.next_up(), nan1);
     assert_f64_biteq!(nan2.next_up(), nan2);
@@ -353,11 +381,11 @@ fn test_next_up() {
 #[cfg(not(target_arch = "x86"))]
 #[test]
 fn test_next_down() {
-    let tiny = f64::from_bits(1);
-    let tiny_up = f64::from_bits(2);
-    let max_down = f64::from_bits(0x7fef_ffff_ffff_fffe);
-    let largest_subnormal = f64::from_bits(0x000f_ffff_ffff_ffff);
-    let smallest_normal = f64::from_bits(0x0010_0000_0000_0000);
+    let tiny = f64::from_bits(TINY_BITS);
+    let tiny_up = f64::from_bits(TINY_UP_BITS);
+    let max_down = f64::from_bits(MAX_DOWN_BITS);
+    let largest_subnormal = f64::from_bits(LARGEST_SUBNORMAL_BITS);
+    let smallest_normal = f64::from_bits(SMALLEST_NORMAL_BITS);
     assert_f64_biteq!(f64::NEG_INFINITY.next_down(), f64::NEG_INFINITY);
     assert_f64_biteq!(f64::MIN.next_down(), f64::NEG_INFINITY);
     assert_f64_biteq!((-max_down).next_down(), f64::MIN);
@@ -374,8 +402,8 @@ fn test_next_down() {
     assert_f64_biteq!(f64::INFINITY.next_down(), f64::MAX);
 
     let nan0 = f64::NAN;
-    let nan1 = f64::from_bits(f64::NAN.to_bits() ^ 0x000a_aaaa_aaaa_aaaa);
-    let nan2 = f64::from_bits(f64::NAN.to_bits() ^ 0x0005_5555_5555_5555);
+    let nan1 = f64::from_bits(f64::NAN.to_bits() ^ NAN_MASK1);
+    let nan2 = f64::from_bits(f64::NAN.to_bits() ^ NAN_MASK2);
     assert_f64_biteq!(nan0.next_down(), nan0);
     assert_f64_biteq!(nan1.next_down(), nan1);
     assert_f64_biteq!(nan2.next_down(), nan2);
@@ -715,9 +743,8 @@ fn test_float_bits_conv() {
     assert_approx_eq!(f64::from_bits(0xc02c800000000000), -14.25);
 
     // Check that NaNs roundtrip their bits regardless of signaling-ness
-    // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits
-    let masked_nan1 = f64::NAN.to_bits() ^ 0x000A_AAAA_AAAA_AAAA;
-    let masked_nan2 = f64::NAN.to_bits() ^ 0x0005_5555_5555_5555;
+    let masked_nan1 = f64::NAN.to_bits() ^ NAN_MASK1;
+    let masked_nan2 = f64::NAN.to_bits() ^ NAN_MASK2;
     assert!(f64::from_bits(masked_nan1).is_nan());
     assert!(f64::from_bits(masked_nan2).is_nan());
 
diff --git a/library/std/src/ffi/os_str.rs b/library/std/src/ffi/os_str.rs
index f6b9de26c1c..4a417c84a30 100644
--- a/library/std/src/ffi/os_str.rs
+++ b/library/std/src/ffi/os_str.rs
@@ -552,10 +552,20 @@ impl OsString {
         OsStr::from_inner_mut(self.inner.leak())
     }
 
-    /// Part of a hack to make PathBuf::push/pop more efficient.
+    /// Provides plumbing to core `Vec::truncate`.
+    /// More well behaving alternative to allowing outer types
+    /// full mutable access to the core `Vec`.
     #[inline]
-    pub(crate) fn as_mut_vec_for_path_buf(&mut self) -> &mut Vec<u8> {
-        self.inner.as_mut_vec_for_path_buf()
+    pub(crate) fn truncate(&mut self, len: usize) {
+        self.inner.truncate(len);
+    }
+
+    /// Provides plumbing to core `Vec::extend_from_slice`.
+    /// More well behaving alternative to allowing outer types
+    /// full mutable access to the core `Vec`.
+    #[inline]
+    pub(crate) fn extend_from_slice(&mut self, other: &[u8]) {
+        self.inner.extend_from_slice(other);
     }
 }
 
diff --git a/library/std/src/macros.rs b/library/std/src/macros.rs
index 58df83bd79d..972b6015932 100644
--- a/library/std/src/macros.rs
+++ b/library/std/src/macros.rs
@@ -373,10 +373,17 @@ macro_rules! dbg {
     };
 }
 
+/// Verify that floats are within a tolerance of each other, 1.0e-6 by default.
 #[cfg(test)]
 macro_rules! assert_approx_eq {
-    ($a:expr, $b:expr) => {{
+    ($a:expr, $b:expr) => {{ assert_approx_eq!($a, $b, 1.0e-6) }};
+    ($a:expr, $b:expr, $lim:expr) => {{
         let (a, b) = (&$a, &$b);
-        assert!((*a - *b).abs() < 1.0e-6, "{} is not approximately equal to {}", *a, *b);
+        let diff = (*a - *b).abs();
+        assert!(
+            diff < $lim,
+            "{a:?} is not approximately equal to {b:?} (threshold {lim:?}, actual {diff:?})",
+            lim = $lim
+        );
     }};
 }
diff --git a/library/std/src/path.rs b/library/std/src/path.rs
index 72073d13280..caae8f924d2 100644
--- a/library/std/src/path.rs
+++ b/library/std/src/path.rs
@@ -1163,11 +1163,6 @@ pub struct PathBuf {
 }
 
 impl PathBuf {
-    #[inline]
-    fn as_mut_vec(&mut self) -> &mut Vec<u8> {
-        self.inner.as_mut_vec_for_path_buf()
-    }
-
     /// Allocates an empty `PathBuf`.
     ///
     /// # Examples
@@ -1290,7 +1285,8 @@ impl PathBuf {
 
     fn _push(&mut self, path: &Path) {
         // in general, a separator is needed if the rightmost byte is not a separator
-        let mut need_sep = self.as_mut_vec().last().map(|c| !is_sep_byte(*c)).unwrap_or(false);
+        let buf = self.inner.as_encoded_bytes();
+        let mut need_sep = buf.last().map(|c| !is_sep_byte(*c)).unwrap_or(false);
 
         // in the special case of `C:` on Windows, do *not* add a separator
         let comps = self.components();
@@ -1304,7 +1300,7 @@ impl PathBuf {
 
         // absolute `path` replaces `self`
         if path.is_absolute() || path.prefix().is_some() {
-            self.as_mut_vec().truncate(0);
+            self.inner.truncate(0);
 
         // verbatim paths need . and .. removed
         } else if comps.prefix_verbatim() && !path.inner.is_empty() {
@@ -1349,7 +1345,7 @@ impl PathBuf {
         // `path` has a root but no prefix, e.g., `\windows` (Windows only)
         } else if path.has_root() {
             let prefix_len = self.components().prefix_remaining();
-            self.as_mut_vec().truncate(prefix_len);
+            self.inner.truncate(prefix_len);
 
         // `path` is a pure relative path
         } else if need_sep {
@@ -1382,7 +1378,7 @@ impl PathBuf {
     pub fn pop(&mut self) -> bool {
         match self.parent().map(|p| p.as_u8_slice().len()) {
             Some(len) => {
-                self.as_mut_vec().truncate(len);
+                self.inner.truncate(len);
                 true
             }
             None => false,
@@ -1510,15 +1506,14 @@ impl PathBuf {
         // truncate until right after the file stem
         let end_file_stem = file_stem[file_stem.len()..].as_ptr().addr();
         let start = self.inner.as_encoded_bytes().as_ptr().addr();
-        let v = self.as_mut_vec();
-        v.truncate(end_file_stem.wrapping_sub(start));
+        self.inner.truncate(end_file_stem.wrapping_sub(start));
 
         // add the new extension, if any
-        let new = extension.as_encoded_bytes();
+        let new = extension;
         if !new.is_empty() {
-            v.reserve_exact(new.len() + 1);
-            v.push(b'.');
-            v.extend_from_slice(new);
+            self.inner.reserve_exact(new.len() + 1);
+            self.inner.push(OsStr::new("."));
+            self.inner.push(new);
         }
 
         true
@@ -2645,18 +2640,18 @@ impl Path {
             None => {
                 // Enough capacity for the extension and the dot
                 let capacity = self_len + extension.len() + 1;
-                let whole_path = self_bytes.iter();
+                let whole_path = self_bytes;
                 (capacity, whole_path)
             }
             Some(previous_extension) => {
                 let capacity = self_len + extension.len() - previous_extension.len();
-                let path_till_dot = self_bytes[..self_len - previous_extension.len()].iter();
+                let path_till_dot = &self_bytes[..self_len - previous_extension.len()];
                 (capacity, path_till_dot)
             }
         };
 
         let mut new_path = PathBuf::with_capacity(new_capacity);
-        new_path.as_mut_vec().extend(slice_to_copy);
+        new_path.inner.extend_from_slice(slice_to_copy);
         new_path.set_extension(extension);
         new_path
     }
diff --git a/library/std/src/sys/os_str/bytes.rs b/library/std/src/sys/os_str/bytes.rs
index f7c6b0877aa..2a7477e3afc 100644
--- a/library/std/src/sys/os_str/bytes.rs
+++ b/library/std/src/sys/os_str/bytes.rs
@@ -202,10 +202,20 @@ impl Buf {
         self.as_slice().into_rc()
     }
 
-    /// Part of a hack to make PathBuf::push/pop more efficient.
+    /// Provides plumbing to core `Vec::truncate`.
+    /// More well behaving alternative to allowing outer types
+    /// full mutable access to the core `Vec`.
     #[inline]
-    pub(crate) fn as_mut_vec_for_path_buf(&mut self) -> &mut Vec<u8> {
-        &mut self.inner
+    pub(crate) fn truncate(&mut self, len: usize) {
+        self.inner.truncate(len);
+    }
+
+    /// Provides plumbing to core `Vec::extend_from_slice`.
+    /// More well behaving alternative to allowing outer types
+    /// full mutable access to the core `Vec`.
+    #[inline]
+    pub(crate) fn extend_from_slice(&mut self, other: &[u8]) {
+        self.inner.extend_from_slice(other);
     }
 }
 
diff --git a/library/std/src/sys/os_str/wtf8.rs b/library/std/src/sys/os_str/wtf8.rs
index 96690f8c44e..edb923a4750 100644
--- a/library/std/src/sys/os_str/wtf8.rs
+++ b/library/std/src/sys/os_str/wtf8.rs
@@ -165,10 +165,20 @@ impl Buf {
         self.as_slice().into_rc()
     }
 
-    /// Part of a hack to make PathBuf::push/pop more efficient.
+    /// Provides plumbing to core `Vec::truncate`.
+    /// More well behaving alternative to allowing outer types
+    /// full mutable access to the core `Vec`.
     #[inline]
-    pub(crate) fn as_mut_vec_for_path_buf(&mut self) -> &mut Vec<u8> {
-        self.inner.as_mut_vec_for_path_buf()
+    pub(crate) fn truncate(&mut self, len: usize) {
+        self.inner.truncate(len);
+    }
+
+    /// Provides plumbing to core `Vec::extend_from_slice`.
+    /// More well behaving alternative to allowing outer types
+    /// full mutable access to the core `Vec`.
+    #[inline]
+    pub(crate) fn extend_from_slice(&mut self, other: &[u8]) {
+        self.inner.extend_from_slice(other);
     }
 }
 
diff --git a/library/std/src/sys_common/wtf8.rs b/library/std/src/sys_common/wtf8.rs
index 84128a4b595..708f62f476e 100644
--- a/library/std/src/sys_common/wtf8.rs
+++ b/library/std/src/sys_common/wtf8.rs
@@ -474,13 +474,13 @@ impl Wtf8Buf {
         Wtf8Buf { bytes: bytes.into_vec(), is_known_utf8: false }
     }
 
-    /// Part of a hack to make PathBuf::push/pop more efficient.
+    /// Provides plumbing to core `Vec::extend_from_slice`.
+    /// More well behaving alternative to allowing outer types
+    /// full mutable access to the core `Vec`.
     #[inline]
-    pub(crate) fn as_mut_vec_for_path_buf(&mut self) -> &mut Vec<u8> {
-        // FIXME: this function should not even exist, as it implies violating Wtf8Buf invariants
-        // For now, simply assume that is about to happen.
-        self.is_known_utf8 = false;
-        &mut self.bytes
+    pub(crate) fn extend_from_slice(&mut self, other: &[u8]) {
+        self.bytes.extend_from_slice(other);
+        self.is_known_utf8 = self.is_known_utf8 || self.next_surrogate(0).is_none();
     }
 }
 
diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs
index 850c8bfe2f8..7411d0ba2be 100644
--- a/src/bootstrap/src/core/build_steps/tool.rs
+++ b/src/bootstrap/src/core/build_steps/tool.rs
@@ -211,6 +211,13 @@ pub fn prepare_tool_cargo(
     // See https://github.com/rust-lang/rust/issues/116538
     cargo.rustflag("-Zunstable-options");
 
+    // `-Zon-broken-pipe=kill` breaks cargo tests
+    if !path.ends_with("cargo") {
+        // If the output is piped to e.g. `head -n1` we want the process to be killed,
+        // rather than having an error bubble up and cause a panic.
+        cargo.rustflag("-Zon-broken-pipe=kill");
+    }
+
     cargo
 }
 
@@ -575,7 +582,8 @@ impl Step for Rustdoc {
             features.push("jemalloc".to_string());
         }
 
-        let mut cargo = prepare_tool_cargo(
+        // NOTE: Never modify the rustflags here, it breaks the build cache for other tools!
+        let cargo = prepare_tool_cargo(
             builder,
             build_compiler,
             Mode::ToolRustc,
@@ -586,11 +594,6 @@ impl Step for Rustdoc {
             features.as_slice(),
         );
 
-        // If the rustdoc output is piped to e.g. `head -n1` we want the process
-        // to be killed, rather than having an error bubble up and cause a
-        // panic.
-        cargo.rustflag("-Zon-broken-pipe=kill");
-
         let _guard = builder.msg_tool(
             Kind::Build,
             Mode::ToolRustc,
diff --git a/src/ci/docker/host-x86_64/disabled/riscv64gc-gnu/Dockerfile b/src/ci/docker/host-x86_64/disabled/riscv64gc-gnu/Dockerfile
index a9ffa5918b5..a52c3839196 100644
--- a/src/ci/docker/host-x86_64/disabled/riscv64gc-gnu/Dockerfile
+++ b/src/ci/docker/host-x86_64/disabled/riscv64gc-gnu/Dockerfile
@@ -91,7 +91,9 @@ RUN sh /scripts/sccache.sh
 # Avoid "fatal: detected dubious ownership in repository at '/checkout'" error
 RUN git config --global --add safe.directory /checkout
 
-ENV RUST_CONFIGURE_ARGS --qemu-riscv64-rootfs=/tmp/rootfs
+ENV RUST_CONFIGURE_ARGS \
+    --qemu-riscv64-rootfs=/tmp/rootfs \
+    --set target.riscv64gc-unknown-linux-gnu.linker=riscv64-linux-gnu-gcc
 ENV SCRIPT python3 ../x.py --stage 2 test --host='' --target riscv64gc-unknown-linux-gnu
 
 ENV NO_CHANGE_USER=1
diff --git a/src/tools/cargo b/src/tools/cargo
-Subproject bc89bffa5987d4af8f71011c7557119b39e44a6
+Subproject 4ed7bee47f7dd4416b36fada1909e9a62c54624
diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs
index d60320d8282..f451758c335 100644
--- a/src/tools/clippy/clippy_lints/src/dereference.rs
+++ b/src/tools/clippy/clippy_lints/src/dereference.rs
@@ -6,7 +6,7 @@ use clippy_utils::{
     expr_use_ctxt, get_parent_expr, is_block_like, is_lint_allowed, path_to_local, DefinedTy, ExprUseNode,
 };
 use core::mem;
-use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX};
+use rustc_ast::util::parser::{PREC_UNAMBIGUOUS, PREC_PREFIX};
 use rustc_data_structures::fx::FxIndexMap;
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::{walk_ty, Visitor};
@@ -1013,7 +1013,7 @@ fn report<'tcx>(
                     let (precedence, calls_field) = match cx.tcx.parent_hir_node(data.first_expr.hir_id) {
                         Node::Expr(e) => match e.kind {
                             ExprKind::Call(callee, _) if callee.hir_id != data.first_expr.hir_id => (0, false),
-                            ExprKind::Call(..) => (PREC_POSTFIX, matches!(expr.kind, ExprKind::Field(..))),
+                            ExprKind::Call(..) => (PREC_UNAMBIGUOUS, matches!(expr.kind, ExprKind::Field(..))),
                             _ => (e.precedence().order(), false),
                         },
                         _ => (0, false),
@@ -1160,7 +1160,7 @@ impl<'tcx> Dereferencing<'tcx> {
                         },
                         Some(parent) if !parent.span.from_expansion() => {
                             // Double reference might be needed at this point.
-                            if parent.precedence().order() == PREC_POSTFIX {
+                            if parent.precedence().order() == PREC_UNAMBIGUOUS {
                                 // Parentheses would be needed here, don't lint.
                                 *outer_pat = None;
                             } else {
diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs b/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs
index 183caab56c5..be80aebed6d 100644
--- a/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs
@@ -7,7 +7,7 @@ use clippy_utils::{
     can_move_expr_to_closure, is_else_clause, is_lint_allowed, is_res_lang_ctor, path_res, path_to_local_id,
     peel_blocks, peel_hir_expr_refs, peel_hir_expr_while, CaptureKind,
 };
-use rustc_ast::util::parser::PREC_POSTFIX;
+use rustc_ast::util::parser::PREC_UNAMBIGUOUS;
 use rustc_errors::Applicability;
 use rustc_hir::def::Res;
 use rustc_hir::LangItem::{OptionNone, OptionSome};
@@ -117,7 +117,7 @@ where
     // it's being passed by value.
     let scrutinee = peel_hir_expr_refs(scrutinee).0;
     let (scrutinee_str, _) = snippet_with_context(cx, scrutinee.span, expr_ctxt, "..", &mut app);
-    let scrutinee_str = if scrutinee.span.eq_ctxt(expr.span) && scrutinee.precedence().order() < PREC_POSTFIX {
+    let scrutinee_str = if scrutinee.span.eq_ctxt(expr.span) && scrutinee.precedence().order() < PREC_UNAMBIGUOUS {
         format!("({scrutinee_str})")
     } else {
         scrutinee_str.into()
diff --git a/src/tools/miri/tests/pass/tls/win_tls_callback.rs b/src/tools/miri/tests/pass/tls/win_tls_callback.rs
new file mode 100644
index 00000000000..99a8de29e91
--- /dev/null
+++ b/src/tools/miri/tests/pass/tls/win_tls_callback.rs
@@ -0,0 +1,16 @@
+//! Ensure that we call Windows TLS callbacks in the local crate.
+//@only-target-windows
+// Calling eprintln in the callback seems to (re-)initialize some thread-local storage
+// and then leak the memory allocated for that. Let's just ignore these leaks,
+// that's not what this test is about.
+//@compile-flags: -Zmiri-ignore-leaks
+
+#[link_section = ".CRT$XLB"]
+#[used] // Miri only considers explicitly `#[used]` statics for `lookup_link_section`
+pub static CALLBACK: unsafe extern "system" fn(*const (), u32, *const ()) = tls_callback;
+
+unsafe extern "system" fn tls_callback(_h: *const (), _dw_reason: u32, _pv: *const ()) {
+    eprintln!("in tls_callback");
+}
+
+fn main() {}
diff --git a/src/tools/miri/tests/pass/tls/win_tls_callback.stderr b/src/tools/miri/tests/pass/tls/win_tls_callback.stderr
new file mode 100644
index 00000000000..84795589544
--- /dev/null
+++ b/src/tools/miri/tests/pass/tls/win_tls_callback.stderr
@@ -0,0 +1 @@
+in tls_callback
diff --git a/src/tools/tidy/config/requirements.in b/src/tools/tidy/config/requirements.in
index 047617c6559..833d62a864b 100644
--- a/src/tools/tidy/config/requirements.in
+++ b/src/tools/tidy/config/requirements.in
@@ -6,5 +6,5 @@
 # Note: this generation step should be run with the oldest supported python
 # version (currently 3.9) to ensure backward compatibility
 
-black==23.3.0
-ruff==0.0.272
+black==24.4.2
+ruff==0.4.9
diff --git a/src/tools/tidy/config/requirements.txt b/src/tools/tidy/config/requirements.txt
index a53c98cac7a..d5236868ad1 100644
--- a/src/tools/tidy/config/requirements.txt
+++ b/src/tools/tidy/config/requirements.txt
@@ -4,32 +4,29 @@
 #
 #    pip-compile --generate-hashes --strip-extras src/tools/tidy/config/requirements.in
 #
-black==23.3.0 \
-    --hash=sha256:064101748afa12ad2291c2b91c960be28b817c0c7eaa35bec09cc63aa56493c5 \
-    --hash=sha256:0945e13506be58bf7db93ee5853243eb368ace1c08a24c65ce108986eac65915 \
-    --hash=sha256:11c410f71b876f961d1de77b9699ad19f939094c3a677323f43d7a29855fe326 \
-    --hash=sha256:1c7b8d606e728a41ea1ccbd7264677e494e87cf630e399262ced92d4a8dac940 \
-    --hash=sha256:1d06691f1eb8de91cd1b322f21e3bfc9efe0c7ca1f0e1eb1db44ea367dff656b \
-    --hash=sha256:3238f2aacf827d18d26db07524e44741233ae09a584273aa059066d644ca7b30 \
-    --hash=sha256:32daa9783106c28815d05b724238e30718f34155653d4d6e125dc7daec8e260c \
-    --hash=sha256:35d1381d7a22cc5b2be2f72c7dfdae4072a3336060635718cc7e1ede24221d6c \
-    --hash=sha256:3a150542a204124ed00683f0db1f5cf1c2aaaa9cc3495b7a3b5976fb136090ab \
-    --hash=sha256:48f9d345675bb7fbc3dd85821b12487e1b9a75242028adad0333ce36ed2a6d27 \
-    --hash=sha256:50cb33cac881766a5cd9913e10ff75b1e8eb71babf4c7104f2e9c52da1fb7de2 \
-    --hash=sha256:562bd3a70495facf56814293149e51aa1be9931567474993c7942ff7d3533961 \
-    --hash=sha256:67de8d0c209eb5b330cce2469503de11bca4085880d62f1628bd9972cc3366b9 \
-    --hash=sha256:6b39abdfb402002b8a7d030ccc85cf5afff64ee90fa4c5aebc531e3ad0175ddb \
-    --hash=sha256:6f3c333ea1dd6771b2d3777482429864f8e258899f6ff05826c3a4fcc5ce3f70 \
-    --hash=sha256:714290490c18fb0126baa0fca0a54ee795f7502b44177e1ce7624ba1c00f2331 \
-    --hash=sha256:7c3eb7cea23904399866c55826b31c1f55bbcd3890ce22ff70466b907b6775c2 \
-    --hash=sha256:92c543f6854c28a3c7f39f4d9b7694f9a6eb9d3c5e2ece488c327b6e7ea9b266 \
-    --hash=sha256:a6f6886c9869d4daae2d1715ce34a19bbc4b95006d20ed785ca00fa03cba312d \
-    --hash=sha256:a8a968125d0a6a404842fa1bf0b349a568634f856aa08ffaff40ae0dfa52e7c6 \
-    --hash=sha256:c7ab5790333c448903c4b721b59c0d80b11fe5e9803d8703e84dcb8da56fec1b \
-    --hash=sha256:e114420bf26b90d4b9daa597351337762b63039752bdf72bf361364c1aa05925 \
-    --hash=sha256:e198cf27888ad6f4ff331ca1c48ffc038848ea9f031a3b40ba36aced7e22f2c8 \
-    --hash=sha256:ec751418022185b0c1bb7d7736e6933d40bbb14c14a0abcf9123d1b159f98dd4 \
-    --hash=sha256:f0bd2f4a58d6666500542b26354978218a9babcdc972722f4bf90779524515f3
+black==24.4.2 \
+    --hash=sha256:257d724c2c9b1660f353b36c802ccece186a30accc7742c176d29c146df6e474 \
+    --hash=sha256:37aae07b029fa0174d39daf02748b379399b909652a806e5708199bd93899da1 \
+    --hash=sha256:415e686e87dbbe6f4cd5ef0fbf764af7b89f9057b97c908742b6008cc554b9c0 \
+    --hash=sha256:48a85f2cb5e6799a9ef05347b476cce6c182d6c71ee36925a6c194d074336ef8 \
+    --hash=sha256:7768a0dbf16a39aa5e9a3ded568bb545c8c2727396d063bbaf847df05b08cd96 \
+    --hash=sha256:7e122b1c4fb252fd85df3ca93578732b4749d9be076593076ef4d07a0233c3e1 \
+    --hash=sha256:88c57dc656038f1ab9f92b3eb5335ee9b021412feaa46330d5eba4e51fe49b04 \
+    --hash=sha256:8e537d281831ad0e71007dcdcbe50a71470b978c453fa41ce77186bbe0ed6021 \
+    --hash=sha256:98e123f1d5cfd42f886624d84464f7756f60ff6eab89ae845210631714f6db94 \
+    --hash=sha256:accf49e151c8ed2c0cdc528691838afd217c50412534e876a19270fea1e28e2d \
+    --hash=sha256:b1530ae42e9d6d5b670a34db49a94115a64596bc77710b1d05e9801e62ca0a7c \
+    --hash=sha256:b9176b9832e84308818a99a561e90aa479e73c523b3f77afd07913380ae2eab7 \
+    --hash=sha256:bdde6f877a18f24844e381d45e9947a49e97933573ac9d4345399be37621e26c \
+    --hash=sha256:be8bef99eb46d5021bf053114442914baeb3649a89dc5f3a555c88737e5e98fc \
+    --hash=sha256:bf10f7310db693bb62692609b397e8d67257c55f949abde4c67f9cc574492cc7 \
+    --hash=sha256:c872b53057f000085da66a19c55d68f6f8ddcac2642392ad3a355878406fbd4d \
+    --hash=sha256:d36ed1124bb81b32f8614555b34cc4259c3fbc7eec17870e8ff8ded335b58d8c \
+    --hash=sha256:da33a1a5e49c4122ccdfd56cd021ff1ebc4a1ec4e2d01594fef9b6f267a9e741 \
+    --hash=sha256:dd1b5a14e417189db4c7b64a6540f31730713d173f0b63e55fabd52d61d8fdce \
+    --hash=sha256:e151054aa00bad1f4e1f04919542885f89f5f7d086b8a59e5000e6c616896ffb \
+    --hash=sha256:eaea3008c281f1038edb473c1aa8ed8143a5535ff18f978a318f10302b254063 \
+    --hash=sha256:ef703f83fc32e131e9bcc0a5094cfe85599e7109f896fe8bc96cc402f3eb4b6e
     # via -r src/tools/tidy/config/requirements.in
 click==8.1.3 \
     --hash=sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e \
@@ -47,28 +44,28 @@ pathspec==0.11.1 \
     --hash=sha256:2798de800fa92780e33acca925945e9a19a133b715067cf165b8866c15a31687 \
     --hash=sha256:d8af70af76652554bd134c22b3e8a1cc46ed7d91edcdd721ef1a0c51a84a5293
     # via black
-platformdirs==3.6.0 \
-    --hash=sha256:57e28820ca8094678b807ff529196506d7a21e17156cb1cddb3e74cebce54640 \
-    --hash=sha256:ffa199e3fbab8365778c4a10e1fbf1b9cd50707de826eb304b50e57ec0cc8d38
+platformdirs==4.2.2 \
+    --hash=sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee \
+    --hash=sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3
     # via black
-ruff==0.0.272 \
-    --hash=sha256:06b8ee4eb8711ab119db51028dd9f5384b44728c23586424fd6e241a5b9c4a3b \
-    --hash=sha256:1609b864a8d7ee75a8c07578bdea0a7db75a144404e75ef3162e0042bfdc100d \
-    --hash=sha256:19643d448f76b1eb8a764719072e9c885968971bfba872e14e7257e08bc2f2b7 \
-    --hash=sha256:273a01dc8c3c4fd4c2af7ea7a67c8d39bb09bce466e640dd170034da75d14cab \
-    --hash=sha256:27b2ea68d2aa69fff1b20b67636b1e3e22a6a39e476c880da1282c3e4bf6ee5a \
-    --hash=sha256:48eccf225615e106341a641f826b15224b8a4240b84269ead62f0afd6d7e2d95 \
-    --hash=sha256:677284430ac539bb23421a2b431b4ebc588097ef3ef918d0e0a8d8ed31fea216 \
-    --hash=sha256:691d72a00a99707a4e0b2846690961157aef7b17b6b884f6b4420a9f25cd39b5 \
-    --hash=sha256:86bc788245361a8148ff98667da938a01e1606b28a45e50ac977b09d3ad2c538 \
-    --hash=sha256:905ff8f3d6206ad56fcd70674453527b9011c8b0dc73ead27618426feff6908e \
-    --hash=sha256:9c4bfb75456a8e1efe14c52fcefb89cfb8f2a0d31ed8d804b82c6cf2dc29c42c \
-    --hash=sha256:a37ec80e238ead2969b746d7d1b6b0d31aa799498e9ba4281ab505b93e1f4b28 \
-    --hash=sha256:ae9b57546e118660175d45d264b87e9b4c19405c75b587b6e4d21e6a17bf4fdf \
-    --hash=sha256:bd2bbe337a3f84958f796c77820d55ac2db1e6753f39d1d1baed44e07f13f96d \
-    --hash=sha256:d5a208f8ef0e51d4746930589f54f9f92f84bb69a7d15b1de34ce80a7681bc00 \
-    --hash=sha256:dc406e5d756d932da95f3af082814d2467943631a587339ee65e5a4f4fbe83eb \
-    --hash=sha256:ee76b4f05fcfff37bd6ac209d1370520d509ea70b5a637bdf0a04d0c99e13dff
+ruff==0.4.9 \
+    --hash=sha256:06b60f91bfa5514bb689b500a25ba48e897d18fea14dce14b48a0c40d1635893 \
+    --hash=sha256:0e8e7b95673f22e0efd3571fb5b0cf71a5eaaa3cc8a776584f3b2cc878e46bff \
+    --hash=sha256:2d45ddc6d82e1190ea737341326ecbc9a61447ba331b0a8962869fcada758505 \
+    --hash=sha256:4555056049d46d8a381f746680db1c46e67ac3b00d714606304077682832998e \
+    --hash=sha256:5d5460f789ccf4efd43f265a58538a2c24dbce15dbf560676e430375f20a8198 \
+    --hash=sha256:673bddb893f21ab47a8334c8e0ea7fd6598ecc8e698da75bcd12a7b9d0a3206e \
+    --hash=sha256:732dd550bfa5d85af8c3c6cbc47ba5b67c6aed8a89e2f011b908fc88f87649db \
+    --hash=sha256:784d3ec9bd6493c3b720a0b76f741e6c2d7d44f6b2be87f5eef1ae8cc1d54c84 \
+    --hash=sha256:78de3fdb95c4af084087628132336772b1c5044f6e710739d440fc0bccf4d321 \
+    --hash=sha256:8064590fd1a50dcf4909c268b0e7c2498253273309ad3d97e4a752bb9df4f521 \
+    --hash=sha256:88bffe9c6a454bf8529f9ab9091c99490578a593cc9f9822b7fc065ee0712a06 \
+    --hash=sha256:8c1aff58c31948cc66d0b22951aa19edb5af0a3af40c936340cd32a8b1ab7438 \
+    --hash=sha256:98ec2775fd2d856dc405635e5ee4ff177920f2141b8e2d9eb5bd6efd50e80317 \
+    --hash=sha256:b262ed08d036ebe162123170b35703aaf9daffecb698cd367a8d585157732991 \
+    --hash=sha256:e0a22c4157e53d006530c902107c7f550b9233e9706313ab57b892d7197d8e52 \
+    --hash=sha256:e91175fbe48f8a2174c9aad70438fe9cb0a5732c4159b2a10a3565fea2d94cde \
+    --hash=sha256:f1cb0828ac9533ba0135d148d214e284711ede33640465e706772645483427e3
     # via -r src/tools/tidy/config/requirements.in
 tomli==2.0.1 \
     --hash=sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc \
diff --git a/src/tools/tidy/config/ruff.toml b/src/tools/tidy/config/ruff.toml
index cf89ffd9ac7..c87c17f7833 100644
--- a/src/tools/tidy/config/ruff.toml
+++ b/src/tools/tidy/config/ruff.toml
@@ -1,17 +1,7 @@
 # Configuration for ruff python linter, run as part of tidy external tools
 
-# B (bugbear), E (pycodestyle, standard), EXE (executables) F (flakes, standard)
-# ERM for error messages would be beneficial at some point
-select = ["B", "E", "EXE", "F"]
-
-ignore = [
-    "E501", # line-too-long
-    "F403", # undefined-local-with-import-star
-    "F405", # undefined-local-with-import-star-usage
-]
-
 # lowest possible for ruff
-target-version = "py37"
+target-version = "py39"
 
 # Ignore all submodules
 extend-exclude = [
@@ -41,3 +31,14 @@ extend-exclude = [
     "../library/backtrace/",
     "../src/tools/rustc-perf/",
 ]
+
+[lint]
+# B (bugbear), E (pycodestyle, standard), EXE (executables) F (flakes, standard)
+# ERM for error messages would be beneficial at some point
+select = ["B", "E", "EXE", "F"]
+
+ignore = [
+    "E501", # line-too-long
+    "F403", # undefined-local-with-import-star
+    "F405", # undefined-local-with-import-star-usage
+]
diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt
index 07073ef5d40..cb68589d8a4 100644
--- a/src/tools/tidy/src/allowed_run_make_makefiles.txt
+++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt
@@ -55,13 +55,11 @@ run-make/incr-foreign-head-span/Makefile
 run-make/interdependent-c-libraries/Makefile
 run-make/intrinsic-unreachable/Makefile
 run-make/invalid-library/Makefile
-run-make/invalid-so/Makefile
 run-make/issue-107094/Makefile
 run-make/issue-109934-lto-debuginfo/Makefile
 run-make/issue-14698/Makefile
 run-make/issue-15460/Makefile
 run-make/issue-18943/Makefile
-run-make/issue-20626/Makefile
 run-make/issue-22131/Makefile
 run-make/issue-25581/Makefile
 run-make/issue-26006/Makefile
@@ -97,7 +95,6 @@ run-make/long-linker-command-lines-cmd-exe/Makefile
 run-make/long-linker-command-lines/Makefile
 run-make/longjmp-across-rust/Makefile
 run-make/lto-dylib-dep/Makefile
-run-make/lto-empty/Makefile
 run-make/lto-linkage-used-attr/Makefile
 run-make/lto-no-link-whole-rlib/Makefile
 run-make/lto-smoke-c/Makefile
diff --git a/src/tools/tidy/src/ext_tool_checks.rs b/src/tools/tidy/src/ext_tool_checks.rs
index 995ad58cb62..e9dd6c66f64 100644
--- a/src/tools/tidy/src/ext_tool_checks.rs
+++ b/src/tools/tidy/src/ext_tool_checks.rs
@@ -110,12 +110,13 @@ fn check_impl(
         }
 
         let mut args = merge_args(&cfg_args_ruff, &file_args_ruff);
+        args.insert(0, "check".as_ref());
         let res = py_runner(py_path.as_ref().unwrap(), "ruff", &args);
 
         if res.is_err() && show_diff {
             eprintln!("\npython linting failed! Printing diff suggestions:");
 
-            args.insert(0, "--diff".as_ref());
+            args.insert(1, "--diff".as_ref());
             let _ = py_runner(py_path.as_ref().unwrap(), "ruff", &args);
         }
         // Rethrow error
diff --git a/tests/coverage/closure_macro.cov-map b/tests/coverage/closure_macro.cov-map
index 21fad22f58f..156947f4e21 100644
--- a/tests/coverage/closure_macro.cov-map
+++ b/tests/coverage/closure_macro.cov-map
@@ -22,19 +22,19 @@ Number of file 0 mappings: 5
 - Code(Counter(0)) at (prev + 3, 1) to (start + 0, 2)
 
 Function name: closure_macro::main::{closure#0}
-Raw bytes (35): 0x[01, 01, 03, 01, 05, 05, 0b, 09, 0d, 05, 01, 10, 1c, 03, 21, 05, 04, 11, 01, 27, 02, 03, 11, 00, 16, 0d, 00, 17, 00, 1e, 07, 02, 09, 00, 0a]
+Raw bytes (35): 0x[01, 01, 03, 01, 05, 05, 0b, 09, 00, 05, 01, 10, 1c, 03, 21, 05, 04, 11, 01, 27, 02, 03, 11, 00, 16, 00, 00, 17, 00, 1e, 07, 02, 09, 00, 0a]
 Number of files: 1
 - file 0 => global file 1
 Number of expressions: 3
 - expression 0 operands: lhs = Counter(0), rhs = Counter(1)
 - expression 1 operands: lhs = Counter(1), rhs = Expression(2, Add)
-- expression 2 operands: lhs = Counter(2), rhs = Counter(3)
+- expression 2 operands: lhs = Counter(2), rhs = Zero
 Number of file 0 mappings: 5
 - Code(Counter(0)) at (prev + 16, 28) to (start + 3, 33)
 - Code(Counter(1)) at (prev + 4, 17) to (start + 1, 39)
 - Code(Expression(0, Sub)) at (prev + 3, 17) to (start + 0, 22)
     = (c0 - c1)
-- Code(Counter(3)) at (prev + 0, 23) to (start + 0, 30)
+- Code(Zero) at (prev + 0, 23) to (start + 0, 30)
 - Code(Expression(1, Add)) at (prev + 2, 9) to (start + 0, 10)
-    = (c1 + (c2 + c3))
+    = (c1 + (c2 + Zero))
 
diff --git a/tests/coverage/closure_macro_async.cov-map b/tests/coverage/closure_macro_async.cov-map
index f2efd550591..0f2b4e01748 100644
--- a/tests/coverage/closure_macro_async.cov-map
+++ b/tests/coverage/closure_macro_async.cov-map
@@ -30,19 +30,19 @@ Number of file 0 mappings: 5
 - Code(Counter(0)) at (prev + 3, 1) to (start + 0, 2)
 
 Function name: closure_macro_async::test::{closure#0}::{closure#0}
-Raw bytes (35): 0x[01, 01, 03, 01, 05, 05, 0b, 09, 0d, 05, 01, 12, 1c, 03, 21, 05, 04, 11, 01, 27, 02, 03, 11, 00, 16, 0d, 00, 17, 00, 1e, 07, 02, 09, 00, 0a]
+Raw bytes (35): 0x[01, 01, 03, 01, 05, 05, 0b, 09, 00, 05, 01, 12, 1c, 03, 21, 05, 04, 11, 01, 27, 02, 03, 11, 00, 16, 00, 00, 17, 00, 1e, 07, 02, 09, 00, 0a]
 Number of files: 1
 - file 0 => global file 1
 Number of expressions: 3
 - expression 0 operands: lhs = Counter(0), rhs = Counter(1)
 - expression 1 operands: lhs = Counter(1), rhs = Expression(2, Add)
-- expression 2 operands: lhs = Counter(2), rhs = Counter(3)
+- expression 2 operands: lhs = Counter(2), rhs = Zero
 Number of file 0 mappings: 5
 - Code(Counter(0)) at (prev + 18, 28) to (start + 3, 33)
 - Code(Counter(1)) at (prev + 4, 17) to (start + 1, 39)
 - Code(Expression(0, Sub)) at (prev + 3, 17) to (start + 0, 22)
     = (c0 - c1)
-- Code(Counter(3)) at (prev + 0, 23) to (start + 0, 30)
+- Code(Zero) at (prev + 0, 23) to (start + 0, 30)
 - Code(Expression(1, Add)) at (prev + 2, 9) to (start + 0, 10)
-    = (c1 + (c2 + c3))
+    = (c1 + (c2 + Zero))
 
diff --git a/tests/mir-opt/gvn.cast_pointer_eq.GVN.panic-abort.diff b/tests/mir-opt/gvn.cast_pointer_eq.GVN.panic-abort.diff
new file mode 100644
index 00000000000..757ab959813
--- /dev/null
+++ b/tests/mir-opt/gvn.cast_pointer_eq.GVN.panic-abort.diff
@@ -0,0 +1,134 @@
+- // MIR for `cast_pointer_eq` before GVN
++ // MIR for `cast_pointer_eq` after GVN
+  
+  fn cast_pointer_eq(_1: *mut u8, _2: *mut u32, _3: *mut u32, _4: *mut [u32]) -> () {
+      debug p1 => _1;
+      debug p2 => _2;
+      debug p3 => _3;
+      debug p4 => _4;
+      let mut _0: ();
+      let _5: *const u32;
+      let mut _6: *mut u8;
+      let mut _8: *const u32;
+      let mut _9: *mut u32;
+      let mut _11: *const u32;
+      let mut _12: *mut u32;
+      let mut _14: *mut [u32];
+      let mut _16: *const u32;
+      let mut _17: *const u32;
+      let mut _19: *const u32;
+      let mut _20: *const u32;
+      let mut _22: *const u32;
+      let mut _23: *const u32;
+      scope 1 {
+          debug m1 => _5;
+          let _7: *const u32;
+          scope 2 {
+              debug m2 => _7;
+              let _10: *const u32;
+              scope 3 {
+                  debug m3 => _10;
+                  let _13: *const u32;
+                  scope 4 {
+                      debug m4 => _13;
+                      let _15: bool;
+                      scope 5 {
+                          debug eq_different_thing => _15;
+                          let _18: bool;
+                          scope 6 {
+                              debug eq_optimize => _18;
+                              let _21: bool;
+                              scope 7 {
+                                  debug eq_thin_fat => _21;
+                              }
+                          }
+                      }
+                  }
+              }
+          }
+      }
+  
+      bb0: {
+-         StorageLive(_5);
++         nop;
+          StorageLive(_6);
+          _6 = _1;
+-         _5 = move _6 as *const u32 (PtrToPtr);
++         _5 = _1 as *const u32 (PtrToPtr);
+          StorageDead(_6);
+          StorageLive(_7);
+-         StorageLive(_8);
++         nop;
+          StorageLive(_9);
+          _9 = _2;
+-         _8 = move _9 as *const u32 (PtrToPtr);
++         _8 = _2 as *const u32 (PtrToPtr);
+          StorageDead(_9);
+-         _7 = move _8 as *const u32 (PtrToPtr);
+-         StorageDead(_8);
++         _7 = _8;
++         nop;
+          StorageLive(_10);
+-         StorageLive(_11);
++         nop;
+          StorageLive(_12);
+          _12 = _3;
+-         _11 = move _12 as *const u32 (PtrToPtr);
++         _11 = _3 as *const u32 (PtrToPtr);
+          StorageDead(_12);
+-         _10 = move _11 as *const u32 (PtrToPtr);
+-         StorageDead(_11);
+-         StorageLive(_13);
++         _10 = _11;
++         nop;
++         nop;
+          StorageLive(_14);
+          _14 = _4;
+-         _13 = move _14 as *const u32 (PtrToPtr);
++         _13 = _4 as *const u32 (PtrToPtr);
+          StorageDead(_14);
+          StorageLive(_15);
+          StorageLive(_16);
+          _16 = _5;
+          StorageLive(_17);
+-         _17 = _7;
+-         _15 = Eq(move _16, move _17);
++         _17 = _8;
++         _15 = Eq(_5, _8);
+          StorageDead(_17);
+          StorageDead(_16);
+          StorageLive(_18);
+          StorageLive(_19);
+-         _19 = _7;
++         _19 = _8;
+          StorageLive(_20);
+-         _20 = _10;
+-         _18 = Eq(move _19, move _20);
++         _20 = _11;
++         _18 = Eq(_2, _3);
+          StorageDead(_20);
+          StorageDead(_19);
+          StorageLive(_21);
+          StorageLive(_22);
+-         _22 = _10;
++         _22 = _11;
+          StorageLive(_23);
+          _23 = _13;
+-         _21 = Eq(move _22, move _23);
++         _21 = Eq(_11, _13);
+          StorageDead(_23);
+          StorageDead(_22);
+          _0 = const ();
+          StorageDead(_21);
+          StorageDead(_18);
+          StorageDead(_15);
+-         StorageDead(_13);
++         nop;
+          StorageDead(_10);
+          StorageDead(_7);
+-         StorageDead(_5);
++         nop;
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/gvn.cast_pointer_eq.GVN.panic-unwind.diff b/tests/mir-opt/gvn.cast_pointer_eq.GVN.panic-unwind.diff
new file mode 100644
index 00000000000..757ab959813
--- /dev/null
+++ b/tests/mir-opt/gvn.cast_pointer_eq.GVN.panic-unwind.diff
@@ -0,0 +1,134 @@
+- // MIR for `cast_pointer_eq` before GVN
++ // MIR for `cast_pointer_eq` after GVN
+  
+  fn cast_pointer_eq(_1: *mut u8, _2: *mut u32, _3: *mut u32, _4: *mut [u32]) -> () {
+      debug p1 => _1;
+      debug p2 => _2;
+      debug p3 => _3;
+      debug p4 => _4;
+      let mut _0: ();
+      let _5: *const u32;
+      let mut _6: *mut u8;
+      let mut _8: *const u32;
+      let mut _9: *mut u32;
+      let mut _11: *const u32;
+      let mut _12: *mut u32;
+      let mut _14: *mut [u32];
+      let mut _16: *const u32;
+      let mut _17: *const u32;
+      let mut _19: *const u32;
+      let mut _20: *const u32;
+      let mut _22: *const u32;
+      let mut _23: *const u32;
+      scope 1 {
+          debug m1 => _5;
+          let _7: *const u32;
+          scope 2 {
+              debug m2 => _7;
+              let _10: *const u32;
+              scope 3 {
+                  debug m3 => _10;
+                  let _13: *const u32;
+                  scope 4 {
+                      debug m4 => _13;
+                      let _15: bool;
+                      scope 5 {
+                          debug eq_different_thing => _15;
+                          let _18: bool;
+                          scope 6 {
+                              debug eq_optimize => _18;
+                              let _21: bool;
+                              scope 7 {
+                                  debug eq_thin_fat => _21;
+                              }
+                          }
+                      }
+                  }
+              }
+          }
+      }
+  
+      bb0: {
+-         StorageLive(_5);
++         nop;
+          StorageLive(_6);
+          _6 = _1;
+-         _5 = move _6 as *const u32 (PtrToPtr);
++         _5 = _1 as *const u32 (PtrToPtr);
+          StorageDead(_6);
+          StorageLive(_7);
+-         StorageLive(_8);
++         nop;
+          StorageLive(_9);
+          _9 = _2;
+-         _8 = move _9 as *const u32 (PtrToPtr);
++         _8 = _2 as *const u32 (PtrToPtr);
+          StorageDead(_9);
+-         _7 = move _8 as *const u32 (PtrToPtr);
+-         StorageDead(_8);
++         _7 = _8;
++         nop;
+          StorageLive(_10);
+-         StorageLive(_11);
++         nop;
+          StorageLive(_12);
+          _12 = _3;
+-         _11 = move _12 as *const u32 (PtrToPtr);
++         _11 = _3 as *const u32 (PtrToPtr);
+          StorageDead(_12);
+-         _10 = move _11 as *const u32 (PtrToPtr);
+-         StorageDead(_11);
+-         StorageLive(_13);
++         _10 = _11;
++         nop;
++         nop;
+          StorageLive(_14);
+          _14 = _4;
+-         _13 = move _14 as *const u32 (PtrToPtr);
++         _13 = _4 as *const u32 (PtrToPtr);
+          StorageDead(_14);
+          StorageLive(_15);
+          StorageLive(_16);
+          _16 = _5;
+          StorageLive(_17);
+-         _17 = _7;
+-         _15 = Eq(move _16, move _17);
++         _17 = _8;
++         _15 = Eq(_5, _8);
+          StorageDead(_17);
+          StorageDead(_16);
+          StorageLive(_18);
+          StorageLive(_19);
+-         _19 = _7;
++         _19 = _8;
+          StorageLive(_20);
+-         _20 = _10;
+-         _18 = Eq(move _19, move _20);
++         _20 = _11;
++         _18 = Eq(_2, _3);
+          StorageDead(_20);
+          StorageDead(_19);
+          StorageLive(_21);
+          StorageLive(_22);
+-         _22 = _10;
++         _22 = _11;
+          StorageLive(_23);
+          _23 = _13;
+-         _21 = Eq(move _22, move _23);
++         _21 = Eq(_11, _13);
+          StorageDead(_23);
+          StorageDead(_22);
+          _0 = const ();
+          StorageDead(_21);
+          StorageDead(_18);
+          StorageDead(_15);
+-         StorageDead(_13);
++         nop;
+          StorageDead(_10);
+          StorageDead(_7);
+-         StorageDead(_5);
++         nop;
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/gvn.cast_pointer_then_transmute.GVN.panic-abort.diff b/tests/mir-opt/gvn.cast_pointer_then_transmute.GVN.panic-abort.diff
new file mode 100644
index 00000000000..8133f6e0b00
--- /dev/null
+++ b/tests/mir-opt/gvn.cast_pointer_then_transmute.GVN.panic-abort.diff
@@ -0,0 +1,47 @@
+- // MIR for `cast_pointer_then_transmute` before GVN
++ // MIR for `cast_pointer_then_transmute` after GVN
+  
+  fn cast_pointer_then_transmute(_1: *mut u32, _2: *mut [u8]) -> () {
+      debug thin => _1;
+      debug fat => _2;
+      let mut _0: ();
+      let _3: usize;
+      let mut _4: *const ();
+      let mut _5: *mut u32;
+      let mut _7: *const ();
+      let mut _8: *mut [u8];
+      scope 1 {
+          debug thin_addr => _3;
+          let _6: usize;
+          scope 2 {
+              debug fat_addr => _6;
+          }
+      }
+  
+      bb0: {
+          StorageLive(_3);
+          StorageLive(_4);
+          StorageLive(_5);
+          _5 = _1;
+-         _4 = move _5 as *const () (PtrToPtr);
++         _4 = _1 as *const () (PtrToPtr);
+          StorageDead(_5);
+-         _3 = move _4 as usize (Transmute);
++         _3 = _1 as usize (Transmute);
+          StorageDead(_4);
+          StorageLive(_6);
+          StorageLive(_7);
+          StorageLive(_8);
+          _8 = _2;
+-         _7 = move _8 as *const () (PtrToPtr);
++         _7 = _2 as *const () (PtrToPtr);
+          StorageDead(_8);
+          _6 = move _7 as usize (Transmute);
+          StorageDead(_7);
+          _0 = const ();
+          StorageDead(_6);
+          StorageDead(_3);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/gvn.cast_pointer_then_transmute.GVN.panic-unwind.diff b/tests/mir-opt/gvn.cast_pointer_then_transmute.GVN.panic-unwind.diff
new file mode 100644
index 00000000000..8133f6e0b00
--- /dev/null
+++ b/tests/mir-opt/gvn.cast_pointer_then_transmute.GVN.panic-unwind.diff
@@ -0,0 +1,47 @@
+- // MIR for `cast_pointer_then_transmute` before GVN
++ // MIR for `cast_pointer_then_transmute` after GVN
+  
+  fn cast_pointer_then_transmute(_1: *mut u32, _2: *mut [u8]) -> () {
+      debug thin => _1;
+      debug fat => _2;
+      let mut _0: ();
+      let _3: usize;
+      let mut _4: *const ();
+      let mut _5: *mut u32;
+      let mut _7: *const ();
+      let mut _8: *mut [u8];
+      scope 1 {
+          debug thin_addr => _3;
+          let _6: usize;
+          scope 2 {
+              debug fat_addr => _6;
+          }
+      }
+  
+      bb0: {
+          StorageLive(_3);
+          StorageLive(_4);
+          StorageLive(_5);
+          _5 = _1;
+-         _4 = move _5 as *const () (PtrToPtr);
++         _4 = _1 as *const () (PtrToPtr);
+          StorageDead(_5);
+-         _3 = move _4 as usize (Transmute);
++         _3 = _1 as usize (Transmute);
+          StorageDead(_4);
+          StorageLive(_6);
+          StorageLive(_7);
+          StorageLive(_8);
+          _8 = _2;
+-         _7 = move _8 as *const () (PtrToPtr);
++         _7 = _2 as *const () (PtrToPtr);
+          StorageDead(_8);
+          _6 = move _7 as usize (Transmute);
+          StorageDead(_7);
+          _0 = const ();
+          StorageDead(_6);
+          StorageDead(_3);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/gvn.rs b/tests/mir-opt/gvn.rs
index 8bc2550b8f5..86f42d23f38 100644
--- a/tests/mir-opt/gvn.rs
+++ b/tests/mir-opt/gvn.rs
@@ -883,6 +883,49 @@ fn generic_cast_metadata<T, A: ?Sized, B: ?Sized>(ps: *const [T], pa: *const A,
     }
 }
 
+fn cast_pointer_eq(p1: *mut u8, p2: *mut u32, p3: *mut u32, p4: *mut [u32]) {
+    // CHECK-LABEL: fn cast_pointer_eq
+    // CHECK: debug p1 => [[P1:_1]];
+    // CHECK: debug p2 => [[P2:_2]];
+    // CHECK: debug p3 => [[P3:_3]];
+    // CHECK: debug p4 => [[P4:_4]];
+
+    // CHECK: [[M1:_.+]] = [[P1]] as *const u32 (PtrToPtr);
+    // CHECK: [[M2:_.+]] = [[P2]] as *const u32 (PtrToPtr);
+    // CHECK: [[M3:_.+]] = [[P3]] as *const u32 (PtrToPtr);
+    // CHECK: [[M4:_.+]] = [[P4]] as *const u32 (PtrToPtr);
+    let m1 = p1 as *const u32;
+    let m2 = p2 as *const u32;
+    let m3 = p3 as *const u32;
+    let m4 = p4 as *const u32;
+
+    // CHECK-NOT: Eq
+    // CHECK: Eq([[M1]], [[M2]])
+    // CHECK-NOT: Eq
+    // CHECK: Eq([[P2]], [[P3]])
+    // CHECK-NOT: Eq
+    // CHECK: Eq([[M3]], [[M4]])
+    // CHECK-NOT: Eq
+    let eq_different_thing = m1 == m2;
+    let eq_optimize = m2 == m3;
+    let eq_thin_fat = m3 == m4;
+
+    // CHECK: _0 = const ();
+}
+
+// Transmuting can skip a pointer cast so long as it wasn't a fat-to-thin cast.
+unsafe fn cast_pointer_then_transmute(thin: *mut u32, fat: *mut [u8]) {
+    // CHECK-LABEL: fn cast_pointer_then_transmute
+
+    // CHECK: [[UNUSED:_.+]] = _1 as *const () (PtrToPtr);
+    // CHECK: = _1 as usize (Transmute);
+    let thin_addr: usize = std::intrinsics::transmute(thin as *const ());
+
+    // CHECK: [[TEMP2:_.+]] = _2 as *const () (PtrToPtr);
+    // CHECK: = move [[TEMP2]] as usize (Transmute);
+    let fat_addr: usize = std::intrinsics::transmute(fat as *const ());
+}
+
 fn main() {
     subexpression_elimination(2, 4, 5);
     wrap_unwrap(5);
@@ -950,3 +993,5 @@ fn identity<T>(x: T) -> T {
 // EMIT_MIR gvn.manual_slice_mut_len.GVN.diff
 // EMIT_MIR gvn.array_len.GVN.diff
 // EMIT_MIR gvn.generic_cast_metadata.GVN.diff
+// EMIT_MIR gvn.cast_pointer_eq.GVN.diff
+// EMIT_MIR gvn.cast_pointer_then_transmute.GVN.diff
diff --git a/tests/mir-opt/lower_array_len.array_bound.GVN.panic-abort.diff b/tests/mir-opt/lower_array_len.array_bound.GVN.panic-abort.diff
index 6c0c7a1d438..7aca2cb0007 100644
--- a/tests/mir-opt/lower_array_len.array_bound.GVN.panic-abort.diff
+++ b/tests/mir-opt/lower_array_len.array_bound.GVN.panic-abort.diff
@@ -15,10 +15,12 @@
       let mut _10: bool;
   
       bb0: {
-          StorageLive(_3);
+-         StorageLive(_3);
++         nop;
           StorageLive(_4);
           _4 = _1;
-          StorageLive(_5);
+-         StorageLive(_5);
++         nop;
           StorageLive(_6);
           StorageLive(_7);
           _7 = &(*_2);
@@ -32,12 +34,14 @@
       bb1: {
           StorageDead(_6);
 -         _3 = Lt(move _4, move _5);
-+         _3 = Lt(_1, move _5);
-          switchInt(move _3) -> [0: bb4, otherwise: bb2];
+-         switchInt(move _3) -> [0: bb4, otherwise: bb2];
++         _3 = Lt(_1, const N);
++         switchInt(_3) -> [0: bb4, otherwise: bb2];
       }
   
       bb2: {
-          StorageDead(_5);
+-         StorageDead(_5);
++         nop;
           StorageDead(_4);
           StorageLive(_8);
           _8 = _1;
@@ -45,8 +49,8 @@
 -         _10 = Lt(_8, _9);
 -         assert(move _10, "index out of bounds: the length is {} but the index is {}", move _9, _8) -> [success: bb3, unwind unreachable];
 +         _9 = const N;
-+         _10 = Lt(_1, const N);
-+         assert(move _10, "index out of bounds: the length is {} but the index is {}", const N, _1) -> [success: bb3, unwind unreachable];
++         _10 = _3;
++         assert(_3, "index out of bounds: the length is {} but the index is {}", const N, _1) -> [success: bb3, unwind unreachable];
       }
   
       bb3: {
@@ -57,14 +61,16 @@
       }
   
       bb4: {
-          StorageDead(_5);
+-         StorageDead(_5);
++         nop;
           StorageDead(_4);
           _0 = const 42_u8;
           goto -> bb5;
       }
   
       bb5: {
-          StorageDead(_3);
+-         StorageDead(_3);
++         nop;
           return;
       }
   }
diff --git a/tests/mir-opt/lower_array_len.array_bound.GVN.panic-unwind.diff b/tests/mir-opt/lower_array_len.array_bound.GVN.panic-unwind.diff
index ed41703c873..ed39c11319a 100644
--- a/tests/mir-opt/lower_array_len.array_bound.GVN.panic-unwind.diff
+++ b/tests/mir-opt/lower_array_len.array_bound.GVN.panic-unwind.diff
@@ -15,10 +15,12 @@
       let mut _10: bool;
   
       bb0: {
-          StorageLive(_3);
+-         StorageLive(_3);
++         nop;
           StorageLive(_4);
           _4 = _1;
-          StorageLive(_5);
+-         StorageLive(_5);
++         nop;
           StorageLive(_6);
           StorageLive(_7);
           _7 = &(*_2);
@@ -32,12 +34,14 @@
       bb1: {
           StorageDead(_6);
 -         _3 = Lt(move _4, move _5);
-+         _3 = Lt(_1, move _5);
-          switchInt(move _3) -> [0: bb4, otherwise: bb2];
+-         switchInt(move _3) -> [0: bb4, otherwise: bb2];
++         _3 = Lt(_1, const N);
++         switchInt(_3) -> [0: bb4, otherwise: bb2];
       }
   
       bb2: {
-          StorageDead(_5);
+-         StorageDead(_5);
++         nop;
           StorageDead(_4);
           StorageLive(_8);
           _8 = _1;
@@ -45,8 +49,8 @@
 -         _10 = Lt(_8, _9);
 -         assert(move _10, "index out of bounds: the length is {} but the index is {}", move _9, _8) -> [success: bb3, unwind continue];
 +         _9 = const N;
-+         _10 = Lt(_1, const N);
-+         assert(move _10, "index out of bounds: the length is {} but the index is {}", const N, _1) -> [success: bb3, unwind continue];
++         _10 = _3;
++         assert(_3, "index out of bounds: the length is {} but the index is {}", const N, _1) -> [success: bb3, unwind continue];
       }
   
       bb3: {
@@ -57,14 +61,16 @@
       }
   
       bb4: {
-          StorageDead(_5);
+-         StorageDead(_5);
++         nop;
           StorageDead(_4);
           _0 = const 42_u8;
           goto -> bb5;
       }
   
       bb5: {
-          StorageDead(_3);
+-         StorageDead(_3);
++         nop;
           return;
       }
   }
diff --git a/tests/mir-opt/lower_array_len.array_bound_mut.GVN.panic-abort.diff b/tests/mir-opt/lower_array_len.array_bound_mut.GVN.panic-abort.diff
index 80e8ea37f41..734d28e9546 100644
--- a/tests/mir-opt/lower_array_len.array_bound_mut.GVN.panic-abort.diff
+++ b/tests/mir-opt/lower_array_len.array_bound_mut.GVN.panic-abort.diff
@@ -18,10 +18,12 @@
       let mut _13: bool;
   
       bb0: {
-          StorageLive(_3);
+-         StorageLive(_3);
++         nop;
           StorageLive(_4);
           _4 = _1;
-          StorageLive(_5);
+-         StorageLive(_5);
++         nop;
           StorageLive(_6);
           StorageLive(_7);
           _7 = &(*_2);
@@ -35,12 +37,14 @@
       bb1: {
           StorageDead(_6);
 -         _3 = Lt(move _4, move _5);
-+         _3 = Lt(_1, move _5);
-          switchInt(move _3) -> [0: bb4, otherwise: bb2];
+-         switchInt(move _3) -> [0: bb4, otherwise: bb2];
++         _3 = Lt(_1, const N);
++         switchInt(_3) -> [0: bb4, otherwise: bb2];
       }
   
       bb2: {
-          StorageDead(_5);
+-         StorageDead(_5);
++         nop;
           StorageDead(_4);
           StorageLive(_8);
           _8 = _1;
@@ -48,8 +52,8 @@
 -         _10 = Lt(_8, _9);
 -         assert(move _10, "index out of bounds: the length is {} but the index is {}", move _9, _8) -> [success: bb3, unwind unreachable];
 +         _9 = const N;
-+         _10 = Lt(_1, const N);
-+         assert(move _10, "index out of bounds: the length is {} but the index is {}", const N, _1) -> [success: bb3, unwind unreachable];
++         _10 = _3;
++         assert(_3, "index out of bounds: the length is {} but the index is {}", const N, _1) -> [success: bb3, unwind unreachable];
       }
   
       bb3: {
@@ -60,7 +64,8 @@
       }
   
       bb4: {
-          StorageDead(_5);
+-         StorageDead(_5);
++         nop;
           StorageDead(_4);
           StorageLive(_11);
           _11 = const 0_usize;
@@ -81,7 +86,8 @@
       }
   
       bb6: {
-          StorageDead(_3);
+-         StorageDead(_3);
++         nop;
           return;
       }
   }
diff --git a/tests/mir-opt/lower_array_len.array_bound_mut.GVN.panic-unwind.diff b/tests/mir-opt/lower_array_len.array_bound_mut.GVN.panic-unwind.diff
index 6e67a6c17ef..ec569ab5042 100644
--- a/tests/mir-opt/lower_array_len.array_bound_mut.GVN.panic-unwind.diff
+++ b/tests/mir-opt/lower_array_len.array_bound_mut.GVN.panic-unwind.diff
@@ -18,10 +18,12 @@
       let mut _13: bool;
   
       bb0: {
-          StorageLive(_3);
+-         StorageLive(_3);
++         nop;
           StorageLive(_4);
           _4 = _1;
-          StorageLive(_5);
+-         StorageLive(_5);
++         nop;
           StorageLive(_6);
           StorageLive(_7);
           _7 = &(*_2);
@@ -35,12 +37,14 @@
       bb1: {
           StorageDead(_6);
 -         _3 = Lt(move _4, move _5);
-+         _3 = Lt(_1, move _5);
-          switchInt(move _3) -> [0: bb4, otherwise: bb2];
+-         switchInt(move _3) -> [0: bb4, otherwise: bb2];
++         _3 = Lt(_1, const N);
++         switchInt(_3) -> [0: bb4, otherwise: bb2];
       }
   
       bb2: {
-          StorageDead(_5);
+-         StorageDead(_5);
++         nop;
           StorageDead(_4);
           StorageLive(_8);
           _8 = _1;
@@ -48,8 +52,8 @@
 -         _10 = Lt(_8, _9);
 -         assert(move _10, "index out of bounds: the length is {} but the index is {}", move _9, _8) -> [success: bb3, unwind continue];
 +         _9 = const N;
-+         _10 = Lt(_1, const N);
-+         assert(move _10, "index out of bounds: the length is {} but the index is {}", const N, _1) -> [success: bb3, unwind continue];
++         _10 = _3;
++         assert(_3, "index out of bounds: the length is {} but the index is {}", const N, _1) -> [success: bb3, unwind continue];
       }
   
       bb3: {
@@ -60,7 +64,8 @@
       }
   
       bb4: {
-          StorageDead(_5);
+-         StorageDead(_5);
++         nop;
           StorageDead(_4);
           StorageLive(_11);
           _11 = const 0_usize;
@@ -81,7 +86,8 @@
       }
   
       bb6: {
-          StorageDead(_3);
+-         StorageDead(_3);
++         nop;
           return;
       }
   }
diff --git a/tests/mir-opt/lower_array_len.rs b/tests/mir-opt/lower_array_len.rs
index caa598d067a..f7ed376726c 100644
--- a/tests/mir-opt/lower_array_len.rs
+++ b/tests/mir-opt/lower_array_len.rs
@@ -5,16 +5,20 @@
 // EMIT_MIR lower_array_len.array_bound.GVN.diff
 pub fn array_bound<const N: usize>(index: usize, slice: &[u8; N]) -> u8 {
     // CHECK-LABEL: fn array_bound(
-    // CHECK: [[len:_.*]] = const N;
-    // CHECK: Lt(_1, move [[len]]);
+    // CHECK-NOT: Lt
+    // CHECK: Lt(_1, const N);
+    // CHECK-NOT: Lt
     if index < slice.len() { slice[index] } else { 42 }
 }
 
 // EMIT_MIR lower_array_len.array_bound_mut.GVN.diff
 pub fn array_bound_mut<const N: usize>(index: usize, slice: &mut [u8; N]) -> u8 {
     // CHECK-LABEL: fn array_bound_mut(
-    // CHECK: [[len:_.*]] = const N;
-    // CHECK: Lt(_1, move [[len]]);
+    // CHECK-NOT: Lt
+    // CHECK: Lt(_1, const N);
+    // CHECK-NOT: Lt
+    // CHECK: Lt(const 0_usize, const N)
+    // CHECK-NOT: Lt
     if index < slice.len() {
         slice[index]
     } else {
diff --git a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir
index fbb887fe76a..79c5bcfe9cd 100644
--- a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir
+++ b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir
@@ -7,19 +7,90 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () {
     let mut _11: std::slice::Iter<'_, T>;
     let mut _12: std::iter::Rev<std::slice::Iter<'_, T>>;
     let mut _13: std::iter::Rev<std::slice::Iter<'_, T>>;
-    let mut _15: std::option::Option<&T>;
-    let mut _16: isize;
-    let mut _18: &impl Fn(&T);
-    let mut _19: (&T,);
-    let _20: ();
+    let mut _37: std::option::Option<&T>;
+    let mut _39: &impl Fn(&T);
+    let mut _40: (&T,);
+    let _41: ();
     scope 1 {
         debug iter => _13;
-        let _17: &T;
+        let _38: &T;
         scope 2 {
-            debug x => _17;
+            debug x => _38;
         }
         scope 17 (inlined <Rev<std::slice::Iter<'_, T>> as Iterator>::next) {
-            let mut _14: &mut std::slice::Iter<'_, T>;
+            scope 18 (inlined <std::slice::Iter<'_, T> as DoubleEndedIterator>::next_back) {
+                let mut _14: *const *const T;
+                let mut _15: *const std::ptr::NonNull<T>;
+                let mut _20: bool;
+                let mut _21: *const T;
+                let _36: &T;
+                scope 19 {
+                    let _16: std::ptr::NonNull<T>;
+                    let _22: usize;
+                    scope 20 {
+                    }
+                    scope 21 {
+                        scope 25 (inlined <NonNull<T> as PartialEq>::eq) {
+                            let mut _17: std::ptr::NonNull<T>;
+                            scope 26 (inlined NonNull::<T>::as_ptr) {
+                                let mut _18: *const T;
+                            }
+                            scope 27 (inlined NonNull::<T>::as_ptr) {
+                                let mut _19: *const T;
+                            }
+                        }
+                    }
+                    scope 22 (inlined std::ptr::const_ptr::<impl *const T>::addr) {
+                        scope 23 (inlined std::ptr::const_ptr::<impl *const T>::cast::<()>) {
+                        }
+                    }
+                    scope 24 (inlined std::ptr::const_ptr::<impl *const *const T>::cast::<NonNull<T>>) {
+                    }
+                }
+                scope 28 (inlined std::slice::Iter::<'_, T>::next_back_unchecked) {
+                    let _29: std::ptr::NonNull<T>;
+                    scope 29 (inlined std::slice::Iter::<'_, T>::pre_dec_end) {
+                        let mut _23: *mut *const T;
+                        let mut _24: *mut std::ptr::NonNull<T>;
+                        let mut _25: std::ptr::NonNull<T>;
+                        let mut _28: std::ptr::NonNull<T>;
+                        let mut _30: *mut *const T;
+                        let mut _31: *mut usize;
+                        let mut _32: usize;
+                        let mut _33: usize;
+                        scope 30 {
+                            scope 31 {
+                            }
+                            scope 32 {
+                                scope 35 (inlined NonNull::<T>::sub) {
+                                    scope 36 (inlined core::num::<impl isize>::unchecked_neg) {
+                                        scope 37 (inlined core::ub_checks::check_language_ub) {
+                                            scope 38 (inlined core::ub_checks::check_language_ub::runtime) {
+                                            }
+                                        }
+                                    }
+                                    scope 39 (inlined NonNull::<T>::offset) {
+                                        let mut _26: *const T;
+                                        let mut _27: *const T;
+                                    }
+                                }
+                            }
+                            scope 33 (inlined std::ptr::mut_ptr::<impl *mut *const T>::cast::<usize>) {
+                            }
+                            scope 34 (inlined std::ptr::mut_ptr::<impl *mut *const T>::cast::<NonNull<T>>) {
+                            }
+                        }
+                    }
+                    scope 40 (inlined NonNull::<T>::as_ref::<'_>) {
+                        let mut _34: std::ptr::NonNull<T>;
+                        scope 41 (inlined NonNull::<T>::as_ptr) {
+                            let mut _35: *const T;
+                        }
+                        scope 42 (inlined std::ptr::mut_ptr::<impl *mut T>::cast_const) {
+                        }
+                    }
+                }
+            }
         }
     }
     scope 3 (inlined core::slice::<impl [T]>::iter) {
@@ -107,45 +178,147 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () {
     }
 
     bb4: {
-        StorageLive(_15);
-        StorageLive(_14);
-        _14 = &mut (_13.0: std::slice::Iter<'_, T>);
-        _15 = <std::slice::Iter<'_, T> as DoubleEndedIterator>::next_back(move _14) -> [return: bb5, unwind unreachable];
+        StorageLive(_37);
+        StorageLive(_22);
+        StorageLive(_21);
+        StorageLive(_16);
+        StorageLive(_36);
+        StorageLive(_20);
+        switchInt(const <T as std::mem::SizedTypeProperties>::IS_ZST) -> [0: bb5, otherwise: bb6];
     }
 
     bb5: {
+        StorageLive(_15);
+        StorageLive(_14);
+        _14 = &raw const ((_13.0: std::slice::Iter<'_, T>).1: *const T);
+        _15 = _14 as *const std::ptr::NonNull<T> (PtrToPtr);
         StorageDead(_14);
-        _16 = discriminant(_15);
-        switchInt(move _16) -> [0: bb6, 1: bb8, otherwise: bb10];
+        _16 = (*_15);
+        StorageDead(_15);
+        StorageLive(_18);
+        StorageLive(_19);
+        StorageLive(_17);
+        _17 = ((_13.0: std::slice::Iter<'_, T>).0: std::ptr::NonNull<T>);
+        _18 = (_17.0: *const T);
+        StorageDead(_17);
+        _19 = (_16.0: *const T);
+        _20 = Eq(_18, _19);
+        StorageDead(_19);
+        StorageDead(_18);
+        goto -> bb7;
     }
 
     bb6: {
-        StorageDead(_15);
-        StorageDead(_13);
-        drop(_2) -> [return: bb7, unwind unreachable];
+        _21 = ((_13.0: std::slice::Iter<'_, T>).1: *const T);
+        _22 = _21 as usize (Transmute);
+        _20 = Eq(_22, const 0_usize);
+        goto -> bb7;
     }
 
     bb7: {
-        return;
+        switchInt(move _20) -> [0: bb8, otherwise: bb16];
     }
 
     bb8: {
-        _17 = ((_15 as Some).0: &T);
-        StorageLive(_18);
-        _18 = &_2;
-        StorageLive(_19);
-        _19 = (_17,);
-        _20 = <impl Fn(&T) as Fn<(&T,)>>::call(move _18, move _19) -> [return: bb9, unwind unreachable];
+        StorageLive(_35);
+        StorageLive(_29);
+        StorageLive(_31);
+        StorageLive(_24);
+        switchInt(const <T as std::mem::SizedTypeProperties>::IS_ZST) -> [0: bb9, otherwise: bb13];
     }
 
     bb9: {
-        StorageDead(_19);
-        StorageDead(_18);
-        StorageDead(_15);
-        goto -> bb4;
+        StorageLive(_23);
+        _23 = &raw mut ((_13.0: std::slice::Iter<'_, T>).1: *const T);
+        _24 = _23 as *mut std::ptr::NonNull<T> (PtrToPtr);
+        StorageDead(_23);
+        StorageLive(_28);
+        _25 = (*_24);
+        switchInt(const <T as std::mem::SizedTypeProperties>::IS_ZST) -> [0: bb10, otherwise: bb11];
     }
 
     bb10: {
-        unreachable;
+        StorageLive(_27);
+        StorageLive(_26);
+        _26 = (_25.0: *const T);
+        _27 = Offset(move _26, const -1_isize);
+        StorageDead(_26);
+        _28 = NonNull::<T> { pointer: move _27 };
+        StorageDead(_27);
+        goto -> bb12;
+    }
+
+    bb11: {
+        _28 = _25;
+        goto -> bb12;
+    }
+
+    bb12: {
+        (*_24) = move _28;
+        StorageDead(_28);
+        _29 = (*_24);
+        goto -> bb14;
+    }
+
+    bb13: {
+        StorageLive(_30);
+        _30 = &raw mut ((_13.0: std::slice::Iter<'_, T>).1: *const T);
+        _31 = _30 as *mut usize (PtrToPtr);
+        StorageDead(_30);
+        StorageLive(_33);
+        StorageLive(_32);
+        _32 = (*_31);
+        _33 = SubUnchecked(move _32, const 1_usize);
+        StorageDead(_32);
+        (*_31) = move _33;
+        StorageDead(_33);
+        _29 = ((_13.0: std::slice::Iter<'_, T>).0: std::ptr::NonNull<T>);
+        goto -> bb14;
+    }
+
+    bb14: {
+        StorageDead(_24);
+        StorageDead(_31);
+        StorageLive(_34);
+        _34 = _29;
+        _35 = (_34.0: *const T);
+        StorageDead(_34);
+        _36 = &(*_35);
+        StorageDead(_29);
+        StorageDead(_35);
+        _37 = Option::<&T>::Some(_36);
+        StorageDead(_20);
+        StorageDead(_36);
+        StorageDead(_16);
+        StorageDead(_21);
+        StorageDead(_22);
+        _38 = ((_37 as Some).0: &T);
+        StorageLive(_39);
+        _39 = &_2;
+        StorageLive(_40);
+        _40 = (_38,);
+        _41 = <impl Fn(&T) as Fn<(&T,)>>::call(move _39, move _40) -> [return: bb15, unwind unreachable];
+    }
+
+    bb15: {
+        StorageDead(_40);
+        StorageDead(_39);
+        StorageDead(_37);
+        goto -> bb4;
+    }
+
+    bb16: {
+        StorageDead(_20);
+        StorageDead(_36);
+        StorageDead(_16);
+        StorageDead(_21);
+        StorageDead(_22);
+        StorageDead(_37);
+        StorageDead(_13);
+        drop(_2) -> [return: bb17, unwind unreachable];
+    }
+
+    bb17: {
+        return;
     }
 }
diff --git a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir
index db9409f72ab..7c107a23f9e 100644
--- a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir
+++ b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir
@@ -7,19 +7,90 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () {
     let mut _11: std::slice::Iter<'_, T>;
     let mut _12: std::iter::Rev<std::slice::Iter<'_, T>>;
     let mut _13: std::iter::Rev<std::slice::Iter<'_, T>>;
-    let mut _15: std::option::Option<&T>;
-    let mut _16: isize;
-    let mut _18: &impl Fn(&T);
-    let mut _19: (&T,);
-    let _20: ();
+    let mut _37: std::option::Option<&T>;
+    let mut _39: &impl Fn(&T);
+    let mut _40: (&T,);
+    let _41: ();
     scope 1 {
         debug iter => _13;
-        let _17: &T;
+        let _38: &T;
         scope 2 {
-            debug x => _17;
+            debug x => _38;
         }
         scope 17 (inlined <Rev<std::slice::Iter<'_, T>> as Iterator>::next) {
-            let mut _14: &mut std::slice::Iter<'_, T>;
+            scope 18 (inlined <std::slice::Iter<'_, T> as DoubleEndedIterator>::next_back) {
+                let mut _14: *const *const T;
+                let mut _15: *const std::ptr::NonNull<T>;
+                let mut _20: bool;
+                let mut _21: *const T;
+                let _36: &T;
+                scope 19 {
+                    let _16: std::ptr::NonNull<T>;
+                    let _22: usize;
+                    scope 20 {
+                    }
+                    scope 21 {
+                        scope 25 (inlined <NonNull<T> as PartialEq>::eq) {
+                            let mut _17: std::ptr::NonNull<T>;
+                            scope 26 (inlined NonNull::<T>::as_ptr) {
+                                let mut _18: *const T;
+                            }
+                            scope 27 (inlined NonNull::<T>::as_ptr) {
+                                let mut _19: *const T;
+                            }
+                        }
+                    }
+                    scope 22 (inlined std::ptr::const_ptr::<impl *const T>::addr) {
+                        scope 23 (inlined std::ptr::const_ptr::<impl *const T>::cast::<()>) {
+                        }
+                    }
+                    scope 24 (inlined std::ptr::const_ptr::<impl *const *const T>::cast::<NonNull<T>>) {
+                    }
+                }
+                scope 28 (inlined std::slice::Iter::<'_, T>::next_back_unchecked) {
+                    let _29: std::ptr::NonNull<T>;
+                    scope 29 (inlined std::slice::Iter::<'_, T>::pre_dec_end) {
+                        let mut _23: *mut *const T;
+                        let mut _24: *mut std::ptr::NonNull<T>;
+                        let mut _25: std::ptr::NonNull<T>;
+                        let mut _28: std::ptr::NonNull<T>;
+                        let mut _30: *mut *const T;
+                        let mut _31: *mut usize;
+                        let mut _32: usize;
+                        let mut _33: usize;
+                        scope 30 {
+                            scope 31 {
+                            }
+                            scope 32 {
+                                scope 35 (inlined NonNull::<T>::sub) {
+                                    scope 36 (inlined core::num::<impl isize>::unchecked_neg) {
+                                        scope 37 (inlined core::ub_checks::check_language_ub) {
+                                            scope 38 (inlined core::ub_checks::check_language_ub::runtime) {
+                                            }
+                                        }
+                                    }
+                                    scope 39 (inlined NonNull::<T>::offset) {
+                                        let mut _26: *const T;
+                                        let mut _27: *const T;
+                                    }
+                                }
+                            }
+                            scope 33 (inlined std::ptr::mut_ptr::<impl *mut *const T>::cast::<usize>) {
+                            }
+                            scope 34 (inlined std::ptr::mut_ptr::<impl *mut *const T>::cast::<NonNull<T>>) {
+                            }
+                        }
+                    }
+                    scope 40 (inlined NonNull::<T>::as_ref::<'_>) {
+                        let mut _34: std::ptr::NonNull<T>;
+                        scope 41 (inlined NonNull::<T>::as_ptr) {
+                            let mut _35: *const T;
+                        }
+                        scope 42 (inlined std::ptr::mut_ptr::<impl *mut T>::cast_const) {
+                        }
+                    }
+                }
+            }
         }
     }
     scope 3 (inlined core::slice::<impl [T]>::iter) {
@@ -107,53 +178,155 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () {
     }
 
     bb4: {
-        StorageLive(_15);
-        StorageLive(_14);
-        _14 = &mut (_13.0: std::slice::Iter<'_, T>);
-        _15 = <std::slice::Iter<'_, T> as DoubleEndedIterator>::next_back(move _14) -> [return: bb5, unwind: bb11];
+        StorageLive(_37);
+        StorageLive(_22);
+        StorageLive(_21);
+        StorageLive(_16);
+        StorageLive(_36);
+        StorageLive(_20);
+        switchInt(const <T as std::mem::SizedTypeProperties>::IS_ZST) -> [0: bb5, otherwise: bb6];
     }
 
     bb5: {
+        StorageLive(_15);
+        StorageLive(_14);
+        _14 = &raw const ((_13.0: std::slice::Iter<'_, T>).1: *const T);
+        _15 = _14 as *const std::ptr::NonNull<T> (PtrToPtr);
         StorageDead(_14);
-        _16 = discriminant(_15);
-        switchInt(move _16) -> [0: bb6, 1: bb8, otherwise: bb10];
+        _16 = (*_15);
+        StorageDead(_15);
+        StorageLive(_18);
+        StorageLive(_19);
+        StorageLive(_17);
+        _17 = ((_13.0: std::slice::Iter<'_, T>).0: std::ptr::NonNull<T>);
+        _18 = (_17.0: *const T);
+        StorageDead(_17);
+        _19 = (_16.0: *const T);
+        _20 = Eq(_18, _19);
+        StorageDead(_19);
+        StorageDead(_18);
+        goto -> bb7;
     }
 
     bb6: {
-        StorageDead(_15);
-        StorageDead(_13);
-        drop(_2) -> [return: bb7, unwind continue];
+        _21 = ((_13.0: std::slice::Iter<'_, T>).1: *const T);
+        _22 = _21 as usize (Transmute);
+        _20 = Eq(_22, const 0_usize);
+        goto -> bb7;
     }
 
     bb7: {
-        return;
+        switchInt(move _20) -> [0: bb8, otherwise: bb18];
     }
 
     bb8: {
-        _17 = ((_15 as Some).0: &T);
-        StorageLive(_18);
-        _18 = &_2;
-        StorageLive(_19);
-        _19 = (_17,);
-        _20 = <impl Fn(&T) as Fn<(&T,)>>::call(move _18, move _19) -> [return: bb9, unwind: bb11];
+        StorageLive(_35);
+        StorageLive(_29);
+        StorageLive(_31);
+        StorageLive(_24);
+        switchInt(const <T as std::mem::SizedTypeProperties>::IS_ZST) -> [0: bb9, otherwise: bb13];
     }
 
     bb9: {
-        StorageDead(_19);
-        StorageDead(_18);
-        StorageDead(_15);
-        goto -> bb4;
+        StorageLive(_23);
+        _23 = &raw mut ((_13.0: std::slice::Iter<'_, T>).1: *const T);
+        _24 = _23 as *mut std::ptr::NonNull<T> (PtrToPtr);
+        StorageDead(_23);
+        StorageLive(_28);
+        _25 = (*_24);
+        switchInt(const <T as std::mem::SizedTypeProperties>::IS_ZST) -> [0: bb10, otherwise: bb11];
     }
 
     bb10: {
-        unreachable;
+        StorageLive(_27);
+        StorageLive(_26);
+        _26 = (_25.0: *const T);
+        _27 = Offset(move _26, const -1_isize);
+        StorageDead(_26);
+        _28 = NonNull::<T> { pointer: move _27 };
+        StorageDead(_27);
+        goto -> bb12;
+    }
+
+    bb11: {
+        _28 = _25;
+        goto -> bb12;
+    }
+
+    bb12: {
+        (*_24) = move _28;
+        StorageDead(_28);
+        _29 = (*_24);
+        goto -> bb14;
+    }
+
+    bb13: {
+        StorageLive(_30);
+        _30 = &raw mut ((_13.0: std::slice::Iter<'_, T>).1: *const T);
+        _31 = _30 as *mut usize (PtrToPtr);
+        StorageDead(_30);
+        StorageLive(_33);
+        StorageLive(_32);
+        _32 = (*_31);
+        _33 = SubUnchecked(move _32, const 1_usize);
+        StorageDead(_32);
+        (*_31) = move _33;
+        StorageDead(_33);
+        _29 = ((_13.0: std::slice::Iter<'_, T>).0: std::ptr::NonNull<T>);
+        goto -> bb14;
     }
 
-    bb11 (cleanup): {
-        drop(_2) -> [return: bb12, unwind terminate(cleanup)];
+    bb14: {
+        StorageDead(_24);
+        StorageDead(_31);
+        StorageLive(_34);
+        _34 = _29;
+        _35 = (_34.0: *const T);
+        StorageDead(_34);
+        _36 = &(*_35);
+        StorageDead(_29);
+        StorageDead(_35);
+        _37 = Option::<&T>::Some(_36);
+        StorageDead(_20);
+        StorageDead(_36);
+        StorageDead(_16);
+        StorageDead(_21);
+        StorageDead(_22);
+        _38 = ((_37 as Some).0: &T);
+        StorageLive(_39);
+        _39 = &_2;
+        StorageLive(_40);
+        _40 = (_38,);
+        _41 = <impl Fn(&T) as Fn<(&T,)>>::call(move _39, move _40) -> [return: bb15, unwind: bb16];
     }
 
-    bb12 (cleanup): {
+    bb15: {
+        StorageDead(_40);
+        StorageDead(_39);
+        StorageDead(_37);
+        goto -> bb4;
+    }
+
+    bb16 (cleanup): {
+        drop(_2) -> [return: bb17, unwind terminate(cleanup)];
+    }
+
+    bb17 (cleanup): {
         resume;
     }
+
+    bb18: {
+        StorageDead(_20);
+        StorageDead(_36);
+        StorageDead(_16);
+        StorageDead(_21);
+        StorageDead(_22);
+        StorageDead(_37);
+        StorageDead(_13);
+        drop(_2) -> [return: bb19, unwind continue];
+    }
+
+    bb19: {
+        return;
+    }
 }
diff --git a/tests/mir-opt/pre-codegen/slice_iter.rs b/tests/mir-opt/pre-codegen/slice_iter.rs
index 86f37ca4d13..3f89ab0e6f3 100644
--- a/tests/mir-opt/pre-codegen/slice_iter.rs
+++ b/tests/mir-opt/pre-codegen/slice_iter.rs
@@ -1,8 +1,10 @@
 // skip-filecheck
 //@ compile-flags: -O -C debuginfo=0 -Zmir-opt-level=2
+//@ only-64bit (constants for `None::<&T>` show in the output)
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
 
 #![crate_type = "lib"]
+#![feature(exact_size_is_empty)]
 
 // When this test was added, the MIR for `next` was 174 lines just for the basic
 // blocks -- far more if you counted the scopes.  The goal of having this here
@@ -13,6 +15,11 @@
 // As such, feel free to `--bless` whatever changes you get here, so long as
 // doing so doesn't add substantially more MIR.
 
+// EMIT_MIR slice_iter.slice_iter_generic_is_empty.PreCodegen.after.mir
+pub fn slice_iter_generic_is_empty<T>(it: &std::slice::Iter<'_, T>) -> bool {
+    it.is_empty()
+}
+
 // EMIT_MIR slice_iter.slice_iter_next.PreCodegen.after.mir
 pub fn slice_iter_next<'a, T>(it: &mut std::slice::Iter<'a, T>) -> Option<&'a T> {
     it.next()
diff --git a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_generic_is_empty.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_generic_is_empty.PreCodegen.after.panic-abort.mir
new file mode 100644
index 00000000000..96e71c298e0
--- /dev/null
+++ b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_generic_is_empty.PreCodegen.after.panic-abort.mir
@@ -0,0 +1,76 @@
+// MIR for `slice_iter_generic_is_empty` after PreCodegen
+
+fn slice_iter_generic_is_empty(_1: &std::slice::Iter<'_, T>) -> bool {
+    debug it => _1;
+    let mut _0: bool;
+    scope 1 (inlined <std::slice::Iter<'_, T> as ExactSizeIterator>::is_empty) {
+        let mut _2: *const *const T;
+        let mut _3: *const std::ptr::NonNull<T>;
+        let mut _8: *const T;
+        scope 2 {
+            let _4: std::ptr::NonNull<T>;
+            let _9: usize;
+            scope 3 {
+            }
+            scope 4 {
+                scope 8 (inlined <NonNull<T> as PartialEq>::eq) {
+                    let mut _5: std::ptr::NonNull<T>;
+                    scope 9 (inlined NonNull::<T>::as_ptr) {
+                        let mut _6: *const T;
+                    }
+                    scope 10 (inlined NonNull::<T>::as_ptr) {
+                        let mut _7: *const T;
+                    }
+                }
+            }
+            scope 5 (inlined std::ptr::const_ptr::<impl *const T>::addr) {
+                scope 6 (inlined std::ptr::const_ptr::<impl *const T>::cast::<()>) {
+                }
+            }
+            scope 7 (inlined std::ptr::const_ptr::<impl *const *const T>::cast::<NonNull<T>>) {
+            }
+        }
+    }
+
+    bb0: {
+        StorageLive(_9);
+        StorageLive(_8);
+        StorageLive(_4);
+        switchInt(const <T as std::mem::SizedTypeProperties>::IS_ZST) -> [0: bb1, otherwise: bb2];
+    }
+
+    bb1: {
+        StorageLive(_3);
+        StorageLive(_2);
+        _2 = &raw const ((*_1).1: *const T);
+        _3 = _2 as *const std::ptr::NonNull<T> (PtrToPtr);
+        StorageDead(_2);
+        _4 = (*_3);
+        StorageDead(_3);
+        StorageLive(_6);
+        StorageLive(_7);
+        StorageLive(_5);
+        _5 = ((*_1).0: std::ptr::NonNull<T>);
+        _6 = (_5.0: *const T);
+        StorageDead(_5);
+        _7 = (_4.0: *const T);
+        _0 = Eq(_6, _7);
+        StorageDead(_7);
+        StorageDead(_6);
+        goto -> bb3;
+    }
+
+    bb2: {
+        _8 = ((*_1).1: *const T);
+        _9 = _8 as usize (Transmute);
+        _0 = Eq(_9, const 0_usize);
+        goto -> bb3;
+    }
+
+    bb3: {
+        StorageDead(_4);
+        StorageDead(_8);
+        StorageDead(_9);
+        return;
+    }
+}
diff --git a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_generic_is_empty.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_generic_is_empty.PreCodegen.after.panic-unwind.mir
new file mode 100644
index 00000000000..96e71c298e0
--- /dev/null
+++ b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_generic_is_empty.PreCodegen.after.panic-unwind.mir
@@ -0,0 +1,76 @@
+// MIR for `slice_iter_generic_is_empty` after PreCodegen
+
+fn slice_iter_generic_is_empty(_1: &std::slice::Iter<'_, T>) -> bool {
+    debug it => _1;
+    let mut _0: bool;
+    scope 1 (inlined <std::slice::Iter<'_, T> as ExactSizeIterator>::is_empty) {
+        let mut _2: *const *const T;
+        let mut _3: *const std::ptr::NonNull<T>;
+        let mut _8: *const T;
+        scope 2 {
+            let _4: std::ptr::NonNull<T>;
+            let _9: usize;
+            scope 3 {
+            }
+            scope 4 {
+                scope 8 (inlined <NonNull<T> as PartialEq>::eq) {
+                    let mut _5: std::ptr::NonNull<T>;
+                    scope 9 (inlined NonNull::<T>::as_ptr) {
+                        let mut _6: *const T;
+                    }
+                    scope 10 (inlined NonNull::<T>::as_ptr) {
+                        let mut _7: *const T;
+                    }
+                }
+            }
+            scope 5 (inlined std::ptr::const_ptr::<impl *const T>::addr) {
+                scope 6 (inlined std::ptr::const_ptr::<impl *const T>::cast::<()>) {
+                }
+            }
+            scope 7 (inlined std::ptr::const_ptr::<impl *const *const T>::cast::<NonNull<T>>) {
+            }
+        }
+    }
+
+    bb0: {
+        StorageLive(_9);
+        StorageLive(_8);
+        StorageLive(_4);
+        switchInt(const <T as std::mem::SizedTypeProperties>::IS_ZST) -> [0: bb1, otherwise: bb2];
+    }
+
+    bb1: {
+        StorageLive(_3);
+        StorageLive(_2);
+        _2 = &raw const ((*_1).1: *const T);
+        _3 = _2 as *const std::ptr::NonNull<T> (PtrToPtr);
+        StorageDead(_2);
+        _4 = (*_3);
+        StorageDead(_3);
+        StorageLive(_6);
+        StorageLive(_7);
+        StorageLive(_5);
+        _5 = ((*_1).0: std::ptr::NonNull<T>);
+        _6 = (_5.0: *const T);
+        StorageDead(_5);
+        _7 = (_4.0: *const T);
+        _0 = Eq(_6, _7);
+        StorageDead(_7);
+        StorageDead(_6);
+        goto -> bb3;
+    }
+
+    bb2: {
+        _8 = ((*_1).1: *const T);
+        _9 = _8 as usize (Transmute);
+        _0 = Eq(_9, const 0_usize);
+        goto -> bb3;
+    }
+
+    bb3: {
+        StorageDead(_4);
+        StorageDead(_8);
+        StorageDead(_9);
+        return;
+    }
+}
diff --git a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-abort.mir
index 78f96bf4195..2df346c081c 100644
--- a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-abort.mir
+++ b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-abort.mir
@@ -3,12 +3,205 @@
 fn slice_iter_mut_next_back(_1: &mut std::slice::IterMut<'_, T>) -> Option<&mut T> {
     debug it => _1;
     let mut _0: std::option::Option<&mut T>;
+    scope 1 (inlined <std::slice::IterMut<'_, T> as DoubleEndedIterator>::next_back) {
+        let mut _2: *const *mut T;
+        let mut _3: *const std::ptr::NonNull<T>;
+        let mut _8: bool;
+        let mut _9: *mut T;
+        let mut _25: &mut T;
+        scope 2 {
+            let _4: std::ptr::NonNull<T>;
+            let _10: usize;
+            scope 3 {
+            }
+            scope 4 {
+                scope 8 (inlined <NonNull<T> as PartialEq>::eq) {
+                    let mut _5: std::ptr::NonNull<T>;
+                    scope 9 (inlined NonNull::<T>::as_ptr) {
+                        let mut _6: *const T;
+                    }
+                    scope 10 (inlined NonNull::<T>::as_ptr) {
+                        let mut _7: *const T;
+                    }
+                }
+            }
+            scope 5 (inlined std::ptr::mut_ptr::<impl *mut T>::addr) {
+                scope 6 (inlined std::ptr::mut_ptr::<impl *mut T>::cast::<()>) {
+                }
+            }
+            scope 7 (inlined std::ptr::const_ptr::<impl *const *mut T>::cast::<NonNull<T>>) {
+            }
+        }
+        scope 11 (inlined std::slice::IterMut::<'_, T>::next_back_unchecked) {
+            let mut _17: std::ptr::NonNull<T>;
+            scope 12 (inlined std::slice::IterMut::<'_, T>::pre_dec_end) {
+                let mut _11: *mut *mut T;
+                let mut _12: *mut std::ptr::NonNull<T>;
+                let mut _13: std::ptr::NonNull<T>;
+                let mut _16: std::ptr::NonNull<T>;
+                let mut _18: *mut *mut T;
+                let mut _19: *mut usize;
+                let mut _20: usize;
+                let mut _21: usize;
+                scope 13 {
+                    scope 14 {
+                    }
+                    scope 15 {
+                        scope 18 (inlined NonNull::<T>::sub) {
+                            scope 19 (inlined core::num::<impl isize>::unchecked_neg) {
+                                scope 20 (inlined core::ub_checks::check_language_ub) {
+                                    scope 21 (inlined core::ub_checks::check_language_ub::runtime) {
+                                    }
+                                }
+                            }
+                            scope 22 (inlined NonNull::<T>::offset) {
+                                let mut _14: *const T;
+                                let mut _15: *const T;
+                            }
+                        }
+                    }
+                    scope 16 (inlined std::ptr::mut_ptr::<impl *mut *mut T>::cast::<usize>) {
+                    }
+                    scope 17 (inlined std::ptr::mut_ptr::<impl *mut *mut T>::cast::<NonNull<T>>) {
+                    }
+                }
+            }
+            scope 23 (inlined NonNull::<T>::as_mut::<'_>) {
+                let mut _22: std::ptr::NonNull<T>;
+                let mut _24: *mut T;
+                scope 24 (inlined NonNull::<T>::as_ptr) {
+                    let mut _23: *const T;
+                }
+            }
+        }
+    }
 
     bb0: {
-        _0 = <std::slice::IterMut<'_, T> as DoubleEndedIterator>::next_back(move _1) -> [return: bb1, unwind unreachable];
+        StorageLive(_10);
+        StorageLive(_9);
+        StorageLive(_4);
+        StorageLive(_25);
+        StorageLive(_8);
+        switchInt(const <T as std::mem::SizedTypeProperties>::IS_ZST) -> [0: bb1, otherwise: bb2];
     }
 
     bb1: {
+        StorageLive(_3);
+        StorageLive(_2);
+        _2 = &raw const ((*_1).1: *mut T);
+        _3 = _2 as *const std::ptr::NonNull<T> (PtrToPtr);
+        StorageDead(_2);
+        _4 = (*_3);
+        StorageDead(_3);
+        StorageLive(_6);
+        StorageLive(_7);
+        StorageLive(_5);
+        _5 = ((*_1).0: std::ptr::NonNull<T>);
+        _6 = (_5.0: *const T);
+        StorageDead(_5);
+        _7 = (_4.0: *const T);
+        _8 = Eq(_6, _7);
+        StorageDead(_7);
+        StorageDead(_6);
+        goto -> bb3;
+    }
+
+    bb2: {
+        _9 = ((*_1).1: *mut T);
+        _10 = _9 as usize (Transmute);
+        _8 = Eq(_10, const 0_usize);
+        goto -> bb3;
+    }
+
+    bb3: {
+        switchInt(move _8) -> [0: bb4, otherwise: bb11];
+    }
+
+    bb4: {
+        StorageLive(_24);
+        StorageLive(_17);
+        StorageLive(_19);
+        StorageLive(_12);
+        switchInt(const <T as std::mem::SizedTypeProperties>::IS_ZST) -> [0: bb5, otherwise: bb9];
+    }
+
+    bb5: {
+        StorageLive(_11);
+        _11 = &raw mut ((*_1).1: *mut T);
+        _12 = _11 as *mut std::ptr::NonNull<T> (PtrToPtr);
+        StorageDead(_11);
+        StorageLive(_16);
+        _13 = (*_12);
+        switchInt(const <T as std::mem::SizedTypeProperties>::IS_ZST) -> [0: bb6, otherwise: bb7];
+    }
+
+    bb6: {
+        StorageLive(_15);
+        StorageLive(_14);
+        _14 = (_13.0: *const T);
+        _15 = Offset(move _14, const -1_isize);
+        StorageDead(_14);
+        _16 = NonNull::<T> { pointer: move _15 };
+        StorageDead(_15);
+        goto -> bb8;
+    }
+
+    bb7: {
+        _16 = _13;
+        goto -> bb8;
+    }
+
+    bb8: {
+        (*_12) = move _16;
+        StorageDead(_16);
+        _17 = (*_12);
+        goto -> bb10;
+    }
+
+    bb9: {
+        StorageLive(_18);
+        _18 = &raw mut ((*_1).1: *mut T);
+        _19 = _18 as *mut usize (PtrToPtr);
+        StorageDead(_18);
+        StorageLive(_21);
+        StorageLive(_20);
+        _20 = (*_19);
+        _21 = SubUnchecked(move _20, const 1_usize);
+        StorageDead(_20);
+        (*_19) = move _21;
+        StorageDead(_21);
+        _17 = ((*_1).0: std::ptr::NonNull<T>);
+        goto -> bb10;
+    }
+
+    bb10: {
+        StorageDead(_12);
+        StorageDead(_19);
+        StorageLive(_22);
+        _22 = _17;
+        StorageLive(_23);
+        _23 = (_22.0: *const T);
+        _24 = move _23 as *mut T (PtrToPtr);
+        StorageDead(_23);
+        StorageDead(_22);
+        _25 = &mut (*_24);
+        StorageDead(_17);
+        StorageDead(_24);
+        _0 = Option::<&mut T>::Some(_25);
+        goto -> bb12;
+    }
+
+    bb11: {
+        _0 = const {transmute(0x0000000000000000): Option<&mut T>};
+        goto -> bb12;
+    }
+
+    bb12: {
+        StorageDead(_8);
+        StorageDead(_25);
+        StorageDead(_4);
+        StorageDead(_9);
+        StorageDead(_10);
         return;
     }
 }
diff --git a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-unwind.mir
index dfe5e206fad..2df346c081c 100644
--- a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-unwind.mir
+++ b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-unwind.mir
@@ -3,12 +3,205 @@
 fn slice_iter_mut_next_back(_1: &mut std::slice::IterMut<'_, T>) -> Option<&mut T> {
     debug it => _1;
     let mut _0: std::option::Option<&mut T>;
+    scope 1 (inlined <std::slice::IterMut<'_, T> as DoubleEndedIterator>::next_back) {
+        let mut _2: *const *mut T;
+        let mut _3: *const std::ptr::NonNull<T>;
+        let mut _8: bool;
+        let mut _9: *mut T;
+        let mut _25: &mut T;
+        scope 2 {
+            let _4: std::ptr::NonNull<T>;
+            let _10: usize;
+            scope 3 {
+            }
+            scope 4 {
+                scope 8 (inlined <NonNull<T> as PartialEq>::eq) {
+                    let mut _5: std::ptr::NonNull<T>;
+                    scope 9 (inlined NonNull::<T>::as_ptr) {
+                        let mut _6: *const T;
+                    }
+                    scope 10 (inlined NonNull::<T>::as_ptr) {
+                        let mut _7: *const T;
+                    }
+                }
+            }
+            scope 5 (inlined std::ptr::mut_ptr::<impl *mut T>::addr) {
+                scope 6 (inlined std::ptr::mut_ptr::<impl *mut T>::cast::<()>) {
+                }
+            }
+            scope 7 (inlined std::ptr::const_ptr::<impl *const *mut T>::cast::<NonNull<T>>) {
+            }
+        }
+        scope 11 (inlined std::slice::IterMut::<'_, T>::next_back_unchecked) {
+            let mut _17: std::ptr::NonNull<T>;
+            scope 12 (inlined std::slice::IterMut::<'_, T>::pre_dec_end) {
+                let mut _11: *mut *mut T;
+                let mut _12: *mut std::ptr::NonNull<T>;
+                let mut _13: std::ptr::NonNull<T>;
+                let mut _16: std::ptr::NonNull<T>;
+                let mut _18: *mut *mut T;
+                let mut _19: *mut usize;
+                let mut _20: usize;
+                let mut _21: usize;
+                scope 13 {
+                    scope 14 {
+                    }
+                    scope 15 {
+                        scope 18 (inlined NonNull::<T>::sub) {
+                            scope 19 (inlined core::num::<impl isize>::unchecked_neg) {
+                                scope 20 (inlined core::ub_checks::check_language_ub) {
+                                    scope 21 (inlined core::ub_checks::check_language_ub::runtime) {
+                                    }
+                                }
+                            }
+                            scope 22 (inlined NonNull::<T>::offset) {
+                                let mut _14: *const T;
+                                let mut _15: *const T;
+                            }
+                        }
+                    }
+                    scope 16 (inlined std::ptr::mut_ptr::<impl *mut *mut T>::cast::<usize>) {
+                    }
+                    scope 17 (inlined std::ptr::mut_ptr::<impl *mut *mut T>::cast::<NonNull<T>>) {
+                    }
+                }
+            }
+            scope 23 (inlined NonNull::<T>::as_mut::<'_>) {
+                let mut _22: std::ptr::NonNull<T>;
+                let mut _24: *mut T;
+                scope 24 (inlined NonNull::<T>::as_ptr) {
+                    let mut _23: *const T;
+                }
+            }
+        }
+    }
 
     bb0: {
-        _0 = <std::slice::IterMut<'_, T> as DoubleEndedIterator>::next_back(move _1) -> [return: bb1, unwind continue];
+        StorageLive(_10);
+        StorageLive(_9);
+        StorageLive(_4);
+        StorageLive(_25);
+        StorageLive(_8);
+        switchInt(const <T as std::mem::SizedTypeProperties>::IS_ZST) -> [0: bb1, otherwise: bb2];
     }
 
     bb1: {
+        StorageLive(_3);
+        StorageLive(_2);
+        _2 = &raw const ((*_1).1: *mut T);
+        _3 = _2 as *const std::ptr::NonNull<T> (PtrToPtr);
+        StorageDead(_2);
+        _4 = (*_3);
+        StorageDead(_3);
+        StorageLive(_6);
+        StorageLive(_7);
+        StorageLive(_5);
+        _5 = ((*_1).0: std::ptr::NonNull<T>);
+        _6 = (_5.0: *const T);
+        StorageDead(_5);
+        _7 = (_4.0: *const T);
+        _8 = Eq(_6, _7);
+        StorageDead(_7);
+        StorageDead(_6);
+        goto -> bb3;
+    }
+
+    bb2: {
+        _9 = ((*_1).1: *mut T);
+        _10 = _9 as usize (Transmute);
+        _8 = Eq(_10, const 0_usize);
+        goto -> bb3;
+    }
+
+    bb3: {
+        switchInt(move _8) -> [0: bb4, otherwise: bb11];
+    }
+
+    bb4: {
+        StorageLive(_24);
+        StorageLive(_17);
+        StorageLive(_19);
+        StorageLive(_12);
+        switchInt(const <T as std::mem::SizedTypeProperties>::IS_ZST) -> [0: bb5, otherwise: bb9];
+    }
+
+    bb5: {
+        StorageLive(_11);
+        _11 = &raw mut ((*_1).1: *mut T);
+        _12 = _11 as *mut std::ptr::NonNull<T> (PtrToPtr);
+        StorageDead(_11);
+        StorageLive(_16);
+        _13 = (*_12);
+        switchInt(const <T as std::mem::SizedTypeProperties>::IS_ZST) -> [0: bb6, otherwise: bb7];
+    }
+
+    bb6: {
+        StorageLive(_15);
+        StorageLive(_14);
+        _14 = (_13.0: *const T);
+        _15 = Offset(move _14, const -1_isize);
+        StorageDead(_14);
+        _16 = NonNull::<T> { pointer: move _15 };
+        StorageDead(_15);
+        goto -> bb8;
+    }
+
+    bb7: {
+        _16 = _13;
+        goto -> bb8;
+    }
+
+    bb8: {
+        (*_12) = move _16;
+        StorageDead(_16);
+        _17 = (*_12);
+        goto -> bb10;
+    }
+
+    bb9: {
+        StorageLive(_18);
+        _18 = &raw mut ((*_1).1: *mut T);
+        _19 = _18 as *mut usize (PtrToPtr);
+        StorageDead(_18);
+        StorageLive(_21);
+        StorageLive(_20);
+        _20 = (*_19);
+        _21 = SubUnchecked(move _20, const 1_usize);
+        StorageDead(_20);
+        (*_19) = move _21;
+        StorageDead(_21);
+        _17 = ((*_1).0: std::ptr::NonNull<T>);
+        goto -> bb10;
+    }
+
+    bb10: {
+        StorageDead(_12);
+        StorageDead(_19);
+        StorageLive(_22);
+        _22 = _17;
+        StorageLive(_23);
+        _23 = (_22.0: *const T);
+        _24 = move _23 as *mut T (PtrToPtr);
+        StorageDead(_23);
+        StorageDead(_22);
+        _25 = &mut (*_24);
+        StorageDead(_17);
+        StorageDead(_24);
+        _0 = Option::<&mut T>::Some(_25);
+        goto -> bb12;
+    }
+
+    bb11: {
+        _0 = const {transmute(0x0000000000000000): Option<&mut T>};
+        goto -> bb12;
+    }
+
+    bb12: {
+        StorageDead(_8);
+        StorageDead(_25);
+        StorageDead(_4);
+        StorageDead(_9);
+        StorageDead(_10);
         return;
     }
 }
diff --git a/tests/run-make/invalid-so/Makefile b/tests/run-make/invalid-so/Makefile
deleted file mode 100644
index e36c7040bc6..00000000000
--- a/tests/run-make/invalid-so/Makefile
+++ /dev/null
@@ -1,7 +0,0 @@
-include ../tools.mk
-
-DYLIB_NAME := $(shell echo | $(RUSTC) --crate-name foo --crate-type dylib --print file-names -)
-
-all:
-	echo >> $(TMPDIR)/$(DYLIB_NAME)
-	$(RUSTC) --crate-type lib --extern foo=$(TMPDIR)/$(DYLIB_NAME) bar.rs 2>&1 | $(CGREP) 'invalid metadata files for crate `foo`'
diff --git a/tests/run-make/invalid-so/rmake.rs b/tests/run-make/invalid-so/rmake.rs
new file mode 100644
index 00000000000..5cfda05334e
--- /dev/null
+++ b/tests/run-make/invalid-so/rmake.rs
@@ -0,0 +1,17 @@
+// When a fake library was given to the compiler, it would
+// result in an obscure and unhelpful error message. This test
+// creates a false "foo" dylib, and checks that the standard error
+// explains that the file exists, but that its metadata is incorrect.
+// See https://github.com/rust-lang/rust/pull/88368
+
+use run_make_support::{dynamic_lib_name, fs_wrapper, rustc};
+
+fn main() {
+    fs_wrapper::create_file(dynamic_lib_name("foo"));
+    rustc()
+        .crate_type("lib")
+        .extern_("foo", dynamic_lib_name("foo"))
+        .input("bar.rs")
+        .run_fail()
+        .assert_stderr_contains("invalid metadata files for crate `foo`");
+}
diff --git a/tests/run-make/issue-20626/Makefile b/tests/run-make/issue-20626/Makefile
deleted file mode 100644
index 63eee910a9c..00000000000
--- a/tests/run-make/issue-20626/Makefile
+++ /dev/null
@@ -1,9 +0,0 @@
-# ignore-cross-compile
-include ../tools.mk
-
-# Test output to be four
-# The original error only occurred when printing, not when comparing using assert!
-
-all:
-	$(RUSTC) foo.rs -O
-	[ `$(call RUN,foo)` = "4" ]
diff --git a/tests/run-make/lto-empty/Makefile b/tests/run-make/lto-empty/Makefile
deleted file mode 100644
index 1b795c4b738..00000000000
--- a/tests/run-make/lto-empty/Makefile
+++ /dev/null
@@ -1,13 +0,0 @@
-# ignore-cross-compile
-include ../tools.mk
-
-all: cdylib-fat cdylib-thin
-
-cdylib-fat:
-	$(RUSTC) lib.rs -C lto=fat -C opt-level=3 -C incremental=$(TMPDIR)/inc-fat
-	$(RUSTC) lib.rs -C lto=fat -C opt-level=3 -C incremental=$(TMPDIR)/inc-fat
-
-cdylib-thin:
-	$(RUSTC) lib.rs -C lto=thin -C opt-level=3 -C incremental=$(TMPDIR)/inc-thin
-	$(RUSTC) lib.rs -C lto=thin -C opt-level=3 -C incremental=$(TMPDIR)/inc-thin
-
diff --git a/tests/run-make/lto-empty/rmake.rs b/tests/run-make/lto-empty/rmake.rs
new file mode 100644
index 00000000000..7146d6e10ef
--- /dev/null
+++ b/tests/run-make/lto-empty/rmake.rs
@@ -0,0 +1,17 @@
+// Compiling Rust code twice in a row with "fat" link-time-optimizations used to cause
+// an internal compiler error (ICE). This was due to how the compiler would cache some modules
+// to make subsequent compilations faster, at least one of which was required for LTO to link
+// into. After this was patched in #63956, this test checks that the bug does not make
+// a resurgence.
+// See https://github.com/rust-lang/rust/issues/63349
+
+//@ ignore-cross-compile
+
+use run_make_support::rustc;
+
+fn main() {
+    rustc().input("lib.rs").arg("-Clto=fat").opt_level("3").incremental("inc-fat").run();
+    rustc().input("lib.rs").arg("-Clto=fat").opt_level("3").incremental("inc-fat").run();
+    rustc().input("lib.rs").arg("-Clto=thin").opt_level("3").incremental("inc-thin").run();
+    rustc().input("lib.rs").arg("-Clto=thin").opt_level("3").incremental("inc-thin").run();
+}
diff --git a/tests/run-make/print-native-static-libs/bar.rs b/tests/run-make/print-native-static-libs/bar.rs
index cd9c1c453e5..74c76da6938 100644
--- a/tests/run-make/print-native-static-libs/bar.rs
+++ b/tests/run-make/print-native-static-libs/bar.rs
@@ -17,3 +17,9 @@ extern "C" {
 extern "C" {
     fn g_free2(p: *mut ());
 }
+
+#[cfg(windows)]
+#[link(name = "glib-2.0", kind = "raw-dylib")]
+extern "C" {
+    fn g_free3(p: *mut ());
+}
diff --git a/tests/run-make/issue-20626/foo.rs b/tests/run-make/raw-fn-pointer-opt-undefined-behavior/foo.rs
index 1007686d9fe..1007686d9fe 100644
--- a/tests/run-make/issue-20626/foo.rs
+++ b/tests/run-make/raw-fn-pointer-opt-undefined-behavior/foo.rs
diff --git a/tests/run-make/raw-fn-pointer-opt-undefined-behavior/rmake.rs b/tests/run-make/raw-fn-pointer-opt-undefined-behavior/rmake.rs
new file mode 100644
index 00000000000..61cf559a8fe
--- /dev/null
+++ b/tests/run-make/raw-fn-pointer-opt-undefined-behavior/rmake.rs
@@ -0,0 +1,16 @@
+// Despite the absence of any unsafe Rust code, foo.rs in this test would,
+// because of the raw function pointer,
+// cause undefined behavior and fail to print the expected result, "4" -
+// only when activating optimizations (opt-level 2). This test checks
+// that this bug does not make a resurgence.
+// Note that the bug cannot be observed in an assert_eq!, only in the stdout.
+// See https://github.com/rust-lang/rust/issues/20626
+
+//@ ignore-cross-compile
+
+use run_make_support::{run, rustc};
+
+fn main() {
+    rustc().input("foo.rs").opt().run();
+    run("foo").assert_stdout_equals("4");
+}
diff --git a/tests/ui/array-slice-vec/vec-res-add.stderr b/tests/ui/array-slice-vec/vec-res-add.stderr
index cf5796f7e4a..34fd69426a8 100644
--- a/tests/ui/array-slice-vec/vec-res-add.stderr
+++ b/tests/ui/array-slice-vec/vec-res-add.stderr
@@ -5,6 +5,11 @@ LL |     let k = i + j;
    |             - ^ - Vec<R>
    |             |
    |             Vec<R>
+   |
+note: the foreign item type `Vec<R>` doesn't implement `Add`
+  --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
+   |
+   = note: not implement `Add`
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/async-await/suggest-missing-await.rs b/tests/ui/async-await/suggest-missing-await.rs
index 96996af0bd2..0bd67cec335 100644
--- a/tests/ui/async-await/suggest-missing-await.rs
+++ b/tests/ui/async-await/suggest-missing-await.rs
@@ -71,4 +71,11 @@ async fn suggest_await_in_generic_pattern() {
     }
 }
 
+// Issue #126903
+async fn do_async() {}
+fn dont_suggest_awaiting_closure_patterns() {
+    Some(do_async()).map(|()| {});
+    //~^ ERROR mismatched types [E0308]
+}
+
 fn main() {}
diff --git a/tests/ui/async-await/suggest-missing-await.stderr b/tests/ui/async-await/suggest-missing-await.stderr
index f0ec34a6a55..f9db86ea40a 100644
--- a/tests/ui/async-await/suggest-missing-await.stderr
+++ b/tests/ui/async-await/suggest-missing-await.stderr
@@ -133,6 +133,18 @@ help: consider `await`ing on the `Future`
 LL |     match dummy_result().await {
    |                         ++++++
 
-error: aborting due to 7 previous errors
+error[E0308]: mismatched types
+  --> $DIR/suggest-missing-await.rs:77:27
+   |
+LL |     Some(do_async()).map(|()| {});
+   |                           ^^
+   |                           |
+   |                           expected future, found `()`
+   |                           expected due to this
+   |
+   = note: expected opaque type `impl Future<Output = ()>`
+                found unit type `()`
+
+error: aborting due to 8 previous errors
 
 For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/autoderef-full-lval.stderr b/tests/ui/autoderef-full-lval.stderr
index 9921ce7c154..d90238a7fb2 100644
--- a/tests/ui/autoderef-full-lval.stderr
+++ b/tests/ui/autoderef-full-lval.stderr
@@ -5,6 +5,12 @@ LL |     let z: isize = a.x + b.y;
    |                    --- ^ --- Box<isize>
    |                    |
    |                    Box<isize>
+   |
+note: the foreign item type `Box<isize>` doesn't implement `Add`
+  --> $SRC_DIR/alloc/src/boxed.rs:LL:COL
+  ::: $SRC_DIR/alloc/src/boxed.rs:LL:COL
+   |
+   = note: not implement `Add`
 
 error[E0369]: cannot add `Box<isize>` to `Box<isize>`
   --> $DIR/autoderef-full-lval.rs:21:33
@@ -13,6 +19,12 @@ LL |     let answer: isize = forty.a + two.a;
    |                         ------- ^ ----- Box<isize>
    |                         |
    |                         Box<isize>
+   |
+note: the foreign item type `Box<isize>` doesn't implement `Add`
+  --> $SRC_DIR/alloc/src/boxed.rs:LL:COL
+  ::: $SRC_DIR/alloc/src/boxed.rs:LL:COL
+   |
+   = note: not implement `Add`
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/binop/binary-op-not-allowed-issue-125631.rs b/tests/ui/binop/binary-op-not-allowed-issue-125631.rs
new file mode 100644
index 00000000000..8827bbb003b
--- /dev/null
+++ b/tests/ui/binop/binary-op-not-allowed-issue-125631.rs
@@ -0,0 +1,16 @@
+use std::io::{Error, ErrorKind};
+use std::thread;
+
+struct T1;
+struct T2;
+
+fn main() {
+    (Error::new(ErrorKind::Other, "1"), T1, 1) == (Error::new(ErrorKind::Other, "1"), T1, 2);
+    //~^ERROR binary operation `==` cannot be applied to type
+    (Error::new(ErrorKind::Other, "2"), thread::current())
+        == (Error::new(ErrorKind::Other, "2"), thread::current());
+    //~^ERROR binary operation `==` cannot be applied to type
+    (Error::new(ErrorKind::Other, "4"), thread::current(), T1, T2)
+        == (Error::new(ErrorKind::Other, "4"), thread::current(), T1, T2);
+    //~^ERROR binary operation `==` cannot be applied to type
+}
diff --git a/tests/ui/binop/binary-op-not-allowed-issue-125631.stderr b/tests/ui/binop/binary-op-not-allowed-issue-125631.stderr
new file mode 100644
index 00000000000..1cf75bbc1a5
--- /dev/null
+++ b/tests/ui/binop/binary-op-not-allowed-issue-125631.stderr
@@ -0,0 +1,75 @@
+error[E0369]: binary operation `==` cannot be applied to type `(std::io::Error, T1, {integer})`
+  --> $DIR/binary-op-not-allowed-issue-125631.rs:8:48
+   |
+LL |     (Error::new(ErrorKind::Other, "1"), T1, 1) == (Error::new(ErrorKind::Other, "1"), T1, 2);
+   |     ------------------------------------------ ^^ ------------------------------------------ (std::io::Error, T1, {integer})
+   |     |
+   |     (std::io::Error, T1, {integer})
+   |
+note: an implementation of `PartialEq` might be missing for `T1`
+  --> $DIR/binary-op-not-allowed-issue-125631.rs:4:1
+   |
+LL | struct T1;
+   | ^^^^^^^^^ must implement `PartialEq`
+note: the foreign item type `std::io::Error` doesn't implement `PartialEq`
+  --> $SRC_DIR/std/src/io/error.rs:LL:COL
+   |
+   = note: not implement `PartialEq`
+help: consider annotating `T1` with `#[derive(PartialEq)]`
+   |
+LL + #[derive(PartialEq)]
+LL | struct T1;
+   |
+
+error[E0369]: binary operation `==` cannot be applied to type `(std::io::Error, Thread)`
+  --> $DIR/binary-op-not-allowed-issue-125631.rs:11:9
+   |
+LL |     (Error::new(ErrorKind::Other, "2"), thread::current())
+   |     ------------------------------------------------------ (std::io::Error, Thread)
+LL |         == (Error::new(ErrorKind::Other, "2"), thread::current());
+   |         ^^ ------------------------------------------------------ (std::io::Error, Thread)
+   |
+note: the foreign item types don't implement required traits for this operation to be valid
+  --> $SRC_DIR/std/src/io/error.rs:LL:COL
+   |
+   = note: not implement `PartialEq`
+  --> $SRC_DIR/std/src/thread/mod.rs:LL:COL
+   |
+   = note: not implement `PartialEq`
+
+error[E0369]: binary operation `==` cannot be applied to type `(std::io::Error, Thread, T1, T2)`
+  --> $DIR/binary-op-not-allowed-issue-125631.rs:14:9
+   |
+LL |     (Error::new(ErrorKind::Other, "4"), thread::current(), T1, T2)
+   |     -------------------------------------------------------------- (std::io::Error, Thread, T1, T2)
+LL |         == (Error::new(ErrorKind::Other, "4"), thread::current(), T1, T2);
+   |         ^^ -------------------------------------------------------------- (std::io::Error, Thread, T1, T2)
+   |
+note: the following types would have to `impl` their required traits for this operation to be valid
+  --> $DIR/binary-op-not-allowed-issue-125631.rs:4:1
+   |
+LL | struct T1;
+   | ^^^^^^^^^ must implement `PartialEq`
+LL | struct T2;
+   | ^^^^^^^^^ must implement `PartialEq`
+note: the foreign item types don't implement required traits for this operation to be valid
+  --> $SRC_DIR/std/src/io/error.rs:LL:COL
+   |
+   = note: not implement `PartialEq`
+  --> $SRC_DIR/std/src/thread/mod.rs:LL:COL
+   |
+   = note: not implement `PartialEq`
+help: consider annotating `T1` with `#[derive(PartialEq)]`
+   |
+LL + #[derive(PartialEq)]
+LL | struct T1;
+   |
+help: consider annotating `T2` with `#[derive(PartialEq)]`
+   |
+LL + #[derive(PartialEq)]
+LL | struct T2;
+   |
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0369`.
diff --git a/tests/ui/binop/binop-bitxor-str.stderr b/tests/ui/binop/binop-bitxor-str.stderr
index 20b1ecc5a93..9d9ec6c5af6 100644
--- a/tests/ui/binop/binop-bitxor-str.stderr
+++ b/tests/ui/binop/binop-bitxor-str.stderr
@@ -5,6 +5,11 @@ LL | fn main() { let x = "a".to_string() ^ "b".to_string(); }
    |                     --------------- ^ --------------- String
    |                     |
    |                     String
+   |
+note: the foreign item type `String` doesn't implement `BitXor`
+  --> $SRC_DIR/alloc/src/string.rs:LL:COL
+   |
+   = note: not implement `BitXor`
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/const-generics/cross_crate_complex.rs b/tests/ui/const-generics/cross_crate_complex.rs
index d13b69aa0cf..b44d889f5e9 100644
--- a/tests/ui/const-generics/cross_crate_complex.rs
+++ b/tests/ui/const-generics/cross_crate_complex.rs
@@ -11,6 +11,7 @@ async fn foo() {
     async_in_foo(async_out_foo::<4>().await).await;
 }
 
+#[allow(dead_code)]
 struct Faz<const N: usize>;
 
 impl<const N: usize> Foo<N> for Faz<N> {}
diff --git a/tests/ui/delegation/explicit-paths.stderr b/tests/ui/delegation/explicit-paths.stderr
index 30891c94c0e..d33c5da4377 100644
--- a/tests/ui/delegation/explicit-paths.stderr
+++ b/tests/ui/delegation/explicit-paths.stderr
@@ -110,10 +110,10 @@ error[E0308]: mismatched types
   --> $DIR/explicit-paths.rs:78:30
    |
 LL |         reuse <S2 as Trait>::foo1;
-   |               ---------------^^^^
-   |               |              |
-   |               |              expected `&S2`, found `&S`
-   |               arguments to this function are incorrect
+   |                              ^^^^
+   |                              |
+   |                              expected `&S2`, found `&S`
+   |                              arguments to this function are incorrect
    |
    = note: expected reference `&S2`
               found reference `&S`
diff --git a/tests/ui/deriving/deriving-default-enum.rs b/tests/ui/deriving/deriving-default-enum.rs
index 96eba258c97..6b59f39a67d 100644
--- a/tests/ui/deriving/deriving-default-enum.rs
+++ b/tests/ui/deriving/deriving-default-enum.rs
@@ -22,6 +22,6 @@ enum MyOption<T> {
 }
 
 fn main() {
-    assert_eq!(Foo::default(), Foo::Alpha);
+    assert!(matches!(Foo::default(), Foo::Alpha));
     assert!(matches!(MyOption::<NotDefault>::default(), MyOption::None));
 }
diff --git a/tests/ui/error-codes/E0067.stderr b/tests/ui/error-codes/E0067.stderr
index ec0358cb7df..71b72080544 100644
--- a/tests/ui/error-codes/E0067.stderr
+++ b/tests/ui/error-codes/E0067.stderr
@@ -5,6 +5,12 @@ LL |     LinkedList::new() += 1;
    |     -----------------^^^^^
    |     |
    |     cannot use `+=` on type `LinkedList<_>`
+   |
+note: the foreign item type `LinkedList<_>` doesn't implement `AddAssign<{integer}>`
+  --> $SRC_DIR/alloc/src/collections/linked_list.rs:LL:COL
+  ::: $SRC_DIR/alloc/src/collections/linked_list.rs:LL:COL
+   |
+   = note: not implement `AddAssign<{integer}>`
 
 error[E0067]: invalid left-hand side of assignment
   --> $DIR/E0067.rs:4:23
diff --git a/tests/ui/generic-associated-types/missing-bounds.fixed b/tests/ui/generic-associated-types/missing-bounds.fixed
index 703d3c1e0fb..ff69016d862 100644
--- a/tests/ui/generic-associated-types/missing-bounds.fixed
+++ b/tests/ui/generic-associated-types/missing-bounds.fixed
@@ -2,6 +2,7 @@
 
 use std::ops::Add;
 
+#[allow(dead_code)]
 struct A<B>(B);
 
 impl<B> Add for A<B> where B: Add<Output = B> {
@@ -12,6 +13,7 @@ impl<B> Add for A<B> where B: Add<Output = B> {
     }
 }
 
+#[allow(dead_code)]
 struct C<B>(B);
 
 impl<B: Add<Output = B>> Add for C<B> {
@@ -22,6 +24,7 @@ impl<B: Add<Output = B>> Add for C<B> {
     }
 }
 
+#[allow(dead_code)]
 struct D<B>(B);
 
 impl<B: std::ops::Add<Output = B>> Add for D<B> {
@@ -32,6 +35,7 @@ impl<B: std::ops::Add<Output = B>> Add for D<B> {
     }
 }
 
+#[allow(dead_code)]
 struct E<B>(B);
 
 impl<B: Add<Output = B>> Add for E<B> where B: Add<Output = B> {
diff --git a/tests/ui/generic-associated-types/missing-bounds.rs b/tests/ui/generic-associated-types/missing-bounds.rs
index f40b4228873..1f83356c2fa 100644
--- a/tests/ui/generic-associated-types/missing-bounds.rs
+++ b/tests/ui/generic-associated-types/missing-bounds.rs
@@ -2,6 +2,7 @@
 
 use std::ops::Add;
 
+#[allow(dead_code)]
 struct A<B>(B);
 
 impl<B> Add for A<B> where B: Add {
@@ -12,6 +13,7 @@ impl<B> Add for A<B> where B: Add {
     }
 }
 
+#[allow(dead_code)]
 struct C<B>(B);
 
 impl<B: Add> Add for C<B> {
@@ -22,6 +24,7 @@ impl<B: Add> Add for C<B> {
     }
 }
 
+#[allow(dead_code)]
 struct D<B>(B);
 
 impl<B> Add for D<B> {
@@ -32,6 +35,7 @@ impl<B> Add for D<B> {
     }
 }
 
+#[allow(dead_code)]
 struct E<B>(B);
 
 impl<B: Add> Add for E<B> where <B as Add>::Output = B {
diff --git a/tests/ui/generic-associated-types/missing-bounds.stderr b/tests/ui/generic-associated-types/missing-bounds.stderr
index 1d7d80d1b07..0f0dc24c06c 100644
--- a/tests/ui/generic-associated-types/missing-bounds.stderr
+++ b/tests/ui/generic-associated-types/missing-bounds.stderr
@@ -1,5 +1,5 @@
 error: equality constraints are not yet supported in `where` clauses
-  --> $DIR/missing-bounds.rs:37:33
+  --> $DIR/missing-bounds.rs:41:33
    |
 LL | impl<B: Add> Add for E<B> where <B as Add>::Output = B {
    |                                 ^^^^^^^^^^^^^^^^^^^^^^ not supported
@@ -11,7 +11,7 @@ LL | impl<B: Add> Add for E<B> where B: Add<Output = B> {
    |                                 ~~~~~~~~~~~~~~~~~~
 
 error[E0308]: mismatched types
-  --> $DIR/missing-bounds.rs:11:11
+  --> $DIR/missing-bounds.rs:12:11
    |
 LL | impl<B> Add for A<B> where B: Add {
    |      - expected this type parameter
@@ -24,14 +24,14 @@ LL |         A(self.0 + rhs.0)
    = note: expected type parameter `B`
              found associated type `<B as Add>::Output`
 help: the type constructed contains `<B as Add>::Output` due to the type of the argument passed
-  --> $DIR/missing-bounds.rs:11:9
+  --> $DIR/missing-bounds.rs:12:9
    |
 LL |         A(self.0 + rhs.0)
    |         ^^--------------^
    |           |
    |           this argument influences the type of `A`
 note: tuple struct defined here
-  --> $DIR/missing-bounds.rs:5:8
+  --> $DIR/missing-bounds.rs:6:8
    |
 LL | struct A<B>(B);
    |        ^
@@ -41,7 +41,7 @@ LL | impl<B> Add for A<B> where B: Add<Output = B> {
    |                                  ++++++++++++
 
 error[E0308]: mismatched types
-  --> $DIR/missing-bounds.rs:21:14
+  --> $DIR/missing-bounds.rs:23:14
    |
 LL | impl<B: Add> Add for C<B> {
    |      - expected this type parameter
@@ -54,7 +54,7 @@ LL |         Self(self.0 + rhs.0)
    = note: expected type parameter `B`
              found associated type `<B as Add>::Output`
 note: tuple struct defined here
-  --> $DIR/missing-bounds.rs:15:8
+  --> $DIR/missing-bounds.rs:17:8
    |
 LL | struct C<B>(B);
    |        ^
@@ -64,7 +64,7 @@ LL | impl<B: Add<Output = B>> Add for C<B> {
    |            ++++++++++++
 
 error[E0369]: cannot add `B` to `B`
-  --> $DIR/missing-bounds.rs:31:21
+  --> $DIR/missing-bounds.rs:34:21
    |
 LL |         Self(self.0 + rhs.0)
    |              ------ ^ ----- B
@@ -77,7 +77,7 @@ LL | impl<B: std::ops::Add<Output = B>> Add for D<B> {
    |       +++++++++++++++++++++++++++
 
 error[E0308]: mismatched types
-  --> $DIR/missing-bounds.rs:42:14
+  --> $DIR/missing-bounds.rs:46:14
    |
 LL | impl<B: Add> Add for E<B> where <B as Add>::Output = B {
    |      - expected this type parameter
@@ -90,7 +90,7 @@ LL |         Self(self.0 + rhs.0)
    = note: expected type parameter `B`
              found associated type `<B as Add>::Output`
 note: tuple struct defined here
-  --> $DIR/missing-bounds.rs:35:8
+  --> $DIR/missing-bounds.rs:39:8
    |
 LL | struct E<B>(B);
    |        ^
diff --git a/tests/ui/impl-trait/in-trait/refine-resolution-errors.rs b/tests/ui/impl-trait/in-trait/refine-resolution-errors.rs
new file mode 100644
index 00000000000..a9936c7bc3f
--- /dev/null
+++ b/tests/ui/impl-trait/in-trait/refine-resolution-errors.rs
@@ -0,0 +1,23 @@
+// This is a non-regression test for issue #126670 where RPITIT refinement checking encountered
+// errors during resolution and ICEd.
+
+//@ edition: 2018
+
+pub trait Mirror {
+    type Assoc;
+}
+impl<T: ?Sized> Mirror for () {
+    //~^ ERROR the type parameter `T` is not constrained
+    type Assoc = T;
+}
+
+pub trait First {
+    async fn first() -> <() as Mirror>::Assoc;
+    //~^ ERROR type annotations needed
+}
+
+impl First for () {
+    async fn first() {}
+}
+
+fn main() {}
diff --git a/tests/ui/impl-trait/in-trait/refine-resolution-errors.stderr b/tests/ui/impl-trait/in-trait/refine-resolution-errors.stderr
new file mode 100644
index 00000000000..0f5573dda04
--- /dev/null
+++ b/tests/ui/impl-trait/in-trait/refine-resolution-errors.stderr
@@ -0,0 +1,16 @@
+error[E0207]: the type parameter `T` is not constrained by the impl trait, self type, or predicates
+  --> $DIR/refine-resolution-errors.rs:9:6
+   |
+LL | impl<T: ?Sized> Mirror for () {
+   |      ^ unconstrained type parameter
+
+error[E0282]: type annotations needed
+  --> $DIR/refine-resolution-errors.rs:15:5
+   |
+LL |     async fn first() -> <() as Mirror>::Assoc;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0207, E0282.
+For more information about an error, try `rustc --explain E0207`.
diff --git a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.rs b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.rs
index 08014985783..0028a45cbf3 100644
--- a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.rs
+++ b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.rs
@@ -6,6 +6,7 @@ fn type_param<T>() -> impl Sized + use<> {}
 trait Foo {
     fn bar() -> impl Sized + use<>;
     //~^ ERROR `impl Trait` must mention the `Self` type of the trait
+    //~| ERROR `use<...>` precise capturing syntax is currently not allowed in return-position `impl Trait` in traits
 }
 
 fn main() {}
diff --git a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.stderr b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.stderr
index 93b44a0c18c..89bd4df4431 100644
--- a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.stderr
+++ b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.stderr
@@ -1,3 +1,11 @@
+error: `use<...>` precise capturing syntax is currently not allowed in return-position `impl Trait` in traits
+  --> $DIR/forgot-to-capture-type.rs:7:30
+   |
+LL |     fn bar() -> impl Sized + use<>;
+   |                              ^^^^^
+   |
+   = note: currently, return-position `impl Trait` in traits and trait implementations capture all lifetimes in scope
+
 error: `impl Trait` must mention all type parameters in scope in `use<...>`
   --> $DIR/forgot-to-capture-type.rs:3:23
    |
@@ -18,5 +26,5 @@ LL |     fn bar() -> impl Sized + use<>;
    |
    = note: currently, all type parameters are required to be mentioned in the precise captures list
 
-error: aborting due to 2 previous errors
+error: aborting due to 3 previous errors
 
diff --git a/tests/ui/impl-trait/precise-capturing/redundant.normal.stderr b/tests/ui/impl-trait/precise-capturing/redundant.normal.stderr
new file mode 100644
index 00000000000..44bc9f7daad
--- /dev/null
+++ b/tests/ui/impl-trait/precise-capturing/redundant.normal.stderr
@@ -0,0 +1,20 @@
+warning: all possible in-scope parameters are already captured, so `use<...>` syntax is redundant
+  --> $DIR/redundant.rs:7:19
+   |
+LL | fn hello<'a>() -> impl Sized + use<'a> {}
+   |                   ^^^^^^^^^^^^^-------
+   |                                |
+   |                                help: remove the `use<...>` syntax
+   |
+   = note: `#[warn(impl_trait_redundant_captures)]` on by default
+
+warning: all possible in-scope parameters are already captured, so `use<...>` syntax is redundant
+  --> $DIR/redundant.rs:12:27
+   |
+LL |     fn inherent(&self) -> impl Sized + use<'_> {}
+   |                           ^^^^^^^^^^^^^-------
+   |                                        |
+   |                                        help: remove the `use<...>` syntax
+
+warning: 2 warnings emitted
+
diff --git a/tests/ui/impl-trait/precise-capturing/redundant.rpitit.stderr b/tests/ui/impl-trait/precise-capturing/redundant.rpitit.stderr
new file mode 100644
index 00000000000..9aa73353126
--- /dev/null
+++ b/tests/ui/impl-trait/precise-capturing/redundant.rpitit.stderr
@@ -0,0 +1,18 @@
+error: `use<...>` precise capturing syntax is currently not allowed in return-position `impl Trait` in traits
+  --> $DIR/redundant.rs:18:35
+   |
+LL |     fn in_trait() -> impl Sized + use<'a, Self>;
+   |                                   ^^^^^^^^^^^^^
+   |
+   = note: currently, return-position `impl Trait` in traits and trait implementations capture all lifetimes in scope
+
+error: `use<...>` precise capturing syntax is currently not allowed in return-position `impl Trait` in traits
+  --> $DIR/redundant.rs:23:35
+   |
+LL |     fn in_trait() -> impl Sized + use<'a> {}
+   |                                   ^^^^^^^
+   |
+   = note: currently, return-position `impl Trait` in traits and trait implementations capture all lifetimes in scope
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/impl-trait/precise-capturing/redundant.rs b/tests/ui/impl-trait/precise-capturing/redundant.rs
index 99c128fdc48..ef4f05bd7e4 100644
--- a/tests/ui/impl-trait/precise-capturing/redundant.rs
+++ b/tests/ui/impl-trait/precise-capturing/redundant.rs
@@ -1,24 +1,27 @@
 //@ compile-flags: -Zunstable-options --edition=2024
-//@ check-pass
+//@ revisions: normal rpitit
+//@[normal] check-pass
 
 #![feature(precise_capturing)]
 
 fn hello<'a>() -> impl Sized + use<'a> {}
-//~^ WARN all possible in-scope parameters are already captured
+//[normal]~^ WARN all possible in-scope parameters are already captured
 
 struct Inherent;
 impl Inherent {
     fn inherent(&self) -> impl Sized + use<'_> {}
-    //~^ WARN all possible in-scope parameters are already captured
+    //[normal]~^ WARN all possible in-scope parameters are already captured
 }
 
+#[cfg(rpitit)]
 trait Test<'a> {
     fn in_trait() -> impl Sized + use<'a, Self>;
-    //~^ WARN all possible in-scope parameters are already captured
+    //[rpitit]~^ ERROR `use<...>` precise capturing syntax is currently not allowed in return-position `impl Trait` in traits
 }
+#[cfg(rpitit)]
 impl<'a> Test<'a> for () {
     fn in_trait() -> impl Sized + use<'a> {}
-    //~^ WARN all possible in-scope parameters are already captured
+    //[rpitit]~^ ERROR `use<...>` precise capturing syntax is currently not allowed in return-position `impl Trait` in traits
 }
 
 fn main() {}
diff --git a/tests/ui/impl-trait/precise-capturing/redundant.stderr b/tests/ui/impl-trait/precise-capturing/redundant.stderr
deleted file mode 100644
index 274d9d2375f..00000000000
--- a/tests/ui/impl-trait/precise-capturing/redundant.stderr
+++ /dev/null
@@ -1,36 +0,0 @@
-warning: all possible in-scope parameters are already captured, so `use<...>` syntax is redundant
-  --> $DIR/redundant.rs:6:19
-   |
-LL | fn hello<'a>() -> impl Sized + use<'a> {}
-   |                   ^^^^^^^^^^^^^-------
-   |                                |
-   |                                help: remove the `use<...>` syntax
-   |
-   = note: `#[warn(impl_trait_redundant_captures)]` on by default
-
-warning: all possible in-scope parameters are already captured, so `use<...>` syntax is redundant
-  --> $DIR/redundant.rs:11:27
-   |
-LL |     fn inherent(&self) -> impl Sized + use<'_> {}
-   |                           ^^^^^^^^^^^^^-------
-   |                                        |
-   |                                        help: remove the `use<...>` syntax
-
-warning: all possible in-scope parameters are already captured, so `use<...>` syntax is redundant
-  --> $DIR/redundant.rs:16:22
-   |
-LL |     fn in_trait() -> impl Sized + use<'a, Self>;
-   |                      ^^^^^^^^^^^^^-------------
-   |                                   |
-   |                                   help: remove the `use<...>` syntax
-
-warning: all possible in-scope parameters are already captured, so `use<...>` syntax is redundant
-  --> $DIR/redundant.rs:20:22
-   |
-LL |     fn in_trait() -> impl Sized + use<'a> {}
-   |                      ^^^^^^^^^^^^^-------
-   |                                   |
-   |                                   help: remove the `use<...>` syntax
-
-warning: 4 warnings emitted
-
diff --git a/tests/ui/impl-trait/precise-capturing/rpitit.rs b/tests/ui/impl-trait/precise-capturing/rpitit.rs
new file mode 100644
index 00000000000..4eb053573e1
--- /dev/null
+++ b/tests/ui/impl-trait/precise-capturing/rpitit.rs
@@ -0,0 +1,21 @@
+//@ known-bug: unknown
+
+// RPITITs don't have variances in their GATs, so they always relate invariantly
+// and act as if they capture all their args.
+// To fix this soundly, we need to make sure that all the trait header args
+// remain captured, since they affect trait selection.
+
+#![feature(precise_capturing)]
+
+trait Foo<'a> {
+    fn hello() -> impl PartialEq + use<Self>;
+}
+
+fn test<'a, 'b, T: for<'r> Foo<'r>>() {
+    PartialEq::eq(
+        &<T as Foo<'a>>::hello(),
+        &<T as Foo<'b>>::hello(),
+    );
+}
+
+fn main() {}
diff --git a/tests/ui/impl-trait/precise-capturing/rpitit.stderr b/tests/ui/impl-trait/precise-capturing/rpitit.stderr
new file mode 100644
index 00000000000..45eceef2f49
--- /dev/null
+++ b/tests/ui/impl-trait/precise-capturing/rpitit.stderr
@@ -0,0 +1,50 @@
+error: `use<...>` precise capturing syntax is currently not allowed in return-position `impl Trait` in traits
+  --> $DIR/rpitit.rs:11:36
+   |
+LL |     fn hello() -> impl PartialEq + use<Self>;
+   |                                    ^^^^^^^^^
+   |
+   = note: currently, return-position `impl Trait` in traits and trait implementations capture all lifetimes in scope
+
+error: `impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list
+  --> $DIR/rpitit.rs:11:19
+   |
+LL | trait Foo<'a> {
+   |           -- this lifetime parameter is captured
+LL |     fn hello() -> impl PartialEq + use<Self>;
+   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetime captured due to being mentioned in the bounds of the `impl Trait`
+
+error: lifetime may not live long enough
+  --> $DIR/rpitit.rs:15:5
+   |
+LL |   fn test<'a, 'b, T: for<'r> Foo<'r>>() {
+   |           --  -- lifetime `'b` defined here
+   |           |
+   |           lifetime `'a` defined here
+LL | /     PartialEq::eq(
+LL | |         &<T as Foo<'a>>::hello(),
+LL | |         &<T as Foo<'b>>::hello(),
+LL | |     );
+   | |_____^ argument requires that `'a` must outlive `'b`
+   |
+   = help: consider adding the following bound: `'a: 'b`
+
+error: lifetime may not live long enough
+  --> $DIR/rpitit.rs:15:5
+   |
+LL |   fn test<'a, 'b, T: for<'r> Foo<'r>>() {
+   |           --  -- lifetime `'b` defined here
+   |           |
+   |           lifetime `'a` defined here
+LL | /     PartialEq::eq(
+LL | |         &<T as Foo<'a>>::hello(),
+LL | |         &<T as Foo<'b>>::hello(),
+LL | |     );
+   | |_____^ argument requires that `'b` must outlive `'a`
+   |
+   = help: consider adding the following bound: `'b: 'a`
+
+help: `'a` and `'b` must be the same: replace one with the other
+
+error: aborting due to 4 previous errors
+
diff --git a/tests/ui/impl-trait/precise-capturing/self-capture.rs b/tests/ui/impl-trait/precise-capturing/self-capture.rs
index e0a4a8b658c..07bb417f9f7 100644
--- a/tests/ui/impl-trait/precise-capturing/self-capture.rs
+++ b/tests/ui/impl-trait/precise-capturing/self-capture.rs
@@ -1,9 +1,8 @@
-//@ check-pass
-
 #![feature(precise_capturing)]
 
 trait Foo {
     fn bar<'a>() -> impl Sized + use<Self>;
+    //~^ ERROR `use<...>` precise capturing syntax is currently not allowed in return-position `impl Trait` in traits
 }
 
 fn main() {}
diff --git a/tests/ui/impl-trait/precise-capturing/self-capture.stderr b/tests/ui/impl-trait/precise-capturing/self-capture.stderr
new file mode 100644
index 00000000000..351de86dd5f
--- /dev/null
+++ b/tests/ui/impl-trait/precise-capturing/self-capture.stderr
@@ -0,0 +1,10 @@
+error: `use<...>` precise capturing syntax is currently not allowed in return-position `impl Trait` in traits
+  --> $DIR/self-capture.rs:4:34
+   |
+LL |     fn bar<'a>() -> impl Sized + use<Self>;
+   |                                  ^^^^^^^^^
+   |
+   = note: currently, return-position `impl Trait` in traits and trait implementations capture all lifetimes in scope
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/issues/issue-14915.stderr b/tests/ui/issues/issue-14915.stderr
index 279f5772d21..3558bd651c6 100644
--- a/tests/ui/issues/issue-14915.stderr
+++ b/tests/ui/issues/issue-14915.stderr
@@ -5,6 +5,12 @@ LL |     println!("{}", x + 1);
    |                    - ^ - {integer}
    |                    |
    |                    Box<isize>
+   |
+note: the foreign item type `Box<isize>` doesn't implement `Add<{integer}>`
+  --> $SRC_DIR/alloc/src/boxed.rs:LL:COL
+  ::: $SRC_DIR/alloc/src/boxed.rs:LL:COL
+   |
+   = note: not implement `Add<{integer}>`
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/issues/issue-68696-catch-during-unwind.rs b/tests/ui/issues/issue-68696-catch-during-unwind.rs
index 2368cccef0d..80d63b0cde7 100644
--- a/tests/ui/issues/issue-68696-catch-during-unwind.rs
+++ b/tests/ui/issues/issue-68696-catch-during-unwind.rs
@@ -7,6 +7,7 @@
 
 use std::panic::catch_unwind;
 
+#[allow(dead_code)]
 #[derive(Default)]
 struct Guard;
 
diff --git a/tests/ui/lint/dead-code/unused-struct-derive-default.rs b/tests/ui/lint/dead-code/unused-struct-derive-default.rs
new file mode 100644
index 00000000000..330ad32dd57
--- /dev/null
+++ b/tests/ui/lint/dead-code/unused-struct-derive-default.rs
@@ -0,0 +1,25 @@
+#![deny(dead_code)]
+
+#[derive(Default)]
+struct T; //~ ERROR struct `T` is never constructed
+
+#[derive(Default)]
+struct Used;
+
+#[derive(Default)]
+enum E {
+    #[default]
+    A,
+    B, //~ ERROR variant `B` is never constructed
+}
+
+// external crate can call T2::default() to construct T2,
+// so that no warnings for pub adts
+#[derive(Default)]
+pub struct T2 {
+    _unread: i32,
+}
+
+fn main() {
+    let _x: Used = Default::default();
+}
diff --git a/tests/ui/lint/dead-code/unused-struct-derive-default.stderr b/tests/ui/lint/dead-code/unused-struct-derive-default.stderr
new file mode 100644
index 00000000000..bbb0bd7be70
--- /dev/null
+++ b/tests/ui/lint/dead-code/unused-struct-derive-default.stderr
@@ -0,0 +1,24 @@
+error: struct `T` is never constructed
+  --> $DIR/unused-struct-derive-default.rs:4:8
+   |
+LL | struct T;
+   |        ^
+   |
+   = note: `T` has a derived impl for the trait `Default`, but this is intentionally ignored during dead code analysis
+note: the lint level is defined here
+  --> $DIR/unused-struct-derive-default.rs:1:9
+   |
+LL | #![deny(dead_code)]
+   |         ^^^^^^^^^
+
+error: variant `B` is never constructed
+  --> $DIR/unused-struct-derive-default.rs:13:5
+   |
+LL | enum E {
+   |      - variant in this enum
+...
+LL |     B,
+   |     ^
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/lint/dead-code/unused-trait-with-assoc-ty.rs b/tests/ui/lint/dead-code/unused-trait-with-assoc-ty.rs
new file mode 100644
index 00000000000..e8116d83ebf
--- /dev/null
+++ b/tests/ui/lint/dead-code/unused-trait-with-assoc-ty.rs
@@ -0,0 +1,11 @@
+#![deny(dead_code)]
+
+struct T1; //~ ERROR struct `T1` is never constructed
+
+trait Foo { type Unused; } //~ ERROR trait `Foo` is never used
+impl Foo for T1 { type Unused = Self; }
+
+pub trait Bar { type Used; }
+impl Bar for T1 { type Used = Self; }
+
+fn main() {}
diff --git a/tests/ui/lint/dead-code/unused-trait-with-assoc-ty.stderr b/tests/ui/lint/dead-code/unused-trait-with-assoc-ty.stderr
new file mode 100644
index 00000000000..ab73c640634
--- /dev/null
+++ b/tests/ui/lint/dead-code/unused-trait-with-assoc-ty.stderr
@@ -0,0 +1,20 @@
+error: struct `T1` is never constructed
+  --> $DIR/unused-trait-with-assoc-ty.rs:3:8
+   |
+LL | struct T1;
+   |        ^^
+   |
+note: the lint level is defined here
+  --> $DIR/unused-trait-with-assoc-ty.rs:1:9
+   |
+LL | #![deny(dead_code)]
+   |         ^^^^^^^^^
+
+error: trait `Foo` is never used
+  --> $DIR/unused-trait-with-assoc-ty.rs:5:7
+   |
+LL | trait Foo { type Unused; }
+   |       ^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/minus-string.stderr b/tests/ui/minus-string.stderr
index 105274ee7d0..cf63ec24416 100644
--- a/tests/ui/minus-string.stderr
+++ b/tests/ui/minus-string.stderr
@@ -3,6 +3,11 @@ error[E0600]: cannot apply unary operator `-` to type `String`
    |
 LL | fn main() { -"foo".to_string(); }
    |             ^^^^^^^^^^^^^^^^^^ cannot apply unary operator `-`
+   |
+note: the foreign item type `String` doesn't implement `Neg`
+  --> $SRC_DIR/alloc/src/string.rs:LL:COL
+   |
+   = note: not implement `Neg`
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/parser/fn-header-semantic-fail.stderr b/tests/ui/parser/fn-header-semantic-fail.stderr
index 29404f1793b..b519ddbe2b4 100644
--- a/tests/ui/parser/fn-header-semantic-fail.stderr
+++ b/tests/ui/parser/fn-header-semantic-fail.stderr
@@ -81,11 +81,13 @@ LL |         async fn fe1();
 error: items in unadorned `extern` blocks cannot have safety qualifiers
   --> $DIR/fn-header-semantic-fail.rs:47:9
    |
-LL |     extern "C" {
-   |     ---------- help: add unsafe to this `extern` block
-LL |         async fn fe1();
 LL |         unsafe fn fe2();
    |         ^^^^^^^^^^^^^^^^
+   |
+help: add unsafe to this `extern` block
+   |
+LL |     unsafe extern "C" {
+   |     ++++++
 
 error: functions in `extern` blocks cannot have qualifiers
   --> $DIR/fn-header-semantic-fail.rs:48:9
@@ -135,11 +137,13 @@ LL |         const async unsafe extern "C" fn fe5();
 error: items in unadorned `extern` blocks cannot have safety qualifiers
   --> $DIR/fn-header-semantic-fail.rs:50:9
    |
-LL |     extern "C" {
-   |     ---------- help: add unsafe to this `extern` block
-...
 LL |         const async unsafe extern "C" fn fe5();
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: add unsafe to this `extern` block
+   |
+LL |     unsafe extern "C" {
+   |     ++++++
 
 error: functions cannot be both `const` and `async`
   --> $DIR/fn-header-semantic-fail.rs:50:9
diff --git a/tests/ui/parser/no-const-fn-in-extern-block.stderr b/tests/ui/parser/no-const-fn-in-extern-block.stderr
index f575acc839d..8c23824a708 100644
--- a/tests/ui/parser/no-const-fn-in-extern-block.stderr
+++ b/tests/ui/parser/no-const-fn-in-extern-block.stderr
@@ -18,11 +18,13 @@ LL |     const unsafe fn bar();
 error: items in unadorned `extern` blocks cannot have safety qualifiers
   --> $DIR/no-const-fn-in-extern-block.rs:4:5
    |
-LL | extern "C" {
-   | ---------- help: add unsafe to this `extern` block
-...
 LL |     const unsafe fn bar();
    |     ^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: add unsafe to this `extern` block
+   |
+LL | unsafe extern "C" {
+   | ++++++
 
 error: aborting due to 3 previous errors
 
diff --git a/tests/ui/parser/unsafe-foreign-mod-2.stderr b/tests/ui/parser/unsafe-foreign-mod-2.stderr
index e59352395ed..77a383d5efa 100644
--- a/tests/ui/parser/unsafe-foreign-mod-2.stderr
+++ b/tests/ui/parser/unsafe-foreign-mod-2.stderr
@@ -13,11 +13,13 @@ LL | extern "C" unsafe {
 error: items in unadorned `extern` blocks cannot have safety qualifiers
   --> $DIR/unsafe-foreign-mod-2.rs:4:5
    |
-LL | extern "C" unsafe {
-   | ----------------- help: add unsafe to this `extern` block
-...
 LL |     unsafe fn foo();
    |     ^^^^^^^^^^^^^^^^
+   |
+help: add unsafe to this `extern` block
+   |
+LL | unsafe extern "C" unsafe {
+   | ++++++
 
 error: aborting due to 3 previous errors
 
diff --git a/tests/ui/pattern/issue-22546.rs b/tests/ui/pattern/issue-22546.rs
index fd1d5fb6c47..d5c5b68be78 100644
--- a/tests/ui/pattern/issue-22546.rs
+++ b/tests/ui/pattern/issue-22546.rs
@@ -15,7 +15,7 @@ impl<T: ::std::fmt::Display> Foo<T> {
     }
 }
 
-trait Tr { //~ WARN trait `Tr` is never used
+trait Tr {
     type U;
 }
 
diff --git a/tests/ui/pattern/issue-22546.stderr b/tests/ui/pattern/issue-22546.stderr
deleted file mode 100644
index e067a95e422..00000000000
--- a/tests/ui/pattern/issue-22546.stderr
+++ /dev/null
@@ -1,10 +0,0 @@
-warning: trait `Tr` is never used
-  --> $DIR/issue-22546.rs:18:7
-   |
-LL | trait Tr {
-   |       ^^
-   |
-   = note: `#[warn(dead_code)]` on by default
-
-warning: 1 warning emitted
-
diff --git a/tests/ui/pattern/pattern-tyvar-2.stderr b/tests/ui/pattern/pattern-tyvar-2.stderr
index c6540e79558..be52fa8b239 100644
--- a/tests/ui/pattern/pattern-tyvar-2.stderr
+++ b/tests/ui/pattern/pattern-tyvar-2.stderr
@@ -5,6 +5,11 @@ LL | fn foo(t: Bar) -> isize { match t { Bar::T1(_, Some(x)) => { return x * 3;
    |                                                                     - ^ - {integer}
    |                                                                     |
    |                                                                     Vec<isize>
+   |
+note: the foreign item type `Vec<isize>` doesn't implement `Mul<{integer}>`
+  --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
+   |
+   = note: not implement `Mul<{integer}>`
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/regions/account-for-lifetimes-in-closure-suggestion.rs b/tests/ui/regions/account-for-lifetimes-in-closure-suggestion.rs
new file mode 100644
index 00000000000..2de92cf62da
--- /dev/null
+++ b/tests/ui/regions/account-for-lifetimes-in-closure-suggestion.rs
@@ -0,0 +1,19 @@
+// #125634
+struct Thing;
+
+// Invariant in 'a, Covariant in 'b
+struct TwoThings<'a, 'b>(*mut &'a (), &'b mut ());
+
+impl Thing {
+    fn enter_scope<'a>(self, _scope: impl for<'b> FnOnce(TwoThings<'a, 'b>)) {}
+}
+
+fn foo() {
+    Thing.enter_scope(|ctx| {
+        SameLifetime(ctx); //~ ERROR lifetime may not live long enough
+    });
+}
+
+struct SameLifetime<'a>(TwoThings<'a, 'a>);
+
+fn main() {}
diff --git a/tests/ui/regions/account-for-lifetimes-in-closure-suggestion.stderr b/tests/ui/regions/account-for-lifetimes-in-closure-suggestion.stderr
new file mode 100644
index 00000000000..5e158f59cdc
--- /dev/null
+++ b/tests/ui/regions/account-for-lifetimes-in-closure-suggestion.stderr
@@ -0,0 +1,17 @@
+error: lifetime may not live long enough
+  --> $DIR/account-for-lifetimes-in-closure-suggestion.rs:13:22
+   |
+LL |     Thing.enter_scope(|ctx| {
+   |                        ---
+   |                        |
+   |                        has type `TwoThings<'_, '1>`
+   |                        has type `TwoThings<'2, '_>`
+LL |         SameLifetime(ctx);
+   |                      ^^^ this usage requires that `'1` must outlive `'2`
+   |
+   = note: requirement occurs because of the type `TwoThings<'_, '_>`, which makes the generic argument `'_` invariant
+   = note: the struct `TwoThings<'a, 'b>` is invariant over the parameter `'a`
+   = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
+
+error: aborting due to 1 previous error
+
diff --git a/tests/crashes/124563.rs b/tests/ui/regions/lifetime-not-long-enough-suggestion-regression-test-124563.rs
index b082739af53..23427838ceb 100644
--- a/tests/crashes/124563.rs
+++ b/tests/ui/regions/lifetime-not-long-enough-suggestion-regression-test-124563.rs
@@ -1,5 +1,4 @@
-//@ known-bug: rust-lang/rust#124563
-
+// #124563
 use std::marker::PhantomData;
 
 pub trait Trait {}
@@ -17,11 +16,11 @@ where
     T: Trait,
 {
     type Trait = T;
-    type Bar = BarImpl<'a, 'b, T>;
+    type Bar = BarImpl<'a, 'b, T>; //~ ERROR lifetime bound not satisfied
 
     fn foo(&mut self) {
-        self.enter_scope(|ctx| {
-            BarImpl(ctx);
+        self.enter_scope(|ctx| { //~ ERROR lifetime may not live long enough
+            BarImpl(ctx); //~ ERROR lifetime may not live long enough
         });
     }
 }
@@ -44,3 +43,5 @@ where
 {
     type Foo = FooImpl<'a, 'b, T>;
 }
+
+fn main() {}
diff --git a/tests/ui/regions/lifetime-not-long-enough-suggestion-regression-test-124563.stderr b/tests/ui/regions/lifetime-not-long-enough-suggestion-regression-test-124563.stderr
new file mode 100644
index 00000000000..fcd0a232a7b
--- /dev/null
+++ b/tests/ui/regions/lifetime-not-long-enough-suggestion-regression-test-124563.stderr
@@ -0,0 +1,49 @@
+error[E0478]: lifetime bound not satisfied
+  --> $DIR/lifetime-not-long-enough-suggestion-regression-test-124563.rs:19:16
+   |
+LL |     type Bar = BarImpl<'a, 'b, T>;
+   |                ^^^^^^^^^^^^^^^^^^
+   |
+note: lifetime parameter instantiated with the lifetime `'a` as defined here
+  --> $DIR/lifetime-not-long-enough-suggestion-regression-test-124563.rs:14:6
+   |
+LL | impl<'a, 'b, T> Foo for FooImpl<'a, 'b, T>
+   |      ^^
+note: but lifetime parameter must outlive the lifetime `'b` as defined here
+  --> $DIR/lifetime-not-long-enough-suggestion-regression-test-124563.rs:14:10
+   |
+LL | impl<'a, 'b, T> Foo for FooImpl<'a, 'b, T>
+   |          ^^
+
+error: lifetime may not live long enough
+  --> $DIR/lifetime-not-long-enough-suggestion-regression-test-124563.rs:23:21
+   |
+LL |         self.enter_scope(|ctx| {
+   |                           ---
+   |                           |
+   |                           has type `&'1 mut FooImpl<'_, '_, T>`
+   |                           has type `&mut FooImpl<'2, '_, T>`
+LL |             BarImpl(ctx);
+   |                     ^^^ this usage requires that `'1` must outlive `'2`
+
+error: lifetime may not live long enough
+  --> $DIR/lifetime-not-long-enough-suggestion-regression-test-124563.rs:22:9
+   |
+LL |   impl<'a, 'b, T> Foo for FooImpl<'a, 'b, T>
+   |        --  -- lifetime `'b` defined here
+   |        |
+   |        lifetime `'a` defined here
+...
+LL | /         self.enter_scope(|ctx| {
+LL | |             BarImpl(ctx);
+LL | |         });
+   | |__________^ argument requires that `'a` must outlive `'b`
+   |
+   = help: consider adding the following bound: `'a: 'b`
+   = note: requirement occurs because of a mutable reference to `FooImpl<'_, '_, T>`
+   = note: mutable references are invariant over their type parameter
+   = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0478`.
diff --git a/tests/ui/regions/regions-escape-method.fixed b/tests/ui/regions/regions-escape-method.fixed
new file mode 100644
index 00000000000..f192dca1e25
--- /dev/null
+++ b/tests/ui/regions/regions-escape-method.fixed
@@ -0,0 +1,17 @@
+// Test a method call where the parameter `B` would (illegally) be
+// inferred to a region bound in the method argument. If this program
+// were accepted, then the closure passed to `s.f` could escape its
+// argument.
+//@ run-rustfix
+
+struct S;
+
+impl S {
+    fn f<B, F>(&self, _: F) where F: FnOnce(&i32) -> B {
+    }
+}
+
+fn main() {
+    let s = S;
+    s.f(|p| *p) //~ ERROR lifetime may not live long enough
+}
diff --git a/tests/ui/regions/regions-escape-method.rs b/tests/ui/regions/regions-escape-method.rs
index 69c01ae6906..82bf86c79b2 100644
--- a/tests/ui/regions/regions-escape-method.rs
+++ b/tests/ui/regions/regions-escape-method.rs
@@ -2,6 +2,7 @@
 // inferred to a region bound in the method argument. If this program
 // were accepted, then the closure passed to `s.f` could escape its
 // argument.
+//@ run-rustfix
 
 struct S;
 
diff --git a/tests/ui/regions/regions-escape-method.stderr b/tests/ui/regions/regions-escape-method.stderr
index aeda923b0ba..687b91bb7b4 100644
--- a/tests/ui/regions/regions-escape-method.stderr
+++ b/tests/ui/regions/regions-escape-method.stderr
@@ -1,11 +1,16 @@
 error: lifetime may not live long enough
-  --> $DIR/regions-escape-method.rs:15:13
+  --> $DIR/regions-escape-method.rs:16:13
    |
 LL |     s.f(|p| p)
    |          -- ^ returning this value requires that `'1` must outlive `'2`
    |          ||
    |          |return type of closure is &'2 i32
    |          has type `&'1 i32`
+   |
+help: dereference the return value
+   |
+LL |     s.f(|p| *p)
+   |             +
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/rust-2024/safe-outside-extern.gated.stderr b/tests/ui/rust-2024/safe-outside-extern.gated.stderr
index ea7aa181445..18a3361f35b 100644
--- a/tests/ui/rust-2024/safe-outside-extern.gated.stderr
+++ b/tests/ui/rust-2024/safe-outside-extern.gated.stderr
@@ -26,7 +26,7 @@ error: function pointers cannot be declared with `safe` safety qualifier
   --> $DIR/safe-outside-extern.rs:24:14
    |
 LL | type FnPtr = safe fn(i32, i32) -> i32;
-   |              ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: aborting due to 5 previous errors
 
diff --git a/tests/ui/rust-2024/safe-outside-extern.ungated.stderr b/tests/ui/rust-2024/safe-outside-extern.ungated.stderr
index 908f5b504eb..9ea6d451e8c 100644
--- a/tests/ui/rust-2024/safe-outside-extern.ungated.stderr
+++ b/tests/ui/rust-2024/safe-outside-extern.ungated.stderr
@@ -26,7 +26,7 @@ error: function pointers cannot be declared with `safe` safety qualifier
   --> $DIR/safe-outside-extern.rs:24:14
    |
 LL | type FnPtr = safe fn(i32, i32) -> i32;
-   |              ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^
 
 error[E0658]: `unsafe extern {}` blocks and `safe` keyword are experimental
   --> $DIR/safe-outside-extern.rs:4:1
diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/safe-unsafe-on-unadorned-extern-block.edition2021.stderr b/tests/ui/rust-2024/unsafe-extern-blocks/safe-unsafe-on-unadorned-extern-block.edition2021.stderr
index 411cf48b486..e90613357b1 100644
--- a/tests/ui/rust-2024/unsafe-extern-blocks/safe-unsafe-on-unadorned-extern-block.edition2021.stderr
+++ b/tests/ui/rust-2024/unsafe-extern-blocks/safe-unsafe-on-unadorned-extern-block.edition2021.stderr
@@ -1,20 +1,24 @@
 error: items in unadorned `extern` blocks cannot have safety qualifiers
   --> $DIR/safe-unsafe-on-unadorned-extern-block.rs:10:5
    |
-LL | extern "C" {
-   | ---------- help: add unsafe to this `extern` block
-LL |
 LL |     safe static TEST1: i32;
    |     ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: add unsafe to this `extern` block
+   |
+LL | unsafe extern "C" {
+   | ++++++
 
 error: items in unadorned `extern` blocks cannot have safety qualifiers
   --> $DIR/safe-unsafe-on-unadorned-extern-block.rs:12:5
    |
-LL | extern "C" {
-   | ---------- help: add unsafe to this `extern` block
-...
 LL |     safe fn test1(i: i32);
    |     ^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: add unsafe to this `extern` block
+   |
+LL | unsafe extern "C" {
+   | ++++++
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/safe-unsafe-on-unadorned-extern-block.edition2024.stderr b/tests/ui/rust-2024/unsafe-extern-blocks/safe-unsafe-on-unadorned-extern-block.edition2024.stderr
index b634adc2999..1207ee158cc 100644
--- a/tests/ui/rust-2024/unsafe-extern-blocks/safe-unsafe-on-unadorned-extern-block.edition2024.stderr
+++ b/tests/ui/rust-2024/unsafe-extern-blocks/safe-unsafe-on-unadorned-extern-block.edition2024.stderr
@@ -13,20 +13,24 @@ LL | | }
 error: items in unadorned `extern` blocks cannot have safety qualifiers
   --> $DIR/safe-unsafe-on-unadorned-extern-block.rs:10:5
    |
-LL | extern "C" {
-   | ---------- help: add unsafe to this `extern` block
-LL |
 LL |     safe static TEST1: i32;
    |     ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: add unsafe to this `extern` block
+   |
+LL | unsafe extern "C" {
+   | ++++++
 
 error: items in unadorned `extern` blocks cannot have safety qualifiers
   --> $DIR/safe-unsafe-on-unadorned-extern-block.rs:12:5
    |
-LL | extern "C" {
-   | ---------- help: add unsafe to this `extern` block
-...
 LL |     safe fn test1(i: i32);
    |     ^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: add unsafe to this `extern` block
+   |
+LL | unsafe extern "C" {
+   | ++++++
 
 error: aborting due to 3 previous errors
 
diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-on-extern-block-issue-126756.fixed b/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-on-extern-block-issue-126756.fixed
new file mode 100644
index 00000000000..2ff595cc44d
--- /dev/null
+++ b/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-on-extern-block-issue-126756.fixed
@@ -0,0 +1,10 @@
+//@ run-rustfix
+
+#![feature(unsafe_extern_blocks)]
+#![allow(dead_code)]
+
+unsafe extern "C" {
+    unsafe fn foo(); //~ ERROR items in unadorned `extern` blocks cannot have safety qualifiers
+}
+
+fn main() {}
diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-on-extern-block-issue-126756.rs b/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-on-extern-block-issue-126756.rs
new file mode 100644
index 00000000000..6fe43f7a5b4
--- /dev/null
+++ b/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-on-extern-block-issue-126756.rs
@@ -0,0 +1,10 @@
+//@ run-rustfix
+
+#![feature(unsafe_extern_blocks)]
+#![allow(dead_code)]
+
+extern "C" {
+    unsafe fn foo(); //~ ERROR items in unadorned `extern` blocks cannot have safety qualifiers
+}
+
+fn main() {}
diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-on-extern-block-issue-126756.stderr b/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-on-extern-block-issue-126756.stderr
new file mode 100644
index 00000000000..05d23d001ad
--- /dev/null
+++ b/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-on-extern-block-issue-126756.stderr
@@ -0,0 +1,13 @@
+error: items in unadorned `extern` blocks cannot have safety qualifiers
+  --> $DIR/unsafe-on-extern-block-issue-126756.rs:7:5
+   |
+LL |     unsafe fn foo();
+   |     ^^^^^^^^^^^^^^^^
+   |
+help: add unsafe to this `extern` block
+   |
+LL | unsafe extern "C" {
+   | ++++++
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/suggestions/missing-impl-trait-block-but-not-ascii.rs b/tests/ui/suggestions/missing-impl-trait-block-but-not-ascii.rs
new file mode 100644
index 00000000000..ddb6bd1e902
--- /dev/null
+++ b/tests/ui/suggestions/missing-impl-trait-block-but-not-ascii.rs
@@ -0,0 +1,13 @@
+// issue#126764
+
+struct S;
+
+trait T {
+    fn f();
+}
+impl T for S;
+//~^ ERROR: unknown start of token
+//~| ERROR: expected `{}`
+//~| ERROR: not all trait items implemented, missing: `f`
+
+fn main() {}
diff --git a/tests/ui/suggestions/missing-impl-trait-block-but-not-ascii.stderr b/tests/ui/suggestions/missing-impl-trait-block-but-not-ascii.stderr
new file mode 100644
index 00000000000..56cdc11b62e
--- /dev/null
+++ b/tests/ui/suggestions/missing-impl-trait-block-but-not-ascii.stderr
@@ -0,0 +1,31 @@
+error: unknown start of token: \u{ff1b}
+  --> $DIR/missing-impl-trait-block-but-not-ascii.rs:8:13
+   |
+LL | impl T for S;
+   |             ^^
+   |
+help: Unicode character ';' (Fullwidth Semicolon) looks like ';' (Semicolon), but it is not
+   |
+LL | impl T for S;
+   |             ~
+
+error: expected `{}`, found `;`
+  --> $DIR/missing-impl-trait-block-but-not-ascii.rs:8:13
+   |
+LL | impl T for S;
+   |             ^^
+   |
+   = help: try using `{}` instead
+
+error[E0046]: not all trait items implemented, missing: `f`
+  --> $DIR/missing-impl-trait-block-but-not-ascii.rs:8:1
+   |
+LL |     fn f();
+   |     ------- `f` from trait
+LL | }
+LL | impl T for S;
+   | ^^^^^^^^^^^^ missing `f` in implementation
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0046`.
diff --git a/tests/ui/typeck/assign-non-lval-derefmut.stderr b/tests/ui/typeck/assign-non-lval-derefmut.stderr
index b26d16da015..ce0ff1d957b 100644
--- a/tests/ui/typeck/assign-non-lval-derefmut.stderr
+++ b/tests/ui/typeck/assign-non-lval-derefmut.stderr
@@ -19,6 +19,10 @@ LL |     x.lock().unwrap() += 1;
    |     |
    |     cannot use `+=` on type `MutexGuard<'_, usize>`
    |
+note: the foreign item type `MutexGuard<'_, usize>` doesn't implement `AddAssign<{integer}>`
+  --> $SRC_DIR/std/src/sync/mutex.rs:LL:COL
+   |
+   = note: not implement `AddAssign<{integer}>`
 help: `+=` can be used on `usize` if you dereference the left-hand side
    |
 LL |     *x.lock().unwrap() += 1;
@@ -47,6 +51,10 @@ LL |     y += 1;
    |     |
    |     cannot use `+=` on type `MutexGuard<'_, usize>`
    |
+note: the foreign item type `MutexGuard<'_, usize>` doesn't implement `AddAssign<{integer}>`
+  --> $SRC_DIR/std/src/sync/mutex.rs:LL:COL
+   |
+   = note: not implement `AddAssign<{integer}>`
 help: `+=` can be used on `usize` if you dereference the left-hand side
    |
 LL |     *y += 1;