about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_abi/src/layout.rs4
-rw-r--r--compiler/rustc_abi/src/lib.rs111
-rw-r--r--compiler/rustc_apfloat/Cargo.toml8
-rw-r--r--compiler/rustc_apfloat/src/ieee.rs2757
-rw-r--r--compiler/rustc_apfloat/src/lib.rs695
-rw-r--r--compiler/rustc_apfloat/src/ppc.rs434
-rw-r--r--compiler/rustc_apfloat/tests/ieee.rs3301
-rw-r--r--compiler/rustc_apfloat/tests/ppc.rs530
-rw-r--r--compiler/rustc_ast/src/ast.rs7
-rw-r--r--compiler/rustc_ast/src/attr/mod.rs37
-rw-r--r--compiler/rustc_ast/src/token.rs17
-rw-r--r--compiler/rustc_ast/src/tokenstream.rs33
-rw-r--r--compiler/rustc_ast/src/util/comments.rs2
-rw-r--r--compiler/rustc_ast_lowering/src/expr.rs2
-rw-r--r--compiler/rustc_ast_passes/src/feature_gate.rs13
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state/expr.rs8
-rw-r--r--compiler/rustc_borrowck/src/borrowck_errors.rs46
-rw-r--r--compiler/rustc_borrowck/src/dataflow.rs2
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs76
-rw-r--r--compiler/rustc_borrowck/src/facts.rs2
-rw-r--r--compiler/rustc_borrowck/src/lib.rs15
-rw-r--r--compiler/rustc_borrowck/src/nll.rs6
-rw-r--r--compiler/rustc_borrowck/src/region_infer/dump_mir.rs11
-rw-r--r--compiler/rustc_borrowck/src/region_infer/graphviz.rs4
-rw-r--r--compiler/rustc_borrowck/src/region_infer/mod.rs2
-rw-r--r--compiler/rustc_borrowck/src/region_infer/opaque_types.rs6
-rw-r--r--compiler/rustc_borrowck/src/region_infer/values.rs6
-rw-r--r--compiler/rustc_borrowck/src/type_check/mod.rs6
-rw-r--r--compiler/rustc_builtin_macros/messages.ftl4
-rw-r--r--compiler/rustc_builtin_macros/src/asm.rs2
-rw-r--r--compiler/rustc_builtin_macros/src/env.rs43
-rw-r--r--compiler/rustc_builtin_macros/src/errors.rs52
-rw-r--r--compiler/rustc_codegen_cranelift/.github/workflows/main.yml10
-rw-r--r--compiler/rustc_codegen_cranelift/Cargo.lock84
-rw-r--r--compiler/rustc_codegen_cranelift/Cargo.toml14
-rw-r--r--compiler/rustc_codegen_cranelift/Readme.md10
-rw-r--r--compiler/rustc_codegen_cranelift/build_system/bench.rs61
-rw-r--r--compiler/rustc_codegen_cranelift/build_system/build_backend.rs4
-rw-r--r--compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs8
-rw-r--r--compiler/rustc_codegen_cranelift/build_system/prepare.rs1
-rw-r--r--compiler/rustc_codegen_cranelift/build_system/tests.rs25
-rw-r--r--compiler/rustc_codegen_cranelift/build_system/utils.rs42
-rw-r--r--compiler/rustc_codegen_cranelift/config.txt1
-rw-r--r--compiler/rustc_codegen_cranelift/example/issue-59326.rs27
-rw-r--r--compiler/rustc_codegen_cranelift/example/mini_core.rs4
-rw-r--r--compiler/rustc_codegen_cranelift/example/std_example.rs32
-rw-r--r--compiler/rustc_codegen_cranelift/patches/0027-coretests-128bit-atomic-operations.patch12
-rw-r--r--compiler/rustc_codegen_cranelift/patches/0027-stdlib-128bit-atomic-operations.patch15
-rw-r--r--compiler/rustc_codegen_cranelift/patches/stdlib-lock.toml31
-rw-r--r--compiler/rustc_codegen_cranelift/rust-toolchain2
-rw-r--r--compiler/rustc_codegen_cranelift/scripts/cargo-clif.rs17
-rw-r--r--compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh5
-rwxr-xr-xcompiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh10
-rw-r--r--compiler/rustc_codegen_cranelift/src/abi/mod.rs13
-rw-r--r--compiler/rustc_codegen_cranelift/src/base.rs20
-rw-r--r--compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs2
-rw-r--r--compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs2
-rw-r--r--compiler/rustc_codegen_cranelift/src/driver/jit.rs4
-rw-r--r--compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs274
-rw-r--r--compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs3
-rw-r--r--compiler/rustc_codegen_cranelift/src/lib.rs2
-rw-r--r--compiler/rustc_codegen_cranelift/src/value_and_place.rs103
-rw-r--r--compiler/rustc_codegen_gcc/messages.ftl57
-rw-r--r--compiler/rustc_codegen_gcc/src/errors.rs198
-rw-r--r--compiler/rustc_codegen_gcc/src/intrinsic/mod.rs4
-rw-r--r--compiler/rustc_codegen_gcc/src/intrinsic/simd.rs121
-rw-r--r--compiler/rustc_codegen_gcc/src/type_of.rs3
-rw-r--r--compiler/rustc_codegen_llvm/src/context.rs18
-rw-r--r--compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs11
-rw-r--r--compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs28
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs15
-rw-r--r--compiler/rustc_codegen_llvm/src/type_of.rs4
-rw-r--r--compiler/rustc_codegen_ssa/src/back/write.rs2
-rw-r--r--compiler/rustc_codegen_ssa/src/base.rs5
-rw-r--r--compiler/rustc_codegen_ssa/src/codegen_attrs.rs17
-rw-r--r--compiler/rustc_codegen_ssa/src/errors.rs2
-rw-r--r--compiler/rustc_codegen_ssa/src/traits/consts.rs6
-rw-r--r--compiler/rustc_const_eval/Cargo.toml2
-rw-r--r--compiler/rustc_const_eval/messages.ftl8
-rw-r--r--compiler/rustc_const_eval/src/const_eval/eval_queries.rs8
-rw-r--r--compiler/rustc_const_eval/src/const_eval/fn_queries.rs13
-rw-r--r--compiler/rustc_const_eval/src/const_eval/machine.rs11
-rw-r--r--compiler/rustc_const_eval/src/const_eval/mod.rs6
-rw-r--r--compiler/rustc_const_eval/src/const_eval/valtrees.rs64
-rw-r--r--compiler/rustc_const_eval/src/errors.rs21
-rw-r--r--compiler/rustc_const_eval/src/interpret/cast.rs6
-rw-r--r--compiler/rustc_const_eval/src/interpret/discriminant.rs126
-rw-r--r--compiler/rustc_const_eval/src/interpret/eval_context.rs16
-rw-r--r--compiler/rustc_const_eval/src/interpret/intern.rs133
-rw-r--r--compiler/rustc_const_eval/src/interpret/intrinsics.rs53
-rw-r--r--compiler/rustc_const_eval/src/interpret/intrinsics/caller_location.rs6
-rw-r--r--compiler/rustc_const_eval/src/interpret/memory.rs45
-rw-r--r--compiler/rustc_const_eval/src/interpret/mod.rs7
-rw-r--r--compiler/rustc_const_eval/src/interpret/operand.rs264
-rw-r--r--compiler/rustc_const_eval/src/interpret/operator.rs9
-rw-r--r--compiler/rustc_const_eval/src/interpret/place.rs488
-rw-r--r--compiler/rustc_const_eval/src/interpret/projection.rs417
-rw-r--r--compiler/rustc_const_eval/src/interpret/step.rs13
-rw-r--r--compiler/rustc_const_eval/src/interpret/terminator.rs25
-rw-r--r--compiler/rustc_const_eval/src/interpret/validity.rs184
-rw-r--r--compiler/rustc_const_eval/src/interpret/visitor.rs692
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/check.rs42
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/ops.rs8
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs6
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs7
-rw-r--r--compiler/rustc_const_eval/src/transform/validate.rs550
-rw-r--r--compiler/rustc_const_eval/src/util/compare_types.rs14
-rw-r--r--compiler/rustc_data_structures/src/sso/map.rs6
-rw-r--r--compiler/rustc_driver_impl/src/lib.rs2
-rw-r--r--compiler/rustc_error_messages/src/lib.rs8
-rw-r--r--compiler/rustc_errors/messages.ftl22
-rw-r--r--compiler/rustc_errors/src/diagnostic.rs10
-rw-r--r--compiler/rustc_errors/src/diagnostic_builder.rs4
-rw-r--r--compiler/rustc_errors/src/diagnostic_impls.rs69
-rw-r--r--compiler/rustc_errors/src/emitter.rs4
-rw-r--r--compiler/rustc_errors/src/json/tests.rs2
-rw-r--r--compiler/rustc_errors/src/lib.rs119
-rw-r--r--compiler/rustc_errors/src/markdown/tests/term.rs2
-rw-r--r--compiler/rustc_expand/src/config.rs6
-rw-r--r--compiler/rustc_expand/src/mbe/macro_rules.rs4
-rw-r--r--compiler/rustc_expand/src/mbe/quoted.rs42
-rw-r--r--compiler/rustc_expand/src/parse/tests.rs21
-rw-r--r--compiler/rustc_expand/src/proc_macro_server.rs4
-rw-r--r--compiler/rustc_expand/src/tests.rs69
-rw-r--r--compiler/rustc_feature/src/active.rs2
-rw-r--r--compiler/rustc_hir/src/hir.rs3
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/errors.rs6
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/generics.rs8
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/lint.rs4
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/mod.rs10
-rw-r--r--compiler/rustc_hir_analysis/src/check/check.rs185
-rw-r--r--compiler/rustc_hir_analysis/src/check/compare_impl_item.rs16
-rw-r--r--compiler/rustc_hir_analysis/src/check/dropck.rs5
-rw-r--r--compiler/rustc_hir_analysis/src/check/intrinsic.rs4
-rw-r--r--compiler/rustc_hir_analysis/src/check/intrinsicck.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/check/mod.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/check/wfcheck.rs16
-rw-r--r--compiler/rustc_hir_analysis/src/check_unused.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/builtin.rs3
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs3
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs8
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/orphan.rs17
-rw-r--r--compiler/rustc_hir_analysis/src/collect.rs16
-rw-r--r--compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs62
-rw-r--r--compiler/rustc_hir_analysis/src/collect/type_of.rs8
-rw-r--r--compiler/rustc_hir_analysis/src/errors.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/impl_wf_check.rs5
-rw-r--r--compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs4
-rw-r--r--compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs12
-rw-r--r--compiler/rustc_hir_analysis/src/variance/terms.rs4
-rw-r--r--compiler/rustc_hir_typeck/src/callee.rs10
-rw-r--r--compiler/rustc_hir_typeck/src/cast.rs12
-rw-r--r--compiler/rustc_hir_typeck/src/closure.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/coercion.rs61
-rw-r--r--compiler/rustc_hir_typeck/src/demand.rs4
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs18
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs10
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs47
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs113
-rw-r--r--compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/mod.rs4
-rw-r--r--compiler/rustc_hir_typeck/src/generator_interior/mod.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/intrinsicck.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/lib.rs12
-rw-r--r--compiler/rustc_hir_typeck/src/method/confirm.rs5
-rw-r--r--compiler/rustc_hir_typeck/src/method/prelude2021.rs20
-rw-r--r--compiler/rustc_hir_typeck/src/method/suggest.rs68
-rw-r--r--compiler/rustc_hir_typeck/src/pat.rs27
-rw-r--r--compiler/rustc_hir_typeck/src/upvar.rs17
-rw-r--r--compiler/rustc_hir_typeck/src/writeback.rs10
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/mod.rs6
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs8
-rw-r--r--compiler/rustc_infer/src/infer/region_constraints/leak_check.rs8
-rw-r--r--compiler/rustc_infer/src/traits/mod.rs9
-rw-r--r--compiler/rustc_interface/Cargo.toml1
-rw-r--r--compiler/rustc_interface/src/tests.rs3
-rw-r--r--compiler/rustc_interface/src/util.rs3
-rw-r--r--compiler/rustc_lexer/src/cursor.rs4
-rw-r--r--compiler/rustc_lexer/src/lib.rs7
-rw-r--r--compiler/rustc_lexer/src/unescape.rs2
-rw-r--r--compiler/rustc_lint/messages.ftl2
-rw-r--r--compiler/rustc_lint/src/builtin.rs2
-rw-r--r--compiler/rustc_lint/src/context.rs34
-rw-r--r--compiler/rustc_lint/src/levels.rs2
-rw-r--r--compiler/rustc_lint/src/lints.rs6
-rw-r--r--compiler/rustc_lint/src/types.rs104
-rw-r--r--compiler/rustc_lint/src/unused.rs10
-rw-r--r--compiler/rustc_lint_defs/src/builtin.rs31
-rw-r--r--compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp5
-rw-r--r--compiler/rustc_macros/Cargo.toml3
-rw-r--r--compiler/rustc_metadata/src/fs.rs4
-rw-r--r--compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs1
-rw-r--r--compiler/rustc_metadata/src/rmeta/encoder.rs10
-rw-r--r--compiler/rustc_metadata/src/rmeta/mod.rs3
-rw-r--r--compiler/rustc_middle/Cargo.toml2
-rw-r--r--compiler/rustc_middle/src/macros.rs4
-rw-r--r--compiler/rustc_middle/src/mir/basic_blocks.rs4
-rw-r--r--compiler/rustc_middle/src/mir/interpret/allocation.rs2
-rw-r--r--compiler/rustc_middle/src/mir/interpret/error.rs18
-rw-r--r--compiler/rustc_middle/src/mir/interpret/pointer.rs30
-rw-r--r--compiler/rustc_middle/src/mir/interpret/queries.rs9
-rw-r--r--compiler/rustc_middle/src/mir/interpret/value.rs8
-rw-r--r--compiler/rustc_middle/src/mir/mod.rs4
-rw-r--r--compiler/rustc_middle/src/query/erase.rs6
-rw-r--r--compiler/rustc_middle/src/query/mod.rs21
-rw-r--r--compiler/rustc_middle/src/traits/mod.rs74
-rw-r--r--compiler/rustc_middle/src/traits/select.rs4
-rw-r--r--compiler/rustc_middle/src/traits/solve/inspect.rs6
-rw-r--r--compiler/rustc_middle/src/traits/solve/inspect/format.rs7
-rw-r--r--compiler/rustc_middle/src/traits/structural_impls.rs34
-rw-r--r--compiler/rustc_middle/src/ty/abstract_const.rs4
-rw-r--r--compiler/rustc_middle/src/ty/binding.rs2
-rw-r--r--compiler/rustc_middle/src/ty/codec.rs4
-rw-r--r--compiler/rustc_middle/src/ty/error.rs9
-rw-r--r--compiler/rustc_middle/src/ty/layout.rs290
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs85
-rw-r--r--compiler/rustc_middle/src/ty/structural_impls.rs2
-rw-r--r--compiler/rustc_middle/src/ty/sty.rs5
-rw-r--r--compiler/rustc_mir_build/Cargo.toml2
-rw-r--r--compiler/rustc_mir_build/src/build/custom/parse.rs2
-rw-r--r--compiler/rustc_mir_build/src/build/expr/as_rvalue.rs3
-rw-r--r--compiler/rustc_mir_build/src/build/matches/mod.rs4
-rw-r--r--compiler/rustc_mir_build/src/build/matches/test.rs6
-rw-r--r--compiler/rustc_mir_build/src/build/mod.rs15
-rw-r--r--compiler/rustc_mir_build/src/errors.rs8
-rw-r--r--compiler/rustc_mir_build/src/thir/cx/expr.rs4
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/check_match.rs25
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs18
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/usefulness.rs4
-rw-r--r--compiler/rustc_mir_transform/src/const_prop_lint.rs4
-rw-r--r--compiler/rustc_mir_transform/src/coverage/counters.rs8
-rw-r--r--compiler/rustc_mir_transform/src/coverage/debug.rs4
-rw-r--r--compiler/rustc_mir_transform/src/coverage/graph.rs15
-rw-r--r--compiler/rustc_mir_transform/src/elaborate_drops.rs5
-rw-r--r--compiler/rustc_mir_transform/src/function_item_references.rs6
-rw-r--r--compiler/rustc_mir_transform/src/generator.rs2
-rw-r--r--compiler/rustc_mir_transform/src/inline.rs134
-rw-r--r--compiler/rustc_mir_transform/src/instsimplify.rs2
-rw-r--r--compiler/rustc_mir_transform/src/lib.rs2
-rw-r--r--compiler/rustc_mir_transform/src/match_branches.rs2
-rw-r--r--compiler/rustc_mir_transform/src/multiple_return_terminators.rs2
-rw-r--r--compiler/rustc_mir_transform/src/nrvo.rs2
-rw-r--r--compiler/rustc_mir_transform/src/pass_manager.rs4
-rw-r--r--compiler/rustc_mir_transform/src/remove_unneeded_drops.rs2
-rw-r--r--compiler/rustc_mir_transform/src/remove_zsts.rs2
-rw-r--r--compiler/rustc_monomorphize/src/partitioning.rs18
-rw-r--r--compiler/rustc_parse/src/lexer/mod.rs41
-rw-r--r--compiler/rustc_parse/src/lexer/unescape_error_reporting.rs2
-rw-r--r--compiler/rustc_parse/src/parser/attr_wrapper.rs2
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs16
-rw-r--r--compiler/rustc_parse/src/parser/generics.rs2
-rw-r--r--compiler/rustc_parse/src/parser/mod.rs46
-rw-r--r--compiler/rustc_parse/src/validate_attr.rs14
-rw-r--r--compiler/rustc_passes/messages.ftl11
-rw-r--r--compiler/rustc_passes/src/check_attr.rs49
-rw-r--r--compiler/rustc_passes/src/check_const.rs6
-rw-r--r--compiler/rustc_passes/src/errors.rs19
-rw-r--r--compiler/rustc_passes/src/hir_id_validator.rs5
-rw-r--r--compiler/rustc_passes/src/hir_stats.rs8
-rw-r--r--compiler/rustc_passes/src/liveness.rs2
-rw-r--r--compiler/rustc_query_impl/src/plumbing.rs2
-rw-r--r--compiler/rustc_query_system/src/query/job.rs3
-rw-r--r--compiler/rustc_resolve/src/diagnostics.rs4
-rw-r--r--compiler/rustc_resolve/src/imports.rs13
-rw-r--r--compiler/rustc_resolve/src/late.rs15
-rw-r--r--compiler/rustc_resolve/src/macros.rs25
-rw-r--r--compiler/rustc_session/Cargo.toml1
-rw-r--r--compiler/rustc_session/messages.ftl1
-rw-r--r--compiler/rustc_session/src/code_stats.rs6
-rw-r--r--compiler/rustc_session/src/config.rs15
-rw-r--r--compiler/rustc_session/src/errors.rs8
-rw-r--r--compiler/rustc_session/src/options.rs36
-rw-r--r--compiler/rustc_session/src/output.rs13
-rw-r--r--compiler/rustc_session/src/parse.rs30
-rw-r--r--compiler/rustc_session/src/session.rs14
-rw-r--r--compiler/rustc_session/src/utils.rs1
-rw-r--r--compiler/rustc_smir/src/rustc_internal/mod.rs16
-rw-r--r--compiler/rustc_smir/src/rustc_smir/mod.rs303
-rw-r--r--compiler/rustc_smir/src/stable_mir/mir/body.rs3
-rw-r--r--compiler/rustc_smir/src/stable_mir/ty.rs68
-rw-r--r--compiler/rustc_span/src/symbol.rs3
-rw-r--r--compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs81
-rw-r--r--compiler/rustc_target/src/abi/mod.rs6
-rw-r--r--compiler/rustc_target/src/spec/aarch64_unknown_hermit.rs14
-rw-r--r--compiler/rustc_target/src/spec/armebv7r_none_eabi.rs4
-rw-r--r--compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs4
-rw-r--r--compiler/rustc_target/src/spec/armv4t_none_eabi.rs4
-rw-r--r--compiler/rustc_target/src/spec/armv7a_none_eabihf.rs2
-rw-r--r--compiler/rustc_target/src/spec/armv7r_none_eabi.rs4
-rw-r--r--compiler/rustc_target/src/spec/armv7r_none_eabihf.rs4
-rw-r--r--compiler/rustc_target/src/spec/hermit_base.rs14
-rw-r--r--compiler/rustc_target/src/spec/mod.rs4
-rw-r--r--compiler/rustc_target/src/spec/riscv64gc_unknown_hermit.rs20
-rw-r--r--compiler/rustc_target/src/spec/thumb_base.rs2
-rw-r--r--compiler/rustc_target/src/spec/thumbv4t_none_eabi.rs2
-rw-r--r--compiler/rustc_target/src/spec/unikraft_linux_musl_base.rs15
-rw-r--r--compiler/rustc_target/src/spec/x86_64_unikraft_linux_musl.rs19
-rw-r--r--compiler/rustc_target/src/spec/x86_64_unknown_hermit.rs20
-rw-r--r--compiler/rustc_trait_selection/messages.ftl10
-rw-r--r--compiler/rustc_trait_selection/src/errors.rs36
-rw-r--r--compiler/rustc_trait_selection/src/solve/alias_relate.rs42
-rw-r--r--compiler/rustc_trait_selection/src/solve/assembly/mod.rs124
-rw-r--r--compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs2
-rw-r--r--compiler/rustc_trait_selection/src/solve/canonicalize.rs25
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt.rs39
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs27
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs167
-rw-r--r--compiler/rustc_trait_selection/src/solve/inherent_projection.rs6
-rw-r--r--compiler/rustc_trait_selection/src/solve/mod.rs13
-rw-r--r--compiler/rustc_trait_selection/src/solve/normalize.rs7
-rw-r--r--compiler/rustc_trait_selection/src/solve/opaques.rs3
-rw-r--r--compiler/rustc_trait_selection/src/solve/project_goals.rs12
-rw-r--r--compiler/rustc_trait_selection/src/solve/trait_goals.rs415
-rw-r--r--compiler/rustc_trait_selection/src/solve/weak_types.rs5
-rw-r--r--compiler/rustc_trait_selection/src/traits/auto_trait.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs7
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs9
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs30
-rw-r--r--compiler/rustc_trait_selection/src/traits/mod.rs28
-rw-r--r--compiler/rustc_trait_selection/src/traits/object_safety.rs6
-rw-r--r--compiler/rustc_trait_selection/src/traits/project.rs26
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs13
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs1
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs86
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/confirmation.rs89
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs87
-rw-r--r--compiler/rustc_trait_selection/src/traits/util.rs6
-rw-r--r--compiler/rustc_trait_selection/src/traits/vtable.rs5
-rw-r--r--compiler/rustc_ty_utils/src/implied_bounds.rs2
-rw-r--r--compiler/rustc_ty_utils/src/instance.rs19
-rw-r--r--compiler/rustc_ty_utils/src/layout.rs252
-rw-r--r--compiler/rustc_ty_utils/src/layout_naive.rs322
-rw-r--r--compiler/rustc_ty_utils/src/layout_sanity_check.rs7
-rw-r--r--compiler/rustc_ty_utils/src/lib.rs2
-rw-r--r--compiler/rustc_ty_utils/src/needs_drop.rs2
-rw-r--r--compiler/rustc_ty_utils/src/ty.rs78
-rw-r--r--compiler/rustc_type_ir/src/sty.rs1
336 files changed, 5349 insertions, 12955 deletions
diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs
index aea88641f82..489a8403c3b 100644
--- a/compiler/rustc_abi/src/layout.rs
+++ b/compiler/rustc_abi/src/layout.rs
@@ -763,7 +763,9 @@ pub trait LayoutCalculator {
         let mut size = Size::ZERO;
         let only_variant = &variants[FIRST_VARIANT];
         for field in only_variant {
-            assert!(field.0.is_sized());
+            if field.0.is_unsized() {
+                self.delay_bug("unsized field in union".to_string());
+            }
 
             align = align.max(field.align());
             max_repr_align = max_repr_align.max(field.max_repr_align());
diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs
index d396f18d59c..835143407d2 100644
--- a/compiler/rustc_abi/src/lib.rs
+++ b/compiler/rustc_abi/src/lib.rs
@@ -49,14 +49,6 @@ bitflags! {
     }
 }
 
-/// Which niches (beyond the `null` niche) are available on references.
-#[derive(Default, Copy, Clone, Hash, Debug, Eq, PartialEq)]
-#[cfg_attr(feature = "nightly", derive(Encodable, Decodable, HashStable_Generic))]
-pub struct ReferenceNichePolicy {
-    pub size: bool,
-    pub align: bool,
-}
-
 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
 #[cfg_attr(feature = "nightly", derive(Encodable, Decodable, HashStable_Generic))]
 pub enum IntegerType {
@@ -355,33 +347,6 @@ impl TargetDataLayout {
     }
 
     #[inline]
-    pub fn target_usize_max(&self) -> u64 {
-        self.pointer_size.unsigned_int_max().try_into().unwrap()
-    }
-
-    #[inline]
-    pub fn target_isize_min(&self) -> i64 {
-        self.pointer_size.signed_int_min().try_into().unwrap()
-    }
-
-    #[inline]
-    pub fn target_isize_max(&self) -> i64 {
-        self.pointer_size.signed_int_max().try_into().unwrap()
-    }
-
-    /// Returns the (inclusive) range of possible addresses for an allocation with
-    /// the given size and alignment.
-    ///
-    /// Note that this doesn't take into account target-specific limitations.
-    #[inline]
-    pub fn address_range_for(&self, size: Size, align: Align) -> (u64, u64) {
-        let end = Size::from_bytes(self.target_usize_max());
-        let min = align.bytes();
-        let max = (end - size).align_down_to(align).bytes();
-        (min, max)
-    }
-
-    #[inline]
     pub fn vector_align(&self, vec_size: Size) -> AbiAndPrefAlign {
         for &(size, align) in &self.vector_align {
             if size == vec_size {
@@ -509,12 +474,6 @@ impl Size {
     }
 
     #[inline]
-    pub fn align_down_to(self, align: Align) -> Size {
-        let mask = align.bytes() - 1;
-        Size::from_bytes(self.bytes() & !mask)
-    }
-
-    #[inline]
     pub fn is_aligned(self, align: Align) -> bool {
         let mask = align.bytes() - 1;
         self.bytes() & mask == 0
@@ -1008,43 +967,6 @@ impl WrappingRange {
         }
     }
 
-    /// Returns `true` if `range` is contained in `self`.
-    #[inline(always)]
-    pub fn contains_range<I: Into<u128> + Ord>(&self, range: RangeInclusive<I>) -> bool {
-        if range.is_empty() {
-            return true;
-        }
-
-        let (vmin, vmax) = range.into_inner();
-        let (vmin, vmax) = (vmin.into(), vmax.into());
-
-        if self.start <= self.end {
-            self.start <= vmin && vmax <= self.end
-        } else {
-            // The last check is needed to cover the following case:
-            // `vmin ... start, end ... vmax`. In this special case there is no gap
-            // between `start` and `end` so we must return true.
-            self.start <= vmin || vmax <= self.end || self.start == self.end + 1
-        }
-    }
-
-    /// Returns `true` if `range` has an overlap with `self`.
-    #[inline(always)]
-    pub fn overlaps_range<I: Into<u128> + Ord>(&self, range: RangeInclusive<I>) -> bool {
-        if range.is_empty() {
-            return false;
-        }
-
-        let (vmin, vmax) = range.into_inner();
-        let (vmin, vmax) = (vmin.into(), vmax.into());
-
-        if self.start <= self.end {
-            self.start <= vmax && vmin <= self.end
-        } else {
-            self.start <= vmax || vmin <= self.end
-        }
-    }
-
     /// Returns `self` with replaced `start`
     #[inline(always)]
     pub fn with_start(mut self, start: u128) -> Self {
@@ -1062,15 +984,9 @@ impl WrappingRange {
     /// Returns `true` if `size` completely fills the range.
     #[inline]
     pub fn is_full_for(&self, size: Size) -> bool {
-        debug_assert!(self.is_in_range_for(size));
-        self.start == (self.end.wrapping_add(1) & size.unsigned_int_max())
-    }
-
-    /// Returns `true` if the range is valid for `size`.
-    #[inline(always)]
-    pub fn is_in_range_for(&self, size: Size) -> bool {
         let max_value = size.unsigned_int_max();
-        self.start <= max_value && self.end <= max_value
+        debug_assert!(self.start <= max_value && self.end <= max_value);
+        self.start == (self.end.wrapping_add(1) & max_value)
     }
 }
 
@@ -1273,7 +1189,7 @@ impl FieldsShape {
             }
             FieldsShape::Array { stride, count } => {
                 let i = u64::try_from(i).unwrap();
-                assert!(i < count);
+                assert!(i < count, "tried to access field {} of array with {} fields", i, count);
                 stride * i
             }
             FieldsShape::Arbitrary { ref offsets, .. } => offsets[FieldIdx::from_usize(i)],
@@ -1429,7 +1345,6 @@ impl Abi {
 
     /// Discard validity range information and allow undef.
     pub fn to_union(&self) -> Self {
-        assert!(self.is_sized());
         match *self {
             Abi::Scalar(s) => Abi::Scalar(s.to_union()),
             Abi::ScalarPair(s1, s2) => Abi::ScalarPair(s1.to_union(), s2.to_union()),
@@ -1511,21 +1426,16 @@ impl Niche {
 
     pub fn reserve<C: HasDataLayout>(&self, cx: &C, count: u128) -> Option<(u128, Scalar)> {
         assert!(count > 0);
-        if count > self.available(cx) {
-            return None;
-        }
 
         let Self { value, valid_range: v, .. } = *self;
-        let max_value = value.size(cx).unsigned_int_max();
-        let distance_end_zero = max_value - v.end;
+        let size = value.size(cx);
+        assert!(size.bits() <= 128);
+        let max_value = size.unsigned_int_max();
 
-        // Null-pointer optimization. This is guaranteed by Rust (at least for `Option<_>`),
-        // and offers better codegen opportunities.
-        if count == 1 && matches!(value, Pointer(_)) && !v.contains(0) {
-            // Select which bound to move to minimize the number of lost niches.
-            let valid_range =
-                if v.start - 1 > distance_end_zero { v.with_end(0) } else { v.with_start(0) };
-            return Some((0, Scalar::Initialized { value, valid_range }));
+        let niche = v.end.wrapping_add(1)..v.start;
+        let available = niche.end.wrapping_sub(niche.start) & max_value;
+        if count > available {
+            return None;
         }
 
         // Extend the range of valid values being reserved by moving either `v.start` or `v.end` bound.
@@ -1548,6 +1458,7 @@ impl Niche {
             let end = v.end.wrapping_add(count) & max_value;
             Some((start, Scalar::Initialized { value, valid_range: v.with_end(end) }))
         };
+        let distance_end_zero = max_value - v.end;
         if v.start > v.end {
             // zero is unavailable because wrapping occurs
             move_end(v)
diff --git a/compiler/rustc_apfloat/Cargo.toml b/compiler/rustc_apfloat/Cargo.toml
deleted file mode 100644
index 98305201bc9..00000000000
--- a/compiler/rustc_apfloat/Cargo.toml
+++ /dev/null
@@ -1,8 +0,0 @@
-[package]
-name = "rustc_apfloat"
-version = "0.0.0"
-edition = "2021"
-
-[dependencies]
-bitflags = "1.2.1"
-smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
diff --git a/compiler/rustc_apfloat/src/ieee.rs b/compiler/rustc_apfloat/src/ieee.rs
deleted file mode 100644
index 2286712f025..00000000000
--- a/compiler/rustc_apfloat/src/ieee.rs
+++ /dev/null
@@ -1,2757 +0,0 @@
-use crate::{Category, ExpInt, IEK_INF, IEK_NAN, IEK_ZERO};
-use crate::{Float, FloatConvert, ParseError, Round, Status, StatusAnd};
-
-use core::cmp::{self, Ordering};
-use core::fmt::{self, Write};
-use core::marker::PhantomData;
-use core::mem;
-use core::ops::Neg;
-use smallvec::{smallvec, SmallVec};
-
-#[must_use]
-pub struct IeeeFloat<S> {
-    /// Absolute significand value (including the integer bit).
-    sig: [Limb; 1],
-
-    /// The signed unbiased exponent of the value.
-    exp: ExpInt,
-
-    /// What kind of floating point number this is.
-    category: Category,
-
-    /// Sign bit of the number.
-    sign: bool,
-
-    marker: PhantomData<S>,
-}
-
-/// Fundamental unit of big integer arithmetic, but also
-/// large to store the largest significands by itself.
-type Limb = u128;
-const LIMB_BITS: usize = 128;
-fn limbs_for_bits(bits: usize) -> usize {
-    (bits + LIMB_BITS - 1) / LIMB_BITS
-}
-
-/// Enum that represents what fraction of the LSB truncated bits of an fp number
-/// represent.
-///
-/// This essentially combines the roles of guard and sticky bits.
-#[must_use]
-#[derive(Copy, Clone, PartialEq, Eq, Debug)]
-enum Loss {
-    // Example of truncated bits:
-    ExactlyZero,  // 000000
-    LessThanHalf, // 0xxxxx  x's not all zero
-    ExactlyHalf,  // 100000
-    MoreThanHalf, // 1xxxxx  x's not all zero
-}
-
-/// Represents floating point arithmetic semantics.
-pub trait Semantics: Sized {
-    /// Total number of bits in the in-memory format.
-    const BITS: usize;
-
-    /// Number of bits in the significand. This includes the integer bit.
-    const PRECISION: usize;
-
-    /// The largest E such that 2<sup>E</sup> is representable; this matches the
-    /// definition of IEEE 754.
-    const MAX_EXP: ExpInt;
-
-    /// The smallest E such that 2<sup>E</sup> is a normalized number; this
-    /// matches the definition of IEEE 754.
-    const MIN_EXP: ExpInt = -Self::MAX_EXP + 1;
-
-    /// The significand bit that marks NaN as quiet.
-    const QNAN_BIT: usize = Self::PRECISION - 2;
-
-    /// The significand bitpattern to mark a NaN as quiet.
-    /// NOTE: for X87DoubleExtended we need to set two bits instead of 2.
-    const QNAN_SIGNIFICAND: Limb = 1 << Self::QNAN_BIT;
-
-    fn from_bits(bits: u128) -> IeeeFloat<Self> {
-        assert!(Self::BITS > Self::PRECISION);
-
-        let sign = bits & (1 << (Self::BITS - 1));
-        let exponent = (bits & !sign) >> (Self::PRECISION - 1);
-        let mut r = IeeeFloat {
-            sig: [bits & ((1 << (Self::PRECISION - 1)) - 1)],
-            // Convert the exponent from its bias representation to a signed integer.
-            exp: (exponent as ExpInt) - Self::MAX_EXP,
-            category: Category::Zero,
-            sign: sign != 0,
-            marker: PhantomData,
-        };
-
-        if r.exp == Self::MIN_EXP - 1 && r.sig == [0] {
-            // Exponent, significand meaningless.
-            r.category = Category::Zero;
-        } else if r.exp == Self::MAX_EXP + 1 && r.sig == [0] {
-            // Exponent, significand meaningless.
-            r.category = Category::Infinity;
-        } else if r.exp == Self::MAX_EXP + 1 && r.sig != [0] {
-            // Sign, exponent, significand meaningless.
-            r.category = Category::NaN;
-        } else {
-            r.category = Category::Normal;
-            if r.exp == Self::MIN_EXP - 1 {
-                // Denormal.
-                r.exp = Self::MIN_EXP;
-            } else {
-                // Set integer bit.
-                sig::set_bit(&mut r.sig, Self::PRECISION - 1);
-            }
-        }
-
-        r
-    }
-
-    fn to_bits(x: IeeeFloat<Self>) -> u128 {
-        assert!(Self::BITS > Self::PRECISION);
-
-        // Split integer bit from significand.
-        let integer_bit = sig::get_bit(&x.sig, Self::PRECISION - 1);
-        let mut significand = x.sig[0] & ((1 << (Self::PRECISION - 1)) - 1);
-        let exponent = match x.category {
-            Category::Normal => {
-                if x.exp == Self::MIN_EXP && !integer_bit {
-                    // Denormal.
-                    Self::MIN_EXP - 1
-                } else {
-                    x.exp
-                }
-            }
-            Category::Zero => {
-                // FIXME(eddyb) Maybe we should guarantee an invariant instead?
-                significand = 0;
-                Self::MIN_EXP - 1
-            }
-            Category::Infinity => {
-                // FIXME(eddyb) Maybe we should guarantee an invariant instead?
-                significand = 0;
-                Self::MAX_EXP + 1
-            }
-            Category::NaN => Self::MAX_EXP + 1,
-        };
-
-        // Convert the exponent from a signed integer to its bias representation.
-        let exponent = (exponent + Self::MAX_EXP) as u128;
-
-        ((x.sign as u128) << (Self::BITS - 1)) | (exponent << (Self::PRECISION - 1)) | significand
-    }
-}
-
-impl<S> Copy for IeeeFloat<S> {}
-impl<S> Clone for IeeeFloat<S> {
-    fn clone(&self) -> Self {
-        *self
-    }
-}
-
-macro_rules! ieee_semantics {
-    ($($name:ident = $sem:ident($bits:tt : $exp_bits:tt)),*) => {
-        $(pub struct $sem;)*
-        $(pub type $name = IeeeFloat<$sem>;)*
-        $(impl Semantics for $sem {
-            const BITS: usize = $bits;
-            const PRECISION: usize = ($bits - 1 - $exp_bits) + 1;
-            const MAX_EXP: ExpInt = (1 << ($exp_bits - 1)) - 1;
-        })*
-    }
-}
-
-ieee_semantics! {
-    Half = HalfS(16:5),
-    Single = SingleS(32:8),
-    Double = DoubleS(64:11),
-    Quad = QuadS(128:15)
-}
-
-pub struct X87DoubleExtendedS;
-pub type X87DoubleExtended = IeeeFloat<X87DoubleExtendedS>;
-impl Semantics for X87DoubleExtendedS {
-    const BITS: usize = 80;
-    const PRECISION: usize = 64;
-    const MAX_EXP: ExpInt = (1 << (15 - 1)) - 1;
-
-    /// For x87 extended precision, we want to make a NaN, not a
-    /// pseudo-NaN. Maybe we should expose the ability to make
-    /// pseudo-NaNs?
-    const QNAN_SIGNIFICAND: Limb = 0b11 << Self::QNAN_BIT;
-
-    /// Integer bit is explicit in this format. Intel hardware (387 and later)
-    /// does not support these bit patterns:
-    ///  exponent = all 1's, integer bit 0, significand 0 ("pseudoinfinity")
-    ///  exponent = all 1's, integer bit 0, significand nonzero ("pseudoNaN")
-    ///  exponent = 0, integer bit 1 ("pseudodenormal")
-    ///  exponent != 0 nor all 1's, integer bit 0 ("unnormal")
-    /// At the moment, the first two are treated as NaNs, the second two as Normal.
-    fn from_bits(bits: u128) -> IeeeFloat<Self> {
-        let sign = bits & (1 << (Self::BITS - 1));
-        let exponent = (bits & !sign) >> Self::PRECISION;
-        let mut r = IeeeFloat {
-            sig: [bits & ((1 << (Self::PRECISION - 1)) - 1)],
-            // Convert the exponent from its bias representation to a signed integer.
-            exp: (exponent as ExpInt) - Self::MAX_EXP,
-            category: Category::Zero,
-            sign: sign != 0,
-            marker: PhantomData,
-        };
-
-        if r.exp == Self::MIN_EXP - 1 && r.sig == [0] {
-            // Exponent, significand meaningless.
-            r.category = Category::Zero;
-        } else if r.exp == Self::MAX_EXP + 1 && r.sig == [1 << (Self::PRECISION - 1)] {
-            // Exponent, significand meaningless.
-            r.category = Category::Infinity;
-        } else if r.exp == Self::MAX_EXP + 1 && r.sig != [1 << (Self::PRECISION - 1)] {
-            // Sign, exponent, significand meaningless.
-            r.category = Category::NaN;
-        } else {
-            r.category = Category::Normal;
-            if r.exp == Self::MIN_EXP - 1 {
-                // Denormal.
-                r.exp = Self::MIN_EXP;
-            }
-        }
-
-        r
-    }
-
-    fn to_bits(x: IeeeFloat<Self>) -> u128 {
-        // Get integer bit from significand.
-        let integer_bit = sig::get_bit(&x.sig, Self::PRECISION - 1);
-        let mut significand = x.sig[0] & ((1 << Self::PRECISION) - 1);
-        let exponent = match x.category {
-            Category::Normal => {
-                if x.exp == Self::MIN_EXP && !integer_bit {
-                    // Denormal.
-                    Self::MIN_EXP - 1
-                } else {
-                    x.exp
-                }
-            }
-            Category::Zero => {
-                // FIXME(eddyb) Maybe we should guarantee an invariant instead?
-                significand = 0;
-                Self::MIN_EXP - 1
-            }
-            Category::Infinity => {
-                // FIXME(eddyb) Maybe we should guarantee an invariant instead?
-                significand = 1 << (Self::PRECISION - 1);
-                Self::MAX_EXP + 1
-            }
-            Category::NaN => Self::MAX_EXP + 1,
-        };
-
-        // Convert the exponent from a signed integer to its bias representation.
-        let exponent = (exponent + Self::MAX_EXP) as u128;
-
-        ((x.sign as u128) << (Self::BITS - 1)) | (exponent << Self::PRECISION) | significand
-    }
-}
-
-float_common_impls!(IeeeFloat<S>);
-
-impl<S: Semantics> PartialEq for IeeeFloat<S> {
-    fn eq(&self, rhs: &Self) -> bool {
-        self.partial_cmp(rhs) == Some(Ordering::Equal)
-    }
-}
-
-impl<S: Semantics> PartialOrd for IeeeFloat<S> {
-    fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
-        match (self.category, rhs.category) {
-            (Category::NaN, _) | (_, Category::NaN) => None,
-
-            (Category::Infinity, Category::Infinity) => Some((!self.sign).cmp(&(!rhs.sign))),
-
-            (Category::Zero, Category::Zero) => Some(Ordering::Equal),
-
-            (Category::Infinity, _) | (Category::Normal, Category::Zero) => {
-                Some((!self.sign).cmp(&self.sign))
-            }
-
-            (_, Category::Infinity) | (Category::Zero, Category::Normal) => {
-                Some(rhs.sign.cmp(&(!rhs.sign)))
-            }
-
-            (Category::Normal, Category::Normal) => {
-                // Two normal numbers. Do they have the same sign?
-                Some((!self.sign).cmp(&(!rhs.sign)).then_with(|| {
-                    // Compare absolute values; invert result if negative.
-                    let result = self.cmp_abs_normal(*rhs);
-
-                    if self.sign { result.reverse() } else { result }
-                }))
-            }
-        }
-    }
-}
-
-impl<S> Neg for IeeeFloat<S> {
-    type Output = Self;
-    fn neg(mut self) -> Self {
-        self.sign = !self.sign;
-        self
-    }
-}
-
-/// Prints this value as a decimal string.
-///
-/// \param precision The maximum number of digits of
-///   precision to output. If there are fewer digits available,
-///   zero padding will not be used unless the value is
-///   integral and small enough to be expressed in
-///   precision digits. 0 means to use the natural
-///   precision of the number.
-/// \param width The maximum number of zeros to
-///   consider inserting before falling back to scientific
-///   notation. 0 means to always use scientific notation.
-///
-/// \param alternate Indicate whether to remove the trailing zero in
-///   fraction part or not. Also setting this parameter to true forces
-///   producing of output more similar to default printf behavior.
-///   Specifically the lower e is used as exponent delimiter and exponent
-///   always contains no less than two digits.
-///
-/// Number       precision    width      Result
-/// ------       ---------    -----      ------
-/// 1.01E+4              5        2       10100
-/// 1.01E+4              4        2       1.01E+4
-/// 1.01E+4              5        1       1.01E+4
-/// 1.01E-2              5        2       0.0101
-/// 1.01E-2              4        2       0.0101
-/// 1.01E-2              4        1       1.01E-2
-impl<S: Semantics> fmt::Display for IeeeFloat<S> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let width = f.width().unwrap_or(3);
-        let alternate = f.alternate();
-
-        match self.category {
-            Category::Infinity => {
-                if self.sign {
-                    return f.write_str("-Inf");
-                } else {
-                    return f.write_str("+Inf");
-                }
-            }
-
-            Category::NaN => return f.write_str("NaN"),
-
-            Category::Zero => {
-                if self.sign {
-                    f.write_char('-')?;
-                }
-
-                if width == 0 {
-                    if alternate {
-                        f.write_str("0.0")?;
-                        if let Some(n) = f.precision() {
-                            for _ in 1..n {
-                                f.write_char('0')?;
-                            }
-                        }
-                        f.write_str("e+00")?;
-                    } else {
-                        f.write_str("0.0E+0")?;
-                    }
-                } else {
-                    f.write_char('0')?;
-                }
-                return Ok(());
-            }
-
-            Category::Normal => {}
-        }
-
-        if self.sign {
-            f.write_char('-')?;
-        }
-
-        // We use enough digits so the number can be round-tripped back to an
-        // APFloat. The formula comes from "How to Print Floating-Point Numbers
-        // Accurately" by Steele and White.
-        // FIXME: Using a formula based purely on the precision is conservative;
-        // we can print fewer digits depending on the actual value being printed.
-
-        // precision = 2 + floor(S::PRECISION / lg_2(10))
-        let precision = f.precision().unwrap_or(2 + S::PRECISION * 59 / 196);
-
-        // Decompose the number into an APInt and an exponent.
-        let mut exp = self.exp - (S::PRECISION as ExpInt - 1);
-        let mut sig = vec![self.sig[0]];
-
-        // Ignore trailing binary zeros.
-        let trailing_zeros = sig[0].trailing_zeros();
-        let _: Loss = sig::shift_right(&mut sig, &mut exp, trailing_zeros as usize);
-
-        // Change the exponent from 2^e to 10^e.
-        if exp == 0 {
-            // Nothing to do.
-        } else if exp > 0 {
-            // Just shift left.
-            let shift = exp as usize;
-            sig.resize(limbs_for_bits(S::PRECISION + shift), 0);
-            sig::shift_left(&mut sig, &mut exp, shift);
-        } else {
-            // exp < 0
-            let mut texp = -exp as usize;
-
-            // We transform this using the identity:
-            //   (N)(2^-e) == (N)(5^e)(10^-e)
-
-            // Multiply significand by 5^e.
-            //   N * 5^0101 == N * 5^(1*1) * 5^(0*2) * 5^(1*4) * 5^(0*8)
-            let mut sig_scratch = vec![];
-            let mut p5 = vec![];
-            let mut p5_scratch = vec![];
-            while texp != 0 {
-                if p5.is_empty() {
-                    p5.push(5);
-                } else {
-                    p5_scratch.resize(p5.len() * 2, 0);
-                    let _: Loss =
-                        sig::mul(&mut p5_scratch, &mut 0, &p5, &p5, p5.len() * 2 * LIMB_BITS);
-                    while p5_scratch.last() == Some(&0) {
-                        p5_scratch.pop();
-                    }
-                    mem::swap(&mut p5, &mut p5_scratch);
-                }
-                if texp & 1 != 0 {
-                    sig_scratch.resize(sig.len() + p5.len(), 0);
-                    let _: Loss = sig::mul(
-                        &mut sig_scratch,
-                        &mut 0,
-                        &sig,
-                        &p5,
-                        (sig.len() + p5.len()) * LIMB_BITS,
-                    );
-                    while sig_scratch.last() == Some(&0) {
-                        sig_scratch.pop();
-                    }
-                    mem::swap(&mut sig, &mut sig_scratch);
-                }
-                texp >>= 1;
-            }
-        }
-
-        // Fill the buffer.
-        let mut buffer = vec![];
-
-        // Ignore digits from the significand until it is no more
-        // precise than is required for the desired precision.
-        // 196/59 is a very slight overestimate of lg_2(10).
-        let required = (precision * 196 + 58) / 59;
-        let mut discard_digits = sig::omsb(&sig).saturating_sub(required) * 59 / 196;
-        let mut in_trail = true;
-        while !sig.is_empty() {
-            // Perform short division by 10 to extract the rightmost digit.
-            // rem <- sig % 10
-            // sig <- sig / 10
-            let mut rem = 0;
-
-            // Use 64-bit division and remainder, with 32-bit chunks from sig.
-            sig::each_chunk(&mut sig, 32, |chunk| {
-                let chunk = chunk as u32;
-                let combined = ((rem as u64) << 32) | (chunk as u64);
-                rem = (combined % 10) as u8;
-                (combined / 10) as u32 as Limb
-            });
-
-            // Reduce the significand to avoid wasting time dividing 0's.
-            while sig.last() == Some(&0) {
-                sig.pop();
-            }
-
-            let digit = rem;
-
-            // Ignore digits we don't need.
-            if discard_digits > 0 {
-                discard_digits -= 1;
-                exp += 1;
-                continue;
-            }
-
-            // Drop trailing zeros.
-            if in_trail && digit == 0 {
-                exp += 1;
-            } else {
-                in_trail = false;
-                buffer.push(b'0' + digit);
-            }
-        }
-
-        assert!(!buffer.is_empty(), "no characters in buffer!");
-
-        // Drop down to precision.
-        // FIXME: don't do more precise calculations above than are required.
-        if buffer.len() > precision {
-            // The most significant figures are the last ones in the buffer.
-            let mut first_sig = buffer.len() - precision;
-
-            // Round.
-            // FIXME: this probably shouldn't use 'round half up'.
-
-            // Rounding down is just a truncation, except we also want to drop
-            // trailing zeros from the new result.
-            if buffer[first_sig - 1] < b'5' {
-                while first_sig < buffer.len() && buffer[first_sig] == b'0' {
-                    first_sig += 1;
-                }
-            } else {
-                // Rounding up requires a decimal add-with-carry. If we continue
-                // the carry, the newly-introduced zeros will just be truncated.
-                for x in &mut buffer[first_sig..] {
-                    if *x == b'9' {
-                        first_sig += 1;
-                    } else {
-                        *x += 1;
-                        break;
-                    }
-                }
-            }
-
-            exp += first_sig as ExpInt;
-            buffer.drain(..first_sig);
-
-            // If we carried through, we have exactly one digit of precision.
-            if buffer.is_empty() {
-                buffer.push(b'1');
-            }
-        }
-
-        let digits = buffer.len();
-
-        // Check whether we should use scientific notation.
-        let scientific = if width == 0 {
-            true
-        } else if exp >= 0 {
-            // 765e3 --> 765000
-            //              ^^^
-            // But we shouldn't make the number look more precise than it is.
-            exp as usize > width || digits + exp as usize > precision
-        } else {
-            // Power of the most significant digit.
-            let msd = exp + (digits - 1) as ExpInt;
-            if msd >= 0 {
-                // 765e-2 == 7.65
-                false
-            } else {
-                // 765e-5 == 0.00765
-                //           ^ ^^
-                -msd as usize > width
-            }
-        };
-
-        // Scientific formatting is pretty straightforward.
-        if scientific {
-            exp += digits as ExpInt - 1;
-
-            f.write_char(buffer[digits - 1] as char)?;
-            f.write_char('.')?;
-            let truncate_zero = !alternate;
-            if digits == 1 && truncate_zero {
-                f.write_char('0')?;
-            } else {
-                for &d in buffer[..digits - 1].iter().rev() {
-                    f.write_char(d as char)?;
-                }
-            }
-            // Fill with zeros up to precision.
-            if !truncate_zero && precision > digits - 1 {
-                for _ in 0..=precision - digits {
-                    f.write_char('0')?;
-                }
-            }
-            // For alternate we use lower 'e'.
-            f.write_char(if alternate { 'e' } else { 'E' })?;
-
-            // Exponent always at least two digits if we do not truncate zeros.
-            if truncate_zero {
-                write!(f, "{:+}", exp)?;
-            } else {
-                write!(f, "{:+03}", exp)?;
-            }
-
-            return Ok(());
-        }
-
-        // Non-scientific, positive exponents.
-        if exp >= 0 {
-            for &d in buffer.iter().rev() {
-                f.write_char(d as char)?;
-            }
-            for _ in 0..exp {
-                f.write_char('0')?;
-            }
-            return Ok(());
-        }
-
-        // Non-scientific, negative exponents.
-        let unit_place = -exp as usize;
-        if unit_place < digits {
-            for &d in buffer[unit_place..].iter().rev() {
-                f.write_char(d as char)?;
-            }
-            f.write_char('.')?;
-            for &d in buffer[..unit_place].iter().rev() {
-                f.write_char(d as char)?;
-            }
-        } else {
-            f.write_str("0.")?;
-            for _ in digits..unit_place {
-                f.write_char('0')?;
-            }
-            for &d in buffer.iter().rev() {
-                f.write_char(d as char)?;
-            }
-        }
-
-        Ok(())
-    }
-}
-
-impl<S: Semantics> fmt::Debug for IeeeFloat<S> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(
-            f,
-            "{}({:?} | {}{:?} * 2^{})",
-            self,
-            self.category,
-            if self.sign { "-" } else { "+" },
-            self.sig,
-            self.exp
-        )
-    }
-}
-
-impl<S: Semantics> Float for IeeeFloat<S> {
-    const BITS: usize = S::BITS;
-    const PRECISION: usize = S::PRECISION;
-    const MAX_EXP: ExpInt = S::MAX_EXP;
-    const MIN_EXP: ExpInt = S::MIN_EXP;
-
-    const ZERO: Self = IeeeFloat {
-        sig: [0],
-        exp: S::MIN_EXP - 1,
-        category: Category::Zero,
-        sign: false,
-        marker: PhantomData,
-    };
-
-    const INFINITY: Self = IeeeFloat {
-        sig: [0],
-        exp: S::MAX_EXP + 1,
-        category: Category::Infinity,
-        sign: false,
-        marker: PhantomData,
-    };
-
-    // FIXME(eddyb) remove when qnan becomes const fn.
-    const NAN: Self = IeeeFloat {
-        sig: [S::QNAN_SIGNIFICAND],
-        exp: S::MAX_EXP + 1,
-        category: Category::NaN,
-        sign: false,
-        marker: PhantomData,
-    };
-
-    fn qnan(payload: Option<u128>) -> Self {
-        IeeeFloat {
-            sig: [S::QNAN_SIGNIFICAND
-                | payload.map_or(0, |payload| {
-                    // Zero out the excess bits of the significand.
-                    payload & ((1 << S::QNAN_BIT) - 1)
-                })],
-            exp: S::MAX_EXP + 1,
-            category: Category::NaN,
-            sign: false,
-            marker: PhantomData,
-        }
-    }
-
-    fn snan(payload: Option<u128>) -> Self {
-        let mut snan = Self::qnan(payload);
-
-        // We always have to clear the QNaN bit to make it an SNaN.
-        sig::clear_bit(&mut snan.sig, S::QNAN_BIT);
-
-        // If there are no bits set in the payload, we have to set
-        // *something* to make it a NaN instead of an infinity;
-        // conventionally, this is the next bit down from the QNaN bit.
-        if snan.sig[0] & !S::QNAN_SIGNIFICAND == 0 {
-            sig::set_bit(&mut snan.sig, S::QNAN_BIT - 1);
-        }
-
-        snan
-    }
-
-    fn largest() -> Self {
-        // We want (in interchange format):
-        //   exponent = 1..10
-        //   significand = 1..1
-        IeeeFloat {
-            sig: [(1 << S::PRECISION) - 1],
-            exp: S::MAX_EXP,
-            category: Category::Normal,
-            sign: false,
-            marker: PhantomData,
-        }
-    }
-
-    // We want (in interchange format):
-    //   exponent = 0..0
-    //   significand = 0..01
-    const SMALLEST: Self = IeeeFloat {
-        sig: [1],
-        exp: S::MIN_EXP,
-        category: Category::Normal,
-        sign: false,
-        marker: PhantomData,
-    };
-
-    fn smallest_normalized() -> Self {
-        // We want (in interchange format):
-        //   exponent = 0..0
-        //   significand = 10..0
-        IeeeFloat {
-            sig: [1 << (S::PRECISION - 1)],
-            exp: S::MIN_EXP,
-            category: Category::Normal,
-            sign: false,
-            marker: PhantomData,
-        }
-    }
-
-    fn add_r(mut self, rhs: Self, round: Round) -> StatusAnd<Self> {
-        let status = match (self.category, rhs.category) {
-            (Category::Infinity, Category::Infinity) => {
-                // Differently signed infinities can only be validly
-                // subtracted.
-                if self.sign != rhs.sign {
-                    self = Self::NAN;
-                    Status::INVALID_OP
-                } else {
-                    Status::OK
-                }
-            }
-
-            // Sign may depend on rounding mode; handled below.
-            (_, Category::Zero) | (Category::NaN, _) | (Category::Infinity, Category::Normal) => {
-                Status::OK
-            }
-
-            (Category::Zero, _) | (_, Category::NaN | Category::Infinity) => {
-                self = rhs;
-                Status::OK
-            }
-
-            // This return code means it was not a simple case.
-            (Category::Normal, Category::Normal) => {
-                let loss = sig::add_or_sub(
-                    &mut self.sig,
-                    &mut self.exp,
-                    &mut self.sign,
-                    &mut [rhs.sig[0]],
-                    rhs.exp,
-                    rhs.sign,
-                );
-                let status;
-                self = unpack!(status=, self.normalize(round, loss));
-
-                // Can only be zero if we lost no fraction.
-                assert!(self.category != Category::Zero || loss == Loss::ExactlyZero);
-
-                status
-            }
-        };
-
-        // If two numbers add (exactly) to zero, IEEE 754 decrees it is a
-        // positive zero unless rounding to minus infinity, except that
-        // adding two like-signed zeroes gives that zero.
-        if self.category == Category::Zero
-            && (rhs.category != Category::Zero || self.sign != rhs.sign)
-        {
-            self.sign = round == Round::TowardNegative;
-        }
-
-        status.and(self)
-    }
-
-    fn mul_r(mut self, rhs: Self, round: Round) -> StatusAnd<Self> {
-        self.sign ^= rhs.sign;
-
-        match (self.category, rhs.category) {
-            (Category::NaN, _) => {
-                self.sign = false;
-                Status::OK.and(self)
-            }
-
-            (_, Category::NaN) => {
-                self.sign = false;
-                self.category = Category::NaN;
-                self.sig = rhs.sig;
-                Status::OK.and(self)
-            }
-
-            (Category::Zero, Category::Infinity) | (Category::Infinity, Category::Zero) => {
-                Status::INVALID_OP.and(Self::NAN)
-            }
-
-            (_, Category::Infinity) | (Category::Infinity, _) => {
-                self.category = Category::Infinity;
-                Status::OK.and(self)
-            }
-
-            (Category::Zero, _) | (_, Category::Zero) => {
-                self.category = Category::Zero;
-                Status::OK.and(self)
-            }
-
-            (Category::Normal, Category::Normal) => {
-                self.exp += rhs.exp;
-                let mut wide_sig = [0; 2];
-                let loss =
-                    sig::mul(&mut wide_sig, &mut self.exp, &self.sig, &rhs.sig, S::PRECISION);
-                self.sig = [wide_sig[0]];
-                let mut status;
-                self = unpack!(status=, self.normalize(round, loss));
-                if loss != Loss::ExactlyZero {
-                    status |= Status::INEXACT;
-                }
-                status.and(self)
-            }
-        }
-    }
-
-    fn mul_add_r(mut self, multiplicand: Self, addend: Self, round: Round) -> StatusAnd<Self> {
-        // If and only if all arguments are normal do we need to do an
-        // extended-precision calculation.
-        if !self.is_finite_non_zero() || !multiplicand.is_finite_non_zero() || !addend.is_finite() {
-            let mut status;
-            self = unpack!(status=, self.mul_r(multiplicand, round));
-
-            // FS can only be Status::OK or Status::INVALID_OP. There is no more work
-            // to do in the latter case. The IEEE-754R standard says it is
-            // implementation-defined in this case whether, if ADDEND is a
-            // quiet NaN, we raise invalid op; this implementation does so.
-            //
-            // If we need to do the addition we can do so with normal
-            // precision.
-            if status == Status::OK {
-                self = unpack!(status=, self.add_r(addend, round));
-            }
-            return status.and(self);
-        }
-
-        // Post-multiplication sign, before addition.
-        self.sign ^= multiplicand.sign;
-
-        // Allocate space for twice as many bits as the original significand, plus one
-        // extra bit for the addition to overflow into.
-        assert!(limbs_for_bits(S::PRECISION * 2 + 1) <= 2);
-        let mut wide_sig = sig::widening_mul(self.sig[0], multiplicand.sig[0]);
-
-        let mut loss = Loss::ExactlyZero;
-        let mut omsb = sig::omsb(&wide_sig);
-        self.exp += multiplicand.exp;
-
-        // Assume the operands involved in the multiplication are single-precision
-        // FP, and the two multiplicants are:
-        //     lhs = a23 . a22 ... a0 * 2^e1
-        //     rhs = b23 . b22 ... b0 * 2^e2
-        // the result of multiplication is:
-        //     lhs = c48 c47 c46 . c45 ... c0 * 2^(e1+e2)
-        // Note that there are three significant bits at the left-hand side of the
-        // radix point: two for the multiplication, and an overflow bit for the
-        // addition (that will always be zero at this point). Move the radix point
-        // toward left by two bits, and adjust exponent accordingly.
-        self.exp += 2;
-
-        if addend.is_non_zero() {
-            // Normalize our MSB to one below the top bit to allow for overflow.
-            let ext_precision = 2 * S::PRECISION + 1;
-            if omsb != ext_precision - 1 {
-                assert!(ext_precision > omsb);
-                sig::shift_left(&mut wide_sig, &mut self.exp, (ext_precision - 1) - omsb);
-            }
-
-            // The intermediate result of the multiplication has "2 * S::PRECISION"
-            // significant bit; adjust the addend to be consistent with mul result.
-            let mut ext_addend_sig = [addend.sig[0], 0];
-
-            // Extend the addend significand to ext_precision - 1. This guarantees
-            // that the high bit of the significand is zero (same as wide_sig),
-            // so the addition will overflow (if it does overflow at all) into the top bit.
-            sig::shift_left(&mut ext_addend_sig, &mut 0, ext_precision - 1 - S::PRECISION);
-            loss = sig::add_or_sub(
-                &mut wide_sig,
-                &mut self.exp,
-                &mut self.sign,
-                &mut ext_addend_sig,
-                addend.exp + 1,
-                addend.sign,
-            );
-
-            omsb = sig::omsb(&wide_sig);
-        }
-
-        // Convert the result having "2 * S::PRECISION" significant-bits back to the one
-        // having "S::PRECISION" significant-bits. First, move the radix point from
-        // position "2*S::PRECISION - 1" to "S::PRECISION - 1". The exponent need to be
-        // adjusted by "2*S::PRECISION - 1" - "S::PRECISION - 1" = "S::PRECISION".
-        self.exp -= S::PRECISION as ExpInt + 1;
-
-        // In case MSB resides at the left-hand side of radix point, shift the
-        // mantissa right by some amount to make sure the MSB reside right before
-        // the radix point (i.e., "MSB . rest-significant-bits").
-        if omsb > S::PRECISION {
-            let bits = omsb - S::PRECISION;
-            loss = sig::shift_right(&mut wide_sig, &mut self.exp, bits).combine(loss);
-        }
-
-        self.sig[0] = wide_sig[0];
-
-        let mut status;
-        self = unpack!(status=, self.normalize(round, loss));
-        if loss != Loss::ExactlyZero {
-            status |= Status::INEXACT;
-        }
-
-        // If two numbers add (exactly) to zero, IEEE 754 decrees it is a
-        // positive zero unless rounding to minus infinity, except that
-        // adding two like-signed zeroes gives that zero.
-        if self.category == Category::Zero
-            && !status.intersects(Status::UNDERFLOW)
-            && self.sign != addend.sign
-        {
-            self.sign = round == Round::TowardNegative;
-        }
-
-        status.and(self)
-    }
-
-    fn div_r(mut self, rhs: Self, round: Round) -> StatusAnd<Self> {
-        self.sign ^= rhs.sign;
-
-        match (self.category, rhs.category) {
-            (Category::NaN, _) => {
-                self.sign = false;
-                Status::OK.and(self)
-            }
-
-            (_, Category::NaN) => {
-                self.category = Category::NaN;
-                self.sig = rhs.sig;
-                self.sign = false;
-                Status::OK.and(self)
-            }
-
-            (Category::Infinity, Category::Infinity) | (Category::Zero, Category::Zero) => {
-                Status::INVALID_OP.and(Self::NAN)
-            }
-
-            (Category::Infinity | Category::Zero, _) => Status::OK.and(self),
-
-            (Category::Normal, Category::Infinity) => {
-                self.category = Category::Zero;
-                Status::OK.and(self)
-            }
-
-            (Category::Normal, Category::Zero) => {
-                self.category = Category::Infinity;
-                Status::DIV_BY_ZERO.and(self)
-            }
-
-            (Category::Normal, Category::Normal) => {
-                self.exp -= rhs.exp;
-                let dividend = self.sig[0];
-                let loss = sig::div(
-                    &mut self.sig,
-                    &mut self.exp,
-                    &mut [dividend],
-                    &mut [rhs.sig[0]],
-                    S::PRECISION,
-                );
-                let mut status;
-                self = unpack!(status=, self.normalize(round, loss));
-                if loss != Loss::ExactlyZero {
-                    status |= Status::INEXACT;
-                }
-                status.and(self)
-            }
-        }
-    }
-
-    fn c_fmod(mut self, rhs: Self) -> StatusAnd<Self> {
-        match (self.category, rhs.category) {
-            (Category::NaN, _)
-            | (Category::Zero, Category::Infinity | Category::Normal)
-            | (Category::Normal, Category::Infinity) => Status::OK.and(self),
-
-            (_, Category::NaN) => {
-                self.sign = false;
-                self.category = Category::NaN;
-                self.sig = rhs.sig;
-                Status::OK.and(self)
-            }
-
-            (Category::Infinity, _) | (_, Category::Zero) => Status::INVALID_OP.and(Self::NAN),
-
-            (Category::Normal, Category::Normal) => {
-                while self.is_finite_non_zero()
-                    && rhs.is_finite_non_zero()
-                    && self.cmp_abs_normal(rhs) != Ordering::Less
-                {
-                    let mut v = rhs.scalbn(self.ilogb() - rhs.ilogb());
-                    if self.cmp_abs_normal(v) == Ordering::Less {
-                        v = v.scalbn(-1);
-                    }
-                    v.sign = self.sign;
-
-                    let status;
-                    self = unpack!(status=, self - v);
-                    assert_eq!(status, Status::OK);
-                }
-                Status::OK.and(self)
-            }
-        }
-    }
-
-    fn round_to_integral(self, round: Round) -> StatusAnd<Self> {
-        // If the exponent is large enough, we know that this value is already
-        // integral, and the arithmetic below would potentially cause it to saturate
-        // to +/-Inf. Bail out early instead.
-        if self.is_finite_non_zero() && self.exp + 1 >= S::PRECISION as ExpInt {
-            return Status::OK.and(self);
-        }
-
-        // The algorithm here is quite simple: we add 2^(p-1), where p is the
-        // precision of our format, and then subtract it back off again. The choice
-        // of rounding modes for the addition/subtraction determines the rounding mode
-        // for our integral rounding as well.
-        // NOTE: When the input value is negative, we do subtraction followed by
-        // addition instead.
-        assert!(S::PRECISION <= 128);
-        let mut status;
-        let magic_const = unpack!(status=, Self::from_u128(1 << (S::PRECISION - 1)));
-        let magic_const = magic_const.copy_sign(self);
-
-        if status != Status::OK {
-            return status.and(self);
-        }
-
-        let mut r = self;
-        r = unpack!(status=, r.add_r(magic_const, round));
-        if status != Status::OK && status != Status::INEXACT {
-            return status.and(self);
-        }
-
-        // Restore the input sign to handle 0.0/-0.0 cases correctly.
-        r.sub_r(magic_const, round).map(|r| r.copy_sign(self))
-    }
-
-    fn next_up(mut self) -> StatusAnd<Self> {
-        // Compute nextUp(x), handling each float category separately.
-        match self.category {
-            Category::Infinity => {
-                if self.sign {
-                    // nextUp(-inf) = -largest
-                    Status::OK.and(-Self::largest())
-                } else {
-                    // nextUp(+inf) = +inf
-                    Status::OK.and(self)
-                }
-            }
-            Category::NaN => {
-                // IEEE-754R 2008 6.2 Par 2: nextUp(sNaN) = qNaN. Set Invalid flag.
-                // IEEE-754R 2008 6.2: nextUp(qNaN) = qNaN. Must be identity so we do not
-                //                     change the payload.
-                if self.is_signaling() {
-                    // For consistency, propagate the sign of the sNaN to the qNaN.
-                    Status::INVALID_OP.and(Self::NAN.copy_sign(self))
-                } else {
-                    Status::OK.and(self)
-                }
-            }
-            Category::Zero => {
-                // nextUp(pm 0) = +smallest
-                Status::OK.and(Self::SMALLEST)
-            }
-            Category::Normal => {
-                // nextUp(-smallest) = -0
-                if self.is_smallest() && self.sign {
-                    return Status::OK.and(-Self::ZERO);
-                }
-
-                // nextUp(largest) == INFINITY
-                if self.is_largest() && !self.sign {
-                    return Status::OK.and(Self::INFINITY);
-                }
-
-                // Excluding the integral bit. This allows us to test for binade boundaries.
-                let sig_mask = (1 << (S::PRECISION - 1)) - 1;
-
-                // nextUp(normal) == normal + inc.
-                if self.sign {
-                    // If we are negative, we need to decrement the significand.
-
-                    // We only cross a binade boundary that requires adjusting the exponent
-                    // if:
-                    //   1. exponent != S::MIN_EXP. This implies we are not in the
-                    //   smallest binade or are dealing with denormals.
-                    //   2. Our significand excluding the integral bit is all zeros.
-                    let crossing_binade_boundary =
-                        self.exp != S::MIN_EXP && self.sig[0] & sig_mask == 0;
-
-                    // Decrement the significand.
-                    //
-                    // We always do this since:
-                    //   1. If we are dealing with a non-binade decrement, by definition we
-                    //   just decrement the significand.
-                    //   2. If we are dealing with a normal -> normal binade decrement, since
-                    //   we have an explicit integral bit the fact that all bits but the
-                    //   integral bit are zero implies that subtracting one will yield a
-                    //   significand with 0 integral bit and 1 in all other spots. Thus we
-                    //   must just adjust the exponent and set the integral bit to 1.
-                    //   3. If we are dealing with a normal -> denormal binade decrement,
-                    //   since we set the integral bit to 0 when we represent denormals, we
-                    //   just decrement the significand.
-                    sig::decrement(&mut self.sig);
-
-                    if crossing_binade_boundary {
-                        // Our result is a normal number. Do the following:
-                        // 1. Set the integral bit to 1.
-                        // 2. Decrement the exponent.
-                        sig::set_bit(&mut self.sig, S::PRECISION - 1);
-                        self.exp -= 1;
-                    }
-                } else {
-                    // If we are positive, we need to increment the significand.
-
-                    // We only cross a binade boundary that requires adjusting the exponent if
-                    // the input is not a denormal and all of said input's significand bits
-                    // are set. If all of said conditions are true: clear the significand, set
-                    // the integral bit to 1, and increment the exponent. If we have a
-                    // denormal always increment since moving denormals and the numbers in the
-                    // smallest normal binade have the same exponent in our representation.
-                    let crossing_binade_boundary =
-                        !self.is_denormal() && self.sig[0] & sig_mask == sig_mask;
-
-                    if crossing_binade_boundary {
-                        self.sig = [0];
-                        sig::set_bit(&mut self.sig, S::PRECISION - 1);
-                        assert_ne!(
-                            self.exp,
-                            S::MAX_EXP,
-                            "We can not increment an exponent beyond the MAX_EXP \
-                             allowed by the given floating point semantics."
-                        );
-                        self.exp += 1;
-                    } else {
-                        sig::increment(&mut self.sig);
-                    }
-                }
-                Status::OK.and(self)
-            }
-        }
-    }
-
-    fn from_bits(input: u128) -> Self {
-        // Dispatch to semantics.
-        S::from_bits(input)
-    }
-
-    fn from_u128_r(input: u128, round: Round) -> StatusAnd<Self> {
-        IeeeFloat {
-            sig: [input],
-            exp: S::PRECISION as ExpInt - 1,
-            category: Category::Normal,
-            sign: false,
-            marker: PhantomData,
-        }
-        .normalize(round, Loss::ExactlyZero)
-    }
-
-    fn from_str_r(mut s: &str, mut round: Round) -> Result<StatusAnd<Self>, ParseError> {
-        if s.is_empty() {
-            return Err(ParseError("Invalid string length"));
-        }
-
-        // Handle special cases.
-        match s {
-            "inf" | "INFINITY" => return Ok(Status::OK.and(Self::INFINITY)),
-            "-inf" | "-INFINITY" => return Ok(Status::OK.and(-Self::INFINITY)),
-            "nan" | "NaN" => return Ok(Status::OK.and(Self::NAN)),
-            "-nan" | "-NaN" => return Ok(Status::OK.and(-Self::NAN)),
-            _ => {}
-        }
-
-        // Handle a leading minus sign.
-        let minus = s.starts_with('-');
-        if minus || s.starts_with('+') {
-            s = &s[1..];
-            if s.is_empty() {
-                return Err(ParseError("String has no digits"));
-            }
-        }
-
-        // Adjust the rounding mode for the absolute value below.
-        if minus {
-            round = -round;
-        }
-
-        let r = if s.starts_with("0x") || s.starts_with("0X") {
-            s = &s[2..];
-            if s.is_empty() {
-                return Err(ParseError("Invalid string"));
-            }
-            Self::from_hexadecimal_string(s, round)?
-        } else {
-            Self::from_decimal_string(s, round)?
-        };
-
-        Ok(r.map(|r| if minus { -r } else { r }))
-    }
-
-    fn to_bits(self) -> u128 {
-        // Dispatch to semantics.
-        S::to_bits(self)
-    }
-
-    fn to_u128_r(self, width: usize, round: Round, is_exact: &mut bool) -> StatusAnd<u128> {
-        // The result of trying to convert a number too large.
-        let overflow = if self.sign {
-            // Negative numbers cannot be represented as unsigned.
-            0
-        } else {
-            // Largest unsigned integer of the given width.
-            !0 >> (128 - width)
-        };
-
-        *is_exact = false;
-
-        match self.category {
-            Category::NaN => Status::INVALID_OP.and(0),
-
-            Category::Infinity => Status::INVALID_OP.and(overflow),
-
-            Category::Zero => {
-                // Negative zero can't be represented as an int.
-                *is_exact = !self.sign;
-                Status::OK.and(0)
-            }
-
-            Category::Normal => {
-                let mut r = 0;
-
-                // Step 1: place our absolute value, with any fraction truncated, in
-                // the destination.
-                let truncated_bits = if self.exp < 0 {
-                    // Our absolute value is less than one; truncate everything.
-                    // For exponent -1 the integer bit represents .5, look at that.
-                    // For smaller exponents leftmost truncated bit is 0.
-                    S::PRECISION - 1 + (-self.exp) as usize
-                } else {
-                    // We want the most significant (exponent + 1) bits; the rest are
-                    // truncated.
-                    let bits = self.exp as usize + 1;
-
-                    // Hopelessly large in magnitude?
-                    if bits > width {
-                        return Status::INVALID_OP.and(overflow);
-                    }
-
-                    if bits < S::PRECISION {
-                        // We truncate (S::PRECISION - bits) bits.
-                        r = self.sig[0] >> (S::PRECISION - bits);
-                        S::PRECISION - bits
-                    } else {
-                        // We want at least as many bits as are available.
-                        r = self.sig[0] << (bits - S::PRECISION);
-                        0
-                    }
-                };
-
-                // Step 2: work out any lost fraction, and increment the absolute
-                // value if we would round away from zero.
-                let mut loss = Loss::ExactlyZero;
-                if truncated_bits > 0 {
-                    loss = Loss::through_truncation(&self.sig, truncated_bits);
-                    if loss != Loss::ExactlyZero
-                        && self.round_away_from_zero(round, loss, truncated_bits)
-                    {
-                        r = r.wrapping_add(1);
-                        if r == 0 {
-                            return Status::INVALID_OP.and(overflow); // Overflow.
-                        }
-                    }
-                }
-
-                // Step 3: check if we fit in the destination.
-                if r > overflow {
-                    return Status::INVALID_OP.and(overflow);
-                }
-
-                if loss == Loss::ExactlyZero {
-                    *is_exact = true;
-                    Status::OK.and(r)
-                } else {
-                    Status::INEXACT.and(r)
-                }
-            }
-        }
-    }
-
-    fn cmp_abs_normal(self, rhs: Self) -> Ordering {
-        assert!(self.is_finite_non_zero());
-        assert!(rhs.is_finite_non_zero());
-
-        // If exponents are equal, do an unsigned comparison of the significands.
-        self.exp.cmp(&rhs.exp).then_with(|| sig::cmp(&self.sig, &rhs.sig))
-    }
-
-    fn bitwise_eq(self, rhs: Self) -> bool {
-        if self.category != rhs.category || self.sign != rhs.sign {
-            return false;
-        }
-
-        if self.category == Category::Zero || self.category == Category::Infinity {
-            return true;
-        }
-
-        if self.is_finite_non_zero() && self.exp != rhs.exp {
-            return false;
-        }
-
-        self.sig == rhs.sig
-    }
-
-    fn is_negative(self) -> bool {
-        self.sign
-    }
-
-    fn is_denormal(self) -> bool {
-        self.is_finite_non_zero()
-            && self.exp == S::MIN_EXP
-            && !sig::get_bit(&self.sig, S::PRECISION - 1)
-    }
-
-    fn is_signaling(self) -> bool {
-        // IEEE-754R 2008 6.2.1: A signaling NaN bit string should be encoded with the
-        // first bit of the trailing significand being 0.
-        self.is_nan() && !sig::get_bit(&self.sig, S::QNAN_BIT)
-    }
-
-    fn category(self) -> Category {
-        self.category
-    }
-
-    fn get_exact_inverse(self) -> Option<Self> {
-        // Special floats and denormals have no exact inverse.
-        if !self.is_finite_non_zero() {
-            return None;
-        }
-
-        // Check that the number is a power of two by making sure that only the
-        // integer bit is set in the significand.
-        if self.sig != [1 << (S::PRECISION - 1)] {
-            return None;
-        }
-
-        // Get the inverse.
-        let mut reciprocal = Self::from_u128(1).value;
-        let status;
-        reciprocal = unpack!(status=, reciprocal / self);
-        if status != Status::OK {
-            return None;
-        }
-
-        // Avoid multiplication with a denormal, it is not safe on all platforms and
-        // may be slower than a normal division.
-        if reciprocal.is_denormal() {
-            return None;
-        }
-
-        assert!(reciprocal.is_finite_non_zero());
-        assert_eq!(reciprocal.sig, [1 << (S::PRECISION - 1)]);
-
-        Some(reciprocal)
-    }
-
-    fn ilogb(mut self) -> ExpInt {
-        if self.is_nan() {
-            return IEK_NAN;
-        }
-        if self.is_zero() {
-            return IEK_ZERO;
-        }
-        if self.is_infinite() {
-            return IEK_INF;
-        }
-        if !self.is_denormal() {
-            return self.exp;
-        }
-
-        let sig_bits = (S::PRECISION - 1) as ExpInt;
-        self.exp += sig_bits;
-        self = self.normalize(Round::NearestTiesToEven, Loss::ExactlyZero).value;
-        self.exp - sig_bits
-    }
-
-    fn scalbn_r(mut self, exp: ExpInt, round: Round) -> Self {
-        // If exp is wildly out-of-scale, simply adding it to self.exp will
-        // overflow; clamp it to a safe range before adding, but ensure that the range
-        // is large enough that the clamp does not change the result. The range we
-        // need to support is the difference between the largest possible exponent and
-        // the normalized exponent of half the smallest denormal.
-
-        let sig_bits = (S::PRECISION - 1) as i32;
-        let max_change = S::MAX_EXP as i32 - (S::MIN_EXP as i32 - sig_bits) + 1;
-
-        // Clamp to one past the range ends to let normalize handle overflow.
-        let exp_change = cmp::min(cmp::max(exp as i32, -max_change - 1), max_change);
-        self.exp = self.exp.saturating_add(exp_change as ExpInt);
-        self = self.normalize(round, Loss::ExactlyZero).value;
-        if self.is_nan() {
-            sig::set_bit(&mut self.sig, S::QNAN_BIT);
-        }
-        self
-    }
-
-    fn frexp_r(mut self, exp: &mut ExpInt, round: Round) -> Self {
-        *exp = self.ilogb();
-
-        // Quiet signalling nans.
-        if *exp == IEK_NAN {
-            sig::set_bit(&mut self.sig, S::QNAN_BIT);
-            return self;
-        }
-
-        if *exp == IEK_INF {
-            return self;
-        }
-
-        // 1 is added because frexp is defined to return a normalized fraction in
-        // +/-[0.5, 1.0), rather than the usual +/-[1.0, 2.0).
-        if *exp == IEK_ZERO {
-            *exp = 0;
-        } else {
-            *exp += 1;
-        }
-        self.scalbn_r(-*exp, round)
-    }
-}
-
-impl<S: Semantics, T: Semantics> FloatConvert<IeeeFloat<T>> for IeeeFloat<S> {
-    fn convert_r(self, round: Round, loses_info: &mut bool) -> StatusAnd<IeeeFloat<T>> {
-        let mut r = IeeeFloat {
-            sig: self.sig,
-            exp: self.exp,
-            category: self.category,
-            sign: self.sign,
-            marker: PhantomData,
-        };
-
-        // x86 has some unusual NaNs which cannot be represented in any other
-        // format; note them here.
-        fn is_x87_double_extended<S: Semantics>() -> bool {
-            S::QNAN_SIGNIFICAND == X87DoubleExtendedS::QNAN_SIGNIFICAND
-        }
-        let x87_special_nan = is_x87_double_extended::<S>()
-            && !is_x87_double_extended::<T>()
-            && r.category == Category::NaN
-            && (r.sig[0] & S::QNAN_SIGNIFICAND) != S::QNAN_SIGNIFICAND;
-
-        // If this is a truncation of a denormal number, and the target semantics
-        // has larger exponent range than the source semantics (this can happen
-        // when truncating from PowerPC double-double to double format), the
-        // right shift could lose result mantissa bits. Adjust exponent instead
-        // of performing excessive shift.
-        let mut shift = T::PRECISION as ExpInt - S::PRECISION as ExpInt;
-        if shift < 0 && r.is_finite_non_zero() {
-            let mut exp_change = sig::omsb(&r.sig) as ExpInt - S::PRECISION as ExpInt;
-            if r.exp + exp_change < T::MIN_EXP {
-                exp_change = T::MIN_EXP - r.exp;
-            }
-            if exp_change < shift {
-                exp_change = shift;
-            }
-            if exp_change < 0 {
-                shift -= exp_change;
-                r.exp += exp_change;
-            }
-        }
-
-        // If this is a truncation, perform the shift.
-        let loss = if shift < 0 && (r.is_finite_non_zero() || r.category == Category::NaN) {
-            sig::shift_right(&mut r.sig, &mut 0, -shift as usize)
-        } else {
-            Loss::ExactlyZero
-        };
-
-        // If this is an extension, perform the shift.
-        if shift > 0 && (r.is_finite_non_zero() || r.category == Category::NaN) {
-            sig::shift_left(&mut r.sig, &mut 0, shift as usize);
-        }
-
-        let status;
-        if r.is_finite_non_zero() {
-            r = unpack!(status=, r.normalize(round, loss));
-            *loses_info = status != Status::OK;
-        } else if r.category == Category::NaN {
-            *loses_info = loss != Loss::ExactlyZero || x87_special_nan;
-
-            // For x87 extended precision, we want to make a NaN, not a special NaN if
-            // the input wasn't special either.
-            if !x87_special_nan && is_x87_double_extended::<T>() {
-                sig::set_bit(&mut r.sig, T::PRECISION - 1);
-            }
-
-            // Convert of sNaN creates qNaN and raises an exception (invalid op).
-            // This also guarantees that a sNaN does not become Inf on a truncation
-            // that loses all payload bits.
-            if self.is_signaling() {
-                // Quiet signaling NaN.
-                sig::set_bit(&mut r.sig, T::QNAN_BIT);
-                status = Status::INVALID_OP;
-            } else {
-                status = Status::OK;
-            }
-        } else {
-            *loses_info = false;
-            status = Status::OK;
-        }
-
-        status.and(r)
-    }
-}
-
-impl<S: Semantics> IeeeFloat<S> {
-    /// Handle positive overflow. We either return infinity or
-    /// the largest finite number. For negative overflow,
-    /// negate the `round` argument before calling.
-    fn overflow_result(round: Round) -> StatusAnd<Self> {
-        match round {
-            // Infinity?
-            Round::NearestTiesToEven | Round::NearestTiesToAway | Round::TowardPositive => {
-                (Status::OVERFLOW | Status::INEXACT).and(Self::INFINITY)
-            }
-            // Otherwise we become the largest finite number.
-            Round::TowardNegative | Round::TowardZero => Status::INEXACT.and(Self::largest()),
-        }
-    }
-
-    /// Returns `true` if, when truncating the current number, with `bit` the
-    /// new LSB, with the given lost fraction and rounding mode, the result
-    /// would need to be rounded away from zero (i.e., by increasing the
-    /// signficand). This routine must work for `Category::Zero` of both signs, and
-    /// `Category::Normal` numbers.
-    fn round_away_from_zero(&self, round: Round, loss: Loss, bit: usize) -> bool {
-        // NaNs and infinities should not have lost fractions.
-        assert!(self.is_finite_non_zero() || self.is_zero());
-
-        // Current callers never pass this so we don't handle it.
-        assert_ne!(loss, Loss::ExactlyZero);
-
-        match round {
-            Round::NearestTiesToAway => loss == Loss::ExactlyHalf || loss == Loss::MoreThanHalf,
-            Round::NearestTiesToEven => {
-                if loss == Loss::MoreThanHalf {
-                    return true;
-                }
-
-                // Our zeros don't have a significand to test.
-                if loss == Loss::ExactlyHalf && self.category != Category::Zero {
-                    return sig::get_bit(&self.sig, bit);
-                }
-
-                false
-            }
-            Round::TowardZero => false,
-            Round::TowardPositive => !self.sign,
-            Round::TowardNegative => self.sign,
-        }
-    }
-
-    fn normalize(mut self, round: Round, mut loss: Loss) -> StatusAnd<Self> {
-        if !self.is_finite_non_zero() {
-            return Status::OK.and(self);
-        }
-
-        // Before rounding normalize the exponent of Category::Normal numbers.
-        let mut omsb = sig::omsb(&self.sig);
-
-        if omsb > 0 {
-            // OMSB is numbered from 1. We want to place it in the integer
-            // bit numbered PRECISION if possible, with a compensating change in
-            // the exponent.
-            let mut final_exp = self.exp.saturating_add(omsb as ExpInt - S::PRECISION as ExpInt);
-
-            // If the resulting exponent is too high, overflow according to
-            // the rounding mode.
-            if final_exp > S::MAX_EXP {
-                let round = if self.sign { -round } else { round };
-                return Self::overflow_result(round).map(|r| r.copy_sign(self));
-            }
-
-            // Subnormal numbers have exponent MIN_EXP, and their MSB
-            // is forced based on that.
-            if final_exp < S::MIN_EXP {
-                final_exp = S::MIN_EXP;
-            }
-
-            // Shifting left is easy as we don't lose precision.
-            if final_exp < self.exp {
-                assert_eq!(loss, Loss::ExactlyZero);
-
-                let exp_change = (self.exp - final_exp) as usize;
-                sig::shift_left(&mut self.sig, &mut self.exp, exp_change);
-
-                return Status::OK.and(self);
-            }
-
-            // Shift right and capture any new lost fraction.
-            if final_exp > self.exp {
-                let exp_change = (final_exp - self.exp) as usize;
-                loss = sig::shift_right(&mut self.sig, &mut self.exp, exp_change).combine(loss);
-
-                // Keep OMSB up-to-date.
-                omsb = omsb.saturating_sub(exp_change);
-            }
-        }
-
-        // Now round the number according to round given the lost
-        // fraction.
-
-        // As specified in IEEE 754, since we do not trap we do not report
-        // underflow for exact results.
-        if loss == Loss::ExactlyZero {
-            // Canonicalize zeros.
-            if omsb == 0 {
-                self.category = Category::Zero;
-            }
-
-            return Status::OK.and(self);
-        }
-
-        // Increment the significand if we're rounding away from zero.
-        if self.round_away_from_zero(round, loss, 0) {
-            if omsb == 0 {
-                self.exp = S::MIN_EXP;
-            }
-
-            // We should never overflow.
-            assert_eq!(sig::increment(&mut self.sig), 0);
-            omsb = sig::omsb(&self.sig);
-
-            // Did the significand increment overflow?
-            if omsb == S::PRECISION + 1 {
-                // Renormalize by incrementing the exponent and shifting our
-                // significand right one. However if we already have the
-                // maximum exponent we overflow to infinity.
-                if self.exp == S::MAX_EXP {
-                    self.category = Category::Infinity;
-
-                    return (Status::OVERFLOW | Status::INEXACT).and(self);
-                }
-
-                let _: Loss = sig::shift_right(&mut self.sig, &mut self.exp, 1);
-
-                return Status::INEXACT.and(self);
-            }
-        }
-
-        // The normal case - we were and are not denormal, and any
-        // significand increment above didn't overflow.
-        if omsb == S::PRECISION {
-            return Status::INEXACT.and(self);
-        }
-
-        // We have a non-zero denormal.
-        assert!(omsb < S::PRECISION);
-
-        // Canonicalize zeros.
-        if omsb == 0 {
-            self.category = Category::Zero;
-        }
-
-        // The Category::Zero case is a denormal that underflowed to zero.
-        (Status::UNDERFLOW | Status::INEXACT).and(self)
-    }
-
-    fn from_hexadecimal_string(s: &str, round: Round) -> Result<StatusAnd<Self>, ParseError> {
-        let mut r = IeeeFloat {
-            sig: [0],
-            exp: 0,
-            category: Category::Normal,
-            sign: false,
-            marker: PhantomData,
-        };
-
-        let mut any_digits = false;
-        let mut has_exp = false;
-        let mut bit_pos = LIMB_BITS as isize;
-        let mut loss = None;
-
-        // Without leading or trailing zeros, irrespective of the dot.
-        let mut first_sig_digit = None;
-        let mut dot = s.len();
-
-        for (p, c) in s.char_indices() {
-            // Skip leading zeros and any (hexa)decimal point.
-            if c == '.' {
-                if dot != s.len() {
-                    return Err(ParseError("String contains multiple dots"));
-                }
-                dot = p;
-            } else if let Some(hex_value) = c.to_digit(16) {
-                any_digits = true;
-
-                if first_sig_digit.is_none() {
-                    if hex_value == 0 {
-                        continue;
-                    }
-                    first_sig_digit = Some(p);
-                }
-
-                // Store the number while we have space.
-                bit_pos -= 4;
-                if bit_pos >= 0 {
-                    r.sig[0] |= (hex_value as Limb) << bit_pos;
-                // If zero or one-half (the hexadecimal digit 8) are followed
-                // by non-zero, they're a little more than zero or one-half.
-                } else if let Some(ref mut loss) = loss {
-                    if hex_value != 0 {
-                        if *loss == Loss::ExactlyZero {
-                            *loss = Loss::LessThanHalf;
-                        }
-                        if *loss == Loss::ExactlyHalf {
-                            *loss = Loss::MoreThanHalf;
-                        }
-                    }
-                } else {
-                    loss = Some(match hex_value {
-                        0 => Loss::ExactlyZero,
-                        1..=7 => Loss::LessThanHalf,
-                        8 => Loss::ExactlyHalf,
-                        9..=15 => Loss::MoreThanHalf,
-                        _ => unreachable!(),
-                    });
-                }
-            } else if c == 'p' || c == 'P' {
-                if !any_digits {
-                    return Err(ParseError("Significand has no digits"));
-                }
-
-                if dot == s.len() {
-                    dot = p;
-                }
-
-                let mut chars = s[p + 1..].chars().peekable();
-
-                // Adjust for the given exponent.
-                let exp_minus = chars.peek() == Some(&'-');
-                if exp_minus || chars.peek() == Some(&'+') {
-                    chars.next();
-                }
-
-                for c in chars {
-                    if let Some(value) = c.to_digit(10) {
-                        has_exp = true;
-                        r.exp = r.exp.saturating_mul(10).saturating_add(value as ExpInt);
-                    } else {
-                        return Err(ParseError("Invalid character in exponent"));
-                    }
-                }
-                if !has_exp {
-                    return Err(ParseError("Exponent has no digits"));
-                }
-
-                if exp_minus {
-                    r.exp = -r.exp;
-                }
-
-                break;
-            } else {
-                return Err(ParseError("Invalid character in significand"));
-            }
-        }
-        if !any_digits {
-            return Err(ParseError("Significand has no digits"));
-        }
-
-        // Hex floats require an exponent but not a hexadecimal point.
-        if !has_exp {
-            return Err(ParseError("Hex strings require an exponent"));
-        }
-
-        // Ignore the exponent if we are zero.
-        let first_sig_digit = match first_sig_digit {
-            Some(p) => p,
-            None => return Ok(Status::OK.and(Self::ZERO)),
-        };
-
-        // Calculate the exponent adjustment implicit in the number of
-        // significant digits and adjust for writing the significand starting
-        // at the most significant nibble.
-        let exp_adjustment = if dot > first_sig_digit {
-            ExpInt::try_from(dot - first_sig_digit).unwrap()
-        } else {
-            -ExpInt::try_from(first_sig_digit - dot - 1).unwrap()
-        };
-        let exp_adjustment = exp_adjustment
-            .saturating_mul(4)
-            .saturating_sub(1)
-            .saturating_add(S::PRECISION as ExpInt)
-            .saturating_sub(LIMB_BITS as ExpInt);
-        r.exp = r.exp.saturating_add(exp_adjustment);
-
-        Ok(r.normalize(round, loss.unwrap_or(Loss::ExactlyZero)))
-    }
-
-    fn from_decimal_string(s: &str, round: Round) -> Result<StatusAnd<Self>, ParseError> {
-        // Given a normal decimal floating point number of the form
-        //
-        //   dddd.dddd[eE][+-]ddd
-        //
-        // where the decimal point and exponent are optional, fill out the
-        // variables below. Exponent is appropriate if the significand is
-        // treated as an integer, and normalized_exp if the significand
-        // is taken to have the decimal point after a single leading
-        // non-zero digit.
-        //
-        // If the value is zero, first_sig_digit is None.
-
-        let mut any_digits = false;
-        let mut dec_exp = 0i32;
-
-        // Without leading or trailing zeros, irrespective of the dot.
-        let mut first_sig_digit = None;
-        let mut last_sig_digit = 0;
-        let mut dot = s.len();
-
-        for (p, c) in s.char_indices() {
-            if c == '.' {
-                if dot != s.len() {
-                    return Err(ParseError("String contains multiple dots"));
-                }
-                dot = p;
-            } else if let Some(dec_value) = c.to_digit(10) {
-                any_digits = true;
-
-                if dec_value != 0 {
-                    if first_sig_digit.is_none() {
-                        first_sig_digit = Some(p);
-                    }
-                    last_sig_digit = p;
-                }
-            } else if c == 'e' || c == 'E' {
-                if !any_digits {
-                    return Err(ParseError("Significand has no digits"));
-                }
-
-                if dot == s.len() {
-                    dot = p;
-                }
-
-                let mut chars = s[p + 1..].chars().peekable();
-
-                // Adjust for the given exponent.
-                let exp_minus = chars.peek() == Some(&'-');
-                if exp_minus || chars.peek() == Some(&'+') {
-                    chars.next();
-                }
-
-                any_digits = false;
-                for c in chars {
-                    if let Some(value) = c.to_digit(10) {
-                        any_digits = true;
-                        dec_exp = dec_exp.saturating_mul(10).saturating_add(value as i32);
-                    } else {
-                        return Err(ParseError("Invalid character in exponent"));
-                    }
-                }
-                if !any_digits {
-                    return Err(ParseError("Exponent has no digits"));
-                }
-
-                if exp_minus {
-                    dec_exp = -dec_exp;
-                }
-
-                break;
-            } else {
-                return Err(ParseError("Invalid character in significand"));
-            }
-        }
-        if !any_digits {
-            return Err(ParseError("Significand has no digits"));
-        }
-
-        // Test if we have a zero number allowing for non-zero exponents.
-        let first_sig_digit = match first_sig_digit {
-            Some(p) => p,
-            None => return Ok(Status::OK.and(Self::ZERO)),
-        };
-
-        // Adjust the exponents for any decimal point.
-        if dot > last_sig_digit {
-            dec_exp = dec_exp.saturating_add((dot - last_sig_digit - 1) as i32);
-        } else {
-            dec_exp = dec_exp.saturating_sub((last_sig_digit - dot) as i32);
-        }
-        let significand_digits = last_sig_digit - first_sig_digit + 1
-            - (dot > first_sig_digit && dot < last_sig_digit) as usize;
-        let normalized_exp = dec_exp.saturating_add(significand_digits as i32 - 1);
-
-        // Handle the cases where exponents are obviously too large or too
-        // small. Writing L for log 10 / log 2, a number d.ddddd*10^dec_exp
-        // definitely overflows if
-        //
-        //       (dec_exp - 1) * L >= MAX_EXP
-        //
-        // and definitely underflows to zero where
-        //
-        //       (dec_exp + 1) * L <= MIN_EXP - PRECISION
-        //
-        // With integer arithmetic the tightest bounds for L are
-        //
-        //       93/28 < L < 196/59            [ numerator <= 256 ]
-        //       42039/12655 < L < 28738/8651  [ numerator <= 65536 ]
-
-        // Check for MAX_EXP.
-        if normalized_exp.saturating_sub(1).saturating_mul(42039) >= 12655 * S::MAX_EXP as i32 {
-            // Overflow and round.
-            return Ok(Self::overflow_result(round));
-        }
-
-        // Check for MIN_EXP.
-        if normalized_exp.saturating_add(1).saturating_mul(28738)
-            <= 8651 * (S::MIN_EXP as i32 - S::PRECISION as i32)
-        {
-            // Underflow to zero and round.
-            let r =
-                if round == Round::TowardPositive { IeeeFloat::SMALLEST } else { IeeeFloat::ZERO };
-            return Ok((Status::UNDERFLOW | Status::INEXACT).and(r));
-        }
-
-        // A tight upper bound on number of bits required to hold an
-        // N-digit decimal integer is N * 196 / 59. Allocate enough space
-        // to hold the full significand, and an extra limb required by
-        // tcMultiplyPart.
-        let max_limbs = limbs_for_bits(1 + 196 * significand_digits / 59);
-        let mut dec_sig: SmallVec<[Limb; 1]> = SmallVec::with_capacity(max_limbs);
-
-        // Convert to binary efficiently - we do almost all multiplication
-        // in a Limb. When this would overflow do we do a single
-        // bignum multiplication, and then revert again to multiplication
-        // in a Limb.
-        let mut chars = s[first_sig_digit..=last_sig_digit].chars();
-        loop {
-            let mut val = 0;
-            let mut multiplier = 1;
-
-            loop {
-                let dec_value = match chars.next() {
-                    Some('.') => continue,
-                    Some(c) => c.to_digit(10).unwrap(),
-                    None => break,
-                };
-
-                multiplier *= 10;
-                val = val * 10 + dec_value as Limb;
-
-                // The maximum number that can be multiplied by ten with any
-                // digit added without overflowing a Limb.
-                if multiplier > (!0 - 9) / 10 {
-                    break;
-                }
-            }
-
-            // If we've consumed no digits, we're done.
-            if multiplier == 1 {
-                break;
-            }
-
-            // Multiply out the current limb.
-            let mut carry = val;
-            for x in &mut dec_sig {
-                let [low, mut high] = sig::widening_mul(*x, multiplier);
-
-                // Now add carry.
-                let (low, overflow) = low.overflowing_add(carry);
-                high += overflow as Limb;
-
-                *x = low;
-                carry = high;
-            }
-
-            // If we had carry, we need another limb (likely but not guaranteed).
-            if carry > 0 {
-                dec_sig.push(carry);
-            }
-        }
-
-        // Calculate pow(5, abs(dec_exp)) into `pow5_full`.
-        // The *_calc Vec's are reused scratch space, as an optimization.
-        let (pow5_full, mut pow5_calc, mut sig_calc, mut sig_scratch_calc) = {
-            let mut power = dec_exp.abs() as usize;
-
-            const FIRST_EIGHT_POWERS: [Limb; 8] = [1, 5, 25, 125, 625, 3125, 15625, 78125];
-
-            let mut p5_scratch = smallvec![];
-            let mut p5: SmallVec<[Limb; 1]> = smallvec![FIRST_EIGHT_POWERS[4]];
-
-            let mut r_scratch = smallvec![];
-            let mut r: SmallVec<[Limb; 1]> = smallvec![FIRST_EIGHT_POWERS[power & 7]];
-            power >>= 3;
-
-            while power > 0 {
-                // Calculate pow(5,pow(2,n+3)).
-                p5_scratch.resize(p5.len() * 2, 0);
-                let _: Loss = sig::mul(&mut p5_scratch, &mut 0, &p5, &p5, p5.len() * 2 * LIMB_BITS);
-                while p5_scratch.last() == Some(&0) {
-                    p5_scratch.pop();
-                }
-                mem::swap(&mut p5, &mut p5_scratch);
-
-                if power & 1 != 0 {
-                    r_scratch.resize(r.len() + p5.len(), 0);
-                    let _: Loss =
-                        sig::mul(&mut r_scratch, &mut 0, &r, &p5, (r.len() + p5.len()) * LIMB_BITS);
-                    while r_scratch.last() == Some(&0) {
-                        r_scratch.pop();
-                    }
-                    mem::swap(&mut r, &mut r_scratch);
-                }
-
-                power >>= 1;
-            }
-
-            (r, r_scratch, p5, p5_scratch)
-        };
-
-        // Attempt dec_sig * 10^dec_exp with increasing precision.
-        let mut attempt = 0;
-        loop {
-            let calc_precision = (LIMB_BITS << attempt) - 1;
-            attempt += 1;
-
-            let calc_normal_from_limbs = |sig: &mut SmallVec<[Limb; 1]>,
-                                          limbs: &[Limb]|
-             -> StatusAnd<ExpInt> {
-                sig.resize(limbs_for_bits(calc_precision), 0);
-                let (mut loss, mut exp) = sig::from_limbs(sig, limbs, calc_precision);
-
-                // Before rounding normalize the exponent of Category::Normal numbers.
-                let mut omsb = sig::omsb(sig);
-
-                assert_ne!(omsb, 0);
-
-                // OMSB is numbered from 1. We want to place it in the integer
-                // bit numbered PRECISION if possible, with a compensating change in
-                // the exponent.
-                let final_exp = exp.saturating_add(omsb as ExpInt - calc_precision as ExpInt);
-
-                // Shifting left is easy as we don't lose precision.
-                if final_exp < exp {
-                    assert_eq!(loss, Loss::ExactlyZero);
-
-                    let exp_change = (exp - final_exp) as usize;
-                    sig::shift_left(sig, &mut exp, exp_change);
-
-                    return Status::OK.and(exp);
-                }
-
-                // Shift right and capture any new lost fraction.
-                if final_exp > exp {
-                    let exp_change = (final_exp - exp) as usize;
-                    loss = sig::shift_right(sig, &mut exp, exp_change).combine(loss);
-
-                    // Keep OMSB up-to-date.
-                    omsb = omsb.saturating_sub(exp_change);
-                }
-
-                assert_eq!(omsb, calc_precision);
-
-                // Now round the number according to round given the lost
-                // fraction.
-
-                // As specified in IEEE 754, since we do not trap we do not report
-                // underflow for exact results.
-                if loss == Loss::ExactlyZero {
-                    return Status::OK.and(exp);
-                }
-
-                // Increment the significand if we're rounding away from zero.
-                if loss == Loss::MoreThanHalf || loss == Loss::ExactlyHalf && sig::get_bit(sig, 0) {
-                    // We should never overflow.
-                    assert_eq!(sig::increment(sig), 0);
-                    omsb = sig::omsb(sig);
-
-                    // Did the significand increment overflow?
-                    if omsb == calc_precision + 1 {
-                        let _: Loss = sig::shift_right(sig, &mut exp, 1);
-
-                        return Status::INEXACT.and(exp);
-                    }
-                }
-
-                // The normal case - we were and are not denormal, and any
-                // significand increment above didn't overflow.
-                Status::INEXACT.and(exp)
-            };
-
-            let status;
-            let mut exp = unpack!(status=,
-                calc_normal_from_limbs(&mut sig_calc, &dec_sig));
-            let pow5_status;
-            let pow5_exp = unpack!(pow5_status=,
-                calc_normal_from_limbs(&mut pow5_calc, &pow5_full));
-
-            // Add dec_exp, as 10^n = 5^n * 2^n.
-            exp += dec_exp as ExpInt;
-
-            let mut used_bits = S::PRECISION;
-            let mut truncated_bits = calc_precision - used_bits;
-
-            let half_ulp_err1 = (status != Status::OK) as Limb;
-            let (calc_loss, half_ulp_err2);
-            if dec_exp >= 0 {
-                exp += pow5_exp;
-
-                sig_scratch_calc.resize(sig_calc.len() + pow5_calc.len(), 0);
-                calc_loss = sig::mul(
-                    &mut sig_scratch_calc,
-                    &mut exp,
-                    &sig_calc,
-                    &pow5_calc,
-                    calc_precision,
-                );
-                mem::swap(&mut sig_calc, &mut sig_scratch_calc);
-
-                half_ulp_err2 = (pow5_status != Status::OK) as Limb;
-            } else {
-                exp -= pow5_exp;
-
-                sig_scratch_calc.resize(sig_calc.len(), 0);
-                calc_loss = sig::div(
-                    &mut sig_scratch_calc,
-                    &mut exp,
-                    &mut sig_calc,
-                    &mut pow5_calc,
-                    calc_precision,
-                );
-                mem::swap(&mut sig_calc, &mut sig_scratch_calc);
-
-                // Denormal numbers have less precision.
-                if exp < S::MIN_EXP {
-                    truncated_bits += (S::MIN_EXP - exp) as usize;
-                    used_bits = calc_precision.saturating_sub(truncated_bits);
-                }
-                // Extra half-ulp lost in reciprocal of exponent.
-                half_ulp_err2 =
-                    2 * (pow5_status != Status::OK || calc_loss != Loss::ExactlyZero) as Limb;
-            }
-
-            // Both sig::mul and sig::div return the
-            // result with the integer bit set.
-            assert!(sig::get_bit(&sig_calc, calc_precision - 1));
-
-            // The error from the true value, in half-ulps, on multiplying two
-            // floating point numbers, which differ from the value they
-            // approximate by at most half_ulp_err1 and half_ulp_err2 half-ulps, is strictly less
-            // than the returned value.
-            //
-            // See "How to Read Floating Point Numbers Accurately" by William D Clinger.
-            assert!(half_ulp_err1 < 2 || half_ulp_err2 < 2 || (half_ulp_err1 + half_ulp_err2 < 8));
-
-            let inexact = (calc_loss != Loss::ExactlyZero) as Limb;
-            let half_ulp_err = if half_ulp_err1 + half_ulp_err2 == 0 {
-                inexact * 2 // <= inexact half-ulps.
-            } else {
-                inexact + 2 * (half_ulp_err1 + half_ulp_err2)
-            };
-
-            let ulps_from_boundary = {
-                let bits = calc_precision - used_bits - 1;
-
-                let i = bits / LIMB_BITS;
-                let limb = sig_calc[i] & (!0 >> (LIMB_BITS - 1 - bits % LIMB_BITS));
-                let boundary = match round {
-                    Round::NearestTiesToEven | Round::NearestTiesToAway => 1 << (bits % LIMB_BITS),
-                    _ => 0,
-                };
-                if i == 0 {
-                    let delta = limb.wrapping_sub(boundary);
-                    cmp::min(delta, delta.wrapping_neg())
-                } else if limb == boundary {
-                    if !sig::is_all_zeros(&sig_calc[1..i]) {
-                        !0 // A lot.
-                    } else {
-                        sig_calc[0]
-                    }
-                } else if limb == boundary.wrapping_sub(1) {
-                    if sig_calc[1..i].iter().any(|&x| x.wrapping_neg() != 1) {
-                        !0 // A lot.
-                    } else {
-                        sig_calc[0].wrapping_neg()
-                    }
-                } else {
-                    !0 // A lot.
-                }
-            };
-
-            // Are we guaranteed to round correctly if we truncate?
-            if ulps_from_boundary.saturating_mul(2) >= half_ulp_err {
-                let mut r = IeeeFloat {
-                    sig: [0],
-                    exp,
-                    category: Category::Normal,
-                    sign: false,
-                    marker: PhantomData,
-                };
-                sig::extract(&mut r.sig, &sig_calc, used_bits, calc_precision - used_bits);
-                // If we extracted less bits above we must adjust our exponent
-                // to compensate for the implicit right shift.
-                r.exp += (S::PRECISION - used_bits) as ExpInt;
-                let loss = Loss::through_truncation(&sig_calc, truncated_bits);
-                return Ok(r.normalize(round, loss));
-            }
-        }
-    }
-}
-
-impl Loss {
-    /// Combine the effect of two lost fractions.
-    fn combine(self, less_significant: Loss) -> Loss {
-        let mut more_significant = self;
-        if less_significant != Loss::ExactlyZero {
-            if more_significant == Loss::ExactlyZero {
-                more_significant = Loss::LessThanHalf;
-            } else if more_significant == Loss::ExactlyHalf {
-                more_significant = Loss::MoreThanHalf;
-            }
-        }
-
-        more_significant
-    }
-
-    /// Returns the fraction lost were a bignum truncated losing the least
-    /// significant `bits` bits.
-    fn through_truncation(limbs: &[Limb], bits: usize) -> Loss {
-        if bits == 0 {
-            return Loss::ExactlyZero;
-        }
-
-        let half_bit = bits - 1;
-        let half_limb = half_bit / LIMB_BITS;
-        let (half_limb, rest) = if half_limb < limbs.len() {
-            (limbs[half_limb], &limbs[..half_limb])
-        } else {
-            (0, limbs)
-        };
-        let half = 1 << (half_bit % LIMB_BITS);
-        let has_half = half_limb & half != 0;
-        let has_rest = half_limb & (half - 1) != 0 || !sig::is_all_zeros(rest);
-
-        match (has_half, has_rest) {
-            (false, false) => Loss::ExactlyZero,
-            (false, true) => Loss::LessThanHalf,
-            (true, false) => Loss::ExactlyHalf,
-            (true, true) => Loss::MoreThanHalf,
-        }
-    }
-}
-
-/// Implementation details of IeeeFloat significands, such as big integer arithmetic.
-/// As a rule of thumb, no functions in this module should dynamically allocate.
-mod sig {
-    use super::{limbs_for_bits, ExpInt, Limb, Loss, LIMB_BITS};
-    use core::cmp::Ordering;
-    use core::iter;
-    use core::mem;
-
-    pub(super) fn is_all_zeros(limbs: &[Limb]) -> bool {
-        limbs.iter().all(|&l| l == 0)
-    }
-
-    /// One, not zero, based LSB. That is, returns 0 for a zeroed significand.
-    pub(super) fn olsb(limbs: &[Limb]) -> usize {
-        limbs
-            .iter()
-            .enumerate()
-            .find(|(_, &limb)| limb != 0)
-            .map_or(0, |(i, limb)| i * LIMB_BITS + limb.trailing_zeros() as usize + 1)
-    }
-
-    /// One, not zero, based MSB. That is, returns 0 for a zeroed significand.
-    pub(super) fn omsb(limbs: &[Limb]) -> usize {
-        limbs
-            .iter()
-            .enumerate()
-            .rfind(|(_, &limb)| limb != 0)
-            .map_or(0, |(i, limb)| (i + 1) * LIMB_BITS - limb.leading_zeros() as usize)
-    }
-
-    /// Comparison (unsigned) of two significands.
-    pub(super) fn cmp(a: &[Limb], b: &[Limb]) -> Ordering {
-        assert_eq!(a.len(), b.len());
-        for (a, b) in a.iter().zip(b).rev() {
-            match a.cmp(b) {
-                Ordering::Equal => {}
-                o => return o,
-            }
-        }
-
-        Ordering::Equal
-    }
-
-    /// Extracts the given bit.
-    pub(super) fn get_bit(limbs: &[Limb], bit: usize) -> bool {
-        limbs[bit / LIMB_BITS] & (1 << (bit % LIMB_BITS)) != 0
-    }
-
-    /// Sets the given bit.
-    pub(super) fn set_bit(limbs: &mut [Limb], bit: usize) {
-        limbs[bit / LIMB_BITS] |= 1 << (bit % LIMB_BITS);
-    }
-
-    /// Clear the given bit.
-    pub(super) fn clear_bit(limbs: &mut [Limb], bit: usize) {
-        limbs[bit / LIMB_BITS] &= !(1 << (bit % LIMB_BITS));
-    }
-
-    /// Shifts `dst` left `bits` bits, subtract `bits` from its exponent.
-    pub(super) fn shift_left(dst: &mut [Limb], exp: &mut ExpInt, bits: usize) {
-        if bits > 0 {
-            // Our exponent should not underflow.
-            *exp = exp.checked_sub(bits as ExpInt).unwrap();
-
-            // Jump is the inter-limb jump; shift is the intra-limb shift.
-            let jump = bits / LIMB_BITS;
-            let shift = bits % LIMB_BITS;
-
-            for i in (0..dst.len()).rev() {
-                let mut limb;
-
-                if i < jump {
-                    limb = 0;
-                } else {
-                    // dst[i] comes from the two limbs src[i - jump] and, if we have
-                    // an intra-limb shift, src[i - jump - 1].
-                    limb = dst[i - jump];
-                    if shift > 0 {
-                        limb <<= shift;
-                        if i > jump {
-                            limb |= dst[i - jump - 1] >> (LIMB_BITS - shift);
-                        }
-                    }
-                }
-
-                dst[i] = limb;
-            }
-        }
-    }
-
-    /// Shifts `dst` right `bits` bits noting lost fraction.
-    pub(super) fn shift_right(dst: &mut [Limb], exp: &mut ExpInt, bits: usize) -> Loss {
-        let loss = Loss::through_truncation(dst, bits);
-
-        if bits > 0 {
-            // Our exponent should not overflow.
-            *exp = exp.checked_add(bits as ExpInt).unwrap();
-
-            // Jump is the inter-limb jump; shift is the intra-limb shift.
-            let jump = bits / LIMB_BITS;
-            let shift = bits % LIMB_BITS;
-
-            // Perform the shift. This leaves the most significant `bits` bits
-            // of the result at zero.
-            for i in 0..dst.len() {
-                let mut limb;
-
-                if i + jump >= dst.len() {
-                    limb = 0;
-                } else {
-                    limb = dst[i + jump];
-                    if shift > 0 {
-                        limb >>= shift;
-                        if i + jump + 1 < dst.len() {
-                            limb |= dst[i + jump + 1] << (LIMB_BITS - shift);
-                        }
-                    }
-                }
-
-                dst[i] = limb;
-            }
-        }
-
-        loss
-    }
-
-    /// Copies the bit vector of width `src_bits` from `src`, starting at bit SRC_LSB,
-    /// to `dst`, such that the bit SRC_LSB becomes the least significant bit of `dst`.
-    /// All high bits above `src_bits` in `dst` are zero-filled.
-    pub(super) fn extract(dst: &mut [Limb], src: &[Limb], src_bits: usize, src_lsb: usize) {
-        if src_bits == 0 {
-            return;
-        }
-
-        let dst_limbs = limbs_for_bits(src_bits);
-        assert!(dst_limbs <= dst.len());
-
-        let src = &src[src_lsb / LIMB_BITS..];
-        dst[..dst_limbs].copy_from_slice(&src[..dst_limbs]);
-
-        let shift = src_lsb % LIMB_BITS;
-        let _: Loss = shift_right(&mut dst[..dst_limbs], &mut 0, shift);
-
-        // We now have (dst_limbs * LIMB_BITS - shift) bits from `src`
-        // in `dst`.  If this is less that src_bits, append the rest, else
-        // clear the high bits.
-        let n = dst_limbs * LIMB_BITS - shift;
-        if n < src_bits {
-            let mask = (1 << (src_bits - n)) - 1;
-            dst[dst_limbs - 1] |= (src[dst_limbs] & mask) << (n % LIMB_BITS);
-        } else if n > src_bits && src_bits % LIMB_BITS > 0 {
-            dst[dst_limbs - 1] &= (1 << (src_bits % LIMB_BITS)) - 1;
-        }
-
-        // Clear high limbs.
-        for x in &mut dst[dst_limbs..] {
-            *x = 0;
-        }
-    }
-
-    /// We want the most significant PRECISION bits of `src`. There may not
-    /// be that many; extract what we can.
-    pub(super) fn from_limbs(dst: &mut [Limb], src: &[Limb], precision: usize) -> (Loss, ExpInt) {
-        let omsb = omsb(src);
-
-        if precision <= omsb {
-            extract(dst, src, precision, omsb - precision);
-            (Loss::through_truncation(src, omsb - precision), omsb as ExpInt - 1)
-        } else {
-            extract(dst, src, omsb, 0);
-            (Loss::ExactlyZero, precision as ExpInt - 1)
-        }
-    }
-
-    /// For every consecutive chunk of `bits` bits from `limbs`,
-    /// going from most significant to the least significant bits,
-    /// call `f` to transform those bits and store the result back.
-    pub(super) fn each_chunk<F: FnMut(Limb) -> Limb>(limbs: &mut [Limb], bits: usize, mut f: F) {
-        assert_eq!(LIMB_BITS % bits, 0);
-        for limb in limbs.iter_mut().rev() {
-            let mut r = 0;
-            for i in (0..LIMB_BITS / bits).rev() {
-                r |= f((*limb >> (i * bits)) & ((1 << bits) - 1)) << (i * bits);
-            }
-            *limb = r;
-        }
-    }
-
-    /// Increment in-place, return the carry flag.
-    pub(super) fn increment(dst: &mut [Limb]) -> Limb {
-        for x in dst {
-            *x = x.wrapping_add(1);
-            if *x != 0 {
-                return 0;
-            }
-        }
-
-        1
-    }
-
-    /// Decrement in-place, return the borrow flag.
-    pub(super) fn decrement(dst: &mut [Limb]) -> Limb {
-        for x in dst {
-            *x = x.wrapping_sub(1);
-            if *x != !0 {
-                return 0;
-            }
-        }
-
-        1
-    }
-
-    /// `a += b + c` where `c` is zero or one. Returns the carry flag.
-    pub(super) fn add(a: &mut [Limb], b: &[Limb], mut c: Limb) -> Limb {
-        assert!(c <= 1);
-
-        for (a, &b) in iter::zip(a, b) {
-            let (r, overflow) = a.overflowing_add(b);
-            let (r, overflow2) = r.overflowing_add(c);
-            *a = r;
-            c = (overflow | overflow2) as Limb;
-        }
-
-        c
-    }
-
-    /// `a -= b + c` where `c` is zero or one. Returns the borrow flag.
-    pub(super) fn sub(a: &mut [Limb], b: &[Limb], mut c: Limb) -> Limb {
-        assert!(c <= 1);
-
-        for (a, &b) in iter::zip(a, b) {
-            let (r, overflow) = a.overflowing_sub(b);
-            let (r, overflow2) = r.overflowing_sub(c);
-            *a = r;
-            c = (overflow | overflow2) as Limb;
-        }
-
-        c
-    }
-
-    /// `a += b` or `a -= b`. Does not preserve `b`.
-    pub(super) fn add_or_sub(
-        a_sig: &mut [Limb],
-        a_exp: &mut ExpInt,
-        a_sign: &mut bool,
-        b_sig: &mut [Limb],
-        b_exp: ExpInt,
-        b_sign: bool,
-    ) -> Loss {
-        // Are we bigger exponent-wise than the RHS?
-        let bits = *a_exp - b_exp;
-
-        // Determine if the operation on the absolute values is effectively
-        // an addition or subtraction.
-        // Subtraction is more subtle than one might naively expect.
-        if *a_sign ^ b_sign {
-            let (reverse, loss);
-
-            if bits == 0 {
-                reverse = cmp(a_sig, b_sig) == Ordering::Less;
-                loss = Loss::ExactlyZero;
-            } else if bits > 0 {
-                loss = shift_right(b_sig, &mut 0, (bits - 1) as usize);
-                shift_left(a_sig, a_exp, 1);
-                reverse = false;
-            } else {
-                loss = shift_right(a_sig, a_exp, (-bits - 1) as usize);
-                shift_left(b_sig, &mut 0, 1);
-                reverse = true;
-            }
-
-            let borrow = (loss != Loss::ExactlyZero) as Limb;
-            if reverse {
-                // The code above is intended to ensure that no borrow is necessary.
-                assert_eq!(sub(b_sig, a_sig, borrow), 0);
-                a_sig.copy_from_slice(b_sig);
-                *a_sign = !*a_sign;
-            } else {
-                // The code above is intended to ensure that no borrow is necessary.
-                assert_eq!(sub(a_sig, b_sig, borrow), 0);
-            }
-
-            // Invert the lost fraction - it was on the RHS and subtracted.
-            match loss {
-                Loss::LessThanHalf => Loss::MoreThanHalf,
-                Loss::MoreThanHalf => Loss::LessThanHalf,
-                _ => loss,
-            }
-        } else {
-            let loss = if bits > 0 {
-                shift_right(b_sig, &mut 0, bits as usize)
-            } else {
-                shift_right(a_sig, a_exp, -bits as usize)
-            };
-            // We have a guard bit; generating a carry cannot happen.
-            assert_eq!(add(a_sig, b_sig, 0), 0);
-            loss
-        }
-    }
-
-    /// `[low, high] = a * b`.
-    ///
-    /// This cannot overflow, because
-    ///
-    /// `(n - 1) * (n - 1) + 2 * (n - 1) == (n - 1) * (n + 1)`
-    ///
-    /// which is less than n<sup>2</sup>.
-    pub(super) fn widening_mul(a: Limb, b: Limb) -> [Limb; 2] {
-        let mut wide = [0, 0];
-
-        if a == 0 || b == 0 {
-            return wide;
-        }
-
-        const HALF_BITS: usize = LIMB_BITS / 2;
-
-        let select = |limb, i| (limb >> (i * HALF_BITS)) & ((1 << HALF_BITS) - 1);
-        for i in 0..2 {
-            for j in 0..2 {
-                let mut x = [select(a, i) * select(b, j), 0];
-                shift_left(&mut x, &mut 0, (i + j) * HALF_BITS);
-                assert_eq!(add(&mut wide, &x, 0), 0);
-            }
-        }
-
-        wide
-    }
-
-    /// `dst = a * b` (for normal `a` and `b`). Returns the lost fraction.
-    pub(super) fn mul<'a>(
-        dst: &mut [Limb],
-        exp: &mut ExpInt,
-        mut a: &'a [Limb],
-        mut b: &'a [Limb],
-        precision: usize,
-    ) -> Loss {
-        // Put the narrower number on the `a` for less loops below.
-        if a.len() > b.len() {
-            mem::swap(&mut a, &mut b);
-        }
-
-        for x in &mut dst[..b.len()] {
-            *x = 0;
-        }
-
-        for i in 0..a.len() {
-            let mut carry = 0;
-            for j in 0..b.len() {
-                let [low, mut high] = widening_mul(a[i], b[j]);
-
-                // Now add carry.
-                let (low, overflow) = low.overflowing_add(carry);
-                high += overflow as Limb;
-
-                // And now `dst[i + j]`, and store the new low part there.
-                let (low, overflow) = low.overflowing_add(dst[i + j]);
-                high += overflow as Limb;
-
-                dst[i + j] = low;
-                carry = high;
-            }
-            dst[i + b.len()] = carry;
-        }
-
-        // Assume the operands involved in the multiplication are single-precision
-        // FP, and the two multiplicants are:
-        //     a = a23 . a22 ... a0 * 2^e1
-        //     b = b23 . b22 ... b0 * 2^e2
-        // the result of multiplication is:
-        //     dst = c48 c47 c46 . c45 ... c0 * 2^(e1+e2)
-        // Note that there are three significant bits at the left-hand side of the
-        // radix point: two for the multiplication, and an overflow bit for the
-        // addition (that will always be zero at this point). Move the radix point
-        // toward left by two bits, and adjust exponent accordingly.
-        *exp += 2;
-
-        // Convert the result having "2 * precision" significant-bits back to the one
-        // having "precision" significant-bits. First, move the radix point from
-        // poision "2*precision - 1" to "precision - 1". The exponent need to be
-        // adjusted by "2*precision - 1" - "precision - 1" = "precision".
-        *exp -= precision as ExpInt + 1;
-
-        // In case MSB resides at the left-hand side of radix point, shift the
-        // mantissa right by some amount to make sure the MSB reside right before
-        // the radix point (i.e., "MSB . rest-significant-bits").
-        //
-        // Note that the result is not normalized when "omsb < precision". So, the
-        // caller needs to call IeeeFloat::normalize() if normalized value is
-        // expected.
-        let omsb = omsb(dst);
-        if omsb <= precision { Loss::ExactlyZero } else { shift_right(dst, exp, omsb - precision) }
-    }
-
-    /// `quotient = dividend / divisor`. Returns the lost fraction.
-    /// Does not preserve `dividend` or `divisor`.
-    pub(super) fn div(
-        quotient: &mut [Limb],
-        exp: &mut ExpInt,
-        dividend: &mut [Limb],
-        divisor: &mut [Limb],
-        precision: usize,
-    ) -> Loss {
-        // Normalize the divisor.
-        let bits = precision - omsb(divisor);
-        shift_left(divisor, &mut 0, bits);
-        *exp += bits as ExpInt;
-
-        // Normalize the dividend.
-        let bits = precision - omsb(dividend);
-        shift_left(dividend, exp, bits);
-
-        // Division by 1.
-        let olsb_divisor = olsb(divisor);
-        if olsb_divisor == precision {
-            quotient.copy_from_slice(dividend);
-            return Loss::ExactlyZero;
-        }
-
-        // Ensure the dividend >= divisor initially for the loop below.
-        // Incidentally, this means that the division loop below is
-        // guaranteed to set the integer bit to one.
-        if cmp(dividend, divisor) == Ordering::Less {
-            shift_left(dividend, exp, 1);
-            assert_ne!(cmp(dividend, divisor), Ordering::Less)
-        }
-
-        // Helper for figuring out the lost fraction.
-        let lost_fraction = |dividend: &[Limb], divisor: &[Limb]| match cmp(dividend, divisor) {
-            Ordering::Greater => Loss::MoreThanHalf,
-            Ordering::Equal => Loss::ExactlyHalf,
-            Ordering::Less => {
-                if is_all_zeros(dividend) {
-                    Loss::ExactlyZero
-                } else {
-                    Loss::LessThanHalf
-                }
-            }
-        };
-
-        // Try to perform a (much faster) short division for small divisors.
-        let divisor_bits = precision - (olsb_divisor - 1);
-        macro_rules! try_short_div {
-            ($W:ty, $H:ty, $half:expr) => {
-                if divisor_bits * 2 <= $half {
-                    // Extract the small divisor.
-                    let _: Loss = shift_right(divisor, &mut 0, olsb_divisor - 1);
-                    let divisor = divisor[0] as $H as $W;
-
-                    // Shift the dividend to produce a quotient with the unit bit set.
-                    let top_limb = *dividend.last().unwrap();
-                    let mut rem = (top_limb >> (LIMB_BITS - (divisor_bits - 1))) as $H;
-                    shift_left(dividend, &mut 0, divisor_bits - 1);
-
-                    // Apply short division in place on $H (of $half bits) chunks.
-                    each_chunk(dividend, $half, |chunk| {
-                        let chunk = chunk as $H;
-                        let combined = ((rem as $W) << $half) | (chunk as $W);
-                        rem = (combined % divisor) as $H;
-                        (combined / divisor) as $H as Limb
-                    });
-                    quotient.copy_from_slice(dividend);
-
-                    return lost_fraction(&[(rem as Limb) << 1], &[divisor as Limb]);
-                }
-            };
-        }
-
-        try_short_div!(u32, u16, 16);
-        try_short_div!(u64, u32, 32);
-        try_short_div!(u128, u64, 64);
-
-        // Zero the quotient before setting bits in it.
-        for x in &mut quotient[..limbs_for_bits(precision)] {
-            *x = 0;
-        }
-
-        // Long division.
-        for bit in (0..precision).rev() {
-            if cmp(dividend, divisor) != Ordering::Less {
-                sub(dividend, divisor, 0);
-                set_bit(quotient, bit);
-            }
-            shift_left(dividend, &mut 0, 1);
-        }
-
-        lost_fraction(dividend, divisor)
-    }
-}
diff --git a/compiler/rustc_apfloat/src/lib.rs b/compiler/rustc_apfloat/src/lib.rs
deleted file mode 100644
index dde368e7b92..00000000000
--- a/compiler/rustc_apfloat/src/lib.rs
+++ /dev/null
@@ -1,695 +0,0 @@
-//! Port of LLVM's APFloat software floating-point implementation from the
-//! following C++ sources (please update commit hash when backporting):
-//! <https://github.com/llvm-mirror/llvm/tree/23efab2bbd424ed13495a420ad8641cb2c6c28f9>
-//!
-//! * `include/llvm/ADT/APFloat.h` -> `Float` and `FloatConvert` traits
-//! * `lib/Support/APFloat.cpp` -> `ieee` and `ppc` modules
-//! * `unittests/ADT/APFloatTest.cpp` -> `tests` directory
-//!
-//! The port contains no unsafe code, global state, or side-effects in general,
-//! and the only allocations are in the conversion to/from decimal strings.
-//!
-//! Most of the API and the testcases are intact in some form or another,
-//! with some ergonomic changes, such as idiomatic short names, returning
-//! new values instead of mutating the receiver, and having separate method
-//! variants that take a non-default rounding mode (with the suffix `_r`).
-//! Comments have been preserved where possible, only slightly adapted.
-//!
-//! Instead of keeping a pointer to a configuration struct and inspecting it
-//! dynamically on every operation, types (e.g., `ieee::Double`), traits
-//! (e.g., `ieee::Semantics`) and associated constants are employed for
-//! increased type safety and performance.
-//!
-//! On-heap bigints are replaced everywhere (except in decimal conversion),
-//! with short arrays of `type Limb = u128` elements (instead of `u64`),
-//! This allows fitting the largest supported significands in one integer
-//! (`ieee::Quad` and `ppc::Fallback` use slightly less than 128 bits).
-//! All of the functions in the `ieee::sig` module operate on slices.
-//!
-//! # Note
-//!
-//! This API is completely unstable and subject to change.
-
-#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
-#![no_std]
-#![forbid(unsafe_code)]
-#![deny(rustc::untranslatable_diagnostic)]
-#![deny(rustc::diagnostic_outside_of_impl)]
-
-#[macro_use]
-extern crate alloc;
-
-use core::cmp::Ordering;
-use core::fmt;
-use core::ops::{Add, Div, Mul, Neg, Rem, Sub};
-use core::ops::{AddAssign, DivAssign, MulAssign, RemAssign, SubAssign};
-use core::str::FromStr;
-
-bitflags::bitflags! {
-    /// IEEE-754R 7: Default exception handling.
-    ///
-    /// UNDERFLOW or OVERFLOW are always returned or-ed with INEXACT.
-    #[must_use]
-    pub struct Status: u8 {
-        const OK = 0x00;
-        const INVALID_OP = 0x01;
-        const DIV_BY_ZERO = 0x02;
-        const OVERFLOW = 0x04;
-        const UNDERFLOW = 0x08;
-        const INEXACT = 0x10;
-    }
-}
-
-#[must_use]
-#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
-pub struct StatusAnd<T> {
-    pub status: Status,
-    pub value: T,
-}
-
-impl Status {
-    pub fn and<T>(self, value: T) -> StatusAnd<T> {
-        StatusAnd { status: self, value }
-    }
-}
-
-impl<T> StatusAnd<T> {
-    pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> StatusAnd<U> {
-        StatusAnd { status: self.status, value: f(self.value) }
-    }
-}
-
-#[macro_export]
-macro_rules! unpack {
-    ($status:ident|=, $e:expr) => {
-        match $e {
-            $crate::StatusAnd { status, value } => {
-                $status |= status;
-                value
-            }
-        }
-    };
-    ($status:ident=, $e:expr) => {
-        match $e {
-            $crate::StatusAnd { status, value } => {
-                $status = status;
-                value
-            }
-        }
-    };
-}
-
-/// Category of internally-represented number.
-#[derive(Copy, Clone, PartialEq, Eq, Debug)]
-pub enum Category {
-    Infinity,
-    NaN,
-    Normal,
-    Zero,
-}
-
-/// IEEE-754R 4.3: Rounding-direction attributes.
-#[derive(Copy, Clone, PartialEq, Eq, Debug)]
-pub enum Round {
-    NearestTiesToEven,
-    TowardPositive,
-    TowardNegative,
-    TowardZero,
-    NearestTiesToAway,
-}
-
-impl Neg for Round {
-    type Output = Round;
-    fn neg(self) -> Round {
-        match self {
-            Round::TowardPositive => Round::TowardNegative,
-            Round::TowardNegative => Round::TowardPositive,
-            Round::NearestTiesToEven | Round::TowardZero | Round::NearestTiesToAway => self,
-        }
-    }
-}
-
-/// A signed type to represent a floating point number's unbiased exponent.
-pub type ExpInt = i16;
-
-// \c ilogb error results.
-pub const IEK_INF: ExpInt = ExpInt::MAX;
-pub const IEK_NAN: ExpInt = ExpInt::MIN;
-pub const IEK_ZERO: ExpInt = ExpInt::MIN + 1;
-
-#[derive(Copy, Clone, PartialEq, Eq, Debug)]
-pub struct ParseError(pub &'static str);
-
-/// A self-contained host- and target-independent arbitrary-precision
-/// floating-point software implementation.
-///
-/// `apfloat` uses significand bignum integer arithmetic as provided by functions
-/// in the `ieee::sig`.
-///
-/// Written for clarity rather than speed, in particular with a view to use in
-/// the front-end of a cross compiler so that target arithmetic can be correctly
-/// performed on the host. Performance should nonetheless be reasonable,
-/// particularly for its intended use. It may be useful as a base
-/// implementation for a run-time library during development of a faster
-/// target-specific one.
-///
-/// All 5 rounding modes in the IEEE-754R draft are handled correctly for all
-/// implemented operations. Currently implemented operations are add, subtract,
-/// multiply, divide, fused-multiply-add, conversion-to-float,
-/// conversion-to-integer and conversion-from-integer. New rounding modes
-/// (e.g., away from zero) can be added with three or four lines of code.
-///
-/// Four formats are built-in: IEEE single precision, double precision,
-/// quadruple precision, and x87 80-bit extended double (when operating with
-/// full extended precision). Adding a new format that obeys IEEE semantics
-/// only requires adding two lines of code: a declaration and definition of the
-/// format.
-///
-/// All operations return the status of that operation as an exception bit-mask,
-/// so multiple operations can be done consecutively with their results or-ed
-/// together. The returned status can be useful for compiler diagnostics; e.g.,
-/// inexact, underflow and overflow can be easily diagnosed on constant folding,
-/// and compiler optimizers can determine what exceptions would be raised by
-/// folding operations and optimize, or perhaps not optimize, accordingly.
-///
-/// At present, underflow tininess is detected after rounding; it should be
-/// straight forward to add support for the before-rounding case too.
-///
-/// The library reads hexadecimal floating point numbers as per C99, and
-/// correctly rounds if necessary according to the specified rounding mode.
-/// Syntax is required to have been validated by the caller.
-///
-/// It also reads decimal floating point numbers and correctly rounds according
-/// to the specified rounding mode.
-///
-/// Non-zero finite numbers are represented internally as a sign bit, a 16-bit
-/// signed exponent, and the significand as an array of integer limbs. After
-/// normalization of a number of precision P the exponent is within the range of
-/// the format, and if the number is not denormal the P-th bit of the
-/// significand is set as an explicit integer bit. For denormals the most
-/// significant bit is shifted right so that the exponent is maintained at the
-/// format's minimum, so that the smallest denormal has just the least
-/// significant bit of the significand set. The sign of zeros and infinities
-/// is significant; the exponent and significand of such numbers is not stored,
-/// but has a known implicit (deterministic) value: 0 for the significands, 0
-/// for zero exponent, all 1 bits for infinity exponent. For NaNs the sign and
-/// significand are deterministic, although not really meaningful, and preserved
-/// in non-conversion operations. The exponent is implicitly all 1 bits.
-///
-/// `apfloat` does not provide any exception handling beyond default exception
-/// handling. We represent Signaling NaNs via IEEE-754R 2008 6.2.1 should clause
-/// by encoding Signaling NaNs with the first bit of its trailing significand
-/// as 0.
-///
-/// Future work
-/// ===========
-///
-/// Some features that may or may not be worth adding:
-///
-/// Optional ability to detect underflow tininess before rounding.
-///
-/// New formats: x87 in single and double precision mode (IEEE apart from
-/// extended exponent range) (hard).
-///
-/// New operations: sqrt, nexttoward.
-///
-pub trait Float:
-    Copy
-    + Default
-    + FromStr<Err = ParseError>
-    + PartialOrd
-    + fmt::Display
-    + Neg<Output = Self>
-    + AddAssign
-    + SubAssign
-    + MulAssign
-    + DivAssign
-    + RemAssign
-    + Add<Output = StatusAnd<Self>>
-    + Sub<Output = StatusAnd<Self>>
-    + Mul<Output = StatusAnd<Self>>
-    + Div<Output = StatusAnd<Self>>
-    + Rem<Output = StatusAnd<Self>>
-{
-    /// Total number of bits in the in-memory format.
-    const BITS: usize;
-
-    /// Number of bits in the significand. This includes the integer bit.
-    const PRECISION: usize;
-
-    /// The largest E such that 2<sup>E</sup> is representable; this matches the
-    /// definition of IEEE 754.
-    const MAX_EXP: ExpInt;
-
-    /// The smallest E such that 2<sup>E</sup> is a normalized number; this
-    /// matches the definition of IEEE 754.
-    const MIN_EXP: ExpInt;
-
-    /// Positive Zero.
-    const ZERO: Self;
-
-    /// Positive Infinity.
-    const INFINITY: Self;
-
-    /// NaN (Not a Number).
-    // FIXME(eddyb) provide a default when qnan becomes const fn.
-    const NAN: Self;
-
-    /// Factory for QNaN values.
-    // FIXME(eddyb) should be const fn.
-    fn qnan(payload: Option<u128>) -> Self;
-
-    /// Factory for SNaN values.
-    // FIXME(eddyb) should be const fn.
-    fn snan(payload: Option<u128>) -> Self;
-
-    /// Largest finite number.
-    // FIXME(eddyb) should be const (but FloatPair::largest is nontrivial).
-    fn largest() -> Self;
-
-    /// Smallest (by magnitude) finite number.
-    /// Might be denormalized, which implies a relative loss of precision.
-    const SMALLEST: Self;
-
-    /// Smallest (by magnitude) normalized finite number.
-    // FIXME(eddyb) should be const (but FloatPair::smallest_normalized is nontrivial).
-    fn smallest_normalized() -> Self;
-
-    // Arithmetic
-
-    fn add_r(self, rhs: Self, round: Round) -> StatusAnd<Self>;
-    fn sub_r(self, rhs: Self, round: Round) -> StatusAnd<Self> {
-        self.add_r(-rhs, round)
-    }
-    fn mul_r(self, rhs: Self, round: Round) -> StatusAnd<Self>;
-    fn mul_add_r(self, multiplicand: Self, addend: Self, round: Round) -> StatusAnd<Self>;
-    fn mul_add(self, multiplicand: Self, addend: Self) -> StatusAnd<Self> {
-        self.mul_add_r(multiplicand, addend, Round::NearestTiesToEven)
-    }
-    fn div_r(self, rhs: Self, round: Round) -> StatusAnd<Self>;
-    /// IEEE remainder.
-    // This is not currently correct in all cases.
-    fn ieee_rem(self, rhs: Self) -> StatusAnd<Self> {
-        let mut v = self;
-
-        let status;
-        v = unpack!(status=, v / rhs);
-        if status == Status::DIV_BY_ZERO {
-            return status.and(self);
-        }
-
-        assert!(Self::PRECISION < 128);
-
-        let status;
-        let x = unpack!(status=, v.to_i128_r(128, Round::NearestTiesToEven, &mut false));
-        if status == Status::INVALID_OP {
-            return status.and(self);
-        }
-
-        let status;
-        let mut v = unpack!(status=, Self::from_i128(x));
-        assert_eq!(status, Status::OK); // should always work
-
-        let status;
-        v = unpack!(status=, v * rhs);
-        assert_eq!(status - Status::INEXACT, Status::OK); // should not overflow or underflow
-
-        let status;
-        v = unpack!(status=, self - v);
-        assert_eq!(status - Status::INEXACT, Status::OK); // likewise
-
-        if v.is_zero() {
-            status.and(v.copy_sign(self)) // IEEE754 requires this
-        } else {
-            status.and(v)
-        }
-    }
-    /// C fmod, or llvm frem.
-    fn c_fmod(self, rhs: Self) -> StatusAnd<Self>;
-    fn round_to_integral(self, round: Round) -> StatusAnd<Self>;
-
-    /// IEEE-754R 2008 5.3.1: nextUp.
-    fn next_up(self) -> StatusAnd<Self>;
-
-    /// IEEE-754R 2008 5.3.1: nextDown.
-    ///
-    /// *NOTE* since nextDown(x) = -nextUp(-x), we only implement nextUp with
-    /// appropriate sign switching before/after the computation.
-    fn next_down(self) -> StatusAnd<Self> {
-        (-self).next_up().map(|r| -r)
-    }
-
-    fn abs(self) -> Self {
-        if self.is_negative() { -self } else { self }
-    }
-    fn copy_sign(self, rhs: Self) -> Self {
-        if self.is_negative() != rhs.is_negative() { -self } else { self }
-    }
-
-    // Conversions
-    fn from_bits(input: u128) -> Self;
-    fn from_i128_r(input: i128, round: Round) -> StatusAnd<Self> {
-        if input < 0 {
-            Self::from_u128_r(input.wrapping_neg() as u128, -round).map(|r| -r)
-        } else {
-            Self::from_u128_r(input as u128, round)
-        }
-    }
-    fn from_i128(input: i128) -> StatusAnd<Self> {
-        Self::from_i128_r(input, Round::NearestTiesToEven)
-    }
-    fn from_u128_r(input: u128, round: Round) -> StatusAnd<Self>;
-    fn from_u128(input: u128) -> StatusAnd<Self> {
-        Self::from_u128_r(input, Round::NearestTiesToEven)
-    }
-    fn from_str_r(s: &str, round: Round) -> Result<StatusAnd<Self>, ParseError>;
-    fn to_bits(self) -> u128;
-
-    /// Converts a floating point number to an integer according to the
-    /// rounding mode. In case of an invalid operation exception,
-    /// deterministic values are returned, namely zero for NaNs and the
-    /// minimal or maximal value respectively for underflow or overflow.
-    /// If the rounded value is in range but the floating point number is
-    /// not the exact integer, the C standard doesn't require an inexact
-    /// exception to be raised. IEEE-854 does require it so we do that.
-    ///
-    /// Note that for conversions to integer type the C standard requires
-    /// round-to-zero to always be used.
-    ///
-    /// The *is_exact output tells whether the result is exact, in the sense
-    /// that converting it back to the original floating point type produces
-    /// the original value. This is almost equivalent to `result == Status::OK`,
-    /// except for negative zeroes.
-    fn to_i128_r(self, width: usize, round: Round, is_exact: &mut bool) -> StatusAnd<i128> {
-        let status;
-        if self.is_negative() {
-            if self.is_zero() {
-                // Negative zero can't be represented as an int.
-                *is_exact = false;
-            }
-            let r = unpack!(status=, (-self).to_u128_r(width, -round, is_exact));
-
-            // Check for values that don't fit in the signed integer.
-            if r > (1 << (width - 1)) {
-                // Return the most negative integer for the given width.
-                *is_exact = false;
-                Status::INVALID_OP.and(-1 << (width - 1))
-            } else {
-                status.and(r.wrapping_neg() as i128)
-            }
-        } else {
-            // Positive case is simpler, can pretend it's a smaller unsigned
-            // integer, and `to_u128` will take care of all the edge cases.
-            self.to_u128_r(width - 1, round, is_exact).map(|r| r as i128)
-        }
-    }
-    fn to_i128(self, width: usize) -> StatusAnd<i128> {
-        self.to_i128_r(width, Round::TowardZero, &mut true)
-    }
-    fn to_u128_r(self, width: usize, round: Round, is_exact: &mut bool) -> StatusAnd<u128>;
-    fn to_u128(self, width: usize) -> StatusAnd<u128> {
-        self.to_u128_r(width, Round::TowardZero, &mut true)
-    }
-
-    fn cmp_abs_normal(self, rhs: Self) -> Ordering;
-
-    /// Bitwise comparison for equality (QNaNs compare equal, 0!=-0).
-    fn bitwise_eq(self, rhs: Self) -> bool;
-
-    // IEEE-754R 5.7.2 General operations.
-
-    /// Implements IEEE minNum semantics. Returns the smaller of the 2 arguments if
-    /// both are not NaN. If either argument is a NaN, returns the other argument.
-    fn min(self, other: Self) -> Self {
-        if self.is_nan() {
-            other
-        } else if other.is_nan() {
-            self
-        } else if other.partial_cmp(&self) == Some(Ordering::Less) {
-            other
-        } else {
-            self
-        }
-    }
-
-    /// Implements IEEE maxNum semantics. Returns the larger of the 2 arguments if
-    /// both are not NaN. If either argument is a NaN, returns the other argument.
-    fn max(self, other: Self) -> Self {
-        if self.is_nan() {
-            other
-        } else if other.is_nan() {
-            self
-        } else if self.partial_cmp(&other) == Some(Ordering::Less) {
-            other
-        } else {
-            self
-        }
-    }
-
-    /// IEEE-754R isSignMinus: Returns whether the current value is
-    /// negative.
-    ///
-    /// This applies to zeros and NaNs as well.
-    fn is_negative(self) -> bool;
-
-    /// IEEE-754R isNormal: Returns whether the current value is normal.
-    ///
-    /// This implies that the current value of the float is not zero, subnormal,
-    /// infinite, or NaN following the definition of normality from IEEE-754R.
-    fn is_normal(self) -> bool {
-        !self.is_denormal() && self.is_finite_non_zero()
-    }
-
-    /// Returns `true` if the current value is zero, subnormal, or
-    /// normal.
-    ///
-    /// This means that the value is not infinite or NaN.
-    fn is_finite(self) -> bool {
-        !self.is_nan() && !self.is_infinite()
-    }
-
-    /// Returns `true` if the float is plus or minus zero.
-    fn is_zero(self) -> bool {
-        self.category() == Category::Zero
-    }
-
-    /// IEEE-754R isSubnormal(): Returns whether the float is a
-    /// denormal.
-    fn is_denormal(self) -> bool;
-
-    /// IEEE-754R isInfinite(): Returns whether the float is infinity.
-    fn is_infinite(self) -> bool {
-        self.category() == Category::Infinity
-    }
-
-    /// Returns `true` if the float is a quiet or signaling NaN.
-    fn is_nan(self) -> bool {
-        self.category() == Category::NaN
-    }
-
-    /// Returns `true` if the float is a signaling NaN.
-    fn is_signaling(self) -> bool;
-
-    // Simple Queries
-
-    fn category(self) -> Category;
-    fn is_non_zero(self) -> bool {
-        !self.is_zero()
-    }
-    fn is_finite_non_zero(self) -> bool {
-        self.is_finite() && !self.is_zero()
-    }
-    fn is_pos_zero(self) -> bool {
-        self.is_zero() && !self.is_negative()
-    }
-    fn is_neg_zero(self) -> bool {
-        self.is_zero() && self.is_negative()
-    }
-
-    /// Returns `true` if the number has the smallest possible non-zero
-    /// magnitude in the current semantics.
-    fn is_smallest(self) -> bool {
-        Self::SMALLEST.copy_sign(self).bitwise_eq(self)
-    }
-
-    /// Returns `true` if the number has the largest possible finite
-    /// magnitude in the current semantics.
-    fn is_largest(self) -> bool {
-        Self::largest().copy_sign(self).bitwise_eq(self)
-    }
-
-    /// Returns `true` if the number is an exact integer.
-    fn is_integer(self) -> bool {
-        // This could be made more efficient; I'm going for obviously correct.
-        if !self.is_finite() {
-            return false;
-        }
-        self.round_to_integral(Round::TowardZero).value.bitwise_eq(self)
-    }
-
-    /// If this value has an exact multiplicative inverse, return it.
-    fn get_exact_inverse(self) -> Option<Self>;
-
-    /// Returns the exponent of the internal representation of the Float.
-    ///
-    /// Because the radix of Float is 2, this is equivalent to floor(log2(x)).
-    /// For special Float values, this returns special error codes:
-    ///
-    ///   NaN -> \c IEK_NAN
-    ///   0   -> \c IEK_ZERO
-    ///   Inf -> \c IEK_INF
-    ///
-    fn ilogb(self) -> ExpInt;
-
-    /// Returns: self * 2<sup>exp</sup> for integral exponents.
-    /// Equivalent to C standard library function `ldexp`.
-    fn scalbn_r(self, exp: ExpInt, round: Round) -> Self;
-    fn scalbn(self, exp: ExpInt) -> Self {
-        self.scalbn_r(exp, Round::NearestTiesToEven)
-    }
-
-    /// Equivalent to C standard library function with the same name.
-    ///
-    /// While the C standard says exp is an unspecified value for infinity and nan,
-    /// this returns INT_MAX for infinities, and INT_MIN for NaNs (see `ilogb`).
-    fn frexp_r(self, exp: &mut ExpInt, round: Round) -> Self;
-    fn frexp(self, exp: &mut ExpInt) -> Self {
-        self.frexp_r(exp, Round::NearestTiesToEven)
-    }
-}
-
-pub trait FloatConvert<T: Float>: Float {
-    /// Converts a value of one floating point type to another.
-    /// The return value corresponds to the IEEE754 exceptions. *loses_info
-    /// records whether the transformation lost information, i.e., whether
-    /// converting the result back to the original type will produce the
-    /// original value (this is almost the same as return `value == Status::OK`,
-    /// but there are edge cases where this is not so).
-    fn convert_r(self, round: Round, loses_info: &mut bool) -> StatusAnd<T>;
-    fn convert(self, loses_info: &mut bool) -> StatusAnd<T> {
-        self.convert_r(Round::NearestTiesToEven, loses_info)
-    }
-}
-
-macro_rules! float_common_impls {
-    ($ty:ident<$t:tt>) => {
-        impl<$t> Default for $ty<$t>
-        where
-            Self: Float,
-        {
-            fn default() -> Self {
-                Self::ZERO
-            }
-        }
-
-        impl<$t> ::core::str::FromStr for $ty<$t>
-        where
-            Self: Float,
-        {
-            type Err = ParseError;
-            fn from_str(s: &str) -> Result<Self, ParseError> {
-                Self::from_str_r(s, Round::NearestTiesToEven).map(|x| x.value)
-            }
-        }
-
-        // Rounding ties to the nearest even, by default.
-
-        impl<$t> ::core::ops::Add for $ty<$t>
-        where
-            Self: Float,
-        {
-            type Output = StatusAnd<Self>;
-            fn add(self, rhs: Self) -> StatusAnd<Self> {
-                self.add_r(rhs, Round::NearestTiesToEven)
-            }
-        }
-
-        impl<$t> ::core::ops::Sub for $ty<$t>
-        where
-            Self: Float,
-        {
-            type Output = StatusAnd<Self>;
-            fn sub(self, rhs: Self) -> StatusAnd<Self> {
-                self.sub_r(rhs, Round::NearestTiesToEven)
-            }
-        }
-
-        impl<$t> ::core::ops::Mul for $ty<$t>
-        where
-            Self: Float,
-        {
-            type Output = StatusAnd<Self>;
-            fn mul(self, rhs: Self) -> StatusAnd<Self> {
-                self.mul_r(rhs, Round::NearestTiesToEven)
-            }
-        }
-
-        impl<$t> ::core::ops::Div for $ty<$t>
-        where
-            Self: Float,
-        {
-            type Output = StatusAnd<Self>;
-            fn div(self, rhs: Self) -> StatusAnd<Self> {
-                self.div_r(rhs, Round::NearestTiesToEven)
-            }
-        }
-
-        impl<$t> ::core::ops::Rem for $ty<$t>
-        where
-            Self: Float,
-        {
-            type Output = StatusAnd<Self>;
-            fn rem(self, rhs: Self) -> StatusAnd<Self> {
-                self.c_fmod(rhs)
-            }
-        }
-
-        impl<$t> ::core::ops::AddAssign for $ty<$t>
-        where
-            Self: Float,
-        {
-            fn add_assign(&mut self, rhs: Self) {
-                *self = (*self + rhs).value;
-            }
-        }
-
-        impl<$t> ::core::ops::SubAssign for $ty<$t>
-        where
-            Self: Float,
-        {
-            fn sub_assign(&mut self, rhs: Self) {
-                *self = (*self - rhs).value;
-            }
-        }
-
-        impl<$t> ::core::ops::MulAssign for $ty<$t>
-        where
-            Self: Float,
-        {
-            fn mul_assign(&mut self, rhs: Self) {
-                *self = (*self * rhs).value;
-            }
-        }
-
-        impl<$t> ::core::ops::DivAssign for $ty<$t>
-        where
-            Self: Float,
-        {
-            fn div_assign(&mut self, rhs: Self) {
-                *self = (*self / rhs).value;
-            }
-        }
-
-        impl<$t> ::core::ops::RemAssign for $ty<$t>
-        where
-            Self: Float,
-        {
-            fn rem_assign(&mut self, rhs: Self) {
-                *self = (*self % rhs).value;
-            }
-        }
-    };
-}
-
-pub mod ieee;
-pub mod ppc;
diff --git a/compiler/rustc_apfloat/src/ppc.rs b/compiler/rustc_apfloat/src/ppc.rs
deleted file mode 100644
index 65a0f66645b..00000000000
--- a/compiler/rustc_apfloat/src/ppc.rs
+++ /dev/null
@@ -1,434 +0,0 @@
-use crate::ieee;
-use crate::{Category, ExpInt, Float, FloatConvert, ParseError, Round, Status, StatusAnd};
-
-use core::cmp::Ordering;
-use core::fmt;
-use core::ops::Neg;
-
-#[must_use]
-#[derive(Copy, Clone, PartialEq, PartialOrd, Debug)]
-pub struct DoubleFloat<F>(F, F);
-pub type DoubleDouble = DoubleFloat<ieee::Double>;
-
-// These are legacy semantics for the Fallback, inaccurate implementation of
-// IBM double-double, if the accurate DoubleDouble doesn't handle the
-// operation. It's equivalent to having an IEEE number with consecutive 106
-// bits of mantissa and 11 bits of exponent.
-//
-// It's not equivalent to IBM double-double. For example, a legit IBM
-// double-double, 1 + epsilon:
-//
-//   1 + epsilon = 1 + (1 >> 1076)
-//
-// is not representable by a consecutive 106 bits of mantissa.
-//
-// Currently, these semantics are used in the following way:
-//
-//   DoubleDouble -> (Double, Double) ->
-//   DoubleDouble's Fallback -> IEEE operations
-//
-// FIXME: Implement all operations in DoubleDouble, and delete these
-// semantics.
-// FIXME(eddyb) This shouldn't need to be `pub`, it's only used in bounds.
-pub struct FallbackS<F>(#[allow(unused)] F);
-type Fallback<F> = ieee::IeeeFloat<FallbackS<F>>;
-impl<F: Float> ieee::Semantics for FallbackS<F> {
-    // Forbid any conversion to/from bits.
-    const BITS: usize = 0;
-    const PRECISION: usize = F::PRECISION * 2;
-    const MAX_EXP: ExpInt = F::MAX_EXP as ExpInt;
-    const MIN_EXP: ExpInt = F::MIN_EXP as ExpInt + F::PRECISION as ExpInt;
-}
-
-// Convert number to F. To avoid spurious underflows, we re-
-// normalize against the F exponent range first, and only *then*
-// truncate the mantissa. The result of that second conversion
-// may be inexact, but should never underflow.
-// FIXME(eddyb) This shouldn't need to be `pub`, it's only used in bounds.
-pub struct FallbackExtendedS<F>(#[allow(unused)] F);
-type FallbackExtended<F> = ieee::IeeeFloat<FallbackExtendedS<F>>;
-impl<F: Float> ieee::Semantics for FallbackExtendedS<F> {
-    // Forbid any conversion to/from bits.
-    const BITS: usize = 0;
-    const PRECISION: usize = Fallback::<F>::PRECISION;
-    const MAX_EXP: ExpInt = F::MAX_EXP as ExpInt;
-}
-
-impl<F: Float> From<Fallback<F>> for DoubleFloat<F>
-where
-    F: FloatConvert<FallbackExtended<F>>,
-    FallbackExtended<F>: FloatConvert<F>,
-{
-    fn from(x: Fallback<F>) -> Self {
-        let mut status;
-        let mut loses_info = false;
-
-        let extended: FallbackExtended<F> = unpack!(status=, x.convert(&mut loses_info));
-        assert_eq!((status, loses_info), (Status::OK, false));
-
-        let a = unpack!(status=, extended.convert(&mut loses_info));
-        assert_eq!(status - Status::INEXACT, Status::OK);
-
-        // If conversion was exact or resulted in a special case, we're done;
-        // just set the second double to zero. Otherwise, re-convert back to
-        // the extended format and compute the difference. This now should
-        // convert exactly to double.
-        let b = if a.is_finite_non_zero() && loses_info {
-            let u: FallbackExtended<F> = unpack!(status=, a.convert(&mut loses_info));
-            assert_eq!((status, loses_info), (Status::OK, false));
-            let v = unpack!(status=, extended - u);
-            assert_eq!(status, Status::OK);
-            let v = unpack!(status=, v.convert(&mut loses_info));
-            assert_eq!((status, loses_info), (Status::OK, false));
-            v
-        } else {
-            F::ZERO
-        };
-
-        DoubleFloat(a, b)
-    }
-}
-
-impl<F: FloatConvert<Self>> From<DoubleFloat<F>> for Fallback<F> {
-    fn from(DoubleFloat(a, b): DoubleFloat<F>) -> Self {
-        let mut status;
-        let mut loses_info = false;
-
-        // Get the first F and convert to our format.
-        let a = unpack!(status=, a.convert(&mut loses_info));
-        assert_eq!((status, loses_info), (Status::OK, false));
-
-        // Unless we have a special case, add in second F.
-        if a.is_finite_non_zero() {
-            let b = unpack!(status=, b.convert(&mut loses_info));
-            assert_eq!((status, loses_info), (Status::OK, false));
-
-            (a + b).value
-        } else {
-            a
-        }
-    }
-}
-
-float_common_impls!(DoubleFloat<F>);
-
-impl<F: Float> Neg for DoubleFloat<F> {
-    type Output = Self;
-    fn neg(self) -> Self {
-        if self.1.is_finite_non_zero() {
-            DoubleFloat(-self.0, -self.1)
-        } else {
-            DoubleFloat(-self.0, self.1)
-        }
-    }
-}
-
-impl<F: FloatConvert<Fallback<F>>> fmt::Display for DoubleFloat<F> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        fmt::Display::fmt(&Fallback::from(*self), f)
-    }
-}
-
-impl<F: FloatConvert<Fallback<F>>> Float for DoubleFloat<F>
-where
-    Self: From<Fallback<F>>,
-{
-    const BITS: usize = F::BITS * 2;
-    const PRECISION: usize = Fallback::<F>::PRECISION;
-    const MAX_EXP: ExpInt = Fallback::<F>::MAX_EXP;
-    const MIN_EXP: ExpInt = Fallback::<F>::MIN_EXP;
-
-    const ZERO: Self = DoubleFloat(F::ZERO, F::ZERO);
-
-    const INFINITY: Self = DoubleFloat(F::INFINITY, F::ZERO);
-
-    // FIXME(eddyb) remove when qnan becomes const fn.
-    const NAN: Self = DoubleFloat(F::NAN, F::ZERO);
-
-    fn qnan(payload: Option<u128>) -> Self {
-        DoubleFloat(F::qnan(payload), F::ZERO)
-    }
-
-    fn snan(payload: Option<u128>) -> Self {
-        DoubleFloat(F::snan(payload), F::ZERO)
-    }
-
-    fn largest() -> Self {
-        let status;
-        let mut r = DoubleFloat(F::largest(), F::largest());
-        r.1 = r.1.scalbn(-(F::PRECISION as ExpInt + 1));
-        r.1 = unpack!(status=, r.1.next_down());
-        assert_eq!(status, Status::OK);
-        r
-    }
-
-    const SMALLEST: Self = DoubleFloat(F::SMALLEST, F::ZERO);
-
-    fn smallest_normalized() -> Self {
-        DoubleFloat(F::smallest_normalized().scalbn(F::PRECISION as ExpInt), F::ZERO)
-    }
-
-    // Implement addition, subtraction, multiplication and division based on:
-    // "Software for Doubled-Precision Floating-Point Computations",
-    // by Seppo Linnainmaa, ACM TOMS vol 7 no 3, September 1981, pages 272-283.
-
-    fn add_r(mut self, rhs: Self, round: Round) -> StatusAnd<Self> {
-        match (self.category(), rhs.category()) {
-            (Category::Infinity, Category::Infinity) => {
-                if self.is_negative() != rhs.is_negative() {
-                    Status::INVALID_OP.and(Self::NAN.copy_sign(self))
-                } else {
-                    Status::OK.and(self)
-                }
-            }
-
-            (_, Category::Zero) | (Category::NaN, _) | (Category::Infinity, Category::Normal) => {
-                Status::OK.and(self)
-            }
-
-            (Category::Zero, _) | (_, Category::NaN | Category::Infinity) => Status::OK.and(rhs),
-
-            (Category::Normal, Category::Normal) => {
-                let mut status = Status::OK;
-                let (a, aa, c, cc) = (self.0, self.1, rhs.0, rhs.1);
-                let mut z = a;
-                z = unpack!(status|=, z.add_r(c, round));
-                if !z.is_finite() {
-                    if !z.is_infinite() {
-                        return status.and(DoubleFloat(z, F::ZERO));
-                    }
-                    status = Status::OK;
-                    let a_cmp_c = a.cmp_abs_normal(c);
-                    z = cc;
-                    z = unpack!(status|=, z.add_r(aa, round));
-                    if a_cmp_c == Ordering::Greater {
-                        // z = cc + aa + c + a;
-                        z = unpack!(status|=, z.add_r(c, round));
-                        z = unpack!(status|=, z.add_r(a, round));
-                    } else {
-                        // z = cc + aa + a + c;
-                        z = unpack!(status|=, z.add_r(a, round));
-                        z = unpack!(status|=, z.add_r(c, round));
-                    }
-                    if !z.is_finite() {
-                        return status.and(DoubleFloat(z, F::ZERO));
-                    }
-                    self.0 = z;
-                    let mut zz = aa;
-                    zz = unpack!(status|=, zz.add_r(cc, round));
-                    if a_cmp_c == Ordering::Greater {
-                        // self.1 = a - z + c + zz;
-                        self.1 = a;
-                        self.1 = unpack!(status|=, self.1.sub_r(z, round));
-                        self.1 = unpack!(status|=, self.1.add_r(c, round));
-                        self.1 = unpack!(status|=, self.1.add_r(zz, round));
-                    } else {
-                        // self.1 = c - z + a + zz;
-                        self.1 = c;
-                        self.1 = unpack!(status|=, self.1.sub_r(z, round));
-                        self.1 = unpack!(status|=, self.1.add_r(a, round));
-                        self.1 = unpack!(status|=, self.1.add_r(zz, round));
-                    }
-                } else {
-                    // q = a - z;
-                    let mut q = a;
-                    q = unpack!(status|=, q.sub_r(z, round));
-
-                    // zz = q + c + (a - (q + z)) + aa + cc;
-                    // Compute a - (q + z) as -((q + z) - a) to avoid temporary copies.
-                    let mut zz = q;
-                    zz = unpack!(status|=, zz.add_r(c, round));
-                    q = unpack!(status|=, q.add_r(z, round));
-                    q = unpack!(status|=, q.sub_r(a, round));
-                    q = -q;
-                    zz = unpack!(status|=, zz.add_r(q, round));
-                    zz = unpack!(status|=, zz.add_r(aa, round));
-                    zz = unpack!(status|=, zz.add_r(cc, round));
-                    if zz.is_zero() && !zz.is_negative() {
-                        return Status::OK.and(DoubleFloat(z, F::ZERO));
-                    }
-                    self.0 = z;
-                    self.0 = unpack!(status|=, self.0.add_r(zz, round));
-                    if !self.0.is_finite() {
-                        self.1 = F::ZERO;
-                        return status.and(self);
-                    }
-                    self.1 = z;
-                    self.1 = unpack!(status|=, self.1.sub_r(self.0, round));
-                    self.1 = unpack!(status|=, self.1.add_r(zz, round));
-                }
-                status.and(self)
-            }
-        }
-    }
-
-    fn mul_r(mut self, rhs: Self, round: Round) -> StatusAnd<Self> {
-        // Interesting observation: For special categories, finding the lowest
-        // common ancestor of the following layered graph gives the correct
-        // return category:
-        //
-        //    NaN
-        //   /   \
-        // Zero  Inf
-        //   \   /
-        //   Normal
-        //
-        // e.g., NaN * NaN = NaN
-        //      Zero * Inf = NaN
-        //      Normal * Zero = Zero
-        //      Normal * Inf = Inf
-        match (self.category(), rhs.category()) {
-            (Category::NaN, _) => Status::OK.and(self),
-
-            (_, Category::NaN) => Status::OK.and(rhs),
-
-            (Category::Zero, Category::Infinity) | (Category::Infinity, Category::Zero) => {
-                Status::OK.and(Self::NAN)
-            }
-
-            (Category::Zero | Category::Infinity, _) => Status::OK.and(self),
-
-            (_, Category::Zero | Category::Infinity) => Status::OK.and(rhs),
-
-            (Category::Normal, Category::Normal) => {
-                let mut status = Status::OK;
-                let (a, b, c, d) = (self.0, self.1, rhs.0, rhs.1);
-                // t = a * c
-                let mut t = a;
-                t = unpack!(status|=, t.mul_r(c, round));
-                if !t.is_finite_non_zero() {
-                    return status.and(DoubleFloat(t, F::ZERO));
-                }
-
-                // tau = fmsub(a, c, t), that is -fmadd(-a, c, t).
-                let mut tau = a;
-                tau = unpack!(status|=, tau.mul_add_r(c, -t, round));
-                // v = a * d
-                let mut v = a;
-                v = unpack!(status|=, v.mul_r(d, round));
-                // w = b * c
-                let mut w = b;
-                w = unpack!(status|=, w.mul_r(c, round));
-                v = unpack!(status|=, v.add_r(w, round));
-                // tau += v + w
-                tau = unpack!(status|=, tau.add_r(v, round));
-                // u = t + tau
-                let mut u = t;
-                u = unpack!(status|=, u.add_r(tau, round));
-
-                self.0 = u;
-                if !u.is_finite() {
-                    self.1 = F::ZERO;
-                } else {
-                    // self.1 = (t - u) + tau
-                    t = unpack!(status|=, t.sub_r(u, round));
-                    t = unpack!(status|=, t.add_r(tau, round));
-                    self.1 = t;
-                }
-                status.and(self)
-            }
-        }
-    }
-
-    fn mul_add_r(self, multiplicand: Self, addend: Self, round: Round) -> StatusAnd<Self> {
-        Fallback::from(self)
-            .mul_add_r(Fallback::from(multiplicand), Fallback::from(addend), round)
-            .map(Self::from)
-    }
-
-    fn div_r(self, rhs: Self, round: Round) -> StatusAnd<Self> {
-        Fallback::from(self).div_r(Fallback::from(rhs), round).map(Self::from)
-    }
-
-    fn c_fmod(self, rhs: Self) -> StatusAnd<Self> {
-        Fallback::from(self).c_fmod(Fallback::from(rhs)).map(Self::from)
-    }
-
-    fn round_to_integral(self, round: Round) -> StatusAnd<Self> {
-        Fallback::from(self).round_to_integral(round).map(Self::from)
-    }
-
-    fn next_up(self) -> StatusAnd<Self> {
-        Fallback::from(self).next_up().map(Self::from)
-    }
-
-    fn from_bits(input: u128) -> Self {
-        let (a, b) = (input, input >> F::BITS);
-        DoubleFloat(F::from_bits(a & ((1 << F::BITS) - 1)), F::from_bits(b & ((1 << F::BITS) - 1)))
-    }
-
-    fn from_u128_r(input: u128, round: Round) -> StatusAnd<Self> {
-        Fallback::from_u128_r(input, round).map(Self::from)
-    }
-
-    fn from_str_r(s: &str, round: Round) -> Result<StatusAnd<Self>, ParseError> {
-        Fallback::from_str_r(s, round).map(|r| r.map(Self::from))
-    }
-
-    fn to_bits(self) -> u128 {
-        self.0.to_bits() | (self.1.to_bits() << F::BITS)
-    }
-
-    fn to_u128_r(self, width: usize, round: Round, is_exact: &mut bool) -> StatusAnd<u128> {
-        Fallback::from(self).to_u128_r(width, round, is_exact)
-    }
-
-    fn cmp_abs_normal(self, rhs: Self) -> Ordering {
-        self.0.cmp_abs_normal(rhs.0).then_with(|| {
-            let result = self.1.cmp_abs_normal(rhs.1);
-            if result != Ordering::Equal {
-                let against = self.0.is_negative() ^ self.1.is_negative();
-                let rhs_against = rhs.0.is_negative() ^ rhs.1.is_negative();
-                (!against)
-                    .cmp(&!rhs_against)
-                    .then_with(|| if against { result.reverse() } else { result })
-            } else {
-                result
-            }
-        })
-    }
-
-    fn bitwise_eq(self, rhs: Self) -> bool {
-        self.0.bitwise_eq(rhs.0) && self.1.bitwise_eq(rhs.1)
-    }
-
-    fn is_negative(self) -> bool {
-        self.0.is_negative()
-    }
-
-    fn is_denormal(self) -> bool {
-        self.category() == Category::Normal
-            && (self.0.is_denormal() || self.0.is_denormal() ||
-          // (double)(Hi + Lo) == Hi defines a normal number.
-          !(self.0 + self.1).value.bitwise_eq(self.0))
-    }
-
-    fn is_signaling(self) -> bool {
-        self.0.is_signaling()
-    }
-
-    fn category(self) -> Category {
-        self.0.category()
-    }
-
-    fn get_exact_inverse(self) -> Option<Self> {
-        Fallback::from(self).get_exact_inverse().map(Self::from)
-    }
-
-    fn ilogb(self) -> ExpInt {
-        self.0.ilogb()
-    }
-
-    fn scalbn_r(self, exp: ExpInt, round: Round) -> Self {
-        DoubleFloat(self.0.scalbn_r(exp, round), self.1.scalbn_r(exp, round))
-    }
-
-    fn frexp_r(self, exp: &mut ExpInt, round: Round) -> Self {
-        let a = self.0.frexp_r(exp, round);
-        let mut b = self.1;
-        if self.category() == Category::Normal {
-            b = b.scalbn_r(-*exp, round);
-        }
-        DoubleFloat(a, b)
-    }
-}
diff --git a/compiler/rustc_apfloat/tests/ieee.rs b/compiler/rustc_apfloat/tests/ieee.rs
deleted file mode 100644
index f8fac0c2358..00000000000
--- a/compiler/rustc_apfloat/tests/ieee.rs
+++ /dev/null
@@ -1,3301 +0,0 @@
-// ignore-tidy-filelength
-
-use rustc_apfloat::ieee::{Double, Half, Quad, Single, X87DoubleExtended};
-use rustc_apfloat::unpack;
-use rustc_apfloat::{Category, ExpInt, IEK_INF, IEK_NAN, IEK_ZERO};
-use rustc_apfloat::{Float, FloatConvert, ParseError, Round, Status};
-
-trait SingleExt {
-    fn from_f32(input: f32) -> Self;
-    fn to_f32(self) -> f32;
-}
-
-impl SingleExt for Single {
-    fn from_f32(input: f32) -> Self {
-        Self::from_bits(input.to_bits() as u128)
-    }
-
-    fn to_f32(self) -> f32 {
-        f32::from_bits(self.to_bits() as u32)
-    }
-}
-
-trait DoubleExt {
-    fn from_f64(input: f64) -> Self;
-    fn to_f64(self) -> f64;
-}
-
-impl DoubleExt for Double {
-    fn from_f64(input: f64) -> Self {
-        Self::from_bits(input.to_bits() as u128)
-    }
-
-    fn to_f64(self) -> f64 {
-        f64::from_bits(self.to_bits() as u64)
-    }
-}
-
-#[test]
-fn is_signaling() {
-    // We test qNaN, -qNaN, +sNaN, -sNaN with and without payloads.
-    let payload = 4;
-    assert!(!Single::qnan(None).is_signaling());
-    assert!(!(-Single::qnan(None)).is_signaling());
-    assert!(!Single::qnan(Some(payload)).is_signaling());
-    assert!(!(-Single::qnan(Some(payload))).is_signaling());
-    assert!(Single::snan(None).is_signaling());
-    assert!((-Single::snan(None)).is_signaling());
-    assert!(Single::snan(Some(payload)).is_signaling());
-    assert!((-Single::snan(Some(payload))).is_signaling());
-}
-
-#[test]
-fn next() {
-    // 1. Test Special Cases Values.
-    //
-    // Test all special values for nextUp and nextDown perscribed by IEEE-754R
-    // 2008. These are:
-    //   1. +inf
-    //   2. -inf
-    //   3. largest
-    //   4. -largest
-    //   5. smallest
-    //   6. -smallest
-    //   7. qNaN
-    //   8. sNaN
-    //   9. +0
-    //   10. -0
-
-    let mut status;
-
-    // nextUp(+inf) = +inf.
-    let test = unpack!(status=, Quad::INFINITY.next_up());
-    let expected = Quad::INFINITY;
-    assert_eq!(status, Status::OK);
-    assert!(test.is_infinite());
-    assert!(!test.is_negative());
-    assert!(test.bitwise_eq(expected));
-
-    // nextDown(+inf) = -nextUp(-inf) = -(-largest) = largest
-    let test = unpack!(status=, Quad::INFINITY.next_down());
-    let expected = Quad::largest();
-    assert_eq!(status, Status::OK);
-    assert!(!test.is_negative());
-    assert!(test.bitwise_eq(expected));
-
-    // nextUp(-inf) = -largest
-    let test = unpack!(status=, (-Quad::INFINITY).next_up());
-    let expected = -Quad::largest();
-    assert_eq!(status, Status::OK);
-    assert!(test.is_negative());
-    assert!(test.bitwise_eq(expected));
-
-    // nextDown(-inf) = -nextUp(+inf) = -(+inf) = -inf.
-    let test = unpack!(status=, (-Quad::INFINITY).next_down());
-    let expected = -Quad::INFINITY;
-    assert_eq!(status, Status::OK);
-    assert!(test.is_infinite() && test.is_negative());
-    assert!(test.bitwise_eq(expected));
-
-    // nextUp(largest) = +inf
-    let test = unpack!(status=, Quad::largest().next_up());
-    let expected = Quad::INFINITY;
-    assert_eq!(status, Status::OK);
-    assert!(test.is_infinite() && !test.is_negative());
-    assert!(test.bitwise_eq(expected));
-
-    // nextDown(largest) = -nextUp(-largest)
-    //                        = -(-largest + inc)
-    //                        = largest - inc.
-    let test = unpack!(status=, Quad::largest().next_down());
-    let expected = "0x1.fffffffffffffffffffffffffffep+16383".parse::<Quad>().unwrap();
-    assert_eq!(status, Status::OK);
-    assert!(!test.is_infinite() && !test.is_negative());
-    assert!(test.bitwise_eq(expected));
-
-    // nextUp(-largest) = -largest + inc.
-    let test = unpack!(status=, (-Quad::largest()).next_up());
-    let expected = "-0x1.fffffffffffffffffffffffffffep+16383".parse::<Quad>().unwrap();
-    assert_eq!(status, Status::OK);
-    assert!(test.bitwise_eq(expected));
-
-    // nextDown(-largest) = -nextUp(largest) = -(inf) = -inf.
-    let test = unpack!(status=, (-Quad::largest()).next_down());
-    let expected = -Quad::INFINITY;
-    assert_eq!(status, Status::OK);
-    assert!(test.is_infinite() && test.is_negative());
-    assert!(test.bitwise_eq(expected));
-
-    // nextUp(smallest) = smallest + inc.
-    let test = unpack!(status=, "0x0.0000000000000000000000000001p-16382"
-        .parse::<Quad>()
-        .unwrap()
-        .next_up());
-    let expected = "0x0.0000000000000000000000000002p-16382".parse::<Quad>().unwrap();
-    assert_eq!(status, Status::OK);
-    assert!(test.bitwise_eq(expected));
-
-    // nextDown(smallest) = -nextUp(-smallest) = -(-0) = +0.
-    let test = unpack!(status=, "0x0.0000000000000000000000000001p-16382"
-        .parse::<Quad>()
-        .unwrap()
-        .next_down());
-    let expected = Quad::ZERO;
-    assert_eq!(status, Status::OK);
-    assert!(test.is_pos_zero());
-    assert!(test.bitwise_eq(expected));
-
-    // nextUp(-smallest) = -0.
-    let test = unpack!(status=, "-0x0.0000000000000000000000000001p-16382"
-        .parse::<Quad>()
-        .unwrap()
-        .next_up());
-    let expected = -Quad::ZERO;
-    assert_eq!(status, Status::OK);
-    assert!(test.is_neg_zero());
-    assert!(test.bitwise_eq(expected));
-
-    // nextDown(-smallest) = -nextUp(smallest) = -smallest - inc.
-    let test = unpack!(status=, "-0x0.0000000000000000000000000001p-16382"
-        .parse::<Quad>()
-        .unwrap()
-        .next_down());
-    let expected = "-0x0.0000000000000000000000000002p-16382".parse::<Quad>().unwrap();
-    assert_eq!(status, Status::OK);
-    assert!(test.bitwise_eq(expected));
-
-    // nextUp(qNaN) = qNaN
-    let test = unpack!(status=, Quad::qnan(None).next_up());
-    let expected = Quad::qnan(None);
-    assert_eq!(status, Status::OK);
-    assert!(test.bitwise_eq(expected));
-
-    // nextDown(qNaN) = qNaN
-    let test = unpack!(status=, Quad::qnan(None).next_down());
-    let expected = Quad::qnan(None);
-    assert_eq!(status, Status::OK);
-    assert!(test.bitwise_eq(expected));
-
-    // nextUp(sNaN) = qNaN
-    let test = unpack!(status=, Quad::snan(None).next_up());
-    let expected = Quad::qnan(None);
-    assert_eq!(status, Status::INVALID_OP);
-    assert!(test.bitwise_eq(expected));
-
-    // nextDown(sNaN) = qNaN
-    let test = unpack!(status=, Quad::snan(None).next_down());
-    let expected = Quad::qnan(None);
-    assert_eq!(status, Status::INVALID_OP);
-    assert!(test.bitwise_eq(expected));
-
-    // nextUp(+0) = +smallest
-    let test = unpack!(status=, Quad::ZERO.next_up());
-    let expected = Quad::SMALLEST;
-    assert_eq!(status, Status::OK);
-    assert!(test.bitwise_eq(expected));
-
-    // nextDown(+0) = -nextUp(-0) = -smallest
-    let test = unpack!(status=, Quad::ZERO.next_down());
-    let expected = -Quad::SMALLEST;
-    assert_eq!(status, Status::OK);
-    assert!(test.bitwise_eq(expected));
-
-    // nextUp(-0) = +smallest
-    let test = unpack!(status=, (-Quad::ZERO).next_up());
-    let expected = Quad::SMALLEST;
-    assert_eq!(status, Status::OK);
-    assert!(test.bitwise_eq(expected));
-
-    // nextDown(-0) = -nextUp(0) = -smallest
-    let test = unpack!(status=, (-Quad::ZERO).next_down());
-    let expected = -Quad::SMALLEST;
-    assert_eq!(status, Status::OK);
-    assert!(test.bitwise_eq(expected));
-
-    // 2. Binade Boundary Tests.
-
-    // 2a. Test denormal <-> normal binade boundaries.
-    //     * nextUp(+Largest Denormal) -> +Smallest Normal.
-    //     * nextDown(-Largest Denormal) -> -Smallest Normal.
-    //     * nextUp(-Smallest Normal) -> -Largest Denormal.
-    //     * nextDown(+Smallest Normal) -> +Largest Denormal.
-
-    // nextUp(+Largest Denormal) -> +Smallest Normal.
-    let test = unpack!(status=, "0x0.ffffffffffffffffffffffffffffp-16382"
-        .parse::<Quad>()
-        .unwrap()
-        .next_up());
-    let expected = "0x1.0000000000000000000000000000p-16382".parse::<Quad>().unwrap();
-    assert_eq!(status, Status::OK);
-    assert!(!test.is_denormal());
-    assert!(test.bitwise_eq(expected));
-
-    // nextDown(-Largest Denormal) -> -Smallest Normal.
-    let test = unpack!(status=, "-0x0.ffffffffffffffffffffffffffffp-16382"
-        .parse::<Quad>()
-        .unwrap()
-        .next_down());
-    let expected = "-0x1.0000000000000000000000000000p-16382".parse::<Quad>().unwrap();
-    assert_eq!(status, Status::OK);
-    assert!(!test.is_denormal());
-    assert!(test.bitwise_eq(expected));
-
-    // nextUp(-Smallest Normal) -> -Largest Denormal.
-    let test = unpack!(status=, "-0x1.0000000000000000000000000000p-16382"
-        .parse::<Quad>()
-        .unwrap()
-        .next_up());
-    let expected = "-0x0.ffffffffffffffffffffffffffffp-16382".parse::<Quad>().unwrap();
-    assert_eq!(status, Status::OK);
-    assert!(test.is_denormal());
-    assert!(test.bitwise_eq(expected));
-
-    // nextDown(+Smallest Normal) -> +Largest Denormal.
-    let test = unpack!(status=, "+0x1.0000000000000000000000000000p-16382"
-        .parse::<Quad>()
-        .unwrap()
-        .next_down());
-    let expected = "+0x0.ffffffffffffffffffffffffffffp-16382".parse::<Quad>().unwrap();
-    assert_eq!(status, Status::OK);
-    assert!(test.is_denormal());
-    assert!(test.bitwise_eq(expected));
-
-    // 2b. Test normal <-> normal binade boundaries.
-    //     * nextUp(-Normal Binade Boundary) -> -Normal Binade Boundary + 1.
-    //     * nextDown(+Normal Binade Boundary) -> +Normal Binade Boundary - 1.
-    //     * nextUp(+Normal Binade Boundary - 1) -> +Normal Binade Boundary.
-    //     * nextDown(-Normal Binade Boundary + 1) -> -Normal Binade Boundary.
-
-    // nextUp(-Normal Binade Boundary) -> -Normal Binade Boundary + 1.
-    let test = unpack!(status=, "-0x1p+1".parse::<Quad>().unwrap().next_up());
-    let expected = "-0x1.ffffffffffffffffffffffffffffp+0".parse::<Quad>().unwrap();
-    assert_eq!(status, Status::OK);
-    assert!(test.bitwise_eq(expected));
-
-    // nextDown(+Normal Binade Boundary) -> +Normal Binade Boundary - 1.
-    let test = unpack!(status=, "0x1p+1".parse::<Quad>().unwrap().next_down());
-    let expected = "0x1.ffffffffffffffffffffffffffffp+0".parse::<Quad>().unwrap();
-    assert_eq!(status, Status::OK);
-    assert!(test.bitwise_eq(expected));
-
-    // nextUp(+Normal Binade Boundary - 1) -> +Normal Binade Boundary.
-    let test = unpack!(status=, "0x1.ffffffffffffffffffffffffffffp+0"
-        .parse::<Quad>()
-        .unwrap()
-        .next_up());
-    let expected = "0x1p+1".parse::<Quad>().unwrap();
-    assert_eq!(status, Status::OK);
-    assert!(test.bitwise_eq(expected));
-
-    // nextDown(-Normal Binade Boundary + 1) -> -Normal Binade Boundary.
-    let test = unpack!(status=, "-0x1.ffffffffffffffffffffffffffffp+0"
-        .parse::<Quad>()
-        .unwrap()
-        .next_down());
-    let expected = "-0x1p+1".parse::<Quad>().unwrap();
-    assert_eq!(status, Status::OK);
-    assert!(test.bitwise_eq(expected));
-
-    // 2c. Test using next at binade boundaries with a direction away from the
-    // binade boundary. Away from denormal <-> normal boundaries.
-    //
-    // This is to make sure that even though we are at a binade boundary, since
-    // we are rounding away, we do not trigger the binade boundary code. Thus we
-    // test:
-    //   * nextUp(-Largest Denormal) -> -Largest Denormal + inc.
-    //   * nextDown(+Largest Denormal) -> +Largest Denormal - inc.
-    //   * nextUp(+Smallest Normal) -> +Smallest Normal + inc.
-    //   * nextDown(-Smallest Normal) -> -Smallest Normal - inc.
-
-    // nextUp(-Largest Denormal) -> -Largest Denormal + inc.
-    let test = unpack!(status=, "-0x0.ffffffffffffffffffffffffffffp-16382"
-        .parse::<Quad>()
-        .unwrap()
-        .next_up());
-    let expected = "-0x0.fffffffffffffffffffffffffffep-16382".parse::<Quad>().unwrap();
-    assert_eq!(status, Status::OK);
-    assert!(test.is_denormal());
-    assert!(test.is_negative());
-    assert!(test.bitwise_eq(expected));
-
-    // nextDown(+Largest Denormal) -> +Largest Denormal - inc.
-    let test = unpack!(status=, "0x0.ffffffffffffffffffffffffffffp-16382"
-        .parse::<Quad>()
-        .unwrap()
-        .next_down());
-    let expected = "0x0.fffffffffffffffffffffffffffep-16382".parse::<Quad>().unwrap();
-    assert_eq!(status, Status::OK);
-    assert!(test.is_denormal());
-    assert!(!test.is_negative());
-    assert!(test.bitwise_eq(expected));
-
-    // nextUp(+Smallest Normal) -> +Smallest Normal + inc.
-    let test = unpack!(status=, "0x1.0000000000000000000000000000p-16382"
-        .parse::<Quad>()
-        .unwrap()
-        .next_up());
-    let expected = "0x1.0000000000000000000000000001p-16382".parse::<Quad>().unwrap();
-    assert_eq!(status, Status::OK);
-    assert!(!test.is_denormal());
-    assert!(!test.is_negative());
-    assert!(test.bitwise_eq(expected));
-
-    // nextDown(-Smallest Normal) -> -Smallest Normal - inc.
-    let test = unpack!(status=, "-0x1.0000000000000000000000000000p-16382"
-        .parse::<Quad>()
-        .unwrap()
-        .next_down());
-    let expected = "-0x1.0000000000000000000000000001p-16382".parse::<Quad>().unwrap();
-    assert_eq!(status, Status::OK);
-    assert!(!test.is_denormal());
-    assert!(test.is_negative());
-    assert!(test.bitwise_eq(expected));
-
-    // 2d. Test values which cause our exponent to go to min exponent. This
-    // is to ensure that guards in the code to check for min exponent
-    // trigger properly.
-    //     * nextUp(-0x1p-16381) -> -0x1.ffffffffffffffffffffffffffffp-16382
-    //     * nextDown(-0x1.ffffffffffffffffffffffffffffp-16382) ->
-    //         -0x1p-16381
-    //     * nextUp(0x1.ffffffffffffffffffffffffffffp-16382) -> 0x1p-16382
-    //     * nextDown(0x1p-16382) -> 0x1.ffffffffffffffffffffffffffffp-16382
-
-    // nextUp(-0x1p-16381) -> -0x1.ffffffffffffffffffffffffffffp-16382
-    let test = unpack!(status=, "-0x1p-16381".parse::<Quad>().unwrap().next_up());
-    let expected = "-0x1.ffffffffffffffffffffffffffffp-16382".parse::<Quad>().unwrap();
-    assert_eq!(status, Status::OK);
-    assert!(test.bitwise_eq(expected));
-
-    // nextDown(-0x1.ffffffffffffffffffffffffffffp-16382) ->
-    //         -0x1p-16381
-    let test = unpack!(status=, "-0x1.ffffffffffffffffffffffffffffp-16382"
-        .parse::<Quad>()
-        .unwrap()
-        .next_down());
-    let expected = "-0x1p-16381".parse::<Quad>().unwrap();
-    assert_eq!(status, Status::OK);
-    assert!(test.bitwise_eq(expected));
-
-    // nextUp(0x1.ffffffffffffffffffffffffffffp-16382) -> 0x1p-16381
-    let test = unpack!(status=, "0x1.ffffffffffffffffffffffffffffp-16382"
-        .parse::<Quad>()
-        .unwrap()
-        .next_up());
-    let expected = "0x1p-16381".parse::<Quad>().unwrap();
-    assert_eq!(status, Status::OK);
-    assert!(test.bitwise_eq(expected));
-
-    // nextDown(0x1p-16381) -> 0x1.ffffffffffffffffffffffffffffp-16382
-    let test = unpack!(status=, "0x1p-16381".parse::<Quad>().unwrap().next_down());
-    let expected = "0x1.ffffffffffffffffffffffffffffp-16382".parse::<Quad>().unwrap();
-    assert_eq!(status, Status::OK);
-    assert!(test.bitwise_eq(expected));
-
-    // 3. Now we test both denormal/normal computation which will not cause us
-    // to go across binade boundaries. Specifically we test:
-    //   * nextUp(+Denormal) -> +Denormal.
-    //   * nextDown(+Denormal) -> +Denormal.
-    //   * nextUp(-Denormal) -> -Denormal.
-    //   * nextDown(-Denormal) -> -Denormal.
-    //   * nextUp(+Normal) -> +Normal.
-    //   * nextDown(+Normal) -> +Normal.
-    //   * nextUp(-Normal) -> -Normal.
-    //   * nextDown(-Normal) -> -Normal.
-
-    // nextUp(+Denormal) -> +Denormal.
-    let test = unpack!(status=, "0x0.ffffffffffffffffffffffff000cp-16382"
-        .parse::<Quad>()
-        .unwrap()
-        .next_up());
-    let expected = "0x0.ffffffffffffffffffffffff000dp-16382".parse::<Quad>().unwrap();
-    assert_eq!(status, Status::OK);
-    assert!(test.is_denormal());
-    assert!(!test.is_negative());
-    assert!(test.bitwise_eq(expected));
-
-    // nextDown(+Denormal) -> +Denormal.
-    let test = unpack!(status=, "0x0.ffffffffffffffffffffffff000cp-16382"
-        .parse::<Quad>()
-        .unwrap()
-        .next_down());
-    let expected = "0x0.ffffffffffffffffffffffff000bp-16382".parse::<Quad>().unwrap();
-    assert_eq!(status, Status::OK);
-    assert!(test.is_denormal());
-    assert!(!test.is_negative());
-    assert!(test.bitwise_eq(expected));
-
-    // nextUp(-Denormal) -> -Denormal.
-    let test = unpack!(status=, "-0x0.ffffffffffffffffffffffff000cp-16382"
-        .parse::<Quad>()
-        .unwrap()
-        .next_up());
-    let expected = "-0x0.ffffffffffffffffffffffff000bp-16382".parse::<Quad>().unwrap();
-    assert_eq!(status, Status::OK);
-    assert!(test.is_denormal());
-    assert!(test.is_negative());
-    assert!(test.bitwise_eq(expected));
-
-    // nextDown(-Denormal) -> -Denormal
-    let test = unpack!(status=, "-0x0.ffffffffffffffffffffffff000cp-16382"
-        .parse::<Quad>()
-        .unwrap()
-        .next_down());
-    let expected = "-0x0.ffffffffffffffffffffffff000dp-16382".parse::<Quad>().unwrap();
-    assert_eq!(status, Status::OK);
-    assert!(test.is_denormal());
-    assert!(test.is_negative());
-    assert!(test.bitwise_eq(expected));
-
-    // nextUp(+Normal) -> +Normal.
-    let test = unpack!(status=, "0x1.ffffffffffffffffffffffff000cp-16000"
-        .parse::<Quad>()
-        .unwrap()
-        .next_up());
-    let expected = "0x1.ffffffffffffffffffffffff000dp-16000".parse::<Quad>().unwrap();
-    assert_eq!(status, Status::OK);
-    assert!(!test.is_denormal());
-    assert!(!test.is_negative());
-    assert!(test.bitwise_eq(expected));
-
-    // nextDown(+Normal) -> +Normal.
-    let test = unpack!(status=, "0x1.ffffffffffffffffffffffff000cp-16000"
-        .parse::<Quad>()
-        .unwrap()
-        .next_down());
-    let expected = "0x1.ffffffffffffffffffffffff000bp-16000".parse::<Quad>().unwrap();
-    assert_eq!(status, Status::OK);
-    assert!(!test.is_denormal());
-    assert!(!test.is_negative());
-    assert!(test.bitwise_eq(expected));
-
-    // nextUp(-Normal) -> -Normal.
-    let test = unpack!(status=, "-0x1.ffffffffffffffffffffffff000cp-16000"
-        .parse::<Quad>()
-        .unwrap()
-        .next_up());
-    let expected = "-0x1.ffffffffffffffffffffffff000bp-16000".parse::<Quad>().unwrap();
-    assert_eq!(status, Status::OK);
-    assert!(!test.is_denormal());
-    assert!(test.is_negative());
-    assert!(test.bitwise_eq(expected));
-
-    // nextDown(-Normal) -> -Normal.
-    let test = unpack!(status=, "-0x1.ffffffffffffffffffffffff000cp-16000"
-        .parse::<Quad>()
-        .unwrap()
-        .next_down());
-    let expected = "-0x1.ffffffffffffffffffffffff000dp-16000".parse::<Quad>().unwrap();
-    assert_eq!(status, Status::OK);
-    assert!(!test.is_denormal());
-    assert!(test.is_negative());
-    assert!(test.bitwise_eq(expected));
-}
-
-#[test]
-fn fma() {
-    {
-        let mut f1 = Single::from_f32(14.5);
-        let f2 = Single::from_f32(-14.5);
-        let f3 = Single::from_f32(225.0);
-        f1 = f1.mul_add(f2, f3).value;
-        assert_eq!(14.75, f1.to_f32());
-    }
-
-    {
-        let val2 = Single::from_f32(2.0);
-        let mut f1 = Single::from_f32(1.17549435e-38);
-        let mut f2 = Single::from_f32(1.17549435e-38);
-        f1 /= val2;
-        f2 /= val2;
-        let f3 = Single::from_f32(12.0);
-        f1 = f1.mul_add(f2, f3).value;
-        assert_eq!(12.0, f1.to_f32());
-    }
-
-    // Test for correct zero sign when answer is exactly zero.
-    // fma(1.0, -1.0, 1.0) -> +ve 0.
-    {
-        let mut f1 = Double::from_f64(1.0);
-        let f2 = Double::from_f64(-1.0);
-        let f3 = Double::from_f64(1.0);
-        f1 = f1.mul_add(f2, f3).value;
-        assert!(!f1.is_negative() && f1.is_zero());
-    }
-
-    // Test for correct zero sign when answer is exactly zero and rounding towards
-    // negative.
-    // fma(1.0, -1.0, 1.0) -> +ve 0.
-    {
-        let mut f1 = Double::from_f64(1.0);
-        let f2 = Double::from_f64(-1.0);
-        let f3 = Double::from_f64(1.0);
-        f1 = f1.mul_add_r(f2, f3, Round::TowardNegative).value;
-        assert!(f1.is_negative() && f1.is_zero());
-    }
-
-    // Test for correct (in this case -ve) sign when adding like signed zeros.
-    // Test fma(0.0, -0.0, -0.0) -> -ve 0.
-    {
-        let mut f1 = Double::from_f64(0.0);
-        let f2 = Double::from_f64(-0.0);
-        let f3 = Double::from_f64(-0.0);
-        f1 = f1.mul_add(f2, f3).value;
-        assert!(f1.is_negative() && f1.is_zero());
-    }
-
-    // Test -ve sign preservation when small negative results underflow.
-    {
-        let mut f1 = "-0x1p-1074".parse::<Double>().unwrap();
-        let f2 = "+0x1p-1074".parse::<Double>().unwrap();
-        let f3 = Double::from_f64(0.0);
-        f1 = f1.mul_add(f2, f3).value;
-        assert!(f1.is_negative() && f1.is_zero());
-    }
-
-    // Test x87 extended precision case from https://llvm.org/PR20728.
-    {
-        let mut m1 = X87DoubleExtended::from_u128(1).value;
-        let m2 = X87DoubleExtended::from_u128(1).value;
-        let a = X87DoubleExtended::from_u128(3).value;
-
-        let mut loses_info = false;
-        m1 = m1.mul_add(m2, a).value;
-        let r: Single = m1.convert(&mut loses_info).value;
-        assert!(!loses_info);
-        assert_eq!(4.0, r.to_f32());
-    }
-}
-
-#[test]
-fn issue_69532() {
-    let f = Double::from_bits(0x7FF0_0000_0000_0001u64 as u128);
-    let mut loses_info = false;
-    let sta = f.convert(&mut loses_info);
-    let r: Single = sta.value;
-    assert!(loses_info);
-    assert!(r.is_nan());
-    assert_eq!(sta.status, Status::INVALID_OP);
-}
-
-#[test]
-fn min_num() {
-    let f1 = Double::from_f64(1.0);
-    let f2 = Double::from_f64(2.0);
-    let nan = Double::NAN;
-
-    assert_eq!(1.0, f1.min(f2).to_f64());
-    assert_eq!(1.0, f2.min(f1).to_f64());
-    assert_eq!(1.0, f1.min(nan).to_f64());
-    assert_eq!(1.0, nan.min(f1).to_f64());
-}
-
-#[test]
-fn max_num() {
-    let f1 = Double::from_f64(1.0);
-    let f2 = Double::from_f64(2.0);
-    let nan = Double::NAN;
-
-    assert_eq!(2.0, f1.max(f2).to_f64());
-    assert_eq!(2.0, f2.max(f1).to_f64());
-    assert_eq!(1.0, f1.max(nan).to_f64());
-    assert_eq!(1.0, nan.max(f1).to_f64());
-}
-
-#[test]
-fn denormal() {
-    // Test single precision
-    {
-        assert!(!Single::from_f32(0.0).is_denormal());
-
-        let mut t = "1.17549435082228750797e-38".parse::<Single>().unwrap();
-        assert!(!t.is_denormal());
-
-        let val2 = Single::from_f32(2.0e0);
-        t /= val2;
-        assert!(t.is_denormal());
-    }
-
-    // Test double precision
-    {
-        assert!(!Double::from_f64(0.0).is_denormal());
-
-        let mut t = "2.22507385850720138309e-308".parse::<Double>().unwrap();
-        assert!(!t.is_denormal());
-
-        let val2 = Double::from_f64(2.0e0);
-        t /= val2;
-        assert!(t.is_denormal());
-    }
-
-    // Test Intel double-ext
-    {
-        assert!(!X87DoubleExtended::from_u128(0).value.is_denormal());
-
-        let mut t = "3.36210314311209350626e-4932".parse::<X87DoubleExtended>().unwrap();
-        assert!(!t.is_denormal());
-
-        t /= X87DoubleExtended::from_u128(2).value;
-        assert!(t.is_denormal());
-    }
-
-    // Test quadruple precision
-    {
-        assert!(!Quad::from_u128(0).value.is_denormal());
-
-        let mut t = "3.36210314311209350626267781732175260e-4932".parse::<Quad>().unwrap();
-        assert!(!t.is_denormal());
-
-        t /= Quad::from_u128(2).value;
-        assert!(t.is_denormal());
-    }
-}
-
-#[test]
-fn decimal_strings_without_null_terminators() {
-    // Make sure that we can parse strings without null terminators.
-    // rdar://14323230.
-    let val = "0.00"[..3].parse::<Double>().unwrap();
-    assert_eq!(val.to_f64(), 0.0);
-    let val = "0.01"[..3].parse::<Double>().unwrap();
-    assert_eq!(val.to_f64(), 0.0);
-    let val = "0.09"[..3].parse::<Double>().unwrap();
-    assert_eq!(val.to_f64(), 0.0);
-    let val = "0.095"[..4].parse::<Double>().unwrap();
-    assert_eq!(val.to_f64(), 0.09);
-    let val = "0.00e+3"[..7].parse::<Double>().unwrap();
-    assert_eq!(val.to_f64(), 0.00);
-    let val = "0e+3"[..4].parse::<Double>().unwrap();
-    assert_eq!(val.to_f64(), 0.00);
-}
-
-#[test]
-fn from_zero_decimal_string() {
-    assert_eq!(0.0, "0".parse::<Double>().unwrap().to_f64());
-    assert_eq!(0.0, "+0".parse::<Double>().unwrap().to_f64());
-    assert_eq!(-0.0, "-0".parse::<Double>().unwrap().to_f64());
-
-    assert_eq!(0.0, "0.".parse::<Double>().unwrap().to_f64());
-    assert_eq!(0.0, "+0.".parse::<Double>().unwrap().to_f64());
-    assert_eq!(-0.0, "-0.".parse::<Double>().unwrap().to_f64());
-
-    assert_eq!(0.0, ".0".parse::<Double>().unwrap().to_f64());
-    assert_eq!(0.0, "+.0".parse::<Double>().unwrap().to_f64());
-    assert_eq!(-0.0, "-.0".parse::<Double>().unwrap().to_f64());
-
-    assert_eq!(0.0, "0.0".parse::<Double>().unwrap().to_f64());
-    assert_eq!(0.0, "+0.0".parse::<Double>().unwrap().to_f64());
-    assert_eq!(-0.0, "-0.0".parse::<Double>().unwrap().to_f64());
-
-    assert_eq!(0.0, "00000.".parse::<Double>().unwrap().to_f64());
-    assert_eq!(0.0, "+00000.".parse::<Double>().unwrap().to_f64());
-    assert_eq!(-0.0, "-00000.".parse::<Double>().unwrap().to_f64());
-
-    assert_eq!(0.0, ".00000".parse::<Double>().unwrap().to_f64());
-    assert_eq!(0.0, "+.00000".parse::<Double>().unwrap().to_f64());
-    assert_eq!(-0.0, "-.00000".parse::<Double>().unwrap().to_f64());
-
-    assert_eq!(0.0, "0000.00000".parse::<Double>().unwrap().to_f64());
-    assert_eq!(0.0, "+0000.00000".parse::<Double>().unwrap().to_f64());
-    assert_eq!(-0.0, "-0000.00000".parse::<Double>().unwrap().to_f64());
-}
-
-#[test]
-fn from_zero_decimal_single_exponent_string() {
-    assert_eq!(0.0, "0e1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(0.0, "+0e1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(-0.0, "-0e1".parse::<Double>().unwrap().to_f64());
-
-    assert_eq!(0.0, "0e+1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(0.0, "+0e+1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(-0.0, "-0e+1".parse::<Double>().unwrap().to_f64());
-
-    assert_eq!(0.0, "0e-1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(0.0, "+0e-1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(-0.0, "-0e-1".parse::<Double>().unwrap().to_f64());
-
-    assert_eq!(0.0, "0.e1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(0.0, "+0.e1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(-0.0, "-0.e1".parse::<Double>().unwrap().to_f64());
-
-    assert_eq!(0.0, "0.e+1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(0.0, "+0.e+1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(-0.0, "-0.e+1".parse::<Double>().unwrap().to_f64());
-
-    assert_eq!(0.0, "0.e-1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(0.0, "+0.e-1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(-0.0, "-0.e-1".parse::<Double>().unwrap().to_f64());
-
-    assert_eq!(0.0, ".0e1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(0.0, "+.0e1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(-0.0, "-.0e1".parse::<Double>().unwrap().to_f64());
-
-    assert_eq!(0.0, ".0e+1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(0.0, "+.0e+1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(-0.0, "-.0e+1".parse::<Double>().unwrap().to_f64());
-
-    assert_eq!(0.0, ".0e-1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(0.0, "+.0e-1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(-0.0, "-.0e-1".parse::<Double>().unwrap().to_f64());
-
-    assert_eq!(0.0, "0.0e1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(0.0, "+0.0e1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(-0.0, "-0.0e1".parse::<Double>().unwrap().to_f64());
-
-    assert_eq!(0.0, "0.0e+1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(0.0, "+0.0e+1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(-0.0, "-0.0e+1".parse::<Double>().unwrap().to_f64());
-
-    assert_eq!(0.0, "0.0e-1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(0.0, "+0.0e-1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(-0.0, "-0.0e-1".parse::<Double>().unwrap().to_f64());
-
-    assert_eq!(0.0, "000.0000e1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(0.0, "+000.0000e+1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(-0.0, "-000.0000e+1".parse::<Double>().unwrap().to_f64());
-}
-
-#[test]
-fn from_zero_decimal_large_exponent_string() {
-    assert_eq!(0.0, "0e1234".parse::<Double>().unwrap().to_f64());
-    assert_eq!(0.0, "+0e1234".parse::<Double>().unwrap().to_f64());
-    assert_eq!(-0.0, "-0e1234".parse::<Double>().unwrap().to_f64());
-
-    assert_eq!(0.0, "0e+1234".parse::<Double>().unwrap().to_f64());
-    assert_eq!(0.0, "+0e+1234".parse::<Double>().unwrap().to_f64());
-    assert_eq!(-0.0, "-0e+1234".parse::<Double>().unwrap().to_f64());
-
-    assert_eq!(0.0, "0e-1234".parse::<Double>().unwrap().to_f64());
-    assert_eq!(0.0, "+0e-1234".parse::<Double>().unwrap().to_f64());
-    assert_eq!(-0.0, "-0e-1234".parse::<Double>().unwrap().to_f64());
-
-    assert_eq!(0.0, "000.0000e1234".parse::<Double>().unwrap().to_f64());
-    assert_eq!(0.0, "000.0000e-1234".parse::<Double>().unwrap().to_f64());
-}
-
-#[test]
-fn from_zero_hexadecimal_string() {
-    assert_eq!(0.0, "0x0p1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(0.0, "+0x0p1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(-0.0, "-0x0p1".parse::<Double>().unwrap().to_f64());
-
-    assert_eq!(0.0, "0x0p+1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(0.0, "+0x0p+1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(-0.0, "-0x0p+1".parse::<Double>().unwrap().to_f64());
-
-    assert_eq!(0.0, "0x0p-1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(0.0, "+0x0p-1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(-0.0, "-0x0p-1".parse::<Double>().unwrap().to_f64());
-
-    assert_eq!(0.0, "0x0.p1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(0.0, "+0x0.p1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(-0.0, "-0x0.p1".parse::<Double>().unwrap().to_f64());
-
-    assert_eq!(0.0, "0x0.p+1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(0.0, "+0x0.p+1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(-0.0, "-0x0.p+1".parse::<Double>().unwrap().to_f64());
-
-    assert_eq!(0.0, "0x0.p-1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(0.0, "+0x0.p-1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(-0.0, "-0x0.p-1".parse::<Double>().unwrap().to_f64());
-
-    assert_eq!(0.0, "0x.0p1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(0.0, "+0x.0p1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(-0.0, "-0x.0p1".parse::<Double>().unwrap().to_f64());
-
-    assert_eq!(0.0, "0x.0p+1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(0.0, "+0x.0p+1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(-0.0, "-0x.0p+1".parse::<Double>().unwrap().to_f64());
-
-    assert_eq!(0.0, "0x.0p-1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(0.0, "+0x.0p-1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(-0.0, "-0x.0p-1".parse::<Double>().unwrap().to_f64());
-
-    assert_eq!(0.0, "0x0.0p1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(0.0, "+0x0.0p1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(-0.0, "-0x0.0p1".parse::<Double>().unwrap().to_f64());
-
-    assert_eq!(0.0, "0x0.0p+1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(0.0, "+0x0.0p+1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(-0.0, "-0x0.0p+1".parse::<Double>().unwrap().to_f64());
-
-    assert_eq!(0.0, "0x0.0p-1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(0.0, "+0x0.0p-1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(-0.0, "-0x0.0p-1".parse::<Double>().unwrap().to_f64());
-
-    assert_eq!(0.0, "0x00000.p1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(0.0, "0x0000.00000p1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(0.0, "0x.00000p1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(0.0, "0x0.p1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(0.0, "0x0p1234".parse::<Double>().unwrap().to_f64());
-    assert_eq!(-0.0, "-0x0p1234".parse::<Double>().unwrap().to_f64());
-    assert_eq!(0.0, "0x00000.p1234".parse::<Double>().unwrap().to_f64());
-    assert_eq!(0.0, "0x0000.00000p1234".parse::<Double>().unwrap().to_f64());
-    assert_eq!(0.0, "0x.00000p1234".parse::<Double>().unwrap().to_f64());
-    assert_eq!(0.0, "0x0.p1234".parse::<Double>().unwrap().to_f64());
-}
-
-#[test]
-fn from_decimal_string() {
-    assert_eq!(1.0, "1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(2.0, "2.".parse::<Double>().unwrap().to_f64());
-    assert_eq!(0.5, ".5".parse::<Double>().unwrap().to_f64());
-    assert_eq!(1.0, "1.0".parse::<Double>().unwrap().to_f64());
-    assert_eq!(-2.0, "-2".parse::<Double>().unwrap().to_f64());
-    assert_eq!(-4.0, "-4.".parse::<Double>().unwrap().to_f64());
-    assert_eq!(-0.5, "-.5".parse::<Double>().unwrap().to_f64());
-    assert_eq!(-1.5, "-1.5".parse::<Double>().unwrap().to_f64());
-    assert_eq!(1.25e12, "1.25e12".parse::<Double>().unwrap().to_f64());
-    assert_eq!(1.25e+12, "1.25e+12".parse::<Double>().unwrap().to_f64());
-    assert_eq!(1.25e-12, "1.25e-12".parse::<Double>().unwrap().to_f64());
-    assert_eq!(1024.0, "1024.".parse::<Double>().unwrap().to_f64());
-    assert_eq!(1024.05, "1024.05000".parse::<Double>().unwrap().to_f64());
-    assert_eq!(0.05, ".05000".parse::<Double>().unwrap().to_f64());
-    assert_eq!(2.0, "2.".parse::<Double>().unwrap().to_f64());
-    assert_eq!(2.0e2, "2.e2".parse::<Double>().unwrap().to_f64());
-    assert_eq!(2.0e+2, "2.e+2".parse::<Double>().unwrap().to_f64());
-    assert_eq!(2.0e-2, "2.e-2".parse::<Double>().unwrap().to_f64());
-    assert_eq!(2.05e2, "002.05000e2".parse::<Double>().unwrap().to_f64());
-    assert_eq!(2.05e+2, "002.05000e+2".parse::<Double>().unwrap().to_f64());
-    assert_eq!(2.05e-2, "002.05000e-2".parse::<Double>().unwrap().to_f64());
-    assert_eq!(2.05e12, "002.05000e12".parse::<Double>().unwrap().to_f64());
-    assert_eq!(2.05e+12, "002.05000e+12".parse::<Double>().unwrap().to_f64());
-    assert_eq!(2.05e-12, "002.05000e-12".parse::<Double>().unwrap().to_f64());
-
-    // These are "carefully selected" to overflow the fast log-base
-    // calculations in the implementation.
-    assert!("99e99999".parse::<Double>().unwrap().is_infinite());
-    assert!("-99e99999".parse::<Double>().unwrap().is_infinite());
-    assert!("1e-99999".parse::<Double>().unwrap().is_pos_zero());
-    assert!("-1e-99999".parse::<Double>().unwrap().is_neg_zero());
-
-    assert_eq!(2.71828, "2.71828".parse::<Double>().unwrap().to_f64());
-}
-
-#[test]
-fn from_hexadecimal_string() {
-    assert_eq!(1.0, "0x1p0".parse::<Double>().unwrap().to_f64());
-    assert_eq!(1.0, "+0x1p0".parse::<Double>().unwrap().to_f64());
-    assert_eq!(-1.0, "-0x1p0".parse::<Double>().unwrap().to_f64());
-
-    assert_eq!(1.0, "0x1p+0".parse::<Double>().unwrap().to_f64());
-    assert_eq!(1.0, "+0x1p+0".parse::<Double>().unwrap().to_f64());
-    assert_eq!(-1.0, "-0x1p+0".parse::<Double>().unwrap().to_f64());
-
-    assert_eq!(1.0, "0x1p-0".parse::<Double>().unwrap().to_f64());
-    assert_eq!(1.0, "+0x1p-0".parse::<Double>().unwrap().to_f64());
-    assert_eq!(-1.0, "-0x1p-0".parse::<Double>().unwrap().to_f64());
-
-    assert_eq!(2.0, "0x1p1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(2.0, "+0x1p1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(-2.0, "-0x1p1".parse::<Double>().unwrap().to_f64());
-
-    assert_eq!(2.0, "0x1p+1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(2.0, "+0x1p+1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(-2.0, "-0x1p+1".parse::<Double>().unwrap().to_f64());
-
-    assert_eq!(0.5, "0x1p-1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(0.5, "+0x1p-1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(-0.5, "-0x1p-1".parse::<Double>().unwrap().to_f64());
-
-    assert_eq!(3.0, "0x1.8p1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(3.0, "+0x1.8p1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(-3.0, "-0x1.8p1".parse::<Double>().unwrap().to_f64());
-
-    assert_eq!(3.0, "0x1.8p+1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(3.0, "+0x1.8p+1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(-3.0, "-0x1.8p+1".parse::<Double>().unwrap().to_f64());
-
-    assert_eq!(0.75, "0x1.8p-1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(0.75, "+0x1.8p-1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(-0.75, "-0x1.8p-1".parse::<Double>().unwrap().to_f64());
-
-    assert_eq!(8192.0, "0x1000.000p1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(8192.0, "+0x1000.000p1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(-8192.0, "-0x1000.000p1".parse::<Double>().unwrap().to_f64());
-
-    assert_eq!(8192.0, "0x1000.000p+1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(8192.0, "+0x1000.000p+1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(-8192.0, "-0x1000.000p+1".parse::<Double>().unwrap().to_f64());
-
-    assert_eq!(2048.0, "0x1000.000p-1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(2048.0, "+0x1000.000p-1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(-2048.0, "-0x1000.000p-1".parse::<Double>().unwrap().to_f64());
-
-    assert_eq!(8192.0, "0x1000p1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(8192.0, "+0x1000p1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(-8192.0, "-0x1000p1".parse::<Double>().unwrap().to_f64());
-
-    assert_eq!(8192.0, "0x1000p+1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(8192.0, "+0x1000p+1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(-8192.0, "-0x1000p+1".parse::<Double>().unwrap().to_f64());
-
-    assert_eq!(2048.0, "0x1000p-1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(2048.0, "+0x1000p-1".parse::<Double>().unwrap().to_f64());
-    assert_eq!(-2048.0, "-0x1000p-1".parse::<Double>().unwrap().to_f64());
-
-    assert_eq!(16384.0, "0x10p10".parse::<Double>().unwrap().to_f64());
-    assert_eq!(16384.0, "+0x10p10".parse::<Double>().unwrap().to_f64());
-    assert_eq!(-16384.0, "-0x10p10".parse::<Double>().unwrap().to_f64());
-
-    assert_eq!(16384.0, "0x10p+10".parse::<Double>().unwrap().to_f64());
-    assert_eq!(16384.0, "+0x10p+10".parse::<Double>().unwrap().to_f64());
-    assert_eq!(-16384.0, "-0x10p+10".parse::<Double>().unwrap().to_f64());
-
-    assert_eq!(0.015625, "0x10p-10".parse::<Double>().unwrap().to_f64());
-    assert_eq!(0.015625, "+0x10p-10".parse::<Double>().unwrap().to_f64());
-    assert_eq!(-0.015625, "-0x10p-10".parse::<Double>().unwrap().to_f64());
-
-    assert_eq!(1.0625, "0x1.1p0".parse::<Double>().unwrap().to_f64());
-    assert_eq!(1.0, "0x1p0".parse::<Double>().unwrap().to_f64());
-
-    assert_eq!(
-        "0x1p-150".parse::<Double>().unwrap().to_f64(),
-        "+0x800000000000000001.p-221".parse::<Double>().unwrap().to_f64()
-    );
-    assert_eq!(
-        2251799813685248.5,
-        "0x80000000000004000000.010p-28".parse::<Double>().unwrap().to_f64()
-    );
-}
-
-#[test]
-fn to_string() {
-    let to_string = |d: f64, precision: usize, width: usize| {
-        let x = Double::from_f64(d);
-        if precision == 0 {
-            format!("{:1$}", x, width)
-        } else {
-            format!("{:2$.1$}", x, precision, width)
-        }
-    };
-    assert_eq!("10", to_string(10.0, 6, 3));
-    assert_eq!("1.0E+1", to_string(10.0, 6, 0));
-    assert_eq!("10100", to_string(1.01E+4, 5, 2));
-    assert_eq!("1.01E+4", to_string(1.01E+4, 4, 2));
-    assert_eq!("1.01E+4", to_string(1.01E+4, 5, 1));
-    assert_eq!("0.0101", to_string(1.01E-2, 5, 2));
-    assert_eq!("0.0101", to_string(1.01E-2, 4, 2));
-    assert_eq!("1.01E-2", to_string(1.01E-2, 5, 1));
-    assert_eq!("0.78539816339744828", to_string(0.78539816339744830961, 0, 3));
-    assert_eq!("4.9406564584124654E-324", to_string(4.9406564584124654e-324, 0, 3));
-    assert_eq!("873.18340000000001", to_string(873.1834, 0, 1));
-    assert_eq!("8.7318340000000001E+2", to_string(873.1834, 0, 0));
-    assert_eq!("1.7976931348623157E+308", to_string(1.7976931348623157E+308, 0, 0));
-
-    let to_string = |d: f64, precision: usize, width: usize| {
-        let x = Double::from_f64(d);
-        if precision == 0 {
-            format!("{:#1$}", x, width)
-        } else {
-            format!("{:#2$.1$}", x, precision, width)
-        }
-    };
-    assert_eq!("10", to_string(10.0, 6, 3));
-    assert_eq!("1.000000e+01", to_string(10.0, 6, 0));
-    assert_eq!("10100", to_string(1.01E+4, 5, 2));
-    assert_eq!("1.0100e+04", to_string(1.01E+4, 4, 2));
-    assert_eq!("1.01000e+04", to_string(1.01E+4, 5, 1));
-    assert_eq!("0.0101", to_string(1.01E-2, 5, 2));
-    assert_eq!("0.0101", to_string(1.01E-2, 4, 2));
-    assert_eq!("1.01000e-02", to_string(1.01E-2, 5, 1));
-    assert_eq!("0.78539816339744828", to_string(0.78539816339744830961, 0, 3));
-    assert_eq!("4.94065645841246540e-324", to_string(4.9406564584124654e-324, 0, 3));
-    assert_eq!("873.18340000000001", to_string(873.1834, 0, 1));
-    assert_eq!("8.73183400000000010e+02", to_string(873.1834, 0, 0));
-    assert_eq!("1.79769313486231570e+308", to_string(1.7976931348623157E+308, 0, 0));
-}
-
-#[test]
-fn to_integer() {
-    let mut is_exact = false;
-
-    assert_eq!(
-        Status::OK.and(10),
-        "10".parse::<Double>().unwrap().to_u128_r(5, Round::TowardZero, &mut is_exact,)
-    );
-    assert!(is_exact);
-
-    assert_eq!(
-        Status::INVALID_OP.and(0),
-        "-10".parse::<Double>().unwrap().to_u128_r(5, Round::TowardZero, &mut is_exact,)
-    );
-    assert!(!is_exact);
-
-    assert_eq!(
-        Status::INVALID_OP.and(31),
-        "32".parse::<Double>().unwrap().to_u128_r(5, Round::TowardZero, &mut is_exact,)
-    );
-    assert!(!is_exact);
-
-    assert_eq!(
-        Status::INEXACT.and(7),
-        "7.9".parse::<Double>().unwrap().to_u128_r(5, Round::TowardZero, &mut is_exact,)
-    );
-    assert!(!is_exact);
-
-    assert_eq!(
-        Status::OK.and(-10),
-        "-10".parse::<Double>().unwrap().to_i128_r(5, Round::TowardZero, &mut is_exact,)
-    );
-    assert!(is_exact);
-
-    assert_eq!(
-        Status::INVALID_OP.and(-16),
-        "-17".parse::<Double>().unwrap().to_i128_r(5, Round::TowardZero, &mut is_exact,)
-    );
-    assert!(!is_exact);
-
-    assert_eq!(
-        Status::INVALID_OP.and(15),
-        "16".parse::<Double>().unwrap().to_i128_r(5, Round::TowardZero, &mut is_exact,)
-    );
-    assert!(!is_exact);
-}
-
-#[test]
-fn nan() {
-    fn nanbits<T: Float>(signaling: bool, negative: bool, fill: u128) -> u128 {
-        let x = if signaling { T::snan(Some(fill)) } else { T::qnan(Some(fill)) };
-        if negative { (-x).to_bits() } else { x.to_bits() }
-    }
-
-    assert_eq!(0x7fc00000, nanbits::<Single>(false, false, 0));
-    assert_eq!(0xffc00000, nanbits::<Single>(false, true, 0));
-    assert_eq!(0x7fc0ae72, nanbits::<Single>(false, false, 0xae72));
-    assert_eq!(0x7fffae72, nanbits::<Single>(false, false, 0xffffae72));
-    assert_eq!(0x7fa00000, nanbits::<Single>(true, false, 0));
-    assert_eq!(0xffa00000, nanbits::<Single>(true, true, 0));
-    assert_eq!(0x7f80ae72, nanbits::<Single>(true, false, 0xae72));
-    assert_eq!(0x7fbfae72, nanbits::<Single>(true, false, 0xffffae72));
-
-    assert_eq!(0x7ff8000000000000, nanbits::<Double>(false, false, 0));
-    assert_eq!(0xfff8000000000000, nanbits::<Double>(false, true, 0));
-    assert_eq!(0x7ff800000000ae72, nanbits::<Double>(false, false, 0xae72));
-    assert_eq!(0x7fffffffffffae72, nanbits::<Double>(false, false, 0xffffffffffffae72));
-    assert_eq!(0x7ff4000000000000, nanbits::<Double>(true, false, 0));
-    assert_eq!(0xfff4000000000000, nanbits::<Double>(true, true, 0));
-    assert_eq!(0x7ff000000000ae72, nanbits::<Double>(true, false, 0xae72));
-    assert_eq!(0x7ff7ffffffffae72, nanbits::<Double>(true, false, 0xffffffffffffae72));
-}
-
-#[test]
-fn string_decimal_death() {
-    assert_eq!("".parse::<Double>(), Err(ParseError("Invalid string length")));
-    assert_eq!("+".parse::<Double>(), Err(ParseError("String has no digits")));
-    assert_eq!("-".parse::<Double>(), Err(ParseError("String has no digits")));
-
-    assert_eq!("\0".parse::<Double>(), Err(ParseError("Invalid character in significand")));
-    assert_eq!("1\0".parse::<Double>(), Err(ParseError("Invalid character in significand")));
-    assert_eq!("1\02".parse::<Double>(), Err(ParseError("Invalid character in significand")));
-    assert_eq!("1\02e1".parse::<Double>(), Err(ParseError("Invalid character in significand")));
-    assert_eq!("1e\0".parse::<Double>(), Err(ParseError("Invalid character in exponent")));
-    assert_eq!("1e1\0".parse::<Double>(), Err(ParseError("Invalid character in exponent")));
-    assert_eq!("1e1\02".parse::<Double>(), Err(ParseError("Invalid character in exponent")));
-
-    assert_eq!("1.0f".parse::<Double>(), Err(ParseError("Invalid character in significand")));
-
-    assert_eq!("..".parse::<Double>(), Err(ParseError("String contains multiple dots")));
-    assert_eq!("..0".parse::<Double>(), Err(ParseError("String contains multiple dots")));
-    assert_eq!("1.0.0".parse::<Double>(), Err(ParseError("String contains multiple dots")));
-}
-
-#[test]
-fn string_decimal_significand_death() {
-    assert_eq!(".".parse::<Double>(), Err(ParseError("Significand has no digits")));
-    assert_eq!("+.".parse::<Double>(), Err(ParseError("Significand has no digits")));
-    assert_eq!("-.".parse::<Double>(), Err(ParseError("Significand has no digits")));
-
-    assert_eq!("e".parse::<Double>(), Err(ParseError("Significand has no digits")));
-    assert_eq!("+e".parse::<Double>(), Err(ParseError("Significand has no digits")));
-    assert_eq!("-e".parse::<Double>(), Err(ParseError("Significand has no digits")));
-
-    assert_eq!("e1".parse::<Double>(), Err(ParseError("Significand has no digits")));
-    assert_eq!("+e1".parse::<Double>(), Err(ParseError("Significand has no digits")));
-    assert_eq!("-e1".parse::<Double>(), Err(ParseError("Significand has no digits")));
-
-    assert_eq!(".e1".parse::<Double>(), Err(ParseError("Significand has no digits")));
-    assert_eq!("+.e1".parse::<Double>(), Err(ParseError("Significand has no digits")));
-    assert_eq!("-.e1".parse::<Double>(), Err(ParseError("Significand has no digits")));
-
-    assert_eq!(".e".parse::<Double>(), Err(ParseError("Significand has no digits")));
-    assert_eq!("+.e".parse::<Double>(), Err(ParseError("Significand has no digits")));
-    assert_eq!("-.e".parse::<Double>(), Err(ParseError("Significand has no digits")));
-}
-
-#[test]
-fn string_decimal_exponent_death() {
-    assert_eq!("1e".parse::<Double>(), Err(ParseError("Exponent has no digits")));
-    assert_eq!("+1e".parse::<Double>(), Err(ParseError("Exponent has no digits")));
-    assert_eq!("-1e".parse::<Double>(), Err(ParseError("Exponent has no digits")));
-
-    assert_eq!("1.e".parse::<Double>(), Err(ParseError("Exponent has no digits")));
-    assert_eq!("+1.e".parse::<Double>(), Err(ParseError("Exponent has no digits")));
-    assert_eq!("-1.e".parse::<Double>(), Err(ParseError("Exponent has no digits")));
-
-    assert_eq!(".1e".parse::<Double>(), Err(ParseError("Exponent has no digits")));
-    assert_eq!("+.1e".parse::<Double>(), Err(ParseError("Exponent has no digits")));
-    assert_eq!("-.1e".parse::<Double>(), Err(ParseError("Exponent has no digits")));
-
-    assert_eq!("1.1e".parse::<Double>(), Err(ParseError("Exponent has no digits")));
-    assert_eq!("+1.1e".parse::<Double>(), Err(ParseError("Exponent has no digits")));
-    assert_eq!("-1.1e".parse::<Double>(), Err(ParseError("Exponent has no digits")));
-
-    assert_eq!("1e+".parse::<Double>(), Err(ParseError("Exponent has no digits")));
-    assert_eq!("1e-".parse::<Double>(), Err(ParseError("Exponent has no digits")));
-
-    assert_eq!(".1e".parse::<Double>(), Err(ParseError("Exponent has no digits")));
-    assert_eq!(".1e+".parse::<Double>(), Err(ParseError("Exponent has no digits")));
-    assert_eq!(".1e-".parse::<Double>(), Err(ParseError("Exponent has no digits")));
-
-    assert_eq!("1.0e".parse::<Double>(), Err(ParseError("Exponent has no digits")));
-    assert_eq!("1.0e+".parse::<Double>(), Err(ParseError("Exponent has no digits")));
-    assert_eq!("1.0e-".parse::<Double>(), Err(ParseError("Exponent has no digits")));
-}
-
-#[test]
-fn string_hexadecimal_death() {
-    assert_eq!("0x".parse::<Double>(), Err(ParseError("Invalid string")));
-    assert_eq!("+0x".parse::<Double>(), Err(ParseError("Invalid string")));
-    assert_eq!("-0x".parse::<Double>(), Err(ParseError("Invalid string")));
-
-    assert_eq!("0x0".parse::<Double>(), Err(ParseError("Hex strings require an exponent")));
-    assert_eq!("+0x0".parse::<Double>(), Err(ParseError("Hex strings require an exponent")));
-    assert_eq!("-0x0".parse::<Double>(), Err(ParseError("Hex strings require an exponent")));
-
-    assert_eq!("0x0.".parse::<Double>(), Err(ParseError("Hex strings require an exponent")));
-    assert_eq!("+0x0.".parse::<Double>(), Err(ParseError("Hex strings require an exponent")));
-    assert_eq!("-0x0.".parse::<Double>(), Err(ParseError("Hex strings require an exponent")));
-
-    assert_eq!("0x.0".parse::<Double>(), Err(ParseError("Hex strings require an exponent")));
-    assert_eq!("+0x.0".parse::<Double>(), Err(ParseError("Hex strings require an exponent")));
-    assert_eq!("-0x.0".parse::<Double>(), Err(ParseError("Hex strings require an exponent")));
-
-    assert_eq!("0x0.0".parse::<Double>(), Err(ParseError("Hex strings require an exponent")));
-    assert_eq!("+0x0.0".parse::<Double>(), Err(ParseError("Hex strings require an exponent")));
-    assert_eq!("-0x0.0".parse::<Double>(), Err(ParseError("Hex strings require an exponent")));
-
-    assert_eq!("0x\0".parse::<Double>(), Err(ParseError("Invalid character in significand")));
-    assert_eq!("0x1\0".parse::<Double>(), Err(ParseError("Invalid character in significand")));
-    assert_eq!("0x1\02".parse::<Double>(), Err(ParseError("Invalid character in significand")));
-    assert_eq!("0x1\02p1".parse::<Double>(), Err(ParseError("Invalid character in significand")));
-    assert_eq!("0x1p\0".parse::<Double>(), Err(ParseError("Invalid character in exponent")));
-    assert_eq!("0x1p1\0".parse::<Double>(), Err(ParseError("Invalid character in exponent")));
-    assert_eq!("0x1p1\02".parse::<Double>(), Err(ParseError("Invalid character in exponent")));
-
-    assert_eq!("0x1p0f".parse::<Double>(), Err(ParseError("Invalid character in exponent")));
-
-    assert_eq!("0x..p1".parse::<Double>(), Err(ParseError("String contains multiple dots")));
-    assert_eq!("0x..0p1".parse::<Double>(), Err(ParseError("String contains multiple dots")));
-    assert_eq!("0x1.0.0p1".parse::<Double>(), Err(ParseError("String contains multiple dots")));
-}
-
-#[test]
-fn string_hexadecimal_significand_death() {
-    assert_eq!("0x.".parse::<Double>(), Err(ParseError("Significand has no digits")));
-    assert_eq!("+0x.".parse::<Double>(), Err(ParseError("Significand has no digits")));
-    assert_eq!("-0x.".parse::<Double>(), Err(ParseError("Significand has no digits")));
-
-    assert_eq!("0xp".parse::<Double>(), Err(ParseError("Significand has no digits")));
-    assert_eq!("+0xp".parse::<Double>(), Err(ParseError("Significand has no digits")));
-    assert_eq!("-0xp".parse::<Double>(), Err(ParseError("Significand has no digits")));
-
-    assert_eq!("0xp+".parse::<Double>(), Err(ParseError("Significand has no digits")));
-    assert_eq!("+0xp+".parse::<Double>(), Err(ParseError("Significand has no digits")));
-    assert_eq!("-0xp+".parse::<Double>(), Err(ParseError("Significand has no digits")));
-
-    assert_eq!("0xp-".parse::<Double>(), Err(ParseError("Significand has no digits")));
-    assert_eq!("+0xp-".parse::<Double>(), Err(ParseError("Significand has no digits")));
-    assert_eq!("-0xp-".parse::<Double>(), Err(ParseError("Significand has no digits")));
-
-    assert_eq!("0x.p".parse::<Double>(), Err(ParseError("Significand has no digits")));
-    assert_eq!("+0x.p".parse::<Double>(), Err(ParseError("Significand has no digits")));
-    assert_eq!("-0x.p".parse::<Double>(), Err(ParseError("Significand has no digits")));
-
-    assert_eq!("0x.p+".parse::<Double>(), Err(ParseError("Significand has no digits")));
-    assert_eq!("+0x.p+".parse::<Double>(), Err(ParseError("Significand has no digits")));
-    assert_eq!("-0x.p+".parse::<Double>(), Err(ParseError("Significand has no digits")));
-
-    assert_eq!("0x.p-".parse::<Double>(), Err(ParseError("Significand has no digits")));
-    assert_eq!("+0x.p-".parse::<Double>(), Err(ParseError("Significand has no digits")));
-    assert_eq!("-0x.p-".parse::<Double>(), Err(ParseError("Significand has no digits")));
-}
-
-#[test]
-fn string_hexadecimal_exponent_death() {
-    assert_eq!("0x1p".parse::<Double>(), Err(ParseError("Exponent has no digits")));
-    assert_eq!("+0x1p".parse::<Double>(), Err(ParseError("Exponent has no digits")));
-    assert_eq!("-0x1p".parse::<Double>(), Err(ParseError("Exponent has no digits")));
-
-    assert_eq!("0x1p+".parse::<Double>(), Err(ParseError("Exponent has no digits")));
-    assert_eq!("+0x1p+".parse::<Double>(), Err(ParseError("Exponent has no digits")));
-    assert_eq!("-0x1p+".parse::<Double>(), Err(ParseError("Exponent has no digits")));
-
-    assert_eq!("0x1p-".parse::<Double>(), Err(ParseError("Exponent has no digits")));
-    assert_eq!("+0x1p-".parse::<Double>(), Err(ParseError("Exponent has no digits")));
-    assert_eq!("-0x1p-".parse::<Double>(), Err(ParseError("Exponent has no digits")));
-
-    assert_eq!("0x1.p".parse::<Double>(), Err(ParseError("Exponent has no digits")));
-    assert_eq!("+0x1.p".parse::<Double>(), Err(ParseError("Exponent has no digits")));
-    assert_eq!("-0x1.p".parse::<Double>(), Err(ParseError("Exponent has no digits")));
-
-    assert_eq!("0x1.p+".parse::<Double>(), Err(ParseError("Exponent has no digits")));
-    assert_eq!("+0x1.p+".parse::<Double>(), Err(ParseError("Exponent has no digits")));
-    assert_eq!("-0x1.p+".parse::<Double>(), Err(ParseError("Exponent has no digits")));
-
-    assert_eq!("0x1.p-".parse::<Double>(), Err(ParseError("Exponent has no digits")));
-    assert_eq!("+0x1.p-".parse::<Double>(), Err(ParseError("Exponent has no digits")));
-    assert_eq!("-0x1.p-".parse::<Double>(), Err(ParseError("Exponent has no digits")));
-
-    assert_eq!("0x.1p".parse::<Double>(), Err(ParseError("Exponent has no digits")));
-    assert_eq!("+0x.1p".parse::<Double>(), Err(ParseError("Exponent has no digits")));
-    assert_eq!("-0x.1p".parse::<Double>(), Err(ParseError("Exponent has no digits")));
-
-    assert_eq!("0x.1p+".parse::<Double>(), Err(ParseError("Exponent has no digits")));
-    assert_eq!("+0x.1p+".parse::<Double>(), Err(ParseError("Exponent has no digits")));
-    assert_eq!("-0x.1p+".parse::<Double>(), Err(ParseError("Exponent has no digits")));
-
-    assert_eq!("0x.1p-".parse::<Double>(), Err(ParseError("Exponent has no digits")));
-    assert_eq!("+0x.1p-".parse::<Double>(), Err(ParseError("Exponent has no digits")));
-    assert_eq!("-0x.1p-".parse::<Double>(), Err(ParseError("Exponent has no digits")));
-
-    assert_eq!("0x1.1p".parse::<Double>(), Err(ParseError("Exponent has no digits")));
-    assert_eq!("+0x1.1p".parse::<Double>(), Err(ParseError("Exponent has no digits")));
-    assert_eq!("-0x1.1p".parse::<Double>(), Err(ParseError("Exponent has no digits")));
-
-    assert_eq!("0x1.1p+".parse::<Double>(), Err(ParseError("Exponent has no digits")));
-    assert_eq!("+0x1.1p+".parse::<Double>(), Err(ParseError("Exponent has no digits")));
-    assert_eq!("-0x1.1p+".parse::<Double>(), Err(ParseError("Exponent has no digits")));
-
-    assert_eq!("0x1.1p-".parse::<Double>(), Err(ParseError("Exponent has no digits")));
-    assert_eq!("+0x1.1p-".parse::<Double>(), Err(ParseError("Exponent has no digits")));
-    assert_eq!("-0x1.1p-".parse::<Double>(), Err(ParseError("Exponent has no digits")));
-}
-
-#[test]
-fn exact_inverse() {
-    // Trivial operation.
-    assert!(Double::from_f64(2.0).get_exact_inverse().unwrap().bitwise_eq(Double::from_f64(0.5)));
-    assert!(Single::from_f32(2.0).get_exact_inverse().unwrap().bitwise_eq(Single::from_f32(0.5)));
-    assert!(
-        "2.0"
-            .parse::<Quad>()
-            .unwrap()
-            .get_exact_inverse()
-            .unwrap()
-            .bitwise_eq("0.5".parse::<Quad>().unwrap())
-    );
-    assert!(
-        "2.0"
-            .parse::<X87DoubleExtended>()
-            .unwrap()
-            .get_exact_inverse()
-            .unwrap()
-            .bitwise_eq("0.5".parse::<X87DoubleExtended>().unwrap())
-    );
-
-    // FLT_MIN
-    assert!(
-        Single::from_f32(1.17549435e-38)
-            .get_exact_inverse()
-            .unwrap()
-            .bitwise_eq(Single::from_f32(8.5070592e+37))
-    );
-
-    // Large float, inverse is a denormal.
-    assert!(Single::from_f32(1.7014118e38).get_exact_inverse().is_none());
-    // Zero
-    assert!(Double::from_f64(0.0).get_exact_inverse().is_none());
-    // Denormalized float
-    assert!(Single::from_f32(1.40129846e-45).get_exact_inverse().is_none());
-}
-
-#[test]
-fn round_to_integral() {
-    let t = Double::from_f64(-0.5);
-    assert_eq!(-0.0, t.round_to_integral(Round::TowardZero).value.to_f64());
-    assert_eq!(-1.0, t.round_to_integral(Round::TowardNegative).value.to_f64());
-    assert_eq!(-0.0, t.round_to_integral(Round::TowardPositive).value.to_f64());
-    assert_eq!(-0.0, t.round_to_integral(Round::NearestTiesToEven).value.to_f64());
-
-    let s = Double::from_f64(3.14);
-    assert_eq!(3.0, s.round_to_integral(Round::TowardZero).value.to_f64());
-    assert_eq!(3.0, s.round_to_integral(Round::TowardNegative).value.to_f64());
-    assert_eq!(4.0, s.round_to_integral(Round::TowardPositive).value.to_f64());
-    assert_eq!(3.0, s.round_to_integral(Round::NearestTiesToEven).value.to_f64());
-
-    let r = Double::largest();
-    assert_eq!(r.to_f64(), r.round_to_integral(Round::TowardZero).value.to_f64());
-    assert_eq!(r.to_f64(), r.round_to_integral(Round::TowardNegative).value.to_f64());
-    assert_eq!(r.to_f64(), r.round_to_integral(Round::TowardPositive).value.to_f64());
-    assert_eq!(r.to_f64(), r.round_to_integral(Round::NearestTiesToEven).value.to_f64());
-
-    let p = Double::ZERO.round_to_integral(Round::TowardZero).value;
-    assert_eq!(0.0, p.to_f64());
-    let p = (-Double::ZERO).round_to_integral(Round::TowardZero).value;
-    assert_eq!(-0.0, p.to_f64());
-    let p = Double::NAN.round_to_integral(Round::TowardZero).value;
-    assert!(p.to_f64().is_nan());
-    let p = Double::INFINITY.round_to_integral(Round::TowardZero).value;
-    assert!(p.to_f64().is_infinite() && p.to_f64() > 0.0);
-    let p = (-Double::INFINITY).round_to_integral(Round::TowardZero).value;
-    assert!(p.to_f64().is_infinite() && p.to_f64() < 0.0);
-}
-
-#[test]
-fn is_integer() {
-    let t = Double::from_f64(-0.0);
-    assert!(t.is_integer());
-    let t = Double::from_f64(3.14159);
-    assert!(!t.is_integer());
-    let t = Double::NAN;
-    assert!(!t.is_integer());
-    let t = Double::INFINITY;
-    assert!(!t.is_integer());
-    let t = -Double::INFINITY;
-    assert!(!t.is_integer());
-    let t = Double::largest();
-    assert!(t.is_integer());
-}
-
-#[test]
-fn largest() {
-    assert_eq!(3.402823466e+38, Single::largest().to_f32());
-    assert_eq!(1.7976931348623158e+308, Double::largest().to_f64());
-}
-
-#[test]
-fn smallest() {
-    let test = Single::SMALLEST;
-    let expected = "0x0.000002p-126".parse::<Single>().unwrap();
-    assert!(!test.is_negative());
-    assert!(test.is_finite_non_zero());
-    assert!(test.is_denormal());
-    assert!(test.bitwise_eq(expected));
-
-    let test = -Single::SMALLEST;
-    let expected = "-0x0.000002p-126".parse::<Single>().unwrap();
-    assert!(test.is_negative());
-    assert!(test.is_finite_non_zero());
-    assert!(test.is_denormal());
-    assert!(test.bitwise_eq(expected));
-
-    let test = Quad::SMALLEST;
-    let expected = "0x0.0000000000000000000000000001p-16382".parse::<Quad>().unwrap();
-    assert!(!test.is_negative());
-    assert!(test.is_finite_non_zero());
-    assert!(test.is_denormal());
-    assert!(test.bitwise_eq(expected));
-
-    let test = -Quad::SMALLEST;
-    let expected = "-0x0.0000000000000000000000000001p-16382".parse::<Quad>().unwrap();
-    assert!(test.is_negative());
-    assert!(test.is_finite_non_zero());
-    assert!(test.is_denormal());
-    assert!(test.bitwise_eq(expected));
-}
-
-#[test]
-fn smallest_normalized() {
-    let test = Single::smallest_normalized();
-    let expected = "0x1p-126".parse::<Single>().unwrap();
-    assert!(!test.is_negative());
-    assert!(test.is_finite_non_zero());
-    assert!(!test.is_denormal());
-    assert!(test.bitwise_eq(expected));
-
-    let test = -Single::smallest_normalized();
-    let expected = "-0x1p-126".parse::<Single>().unwrap();
-    assert!(test.is_negative());
-    assert!(test.is_finite_non_zero());
-    assert!(!test.is_denormal());
-    assert!(test.bitwise_eq(expected));
-
-    let test = Quad::smallest_normalized();
-    let expected = "0x1p-16382".parse::<Quad>().unwrap();
-    assert!(!test.is_negative());
-    assert!(test.is_finite_non_zero());
-    assert!(!test.is_denormal());
-    assert!(test.bitwise_eq(expected));
-
-    let test = -Quad::smallest_normalized();
-    let expected = "-0x1p-16382".parse::<Quad>().unwrap();
-    assert!(test.is_negative());
-    assert!(test.is_finite_non_zero());
-    assert!(!test.is_denormal());
-    assert!(test.bitwise_eq(expected));
-}
-
-#[test]
-fn zero() {
-    assert_eq!(0.0, Single::from_f32(0.0).to_f32());
-    assert_eq!(-0.0, Single::from_f32(-0.0).to_f32());
-    assert!(Single::from_f32(-0.0).is_negative());
-
-    assert_eq!(0.0, Double::from_f64(0.0).to_f64());
-    assert_eq!(-0.0, Double::from_f64(-0.0).to_f64());
-    assert!(Double::from_f64(-0.0).is_negative());
-
-    fn test<T: Float>(sign: bool, bits: u128) {
-        let test = if sign { -T::ZERO } else { T::ZERO };
-        let pattern = if sign { "-0x0p+0" } else { "0x0p+0" };
-        let expected = pattern.parse::<T>().unwrap();
-        assert!(test.is_zero());
-        assert_eq!(sign, test.is_negative());
-        assert!(test.bitwise_eq(expected));
-        assert_eq!(bits, test.to_bits());
-    }
-    test::<Half>(false, 0);
-    test::<Half>(true, 0x8000);
-    test::<Single>(false, 0);
-    test::<Single>(true, 0x80000000);
-    test::<Double>(false, 0);
-    test::<Double>(true, 0x8000000000000000);
-    test::<Quad>(false, 0);
-    test::<Quad>(true, 0x8000000000000000_0000000000000000);
-    test::<X87DoubleExtended>(false, 0);
-    test::<X87DoubleExtended>(true, 0x8000_0000000000000000);
-}
-
-#[test]
-fn copy_sign() {
-    assert!(
-        Double::from_f64(-42.0)
-            .bitwise_eq(Double::from_f64(42.0).copy_sign(Double::from_f64(-1.0),),)
-    );
-    assert!(
-        Double::from_f64(42.0)
-            .bitwise_eq(Double::from_f64(-42.0).copy_sign(Double::from_f64(1.0),),)
-    );
-    assert!(
-        Double::from_f64(-42.0)
-            .bitwise_eq(Double::from_f64(-42.0).copy_sign(Double::from_f64(-1.0),),)
-    );
-    assert!(
-        Double::from_f64(42.0)
-            .bitwise_eq(Double::from_f64(42.0).copy_sign(Double::from_f64(1.0),),)
-    );
-}
-
-#[test]
-fn convert() {
-    let mut loses_info = false;
-    let test = "1.0".parse::<Double>().unwrap();
-    let test: Single = test.convert(&mut loses_info).value;
-    assert_eq!(1.0, test.to_f32());
-    assert!(!loses_info);
-
-    let mut test = "0x1p-53".parse::<X87DoubleExtended>().unwrap();
-    let one = "1.0".parse::<X87DoubleExtended>().unwrap();
-    test += one;
-    let test: Double = test.convert(&mut loses_info).value;
-    assert_eq!(1.0, test.to_f64());
-    assert!(loses_info);
-
-    let mut test = "0x1p-53".parse::<Quad>().unwrap();
-    let one = "1.0".parse::<Quad>().unwrap();
-    test += one;
-    let test: Double = test.convert(&mut loses_info).value;
-    assert_eq!(1.0, test.to_f64());
-    assert!(loses_info);
-
-    let test = "0xf.fffffffp+28".parse::<X87DoubleExtended>().unwrap();
-    let test: Double = test.convert(&mut loses_info).value;
-    assert_eq!(4294967295.0, test.to_f64());
-    assert!(!loses_info);
-
-    let test = Single::qnan(None);
-    let x87_qnan = X87DoubleExtended::qnan(None);
-    let test: X87DoubleExtended = test.convert(&mut loses_info).value;
-    assert!(test.bitwise_eq(x87_qnan));
-    assert!(!loses_info);
-
-    let test = Single::snan(None);
-    let sta = test.convert(&mut loses_info);
-    let test: X87DoubleExtended = sta.value;
-    assert!(test.is_nan());
-    assert!(!test.is_signaling());
-    assert!(!loses_info);
-    assert_eq!(sta.status, Status::INVALID_OP);
-
-    let test = X87DoubleExtended::qnan(None);
-    let test: X87DoubleExtended = test.convert(&mut loses_info).value;
-    assert!(test.bitwise_eq(x87_qnan));
-    assert!(!loses_info);
-
-    let test = X87DoubleExtended::snan(None);
-    let sta = test.convert(&mut loses_info);
-    let test: X87DoubleExtended = sta.value;
-    assert!(test.is_nan());
-    assert!(!test.is_signaling());
-    assert!(!loses_info);
-    assert_eq!(sta.status, Status::INVALID_OP);
-}
-
-#[test]
-fn is_negative() {
-    let t = "0x1p+0".parse::<Single>().unwrap();
-    assert!(!t.is_negative());
-    let t = "-0x1p+0".parse::<Single>().unwrap();
-    assert!(t.is_negative());
-
-    assert!(!Single::INFINITY.is_negative());
-    assert!((-Single::INFINITY).is_negative());
-
-    assert!(!Single::ZERO.is_negative());
-    assert!((-Single::ZERO).is_negative());
-
-    assert!(!Single::NAN.is_negative());
-    assert!((-Single::NAN).is_negative());
-
-    assert!(!Single::snan(None).is_negative());
-    assert!((-Single::snan(None)).is_negative());
-}
-
-#[test]
-fn is_normal() {
-    let t = "0x1p+0".parse::<Single>().unwrap();
-    assert!(t.is_normal());
-
-    assert!(!Single::INFINITY.is_normal());
-    assert!(!Single::ZERO.is_normal());
-    assert!(!Single::NAN.is_normal());
-    assert!(!Single::snan(None).is_normal());
-    assert!(!"0x1p-149".parse::<Single>().unwrap().is_normal());
-}
-
-#[test]
-fn is_finite() {
-    let t = "0x1p+0".parse::<Single>().unwrap();
-    assert!(t.is_finite());
-    assert!(!Single::INFINITY.is_finite());
-    assert!(Single::ZERO.is_finite());
-    assert!(!Single::NAN.is_finite());
-    assert!(!Single::snan(None).is_finite());
-    assert!("0x1p-149".parse::<Single>().unwrap().is_finite());
-}
-
-#[test]
-fn is_infinite() {
-    let t = "0x1p+0".parse::<Single>().unwrap();
-    assert!(!t.is_infinite());
-    assert!(Single::INFINITY.is_infinite());
-    assert!(!Single::ZERO.is_infinite());
-    assert!(!Single::NAN.is_infinite());
-    assert!(!Single::snan(None).is_infinite());
-    assert!(!"0x1p-149".parse::<Single>().unwrap().is_infinite());
-}
-
-#[test]
-fn is_nan() {
-    let t = "0x1p+0".parse::<Single>().unwrap();
-    assert!(!t.is_nan());
-    assert!(!Single::INFINITY.is_nan());
-    assert!(!Single::ZERO.is_nan());
-    assert!(Single::NAN.is_nan());
-    assert!(Single::snan(None).is_nan());
-    assert!(!"0x1p-149".parse::<Single>().unwrap().is_nan());
-}
-
-#[test]
-fn is_finite_non_zero() {
-    // Test positive/negative normal value.
-    assert!("0x1p+0".parse::<Single>().unwrap().is_finite_non_zero());
-    assert!("-0x1p+0".parse::<Single>().unwrap().is_finite_non_zero());
-
-    // Test positive/negative denormal value.
-    assert!("0x1p-149".parse::<Single>().unwrap().is_finite_non_zero());
-    assert!("-0x1p-149".parse::<Single>().unwrap().is_finite_non_zero());
-
-    // Test +/- Infinity.
-    assert!(!Single::INFINITY.is_finite_non_zero());
-    assert!(!(-Single::INFINITY).is_finite_non_zero());
-
-    // Test +/- Zero.
-    assert!(!Single::ZERO.is_finite_non_zero());
-    assert!(!(-Single::ZERO).is_finite_non_zero());
-
-    // Test +/- qNaN. +/- don't mean anything with qNaN but paranoia can't hurt in
-    // this instance.
-    assert!(!Single::NAN.is_finite_non_zero());
-    assert!(!(-Single::NAN).is_finite_non_zero());
-
-    // Test +/- sNaN. +/- don't mean anything with sNaN but paranoia can't hurt in
-    // this instance.
-    assert!(!Single::snan(None).is_finite_non_zero());
-    assert!(!(-Single::snan(None)).is_finite_non_zero());
-}
-
-#[test]
-fn add() {
-    // Test Special Cases against each other and normal values.
-
-    // FIXMES/NOTES:
-    // 1. Since we perform only default exception handling all operations with
-    // signaling NaNs should have a result that is a quiet NaN. Currently they
-    // return sNaN.
-
-    let p_inf = Single::INFINITY;
-    let m_inf = -Single::INFINITY;
-    let p_zero = Single::ZERO;
-    let m_zero = -Single::ZERO;
-    let qnan = Single::NAN;
-    let p_normal_value = "0x1p+0".parse::<Single>().unwrap();
-    let m_normal_value = "-0x1p+0".parse::<Single>().unwrap();
-    let p_largest_value = Single::largest();
-    let m_largest_value = -Single::largest();
-    let p_smallest_value = Single::SMALLEST;
-    let m_smallest_value = -Single::SMALLEST;
-    let p_smallest_normalized = Single::smallest_normalized();
-    let m_smallest_normalized = -Single::smallest_normalized();
-
-    let overflow_status = Status::OVERFLOW | Status::INEXACT;
-
-    let special_cases = [
-        (p_inf, p_inf, "inf", Status::OK, Category::Infinity),
-        (p_inf, m_inf, "nan", Status::INVALID_OP, Category::NaN),
-        (p_inf, p_zero, "inf", Status::OK, Category::Infinity),
-        (p_inf, m_zero, "inf", Status::OK, Category::Infinity),
-        (p_inf, qnan, "nan", Status::OK, Category::NaN),
-        /*
-        // See Note 1.
-        (p_inf, snan, "nan", Status::INVALID_OP, Category::NaN),
-                */
-        (p_inf, p_normal_value, "inf", Status::OK, Category::Infinity),
-        (p_inf, m_normal_value, "inf", Status::OK, Category::Infinity),
-        (p_inf, p_largest_value, "inf", Status::OK, Category::Infinity),
-        (p_inf, m_largest_value, "inf", Status::OK, Category::Infinity),
-        (p_inf, p_smallest_value, "inf", Status::OK, Category::Infinity),
-        (p_inf, m_smallest_value, "inf", Status::OK, Category::Infinity),
-        (p_inf, p_smallest_normalized, "inf", Status::OK, Category::Infinity),
-        (p_inf, m_smallest_normalized, "inf", Status::OK, Category::Infinity),
-        (m_inf, p_inf, "nan", Status::INVALID_OP, Category::NaN),
-        (m_inf, m_inf, "-inf", Status::OK, Category::Infinity),
-        (m_inf, p_zero, "-inf", Status::OK, Category::Infinity),
-        (m_inf, m_zero, "-inf", Status::OK, Category::Infinity),
-        (m_inf, qnan, "nan", Status::OK, Category::NaN),
-        /*
-        // See Note 1.
-        (m_inf, snan, "nan", Status::INVALID_OP, Category::NaN),
-                */
-        (m_inf, p_normal_value, "-inf", Status::OK, Category::Infinity),
-        (m_inf, m_normal_value, "-inf", Status::OK, Category::Infinity),
-        (m_inf, p_largest_value, "-inf", Status::OK, Category::Infinity),
-        (m_inf, m_largest_value, "-inf", Status::OK, Category::Infinity),
-        (m_inf, p_smallest_value, "-inf", Status::OK, Category::Infinity),
-        (m_inf, m_smallest_value, "-inf", Status::OK, Category::Infinity),
-        (m_inf, p_smallest_normalized, "-inf", Status::OK, Category::Infinity),
-        (m_inf, m_smallest_normalized, "-inf", Status::OK, Category::Infinity),
-        (p_zero, p_inf, "inf", Status::OK, Category::Infinity),
-        (p_zero, m_inf, "-inf", Status::OK, Category::Infinity),
-        (p_zero, p_zero, "0x0p+0", Status::OK, Category::Zero),
-        (p_zero, m_zero, "0x0p+0", Status::OK, Category::Zero),
-        (p_zero, qnan, "nan", Status::OK, Category::NaN),
-        /*
-        // See Note 1.
-        (p_zero, snan, "nan", Status::INVALID_OP, Category::NaN),
-                */
-        (p_zero, p_normal_value, "0x1p+0", Status::OK, Category::Normal),
-        (p_zero, m_normal_value, "-0x1p+0", Status::OK, Category::Normal),
-        (p_zero, p_largest_value, "0x1.fffffep+127", Status::OK, Category::Normal),
-        (p_zero, m_largest_value, "-0x1.fffffep+127", Status::OK, Category::Normal),
-        (p_zero, p_smallest_value, "0x1p-149", Status::OK, Category::Normal),
-        (p_zero, m_smallest_value, "-0x1p-149", Status::OK, Category::Normal),
-        (p_zero, p_smallest_normalized, "0x1p-126", Status::OK, Category::Normal),
-        (p_zero, m_smallest_normalized, "-0x1p-126", Status::OK, Category::Normal),
-        (m_zero, p_inf, "inf", Status::OK, Category::Infinity),
-        (m_zero, m_inf, "-inf", Status::OK, Category::Infinity),
-        (m_zero, p_zero, "0x0p+0", Status::OK, Category::Zero),
-        (m_zero, m_zero, "-0x0p+0", Status::OK, Category::Zero),
-        (m_zero, qnan, "nan", Status::OK, Category::NaN),
-        /*
-        // See Note 1.
-        (m_zero, snan, "nan", Status::INVALID_OP, Category::NaN),
-                */
-        (m_zero, p_normal_value, "0x1p+0", Status::OK, Category::Normal),
-        (m_zero, m_normal_value, "-0x1p+0", Status::OK, Category::Normal),
-        (m_zero, p_largest_value, "0x1.fffffep+127", Status::OK, Category::Normal),
-        (m_zero, m_largest_value, "-0x1.fffffep+127", Status::OK, Category::Normal),
-        (m_zero, p_smallest_value, "0x1p-149", Status::OK, Category::Normal),
-        (m_zero, m_smallest_value, "-0x1p-149", Status::OK, Category::Normal),
-        (m_zero, p_smallest_normalized, "0x1p-126", Status::OK, Category::Normal),
-        (m_zero, m_smallest_normalized, "-0x1p-126", Status::OK, Category::Normal),
-        (qnan, p_inf, "nan", Status::OK, Category::NaN),
-        (qnan, m_inf, "nan", Status::OK, Category::NaN),
-        (qnan, p_zero, "nan", Status::OK, Category::NaN),
-        (qnan, m_zero, "nan", Status::OK, Category::NaN),
-        (qnan, qnan, "nan", Status::OK, Category::NaN),
-        /*
-        // See Note 1.
-        (qnan, snan, "nan", Status::INVALID_OP, Category::NaN),
-                */
-        (qnan, p_normal_value, "nan", Status::OK, Category::NaN),
-        (qnan, m_normal_value, "nan", Status::OK, Category::NaN),
-        (qnan, p_largest_value, "nan", Status::OK, Category::NaN),
-        (qnan, m_largest_value, "nan", Status::OK, Category::NaN),
-        (qnan, p_smallest_value, "nan", Status::OK, Category::NaN),
-        (qnan, m_smallest_value, "nan", Status::OK, Category::NaN),
-        (qnan, p_smallest_normalized, "nan", Status::OK, Category::NaN),
-        (qnan, m_smallest_normalized, "nan", Status::OK, Category::NaN),
-        /*
-        // See Note 1.
-        (snan, p_inf, "nan", Status::INVALID_OP, Category::NaN),
-        (snan, m_inf, "nan", Status::INVALID_OP, Category::NaN),
-        (snan, p_zero, "nan", Status::INVALID_OP, Category::NaN),
-        (snan, m_zero, "nan", Status::INVALID_OP, Category::NaN),
-        (snan, qnan, "nan", Status::INVALID_OP, Category::NaN),
-        (snan, snan, "nan", Status::INVALID_OP, Category::NaN),
-        (snan, p_normal_value, "nan", Status::INVALID_OP, Category::NaN),
-        (snan, m_normal_value, "nan", Status::INVALID_OP, Category::NaN),
-        (snan, p_largest_value, "nan", Status::INVALID_OP, Category::NaN),
-        (snan, m_largest_value, "nan", Status::INVALID_OP, Category::NaN),
-        (snan, p_smallest_value, "nan", Status::INVALID_OP, Category::NaN),
-        (snan, m_smallest_value, "nan", Status::INVALID_OP, Category::NaN),
-        (snan, p_smallest_normalized, "nan", Status::INVALID_OP, Category::NaN),
-        (snan, m_smallest_normalized, "nan", Status::INVALID_OP, Category::NaN),
-                */
-        (p_normal_value, p_inf, "inf", Status::OK, Category::Infinity),
-        (p_normal_value, m_inf, "-inf", Status::OK, Category::Infinity),
-        (p_normal_value, p_zero, "0x1p+0", Status::OK, Category::Normal),
-        (p_normal_value, m_zero, "0x1p+0", Status::OK, Category::Normal),
-        (p_normal_value, qnan, "nan", Status::OK, Category::NaN),
-        /*
-        // See Note 1.
-        (p_normal_value, snan, "nan", Status::INVALID_OP, Category::NaN),
-                */
-        (p_normal_value, p_normal_value, "0x1p+1", Status::OK, Category::Normal),
-        (p_normal_value, m_normal_value, "0x0p+0", Status::OK, Category::Zero),
-        (p_normal_value, p_largest_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal),
-        (p_normal_value, m_largest_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal),
-        (p_normal_value, p_smallest_value, "0x1p+0", Status::INEXACT, Category::Normal),
-        (p_normal_value, m_smallest_value, "0x1p+0", Status::INEXACT, Category::Normal),
-        (p_normal_value, p_smallest_normalized, "0x1p+0", Status::INEXACT, Category::Normal),
-        (p_normal_value, m_smallest_normalized, "0x1p+0", Status::INEXACT, Category::Normal),
-        (m_normal_value, p_inf, "inf", Status::OK, Category::Infinity),
-        (m_normal_value, m_inf, "-inf", Status::OK, Category::Infinity),
-        (m_normal_value, p_zero, "-0x1p+0", Status::OK, Category::Normal),
-        (m_normal_value, m_zero, "-0x1p+0", Status::OK, Category::Normal),
-        (m_normal_value, qnan, "nan", Status::OK, Category::NaN),
-        /*
-        // See Note 1.
-        (m_normal_value, snan, "nan", Status::INVALID_OP, Category::NaN),
-                */
-        (m_normal_value, p_normal_value, "0x0p+0", Status::OK, Category::Zero),
-        (m_normal_value, m_normal_value, "-0x1p+1", Status::OK, Category::Normal),
-        (m_normal_value, p_largest_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal),
-        (m_normal_value, m_largest_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal),
-        (m_normal_value, p_smallest_value, "-0x1p+0", Status::INEXACT, Category::Normal),
-        (m_normal_value, m_smallest_value, "-0x1p+0", Status::INEXACT, Category::Normal),
-        (m_normal_value, p_smallest_normalized, "-0x1p+0", Status::INEXACT, Category::Normal),
-        (m_normal_value, m_smallest_normalized, "-0x1p+0", Status::INEXACT, Category::Normal),
-        (p_largest_value, p_inf, "inf", Status::OK, Category::Infinity),
-        (p_largest_value, m_inf, "-inf", Status::OK, Category::Infinity),
-        (p_largest_value, p_zero, "0x1.fffffep+127", Status::OK, Category::Normal),
-        (p_largest_value, m_zero, "0x1.fffffep+127", Status::OK, Category::Normal),
-        (p_largest_value, qnan, "nan", Status::OK, Category::NaN),
-        /*
-        // See Note 1.
-        (p_largest_value, snan, "nan", Status::INVALID_OP, Category::NaN),
-                */
-        (p_largest_value, p_normal_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal),
-        (p_largest_value, m_normal_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal),
-        (p_largest_value, p_largest_value, "inf", overflow_status, Category::Infinity),
-        (p_largest_value, m_largest_value, "0x0p+0", Status::OK, Category::Zero),
-        (p_largest_value, p_smallest_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal),
-        (p_largest_value, m_smallest_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal),
-        (
-            p_largest_value,
-            p_smallest_normalized,
-            "0x1.fffffep+127",
-            Status::INEXACT,
-            Category::Normal,
-        ),
-        (
-            p_largest_value,
-            m_smallest_normalized,
-            "0x1.fffffep+127",
-            Status::INEXACT,
-            Category::Normal,
-        ),
-        (m_largest_value, p_inf, "inf", Status::OK, Category::Infinity),
-        (m_largest_value, m_inf, "-inf", Status::OK, Category::Infinity),
-        (m_largest_value, p_zero, "-0x1.fffffep+127", Status::OK, Category::Normal),
-        (m_largest_value, m_zero, "-0x1.fffffep+127", Status::OK, Category::Normal),
-        (m_largest_value, qnan, "nan", Status::OK, Category::NaN),
-        /*
-        // See Note 1.
-        (m_largest_value, snan, "nan", Status::INVALID_OP, Category::NaN),
-                */
-        (m_largest_value, p_normal_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal),
-        (m_largest_value, m_normal_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal),
-        (m_largest_value, p_largest_value, "0x0p+0", Status::OK, Category::Zero),
-        (m_largest_value, m_largest_value, "-inf", overflow_status, Category::Infinity),
-        (m_largest_value, p_smallest_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal),
-        (m_largest_value, m_smallest_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal),
-        (
-            m_largest_value,
-            p_smallest_normalized,
-            "-0x1.fffffep+127",
-            Status::INEXACT,
-            Category::Normal,
-        ),
-        (
-            m_largest_value,
-            m_smallest_normalized,
-            "-0x1.fffffep+127",
-            Status::INEXACT,
-            Category::Normal,
-        ),
-        (p_smallest_value, p_inf, "inf", Status::OK, Category::Infinity),
-        (p_smallest_value, m_inf, "-inf", Status::OK, Category::Infinity),
-        (p_smallest_value, p_zero, "0x1p-149", Status::OK, Category::Normal),
-        (p_smallest_value, m_zero, "0x1p-149", Status::OK, Category::Normal),
-        (p_smallest_value, qnan, "nan", Status::OK, Category::NaN),
-        /*
-        // See Note 1.
-        (p_smallest_value, snan, "nan", Status::INVALID_OP, Category::NaN),
-                */
-        (p_smallest_value, p_normal_value, "0x1p+0", Status::INEXACT, Category::Normal),
-        (p_smallest_value, m_normal_value, "-0x1p+0", Status::INEXACT, Category::Normal),
-        (p_smallest_value, p_largest_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal),
-        (p_smallest_value, m_largest_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal),
-        (p_smallest_value, p_smallest_value, "0x1p-148", Status::OK, Category::Normal),
-        (p_smallest_value, m_smallest_value, "0x0p+0", Status::OK, Category::Zero),
-        (p_smallest_value, p_smallest_normalized, "0x1.000002p-126", Status::OK, Category::Normal),
-        (p_smallest_value, m_smallest_normalized, "-0x1.fffffcp-127", Status::OK, Category::Normal),
-        (m_smallest_value, p_inf, "inf", Status::OK, Category::Infinity),
-        (m_smallest_value, m_inf, "-inf", Status::OK, Category::Infinity),
-        (m_smallest_value, p_zero, "-0x1p-149", Status::OK, Category::Normal),
-        (m_smallest_value, m_zero, "-0x1p-149", Status::OK, Category::Normal),
-        (m_smallest_value, qnan, "nan", Status::OK, Category::NaN),
-        /*
-        // See Note 1.
-        (m_smallest_value, snan, "nan", Status::INVALID_OP, Category::NaN),
-                */
-        (m_smallest_value, p_normal_value, "0x1p+0", Status::INEXACT, Category::Normal),
-        (m_smallest_value, m_normal_value, "-0x1p+0", Status::INEXACT, Category::Normal),
-        (m_smallest_value, p_largest_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal),
-        (m_smallest_value, m_largest_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal),
-        (m_smallest_value, p_smallest_value, "0x0p+0", Status::OK, Category::Zero),
-        (m_smallest_value, m_smallest_value, "-0x1p-148", Status::OK, Category::Normal),
-        (m_smallest_value, p_smallest_normalized, "0x1.fffffcp-127", Status::OK, Category::Normal),
-        (m_smallest_value, m_smallest_normalized, "-0x1.000002p-126", Status::OK, Category::Normal),
-        (p_smallest_normalized, p_inf, "inf", Status::OK, Category::Infinity),
-        (p_smallest_normalized, m_inf, "-inf", Status::OK, Category::Infinity),
-        (p_smallest_normalized, p_zero, "0x1p-126", Status::OK, Category::Normal),
-        (p_smallest_normalized, m_zero, "0x1p-126", Status::OK, Category::Normal),
-        (p_smallest_normalized, qnan, "nan", Status::OK, Category::NaN),
-        /*
-        // See Note 1.
-        (p_smallest_normalized, snan, "nan", Status::INVALID_OP, Category::NaN),
-                */
-        (p_smallest_normalized, p_normal_value, "0x1p+0", Status::INEXACT, Category::Normal),
-        (p_smallest_normalized, m_normal_value, "-0x1p+0", Status::INEXACT, Category::Normal),
-        (
-            p_smallest_normalized,
-            p_largest_value,
-            "0x1.fffffep+127",
-            Status::INEXACT,
-            Category::Normal,
-        ),
-        (
-            p_smallest_normalized,
-            m_largest_value,
-            "-0x1.fffffep+127",
-            Status::INEXACT,
-            Category::Normal,
-        ),
-        (p_smallest_normalized, p_smallest_value, "0x1.000002p-126", Status::OK, Category::Normal),
-        (p_smallest_normalized, m_smallest_value, "0x1.fffffcp-127", Status::OK, Category::Normal),
-        (p_smallest_normalized, p_smallest_normalized, "0x1p-125", Status::OK, Category::Normal),
-        (p_smallest_normalized, m_smallest_normalized, "0x0p+0", Status::OK, Category::Zero),
-        (m_smallest_normalized, p_inf, "inf", Status::OK, Category::Infinity),
-        (m_smallest_normalized, m_inf, "-inf", Status::OK, Category::Infinity),
-        (m_smallest_normalized, p_zero, "-0x1p-126", Status::OK, Category::Normal),
-        (m_smallest_normalized, m_zero, "-0x1p-126", Status::OK, Category::Normal),
-        (m_smallest_normalized, qnan, "nan", Status::OK, Category::NaN),
-        /*
-        // See Note 1.
-        (m_smallest_normalized, snan, "nan", Status::INVALID_OP, Category::NaN),
-                */
-        (m_smallest_normalized, p_normal_value, "0x1p+0", Status::INEXACT, Category::Normal),
-        (m_smallest_normalized, m_normal_value, "-0x1p+0", Status::INEXACT, Category::Normal),
-        (
-            m_smallest_normalized,
-            p_largest_value,
-            "0x1.fffffep+127",
-            Status::INEXACT,
-            Category::Normal,
-        ),
-        (
-            m_smallest_normalized,
-            m_largest_value,
-            "-0x1.fffffep+127",
-            Status::INEXACT,
-            Category::Normal,
-        ),
-        (m_smallest_normalized, p_smallest_value, "-0x1.fffffcp-127", Status::OK, Category::Normal),
-        (m_smallest_normalized, m_smallest_value, "-0x1.000002p-126", Status::OK, Category::Normal),
-        (m_smallest_normalized, p_smallest_normalized, "0x0p+0", Status::OK, Category::Zero),
-        (m_smallest_normalized, m_smallest_normalized, "-0x1p-125", Status::OK, Category::Normal),
-    ];
-
-    for (x, y, e_result, e_status, e_category) in special_cases {
-        let status;
-        let result = unpack!(status=, x + y);
-        assert_eq!(status, e_status);
-        assert_eq!(result.category(), e_category);
-        assert!(result.bitwise_eq(e_result.parse::<Single>().unwrap()));
-    }
-}
-
-#[test]
-fn subtract() {
-    // Test Special Cases against each other and normal values.
-
-    // FIXMES/NOTES:
-    // 1. Since we perform only default exception handling all operations with
-    // signaling NaNs should have a result that is a quiet NaN. Currently they
-    // return sNaN.
-
-    let p_inf = Single::INFINITY;
-    let m_inf = -Single::INFINITY;
-    let p_zero = Single::ZERO;
-    let m_zero = -Single::ZERO;
-    let qnan = Single::NAN;
-    let p_normal_value = "0x1p+0".parse::<Single>().unwrap();
-    let m_normal_value = "-0x1p+0".parse::<Single>().unwrap();
-    let p_largest_value = Single::largest();
-    let m_largest_value = -Single::largest();
-    let p_smallest_value = Single::SMALLEST;
-    let m_smallest_value = -Single::SMALLEST;
-    let p_smallest_normalized = Single::smallest_normalized();
-    let m_smallest_normalized = -Single::smallest_normalized();
-
-    let overflow_status = Status::OVERFLOW | Status::INEXACT;
-
-    let special_cases = [
-        (p_inf, p_inf, "nan", Status::INVALID_OP, Category::NaN),
-        (p_inf, m_inf, "inf", Status::OK, Category::Infinity),
-        (p_inf, p_zero, "inf", Status::OK, Category::Infinity),
-        (p_inf, m_zero, "inf", Status::OK, Category::Infinity),
-        (p_inf, qnan, "-nan", Status::OK, Category::NaN),
-        /*
-        // See Note 1.
-        (p_inf, snan, "-nan", Status::INVALID_OP, Category::NaN),
-                */
-        (p_inf, p_normal_value, "inf", Status::OK, Category::Infinity),
-        (p_inf, m_normal_value, "inf", Status::OK, Category::Infinity),
-        (p_inf, p_largest_value, "inf", Status::OK, Category::Infinity),
-        (p_inf, m_largest_value, "inf", Status::OK, Category::Infinity),
-        (p_inf, p_smallest_value, "inf", Status::OK, Category::Infinity),
-        (p_inf, m_smallest_value, "inf", Status::OK, Category::Infinity),
-        (p_inf, p_smallest_normalized, "inf", Status::OK, Category::Infinity),
-        (p_inf, m_smallest_normalized, "inf", Status::OK, Category::Infinity),
-        (m_inf, p_inf, "-inf", Status::OK, Category::Infinity),
-        (m_inf, m_inf, "nan", Status::INVALID_OP, Category::NaN),
-        (m_inf, p_zero, "-inf", Status::OK, Category::Infinity),
-        (m_inf, m_zero, "-inf", Status::OK, Category::Infinity),
-        (m_inf, qnan, "-nan", Status::OK, Category::NaN),
-        /*
-        // See Note 1.
-        (m_inf, snan, "-nan", Status::INVALID_OP, Category::NaN),
-                */
-        (m_inf, p_normal_value, "-inf", Status::OK, Category::Infinity),
-        (m_inf, m_normal_value, "-inf", Status::OK, Category::Infinity),
-        (m_inf, p_largest_value, "-inf", Status::OK, Category::Infinity),
-        (m_inf, m_largest_value, "-inf", Status::OK, Category::Infinity),
-        (m_inf, p_smallest_value, "-inf", Status::OK, Category::Infinity),
-        (m_inf, m_smallest_value, "-inf", Status::OK, Category::Infinity),
-        (m_inf, p_smallest_normalized, "-inf", Status::OK, Category::Infinity),
-        (m_inf, m_smallest_normalized, "-inf", Status::OK, Category::Infinity),
-        (p_zero, p_inf, "-inf", Status::OK, Category::Infinity),
-        (p_zero, m_inf, "inf", Status::OK, Category::Infinity),
-        (p_zero, p_zero, "0x0p+0", Status::OK, Category::Zero),
-        (p_zero, m_zero, "0x0p+0", Status::OK, Category::Zero),
-        (p_zero, qnan, "-nan", Status::OK, Category::NaN),
-        /*
-        // See Note 1.
-        (p_zero, snan, "-nan", Status::INVALID_OP, Category::NaN),
-                */
-        (p_zero, p_normal_value, "-0x1p+0", Status::OK, Category::Normal),
-        (p_zero, m_normal_value, "0x1p+0", Status::OK, Category::Normal),
-        (p_zero, p_largest_value, "-0x1.fffffep+127", Status::OK, Category::Normal),
-        (p_zero, m_largest_value, "0x1.fffffep+127", Status::OK, Category::Normal),
-        (p_zero, p_smallest_value, "-0x1p-149", Status::OK, Category::Normal),
-        (p_zero, m_smallest_value, "0x1p-149", Status::OK, Category::Normal),
-        (p_zero, p_smallest_normalized, "-0x1p-126", Status::OK, Category::Normal),
-        (p_zero, m_smallest_normalized, "0x1p-126", Status::OK, Category::Normal),
-        (m_zero, p_inf, "-inf", Status::OK, Category::Infinity),
-        (m_zero, m_inf, "inf", Status::OK, Category::Infinity),
-        (m_zero, p_zero, "-0x0p+0", Status::OK, Category::Zero),
-        (m_zero, m_zero, "0x0p+0", Status::OK, Category::Zero),
-        (m_zero, qnan, "-nan", Status::OK, Category::NaN),
-        /*
-        // See Note 1.
-        (m_zero, snan, "-nan", Status::INVALID_OP, Category::NaN),
-                */
-        (m_zero, p_normal_value, "-0x1p+0", Status::OK, Category::Normal),
-        (m_zero, m_normal_value, "0x1p+0", Status::OK, Category::Normal),
-        (m_zero, p_largest_value, "-0x1.fffffep+127", Status::OK, Category::Normal),
-        (m_zero, m_largest_value, "0x1.fffffep+127", Status::OK, Category::Normal),
-        (m_zero, p_smallest_value, "-0x1p-149", Status::OK, Category::Normal),
-        (m_zero, m_smallest_value, "0x1p-149", Status::OK, Category::Normal),
-        (m_zero, p_smallest_normalized, "-0x1p-126", Status::OK, Category::Normal),
-        (m_zero, m_smallest_normalized, "0x1p-126", Status::OK, Category::Normal),
-        (qnan, p_inf, "nan", Status::OK, Category::NaN),
-        (qnan, m_inf, "nan", Status::OK, Category::NaN),
-        (qnan, p_zero, "nan", Status::OK, Category::NaN),
-        (qnan, m_zero, "nan", Status::OK, Category::NaN),
-        (qnan, qnan, "nan", Status::OK, Category::NaN),
-        /*
-        // See Note 1.
-        (qnan, snan, "nan", Status::INVALID_OP, Category::NaN),
-                */
-        (qnan, p_normal_value, "nan", Status::OK, Category::NaN),
-        (qnan, m_normal_value, "nan", Status::OK, Category::NaN),
-        (qnan, p_largest_value, "nan", Status::OK, Category::NaN),
-        (qnan, m_largest_value, "nan", Status::OK, Category::NaN),
-        (qnan, p_smallest_value, "nan", Status::OK, Category::NaN),
-        (qnan, m_smallest_value, "nan", Status::OK, Category::NaN),
-        (qnan, p_smallest_normalized, "nan", Status::OK, Category::NaN),
-        (qnan, m_smallest_normalized, "nan", Status::OK, Category::NaN),
-        /*
-        // See Note 1.
-        (snan, p_inf, "nan", Status::INVALID_OP, Category::NaN),
-        (snan, m_inf, "nan", Status::INVALID_OP, Category::NaN),
-        (snan, p_zero, "nan", Status::INVALID_OP, Category::NaN),
-        (snan, m_zero, "nan", Status::INVALID_OP, Category::NaN),
-        (snan, qnan, "nan", Status::INVALID_OP, Category::NaN),
-        (snan, snan, "nan", Status::INVALID_OP, Category::NaN),
-        (snan, p_normal_value, "nan", Status::INVALID_OP, Category::NaN),
-        (snan, m_normal_value, "nan", Status::INVALID_OP, Category::NaN),
-        (snan, p_largest_value, "nan", Status::INVALID_OP, Category::NaN),
-        (snan, m_largest_value, "nan", Status::INVALID_OP, Category::NaN),
-        (snan, p_smallest_value, "nan", Status::INVALID_OP, Category::NaN),
-        (snan, m_smallest_value, "nan", Status::INVALID_OP, Category::NaN),
-        (snan, p_smallest_normalized, "nan", Status::INVALID_OP, Category::NaN),
-        (snan, m_smallest_normalized, "nan", Status::INVALID_OP, Category::NaN),
-                */
-        (p_normal_value, p_inf, "-inf", Status::OK, Category::Infinity),
-        (p_normal_value, m_inf, "inf", Status::OK, Category::Infinity),
-        (p_normal_value, p_zero, "0x1p+0", Status::OK, Category::Normal),
-        (p_normal_value, m_zero, "0x1p+0", Status::OK, Category::Normal),
-        (p_normal_value, qnan, "-nan", Status::OK, Category::NaN),
-        /*
-        // See Note 1.
-        (p_normal_value, snan, "-nan", Status::INVALID_OP, Category::NaN),
-                */
-        (p_normal_value, p_normal_value, "0x0p+0", Status::OK, Category::Zero),
-        (p_normal_value, m_normal_value, "0x1p+1", Status::OK, Category::Normal),
-        (p_normal_value, p_largest_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal),
-        (p_normal_value, m_largest_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal),
-        (p_normal_value, p_smallest_value, "0x1p+0", Status::INEXACT, Category::Normal),
-        (p_normal_value, m_smallest_value, "0x1p+0", Status::INEXACT, Category::Normal),
-        (p_normal_value, p_smallest_normalized, "0x1p+0", Status::INEXACT, Category::Normal),
-        (p_normal_value, m_smallest_normalized, "0x1p+0", Status::INEXACT, Category::Normal),
-        (m_normal_value, p_inf, "-inf", Status::OK, Category::Infinity),
-        (m_normal_value, m_inf, "inf", Status::OK, Category::Infinity),
-        (m_normal_value, p_zero, "-0x1p+0", Status::OK, Category::Normal),
-        (m_normal_value, m_zero, "-0x1p+0", Status::OK, Category::Normal),
-        (m_normal_value, qnan, "-nan", Status::OK, Category::NaN),
-        /*
-        // See Note 1.
-        (m_normal_value, snan, "-nan", Status::INVALID_OP, Category::NaN),
-                */
-        (m_normal_value, p_normal_value, "-0x1p+1", Status::OK, Category::Normal),
-        (m_normal_value, m_normal_value, "0x0p+0", Status::OK, Category::Zero),
-        (m_normal_value, p_largest_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal),
-        (m_normal_value, m_largest_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal),
-        (m_normal_value, p_smallest_value, "-0x1p+0", Status::INEXACT, Category::Normal),
-        (m_normal_value, m_smallest_value, "-0x1p+0", Status::INEXACT, Category::Normal),
-        (m_normal_value, p_smallest_normalized, "-0x1p+0", Status::INEXACT, Category::Normal),
-        (m_normal_value, m_smallest_normalized, "-0x1p+0", Status::INEXACT, Category::Normal),
-        (p_largest_value, p_inf, "-inf", Status::OK, Category::Infinity),
-        (p_largest_value, m_inf, "inf", Status::OK, Category::Infinity),
-        (p_largest_value, p_zero, "0x1.fffffep+127", Status::OK, Category::Normal),
-        (p_largest_value, m_zero, "0x1.fffffep+127", Status::OK, Category::Normal),
-        (p_largest_value, qnan, "-nan", Status::OK, Category::NaN),
-        /*
-        // See Note 1.
-        (p_largest_value, snan, "-nan", Status::INVALID_OP, Category::NaN),
-                */
-        (p_largest_value, p_normal_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal),
-        (p_largest_value, m_normal_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal),
-        (p_largest_value, p_largest_value, "0x0p+0", Status::OK, Category::Zero),
-        (p_largest_value, m_largest_value, "inf", overflow_status, Category::Infinity),
-        (p_largest_value, p_smallest_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal),
-        (p_largest_value, m_smallest_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal),
-        (
-            p_largest_value,
-            p_smallest_normalized,
-            "0x1.fffffep+127",
-            Status::INEXACT,
-            Category::Normal,
-        ),
-        (
-            p_largest_value,
-            m_smallest_normalized,
-            "0x1.fffffep+127",
-            Status::INEXACT,
-            Category::Normal,
-        ),
-        (m_largest_value, p_inf, "-inf", Status::OK, Category::Infinity),
-        (m_largest_value, m_inf, "inf", Status::OK, Category::Infinity),
-        (m_largest_value, p_zero, "-0x1.fffffep+127", Status::OK, Category::Normal),
-        (m_largest_value, m_zero, "-0x1.fffffep+127", Status::OK, Category::Normal),
-        (m_largest_value, qnan, "-nan", Status::OK, Category::NaN),
-        /*
-        // See Note 1.
-        (m_largest_value, snan, "-nan", Status::INVALID_OP, Category::NaN),
-                */
-        (m_largest_value, p_normal_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal),
-        (m_largest_value, m_normal_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal),
-        (m_largest_value, p_largest_value, "-inf", overflow_status, Category::Infinity),
-        (m_largest_value, m_largest_value, "0x0p+0", Status::OK, Category::Zero),
-        (m_largest_value, p_smallest_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal),
-        (m_largest_value, m_smallest_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal),
-        (
-            m_largest_value,
-            p_smallest_normalized,
-            "-0x1.fffffep+127",
-            Status::INEXACT,
-            Category::Normal,
-        ),
-        (
-            m_largest_value,
-            m_smallest_normalized,
-            "-0x1.fffffep+127",
-            Status::INEXACT,
-            Category::Normal,
-        ),
-        (p_smallest_value, p_inf, "-inf", Status::OK, Category::Infinity),
-        (p_smallest_value, m_inf, "inf", Status::OK, Category::Infinity),
-        (p_smallest_value, p_zero, "0x1p-149", Status::OK, Category::Normal),
-        (p_smallest_value, m_zero, "0x1p-149", Status::OK, Category::Normal),
-        (p_smallest_value, qnan, "-nan", Status::OK, Category::NaN),
-        /*
-        // See Note 1.
-        (p_smallest_value, snan, "-nan", Status::INVALID_OP, Category::NaN),
-                */
-        (p_smallest_value, p_normal_value, "-0x1p+0", Status::INEXACT, Category::Normal),
-        (p_smallest_value, m_normal_value, "0x1p+0", Status::INEXACT, Category::Normal),
-        (p_smallest_value, p_largest_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal),
-        (p_smallest_value, m_largest_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal),
-        (p_smallest_value, p_smallest_value, "0x0p+0", Status::OK, Category::Zero),
-        (p_smallest_value, m_smallest_value, "0x1p-148", Status::OK, Category::Normal),
-        (p_smallest_value, p_smallest_normalized, "-0x1.fffffcp-127", Status::OK, Category::Normal),
-        (p_smallest_value, m_smallest_normalized, "0x1.000002p-126", Status::OK, Category::Normal),
-        (m_smallest_value, p_inf, "-inf", Status::OK, Category::Infinity),
-        (m_smallest_value, m_inf, "inf", Status::OK, Category::Infinity),
-        (m_smallest_value, p_zero, "-0x1p-149", Status::OK, Category::Normal),
-        (m_smallest_value, m_zero, "-0x1p-149", Status::OK, Category::Normal),
-        (m_smallest_value, qnan, "-nan", Status::OK, Category::NaN),
-        /*
-        // See Note 1.
-        (m_smallest_value, snan, "-nan", Status::INVALID_OP, Category::NaN),
-                */
-        (m_smallest_value, p_normal_value, "-0x1p+0", Status::INEXACT, Category::Normal),
-        (m_smallest_value, m_normal_value, "0x1p+0", Status::INEXACT, Category::Normal),
-        (m_smallest_value, p_largest_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal),
-        (m_smallest_value, m_largest_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal),
-        (m_smallest_value, p_smallest_value, "-0x1p-148", Status::OK, Category::Normal),
-        (m_smallest_value, m_smallest_value, "0x0p+0", Status::OK, Category::Zero),
-        (m_smallest_value, p_smallest_normalized, "-0x1.000002p-126", Status::OK, Category::Normal),
-        (m_smallest_value, m_smallest_normalized, "0x1.fffffcp-127", Status::OK, Category::Normal),
-        (p_smallest_normalized, p_inf, "-inf", Status::OK, Category::Infinity),
-        (p_smallest_normalized, m_inf, "inf", Status::OK, Category::Infinity),
-        (p_smallest_normalized, p_zero, "0x1p-126", Status::OK, Category::Normal),
-        (p_smallest_normalized, m_zero, "0x1p-126", Status::OK, Category::Normal),
-        (p_smallest_normalized, qnan, "-nan", Status::OK, Category::NaN),
-        /*
-        // See Note 1.
-        (p_smallest_normalized, snan, "-nan", Status::INVALID_OP, Category::NaN),
-                */
-        (p_smallest_normalized, p_normal_value, "-0x1p+0", Status::INEXACT, Category::Normal),
-        (p_smallest_normalized, m_normal_value, "0x1p+0", Status::INEXACT, Category::Normal),
-        (
-            p_smallest_normalized,
-            p_largest_value,
-            "-0x1.fffffep+127",
-            Status::INEXACT,
-            Category::Normal,
-        ),
-        (
-            p_smallest_normalized,
-            m_largest_value,
-            "0x1.fffffep+127",
-            Status::INEXACT,
-            Category::Normal,
-        ),
-        (p_smallest_normalized, p_smallest_value, "0x1.fffffcp-127", Status::OK, Category::Normal),
-        (p_smallest_normalized, m_smallest_value, "0x1.000002p-126", Status::OK, Category::Normal),
-        (p_smallest_normalized, p_smallest_normalized, "0x0p+0", Status::OK, Category::Zero),
-        (p_smallest_normalized, m_smallest_normalized, "0x1p-125", Status::OK, Category::Normal),
-        (m_smallest_normalized, p_inf, "-inf", Status::OK, Category::Infinity),
-        (m_smallest_normalized, m_inf, "inf", Status::OK, Category::Infinity),
-        (m_smallest_normalized, p_zero, "-0x1p-126", Status::OK, Category::Normal),
-        (m_smallest_normalized, m_zero, "-0x1p-126", Status::OK, Category::Normal),
-        (m_smallest_normalized, qnan, "-nan", Status::OK, Category::NaN),
-        /*
-        // See Note 1.
-        (m_smallest_normalized, snan, "-nan", Status::INVALID_OP, Category::NaN),
-                */
-        (m_smallest_normalized, p_normal_value, "-0x1p+0", Status::INEXACT, Category::Normal),
-        (m_smallest_normalized, m_normal_value, "0x1p+0", Status::INEXACT, Category::Normal),
-        (
-            m_smallest_normalized,
-            p_largest_value,
-            "-0x1.fffffep+127",
-            Status::INEXACT,
-            Category::Normal,
-        ),
-        (
-            m_smallest_normalized,
-            m_largest_value,
-            "0x1.fffffep+127",
-            Status::INEXACT,
-            Category::Normal,
-        ),
-        (m_smallest_normalized, p_smallest_value, "-0x1.000002p-126", Status::OK, Category::Normal),
-        (m_smallest_normalized, m_smallest_value, "-0x1.fffffcp-127", Status::OK, Category::Normal),
-        (m_smallest_normalized, p_smallest_normalized, "-0x1p-125", Status::OK, Category::Normal),
-        (m_smallest_normalized, m_smallest_normalized, "0x0p+0", Status::OK, Category::Zero),
-    ];
-
-    for (x, y, e_result, e_status, e_category) in special_cases {
-        let status;
-        let result = unpack!(status=, x - y);
-        assert_eq!(status, e_status);
-        assert_eq!(result.category(), e_category);
-        assert!(result.bitwise_eq(e_result.parse::<Single>().unwrap()));
-    }
-}
-
-#[test]
-fn multiply() {
-    // Test Special Cases against each other and normal values.
-
-    // FIXMES/NOTES:
-    // 1. Since we perform only default exception handling all operations with
-    // signaling NaNs should have a result that is a quiet NaN. Currently they
-    // return sNaN.
-
-    let p_inf = Single::INFINITY;
-    let m_inf = -Single::INFINITY;
-    let p_zero = Single::ZERO;
-    let m_zero = -Single::ZERO;
-    let qnan = Single::NAN;
-    let p_normal_value = "0x1p+0".parse::<Single>().unwrap();
-    let m_normal_value = "-0x1p+0".parse::<Single>().unwrap();
-    let p_largest_value = Single::largest();
-    let m_largest_value = -Single::largest();
-    let p_smallest_value = Single::SMALLEST;
-    let m_smallest_value = -Single::SMALLEST;
-    let p_smallest_normalized = Single::smallest_normalized();
-    let m_smallest_normalized = -Single::smallest_normalized();
-
-    let overflow_status = Status::OVERFLOW | Status::INEXACT;
-    let underflow_status = Status::UNDERFLOW | Status::INEXACT;
-
-    let special_cases = [
-        (p_inf, p_inf, "inf", Status::OK, Category::Infinity),
-        (p_inf, m_inf, "-inf", Status::OK, Category::Infinity),
-        (p_inf, p_zero, "nan", Status::INVALID_OP, Category::NaN),
-        (p_inf, m_zero, "nan", Status::INVALID_OP, Category::NaN),
-        (p_inf, qnan, "nan", Status::OK, Category::NaN),
-        /*
-        // See Note 1.
-        (p_inf, snan, "nan", Status::INVALID_OP, Category::NaN),
-                */
-        (p_inf, p_normal_value, "inf", Status::OK, Category::Infinity),
-        (p_inf, m_normal_value, "-inf", Status::OK, Category::Infinity),
-        (p_inf, p_largest_value, "inf", Status::OK, Category::Infinity),
-        (p_inf, m_largest_value, "-inf", Status::OK, Category::Infinity),
-        (p_inf, p_smallest_value, "inf", Status::OK, Category::Infinity),
-        (p_inf, m_smallest_value, "-inf", Status::OK, Category::Infinity),
-        (p_inf, p_smallest_normalized, "inf", Status::OK, Category::Infinity),
-        (p_inf, m_smallest_normalized, "-inf", Status::OK, Category::Infinity),
-        (m_inf, p_inf, "-inf", Status::OK, Category::Infinity),
-        (m_inf, m_inf, "inf", Status::OK, Category::Infinity),
-        (m_inf, p_zero, "nan", Status::INVALID_OP, Category::NaN),
-        (m_inf, m_zero, "nan", Status::INVALID_OP, Category::NaN),
-        (m_inf, qnan, "nan", Status::OK, Category::NaN),
-        /*
-        // See Note 1.
-        (m_inf, snan, "nan", Status::INVALID_OP, Category::NaN),
-                */
-        (m_inf, p_normal_value, "-inf", Status::OK, Category::Infinity),
-        (m_inf, m_normal_value, "inf", Status::OK, Category::Infinity),
-        (m_inf, p_largest_value, "-inf", Status::OK, Category::Infinity),
-        (m_inf, m_largest_value, "inf", Status::OK, Category::Infinity),
-        (m_inf, p_smallest_value, "-inf", Status::OK, Category::Infinity),
-        (m_inf, m_smallest_value, "inf", Status::OK, Category::Infinity),
-        (m_inf, p_smallest_normalized, "-inf", Status::OK, Category::Infinity),
-        (m_inf, m_smallest_normalized, "inf", Status::OK, Category::Infinity),
-        (p_zero, p_inf, "nan", Status::INVALID_OP, Category::NaN),
-        (p_zero, m_inf, "nan", Status::INVALID_OP, Category::NaN),
-        (p_zero, p_zero, "0x0p+0", Status::OK, Category::Zero),
-        (p_zero, m_zero, "-0x0p+0", Status::OK, Category::Zero),
-        (p_zero, qnan, "nan", Status::OK, Category::NaN),
-        /*
-        // See Note 1.
-        (p_zero, snan, "nan", Status::INVALID_OP, Category::NaN),
-                */
-        (p_zero, p_normal_value, "0x0p+0", Status::OK, Category::Zero),
-        (p_zero, m_normal_value, "-0x0p+0", Status::OK, Category::Zero),
-        (p_zero, p_largest_value, "0x0p+0", Status::OK, Category::Zero),
-        (p_zero, m_largest_value, "-0x0p+0", Status::OK, Category::Zero),
-        (p_zero, p_smallest_value, "0x0p+0", Status::OK, Category::Zero),
-        (p_zero, m_smallest_value, "-0x0p+0", Status::OK, Category::Zero),
-        (p_zero, p_smallest_normalized, "0x0p+0", Status::OK, Category::Zero),
-        (p_zero, m_smallest_normalized, "-0x0p+0", Status::OK, Category::Zero),
-        (m_zero, p_inf, "nan", Status::INVALID_OP, Category::NaN),
-        (m_zero, m_inf, "nan", Status::INVALID_OP, Category::NaN),
-        (m_zero, p_zero, "-0x0p+0", Status::OK, Category::Zero),
-        (m_zero, m_zero, "0x0p+0", Status::OK, Category::Zero),
-        (m_zero, qnan, "nan", Status::OK, Category::NaN),
-        /*
-        // See Note 1.
-        (m_zero, snan, "nan", Status::INVALID_OP, Category::NaN),
-                */
-        (m_zero, p_normal_value, "-0x0p+0", Status::OK, Category::Zero),
-        (m_zero, m_normal_value, "0x0p+0", Status::OK, Category::Zero),
-        (m_zero, p_largest_value, "-0x0p+0", Status::OK, Category::Zero),
-        (m_zero, m_largest_value, "0x0p+0", Status::OK, Category::Zero),
-        (m_zero, p_smallest_value, "-0x0p+0", Status::OK, Category::Zero),
-        (m_zero, m_smallest_value, "0x0p+0", Status::OK, Category::Zero),
-        (m_zero, p_smallest_normalized, "-0x0p+0", Status::OK, Category::Zero),
-        (m_zero, m_smallest_normalized, "0x0p+0", Status::OK, Category::Zero),
-        (qnan, p_inf, "nan", Status::OK, Category::NaN),
-        (qnan, m_inf, "nan", Status::OK, Category::NaN),
-        (qnan, p_zero, "nan", Status::OK, Category::NaN),
-        (qnan, m_zero, "nan", Status::OK, Category::NaN),
-        (qnan, qnan, "nan", Status::OK, Category::NaN),
-        /*
-        // See Note 1.
-        (qnan, snan, "nan", Status::INVALID_OP, Category::NaN),
-                */
-        (qnan, p_normal_value, "nan", Status::OK, Category::NaN),
-        (qnan, m_normal_value, "nan", Status::OK, Category::NaN),
-        (qnan, p_largest_value, "nan", Status::OK, Category::NaN),
-        (qnan, m_largest_value, "nan", Status::OK, Category::NaN),
-        (qnan, p_smallest_value, "nan", Status::OK, Category::NaN),
-        (qnan, m_smallest_value, "nan", Status::OK, Category::NaN),
-        (qnan, p_smallest_normalized, "nan", Status::OK, Category::NaN),
-        (qnan, m_smallest_normalized, "nan", Status::OK, Category::NaN),
-        /*
-        // See Note 1.
-        (snan, p_inf, "nan", Status::INVALID_OP, Category::NaN),
-        (snan, m_inf, "nan", Status::INVALID_OP, Category::NaN),
-        (snan, p_zero, "nan", Status::INVALID_OP, Category::NaN),
-        (snan, m_zero, "nan", Status::INVALID_OP, Category::NaN),
-        (snan, qnan, "nan", Status::INVALID_OP, Category::NaN),
-        (snan, snan, "nan", Status::INVALID_OP, Category::NaN),
-        (snan, p_normal_value, "nan", Status::INVALID_OP, Category::NaN),
-        (snan, m_normal_value, "nan", Status::INVALID_OP, Category::NaN),
-        (snan, p_largest_value, "nan", Status::INVALID_OP, Category::NaN),
-        (snan, m_largest_value, "nan", Status::INVALID_OP, Category::NaN),
-        (snan, p_smallest_value, "nan", Status::INVALID_OP, Category::NaN),
-        (snan, m_smallest_value, "nan", Status::INVALID_OP, Category::NaN),
-        (snan, p_smallest_normalized, "nan", Status::INVALID_OP, Category::NaN),
-        (snan, m_smallest_normalized, "nan", Status::INVALID_OP, Category::NaN),
-                */
-        (p_normal_value, p_inf, "inf", Status::OK, Category::Infinity),
-        (p_normal_value, m_inf, "-inf", Status::OK, Category::Infinity),
-        (p_normal_value, p_zero, "0x0p+0", Status::OK, Category::Zero),
-        (p_normal_value, m_zero, "-0x0p+0", Status::OK, Category::Zero),
-        (p_normal_value, qnan, "nan", Status::OK, Category::NaN),
-        /*
-        // See Note 1.
-        (p_normal_value, snan, "nan", Status::INVALID_OP, Category::NaN),
-                */
-        (p_normal_value, p_normal_value, "0x1p+0", Status::OK, Category::Normal),
-        (p_normal_value, m_normal_value, "-0x1p+0", Status::OK, Category::Normal),
-        (p_normal_value, p_largest_value, "0x1.fffffep+127", Status::OK, Category::Normal),
-        (p_normal_value, m_largest_value, "-0x1.fffffep+127", Status::OK, Category::Normal),
-        (p_normal_value, p_smallest_value, "0x1p-149", Status::OK, Category::Normal),
-        (p_normal_value, m_smallest_value, "-0x1p-149", Status::OK, Category::Normal),
-        (p_normal_value, p_smallest_normalized, "0x1p-126", Status::OK, Category::Normal),
-        (p_normal_value, m_smallest_normalized, "-0x1p-126", Status::OK, Category::Normal),
-        (m_normal_value, p_inf, "-inf", Status::OK, Category::Infinity),
-        (m_normal_value, m_inf, "inf", Status::OK, Category::Infinity),
-        (m_normal_value, p_zero, "-0x0p+0", Status::OK, Category::Zero),
-        (m_normal_value, m_zero, "0x0p+0", Status::OK, Category::Zero),
-        (m_normal_value, qnan, "nan", Status::OK, Category::NaN),
-        /*
-        // See Note 1.
-        (m_normal_value, snan, "nan", Status::INVALID_OP, Category::NaN),
-                */
-        (m_normal_value, p_normal_value, "-0x1p+0", Status::OK, Category::Normal),
-        (m_normal_value, m_normal_value, "0x1p+0", Status::OK, Category::Normal),
-        (m_normal_value, p_largest_value, "-0x1.fffffep+127", Status::OK, Category::Normal),
-        (m_normal_value, m_largest_value, "0x1.fffffep+127", Status::OK, Category::Normal),
-        (m_normal_value, p_smallest_value, "-0x1p-149", Status::OK, Category::Normal),
-        (m_normal_value, m_smallest_value, "0x1p-149", Status::OK, Category::Normal),
-        (m_normal_value, p_smallest_normalized, "-0x1p-126", Status::OK, Category::Normal),
-        (m_normal_value, m_smallest_normalized, "0x1p-126", Status::OK, Category::Normal),
-        (p_largest_value, p_inf, "inf", Status::OK, Category::Infinity),
-        (p_largest_value, m_inf, "-inf", Status::OK, Category::Infinity),
-        (p_largest_value, p_zero, "0x0p+0", Status::OK, Category::Zero),
-        (p_largest_value, m_zero, "-0x0p+0", Status::OK, Category::Zero),
-        (p_largest_value, qnan, "nan", Status::OK, Category::NaN),
-        /*
-        // See Note 1.
-        (p_largest_value, snan, "nan", Status::INVALID_OP, Category::NaN),
-                */
-        (p_largest_value, p_normal_value, "0x1.fffffep+127", Status::OK, Category::Normal),
-        (p_largest_value, m_normal_value, "-0x1.fffffep+127", Status::OK, Category::Normal),
-        (p_largest_value, p_largest_value, "inf", overflow_status, Category::Infinity),
-        (p_largest_value, m_largest_value, "-inf", overflow_status, Category::Infinity),
-        (p_largest_value, p_smallest_value, "0x1.fffffep-22", Status::OK, Category::Normal),
-        (p_largest_value, m_smallest_value, "-0x1.fffffep-22", Status::OK, Category::Normal),
-        (p_largest_value, p_smallest_normalized, "0x1.fffffep+1", Status::OK, Category::Normal),
-        (p_largest_value, m_smallest_normalized, "-0x1.fffffep+1", Status::OK, Category::Normal),
-        (m_largest_value, p_inf, "-inf", Status::OK, Category::Infinity),
-        (m_largest_value, m_inf, "inf", Status::OK, Category::Infinity),
-        (m_largest_value, p_zero, "-0x0p+0", Status::OK, Category::Zero),
-        (m_largest_value, m_zero, "0x0p+0", Status::OK, Category::Zero),
-        (m_largest_value, qnan, "nan", Status::OK, Category::NaN),
-        /*
-        // See Note 1.
-        (m_largest_value, snan, "nan", Status::INVALID_OP, Category::NaN),
-                */
-        (m_largest_value, p_normal_value, "-0x1.fffffep+127", Status::OK, Category::Normal),
-        (m_largest_value, m_normal_value, "0x1.fffffep+127", Status::OK, Category::Normal),
-        (m_largest_value, p_largest_value, "-inf", overflow_status, Category::Infinity),
-        (m_largest_value, m_largest_value, "inf", overflow_status, Category::Infinity),
-        (m_largest_value, p_smallest_value, "-0x1.fffffep-22", Status::OK, Category::Normal),
-        (m_largest_value, m_smallest_value, "0x1.fffffep-22", Status::OK, Category::Normal),
-        (m_largest_value, p_smallest_normalized, "-0x1.fffffep+1", Status::OK, Category::Normal),
-        (m_largest_value, m_smallest_normalized, "0x1.fffffep+1", Status::OK, Category::Normal),
-        (p_smallest_value, p_inf, "inf", Status::OK, Category::Infinity),
-        (p_smallest_value, m_inf, "-inf", Status::OK, Category::Infinity),
-        (p_smallest_value, p_zero, "0x0p+0", Status::OK, Category::Zero),
-        (p_smallest_value, m_zero, "-0x0p+0", Status::OK, Category::Zero),
-        (p_smallest_value, qnan, "nan", Status::OK, Category::NaN),
-        /*
-        // See Note 1.
-        (p_smallest_value, snan, "nan", Status::INVALID_OP, Category::NaN),
-                */
-        (p_smallest_value, p_normal_value, "0x1p-149", Status::OK, Category::Normal),
-        (p_smallest_value, m_normal_value, "-0x1p-149", Status::OK, Category::Normal),
-        (p_smallest_value, p_largest_value, "0x1.fffffep-22", Status::OK, Category::Normal),
-        (p_smallest_value, m_largest_value, "-0x1.fffffep-22", Status::OK, Category::Normal),
-        (p_smallest_value, p_smallest_value, "0x0p+0", underflow_status, Category::Zero),
-        (p_smallest_value, m_smallest_value, "-0x0p+0", underflow_status, Category::Zero),
-        (p_smallest_value, p_smallest_normalized, "0x0p+0", underflow_status, Category::Zero),
-        (p_smallest_value, m_smallest_normalized, "-0x0p+0", underflow_status, Category::Zero),
-        (m_smallest_value, p_inf, "-inf", Status::OK, Category::Infinity),
-        (m_smallest_value, m_inf, "inf", Status::OK, Category::Infinity),
-        (m_smallest_value, p_zero, "-0x0p+0", Status::OK, Category::Zero),
-        (m_smallest_value, m_zero, "0x0p+0", Status::OK, Category::Zero),
-        (m_smallest_value, qnan, "nan", Status::OK, Category::NaN),
-        /*
-        // See Note 1.
-        (m_smallest_value, snan, "nan", Status::INVALID_OP, Category::NaN),
-                */
-        (m_smallest_value, p_normal_value, "-0x1p-149", Status::OK, Category::Normal),
-        (m_smallest_value, m_normal_value, "0x1p-149", Status::OK, Category::Normal),
-        (m_smallest_value, p_largest_value, "-0x1.fffffep-22", Status::OK, Category::Normal),
-        (m_smallest_value, m_largest_value, "0x1.fffffep-22", Status::OK, Category::Normal),
-        (m_smallest_value, p_smallest_value, "-0x0p+0", underflow_status, Category::Zero),
-        (m_smallest_value, m_smallest_value, "0x0p+0", underflow_status, Category::Zero),
-        (m_smallest_value, p_smallest_normalized, "-0x0p+0", underflow_status, Category::Zero),
-        (m_smallest_value, m_smallest_normalized, "0x0p+0", underflow_status, Category::Zero),
-        (p_smallest_normalized, p_inf, "inf", Status::OK, Category::Infinity),
-        (p_smallest_normalized, m_inf, "-inf", Status::OK, Category::Infinity),
-        (p_smallest_normalized, p_zero, "0x0p+0", Status::OK, Category::Zero),
-        (p_smallest_normalized, m_zero, "-0x0p+0", Status::OK, Category::Zero),
-        (p_smallest_normalized, qnan, "nan", Status::OK, Category::NaN),
-        /*
-        // See Note 1.
-        (p_smallest_normalized, snan, "nan", Status::INVALID_OP, Category::NaN),
-                */
-        (p_smallest_normalized, p_normal_value, "0x1p-126", Status::OK, Category::Normal),
-        (p_smallest_normalized, m_normal_value, "-0x1p-126", Status::OK, Category::Normal),
-        (p_smallest_normalized, p_largest_value, "0x1.fffffep+1", Status::OK, Category::Normal),
-        (p_smallest_normalized, m_largest_value, "-0x1.fffffep+1", Status::OK, Category::Normal),
-        (p_smallest_normalized, p_smallest_value, "0x0p+0", underflow_status, Category::Zero),
-        (p_smallest_normalized, m_smallest_value, "-0x0p+0", underflow_status, Category::Zero),
-        (p_smallest_normalized, p_smallest_normalized, "0x0p+0", underflow_status, Category::Zero),
-        (p_smallest_normalized, m_smallest_normalized, "-0x0p+0", underflow_status, Category::Zero),
-        (m_smallest_normalized, p_inf, "-inf", Status::OK, Category::Infinity),
-        (m_smallest_normalized, m_inf, "inf", Status::OK, Category::Infinity),
-        (m_smallest_normalized, p_zero, "-0x0p+0", Status::OK, Category::Zero),
-        (m_smallest_normalized, m_zero, "0x0p+0", Status::OK, Category::Zero),
-        (m_smallest_normalized, qnan, "nan", Status::OK, Category::NaN),
-        /*
-        // See Note 1.
-        (m_smallest_normalized, snan, "nan", Status::INVALID_OP, Category::NaN),
-                */
-        (m_smallest_normalized, p_normal_value, "-0x1p-126", Status::OK, Category::Normal),
-        (m_smallest_normalized, m_normal_value, "0x1p-126", Status::OK, Category::Normal),
-        (m_smallest_normalized, p_largest_value, "-0x1.fffffep+1", Status::OK, Category::Normal),
-        (m_smallest_normalized, m_largest_value, "0x1.fffffep+1", Status::OK, Category::Normal),
-        (m_smallest_normalized, p_smallest_value, "-0x0p+0", underflow_status, Category::Zero),
-        (m_smallest_normalized, m_smallest_value, "0x0p+0", underflow_status, Category::Zero),
-        (m_smallest_normalized, p_smallest_normalized, "-0x0p+0", underflow_status, Category::Zero),
-        (m_smallest_normalized, m_smallest_normalized, "0x0p+0", underflow_status, Category::Zero),
-    ];
-
-    for (x, y, e_result, e_status, e_category) in special_cases {
-        let status;
-        let result = unpack!(status=, x * y);
-        assert_eq!(status, e_status);
-        assert_eq!(result.category(), e_category);
-        assert!(result.bitwise_eq(e_result.parse::<Single>().unwrap()));
-    }
-}
-
-#[test]
-fn divide() {
-    // Test Special Cases against each other and normal values.
-
-    // FIXMES/NOTES:
-    // 1. Since we perform only default exception handling all operations with
-    // signaling NaNs should have a result that is a quiet NaN. Currently they
-    // return sNaN.
-
-    let p_inf = Single::INFINITY;
-    let m_inf = -Single::INFINITY;
-    let p_zero = Single::ZERO;
-    let m_zero = -Single::ZERO;
-    let qnan = Single::NAN;
-    let p_normal_value = "0x1p+0".parse::<Single>().unwrap();
-    let m_normal_value = "-0x1p+0".parse::<Single>().unwrap();
-    let p_largest_value = Single::largest();
-    let m_largest_value = -Single::largest();
-    let p_smallest_value = Single::SMALLEST;
-    let m_smallest_value = -Single::SMALLEST;
-    let p_smallest_normalized = Single::smallest_normalized();
-    let m_smallest_normalized = -Single::smallest_normalized();
-
-    let overflow_status = Status::OVERFLOW | Status::INEXACT;
-    let underflow_status = Status::UNDERFLOW | Status::INEXACT;
-
-    let special_cases = [
-        (p_inf, p_inf, "nan", Status::INVALID_OP, Category::NaN),
-        (p_inf, m_inf, "nan", Status::INVALID_OP, Category::NaN),
-        (p_inf, p_zero, "inf", Status::OK, Category::Infinity),
-        (p_inf, m_zero, "-inf", Status::OK, Category::Infinity),
-        (p_inf, qnan, "nan", Status::OK, Category::NaN),
-        /*
-        // See Note 1.
-        (p_inf, snan, "nan", Status::INVALID_OP, Category::NaN),
-                */
-        (p_inf, p_normal_value, "inf", Status::OK, Category::Infinity),
-        (p_inf, m_normal_value, "-inf", Status::OK, Category::Infinity),
-        (p_inf, p_largest_value, "inf", Status::OK, Category::Infinity),
-        (p_inf, m_largest_value, "-inf", Status::OK, Category::Infinity),
-        (p_inf, p_smallest_value, "inf", Status::OK, Category::Infinity),
-        (p_inf, m_smallest_value, "-inf", Status::OK, Category::Infinity),
-        (p_inf, p_smallest_normalized, "inf", Status::OK, Category::Infinity),
-        (p_inf, m_smallest_normalized, "-inf", Status::OK, Category::Infinity),
-        (m_inf, p_inf, "nan", Status::INVALID_OP, Category::NaN),
-        (m_inf, m_inf, "nan", Status::INVALID_OP, Category::NaN),
-        (m_inf, p_zero, "-inf", Status::OK, Category::Infinity),
-        (m_inf, m_zero, "inf", Status::OK, Category::Infinity),
-        (m_inf, qnan, "nan", Status::OK, Category::NaN),
-        /*
-        // See Note 1.
-        (m_inf, snan, "nan", Status::INVALID_OP, Category::NaN),
-                */
-        (m_inf, p_normal_value, "-inf", Status::OK, Category::Infinity),
-        (m_inf, m_normal_value, "inf", Status::OK, Category::Infinity),
-        (m_inf, p_largest_value, "-inf", Status::OK, Category::Infinity),
-        (m_inf, m_largest_value, "inf", Status::OK, Category::Infinity),
-        (m_inf, p_smallest_value, "-inf", Status::OK, Category::Infinity),
-        (m_inf, m_smallest_value, "inf", Status::OK, Category::Infinity),
-        (m_inf, p_smallest_normalized, "-inf", Status::OK, Category::Infinity),
-        (m_inf, m_smallest_normalized, "inf", Status::OK, Category::Infinity),
-        (p_zero, p_inf, "0x0p+0", Status::OK, Category::Zero),
-        (p_zero, m_inf, "-0x0p+0", Status::OK, Category::Zero),
-        (p_zero, p_zero, "nan", Status::INVALID_OP, Category::NaN),
-        (p_zero, m_zero, "nan", Status::INVALID_OP, Category::NaN),
-        (p_zero, qnan, "nan", Status::OK, Category::NaN),
-        /*
-        // See Note 1.
-        (p_zero, snan, "nan", Status::INVALID_OP, Category::NaN),
-                */
-        (p_zero, p_normal_value, "0x0p+0", Status::OK, Category::Zero),
-        (p_zero, m_normal_value, "-0x0p+0", Status::OK, Category::Zero),
-        (p_zero, p_largest_value, "0x0p+0", Status::OK, Category::Zero),
-        (p_zero, m_largest_value, "-0x0p+0", Status::OK, Category::Zero),
-        (p_zero, p_smallest_value, "0x0p+0", Status::OK, Category::Zero),
-        (p_zero, m_smallest_value, "-0x0p+0", Status::OK, Category::Zero),
-        (p_zero, p_smallest_normalized, "0x0p+0", Status::OK, Category::Zero),
-        (p_zero, m_smallest_normalized, "-0x0p+0", Status::OK, Category::Zero),
-        (m_zero, p_inf, "-0x0p+0", Status::OK, Category::Zero),
-        (m_zero, m_inf, "0x0p+0", Status::OK, Category::Zero),
-        (m_zero, p_zero, "nan", Status::INVALID_OP, Category::NaN),
-        (m_zero, m_zero, "nan", Status::INVALID_OP, Category::NaN),
-        (m_zero, qnan, "nan", Status::OK, Category::NaN),
-        /*
-        // See Note 1.
-        (m_zero, snan, "nan", Status::INVALID_OP, Category::NaN),
-                */
-        (m_zero, p_normal_value, "-0x0p+0", Status::OK, Category::Zero),
-        (m_zero, m_normal_value, "0x0p+0", Status::OK, Category::Zero),
-        (m_zero, p_largest_value, "-0x0p+0", Status::OK, Category::Zero),
-        (m_zero, m_largest_value, "0x0p+0", Status::OK, Category::Zero),
-        (m_zero, p_smallest_value, "-0x0p+0", Status::OK, Category::Zero),
-        (m_zero, m_smallest_value, "0x0p+0", Status::OK, Category::Zero),
-        (m_zero, p_smallest_normalized, "-0x0p+0", Status::OK, Category::Zero),
-        (m_zero, m_smallest_normalized, "0x0p+0", Status::OK, Category::Zero),
-        (qnan, p_inf, "nan", Status::OK, Category::NaN),
-        (qnan, m_inf, "nan", Status::OK, Category::NaN),
-        (qnan, p_zero, "nan", Status::OK, Category::NaN),
-        (qnan, m_zero, "nan", Status::OK, Category::NaN),
-        (qnan, qnan, "nan", Status::OK, Category::NaN),
-        /*
-        // See Note 1.
-        (qnan, snan, "nan", Status::INVALID_OP, Category::NaN),
-                */
-        (qnan, p_normal_value, "nan", Status::OK, Category::NaN),
-        (qnan, m_normal_value, "nan", Status::OK, Category::NaN),
-        (qnan, p_largest_value, "nan", Status::OK, Category::NaN),
-        (qnan, m_largest_value, "nan", Status::OK, Category::NaN),
-        (qnan, p_smallest_value, "nan", Status::OK, Category::NaN),
-        (qnan, m_smallest_value, "nan", Status::OK, Category::NaN),
-        (qnan, p_smallest_normalized, "nan", Status::OK, Category::NaN),
-        (qnan, m_smallest_normalized, "nan", Status::OK, Category::NaN),
-        /*
-        // See Note 1.
-        (snan, p_inf, "nan", Status::INVALID_OP, Category::NaN),
-        (snan, m_inf, "nan", Status::INVALID_OP, Category::NaN),
-        (snan, p_zero, "nan", Status::INVALID_OP, Category::NaN),
-        (snan, m_zero, "nan", Status::INVALID_OP, Category::NaN),
-        (snan, qnan, "nan", Status::INVALID_OP, Category::NaN),
-        (snan, snan, "nan", Status::INVALID_OP, Category::NaN),
-        (snan, p_normal_value, "nan", Status::INVALID_OP, Category::NaN),
-        (snan, m_normal_value, "nan", Status::INVALID_OP, Category::NaN),
-        (snan, p_largest_value, "nan", Status::INVALID_OP, Category::NaN),
-        (snan, m_largest_value, "nan", Status::INVALID_OP, Category::NaN),
-        (snan, p_smallest_value, "nan", Status::INVALID_OP, Category::NaN),
-        (snan, m_smallest_value, "nan", Status::INVALID_OP, Category::NaN),
-        (snan, p_smallest_normalized, "nan", Status::INVALID_OP, Category::NaN),
-        (snan, m_smallest_normalized, "nan", Status::INVALID_OP, Category::NaN),
-                */
-        (p_normal_value, p_inf, "0x0p+0", Status::OK, Category::Zero),
-        (p_normal_value, m_inf, "-0x0p+0", Status::OK, Category::Zero),
-        (p_normal_value, p_zero, "inf", Status::DIV_BY_ZERO, Category::Infinity),
-        (p_normal_value, m_zero, "-inf", Status::DIV_BY_ZERO, Category::Infinity),
-        (p_normal_value, qnan, "nan", Status::OK, Category::NaN),
-        /*
-        // See Note 1.
-        (p_normal_value, snan, "nan", Status::INVALID_OP, Category::NaN),
-                */
-        (p_normal_value, p_normal_value, "0x1p+0", Status::OK, Category::Normal),
-        (p_normal_value, m_normal_value, "-0x1p+0", Status::OK, Category::Normal),
-        (p_normal_value, p_largest_value, "0x1p-128", underflow_status, Category::Normal),
-        (p_normal_value, m_largest_value, "-0x1p-128", underflow_status, Category::Normal),
-        (p_normal_value, p_smallest_value, "inf", overflow_status, Category::Infinity),
-        (p_normal_value, m_smallest_value, "-inf", overflow_status, Category::Infinity),
-        (p_normal_value, p_smallest_normalized, "0x1p+126", Status::OK, Category::Normal),
-        (p_normal_value, m_smallest_normalized, "-0x1p+126", Status::OK, Category::Normal),
-        (m_normal_value, p_inf, "-0x0p+0", Status::OK, Category::Zero),
-        (m_normal_value, m_inf, "0x0p+0", Status::OK, Category::Zero),
-        (m_normal_value, p_zero, "-inf", Status::DIV_BY_ZERO, Category::Infinity),
-        (m_normal_value, m_zero, "inf", Status::DIV_BY_ZERO, Category::Infinity),
-        (m_normal_value, qnan, "nan", Status::OK, Category::NaN),
-        /*
-        // See Note 1.
-        (m_normal_value, snan, "nan", Status::INVALID_OP, Category::NaN),
-                */
-        (m_normal_value, p_normal_value, "-0x1p+0", Status::OK, Category::Normal),
-        (m_normal_value, m_normal_value, "0x1p+0", Status::OK, Category::Normal),
-        (m_normal_value, p_largest_value, "-0x1p-128", underflow_status, Category::Normal),
-        (m_normal_value, m_largest_value, "0x1p-128", underflow_status, Category::Normal),
-        (m_normal_value, p_smallest_value, "-inf", overflow_status, Category::Infinity),
-        (m_normal_value, m_smallest_value, "inf", overflow_status, Category::Infinity),
-        (m_normal_value, p_smallest_normalized, "-0x1p+126", Status::OK, Category::Normal),
-        (m_normal_value, m_smallest_normalized, "0x1p+126", Status::OK, Category::Normal),
-        (p_largest_value, p_inf, "0x0p+0", Status::OK, Category::Zero),
-        (p_largest_value, m_inf, "-0x0p+0", Status::OK, Category::Zero),
-        (p_largest_value, p_zero, "inf", Status::DIV_BY_ZERO, Category::Infinity),
-        (p_largest_value, m_zero, "-inf", Status::DIV_BY_ZERO, Category::Infinity),
-        (p_largest_value, qnan, "nan", Status::OK, Category::NaN),
-        /*
-        // See Note 1.
-        (p_largest_value, snan, "nan", Status::INVALID_OP, Category::NaN),
-                */
-        (p_largest_value, p_normal_value, "0x1.fffffep+127", Status::OK, Category::Normal),
-        (p_largest_value, m_normal_value, "-0x1.fffffep+127", Status::OK, Category::Normal),
-        (p_largest_value, p_largest_value, "0x1p+0", Status::OK, Category::Normal),
-        (p_largest_value, m_largest_value, "-0x1p+0", Status::OK, Category::Normal),
-        (p_largest_value, p_smallest_value, "inf", overflow_status, Category::Infinity),
-        (p_largest_value, m_smallest_value, "-inf", overflow_status, Category::Infinity),
-        (p_largest_value, p_smallest_normalized, "inf", overflow_status, Category::Infinity),
-        (p_largest_value, m_smallest_normalized, "-inf", overflow_status, Category::Infinity),
-        (m_largest_value, p_inf, "-0x0p+0", Status::OK, Category::Zero),
-        (m_largest_value, m_inf, "0x0p+0", Status::OK, Category::Zero),
-        (m_largest_value, p_zero, "-inf", Status::DIV_BY_ZERO, Category::Infinity),
-        (m_largest_value, m_zero, "inf", Status::DIV_BY_ZERO, Category::Infinity),
-        (m_largest_value, qnan, "nan", Status::OK, Category::NaN),
-        /*
-        // See Note 1.
-        (m_largest_value, snan, "nan", Status::INVALID_OP, Category::NaN),
-                */
-        (m_largest_value, p_normal_value, "-0x1.fffffep+127", Status::OK, Category::Normal),
-        (m_largest_value, m_normal_value, "0x1.fffffep+127", Status::OK, Category::Normal),
-        (m_largest_value, p_largest_value, "-0x1p+0", Status::OK, Category::Normal),
-        (m_largest_value, m_largest_value, "0x1p+0", Status::OK, Category::Normal),
-        (m_largest_value, p_smallest_value, "-inf", overflow_status, Category::Infinity),
-        (m_largest_value, m_smallest_value, "inf", overflow_status, Category::Infinity),
-        (m_largest_value, p_smallest_normalized, "-inf", overflow_status, Category::Infinity),
-        (m_largest_value, m_smallest_normalized, "inf", overflow_status, Category::Infinity),
-        (p_smallest_value, p_inf, "0x0p+0", Status::OK, Category::Zero),
-        (p_smallest_value, m_inf, "-0x0p+0", Status::OK, Category::Zero),
-        (p_smallest_value, p_zero, "inf", Status::DIV_BY_ZERO, Category::Infinity),
-        (p_smallest_value, m_zero, "-inf", Status::DIV_BY_ZERO, Category::Infinity),
-        (p_smallest_value, qnan, "nan", Status::OK, Category::NaN),
-        /*
-        // See Note 1.
-        (p_smallest_value, snan, "nan", Status::INVALID_OP, Category::NaN),
-                */
-        (p_smallest_value, p_normal_value, "0x1p-149", Status::OK, Category::Normal),
-        (p_smallest_value, m_normal_value, "-0x1p-149", Status::OK, Category::Normal),
-        (p_smallest_value, p_largest_value, "0x0p+0", underflow_status, Category::Zero),
-        (p_smallest_value, m_largest_value, "-0x0p+0", underflow_status, Category::Zero),
-        (p_smallest_value, p_smallest_value, "0x1p+0", Status::OK, Category::Normal),
-        (p_smallest_value, m_smallest_value, "-0x1p+0", Status::OK, Category::Normal),
-        (p_smallest_value, p_smallest_normalized, "0x1p-23", Status::OK, Category::Normal),
-        (p_smallest_value, m_smallest_normalized, "-0x1p-23", Status::OK, Category::Normal),
-        (m_smallest_value, p_inf, "-0x0p+0", Status::OK, Category::Zero),
-        (m_smallest_value, m_inf, "0x0p+0", Status::OK, Category::Zero),
-        (m_smallest_value, p_zero, "-inf", Status::DIV_BY_ZERO, Category::Infinity),
-        (m_smallest_value, m_zero, "inf", Status::DIV_BY_ZERO, Category::Infinity),
-        (m_smallest_value, qnan, "nan", Status::OK, Category::NaN),
-        /*
-        // See Note 1.
-        (m_smallest_value, snan, "nan", Status::INVALID_OP, Category::NaN),
-                */
-        (m_smallest_value, p_normal_value, "-0x1p-149", Status::OK, Category::Normal),
-        (m_smallest_value, m_normal_value, "0x1p-149", Status::OK, Category::Normal),
-        (m_smallest_value, p_largest_value, "-0x0p+0", underflow_status, Category::Zero),
-        (m_smallest_value, m_largest_value, "0x0p+0", underflow_status, Category::Zero),
-        (m_smallest_value, p_smallest_value, "-0x1p+0", Status::OK, Category::Normal),
-        (m_smallest_value, m_smallest_value, "0x1p+0", Status::OK, Category::Normal),
-        (m_smallest_value, p_smallest_normalized, "-0x1p-23", Status::OK, Category::Normal),
-        (m_smallest_value, m_smallest_normalized, "0x1p-23", Status::OK, Category::Normal),
-        (p_smallest_normalized, p_inf, "0x0p+0", Status::OK, Category::Zero),
-        (p_smallest_normalized, m_inf, "-0x0p+0", Status::OK, Category::Zero),
-        (p_smallest_normalized, p_zero, "inf", Status::DIV_BY_ZERO, Category::Infinity),
-        (p_smallest_normalized, m_zero, "-inf", Status::DIV_BY_ZERO, Category::Infinity),
-        (p_smallest_normalized, qnan, "nan", Status::OK, Category::NaN),
-        /*
-        // See Note 1.
-        (p_smallest_normalized, snan, "nan", Status::INVALID_OP, Category::NaN),
-                */
-        (p_smallest_normalized, p_normal_value, "0x1p-126", Status::OK, Category::Normal),
-        (p_smallest_normalized, m_normal_value, "-0x1p-126", Status::OK, Category::Normal),
-        (p_smallest_normalized, p_largest_value, "0x0p+0", underflow_status, Category::Zero),
-        (p_smallest_normalized, m_largest_value, "-0x0p+0", underflow_status, Category::Zero),
-        (p_smallest_normalized, p_smallest_value, "0x1p+23", Status::OK, Category::Normal),
-        (p_smallest_normalized, m_smallest_value, "-0x1p+23", Status::OK, Category::Normal),
-        (p_smallest_normalized, p_smallest_normalized, "0x1p+0", Status::OK, Category::Normal),
-        (p_smallest_normalized, m_smallest_normalized, "-0x1p+0", Status::OK, Category::Normal),
-        (m_smallest_normalized, p_inf, "-0x0p+0", Status::OK, Category::Zero),
-        (m_smallest_normalized, m_inf, "0x0p+0", Status::OK, Category::Zero),
-        (m_smallest_normalized, p_zero, "-inf", Status::DIV_BY_ZERO, Category::Infinity),
-        (m_smallest_normalized, m_zero, "inf", Status::DIV_BY_ZERO, Category::Infinity),
-        (m_smallest_normalized, qnan, "nan", Status::OK, Category::NaN),
-        /*
-        // See Note 1.
-        (m_smallest_normalized, snan, "nan", Status::INVALID_OP, Category::NaN),
-                */
-        (m_smallest_normalized, p_normal_value, "-0x1p-126", Status::OK, Category::Normal),
-        (m_smallest_normalized, m_normal_value, "0x1p-126", Status::OK, Category::Normal),
-        (m_smallest_normalized, p_largest_value, "-0x0p+0", underflow_status, Category::Zero),
-        (m_smallest_normalized, m_largest_value, "0x0p+0", underflow_status, Category::Zero),
-        (m_smallest_normalized, p_smallest_value, "-0x1p+23", Status::OK, Category::Normal),
-        (m_smallest_normalized, m_smallest_value, "0x1p+23", Status::OK, Category::Normal),
-        (m_smallest_normalized, p_smallest_normalized, "-0x1p+0", Status::OK, Category::Normal),
-        (m_smallest_normalized, m_smallest_normalized, "0x1p+0", Status::OK, Category::Normal),
-    ];
-
-    for (x, y, e_result, e_status, e_category) in special_cases {
-        let status;
-        let result = unpack!(status=, x / y);
-        assert_eq!(status, e_status);
-        assert_eq!(result.category(), e_category);
-        assert!(result.bitwise_eq(e_result.parse::<Single>().unwrap()));
-    }
-}
-
-#[test]
-fn operator_overloads() {
-    // This is mostly testing that these operator overloads compile.
-    let one = "0x1p+0".parse::<Single>().unwrap();
-    let two = "0x2p+0".parse::<Single>().unwrap();
-    assert!(two.bitwise_eq((one + one).value));
-    assert!(one.bitwise_eq((two - one).value));
-    assert!(two.bitwise_eq((one * two).value));
-    assert!(one.bitwise_eq((two / two).value));
-}
-
-#[test]
-fn abs() {
-    let p_inf = Single::INFINITY;
-    let m_inf = -Single::INFINITY;
-    let p_zero = Single::ZERO;
-    let m_zero = -Single::ZERO;
-    let p_qnan = Single::NAN;
-    let m_qnan = -Single::NAN;
-    let p_snan = Single::snan(None);
-    let m_snan = -Single::snan(None);
-    let p_normal_value = "0x1p+0".parse::<Single>().unwrap();
-    let m_normal_value = "-0x1p+0".parse::<Single>().unwrap();
-    let p_largest_value = Single::largest();
-    let m_largest_value = -Single::largest();
-    let p_smallest_value = Single::SMALLEST;
-    let m_smallest_value = -Single::SMALLEST;
-    let p_smallest_normalized = Single::smallest_normalized();
-    let m_smallest_normalized = -Single::smallest_normalized();
-
-    assert!(p_inf.bitwise_eq(p_inf.abs()));
-    assert!(p_inf.bitwise_eq(m_inf.abs()));
-    assert!(p_zero.bitwise_eq(p_zero.abs()));
-    assert!(p_zero.bitwise_eq(m_zero.abs()));
-    assert!(p_qnan.bitwise_eq(p_qnan.abs()));
-    assert!(p_qnan.bitwise_eq(m_qnan.abs()));
-    assert!(p_snan.bitwise_eq(p_snan.abs()));
-    assert!(p_snan.bitwise_eq(m_snan.abs()));
-    assert!(p_normal_value.bitwise_eq(p_normal_value.abs()));
-    assert!(p_normal_value.bitwise_eq(m_normal_value.abs()));
-    assert!(p_largest_value.bitwise_eq(p_largest_value.abs()));
-    assert!(p_largest_value.bitwise_eq(m_largest_value.abs()));
-    assert!(p_smallest_value.bitwise_eq(p_smallest_value.abs()));
-    assert!(p_smallest_value.bitwise_eq(m_smallest_value.abs()));
-    assert!(p_smallest_normalized.bitwise_eq(p_smallest_normalized.abs(),));
-    assert!(p_smallest_normalized.bitwise_eq(m_smallest_normalized.abs(),));
-}
-
-#[test]
-fn neg() {
-    let one = "1.0".parse::<Single>().unwrap();
-    let neg_one = "-1.0".parse::<Single>().unwrap();
-    let zero = Single::ZERO;
-    let neg_zero = -Single::ZERO;
-    let inf = Single::INFINITY;
-    let neg_inf = -Single::INFINITY;
-    let qnan = Single::NAN;
-    let neg_qnan = -Single::NAN;
-
-    assert!(neg_one.bitwise_eq(-one));
-    assert!(one.bitwise_eq(-neg_one));
-    assert!(neg_zero.bitwise_eq(-zero));
-    assert!(zero.bitwise_eq(-neg_zero));
-    assert!(neg_inf.bitwise_eq(-inf));
-    assert!(inf.bitwise_eq(-neg_inf));
-    assert!(neg_inf.bitwise_eq(-inf));
-    assert!(inf.bitwise_eq(-neg_inf));
-    assert!(neg_qnan.bitwise_eq(-qnan));
-    assert!(qnan.bitwise_eq(-neg_qnan));
-}
-
-#[test]
-fn ilogb() {
-    assert_eq!(-1074, Double::SMALLEST.ilogb());
-    assert_eq!(-1074, (-Double::SMALLEST).ilogb());
-    assert_eq!(-1023, "0x1.ffffffffffffep-1024".parse::<Double>().unwrap().ilogb());
-    assert_eq!(-1023, "0x1.ffffffffffffep-1023".parse::<Double>().unwrap().ilogb());
-    assert_eq!(-1023, "-0x1.ffffffffffffep-1023".parse::<Double>().unwrap().ilogb());
-    assert_eq!(-51, "0x1p-51".parse::<Double>().unwrap().ilogb());
-    assert_eq!(-1023, "0x1.c60f120d9f87cp-1023".parse::<Double>().unwrap().ilogb());
-    assert_eq!(-2, "0x0.ffffp-1".parse::<Double>().unwrap().ilogb());
-    assert_eq!(-1023, "0x1.fffep-1023".parse::<Double>().unwrap().ilogb());
-    assert_eq!(1023, Double::largest().ilogb());
-    assert_eq!(1023, (-Double::largest()).ilogb());
-
-    assert_eq!(0, "0x1p+0".parse::<Single>().unwrap().ilogb());
-    assert_eq!(0, "-0x1p+0".parse::<Single>().unwrap().ilogb());
-    assert_eq!(42, "0x1p+42".parse::<Single>().unwrap().ilogb());
-    assert_eq!(-42, "0x1p-42".parse::<Single>().unwrap().ilogb());
-
-    assert_eq!(IEK_INF, Single::INFINITY.ilogb());
-    assert_eq!(IEK_INF, (-Single::INFINITY).ilogb());
-    assert_eq!(IEK_ZERO, Single::ZERO.ilogb());
-    assert_eq!(IEK_ZERO, (-Single::ZERO).ilogb());
-    assert_eq!(IEK_NAN, Single::NAN.ilogb());
-    assert_eq!(IEK_NAN, Single::snan(None).ilogb());
-
-    assert_eq!(127, Single::largest().ilogb());
-    assert_eq!(127, (-Single::largest()).ilogb());
-
-    assert_eq!(-149, Single::SMALLEST.ilogb());
-    assert_eq!(-149, (-Single::SMALLEST).ilogb());
-    assert_eq!(-126, Single::smallest_normalized().ilogb());
-    assert_eq!(-126, (-Single::smallest_normalized()).ilogb());
-}
-
-#[test]
-fn scalbn() {
-    assert!(
-        "0x1p+0"
-            .parse::<Single>()
-            .unwrap()
-            .bitwise_eq("0x1p+0".parse::<Single>().unwrap().scalbn(0),)
-    );
-    assert!(
-        "0x1p+42"
-            .parse::<Single>()
-            .unwrap()
-            .bitwise_eq("0x1p+0".parse::<Single>().unwrap().scalbn(42),)
-    );
-    assert!(
-        "0x1p-42"
-            .parse::<Single>()
-            .unwrap()
-            .bitwise_eq("0x1p+0".parse::<Single>().unwrap().scalbn(-42),)
-    );
-
-    let p_inf = Single::INFINITY;
-    let m_inf = -Single::INFINITY;
-    let p_zero = Single::ZERO;
-    let m_zero = -Single::ZERO;
-    let p_qnan = Single::NAN;
-    let m_qnan = -Single::NAN;
-    let snan = Single::snan(None);
-
-    assert!(p_inf.bitwise_eq(p_inf.scalbn(0)));
-    assert!(m_inf.bitwise_eq(m_inf.scalbn(0)));
-    assert!(p_zero.bitwise_eq(p_zero.scalbn(0)));
-    assert!(m_zero.bitwise_eq(m_zero.scalbn(0)));
-    assert!(p_qnan.bitwise_eq(p_qnan.scalbn(0)));
-    assert!(m_qnan.bitwise_eq(m_qnan.scalbn(0)));
-    assert!(!snan.scalbn(0).is_signaling());
-
-    let scalbn_snan = snan.scalbn(1);
-    assert!(scalbn_snan.is_nan() && !scalbn_snan.is_signaling());
-
-    // Make sure highest bit of payload is preserved.
-    let payload = (1 << 50) | (1 << 49) | (1234 << 32) | 1;
-
-    let snan_with_payload = Double::snan(Some(payload));
-    let quiet_payload = snan_with_payload.scalbn(1);
-    assert!(quiet_payload.is_nan() && !quiet_payload.is_signaling());
-    assert_eq!(payload, quiet_payload.to_bits() & ((1 << 51) - 1));
-
-    assert!(p_inf.bitwise_eq("0x1p+0".parse::<Single>().unwrap().scalbn(128),));
-    assert!(m_inf.bitwise_eq("-0x1p+0".parse::<Single>().unwrap().scalbn(128),));
-    assert!(p_inf.bitwise_eq("0x1p+127".parse::<Single>().unwrap().scalbn(1),));
-    assert!(p_zero.bitwise_eq("0x1p-127".parse::<Single>().unwrap().scalbn(-127),));
-    assert!(m_zero.bitwise_eq("-0x1p-127".parse::<Single>().unwrap().scalbn(-127),));
-    assert!(
-        "-0x1p-149"
-            .parse::<Single>()
-            .unwrap()
-            .bitwise_eq("-0x1p-127".parse::<Single>().unwrap().scalbn(-22),)
-    );
-    assert!(p_zero.bitwise_eq("0x1p-126".parse::<Single>().unwrap().scalbn(-24),));
-
-    let smallest_f64 = Double::SMALLEST;
-    let neg_smallest_f64 = -Double::SMALLEST;
-
-    let largest_f64 = Double::largest();
-    let neg_largest_f64 = -Double::largest();
-
-    let largest_denormal_f64 = "0x1.ffffffffffffep-1023".parse::<Double>().unwrap();
-    let neg_largest_denormal_f64 = "-0x1.ffffffffffffep-1023".parse::<Double>().unwrap();
-
-    assert!(smallest_f64.bitwise_eq("0x1p-1074".parse::<Double>().unwrap().scalbn(0),));
-    assert!(neg_smallest_f64.bitwise_eq("-0x1p-1074".parse::<Double>().unwrap().scalbn(0),));
-
-    assert!("0x1p+1023".parse::<Double>().unwrap().bitwise_eq(smallest_f64.scalbn(2097,),));
-
-    assert!(smallest_f64.scalbn(-2097).is_pos_zero());
-    assert!(smallest_f64.scalbn(-2098).is_pos_zero());
-    assert!(smallest_f64.scalbn(-2099).is_pos_zero());
-    assert!("0x1p+1022".parse::<Double>().unwrap().bitwise_eq(smallest_f64.scalbn(2096,),));
-    assert!("0x1p+1023".parse::<Double>().unwrap().bitwise_eq(smallest_f64.scalbn(2097,),));
-    assert!(smallest_f64.scalbn(2098).is_infinite());
-    assert!(smallest_f64.scalbn(2099).is_infinite());
-
-    // Test for integer overflows when adding to exponent.
-    assert!(smallest_f64.scalbn(-ExpInt::MAX).is_pos_zero());
-    assert!(largest_f64.scalbn(ExpInt::MAX).is_infinite());
-
-    assert!(largest_denormal_f64.bitwise_eq(largest_denormal_f64.scalbn(0),));
-    assert!(neg_largest_denormal_f64.bitwise_eq(neg_largest_denormal_f64.scalbn(0),));
-
-    assert!(
-        "0x1.ffffffffffffep-1022"
-            .parse::<Double>()
-            .unwrap()
-            .bitwise_eq(largest_denormal_f64.scalbn(1))
-    );
-    assert!(
-        "-0x1.ffffffffffffep-1021"
-            .parse::<Double>()
-            .unwrap()
-            .bitwise_eq(neg_largest_denormal_f64.scalbn(2))
-    );
-
-    assert!(
-        "0x1.ffffffffffffep+1"
-            .parse::<Double>()
-            .unwrap()
-            .bitwise_eq(largest_denormal_f64.scalbn(1024))
-    );
-    assert!(largest_denormal_f64.scalbn(-1023).is_pos_zero());
-    assert!(largest_denormal_f64.scalbn(-1024).is_pos_zero());
-    assert!(largest_denormal_f64.scalbn(-2048).is_pos_zero());
-    assert!(largest_denormal_f64.scalbn(2047).is_infinite());
-    assert!(largest_denormal_f64.scalbn(2098).is_infinite());
-    assert!(largest_denormal_f64.scalbn(2099).is_infinite());
-
-    assert!(
-        "0x1.ffffffffffffep-2"
-            .parse::<Double>()
-            .unwrap()
-            .bitwise_eq(largest_denormal_f64.scalbn(1021))
-    );
-    assert!(
-        "0x1.ffffffffffffep-1"
-            .parse::<Double>()
-            .unwrap()
-            .bitwise_eq(largest_denormal_f64.scalbn(1022))
-    );
-    assert!(
-        "0x1.ffffffffffffep+0"
-            .parse::<Double>()
-            .unwrap()
-            .bitwise_eq(largest_denormal_f64.scalbn(1023))
-    );
-    assert!(
-        "0x1.ffffffffffffep+1023"
-            .parse::<Double>()
-            .unwrap()
-            .bitwise_eq(largest_denormal_f64.scalbn(2046))
-    );
-    assert!("0x1p+974".parse::<Double>().unwrap().bitwise_eq(smallest_f64.scalbn(2048,),));
-
-    let random_denormal_f64 = "0x1.c60f120d9f87cp+51".parse::<Double>().unwrap();
-    assert!(
-        "0x1.c60f120d9f87cp-972"
-            .parse::<Double>()
-            .unwrap()
-            .bitwise_eq(random_denormal_f64.scalbn(-1023))
-    );
-    assert!(
-        "0x1.c60f120d9f87cp-1"
-            .parse::<Double>()
-            .unwrap()
-            .bitwise_eq(random_denormal_f64.scalbn(-52))
-    );
-    assert!(
-        "0x1.c60f120d9f87cp-2"
-            .parse::<Double>()
-            .unwrap()
-            .bitwise_eq(random_denormal_f64.scalbn(-53))
-    );
-    assert!(
-        "0x1.c60f120d9f87cp+0"
-            .parse::<Double>()
-            .unwrap()
-            .bitwise_eq(random_denormal_f64.scalbn(-51))
-    );
-
-    assert!(random_denormal_f64.scalbn(-2097).is_pos_zero());
-    assert!(random_denormal_f64.scalbn(-2090).is_pos_zero());
-
-    assert!("-0x1p-1073".parse::<Double>().unwrap().bitwise_eq(neg_largest_f64.scalbn(-2097),));
-
-    assert!("-0x1p-1024".parse::<Double>().unwrap().bitwise_eq(neg_largest_f64.scalbn(-2048),));
-
-    assert!("0x1p-1073".parse::<Double>().unwrap().bitwise_eq(largest_f64.scalbn(-2097,),));
-
-    assert!("0x1p-1074".parse::<Double>().unwrap().bitwise_eq(largest_f64.scalbn(-2098,),));
-    assert!("-0x1p-1074".parse::<Double>().unwrap().bitwise_eq(neg_largest_f64.scalbn(-2098),));
-    assert!(neg_largest_f64.scalbn(-2099).is_neg_zero());
-    assert!(largest_f64.scalbn(1).is_infinite());
-
-    assert!(
-        "0x1p+0"
-            .parse::<Double>()
-            .unwrap()
-            .bitwise_eq("0x1p+52".parse::<Double>().unwrap().scalbn(-52),)
-    );
-
-    assert!(
-        "0x1p-103"
-            .parse::<Double>()
-            .unwrap()
-            .bitwise_eq("0x1p-51".parse::<Double>().unwrap().scalbn(-52),)
-    );
-}
-
-#[test]
-fn frexp() {
-    let p_zero = Double::ZERO;
-    let m_zero = -Double::ZERO;
-    let one = Double::from_f64(1.0);
-    let m_one = Double::from_f64(-1.0);
-
-    let largest_denormal = "0x1.ffffffffffffep-1023".parse::<Double>().unwrap();
-    let neg_largest_denormal = "-0x1.ffffffffffffep-1023".parse::<Double>().unwrap();
-
-    let smallest = Double::SMALLEST;
-    let neg_smallest = -Double::SMALLEST;
-
-    let largest = Double::largest();
-    let neg_largest = -Double::largest();
-
-    let p_inf = Double::INFINITY;
-    let m_inf = -Double::INFINITY;
-
-    let p_qnan = Double::NAN;
-    let m_qnan = -Double::NAN;
-    let snan = Double::snan(None);
-
-    // Make sure highest bit of payload is preserved.
-    let payload = (1 << 50) | (1 << 49) | (1234 << 32) | 1;
-
-    let snan_with_payload = Double::snan(Some(payload));
-
-    let mut exp = 0;
-
-    let frac = p_zero.frexp(&mut exp);
-    assert_eq!(0, exp);
-    assert!(frac.is_pos_zero());
-
-    let frac = m_zero.frexp(&mut exp);
-    assert_eq!(0, exp);
-    assert!(frac.is_neg_zero());
-
-    let frac = one.frexp(&mut exp);
-    assert_eq!(1, exp);
-    assert!("0x1p-1".parse::<Double>().unwrap().bitwise_eq(frac));
-
-    let frac = m_one.frexp(&mut exp);
-    assert_eq!(1, exp);
-    assert!("-0x1p-1".parse::<Double>().unwrap().bitwise_eq(frac));
-
-    let frac = largest_denormal.frexp(&mut exp);
-    assert_eq!(-1022, exp);
-    assert!("0x1.ffffffffffffep-1".parse::<Double>().unwrap().bitwise_eq(frac));
-
-    let frac = neg_largest_denormal.frexp(&mut exp);
-    assert_eq!(-1022, exp);
-    assert!("-0x1.ffffffffffffep-1".parse::<Double>().unwrap().bitwise_eq(frac));
-
-    let frac = smallest.frexp(&mut exp);
-    assert_eq!(-1073, exp);
-    assert!("0x1p-1".parse::<Double>().unwrap().bitwise_eq(frac));
-
-    let frac = neg_smallest.frexp(&mut exp);
-    assert_eq!(-1073, exp);
-    assert!("-0x1p-1".parse::<Double>().unwrap().bitwise_eq(frac));
-
-    let frac = largest.frexp(&mut exp);
-    assert_eq!(1024, exp);
-    assert!("0x1.fffffffffffffp-1".parse::<Double>().unwrap().bitwise_eq(frac));
-
-    let frac = neg_largest.frexp(&mut exp);
-    assert_eq!(1024, exp);
-    assert!("-0x1.fffffffffffffp-1".parse::<Double>().unwrap().bitwise_eq(frac));
-
-    let frac = p_inf.frexp(&mut exp);
-    assert_eq!(IEK_INF, exp);
-    assert!(frac.is_infinite() && !frac.is_negative());
-
-    let frac = m_inf.frexp(&mut exp);
-    assert_eq!(IEK_INF, exp);
-    assert!(frac.is_infinite() && frac.is_negative());
-
-    let frac = p_qnan.frexp(&mut exp);
-    assert_eq!(IEK_NAN, exp);
-    assert!(frac.is_nan());
-
-    let frac = m_qnan.frexp(&mut exp);
-    assert_eq!(IEK_NAN, exp);
-    assert!(frac.is_nan());
-
-    let frac = snan.frexp(&mut exp);
-    assert_eq!(IEK_NAN, exp);
-    assert!(frac.is_nan() && !frac.is_signaling());
-
-    let frac = snan_with_payload.frexp(&mut exp);
-    assert_eq!(IEK_NAN, exp);
-    assert!(frac.is_nan() && !frac.is_signaling());
-    assert_eq!(payload, frac.to_bits() & ((1 << 51) - 1));
-
-    let frac = "0x0.ffffp-1".parse::<Double>().unwrap().frexp(&mut exp);
-    assert_eq!(-1, exp);
-    assert!("0x1.fffep-1".parse::<Double>().unwrap().bitwise_eq(frac));
-
-    let frac = "0x1p-51".parse::<Double>().unwrap().frexp(&mut exp);
-    assert_eq!(-50, exp);
-    assert!("0x1p-1".parse::<Double>().unwrap().bitwise_eq(frac));
-
-    let frac = "0x1.c60f120d9f87cp+51".parse::<Double>().unwrap().frexp(&mut exp);
-    assert_eq!(52, exp);
-    assert!("0x1.c60f120d9f87cp-1".parse::<Double>().unwrap().bitwise_eq(frac));
-}
-
-#[test]
-fn modulo() {
-    let mut status;
-    {
-        let f1 = "1.5".parse::<Double>().unwrap();
-        let f2 = "1.0".parse::<Double>().unwrap();
-        let expected = "0.5".parse::<Double>().unwrap();
-        assert!(unpack!(status=, f1 % f2).bitwise_eq(expected));
-        assert_eq!(status, Status::OK);
-    }
-    {
-        let f1 = "0.5".parse::<Double>().unwrap();
-        let f2 = "1.0".parse::<Double>().unwrap();
-        let expected = "0.5".parse::<Double>().unwrap();
-        assert!(unpack!(status=, f1 % f2).bitwise_eq(expected));
-        assert_eq!(status, Status::OK);
-    }
-    {
-        let f1 = "0x1.3333333333333p-2".parse::<Double>().unwrap(); // 0.3
-        let f2 = "0x1.47ae147ae147bp-7".parse::<Double>().unwrap(); // 0.01
-        // 0.009999999999999983
-        let expected = "0x1.47ae147ae1471p-7".parse::<Double>().unwrap();
-        assert!(unpack!(status=, f1 % f2).bitwise_eq(expected));
-        assert_eq!(status, Status::OK);
-    }
-    {
-        let f1 = "0x1p64".parse::<Double>().unwrap(); // 1.8446744073709552e19
-        let f2 = "1.5".parse::<Double>().unwrap();
-        let expected = "1.0".parse::<Double>().unwrap();
-        assert!(unpack!(status=, f1 % f2).bitwise_eq(expected));
-        assert_eq!(status, Status::OK);
-    }
-    {
-        let f1 = "0x1p1000".parse::<Double>().unwrap();
-        let f2 = "0x1p-1000".parse::<Double>().unwrap();
-        let expected = "0.0".parse::<Double>().unwrap();
-        assert!(unpack!(status=, f1 % f2).bitwise_eq(expected));
-        assert_eq!(status, Status::OK);
-    }
-    {
-        let f1 = "0.0".parse::<Double>().unwrap();
-        let f2 = "1.0".parse::<Double>().unwrap();
-        let expected = "0.0".parse::<Double>().unwrap();
-        assert!(unpack!(status=, f1 % f2).bitwise_eq(expected));
-        assert_eq!(status, Status::OK);
-    }
-    {
-        let f1 = "1.0".parse::<Double>().unwrap();
-        let f2 = "0.0".parse::<Double>().unwrap();
-        assert!(unpack!(status=, f1 % f2).is_nan());
-        assert_eq!(status, Status::INVALID_OP);
-    }
-    {
-        let f1 = "0.0".parse::<Double>().unwrap();
-        let f2 = "0.0".parse::<Double>().unwrap();
-        assert!(unpack!(status=, f1 % f2).is_nan());
-        assert_eq!(status, Status::INVALID_OP);
-    }
-    {
-        let f1 = Double::INFINITY;
-        let f2 = "1.0".parse::<Double>().unwrap();
-        assert!(unpack!(status=, f1 % f2).is_nan());
-        assert_eq!(status, Status::INVALID_OP);
-    }
-}
diff --git a/compiler/rustc_apfloat/tests/ppc.rs b/compiler/rustc_apfloat/tests/ppc.rs
deleted file mode 100644
index c769d265437..00000000000
--- a/compiler/rustc_apfloat/tests/ppc.rs
+++ /dev/null
@@ -1,530 +0,0 @@
-use rustc_apfloat::ppc::DoubleDouble;
-use rustc_apfloat::{Category, Float, Round};
-
-use std::cmp::Ordering;
-
-#[test]
-fn ppc_double_double() {
-    let test = DoubleDouble::ZERO;
-    let expected = "0x0p+0".parse::<DoubleDouble>().unwrap();
-    assert!(test.is_zero());
-    assert!(!test.is_negative());
-    assert!(test.bitwise_eq(expected));
-    assert_eq!(0, test.to_bits());
-
-    let test = -DoubleDouble::ZERO;
-    let expected = "-0x0p+0".parse::<DoubleDouble>().unwrap();
-    assert!(test.is_zero());
-    assert!(test.is_negative());
-    assert!(test.bitwise_eq(expected));
-    assert_eq!(0x8000000000000000, test.to_bits());
-
-    let test = "1.0".parse::<DoubleDouble>().unwrap();
-    assert_eq!(0x3ff0000000000000, test.to_bits());
-
-    // LDBL_MAX
-    let test = "1.79769313486231580793728971405301e+308".parse::<DoubleDouble>().unwrap();
-    assert_eq!(0x7c8ffffffffffffe_7fefffffffffffff, test.to_bits());
-
-    // LDBL_MIN
-    let test = "2.00416836000897277799610805135016e-292".parse::<DoubleDouble>().unwrap();
-    assert_eq!(0x0000000000000000_0360000000000000, test.to_bits());
-}
-
-#[test]
-fn ppc_double_double_add_special() {
-    let data = [
-        // (1 + 0) + (-1 + 0) = Category::Zero
-        (0x3ff0000000000000, 0xbff0000000000000, Category::Zero, Round::NearestTiesToEven),
-        // LDBL_MAX + (1.1 >> (1023 - 106) + 0)) = Category::Infinity
-        (
-            0x7c8ffffffffffffe_7fefffffffffffff,
-            0x7948000000000000,
-            Category::Infinity,
-            Round::NearestTiesToEven,
-        ),
-        // FIXME: change the 4th 0x75effffffffffffe to 0x75efffffffffffff when
-        // DoubleDouble's fallback is gone.
-        // LDBL_MAX + (1.011111... >> (1023 - 106) + (1.1111111...0 >> (1023 -
-        // 160))) = Category::Normal
-        (
-            0x7c8ffffffffffffe_7fefffffffffffff,
-            0x75effffffffffffe_7947ffffffffffff,
-            Category::Normal,
-            Round::NearestTiesToEven,
-        ),
-        // LDBL_MAX + (1.1 >> (1023 - 106) + 0)) = Category::Infinity
-        (
-            0x7c8ffffffffffffe_7fefffffffffffff,
-            0x7c8ffffffffffffe_7fefffffffffffff,
-            Category::Infinity,
-            Round::NearestTiesToEven,
-        ),
-        // NaN + (1 + 0) = Category::NaN
-        (0x7ff8000000000000, 0x3ff0000000000000, Category::NaN, Round::NearestTiesToEven),
-    ];
-
-    for (op1, op2, expected, round) in data {
-        {
-            let mut a1 = DoubleDouble::from_bits(op1);
-            let a2 = DoubleDouble::from_bits(op2);
-            a1 = a1.add_r(a2, round).value;
-
-            assert_eq!(expected, a1.category(), "{:#x} + {:#x}", op1, op2);
-        }
-        {
-            let a1 = DoubleDouble::from_bits(op1);
-            let mut a2 = DoubleDouble::from_bits(op2);
-            a2 = a2.add_r(a1, round).value;
-
-            assert_eq!(expected, a2.category(), "{:#x} + {:#x}", op2, op1);
-        }
-    }
-}
-
-#[test]
-fn ppc_double_double_add() {
-    let data = [
-        // (1 + 0) + (1e-105 + 0) = (1 + 1e-105)
-        (
-            0x3ff0000000000000,
-            0x3960000000000000,
-            0x3960000000000000_3ff0000000000000,
-            Round::NearestTiesToEven,
-        ),
-        // (1 + 0) + (1e-106 + 0) = (1 + 1e-106)
-        (
-            0x3ff0000000000000,
-            0x3950000000000000,
-            0x3950000000000000_3ff0000000000000,
-            Round::NearestTiesToEven,
-        ),
-        // (1 + 1e-106) + (1e-106 + 0) = (1 + 1e-105)
-        (
-            0x3950000000000000_3ff0000000000000,
-            0x3950000000000000,
-            0x3960000000000000_3ff0000000000000,
-            Round::NearestTiesToEven,
-        ),
-        // (1 + 0) + (epsilon + 0) = (1 + epsilon)
-        (
-            0x3ff0000000000000,
-            0x0000000000000001,
-            0x0000000000000001_3ff0000000000000,
-            Round::NearestTiesToEven,
-        ),
-        // FIXME: change 0xf950000000000000 to 0xf940000000000000, when
-        // DoubleDouble's fallback is gone.
-        // (DBL_MAX - 1 << (1023 - 105)) + (1 << (1023 - 53) + 0) = DBL_MAX +
-        // 1.11111... << (1023 - 52)
-        (
-            0xf950000000000000_7fefffffffffffff,
-            0x7c90000000000000,
-            0x7c8ffffffffffffe_7fefffffffffffff,
-            Round::NearestTiesToEven,
-        ),
-        // FIXME: change 0xf950000000000000 to 0xf940000000000000, when
-        // DoubleDouble's fallback is gone.
-        // (1 << (1023 - 53) + 0) + (DBL_MAX - 1 << (1023 - 105)) = DBL_MAX +
-        // 1.11111... << (1023 - 52)
-        (
-            0x7c90000000000000,
-            0xf950000000000000_7fefffffffffffff,
-            0x7c8ffffffffffffe_7fefffffffffffff,
-            Round::NearestTiesToEven,
-        ),
-    ];
-
-    for (op1, op2, expected, round) in data {
-        {
-            let mut a1 = DoubleDouble::from_bits(op1);
-            let a2 = DoubleDouble::from_bits(op2);
-            a1 = a1.add_r(a2, round).value;
-
-            assert_eq!(expected, a1.to_bits(), "{:#x} + {:#x}", op1, op2);
-        }
-        {
-            let a1 = DoubleDouble::from_bits(op1);
-            let mut a2 = DoubleDouble::from_bits(op2);
-            a2 = a2.add_r(a1, round).value;
-
-            assert_eq!(expected, a2.to_bits(), "{:#x} + {:#x}", op2, op1);
-        }
-    }
-}
-
-#[test]
-fn ppc_double_double_subtract() {
-    let data = [
-        // (1 + 0) - (-1e-105 + 0) = (1 + 1e-105)
-        (
-            0x3ff0000000000000,
-            0xb960000000000000,
-            0x3960000000000000_3ff0000000000000,
-            Round::NearestTiesToEven,
-        ),
-        // (1 + 0) - (-1e-106 + 0) = (1 + 1e-106)
-        (
-            0x3ff0000000000000,
-            0xb950000000000000,
-            0x3950000000000000_3ff0000000000000,
-            Round::NearestTiesToEven,
-        ),
-    ];
-
-    for (op1, op2, expected, round) in data {
-        let mut a1 = DoubleDouble::from_bits(op1);
-        let a2 = DoubleDouble::from_bits(op2);
-        a1 = a1.sub_r(a2, round).value;
-
-        assert_eq!(expected, a1.to_bits(), "{:#x} - {:#x}", op1, op2);
-    }
-}
-
-#[test]
-fn ppc_double_double_multiply_special() {
-    let data = [
-        // Category::NaN * Category::NaN = Category::NaN
-        (0x7ff8000000000000, 0x7ff8000000000000, Category::NaN, Round::NearestTiesToEven),
-        // Category::NaN * Category::Zero = Category::NaN
-        (0x7ff8000000000000, 0, Category::NaN, Round::NearestTiesToEven),
-        // Category::NaN * Category::Infinity = Category::NaN
-        (0x7ff8000000000000, 0x7ff0000000000000, Category::NaN, Round::NearestTiesToEven),
-        // Category::NaN * Category::Normal = Category::NaN
-        (0x7ff8000000000000, 0x3ff0000000000000, Category::NaN, Round::NearestTiesToEven),
-        // Category::Infinity * Category::Infinity = Category::Infinity
-        (0x7ff0000000000000, 0x7ff0000000000000, Category::Infinity, Round::NearestTiesToEven),
-        // Category::Infinity * Category::Zero = Category::NaN
-        (0x7ff0000000000000, 0, Category::NaN, Round::NearestTiesToEven),
-        // Category::Infinity * Category::Normal = Category::Infinity
-        (0x7ff0000000000000, 0x3ff0000000000000, Category::Infinity, Round::NearestTiesToEven),
-        // Category::Zero * Category::Zero = Category::Zero
-        (0, 0, Category::Zero, Round::NearestTiesToEven),
-        // Category::Zero * Category::Normal = Category::Zero
-        (0, 0x3ff0000000000000, Category::Zero, Round::NearestTiesToEven),
-    ];
-
-    for (op1, op2, expected, round) in data {
-        {
-            let mut a1 = DoubleDouble::from_bits(op1);
-            let a2 = DoubleDouble::from_bits(op2);
-            a1 = a1.mul_r(a2, round).value;
-
-            assert_eq!(expected, a1.category(), "{:#x} * {:#x}", op1, op2);
-        }
-        {
-            let a1 = DoubleDouble::from_bits(op1);
-            let mut a2 = DoubleDouble::from_bits(op2);
-            a2 = a2.mul_r(a1, round).value;
-
-            assert_eq!(expected, a2.category(), "{:#x} * {:#x}", op2, op1);
-        }
-    }
-}
-
-#[test]
-fn ppc_double_double_multiply() {
-    let data = [
-        // 1/3 * 3 = 1.0
-        (
-            0x3c75555555555556_3fd5555555555555,
-            0x4008000000000000,
-            0x3ff0000000000000,
-            Round::NearestTiesToEven,
-        ),
-        // (1 + epsilon) * (1 + 0) = Category::Zero
-        (
-            0x0000000000000001_3ff0000000000000,
-            0x3ff0000000000000,
-            0x0000000000000001_3ff0000000000000,
-            Round::NearestTiesToEven,
-        ),
-        // (1 + epsilon) * (1 + epsilon) = 1 + 2 * epsilon
-        (
-            0x0000000000000001_3ff0000000000000,
-            0x0000000000000001_3ff0000000000000,
-            0x0000000000000002_3ff0000000000000,
-            Round::NearestTiesToEven,
-        ),
-        // -(1 + epsilon) * (1 + epsilon) = -1
-        (
-            0x0000000000000001_bff0000000000000,
-            0x0000000000000001_3ff0000000000000,
-            0xbff0000000000000,
-            Round::NearestTiesToEven,
-        ),
-        // (0.5 + 0) * (1 + 2 * epsilon) = 0.5 + epsilon
-        (
-            0x3fe0000000000000,
-            0x0000000000000002_3ff0000000000000,
-            0x0000000000000001_3fe0000000000000,
-            Round::NearestTiesToEven,
-        ),
-        // (0.5 + 0) * (1 + epsilon) = 0.5
-        (
-            0x3fe0000000000000,
-            0x0000000000000001_3ff0000000000000,
-            0x3fe0000000000000,
-            Round::NearestTiesToEven,
-        ),
-        // __LDBL_MAX__ * (1 + 1 << 106) = inf
-        (
-            0x7c8ffffffffffffe_7fefffffffffffff,
-            0x3950000000000000_3ff0000000000000,
-            0x7ff0000000000000,
-            Round::NearestTiesToEven,
-        ),
-        // __LDBL_MAX__ * (1 + 1 << 107) > __LDBL_MAX__, but not inf, yes =_=|||
-        (
-            0x7c8ffffffffffffe_7fefffffffffffff,
-            0x3940000000000000_3ff0000000000000,
-            0x7c8fffffffffffff_7fefffffffffffff,
-            Round::NearestTiesToEven,
-        ),
-        // __LDBL_MAX__ * (1 + 1 << 108) = __LDBL_MAX__
-        (
-            0x7c8ffffffffffffe_7fefffffffffffff,
-            0x3930000000000000_3ff0000000000000,
-            0x7c8ffffffffffffe_7fefffffffffffff,
-            Round::NearestTiesToEven,
-        ),
-    ];
-
-    for (op1, op2, expected, round) in data {
-        {
-            let mut a1 = DoubleDouble::from_bits(op1);
-            let a2 = DoubleDouble::from_bits(op2);
-            a1 = a1.mul_r(a2, round).value;
-
-            assert_eq!(expected, a1.to_bits(), "{:#x} * {:#x}", op1, op2);
-        }
-        {
-            let a1 = DoubleDouble::from_bits(op1);
-            let mut a2 = DoubleDouble::from_bits(op2);
-            a2 = a2.mul_r(a1, round).value;
-
-            assert_eq!(expected, a2.to_bits(), "{:#x} * {:#x}", op2, op1);
-        }
-    }
-}
-
-#[test]
-fn ppc_double_double_divide() {
-    // FIXME: Only a sanity check for now. Add more edge cases when the
-    // double-double algorithm is implemented.
-    let data = [
-        // 1 / 3 = 1/3
-        (
-            0x3ff0000000000000,
-            0x4008000000000000,
-            0x3c75555555555556_3fd5555555555555,
-            Round::NearestTiesToEven,
-        ),
-    ];
-
-    for (op1, op2, expected, round) in data {
-        let mut a1 = DoubleDouble::from_bits(op1);
-        let a2 = DoubleDouble::from_bits(op2);
-        a1 = a1.div_r(a2, round).value;
-
-        assert_eq!(expected, a1.to_bits(), "{:#x} / {:#x}", op1, op2);
-    }
-}
-
-#[test]
-fn ppc_double_double_remainder() {
-    let data = [
-        // ieee_rem(3.0 + 3.0 << 53, 1.25 + 1.25 << 53) = (0.5 + 0.5 << 53)
-        (
-            0x3cb8000000000000_4008000000000000,
-            0x3ca4000000000000_3ff4000000000000,
-            0x3c90000000000000_3fe0000000000000,
-        ),
-        // ieee_rem(3.0 + 3.0 << 53, 1.75 + 1.75 << 53) = (-0.5 - 0.5 << 53)
-        (
-            0x3cb8000000000000_4008000000000000,
-            0x3cac000000000000_3ffc000000000000,
-            0xbc90000000000000_bfe0000000000000,
-        ),
-    ];
-
-    for (op1, op2, expected) in data {
-        let a1 = DoubleDouble::from_bits(op1);
-        let a2 = DoubleDouble::from_bits(op2);
-        let result = a1.ieee_rem(a2).value;
-
-        assert_eq!(expected, result.to_bits(), "ieee_rem({:#x}, {:#x})", op1, op2);
-    }
-}
-
-#[test]
-fn ppc_double_double_mod() {
-    let data = [
-        // mod(3.0 + 3.0 << 53, 1.25 + 1.25 << 53) = (0.5 + 0.5 << 53)
-        (
-            0x3cb8000000000000_4008000000000000,
-            0x3ca4000000000000_3ff4000000000000,
-            0x3c90000000000000_3fe0000000000000,
-        ),
-        // mod(3.0 + 3.0 << 53, 1.75 + 1.75 << 53) = (1.25 + 1.25 << 53)
-        // 0xbc98000000000000 doesn't seem right, but it's what we currently have.
-        // FIXME: investigate
-        (
-            0x3cb8000000000000_4008000000000000,
-            0x3cac000000000000_3ffc000000000000,
-            0xbc98000000000000_3ff4000000000001,
-        ),
-    ];
-
-    for (op1, op2, expected) in data {
-        let a1 = DoubleDouble::from_bits(op1);
-        let a2 = DoubleDouble::from_bits(op2);
-        let r = (a1 % a2).value;
-
-        assert_eq!(expected, r.to_bits(), "fmod({:#x}, {:#x})", op1, op2);
-    }
-}
-
-#[test]
-fn ppc_double_double_fma() {
-    // Sanity check for now.
-    let mut a = "2".parse::<DoubleDouble>().unwrap();
-    a = a.mul_add("3".parse::<DoubleDouble>().unwrap(), "4".parse::<DoubleDouble>().unwrap()).value;
-    assert_eq!(Some(Ordering::Equal), "10".parse::<DoubleDouble>().unwrap().partial_cmp(&a));
-}
-
-#[test]
-fn ppc_double_double_round_to_integral() {
-    {
-        let a = "1.5".parse::<DoubleDouble>().unwrap();
-        let a = a.round_to_integral(Round::NearestTiesToEven).value;
-        assert_eq!(Some(Ordering::Equal), "2".parse::<DoubleDouble>().unwrap().partial_cmp(&a));
-    }
-    {
-        let a = "2.5".parse::<DoubleDouble>().unwrap();
-        let a = a.round_to_integral(Round::NearestTiesToEven).value;
-        assert_eq!(Some(Ordering::Equal), "2".parse::<DoubleDouble>().unwrap().partial_cmp(&a));
-    }
-}
-
-#[test]
-fn ppc_double_double_compare() {
-    let data = [
-        // (1 + 0) = (1 + 0)
-        (0x3ff0000000000000, 0x3ff0000000000000, Some(Ordering::Equal)),
-        // (1 + 0) < (1.00...1 + 0)
-        (0x3ff0000000000000, 0x3ff0000000000001, Some(Ordering::Less)),
-        // (1.00...1 + 0) > (1 + 0)
-        (0x3ff0000000000001, 0x3ff0000000000000, Some(Ordering::Greater)),
-        // (1 + 0) < (1 + epsilon)
-        (0x3ff0000000000000, 0x0000000000000001_3ff0000000000001, Some(Ordering::Less)),
-        // NaN != NaN
-        (0x7ff8000000000000, 0x7ff8000000000000, None),
-        // (1 + 0) != NaN
-        (0x3ff0000000000000, 0x7ff8000000000000, None),
-        // Inf = Inf
-        (0x7ff0000000000000, 0x7ff0000000000000, Some(Ordering::Equal)),
-    ];
-
-    for (op1, op2, expected) in data {
-        let a1 = DoubleDouble::from_bits(op1);
-        let a2 = DoubleDouble::from_bits(op2);
-        assert_eq!(expected, a1.partial_cmp(&a2), "compare({:#x}, {:#x})", op1, op2,);
-    }
-}
-
-#[test]
-fn ppc_double_double_bitwise_eq() {
-    let data = [
-        // (1 + 0) = (1 + 0)
-        (0x3ff0000000000000, 0x3ff0000000000000, true),
-        // (1 + 0) != (1.00...1 + 0)
-        (0x3ff0000000000000, 0x3ff0000000000001, false),
-        // NaN = NaN
-        (0x7ff8000000000000, 0x7ff8000000000000, true),
-        // NaN != NaN with a different bit pattern
-        (0x7ff8000000000000, 0x3ff0000000000000_7ff8000000000000, false),
-        // Inf = Inf
-        (0x7ff0000000000000, 0x7ff0000000000000, true),
-    ];
-
-    for (op1, op2, expected) in data {
-        let a1 = DoubleDouble::from_bits(op1);
-        let a2 = DoubleDouble::from_bits(op2);
-        assert_eq!(expected, a1.bitwise_eq(a2), "{:#x} = {:#x}", op1, op2);
-    }
-}
-
-#[test]
-fn ppc_double_double_change_sign() {
-    let float = DoubleDouble::from_bits(0xbcb0000000000000_400f000000000000);
-    {
-        let actual = float.copy_sign("1".parse::<DoubleDouble>().unwrap());
-        assert_eq!(0xbcb0000000000000_400f000000000000, actual.to_bits());
-    }
-    {
-        let actual = float.copy_sign("-1".parse::<DoubleDouble>().unwrap());
-        assert_eq!(0x3cb0000000000000_c00f000000000000, actual.to_bits());
-    }
-}
-
-#[test]
-fn ppc_double_double_factories() {
-    assert_eq!(0, DoubleDouble::ZERO.to_bits());
-    assert_eq!(0x7c8ffffffffffffe_7fefffffffffffff, DoubleDouble::largest().to_bits());
-    assert_eq!(0x0000000000000001, DoubleDouble::SMALLEST.to_bits());
-    assert_eq!(0x0360000000000000, DoubleDouble::smallest_normalized().to_bits());
-    assert_eq!(0x0000000000000000_8000000000000000, (-DoubleDouble::ZERO).to_bits());
-    assert_eq!(0xfc8ffffffffffffe_ffefffffffffffff, (-DoubleDouble::largest()).to_bits());
-    assert_eq!(0x0000000000000000_8000000000000001, (-DoubleDouble::SMALLEST).to_bits());
-    assert_eq!(
-        0x0000000000000000_8360000000000000,
-        (-DoubleDouble::smallest_normalized()).to_bits()
-    );
-    assert!(DoubleDouble::SMALLEST.is_smallest());
-    assert!(DoubleDouble::largest().is_largest());
-}
-
-#[test]
-fn ppc_double_double_is_denormal() {
-    assert!(DoubleDouble::SMALLEST.is_denormal());
-    assert!(!DoubleDouble::largest().is_denormal());
-    assert!(!DoubleDouble::smallest_normalized().is_denormal());
-    {
-        // (4 + 3) is not normalized
-        let data = 0x4008000000000000_4010000000000000;
-        assert!(DoubleDouble::from_bits(data).is_denormal());
-    }
-}
-
-#[test]
-fn ppc_double_double_exact_inverse() {
-    assert!(
-        "2.0"
-            .parse::<DoubleDouble>()
-            .unwrap()
-            .get_exact_inverse()
-            .unwrap()
-            .bitwise_eq("0.5".parse::<DoubleDouble>().unwrap())
-    );
-}
-
-#[test]
-fn ppc_double_double_scalbn() {
-    // 3.0 + 3.0 << 53
-    let input = 0x3cb8000000000000_4008000000000000;
-    let result = DoubleDouble::from_bits(input).scalbn(1);
-    // 6.0 + 6.0 << 53
-    assert_eq!(0x3cc8000000000000_4018000000000000, result.to_bits());
-}
-
-#[test]
-fn ppc_double_double_frexp() {
-    // 3.0 + 3.0 << 53
-    let input = 0x3cb8000000000000_4008000000000000;
-    let mut exp = 0;
-    // 0.75 + 0.75 << 53
-    let result = DoubleDouble::from_bits(input).frexp(&mut exp);
-    assert_eq!(2, exp);
-    assert_eq!(0x3c98000000000000_3fe8000000000000, result.to_bits());
-}
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index a7198fbf887..17b73468a31 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -2353,7 +2353,12 @@ impl Param {
     /// Builds a `Param` object from `ExplicitSelf`.
     pub fn from_self(attrs: AttrVec, eself: ExplicitSelf, eself_ident: Ident) -> Param {
         let span = eself.span.to(eself_ident.span);
-        let infer_ty = P(Ty { id: DUMMY_NODE_ID, kind: TyKind::ImplicitSelf, span, tokens: None });
+        let infer_ty = P(Ty {
+            id: DUMMY_NODE_ID,
+            kind: TyKind::ImplicitSelf,
+            span: eself_ident.span,
+            tokens: None,
+        });
         let (mutbl, ty) = match eself.node {
             SelfKind::Explicit(ty, mutbl) => (mutbl, ty),
             SelfKind::Value(mutbl) => (mutbl, infer_ty),
diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs
index 15fe29580c2..8b9bb1df5dc 100644
--- a/compiler/rustc_ast/src/attr/mod.rs
+++ b/compiler/rustc_ast/src/attr/mod.rs
@@ -285,17 +285,17 @@ impl MetaItem {
         self.kind.value_str()
     }
 
-    fn from_tokens<I>(tokens: &mut iter::Peekable<I>) -> Option<MetaItem>
+    fn from_tokens<'a, I>(tokens: &mut iter::Peekable<I>) -> Option<MetaItem>
     where
-        I: Iterator<Item = TokenTree>,
+        I: Iterator<Item = &'a TokenTree>,
     {
         // FIXME: Share code with `parse_path`.
-        let path = match tokens.next().map(TokenTree::uninterpolate) {
-            Some(TokenTree::Token(
-                Token { kind: kind @ (token::Ident(..) | token::ModSep), span },
+        let path = match tokens.next().map(|tt| TokenTree::uninterpolate(tt)).as_deref() {
+            Some(&TokenTree::Token(
+                Token { kind: ref kind @ (token::Ident(..) | token::ModSep), span },
                 _,
             )) => 'arm: {
-                let mut segments = if let token::Ident(name, _) = kind {
+                let mut segments = if let &token::Ident(name, _) = kind {
                     if let Some(TokenTree::Token(Token { kind: token::ModSep, .. }, _)) =
                         tokens.peek()
                     {
@@ -308,8 +308,8 @@ impl MetaItem {
                     thin_vec![PathSegment::path_root(span)]
                 };
                 loop {
-                    if let Some(TokenTree::Token(Token { kind: token::Ident(name, _), span }, _)) =
-                        tokens.next().map(TokenTree::uninterpolate)
+                    if let Some(&TokenTree::Token(Token { kind: token::Ident(name, _), span }, _)) =
+                        tokens.next().map(|tt| TokenTree::uninterpolate(tt)).as_deref()
                     {
                         segments.push(PathSegment::from_ident(Ident::new(name, span)));
                     } else {
@@ -326,7 +326,7 @@ impl MetaItem {
                 let span = span.with_hi(segments.last().unwrap().ident.span.hi());
                 Path { span, segments, tokens: None }
             }
-            Some(TokenTree::Token(Token { kind: token::Interpolated(nt), .. }, _)) => match &*nt {
+            Some(TokenTree::Token(Token { kind: token::Interpolated(nt), .. }, _)) => match &**nt {
                 token::Nonterminal::NtMeta(item) => return item.meta(item.path.span),
                 token::Nonterminal::NtPath(path) => (**path).clone(),
                 _ => return None,
@@ -354,7 +354,7 @@ impl MetaItemKind {
     }
 
     fn list_from_tokens(tokens: TokenStream) -> Option<ThinVec<NestedMetaItem>> {
-        let mut tokens = tokens.into_trees().peekable();
+        let mut tokens = tokens.trees().peekable();
         let mut result = ThinVec::new();
         while tokens.peek().is_some() {
             let item = NestedMetaItem::from_tokens(&mut tokens)?;
@@ -367,12 +367,12 @@ impl MetaItemKind {
         Some(result)
     }
 
-    fn name_value_from_tokens(
-        tokens: &mut impl Iterator<Item = TokenTree>,
+    fn name_value_from_tokens<'a>(
+        tokens: &mut impl Iterator<Item = &'a TokenTree>,
     ) -> Option<MetaItemKind> {
         match tokens.next() {
             Some(TokenTree::Delimited(_, Delimiter::Invisible, inner_tokens)) => {
-                MetaItemKind::name_value_from_tokens(&mut inner_tokens.into_trees())
+                MetaItemKind::name_value_from_tokens(&mut inner_tokens.trees())
             }
             Some(TokenTree::Token(token, _)) => {
                 MetaItemLit::from_token(&token).map(MetaItemKind::NameValue)
@@ -381,8 +381,8 @@ impl MetaItemKind {
         }
     }
 
-    fn from_tokens(
-        tokens: &mut iter::Peekable<impl Iterator<Item = TokenTree>>,
+    fn from_tokens<'a>(
+        tokens: &mut iter::Peekable<impl Iterator<Item = &'a TokenTree>>,
     ) -> Option<MetaItemKind> {
         match tokens.peek() {
             Some(TokenTree::Delimited(_, Delimiter::Parenthesis, inner_tokens)) => {
@@ -501,9 +501,9 @@ impl NestedMetaItem {
         self.meta_item().is_some()
     }
 
-    fn from_tokens<I>(tokens: &mut iter::Peekable<I>) -> Option<NestedMetaItem>
+    fn from_tokens<'a, I>(tokens: &mut iter::Peekable<I>) -> Option<NestedMetaItem>
     where
-        I: Iterator<Item = TokenTree>,
+        I: Iterator<Item = &'a TokenTree>,
     {
         match tokens.peek() {
             Some(TokenTree::Token(token, _))
@@ -513,9 +513,8 @@ impl NestedMetaItem {
                 return Some(NestedMetaItem::Lit(lit));
             }
             Some(TokenTree::Delimited(_, Delimiter::Invisible, inner_tokens)) => {
-                let inner_tokens = inner_tokens.clone();
                 tokens.next();
-                return NestedMetaItem::from_tokens(&mut inner_tokens.into_trees().peekable());
+                return NestedMetaItem::from_tokens(&mut inner_tokens.trees().peekable());
             }
             _ => {}
         }
diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs
index 303bdd3a307..4c920e84f86 100644
--- a/compiler/rustc_ast/src/token.rs
+++ b/compiler/rustc_ast/src/token.rs
@@ -226,7 +226,9 @@ fn ident_can_begin_type(name: Symbol, span: Span, is_raw: bool) -> bool {
             .contains(&name)
 }
 
-#[derive(Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
+// SAFETY: due to the `Clone` impl below, all fields of all variants other than
+// `Interpolated` must impl `Copy`.
+#[derive(PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
 pub enum TokenKind {
     /* Expression-operator symbols. */
     Eq,
@@ -299,6 +301,19 @@ pub enum TokenKind {
     Eof,
 }
 
+impl Clone for TokenKind {
+    fn clone(&self) -> Self {
+        // `TokenKind` would impl `Copy` if it weren't for `Interpolated`. So
+        // for all other variants, this implementation of `clone` is just like
+        // a copy. This is faster than the `derive(Clone)` version which has a
+        // separate path for every variant.
+        match self {
+            Interpolated(nt) => Interpolated(nt.clone()),
+            _ => unsafe { std::ptr::read(self) },
+        }
+    }
+}
+
 #[derive(Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
 pub struct Token {
     pub kind: TokenKind,
diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs
index ca4a739abd7..348c37c480f 100644
--- a/compiler/rustc_ast/src/tokenstream.rs
+++ b/compiler/rustc_ast/src/tokenstream.rs
@@ -25,6 +25,7 @@ use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
 use rustc_span::{Span, DUMMY_SP};
 use smallvec::{smallvec, SmallVec};
 
+use std::borrow::Cow;
 use std::{fmt, iter, mem};
 
 /// When the main Rust parser encounters a syntax-extension invocation, it
@@ -98,12 +99,13 @@ impl TokenTree {
         TokenTree::Token(Token::new(kind, span), Spacing::Joint)
     }
 
-    pub fn uninterpolate(self) -> TokenTree {
+    pub fn uninterpolate(&self) -> Cow<'_, TokenTree> {
         match self {
-            TokenTree::Token(token, spacing) => {
-                TokenTree::Token(token.uninterpolate().into_owned(), spacing)
-            }
-            tt => tt,
+            TokenTree::Token(token, spacing) => match token.uninterpolate() {
+                Cow::Owned(token) => Cow::Owned(TokenTree::Token(token, *spacing)),
+                Cow::Borrowed(_) => Cow::Borrowed(self),
+            },
+            _ => Cow::Borrowed(self),
         }
     }
 }
@@ -595,26 +597,21 @@ impl<'t> Iterator for RefTokenTreeCursor<'t> {
     }
 }
 
-/// Owning by-value iterator over a [`TokenStream`], that produces `TokenTree`
+/// Owning by-value iterator over a [`TokenStream`], that produces `&TokenTree`
 /// items.
-// FIXME: Many uses of this can be replaced with by-reference iterator to avoid clones.
+///
+/// Doesn't impl `Iterator` because Rust doesn't permit an owning iterator to
+/// return `&T` from `next`; the need for an explicit lifetime in the `Item`
+/// associated type gets in the way. Instead, use `next_ref` (which doesn't
+/// involve associated types) for getting individual elements, or
+/// `RefTokenTreeCursor` if you really want an `Iterator`, e.g. in a `for`
+/// loop.
 #[derive(Clone)]
 pub struct TokenTreeCursor {
     pub stream: TokenStream,
     index: usize,
 }
 
-impl Iterator for TokenTreeCursor {
-    type Item = TokenTree;
-
-    fn next(&mut self) -> Option<TokenTree> {
-        self.stream.0.get(self.index).map(|tree| {
-            self.index += 1;
-            tree.clone()
-        })
-    }
-}
-
 impl TokenTreeCursor {
     fn new(stream: TokenStream) -> Self {
         TokenTreeCursor { stream, index: 0 }
diff --git a/compiler/rustc_ast/src/util/comments.rs b/compiler/rustc_ast/src/util/comments.rs
index eece99a3eef..bdf5143b0f7 100644
--- a/compiler/rustc_ast/src/util/comments.rs
+++ b/compiler/rustc_ast/src/util/comments.rs
@@ -62,7 +62,7 @@ pub fn beautify_doc_string(data: Symbol, kind: CommentKind) -> Symbol {
             CommentKind::Block => {
                 // Whatever happens, we skip the first line.
                 let mut i = lines
-                    .get(0)
+                    .first()
                     .map(|l| if l.trim_start().starts_with('*') { 0 } else { 1 })
                     .unwrap_or(0);
                 let mut j = lines.len();
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index dcaaaafedbe..0954cf03da9 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -286,7 +286,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 ExprKind::OffsetOf(container, fields) => hir::ExprKind::OffsetOf(
                     self.lower_ty(
                         container,
-                        &mut ImplTraitContext::Disallowed(ImplTraitPosition::OffsetOf),
+                        &ImplTraitContext::Disallowed(ImplTraitPosition::OffsetOf),
                     ),
                     self.arena.alloc_from_iter(fields.iter().map(|&ident| self.lower_ident(ident))),
                 ),
diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
index b0dbc2c2340..c0d2e76f310 100644
--- a/compiler/rustc_ast_passes/src/feature_gate.rs
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -218,6 +218,19 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
                 }
             }
         }
+        if !attr.is_doc_comment()
+            && attr.get_normal_item().path.segments.len() == 2
+            && attr.get_normal_item().path.segments[0].ident.name == sym::diagnostic
+            && !self.features.diagnostic_namespace
+        {
+            let msg = "`#[diagnostic]` attribute name space is experimental";
+            gate_feature_post!(
+                self,
+                diagnostic_namespace,
+                attr.get_normal_item().path.segments[0].ident.span,
+                msg
+            );
+        }
 
         // Emit errors for non-staged-api crates.
         if !self.features.staged_api {
diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
index 609920180a2..8767bbcb210 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
@@ -697,15 +697,15 @@ pub fn reconstruct_format_args_template_string(pieces: &[FormatArgsPiece]) -> St
                 write!(template, "{n}").unwrap();
                 if p.format_options != Default::default() || p.format_trait != FormatTrait::Display
                 {
-                    template.push_str(":");
+                    template.push(':');
                 }
                 if let Some(fill) = p.format_options.fill {
                     template.push(fill);
                 }
                 match p.format_options.alignment {
-                    Some(FormatAlignment::Left) => template.push_str("<"),
-                    Some(FormatAlignment::Right) => template.push_str(">"),
-                    Some(FormatAlignment::Center) => template.push_str("^"),
+                    Some(FormatAlignment::Left) => template.push('<'),
+                    Some(FormatAlignment::Right) => template.push('>'),
+                    Some(FormatAlignment::Center) => template.push('^'),
                     None => {}
                 }
                 match p.format_options.sign {
diff --git a/compiler/rustc_borrowck/src/borrowck_errors.rs b/compiler/rustc_borrowck/src/borrowck_errors.rs
index a4e0e773a81..a2c7e767b4c 100644
--- a/compiler/rustc_borrowck/src/borrowck_errors.rs
+++ b/compiler/rustc_borrowck/src/borrowck_errors.rs
@@ -37,8 +37,8 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
             desc,
         );
 
-        err.span_label(borrow_span, format!("{} is borrowed here", borrow_desc));
-        err.span_label(span, format!("use of borrowed {}", borrow_desc));
+        err.span_label(borrow_span, format!("{borrow_desc} is borrowed here"));
+        err.span_label(span, format!("use of borrowed {borrow_desc}"));
         err
     }
 
@@ -51,8 +51,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         old_opt_via: &str,
         old_load_end_span: Option<Span>,
     ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
-        let via =
-            |msg: &str| if msg.is_empty() { "".to_string() } else { format!(" (via {})", msg) };
+        let via = |msg: &str| if msg.is_empty() { "".to_string() } else { format!(" (via {msg})") };
         let mut err = struct_span_err!(
             self,
             new_loan_span,
@@ -143,9 +142,9 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         );
         err.span_label(
             new_loan_span,
-            format!("{} construction occurs here{}", container_name, opt_via),
+            format!("{container_name} construction occurs here{opt_via}"),
         );
-        err.span_label(old_loan_span, format!("borrow occurs here{}", old_opt_via));
+        err.span_label(old_loan_span, format!("borrow occurs here{old_opt_via}"));
         if let Some(previous_end_span) = previous_end_span {
             err.span_label(previous_end_span, "borrow ends here");
         }
@@ -173,13 +172,10 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
             opt_via,
             kind_new,
         );
-        err.span_label(
-            new_loan_span,
-            format!("{}borrow occurs here{}", second_borrow_desc, opt_via),
-        );
+        err.span_label(new_loan_span, format!("{second_borrow_desc}borrow occurs here{opt_via}"));
         err.span_label(
             old_loan_span,
-            format!("{} construction occurs here{}", container_name, old_opt_via),
+            format!("{container_name} construction occurs here{old_opt_via}"),
         );
         if let Some(previous_end_span) = previous_end_span {
             err.span_label(previous_end_span, "borrow from closure ends here");
@@ -199,8 +195,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         msg_old: &str,
         old_load_end_span: Option<Span>,
     ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
-        let via =
-            |msg: &str| if msg.is_empty() { "".to_string() } else { format!(" (via {})", msg) };
+        let via = |msg: &str| if msg.is_empty() { "".to_string() } else { format!(" (via {msg})") };
         let mut err = struct_span_err!(
             self,
             span,
@@ -216,22 +211,21 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
 
         if msg_new == "" {
             // If `msg_new` is empty, then this isn't a borrow of a union field.
-            err.span_label(span, format!("{} borrow occurs here", kind_new));
-            err.span_label(old_span, format!("{} borrow occurs here", kind_old));
+            err.span_label(span, format!("{kind_new} borrow occurs here"));
+            err.span_label(old_span, format!("{kind_old} borrow occurs here"));
         } else {
             // If `msg_new` isn't empty, then this a borrow of a union field.
             err.span_label(
                 span,
                 format!(
-                    "{} borrow of {} -- which overlaps with {} -- occurs here",
-                    kind_new, msg_new, msg_old,
+                    "{kind_new} borrow of {msg_new} -- which overlaps with {msg_old} -- occurs here",
                 ),
             );
             err.span_label(old_span, format!("{} borrow occurs here{}", kind_old, via(msg_old)));
         }
 
         if let Some(old_load_end_span) = old_load_end_span {
-            err.span_label(old_load_end_span, format!("{} borrow ends here", kind_old));
+            err.span_label(old_load_end_span, format!("{kind_old} borrow ends here"));
         }
         err
     }
@@ -250,8 +244,8 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
             desc,
         );
 
-        err.span_label(borrow_span, format!("{} is borrowed here", desc));
-        err.span_label(span, format!("{} is assigned to here but it was already borrowed", desc));
+        err.span_label(borrow_span, format!("{desc} is borrowed here"));
+        err.span_label(span, format!("{desc} is assigned to here but it was already borrowed"));
         err
     }
 
@@ -330,7 +324,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         optional_adverb_for_moved: &str,
         moved_path: Option<String>,
     ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
-        let moved_path = moved_path.map(|mp| format!(": `{}`", mp)).unwrap_or_default();
+        let moved_path = moved_path.map(|mp| format!(": `{mp}`")).unwrap_or_default();
 
         struct_span_err!(
             self,
@@ -369,8 +363,8 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
             immutable_place,
             immutable_section,
         );
-        err.span_label(mutate_span, format!("cannot {}", action));
-        err.span_label(immutable_span, format!("value is immutable in {}", immutable_section));
+        err.span_label(mutate_span, format!("cannot {action}"));
+        err.span_label(immutable_span, format!("value is immutable in {immutable_section}"));
         err
     }
 
@@ -428,7 +422,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
 
         err.span_label(
             span,
-            format!("{}s a {} data owned by the current function", return_kind, reference_desc),
+            format!("{return_kind}s a {reference_desc} data owned by the current function"),
         );
 
         err
@@ -449,8 +443,8 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
             "{closure_kind} may outlive the current {scope}, but it borrows {borrowed_path}, \
              which is owned by the current {scope}",
         );
-        err.span_label(capture_span, format!("{} is borrowed here", borrowed_path))
-            .span_label(closure_span, format!("may outlive borrowed value {}", borrowed_path));
+        err.span_label(capture_span, format!("{borrowed_path} is borrowed here"))
+            .span_label(closure_span, format!("may outlive borrowed value {borrowed_path}"));
         err
     }
 
diff --git a/compiler/rustc_borrowck/src/dataflow.rs b/compiler/rustc_borrowck/src/dataflow.rs
index 1064b44d2cd..1e89a9f5144 100644
--- a/compiler/rustc_borrowck/src/dataflow.rs
+++ b/compiler/rustc_borrowck/src/dataflow.rs
@@ -360,7 +360,7 @@ impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> {
                         return;
                     }
                     let index = self.borrow_set.get_index_of(&location).unwrap_or_else(|| {
-                        panic!("could not find BorrowIndex for location {:?}", location);
+                        panic!("could not find BorrowIndex for location {location:?}");
                     });
 
                     trans.gen(index);
diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
index 97c3e0b879a..03b90f4ab18 100644
--- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
@@ -653,7 +653,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             err.span_suggestion_verbose(
                 sugg_span.shrink_to_hi(),
                 "consider assigning a value",
-                format!(" = {}", assign_value),
+                format!(" = {assign_value}"),
                 Applicability::MaybeIncorrect,
             );
         }
@@ -738,7 +738,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         // Try to find predicates on *generic params* that would allow copying `ty`
         let suggestion =
             if let Some(symbol) = tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) {
-                format!(": {}.clone()", symbol)
+                format!(": {symbol}.clone()")
             } else {
                 ".clone()".to_owned()
             };
@@ -1162,8 +1162,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
 
         if union_type_name != "" {
             err.note(format!(
-                "{} is a field of the union `{}`, so it overlaps the field {}",
-                msg_place, union_type_name, msg_borrow,
+                "{msg_place} is a field of the union `{union_type_name}`, so it overlaps the field {msg_borrow}",
             ));
         }
 
@@ -1353,8 +1352,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                 let Some(trait_did) = tcx.trait_of_item(def_id) &&
                 tcx.is_diagnostic_item(sym::Iterator, trait_did) {
                     err.note(format!(
-                        "a for loop advances the iterator for you, the result is stored in `{}`.",
-                        loop_bind
+                        "a for loop advances the iterator for you, the result is stored in `{loop_bind}`."
                     ));
                     err.help("if you want to call `next` on a iterator within the loop, consider using `while let`.");
         }
@@ -1825,7 +1823,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                     },
                     ConstraintCategory::CallArgument(None),
                     var_or_use_span,
-                    &format!("`{}`", name),
+                    &format!("`{name}`"),
                     "block",
                 ),
             (
@@ -1847,7 +1845,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                     region_name,
                     category,
                     span,
-                    &format!("`{}`", name),
+                    &format!("`{name}`"),
                     "function",
                 ),
             (
@@ -1921,14 +1919,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             }
         }
 
-        let mut err = self.path_does_not_live_long_enough(borrow_span, &format!("`{}`", name));
+        let mut err = self.path_does_not_live_long_enough(borrow_span, &format!("`{name}`"));
 
         if let Some(annotation) = self.annotate_argument_and_return_for_borrow(borrow) {
             let region_name = annotation.emit(self, &mut err);
 
             err.span_label(
                 borrow_span,
-                format!("`{}` would have to be valid for `{}`...", name, region_name),
+                format!("`{name}` would have to be valid for `{region_name}`..."),
             );
 
             err.span_label(
@@ -1939,7 +1937,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                     self.infcx
                         .tcx
                         .opt_item_name(self.mir_def_id().to_def_id())
-                        .map(|name| format!("function `{}`", name))
+                        .map(|name| format!("function `{name}`"))
                         .unwrap_or_else(|| {
                             match &self.infcx.tcx.def_kind(self.mir_def_id()) {
                                 DefKind::Closure => "enclosing closure",
@@ -1974,7 +1972,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             }
         } else {
             err.span_label(borrow_span, "borrowed value does not live long enough");
-            err.span_label(drop_span, format!("`{}` dropped here while still borrowed", name));
+            err.span_label(drop_span, format!("`{name}` dropped here while still borrowed"));
 
             borrow_spans.args_subdiag(&mut err, |args_span| {
                 crate::session_diagnostics::CaptureArgLabel::Capture {
@@ -2018,22 +2016,17 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         let mut err = self.cannot_borrow_across_destructor(borrow_span);
 
         let what_was_dropped = match self.describe_place(place.as_ref()) {
-            Some(name) => format!("`{}`", name),
+            Some(name) => format!("`{name}`"),
             None => String::from("temporary value"),
         };
 
         let label = match self.describe_place(borrow.borrowed_place.as_ref()) {
             Some(borrowed) => format!(
-                "here, drop of {D} needs exclusive access to `{B}`, \
-                 because the type `{T}` implements the `Drop` trait",
-                D = what_was_dropped,
-                T = dropped_ty,
-                B = borrowed
+                "here, drop of {what_was_dropped} needs exclusive access to `{borrowed}`, \
+                 because the type `{dropped_ty}` implements the `Drop` trait"
             ),
             None => format!(
-                "here is drop of {D}; whose type `{T}` implements the `Drop` trait",
-                D = what_was_dropped,
-                T = dropped_ty
+                "here is drop of {what_was_dropped}; whose type `{dropped_ty}` implements the `Drop` trait"
             ),
         };
         err.span_label(drop_span, label);
@@ -2245,10 +2238,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             } else {
                 "local data "
             };
-            (
-                format!("{}`{}`", local_kind, place_desc),
-                format!("`{}` is borrowed here", place_desc),
-            )
+            (format!("{local_kind}`{place_desc}`"), format!("`{place_desc}` is borrowed here"))
         } else {
             let root_place =
                 self.prefixes(borrow.borrowed_place.as_ref(), PrefixSet::All).last().unwrap();
@@ -2350,9 +2340,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         err.span_suggestion_verbose(
             sugg_span,
             format!(
-                "to force the {} to take ownership of {} (and any \
-                 other referenced variables), use the `move` keyword",
-                kind, captured_var
+                "to force the {kind} to take ownership of {captured_var} (and any \
+                 other referenced variables), use the `move` keyword"
             ),
             suggestion,
             Applicability::MachineApplicable,
@@ -2360,7 +2349,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
 
         match category {
             ConstraintCategory::Return(_) | ConstraintCategory::OpaqueType => {
-                let msg = format!("{} is returned here", kind);
+                let msg = format!("{kind} is returned here");
                 err.span_note(constraint_span, msg);
             }
             ConstraintCategory::CallArgument(_) => {
@@ -2402,21 +2391,18 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
 
         err.span_label(
             upvar_span,
-            format!("`{}` declared here, outside of the {} body", upvar_name, escapes_from),
+            format!("`{upvar_name}` declared here, outside of the {escapes_from} body"),
         );
 
-        err.span_label(borrow_span, format!("borrow is only valid in the {} body", escapes_from));
+        err.span_label(borrow_span, format!("borrow is only valid in the {escapes_from} body"));
 
         if let Some(name) = name {
             err.span_label(
                 escape_span,
-                format!("reference to `{}` escapes the {} body here", name, escapes_from),
+                format!("reference to `{name}` escapes the {escapes_from} body here"),
             );
         } else {
-            err.span_label(
-                escape_span,
-                format!("reference escapes the {} body here", escapes_from),
-            );
+            err.span_label(escape_span, format!("reference escapes the {escapes_from} body here"));
         }
 
         err
@@ -2697,10 +2683,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                     });
                 if let Some(Ok(instance)) = deref_target {
                     let deref_target_ty = instance.ty(tcx, self.param_env);
-                    err.note(format!(
-                        "borrow occurs due to deref coercion to `{}`",
-                        deref_target_ty
-                    ));
+                    err.note(format!("borrow occurs due to deref coercion to `{deref_target_ty}`"));
                     err.span_note(tcx.def_span(instance.def_id()), "deref defined here");
                 }
             }
@@ -2756,7 +2739,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             "cannot assign twice to immutable variable"
         };
         if span != assigned_span && !from_arg {
-            err.span_label(assigned_span, format!("first assignment to {}", place_description));
+            err.span_label(assigned_span, format!("first assignment to {place_description}"));
         }
         if let Some(decl) = local_decl
             && let Some(name) = local_name
@@ -2765,7 +2748,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             err.span_suggestion(
                 decl.source_info.span,
                 "consider making this binding mutable",
-                format!("mut {}", name),
+                format!("mut {name}"),
                 Applicability::MachineApplicable,
             );
         }
@@ -3226,7 +3209,7 @@ impl<'tcx> AnnotatedBorrowFnSignature<'tcx> {
                 return_span,
             } => {
                 let argument_ty_name = cx.get_name_for_ty(argument_ty, 0);
-                diag.span_label(argument_span, format!("has type `{}`", argument_ty_name));
+                diag.span_label(argument_span, format!("has type `{argument_ty_name}`"));
 
                 let return_ty_name = cx.get_name_for_ty(return_ty, 0);
                 let types_equal = return_ty_name == argument_ty_name;
@@ -3253,15 +3236,14 @@ impl<'tcx> AnnotatedBorrowFnSignature<'tcx> {
                 // Region of return type and arguments checked to be the same earlier.
                 let region_name = cx.get_region_name_for_ty(*return_ty, 0);
                 for (_, argument_span) in arguments {
-                    diag.span_label(*argument_span, format!("has lifetime `{}`", region_name));
+                    diag.span_label(*argument_span, format!("has lifetime `{region_name}`"));
                 }
 
-                diag.span_label(*return_span, format!("also has lifetime `{}`", region_name,));
+                diag.span_label(*return_span, format!("also has lifetime `{region_name}`",));
 
                 diag.help(format!(
-                    "use data from the highlighted arguments which match the `{}` lifetime of \
+                    "use data from the highlighted arguments which match the `{region_name}` lifetime of \
                      the return type",
-                    region_name,
                 ));
 
                 region_name
diff --git a/compiler/rustc_borrowck/src/facts.rs b/compiler/rustc_borrowck/src/facts.rs
index 87fad9a355d..9916ebca32f 100644
--- a/compiler/rustc_borrowck/src/facts.rs
+++ b/compiler/rustc_borrowck/src/facts.rs
@@ -202,7 +202,7 @@ trait FactCell {
 
 impl<A: Debug> FactCell for A {
     default fn to_string(&self, _location_table: &LocationTable) -> String {
-        format!("{:?}", self)
+        format!("{self:?}")
     }
 }
 
diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs
index ea32506fc89..ba1d31ee002 100644
--- a/compiler/rustc_borrowck/src/lib.rs
+++ b/compiler/rustc_borrowck/src/lib.rs
@@ -1817,8 +1817,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                 }
 
                 ProjectionElem::Subslice { .. } => {
-                    panic!("we don't allow assignments to subslices, location: {:?}",
-                           location);
+                    panic!("we don't allow assignments to subslices, location: {location:?}");
                 }
 
                 ProjectionElem::Field(..) => {
@@ -2017,8 +2016,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                     self.infcx.tcx.sess.delay_span_bug(
                         span,
                         format!(
-                            "Accessing `{:?}` with the kind `{:?}` shouldn't be possible",
-                            place, kind,
+                            "Accessing `{place:?}` with the kind `{kind:?}` shouldn't be possible",
                         ),
                     );
                 }
@@ -2306,11 +2304,10 @@ mod error {
 
         pub fn buffer_error(&mut self, t: DiagnosticBuilder<'_, ErrorGuaranteed>) {
             if let None = self.tainted_by_errors {
-                self.tainted_by_errors = Some(
-                    self.tcx
-                        .sess
-                        .delay_span_bug(t.span.clone(), "diagnostic buffered but not emitted"),
-                )
+                self.tainted_by_errors = Some(self.tcx.sess.delay_span_bug(
+                    t.span.clone_ignoring_labels(),
+                    "diagnostic buffered but not emitted",
+                ))
             }
             t.buffer(&mut self.buffered);
         }
diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs
index b5014a3f479..679a19710a7 100644
--- a/compiler/rustc_borrowck/src/nll.rs
+++ b/compiler/rustc_borrowck/src/nll.rs
@@ -347,7 +347,7 @@ pub(super) fn dump_mir_results<'tcx>(
                     for_each_region_constraint(
                         infcx.tcx,
                         closure_region_requirements,
-                        &mut |msg| writeln!(out, "| {}", msg),
+                        &mut |msg| writeln!(out, "| {msg}"),
                     )?;
                     writeln!(out, "|")?;
                 }
@@ -426,7 +426,7 @@ pub(super) fn dump_annotation<'tcx>(
     };
 
     if !opaque_type_values.is_empty() {
-        err.note(format!("Inferred opaque type values:\n{:#?}", opaque_type_values));
+        err.note(format!("Inferred opaque type values:\n{opaque_type_values:#?}"));
     }
 
     errors.buffer_non_error_diag(err);
@@ -439,7 +439,7 @@ fn for_each_region_constraint<'tcx>(
 ) -> io::Result<()> {
     for req in &closure_region_requirements.outlives_requirements {
         let subject = match req.subject {
-            ClosureOutlivesSubject::Region(subject) => format!("{:?}", subject),
+            ClosureOutlivesSubject::Region(subject) => format!("{subject:?}"),
             ClosureOutlivesSubject::Ty(ty) => {
                 format!("{:?}", ty.instantiate(tcx, |vid| ty::Region::new_var(tcx, vid)))
             }
diff --git a/compiler/rustc_borrowck/src/region_infer/dump_mir.rs b/compiler/rustc_borrowck/src/region_infer/dump_mir.rs
index 6524b594e44..4d620ac9de6 100644
--- a/compiler/rustc_borrowck/src/region_infer/dump_mir.rs
+++ b/compiler/rustc_borrowck/src/region_infer/dump_mir.rs
@@ -52,7 +52,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
 
         writeln!(out, "|")?;
         writeln!(out, "| Inference Constraints")?;
-        self.for_each_constraint(tcx, &mut |msg| writeln!(out, "| {}", msg))?;
+        self.for_each_constraint(tcx, &mut |msg| writeln!(out, "| {msg}"))?;
 
         Ok(())
     }
@@ -69,7 +69,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         for region in self.definitions.indices() {
             let value = self.liveness_constraints.region_value_str(region);
             if value != "{}" {
-                with_msg(&format!("{:?} live at {}", region, value))?;
+                with_msg(&format!("{region:?} live at {value}"))?;
             }
         }
 
@@ -81,12 +81,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
                 Locations::All(span) => {
                     ("All", tcx.sess.source_map().span_to_embeddable_string(*span))
                 }
-                Locations::Single(loc) => ("Single", format!("{:?}", loc)),
+                Locations::Single(loc) => ("Single", format!("{loc:?}")),
             };
-            with_msg(&format!(
-                "{:?}: {:?} due to {:?} at {}({}) ({:?}",
-                sup, sub, category, name, arg, span
-            ))?;
+            with_msg(&format!("{sup:?}: {sub:?} due to {category:?} at {name}({arg}) ({span:?}"))?;
         }
 
         Ok(())
diff --git a/compiler/rustc_borrowck/src/region_infer/graphviz.rs b/compiler/rustc_borrowck/src/region_infer/graphviz.rs
index 2e15586e03b..a0cf22e935a 100644
--- a/compiler/rustc_borrowck/src/region_infer/graphviz.rs
+++ b/compiler/rustc_borrowck/src/region_infer/graphviz.rs
@@ -49,7 +49,7 @@ impl<'a, 'this, 'tcx> dot::Labeller<'this> for RawConstraints<'a, 'tcx> {
         Some(dot::LabelText::LabelStr(Cow::Borrowed("box")))
     }
     fn node_label(&'this self, n: &RegionVid) -> dot::LabelText<'this> {
-        dot::LabelText::LabelStr(format!("{:?}", n).into())
+        dot::LabelText::LabelStr(format!("{n:?}").into())
     }
     fn edge_label(&'this self, e: &OutlivesConstraint<'tcx>) -> dot::LabelText<'this> {
         dot::LabelText::LabelStr(format!("{:?}", e.locations).into())
@@ -100,7 +100,7 @@ impl<'a, 'this, 'tcx> dot::Labeller<'this> for SccConstraints<'a, 'tcx> {
     }
     fn node_label(&'this self, n: &ConstraintSccIndex) -> dot::LabelText<'this> {
         let nodes = &self.nodes_per_scc[*n];
-        dot::LabelText::LabelStr(format!("{:?} = {:?}", n, nodes).into())
+        dot::LabelText::LabelStr(format!("{n:?} = {nodes:?}").into())
     }
 }
 
diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs
index fbbccc58ad6..30dfb2d919a 100644
--- a/compiler/rustc_borrowck/src/region_infer/mod.rs
+++ b/compiler/rustc_borrowck/src/region_infer/mod.rs
@@ -259,7 +259,7 @@ fn sccs_info<'cx, 'tcx>(
 
     let mut reg_vars_to_origins_str = "region variables to origins:\n".to_string();
     for (reg_var, origin) in var_to_origin_sorted.into_iter() {
-        reg_vars_to_origins_str.push_str(&format!("{:?}: {:?}\n", reg_var, origin));
+        reg_vars_to_origins_str.push_str(&format!("{reg_var:?}: {origin:?}\n"));
     }
     debug!("{}", reg_vars_to_origins_str);
 
diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
index d8e81827a3b..90bde88f792 100644
--- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
+++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
@@ -339,8 +339,8 @@ fn check_opaque_type_well_formed<'tcx>(
     // version.
     let errors = ocx.select_all_or_error();
 
-    // This is still required for many(half of the tests in ui/type-alias-impl-trait)
-    // tests to pass
+    // This is fishy, but we check it again in `check_opaque_meets_bounds`.
+    // Remove once we can prepopulate with known hidden types.
     let _ = infcx.take_opaque_types();
 
     if errors.is_empty() {
@@ -419,7 +419,7 @@ fn check_opaque_type_parameter_valid(
             return Err(tcx
                 .sess
                 .struct_span_err(span, "non-defining opaque type use in defining scope")
-                .span_note(spans, format!("{} used multiple times", descr))
+                .span_note(spans, format!("{descr} used multiple times"))
                 .emit());
         }
     }
diff --git a/compiler/rustc_borrowck/src/region_infer/values.rs b/compiler/rustc_borrowck/src/region_infer/values.rs
index 9290e747914..d205862cd3f 100644
--- a/compiler/rustc_borrowck/src/region_infer/values.rs
+++ b/compiler/rustc_borrowck/src/region_infer/values.rs
@@ -470,7 +470,7 @@ fn region_value_str(elements: impl IntoIterator<Item = RegionElement>) -> String
                 }
 
                 push_sep(&mut result);
-                result.push_str(&format!("{:?}", fr));
+                result.push_str(&format!("{fr:?}"));
             }
 
             RegionElement::PlaceholderRegion(placeholder) => {
@@ -481,7 +481,7 @@ fn region_value_str(elements: impl IntoIterator<Item = RegionElement>) -> String
                 }
 
                 push_sep(&mut result);
-                result.push_str(&format!("{:?}", placeholder));
+                result.push_str(&format!("{placeholder:?}"));
             }
         }
     }
@@ -497,7 +497,7 @@ fn region_value_str(elements: impl IntoIterator<Item = RegionElement>) -> String
 
     fn push_location_range(str: &mut String, location1: Location, location2: Location) {
         if location1 == location2 {
-            str.push_str(&format!("{:?}", location1));
+            str.push_str(&format!("{location1:?}"));
         } else {
             assert_eq!(location1.block, location2.block);
             str.push_str(&format!(
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index 78aa513033c..fd4a3ec1a5e 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -409,18 +409,12 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
             }
 
             if let ty::FnDef(def_id, args) = *constant.literal.ty().kind() {
-                // const_trait_impl: use a non-const param env when checking that a FnDef type is well formed.
-                // this is because the well-formedness of the function does not need to be proved to have `const`
-                // impls for trait bounds.
                 let instantiated_predicates = tcx.predicates_of(def_id).instantiate(tcx, args);
-                let prev = self.cx.param_env;
-                self.cx.param_env = prev.without_const();
                 self.cx.normalize_and_prove_instantiated_predicates(
                     def_id,
                     instantiated_predicates,
                     locations,
                 );
-                self.cx.param_env = prev;
             }
         }
     }
diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl
index 322222ae330..79fae7f92f1 100644
--- a/compiler/rustc_builtin_macros/messages.ftl
+++ b/compiler/rustc_builtin_macros/messages.ftl
@@ -109,8 +109,8 @@ builtin_macros_derive_path_args_value = traits in `#[derive(...)]` don't accept
     .suggestion = remove the value
 
 builtin_macros_env_not_defined = environment variable `{$var}` not defined at compile time
-    .cargo = Cargo sets build script variables at run time. Use `std::env::var("{$var}")` instead
-    .other = use `std::env::var("{$var}")` to read the variable at run time
+    .cargo = Cargo sets build script variables at run time. Use `std::env::var({$var_expr})` instead
+    .custom = use `std::env::var({$var_expr})` to read the variable at run time
 
 builtin_macros_env_takes_args = `env!()` takes 1 or 2 arguments
 
diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs
index 19acefc38e3..21f45935218 100644
--- a/compiler/rustc_builtin_macros/src/asm.rs
+++ b/compiler/rustc_builtin_macros/src/asm.rs
@@ -401,7 +401,7 @@ fn parse_clobber_abi<'a>(p: &mut Parser<'a>, args: &mut AsmArgs) -> PResult<'a,
         // should have errored above during parsing
         [] => unreachable!(),
         [(abi, _span)] => args.clobber_abis.push((*abi, full_span)),
-        [abis @ ..] => {
+        abis => {
             for (abi, span) in abis {
                 args.clobber_abis.push((*abi, *span));
             }
diff --git a/compiler/rustc_builtin_macros/src/env.rs b/compiler/rustc_builtin_macros/src/env.rs
index bcff475f626..92da0c069e5 100644
--- a/compiler/rustc_builtin_macros/src/env.rs
+++ b/compiler/rustc_builtin_macros/src/env.rs
@@ -4,7 +4,7 @@
 //
 
 use rustc_ast::tokenstream::TokenStream;
-use rustc_ast::{self as ast, GenericArg};
+use rustc_ast::{self as ast, AstDeref, GenericArg};
 use rustc_expand::base::{self, *};
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use rustc_span::Span;
@@ -76,27 +76,36 @@ pub fn expand_env<'cx>(
         },
     };
 
-    let sp = cx.with_def_site_ctxt(sp);
+    let span = cx.with_def_site_ctxt(sp);
     let value = env::var(var.as_str()).ok().as_deref().map(Symbol::intern);
     cx.sess.parse_sess.env_depinfo.borrow_mut().insert((var, value));
     let e = match value {
         None => {
-            // Use the string literal in the code in the diagnostic to avoid confusing diagnostics,
-            // e.g. when the literal contains escape sequences.
             let ast::ExprKind::Lit(ast::token::Lit {
-                kind: ast::token::LitKind::Str,
-                symbol: original_var,
+                kind: ast::token::LitKind::Str | ast::token::LitKind::StrRaw(..),
+                symbol,
                 ..
             }) = &var_expr.kind
             else {
                 unreachable!("`expr_to_string` ensures this is a string lit")
             };
-            cx.emit_err(errors::EnvNotDefined {
-                span: sp,
-                msg: custom_msg,
-                var: *original_var,
-                help: custom_msg.is_none().then(|| help_for_missing_env_var(var.as_str())),
-            });
+
+            if let Some(msg_from_user) = custom_msg {
+                cx.emit_err(errors::EnvNotDefinedWithUserMessage { span, msg_from_user });
+            } else if is_cargo_env_var(var.as_str()) {
+                cx.emit_err(errors::EnvNotDefined::CargoEnvVar {
+                    span,
+                    var: *symbol,
+                    var_expr: var_expr.ast_deref(),
+                });
+            } else {
+                cx.emit_err(errors::EnvNotDefined::CustomEnvVar {
+                    span,
+                    var: *symbol,
+                    var_expr: var_expr.ast_deref(),
+                });
+            }
+
             return DummyResult::any(sp);
         }
         Some(value) => cx.expr_str(sp, value),
@@ -104,13 +113,9 @@ pub fn expand_env<'cx>(
     MacEager::expr(e)
 }
 
-fn help_for_missing_env_var(var: &str) -> errors::EnvNotDefinedHelp {
-    if var.starts_with("CARGO_")
+/// Returns `true` if an environment variable from `env!` is one used by Cargo.
+fn is_cargo_env_var(var: &str) -> bool {
+    var.starts_with("CARGO_")
         || var.starts_with("DEP_")
         || matches!(var, "OUT_DIR" | "OPT_LEVEL" | "PROFILE" | "HOST" | "TARGET")
-    {
-        errors::EnvNotDefinedHelp::CargoVar
-    } else {
-        errors::EnvNotDefinedHelp::Other
-    }
 }
diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs
index 7b2a375a822..fbf0395bb05 100644
--- a/compiler/rustc_builtin_macros/src/errors.rs
+++ b/compiler/rustc_builtin_macros/src/errors.rs
@@ -440,43 +440,43 @@ pub(crate) struct EnvTakesArgs {
     pub(crate) span: Span,
 }
 
-//#[derive(Diagnostic)]
-//#[diag(builtin_macros_env_not_defined)]
-pub(crate) struct EnvNotDefined {
+pub(crate) struct EnvNotDefinedWithUserMessage {
     pub(crate) span: Span,
-    pub(crate) msg: Option<Symbol>,
-    pub(crate) var: Symbol,
-    pub(crate) help: Option<EnvNotDefinedHelp>,
+    pub(crate) msg_from_user: Symbol,
 }
 
-// Hand-written implementation to support custom user messages
-impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for EnvNotDefined {
+// Hand-written implementation to support custom user messages.
+impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for EnvNotDefinedWithUserMessage {
     #[track_caller]
     fn into_diagnostic(self, handler: &'a Handler) -> DiagnosticBuilder<'a, G> {
-        let mut diag = if let Some(msg) = self.msg {
-            #[expect(
-                rustc::untranslatable_diagnostic,
-                reason = "cannot translate user-provided messages"
-            )]
-            handler.struct_diagnostic(msg.to_string())
-        } else {
-            handler.struct_diagnostic(crate::fluent_generated::builtin_macros_env_not_defined)
-        };
-        diag.set_arg("var", self.var);
+        #[expect(
+            rustc::untranslatable_diagnostic,
+            reason = "cannot translate user-provided messages"
+        )]
+        let mut diag = handler.struct_diagnostic(self.msg_from_user.to_string());
         diag.set_span(self.span);
-        if let Some(help) = self.help {
-            diag.subdiagnostic(help);
-        }
         diag
     }
 }
 
-#[derive(Subdiagnostic)]
-pub(crate) enum EnvNotDefinedHelp {
+#[derive(Diagnostic)]
+pub(crate) enum EnvNotDefined<'a> {
+    #[diag(builtin_macros_env_not_defined)]
     #[help(builtin_macros_cargo)]
-    CargoVar,
-    #[help(builtin_macros_other)]
-    Other,
+    CargoEnvVar {
+        #[primary_span]
+        span: Span,
+        var: Symbol,
+        var_expr: &'a rustc_ast::Expr,
+    },
+    #[diag(builtin_macros_env_not_defined)]
+    #[help(builtin_macros_custom)]
+    CustomEnvVar {
+        #[primary_span]
+        span: Span,
+        var: Symbol,
+        var_expr: &'a rustc_ast::Expr,
+    },
 }
 
 #[derive(Diagnostic)]
diff --git a/compiler/rustc_codegen_cranelift/.github/workflows/main.yml b/compiler/rustc_codegen_cranelift/.github/workflows/main.yml
index 8e6c1e8ade0..652d6eca3f6 100644
--- a/compiler/rustc_codegen_cranelift/.github/workflows/main.yml
+++ b/compiler/rustc_codegen_cranelift/.github/workflows/main.yml
@@ -12,9 +12,11 @@ jobs:
     steps:
     - uses: actions/checkout@v3
 
-    - name: Install rustfmt
+    - name: Avoid installing rustc-dev
       run: |
-        rustup component add rustfmt
+        sed -i 's/components.*/components = ["rustfmt"]/' rust-toolchain
+        echo 'profile = "minimal"' >> rust-toolchain
+        rustfmt -v
 
     - name: Rustfmt
       run: |
@@ -127,7 +129,7 @@ jobs:
     - uses: actions/checkout@v3
 
     - name: Prepare dependencies
-      run: ./y.rs prepare
+      run: ./y.sh prepare
 
     - name: Disable JIT tests
       run: |
@@ -136,7 +138,7 @@ jobs:
     - name: Test
       env:
         TARGET_TRIPLE: x86_64-unknown-linux-gnu
-      run: ./y.rs test --use-backend llvm
+      run: ./y.sh test --use-backend llvm
 
   bench:
     runs-on: ubuntu-latest
diff --git a/compiler/rustc_codegen_cranelift/Cargo.lock b/compiler/rustc_codegen_cranelift/Cargo.lock
index 904233d4242..af8e43da4ea 100644
--- a/compiler/rustc_codegen_cranelift/Cargo.lock
+++ b/compiler/rustc_codegen_cranelift/Cargo.lock
@@ -51,18 +51,18 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
 [[package]]
 name = "cranelift-bforest"
-version = "0.96.1"
+version = "0.98.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9b6160c0a96253993b79fb7e0983534a4515ecf666120ddf8f92068114997ebc"
+checksum = "ec27af72e56235eb326b5bf2de4e70ab7c5ac1fb683a1829595badaf821607fd"
 dependencies = [
  "cranelift-entity",
 ]
 
 [[package]]
 name = "cranelift-codegen"
-version = "0.96.1"
+version = "0.98.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7b38da5f63562e42f3c929d7c76871098e5ad12c8ab44b0659ffc529f22a5b3a"
+checksum = "2231e12925e6c5f4bc9c95b62a798eea6ed669a95bc3e00f8b2adb3b7b9b7a80"
 dependencies = [
  "bumpalo",
  "cranelift-bforest",
@@ -81,39 +81,39 @@ dependencies = [
 
 [[package]]
 name = "cranelift-codegen-meta"
-version = "0.96.1"
+version = "0.98.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "011371e213e163b55dd9e8404b3f2d9fa52cd14dc2f3dc5b83e61ffceff126db"
+checksum = "413b00b8dfb3aab85674a534677e7ca08854b503f164a70ec0634fce80996e2c"
 dependencies = [
  "cranelift-codegen-shared",
 ]
 
 [[package]]
 name = "cranelift-codegen-shared"
-version = "0.96.1"
+version = "0.98.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1bf97dde7f5ad571161cdd203a2c9c88682ef669830aea3c14ea5d164ef8bb43"
+checksum = "cd0feb9ecc8193ef5cb04f494c5bd835e5bfec4bde726e7ac0444fc9dd76229e"
 
 [[package]]
 name = "cranelift-control"
-version = "0.96.1"
+version = "0.98.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fd9a9254aee733b0f2b68e5eaaf0337ad53cb23252a056c10a35370551be8d40"
+checksum = "72eedd2afcf5fee1e042eaaf18d3750e48ad0eca364a9f5971ecfdd5ef85bf71"
 dependencies = [
  "arbitrary",
 ]
 
 [[package]]
 name = "cranelift-entity"
-version = "0.96.1"
+version = "0.98.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "baf39a33ee39479d1337cd9333f3c09786c5a0ca1ec509edcaf9d1346d5de0e5"
+checksum = "7af19157be42671073cf8c2a52d6a4ae1e7b11f1dcb4131fede356d9f91c29dd"
 
 [[package]]
 name = "cranelift-frontend"
-version = "0.96.1"
+version = "0.98.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "65e260b92a193a0a2dccc3938f133d9532e7dcfe8d03e36bf8b7d3518c1c1793"
+checksum = "c2dc7636c5fad156be7d9ae691cd1aaecd97326caf2ab534ba168056d56aa76c"
 dependencies = [
  "cranelift-codegen",
  "log",
@@ -123,15 +123,15 @@ dependencies = [
 
 [[package]]
 name = "cranelift-isle"
-version = "0.96.1"
+version = "0.98.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9446c8e1aadfcdacee1a49592bc2c25d1d9bf5484782c163e7f5485c92cd3c1c"
+checksum = "c1111aea4fb6fade5779903f184249a3fc685a799fe4ec59126f9af59c7c2a74"
 
 [[package]]
 name = "cranelift-jit"
-version = "0.96.1"
+version = "0.98.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "689a6df165d0f860c1e1a3d53c28944e2743c3e9ee4c678cf190fe60ad7a6ef5"
+checksum = "dadf88076317f6286ec77ebbe65978734fb43b6befdc96f52ff4c4c511841644"
 dependencies = [
  "anyhow",
  "cranelift-codegen",
@@ -149,9 +149,9 @@ dependencies = [
 
 [[package]]
 name = "cranelift-module"
-version = "0.96.1"
+version = "0.98.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b1402d6ff1695b429536b2eaa126db560fc94c375ed0e9cfb15051fc07427f7"
+checksum = "c6bae8a82dbf82241b1083e57e06870d2c2bdc9852727be99d58477513816953"
 dependencies = [
  "anyhow",
  "cranelift-codegen",
@@ -160,9 +160,9 @@ dependencies = [
 
 [[package]]
 name = "cranelift-native"
-version = "0.96.1"
+version = "0.98.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eac916f3c5aff4b817e42fc2e682292b931495b3fe2603d5e3c3cf602d74e344"
+checksum = "1ecfc01a634448468a698beac433d98040033046678a0eed3ca39a3a9f63ae86"
 dependencies = [
  "cranelift-codegen",
  "libc",
@@ -171,9 +171,9 @@ dependencies = [
 
 [[package]]
 name = "cranelift-object"
-version = "0.96.1"
+version = "0.98.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "23860f4cd064017f2108e6bc5d25660a77cd6eea77f1ac0756870a00abb12e93"
+checksum = "0ee14a7276999f0dcaae2de84043e2c2de50820fb89b3db56fab586a4ad26734"
 dependencies = [
  "anyhow",
  "cranelift-codegen",
@@ -194,6 +194,12 @@ dependencies = [
 ]
 
 [[package]]
+name = "equivalent"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "88bffebc5d80432c9b140ee17875ff173a8ab62faad5b257da912bd2f6c1c0a1"
+
+[[package]]
 name = "fallible-iterator"
 version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -206,7 +212,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4"
 dependencies = [
  "fallible-iterator",
- "indexmap",
+ "indexmap 1.9.3",
  "stable_deref_trait",
 ]
 
@@ -226,6 +232,12 @@ dependencies = [
 ]
 
 [[package]]
+name = "hashbrown"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
+
+[[package]]
 name = "indexmap"
 version = "1.9.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -236,6 +248,16 @@ dependencies = [
 ]
 
 [[package]]
+name = "indexmap"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d"
+dependencies = [
+ "equivalent",
+ "hashbrown 0.14.0",
+]
+
+[[package]]
 name = "libc"
 version = "0.2.138"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -283,7 +305,7 @@ checksum = "03b4680b86d9cfafba8fc491dc9b6df26b68cf40e9e6cd73909194759a63c385"
 dependencies = [
  "crc32fast",
  "hashbrown 0.13.2",
- "indexmap",
+ "indexmap 1.9.3",
  "memchr",
 ]
 
@@ -295,9 +317,9 @@ checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
 
 [[package]]
 name = "regalloc2"
-version = "0.8.1"
+version = "0.9.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d4a52e724646c6c0800fc456ec43b4165d2f91fba88ceaca06d9e0b400023478"
+checksum = "5b4dcbd3a2ae7fb94b5813fa0e957c6ab51bf5d0a8ee1b69e0c2d0f1e6eb8485"
 dependencies = [
  "hashbrown 0.13.2",
  "log",
@@ -335,7 +357,7 @@ dependencies = [
  "cranelift-native",
  "cranelift-object",
  "gimli",
- "indexmap",
+ "indexmap 2.0.0",
  "libloading",
  "object",
  "smallvec",
@@ -374,9 +396,9 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
 
 [[package]]
 name = "wasmtime-jit-icache-coherence"
-version = "9.0.1"
+version = "11.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d90933b781e1cef7656baed671c7a90bdba0c1c694e04fdd4124419308f5cbb"
+checksum = "e34eb67f0829a5614ec54716c8e0c9fe68fab7b9df3686c85f719c9d247f7169"
 dependencies = [
  "cfg-if",
  "libc",
diff --git a/compiler/rustc_codegen_cranelift/Cargo.toml b/compiler/rustc_codegen_cranelift/Cargo.toml
index 1c1f2d8577b..8ded81d7399 100644
--- a/compiler/rustc_codegen_cranelift/Cargo.toml
+++ b/compiler/rustc_codegen_cranelift/Cargo.toml
@@ -8,17 +8,17 @@ crate-type = ["dylib"]
 
 [dependencies]
 # These have to be in sync with each other
-cranelift-codegen = {  version = "0.96.1", features = ["unwind", "all-arch"] }
-cranelift-frontend = { version = "0.96.1" }
-cranelift-module = { version = "0.96.1" }
-cranelift-native = { version = "0.96.1" }
-cranelift-jit = { version = "0.96.1", optional = true }
-cranelift-object = { version = "0.96.1" }
+cranelift-codegen = { version = "0.98", features = ["unwind", "all-arch"] }
+cranelift-frontend = { version = "0.98" }
+cranelift-module = { version = "0.98" }
+cranelift-native = { version = "0.98" }
+cranelift-jit = { version = "0.98", optional = true }
+cranelift-object = { version = "0.98" }
 target-lexicon = "0.12.0"
 gimli = { version = "0.27.2", default-features = false, features = ["write"]}
 object = { version = "0.30.3", default-features = false, features = ["std", "read_core", "write", "archive", "coff", "elf", "macho", "pe"] }
 
-indexmap = "1.9.3"
+indexmap = "2.0.0"
 libloading = { version = "0.7.3", optional = true }
 smallvec = "1.8.1"
 
diff --git a/compiler/rustc_codegen_cranelift/Readme.md b/compiler/rustc_codegen_cranelift/Readme.md
index 9469feea0cb..62eaef359af 100644
--- a/compiler/rustc_codegen_cranelift/Readme.md
+++ b/compiler/rustc_codegen_cranelift/Readme.md
@@ -65,12 +65,12 @@ to `./build/host/stage2/bin/`. Note that you would need to do this every time yo
 5. Copy cargo from another toolchain: `cp $(rustup which cargo) .build/<your hostname triple>/stage2/bin/cargo`
    * Another option is to build it at step 3 and copy with other executables at step 4.
 6. Link your new `rustc` to toolchain: `rustup toolchain link stage2 ./build/host/stage2/`.
-7. (Windows only) compile y.rs: `rustc +stage2 -O y.rs`.
-8. You need to prefix every `./y.rs` (or `y` if you built `y.rs`) command by `rustup run stage2` to make cg_clif use your local changes in rustc.
+7. (Windows only) compile the build system: `rustc +stage2 -O build_system/main.rs -o y.exe`.
+8. You need to prefix every `./y.sh` (or `y` if you built `build_system/main.rs` as `y`) command by `rustup run stage2` to make cg_clif use your local changes in rustc.
 
-  * `rustup run stage2 ./y.rs prepare`
-  * `rustup run stage2 ./y.rs build`
-  * (Optional) run tests: `rustup run stage2 ./y.rs test`
+  * `rustup run stage2 ./y.sh prepare`
+  * `rustup run stage2 ./y.sh build`
+  * (Optional) run tests: `rustup run stage2 ./y.sh test`
 9. Now you can use your cg_clif build to compile other Rust programs, e.g. you can open any Rust crate and run commands like `$RustCheckoutDir/compiler/rustc_codegen_cranelift/dist/cargo-clif build --release`.
 
 ## Configuration
diff --git a/compiler/rustc_codegen_cranelift/build_system/bench.rs b/compiler/rustc_codegen_cranelift/build_system/bench.rs
index 2bb11800034..cec608ea042 100644
--- a/compiler/rustc_codegen_cranelift/build_system/bench.rs
+++ b/compiler/rustc_codegen_cranelift/build_system/bench.rs
@@ -1,4 +1,5 @@
 use std::env;
+use std::io::Write;
 use std::path::Path;
 
 use super::path::{Dirs, RelPath};
@@ -30,6 +31,12 @@ fn benchmark_simple_raytracer(dirs: &Dirs, bootstrap_host_compiler: &Compiler) {
 
     let bench_runs = env::var("BENCH_RUNS").unwrap_or_else(|_| "10".to_string()).parse().unwrap();
 
+    let mut gha_step_summary = if let Ok(file) = std::env::var("GITHUB_STEP_SUMMARY") {
+        Some(std::fs::OpenOptions::new().append(true).open(file).unwrap())
+    } else {
+        None
+    };
+
     eprintln!("[BENCH COMPILE] ebobby/simple-raytracer");
     let cargo_clif = RelPath::DIST
         .to_path(dirs)
@@ -60,36 +67,64 @@ fn benchmark_simple_raytracer(dirs: &Dirs, bootstrap_host_compiler: &Compiler) {
         target_dir = target_dir.display(),
     );
 
+    let bench_compile_markdown = RelPath::DIST.to_path(dirs).join("bench_compile.md");
+
     let bench_compile = hyperfine_command(
         1,
         bench_runs,
         Some(&clean_cmd),
-        &[&llvm_build_cmd, &clif_build_cmd, &clif_build_opt_cmd],
+        &[
+            ("cargo build", &llvm_build_cmd),
+            ("cargo-clif build", &clif_build_cmd),
+            ("cargo-clif build --release", &clif_build_opt_cmd),
+        ],
+        &bench_compile_markdown,
     );
 
     spawn_and_wait(bench_compile);
 
+    if let Some(gha_step_summary) = gha_step_summary.as_mut() {
+        gha_step_summary.write_all(b"## Compile ebobby/simple-raytracer\n\n").unwrap();
+        gha_step_summary.write_all(&std::fs::read(bench_compile_markdown).unwrap()).unwrap();
+        gha_step_summary.write_all(b"\n").unwrap();
+    }
+
     eprintln!("[BENCH RUN] ebobby/simple-raytracer");
 
+    let bench_run_markdown = RelPath::DIST.to_path(dirs).join("bench_run.md");
+
+    let raytracer_cg_llvm = Path::new(".").join(get_file_name(
+        &bootstrap_host_compiler.rustc,
+        "raytracer_cg_llvm",
+        "bin",
+    ));
+    let raytracer_cg_clif = Path::new(".").join(get_file_name(
+        &bootstrap_host_compiler.rustc,
+        "raytracer_cg_clif",
+        "bin",
+    ));
+    let raytracer_cg_clif_opt = Path::new(".").join(get_file_name(
+        &bootstrap_host_compiler.rustc,
+        "raytracer_cg_clif_opt",
+        "bin",
+    ));
     let mut bench_run = hyperfine_command(
         0,
         bench_runs,
         None,
         &[
-            Path::new(".")
-                .join(get_file_name(&bootstrap_host_compiler.rustc, "raytracer_cg_llvm", "bin"))
-                .to_str()
-                .unwrap(),
-            Path::new(".")
-                .join(get_file_name(&bootstrap_host_compiler.rustc, "raytracer_cg_clif", "bin"))
-                .to_str()
-                .unwrap(),
-            Path::new(".")
-                .join(get_file_name(&bootstrap_host_compiler.rustc, "raytracer_cg_clif_opt", "bin"))
-                .to_str()
-                .unwrap(),
+            ("", raytracer_cg_llvm.to_str().unwrap()),
+            ("", raytracer_cg_clif.to_str().unwrap()),
+            ("", raytracer_cg_clif_opt.to_str().unwrap()),
         ],
+        &bench_run_markdown,
     );
     bench_run.current_dir(RelPath::BUILD.to_path(dirs));
     spawn_and_wait(bench_run);
+
+    if let Some(gha_step_summary) = gha_step_summary.as_mut() {
+        gha_step_summary.write_all(b"## Run ebobby/simple-raytracer\n\n").unwrap();
+        gha_step_summary.write_all(&std::fs::read(bench_run_markdown).unwrap()).unwrap();
+        gha_step_summary.write_all(b"\n").unwrap();
+    }
 }
diff --git a/compiler/rustc_codegen_cranelift/build_system/build_backend.rs b/compiler/rustc_codegen_cranelift/build_system/build_backend.rs
index 6855c1a7fc5..1c5db23299d 100644
--- a/compiler/rustc_codegen_cranelift/build_system/build_backend.rs
+++ b/compiler/rustc_codegen_cranelift/build_system/build_backend.rs
@@ -3,7 +3,7 @@ use std::path::PathBuf;
 
 use super::path::{Dirs, RelPath};
 use super::rustc_info::get_file_name;
-use super::utils::{is_ci, is_ci_opt, maybe_incremental, CargoProject, Compiler};
+use super::utils::{is_ci, is_ci_opt, maybe_incremental, CargoProject, Compiler, LogGroup};
 
 pub(crate) static CG_CLIF: CargoProject = CargoProject::new(&RelPath::SOURCE, "cg_clif");
 
@@ -13,6 +13,8 @@ pub(crate) fn build_backend(
     bootstrap_host_compiler: &Compiler,
     use_unstable_features: bool,
 ) -> PathBuf {
+    let _group = LogGroup::guard("Build backend");
+
     let mut cmd = CG_CLIF.build(&bootstrap_host_compiler, dirs);
     maybe_incremental(&mut cmd);
 
diff --git a/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs b/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs
index 74bba9ed5eb..04097936d03 100644
--- a/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs
+++ b/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs
@@ -6,6 +6,7 @@ use super::path::{Dirs, RelPath};
 use super::rustc_info::get_file_name;
 use super::utils::{
     maybe_incremental, remove_dir_if_exists, spawn_and_wait, try_hard_link, CargoProject, Compiler,
+    LogGroup,
 };
 use super::{CodegenBackend, SysrootKind};
 
@@ -22,6 +23,8 @@ pub(crate) fn build_sysroot(
     rustup_toolchain_name: Option<&str>,
     target_triple: String,
 ) -> Compiler {
+    let _guard = LogGroup::guard("Build sysroot");
+
     eprintln!("[BUILD] sysroot {:?}", sysroot_kind);
 
     DIST_DIR.ensure_fresh(dirs);
@@ -251,7 +254,10 @@ fn build_clif_sysroot_for_triple(
     rustflags
         .push_str(&format!(" --sysroot {}", RTSTARTUP_SYSROOT.to_path(dirs).to_str().unwrap()));
     if channel == "release" {
-        rustflags.push_str(" -Zmir-opt-level=3");
+        // Incremental compilation by default disables mir inlining. This leads to both a decent
+        // compile perf and a significant runtime perf regression. As such forcefully enable mir
+        // inlining.
+        rustflags.push_str(" -Zinline-mir");
     }
     compiler.rustflags += &rustflags;
     let mut build_cmd = STANDARD_LIBRARY.build(&compiler, dirs);
diff --git a/compiler/rustc_codegen_cranelift/build_system/prepare.rs b/compiler/rustc_codegen_cranelift/build_system/prepare.rs
index e31e39a483f..3ee2e8f4a4e 100644
--- a/compiler/rustc_codegen_cranelift/build_system/prepare.rs
+++ b/compiler/rustc_codegen_cranelift/build_system/prepare.rs
@@ -27,6 +27,7 @@ pub(crate) fn prepare_stdlib(dirs: &Dirs, rustc: &Path) {
         STDLIB_SRC.to_path(dirs).join("Cargo.toml"),
         r#"
 [workspace]
+resolver = "1"
 members = ["./library/sysroot"]
 
 [patch.crates-io]
diff --git a/compiler/rustc_codegen_cranelift/build_system/tests.rs b/compiler/rustc_codegen_cranelift/build_system/tests.rs
index 08d8f708c7d..0254d18cf7c 100644
--- a/compiler/rustc_codegen_cranelift/build_system/tests.rs
+++ b/compiler/rustc_codegen_cranelift/build_system/tests.rs
@@ -3,7 +3,7 @@ use super::config;
 use super::path::{Dirs, RelPath};
 use super::prepare::{apply_patches, GitRepo};
 use super::rustc_info::get_default_sysroot;
-use super::utils::{spawn_and_wait, spawn_and_wait_with_input, CargoProject, Compiler};
+use super::utils::{spawn_and_wait, spawn_and_wait_with_input, CargoProject, Compiler, LogGroup};
 use super::{CodegenBackend, SysrootKind};
 use std::env;
 use std::ffi::OsStr;
@@ -21,6 +21,7 @@ struct TestCase {
 enum TestCaseCmd {
     Custom { func: &'static dyn Fn(&TestRunner<'_>) },
     BuildLib { source: &'static str, crate_types: &'static str },
+    BuildBin { source: &'static str },
     BuildBinAndRun { source: &'static str, args: &'static [&'static str] },
     JitBin { source: &'static str, args: &'static str },
 }
@@ -39,6 +40,10 @@ impl TestCase {
         Self { config, cmd: TestCaseCmd::BuildLib { source, crate_types } }
     }
 
+    const fn build_bin(config: &'static str, source: &'static str) -> Self {
+        Self { config, cmd: TestCaseCmd::BuildBin { source } }
+    }
+
     const fn build_bin_and_run(
         config: &'static str,
         source: &'static str,
@@ -92,6 +97,7 @@ const BASE_SYSROOT_SUITE: &[TestCase] = &[
     TestCase::build_bin_and_run("aot.float-minmax-pass", "example/float-minmax-pass.rs", &[]),
     TestCase::build_bin_and_run("aot.mod_bench", "example/mod_bench.rs", &[]),
     TestCase::build_bin_and_run("aot.issue-72793", "example/issue-72793.rs", &[]),
+    TestCase::build_bin("aot.issue-59326", "example/issue-59326.rs"),
 ];
 
 // FIXME(rust-random/rand#1293): Newer rand versions fail to test on Windows. Update once this is
@@ -119,8 +125,8 @@ pub(crate) static REGEX: CargoProject = CargoProject::new(&REGEX_REPO.source_dir
 pub(crate) static PORTABLE_SIMD_REPO: GitRepo = GitRepo::github(
     "rust-lang",
     "portable-simd",
-    "ad8afa8c81273b3b49acbea38cd3bcf17a34cf2b",
-    "800548f8000e31bd",
+    "7c7dbe0c505ccbc02ff30c1e37381ab1d47bf46f",
+    "5bcc9c544f6fa7bd",
     "portable-simd",
 );
 
@@ -380,15 +386,17 @@ impl<'a> TestRunner<'a> {
             let tag = tag.to_uppercase();
             let is_jit_test = tag == "JIT";
 
-            if !config::get_bool(config)
+            let _guard = if !config::get_bool(config)
                 || (is_jit_test && !self.jit_supported)
                 || self.skip_tests.contains(&config)
             {
                 eprintln!("[{tag}] {testname} (skipped)");
                 continue;
             } else {
+                let guard = LogGroup::guard(&format!("[{tag}] {testname}"));
                 eprintln!("[{tag}] {testname}");
-            }
+                guard
+            };
 
             match *cmd {
                 TestCaseCmd::Custom { func } => func(self),
@@ -405,6 +413,13 @@ impl<'a> TestRunner<'a> {
                         ]);
                     }
                 }
+                TestCaseCmd::BuildBin { source } => {
+                    if self.use_unstable_features {
+                        self.run_rustc([source]);
+                    } else {
+                        self.run_rustc([source, "--cfg", "no_unstable_features"]);
+                    }
+                }
                 TestCaseCmd::BuildBinAndRun { source, args } => {
                     if self.use_unstable_features {
                         self.run_rustc([source]);
diff --git a/compiler/rustc_codegen_cranelift/build_system/utils.rs b/compiler/rustc_codegen_cranelift/build_system/utils.rs
index 41fc366e290..97c82d501c5 100644
--- a/compiler/rustc_codegen_cranelift/build_system/utils.rs
+++ b/compiler/rustc_codegen_cranelift/build_system/utils.rs
@@ -3,6 +3,7 @@ use std::fs;
 use std::io::{self, Write};
 use std::path::{Path, PathBuf};
 use std::process::{self, Command, Stdio};
+use std::sync::atomic::{AtomicBool, Ordering};
 
 use super::path::{Dirs, RelPath};
 
@@ -136,10 +137,13 @@ pub(crate) fn hyperfine_command(
     warmup: u64,
     runs: u64,
     prepare: Option<&str>,
-    cmds: &[&str],
+    cmds: &[(&str, &str)],
+    markdown_export: &Path,
 ) -> Command {
     let mut bench = Command::new("hyperfine");
 
+    bench.arg("--export-markdown").arg(markdown_export);
+
     if warmup != 0 {
         bench.arg("--warmup").arg(warmup.to_string());
     }
@@ -152,7 +156,12 @@ pub(crate) fn hyperfine_command(
         bench.arg("--prepare").arg(prepare);
     }
 
-    bench.args(cmds);
+    for &(name, cmd) in cmds {
+        if name != "" {
+            bench.arg("-n").arg(name);
+        }
+        bench.arg(cmd);
+    }
 
     bench
 }
@@ -167,6 +176,8 @@ pub(crate) fn git_command<'a>(repo_dir: impl Into<Option<&'a Path>>, cmd: &str)
         .arg("user.email=dummy@example.com")
         .arg("-c")
         .arg("core.autocrlf=false")
+        .arg("-c")
+        .arg("commit.gpgSign=false")
         .arg(cmd);
     if let Some(repo_dir) = repo_dir.into() {
         git_cmd.current_dir(repo_dir);
@@ -259,6 +270,33 @@ pub(crate) fn is_ci_opt() -> bool {
     env::var("CI_OPT").is_ok()
 }
 
+static IN_GROUP: AtomicBool = AtomicBool::new(false);
+pub(crate) struct LogGroup {
+    is_gha: bool,
+}
+
+impl LogGroup {
+    pub(crate) fn guard(name: &str) -> LogGroup {
+        let is_gha = env::var("GITHUB_ACTIONS").is_ok();
+
+        assert!(!IN_GROUP.swap(true, Ordering::SeqCst));
+        if is_gha {
+            eprintln!("::group::{name}");
+        }
+
+        LogGroup { is_gha }
+    }
+}
+
+impl Drop for LogGroup {
+    fn drop(&mut self) {
+        if self.is_gha {
+            eprintln!("::endgroup::");
+        }
+        IN_GROUP.store(false, Ordering::SeqCst);
+    }
+}
+
 pub(crate) fn maybe_incremental(cmd: &mut Command) {
     if is_ci() || std::env::var("CARGO_BUILD_INCREMENTAL").map_or(false, |val| val == "false") {
         // Disabling incr comp reduces cache size and incr comp doesn't save as much on CI anyway
diff --git a/compiler/rustc_codegen_cranelift/config.txt b/compiler/rustc_codegen_cranelift/config.txt
index d6e3924a24d..fa1c9f4259c 100644
--- a/compiler/rustc_codegen_cranelift/config.txt
+++ b/compiler/rustc_codegen_cranelift/config.txt
@@ -41,6 +41,7 @@ aot.track-caller-attribute
 aot.float-minmax-pass
 aot.mod_bench
 aot.issue-72793
+aot.issue-59326
 
 testsuite.extended_sysroot
 test.rust-random/rand
diff --git a/compiler/rustc_codegen_cranelift/example/issue-59326.rs b/compiler/rustc_codegen_cranelift/example/issue-59326.rs
new file mode 100644
index 00000000000..70b7c94e15c
--- /dev/null
+++ b/compiler/rustc_codegen_cranelift/example/issue-59326.rs
@@ -0,0 +1,27 @@
+// Based on https://github.com/rust-lang/rust/blob/689511047a75a30825e367d4fd45c74604d0b15e/tests/ui/issues/issue-59326.rs#L1
+// check-pass
+trait Service {
+    type S;
+}
+
+trait Framing {
+    type F;
+}
+
+impl Framing for () {
+    type F = ();
+}
+
+trait HttpService<F: Framing>: Service<S = F::F> {}
+
+type BoxService = Box<dyn HttpService<(), S = ()>>;
+
+fn build_server<F: FnOnce() -> BoxService>(_: F) {}
+
+fn make_server<F: Framing>() -> Box<dyn HttpService<F, S = F::F>> {
+    unimplemented!()
+}
+
+fn main() {
+    build_server(|| make_server())
+}
diff --git a/compiler/rustc_codegen_cranelift/example/mini_core.rs b/compiler/rustc_codegen_cranelift/example/mini_core.rs
index 79ca4c03985..9ecc4c5dd5b 100644
--- a/compiler/rustc_codegen_cranelift/example/mini_core.rs
+++ b/compiler/rustc_codegen_cranelift/example/mini_core.rs
@@ -547,7 +547,9 @@ impl<T> Box<T> {
 impl<T: ?Sized, A> Drop for Box<T, A> {
     fn drop(&mut self) {
         // inner value is dropped by compiler
-        libc::free(self.0.pointer.0 as *mut u8);
+        unsafe {
+            libc::free(self.0.pointer.0 as *mut u8);
+        }
     }
 }
 
diff --git a/compiler/rustc_codegen_cranelift/example/std_example.rs b/compiler/rustc_codegen_cranelift/example/std_example.rs
index 1bf0ff64c92..490cc2404f6 100644
--- a/compiler/rustc_codegen_cranelift/example/std_example.rs
+++ b/compiler/rustc_codegen_cranelift/example/std_example.rs
@@ -1,4 +1,12 @@
-#![feature(core_intrinsics, generators, generator_trait, is_sorted, repr_simd)]
+#![feature(
+    core_intrinsics,
+    generators,
+    generator_trait,
+    is_sorted,
+    repr_simd,
+    tuple_trait,
+    unboxed_closures
+)]
 
 #[cfg(target_arch = "x86_64")]
 use std::arch::x86_64::*;
@@ -155,12 +163,34 @@ fn main() {
     }
 
     foo(I64X2(0, 0));
+
+    transmute_fat_pointer();
+
+    rust_call_abi();
 }
 
 fn panic(_: u128) {
     panic!();
 }
 
+use std::mem::transmute;
+
+#[cfg(target_pointer_width = "32")]
+type TwoPtrs = i64;
+#[cfg(target_pointer_width = "64")]
+type TwoPtrs = i128;
+
+fn transmute_fat_pointer() -> TwoPtrs {
+    unsafe { transmute::<_, TwoPtrs>("true !") }
+}
+
+extern "rust-call" fn rust_call_abi_callee<T: std::marker::Tuple>(_: T) {}
+
+fn rust_call_abi() {
+    rust_call_abi_callee(());
+    rust_call_abi_callee((1, 2));
+}
+
 #[repr(simd)]
 struct I64X2(i64, i64);
 
diff --git a/compiler/rustc_codegen_cranelift/patches/0027-coretests-128bit-atomic-operations.patch b/compiler/rustc_codegen_cranelift/patches/0027-coretests-128bit-atomic-operations.patch
index 1d5479bedde..a650e10110b 100644
--- a/compiler/rustc_codegen_cranelift/patches/0027-coretests-128bit-atomic-operations.patch
+++ b/compiler/rustc_codegen_cranelift/patches/0027-coretests-128bit-atomic-operations.patch
@@ -10,6 +10,18 @@ Cranelift doesn't support them yet
  library/core/tests/atomic.rs          |  4 ---
  4 files changed, 4 insertions(+), 50 deletions(-)
 
+diff --git a/lib.rs b/lib.rs
+index 897a5e9..331f66f 100644
+--- a/lib.rs
++++ b/lib.rs
+@@ -93,7 +93,6 @@
+ #![feature(const_option)]
+ #![feature(const_option_ext)]
+ #![feature(const_result)]
+-#![cfg_attr(target_has_atomic = "128", feature(integer_atomics))]
+ #![feature(int_roundings)]
+ #![feature(slice_group_by)]
+ #![feature(split_array)]
 diff --git a/atomic.rs b/atomic.rs
 index b735957..ea728b6 100644
 --- a/atomic.rs
diff --git a/compiler/rustc_codegen_cranelift/patches/0027-stdlib-128bit-atomic-operations.patch b/compiler/rustc_codegen_cranelift/patches/0027-stdlib-128bit-atomic-operations.patch
index 45f73f36b93..646928893e9 100644
--- a/compiler/rustc_codegen_cranelift/patches/0027-stdlib-128bit-atomic-operations.patch
+++ b/compiler/rustc_codegen_cranelift/patches/0027-stdlib-128bit-atomic-operations.patch
@@ -38,9 +38,9 @@ diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs
 index d9de37e..8293fce 100644
 --- a/library/core/src/sync/atomic.rs
 +++ b/library/core/src/sync/atomic.rs
-@@ -2234,46 +2234,6 @@ atomic_int! {
-     "AtomicU64::new(0)",
-     u64 AtomicU64 ATOMIC_U64_INIT
+@@ -2996,42 +2996,6 @@ atomic_int! {
+     8,
+     u64 AtomicU64
  }
 -#[cfg(target_has_atomic_load_store = "128")]
 -atomic_int! {
@@ -53,14 +53,12 @@ index d9de37e..8293fce 100644
 -    unstable(feature = "integer_atomics", issue = "99069"),
 -    unstable(feature = "integer_atomics", issue = "99069"),
 -    rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"),
--    unstable(feature = "integer_atomics", issue = "99069"),
 -    cfg_attr(not(test), rustc_diagnostic_item = "AtomicI128"),
 -    "i128",
 -    "#![feature(integer_atomics)]\n\n",
 -    atomic_min, atomic_max,
 -    16,
--    "AtomicI128::new(0)",
--    i128 AtomicI128 ATOMIC_I128_INIT
+-    i128 AtomicI128
 -}
 -#[cfg(target_has_atomic_load_store = "128")]
 -atomic_int! {
@@ -73,16 +71,15 @@ index d9de37e..8293fce 100644
 -    unstable(feature = "integer_atomics", issue = "99069"),
 -    unstable(feature = "integer_atomics", issue = "99069"),
 -    rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"),
--    unstable(feature = "integer_atomics", issue = "99069"),
 -    cfg_attr(not(test), rustc_diagnostic_item = "AtomicU128"),
 -    "u128",
 -    "#![feature(integer_atomics)]\n\n",
 -    atomic_umin, atomic_umax,
 -    16,
--    "AtomicU128::new(0)",
--    u128 AtomicU128 ATOMIC_U128_INIT
+-    u128 AtomicU128
 -}
  
+ #[cfg(target_has_atomic_load_store = "ptr")]
  macro_rules! atomic_int_ptr_sized {
      ( $($target_pointer_width:literal $align:literal)* ) => { $(
 --
diff --git a/compiler/rustc_codegen_cranelift/patches/stdlib-lock.toml b/compiler/rustc_codegen_cranelift/patches/stdlib-lock.toml
index 1dde9e54d7e..aea47bdfba2 100644
--- a/compiler/rustc_codegen_cranelift/patches/stdlib-lock.toml
+++ b/compiler/rustc_codegen_cranelift/patches/stdlib-lock.toml
@@ -4,9 +4,9 @@ version = 3
 
 [[package]]
 name = "addr2line"
-version = "0.19.0"
+version = "0.20.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97"
+checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3"
 dependencies = [
  "compiler_builtins",
  "gimli",
@@ -35,6 +35,12 @@ dependencies = [
 ]
 
 [[package]]
+name = "allocator-api2"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56fc6cf8dc8c4158eed8649f9b8b0ea1518eb62b544fe9490d66fa0b349eafe9"
+
+[[package]]
 name = "auxv"
 version = "0.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -68,9 +74,9 @@ dependencies = [
 
 [[package]]
 name = "compiler_builtins"
-version = "0.1.93"
+version = "0.1.95"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "76630810d973ecea3dbf611e1b7aecfb1012751ef1ff8de3998f89014a166781"
+checksum = "6866e0f3638013234db3c89ead7a14d278354338e7237257407500009012b23f"
 dependencies = [
  "cc",
  "rustc-std-workspace-core",
@@ -145,10 +151,11 @@ dependencies = [
 
 [[package]]
 name = "hashbrown"
-version = "0.13.2"
+version = "0.14.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e"
+checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
 dependencies = [
+ "allocator-api2",
  "compiler_builtins",
  "rustc-std-workspace-alloc",
  "rustc-std-workspace-core",
@@ -156,9 +163,9 @@ dependencies = [
 
 [[package]]
 name = "hermit-abi"
-version = "0.3.1"
+version = "0.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
+checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b"
 dependencies = [
  "compiler_builtins",
  "rustc-std-workspace-alloc",
@@ -186,9 +193,9 @@ dependencies = [
 
 [[package]]
 name = "miniz_oxide"
-version = "0.6.2"
+version = "0.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa"
+checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
 dependencies = [
  "adler",
  "compiler_builtins",
@@ -198,9 +205,9 @@ dependencies = [
 
 [[package]]
 name = "object"
-version = "0.30.4"
+version = "0.31.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "03b4680b86d9cfafba8fc491dc9b6df26b68cf40e9e6cd73909194759a63c385"
+checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1"
 dependencies = [
  "compiler_builtins",
  "memchr",
diff --git a/compiler/rustc_codegen_cranelift/rust-toolchain b/compiler/rustc_codegen_cranelift/rust-toolchain
index fa3a10b9adc..34514658359 100644
--- a/compiler/rustc_codegen_cranelift/rust-toolchain
+++ b/compiler/rustc_codegen_cranelift/rust-toolchain
@@ -1,3 +1,3 @@
 [toolchain]
-channel = "nightly-2023-06-15"
+channel = "nightly-2023-07-22"
 components = ["rust-src", "rustc-dev", "llvm-tools"]
diff --git a/compiler/rustc_codegen_cranelift/scripts/cargo-clif.rs b/compiler/rustc_codegen_cranelift/scripts/cargo-clif.rs
index 99b97be24e6..f73b2012684 100644
--- a/compiler/rustc_codegen_cranelift/scripts/cargo-clif.rs
+++ b/compiler/rustc_codegen_cranelift/scripts/cargo-clif.rs
@@ -40,14 +40,22 @@ fn main() {
         "cargo"
     };
 
-    let args: Vec<_> = match env::args().nth(1).as_deref() {
+    let mut args = env::args().skip(1).collect::<Vec<_>>();
+    if args.get(0).map(|arg| &**arg) == Some("clif") {
+        // Avoid infinite recursion when invoking `cargo-clif` as cargo subcommand using
+        // `cargo clif`.
+        args.remove(0);
+    }
+
+    let args: Vec<_> = match args.get(0).map(|arg| &**arg) {
         Some("jit") => {
             env::set_var(
                 "RUSTFLAGS",
                 env::var("RUSTFLAGS").unwrap_or(String::new()) + " -Cprefer-dynamic",
             );
+            args.remove(0);
             IntoIterator::into_iter(["rustc".to_string()])
-                .chain(env::args().skip(2))
+                .chain(args)
                 .chain([
                     "--".to_string(),
                     "-Zunstable-options".to_string(),
@@ -60,8 +68,9 @@ fn main() {
                 "RUSTFLAGS",
                 env::var("RUSTFLAGS").unwrap_or(String::new()) + " -Cprefer-dynamic",
             );
+            args.remove(0);
             IntoIterator::into_iter(["rustc".to_string()])
-                .chain(env::args().skip(2))
+                .chain(args)
                 .chain([
                     "--".to_string(),
                     "-Zunstable-options".to_string(),
@@ -69,7 +78,7 @@ fn main() {
                 ])
                 .collect()
         }
-        _ => env::args().skip(1).collect(),
+        _ => args,
     };
 
     #[cfg(unix)]
diff --git a/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh b/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh
index 15b16b42be5..e6bbac647e5 100644
--- a/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh
+++ b/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh
@@ -10,7 +10,8 @@ git fetch
 git checkout -- .
 git checkout "$(rustc -V | cut -d' ' -f3 | tr -d '(')"
 
-git -c user.name=Dummy -c user.email=dummy@example.com am ../patches/*-stdlib-*.patch
+git -c user.name=Dummy -c user.email=dummy@example.com -c commit.gpgSign=false \
+    am ../patches/*-stdlib-*.patch
 
 git apply - <<EOF
 diff --git a/library/alloc/Cargo.toml b/library/alloc/Cargo.toml
@@ -51,7 +52,7 @@ popd
 # FIXME remove once inline asm is fully supported
 export RUSTFLAGS="$RUSTFLAGS --cfg=rustix_use_libc"
 
-export CFG_VIRTUAL_RUST_SOURCE_BASE_DIR="$(cd download/sysroot/sysroot_src; pwd)"
+export CFG_VIRTUAL_RUST_SOURCE_BASE_DIR="$(cd build/stdlib; pwd)"
 
 # Allow the testsuite to use llvm tools
 host_triple=$(rustc -vV | grep host | cut -d: -f2 | tr -d " ")
diff --git a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh
index a7920cc54ea..83cbe0db633 100755
--- a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh
+++ b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh
@@ -32,6 +32,8 @@ rm tests/ui/parser/unclosed-delimiter-in-dep.rs # submodule contains //~ERROR
 # missing features
 # ================
 
+rm -r tests/run-make/comment-section # cg_clif doesn't yet write the .comment section
+
 # requires stack unwinding
 # FIXME add needs-unwind to this test
 rm -r tests/run-make/libtest-junit
@@ -98,8 +100,11 @@ rm -r tests/run-make/sepcomp-inlining # same
 rm -r tests/run-make/sepcomp-separate # same
 rm -r tests/run-make/sepcomp-cci-copies # same
 rm -r tests/run-make/volatile-intrinsics # same
+rm -r tests/run-make/llvm-ident # same
+rm -r tests/run-make/no-builtins-attribute # same
 rm tests/ui/abi/stack-protector.rs # requires stack protector support
 rm -r tests/run-make/emit-stack-sizes # requires support for -Z emit-stack-sizes
+rm -r tests/run-make/optimization-remarks-dir # remarks are LLVM specific
 
 # giving different but possibly correct results
 # =============================================
@@ -118,6 +123,7 @@ rm tests/ui/suggestions/derive-trait-for-method-call.rs # same
 rm tests/ui/typeck/issue-46112.rs # same
 rm tests/ui/consts/const_cmp_type_id.rs # same
 rm tests/ui/consts/issue-73976-monomorphic.rs # same
+rm tests/ui/rfcs/rfc-3348-c-string-literals/non-ascii.rs # same
 
 # rustdoc-clif passes extra args, suppressing the help message when no args are passed
 rm -r tests/run-make/issue-88756-default-output
@@ -143,6 +149,8 @@ rm -r tests/run-make/used # same
 rm -r tests/run-make/no-alloc-shim
 rm -r tests/run-make/emit-to-stdout
 
+rm -r tests/run-make/extern-fn-explicit-align # argument alignment not yet supported
+
 # bugs in the test suite
 # ======================
 rm tests/ui/backtrace.rs # TODO warning
@@ -150,6 +158,8 @@ rm tests/ui/process/nofile-limit.rs # TODO some AArch64 linking issue
 
 rm tests/ui/stdio-is-blocking.rs # really slow with unoptimized libstd
 
+rm tests/ui/panic-handler/weak-lang-item-2.rs # Will be fixed by #113568
+
 cp ../dist/bin/rustdoc-clif ../dist/bin/rustdoc # some tests expect bin/rustdoc to exist
 
 # prevent $(RUSTDOC) from picking up the sysroot built by x.py. It conflicts with the one used by
diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs
index eddb479073c..2c038f22ca9 100644
--- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs
+++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs
@@ -445,9 +445,14 @@ pub(crate) fn codegen_terminator_call<'tcx>(
 
     // Unpack arguments tuple for closures
     let mut args = if fn_sig.abi() == Abi::RustCall {
-        assert_eq!(args.len(), 2, "rust-call abi requires two arguments");
-        let self_arg = codegen_call_argument_operand(fx, &args[0]);
-        let pack_arg = codegen_call_argument_operand(fx, &args[1]);
+        let (self_arg, pack_arg) = match args {
+            [pack_arg] => (None, codegen_call_argument_operand(fx, pack_arg)),
+            [self_arg, pack_arg] => (
+                Some(codegen_call_argument_operand(fx, self_arg)),
+                codegen_call_argument_operand(fx, pack_arg),
+            ),
+            _ => panic!("rust-call abi requires one or two arguments"),
+        };
 
         let tupled_arguments = match pack_arg.value.layout().ty.kind() {
             ty::Tuple(ref tupled_arguments) => tupled_arguments,
@@ -455,7 +460,7 @@ pub(crate) fn codegen_terminator_call<'tcx>(
         };
 
         let mut args = Vec::with_capacity(1 + tupled_arguments.len());
-        args.push(self_arg);
+        args.extend(self_arg);
         for i in 0..tupled_arguments.len() {
             args.push(CallArgument {
                 value: pack_arg.value.value_field(fx, FieldIdx::new(i)),
diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs
index e05f2146f0c..522dd7189fe 100644
--- a/compiler/rustc_codegen_cranelift/src/base.rs
+++ b/compiler/rustc_codegen_cranelift/src/base.rs
@@ -7,6 +7,8 @@ use rustc_middle::ty::layout::FnAbiOf;
 use rustc_middle::ty::print::with_no_trimmed_paths;
 
 use cranelift_codegen::ir::UserFuncName;
+use cranelift_codegen::CodegenError;
+use cranelift_module::ModuleError;
 
 use crate::constant::ConstantCx;
 use crate::debuginfo::FunctionDebugContext;
@@ -172,7 +174,21 @@ pub(crate) fn compile_fn(
     // Define function
     cx.profiler.generic_activity("define function").run(|| {
         context.want_disasm = cx.should_write_ir;
-        module.define_function(codegened_func.func_id, context).unwrap();
+        match module.define_function(codegened_func.func_id, context) {
+            Ok(()) => {}
+            Err(ModuleError::Compilation(CodegenError::ImplLimitExceeded)) => {
+                let handler = rustc_session::EarlyErrorHandler::new(
+                    rustc_session::config::ErrorOutputType::default(),
+                );
+                handler.early_error(format!(
+                    "backend implementation limit exceeded while compiling {name}",
+                    name = codegened_func.symbol_name
+                ));
+            }
+            Err(err) => {
+                panic!("Error while defining {name}: {err:?}", name = codegened_func.symbol_name);
+            }
+        }
     });
 
     if cx.should_write_ir {
@@ -356,7 +372,7 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) {
 
                         codegen_panic_inner(
                             fx,
-                            rustc_hir::LangItem::PanicBoundsCheck,
+                            rustc_hir::LangItem::PanicMisalignedPointerDereference,
                             &[required, found, location],
                             source_info.span,
                         );
diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs
index 1b454b6667c..50bc7a127af 100644
--- a/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs
+++ b/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs
@@ -165,7 +165,7 @@ impl FunctionDebugContext {
         for &MachSrcLoc { start, end, loc } in mcr.buffer.get_srclocs_sorted() {
             debug_context.dwarf.unit.line_program.row().address_offset = u64::from(start);
             if !loc.is_default() {
-                let source_loc = *self.source_loc_set.get_index(loc.bits() as usize).unwrap();
+                let source_loc = self.source_loc_set[loc.bits() as usize];
                 create_row_for_span(debug_context, source_loc);
             } else {
                 create_row_for_span(debug_context, self.function_source_loc);
diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs
index 3a7421d8b30..8a4b1cccf14 100644
--- a/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs
+++ b/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs
@@ -38,7 +38,7 @@ pub(crate) struct DebugContext {
 pub(crate) struct FunctionDebugContext {
     entry_id: UnitEntryId,
     function_source_loc: (FileId, u64, u64),
-    source_loc_set: indexmap::IndexSet<(FileId, u64, u64)>,
+    source_loc_set: IndexSet<(FileId, u64, u64)>,
 }
 
 impl DebugContext {
diff --git a/compiler/rustc_codegen_cranelift/src/driver/jit.rs b/compiler/rustc_codegen_cranelift/src/driver/jit.rs
index 41e24acefbe..3ea38842148 100644
--- a/compiler/rustc_codegen_cranelift/src/driver/jit.rs
+++ b/compiler/rustc_codegen_cranelift/src/driver/jit.rs
@@ -114,9 +114,9 @@ pub(crate) fn run_jit(tcx: TyCtxt<'_>, backend_config: BackendConfig) -> ! {
         .iter()
         .map(|cgu| cgu.items_in_deterministic_order(tcx).into_iter())
         .flatten()
-        .collect::<FxHashMap<_, (_, _)>>()
+        .collect::<FxHashMap<_, _>>()
         .into_iter()
-        .collect::<Vec<(_, (_, _))>>();
+        .collect::<Vec<(_, _)>>();
 
     tcx.sess.time("codegen mono items", || {
         super::predefine_mono_items(tcx, &mut jit_module, &mono_items);
diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs
index 18162fb5ab2..fdd27a454e0 100644
--- a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs
+++ b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs
@@ -18,6 +18,20 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>(
             // Spin loop hint
         }
 
+        // Used by is_x86_feature_detected!();
+        "llvm.x86.xgetbv" => {
+            // FIXME use the actual xgetbv instruction
+            intrinsic_args!(fx, args => (v); intrinsic);
+
+            let v = v.load_scalar(fx);
+
+            // As of writing on XCR0 exists
+            fx.bcx.ins().trapnz(v, TrapCode::UnreachableCodeReached);
+
+            let res = fx.bcx.ins().iconst(types::I64, 1 /* bit 0 must be set */);
+            ret.write_cvalue(fx, CValue::by_val(res, fx.layout_of(fx.tcx.types.i64)));
+        }
+
         // Used by `_mm_movemask_epi8` and `_mm256_movemask_epi8`
         "llvm.x86.sse2.pmovmskb.128"
         | "llvm.x86.avx2.pmovmskb"
@@ -53,7 +67,7 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>(
             let res = CValue::by_val(res, fx.layout_of(fx.tcx.types.i32));
             ret.write_cvalue(fx, res);
         }
-        "llvm.x86.sse2.cmp.ps" | "llvm.x86.sse2.cmp.pd" => {
+        "llvm.x86.sse.cmp.ps" | "llvm.x86.sse2.cmp.pd" => {
             let (x, y, kind) = match args {
                 [x, y, kind] => (x, y, kind),
                 _ => bug!("wrong number of args for intrinsic {intrinsic}"),
@@ -66,18 +80,95 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>(
             let flt_cc = match kind
                 .try_to_bits(Size::from_bytes(1))
                 .unwrap_or_else(|| panic!("kind not scalar: {:?}", kind))
+                .try_into()
+                .unwrap()
             {
-                0 => FloatCC::Equal,
-                1 => FloatCC::LessThan,
-                2 => FloatCC::LessThanOrEqual,
-                7 => FloatCC::Ordered,
-                3 => FloatCC::Unordered,
-                4 => FloatCC::NotEqual,
-                5 => FloatCC::UnorderedOrGreaterThanOrEqual,
-                6 => FloatCC::UnorderedOrGreaterThan,
+                _CMP_EQ_OQ | _CMP_EQ_OS => FloatCC::Equal,
+                _CMP_LT_OS | _CMP_LT_OQ => FloatCC::LessThan,
+                _CMP_LE_OS | _CMP_LE_OQ => FloatCC::LessThanOrEqual,
+                _CMP_UNORD_Q | _CMP_UNORD_S => FloatCC::Unordered,
+                _CMP_NEQ_UQ | _CMP_NEQ_US => FloatCC::NotEqual,
+                _CMP_NLT_US | _CMP_NLT_UQ => FloatCC::UnorderedOrGreaterThanOrEqual,
+                _CMP_NLE_US | _CMP_NLE_UQ => FloatCC::UnorderedOrGreaterThan,
+                _CMP_ORD_Q | _CMP_ORD_S => FloatCC::Ordered,
+                _CMP_EQ_UQ | _CMP_EQ_US => FloatCC::UnorderedOrEqual,
+                _CMP_NGE_US | _CMP_NGE_UQ => FloatCC::UnorderedOrLessThan,
+                _CMP_NGT_US | _CMP_NGT_UQ => FloatCC::UnorderedOrLessThanOrEqual,
+                _CMP_FALSE_OQ | _CMP_FALSE_OS => todo!(),
+                _CMP_NEQ_OQ | _CMP_NEQ_OS => FloatCC::OrderedNotEqual,
+                _CMP_GE_OS | _CMP_GE_OQ => FloatCC::GreaterThanOrEqual,
+                _CMP_GT_OS | _CMP_GT_OQ => FloatCC::GreaterThan,
+                _CMP_TRUE_UQ | _CMP_TRUE_US => todo!(),
+
                 kind => unreachable!("kind {:?}", kind),
             };
 
+            // Copied from stdarch
+            /// Equal (ordered, non-signaling)
+            const _CMP_EQ_OQ: i32 = 0x00;
+            /// Less-than (ordered, signaling)
+            const _CMP_LT_OS: i32 = 0x01;
+            /// Less-than-or-equal (ordered, signaling)
+            const _CMP_LE_OS: i32 = 0x02;
+            /// Unordered (non-signaling)
+            const _CMP_UNORD_Q: i32 = 0x03;
+            /// Not-equal (unordered, non-signaling)
+            const _CMP_NEQ_UQ: i32 = 0x04;
+            /// Not-less-than (unordered, signaling)
+            const _CMP_NLT_US: i32 = 0x05;
+            /// Not-less-than-or-equal (unordered, signaling)
+            const _CMP_NLE_US: i32 = 0x06;
+            /// Ordered (non-signaling)
+            const _CMP_ORD_Q: i32 = 0x07;
+            /// Equal (unordered, non-signaling)
+            const _CMP_EQ_UQ: i32 = 0x08;
+            /// Not-greater-than-or-equal (unordered, signaling)
+            const _CMP_NGE_US: i32 = 0x09;
+            /// Not-greater-than (unordered, signaling)
+            const _CMP_NGT_US: i32 = 0x0a;
+            /// False (ordered, non-signaling)
+            const _CMP_FALSE_OQ: i32 = 0x0b;
+            /// Not-equal (ordered, non-signaling)
+            const _CMP_NEQ_OQ: i32 = 0x0c;
+            /// Greater-than-or-equal (ordered, signaling)
+            const _CMP_GE_OS: i32 = 0x0d;
+            /// Greater-than (ordered, signaling)
+            const _CMP_GT_OS: i32 = 0x0e;
+            /// True (unordered, non-signaling)
+            const _CMP_TRUE_UQ: i32 = 0x0f;
+            /// Equal (ordered, signaling)
+            const _CMP_EQ_OS: i32 = 0x10;
+            /// Less-than (ordered, non-signaling)
+            const _CMP_LT_OQ: i32 = 0x11;
+            /// Less-than-or-equal (ordered, non-signaling)
+            const _CMP_LE_OQ: i32 = 0x12;
+            /// Unordered (signaling)
+            const _CMP_UNORD_S: i32 = 0x13;
+            /// Not-equal (unordered, signaling)
+            const _CMP_NEQ_US: i32 = 0x14;
+            /// Not-less-than (unordered, non-signaling)
+            const _CMP_NLT_UQ: i32 = 0x15;
+            /// Not-less-than-or-equal (unordered, non-signaling)
+            const _CMP_NLE_UQ: i32 = 0x16;
+            /// Ordered (signaling)
+            const _CMP_ORD_S: i32 = 0x17;
+            /// Equal (unordered, signaling)
+            const _CMP_EQ_US: i32 = 0x18;
+            /// Not-greater-than-or-equal (unordered, non-signaling)
+            const _CMP_NGE_UQ: i32 = 0x19;
+            /// Not-greater-than (unordered, non-signaling)
+            const _CMP_NGT_UQ: i32 = 0x1a;
+            /// False (ordered, signaling)
+            const _CMP_FALSE_OS: i32 = 0x1b;
+            /// Not-equal (ordered, signaling)
+            const _CMP_NEQ_OS: i32 = 0x1c;
+            /// Greater-than-or-equal (ordered, non-signaling)
+            const _CMP_GE_OQ: i32 = 0x1d;
+            /// Greater-than (ordered, non-signaling)
+            const _CMP_GT_OQ: i32 = 0x1e;
+            /// True (unordered, signaling)
+            const _CMP_TRUE_US: i32 = 0x1f;
+
             simd_pair_for_each_lane(fx, x, y, ret, &|fx, lane_ty, res_lane_ty, x_lane, y_lane| {
                 let res_lane = match lane_ty.kind() {
                     ty::Float(_) => fx.bcx.ins().fcmp(flt_cc, x_lane, y_lane),
@@ -103,6 +194,23 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>(
                 _ => fx.bcx.ins().iconst(types::I32, 0),
             });
         }
+        "llvm.x86.sse2.psrai.d" => {
+            let (a, imm8) = match args {
+                [a, imm8] => (a, imm8),
+                _ => bug!("wrong number of args for intrinsic {intrinsic}"),
+            };
+            let a = codegen_operand(fx, a);
+            let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8)
+                .expect("llvm.x86.sse2.psrai.d imm8 not const");
+
+            simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| match imm8
+                .try_to_bits(Size::from_bytes(4))
+                .unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8))
+            {
+                imm8 if imm8 < 32 => fx.bcx.ins().sshr_imm(lane, i64::from(imm8 as u8)),
+                _ => fx.bcx.ins().iconst(types::I32, 0),
+            });
+        }
         "llvm.x86.sse2.pslli.d" => {
             let (a, imm8) = match args {
                 [a, imm8] => (a, imm8),
@@ -137,6 +245,23 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>(
                 _ => fx.bcx.ins().iconst(types::I32, 0),
             });
         }
+        "llvm.x86.sse2.psrai.w" => {
+            let (a, imm8) = match args {
+                [a, imm8] => (a, imm8),
+                _ => bug!("wrong number of args for intrinsic {intrinsic}"),
+            };
+            let a = codegen_operand(fx, a);
+            let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8)
+                .expect("llvm.x86.sse2.psrai.d imm8 not const");
+
+            simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| match imm8
+                .try_to_bits(Size::from_bytes(4))
+                .unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8))
+            {
+                imm8 if imm8 < 16 => fx.bcx.ins().sshr_imm(lane, i64::from(imm8 as u8)),
+                _ => fx.bcx.ins().iconst(types::I32, 0),
+            });
+        }
         "llvm.x86.sse2.pslli.w" => {
             let (a, imm8) = match args {
                 [a, imm8] => (a, imm8),
@@ -171,6 +296,57 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>(
                 _ => fx.bcx.ins().iconst(types::I32, 0),
             });
         }
+        "llvm.x86.avx.psrai.d" => {
+            let (a, imm8) = match args {
+                [a, imm8] => (a, imm8),
+                _ => bug!("wrong number of args for intrinsic {intrinsic}"),
+            };
+            let a = codegen_operand(fx, a);
+            let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8)
+                .expect("llvm.x86.avx.psrai.d imm8 not const");
+
+            simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| match imm8
+                .try_to_bits(Size::from_bytes(4))
+                .unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8))
+            {
+                imm8 if imm8 < 32 => fx.bcx.ins().sshr_imm(lane, i64::from(imm8 as u8)),
+                _ => fx.bcx.ins().iconst(types::I32, 0),
+            });
+        }
+        "llvm.x86.sse2.psrli.q" => {
+            let (a, imm8) = match args {
+                [a, imm8] => (a, imm8),
+                _ => bug!("wrong number of args for intrinsic {intrinsic}"),
+            };
+            let a = codegen_operand(fx, a);
+            let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8)
+                .expect("llvm.x86.avx.psrli.q imm8 not const");
+
+            simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| match imm8
+                .try_to_bits(Size::from_bytes(4))
+                .unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8))
+            {
+                imm8 if imm8 < 64 => fx.bcx.ins().ushr_imm(lane, i64::from(imm8 as u8)),
+                _ => fx.bcx.ins().iconst(types::I32, 0),
+            });
+        }
+        "llvm.x86.sse2.pslli.q" => {
+            let (a, imm8) = match args {
+                [a, imm8] => (a, imm8),
+                _ => bug!("wrong number of args for intrinsic {intrinsic}"),
+            };
+            let a = codegen_operand(fx, a);
+            let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8)
+                .expect("llvm.x86.avx.pslli.q imm8 not const");
+
+            simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| match imm8
+                .try_to_bits(Size::from_bytes(4))
+                .unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8))
+            {
+                imm8 if imm8 < 64 => fx.bcx.ins().ishl_imm(lane, i64::from(imm8 as u8)),
+                _ => fx.bcx.ins().iconst(types::I32, 0),
+            });
+        }
         "llvm.x86.avx.pslli.d" => {
             let (a, imm8) = match args {
                 [a, imm8] => (a, imm8),
@@ -205,6 +381,23 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>(
                 _ => fx.bcx.ins().iconst(types::I32, 0),
             });
         }
+        "llvm.x86.avx2.psrai.w" => {
+            let (a, imm8) = match args {
+                [a, imm8] => (a, imm8),
+                _ => bug!("wrong number of args for intrinsic {intrinsic}"),
+            };
+            let a = codegen_operand(fx, a);
+            let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8)
+                .expect("llvm.x86.avx.psrai.w imm8 not const");
+
+            simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| match imm8
+                .try_to_bits(Size::from_bytes(4))
+                .unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8))
+            {
+                imm8 if imm8 < 16 => fx.bcx.ins().sshr_imm(lane, i64::from(imm8 as u8)),
+                _ => fx.bcx.ins().iconst(types::I32, 0),
+            });
+        }
         "llvm.x86.avx2.pslli.w" => {
             let (a, imm8) = match args {
                 [a, imm8] => (a, imm8),
@@ -313,7 +506,7 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>(
             ret.place_lane(fx, 2).to_ptr().store(fx, res_2, MemFlags::trusted());
             ret.place_lane(fx, 3).to_ptr().store(fx, res_3, MemFlags::trusted());
         }
-        "llvm.x86.sse2.storeu.dq" => {
+        "llvm.x86.sse2.storeu.dq" | "llvm.x86.sse2.storeu.pd" => {
             intrinsic_args!(fx, args => (mem_addr, a); intrinsic);
             let mem_addr = mem_addr.load_scalar(fx);
 
@@ -321,17 +514,45 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>(
             let dest = CPlace::for_ptr(Pointer::new(mem_addr), a.layout());
             dest.write_cvalue(fx, a);
         }
-        "llvm.x86.addcarry.64" => {
+        "llvm.x86.ssse3.pabs.b.128" | "llvm.x86.ssse3.pabs.w.128" | "llvm.x86.ssse3.pabs.d.128" => {
+            let a = match args {
+                [a] => a,
+                _ => bug!("wrong number of args for intrinsic {intrinsic}"),
+            };
+            let a = codegen_operand(fx, a);
+
+            simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| {
+                fx.bcx.ins().iabs(lane)
+            });
+        }
+        "llvm.x86.addcarry.32" | "llvm.x86.addcarry.64" => {
             intrinsic_args!(fx, args => (c_in, a, b); intrinsic);
             let c_in = c_in.load_scalar(fx);
 
-            llvm_add_sub(fx, BinOp::Add, ret, c_in, a, b);
+            let (cb_out, c) = llvm_add_sub(fx, BinOp::Add, c_in, a, b);
+
+            let layout = fx.layout_of(Ty::new_tup(fx.tcx, &[fx.tcx.types.u8, a.layout().ty]));
+            let val = CValue::by_val_pair(cb_out, c, layout);
+            ret.write_cvalue(fx, val);
         }
-        "llvm.x86.subborrow.64" => {
+        "llvm.x86.addcarryx.u32" | "llvm.x86.addcarryx.u64" => {
+            intrinsic_args!(fx, args => (c_in, a, b, out); intrinsic);
+            let c_in = c_in.load_scalar(fx);
+
+            let (cb_out, c) = llvm_add_sub(fx, BinOp::Add, c_in, a, b);
+
+            Pointer::new(out.load_scalar(fx)).store(fx, c, MemFlags::trusted());
+            ret.write_cvalue(fx, CValue::by_val(cb_out, fx.layout_of(fx.tcx.types.u8)));
+        }
+        "llvm.x86.subborrow.32" | "llvm.x86.subborrow.64" => {
             intrinsic_args!(fx, args => (b_in, a, b); intrinsic);
             let b_in = b_in.load_scalar(fx);
 
-            llvm_add_sub(fx, BinOp::Sub, ret, b_in, a, b);
+            let (cb_out, c) = llvm_add_sub(fx, BinOp::Sub, b_in, a, b);
+
+            let layout = fx.layout_of(Ty::new_tup(fx.tcx, &[fx.tcx.types.u8, a.layout().ty]));
+            let val = CValue::by_val_pair(cb_out, c, layout);
+            ret.write_cvalue(fx, val);
         }
         _ => {
             fx.tcx
@@ -356,21 +577,11 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>(
 fn llvm_add_sub<'tcx>(
     fx: &mut FunctionCx<'_, '_, 'tcx>,
     bin_op: BinOp,
-    ret: CPlace<'tcx>,
     cb_in: Value,
     a: CValue<'tcx>,
     b: CValue<'tcx>,
-) {
-    assert_eq!(
-        a.layout().ty,
-        fx.tcx.types.u64,
-        "llvm.x86.addcarry.64/llvm.x86.subborrow.64 second operand must be u64"
-    );
-    assert_eq!(
-        b.layout().ty,
-        fx.tcx.types.u64,
-        "llvm.x86.addcarry.64/llvm.x86.subborrow.64 third operand must be u64"
-    );
+) -> (Value, Value) {
+    assert_eq!(a.layout().ty, b.layout().ty);
 
     // c + carry -> c + first intermediate carry or borrow respectively
     let int0 = crate::num::codegen_checked_int_binop(fx, bin_op, a, b);
@@ -378,15 +589,14 @@ fn llvm_add_sub<'tcx>(
     let cb0 = int0.value_field(fx, FieldIdx::new(1)).load_scalar(fx);
 
     // c + carry -> c + second intermediate carry or borrow respectively
-    let cb_in_as_u64 = fx.bcx.ins().uextend(types::I64, cb_in);
-    let cb_in_as_u64 = CValue::by_val(cb_in_as_u64, fx.layout_of(fx.tcx.types.u64));
-    let int1 = crate::num::codegen_checked_int_binop(fx, bin_op, c, cb_in_as_u64);
+    let clif_ty = fx.clif_type(a.layout().ty).unwrap();
+    let cb_in_as_int = fx.bcx.ins().uextend(clif_ty, cb_in);
+    let cb_in_as_int = CValue::by_val(cb_in_as_int, fx.layout_of(a.layout().ty));
+    let int1 = crate::num::codegen_checked_int_binop(fx, bin_op, c, cb_in_as_int);
     let (c, cb1) = int1.load_scalar_pair(fx);
 
     // carry0 | carry1 -> carry or borrow respectively
     let cb_out = fx.bcx.ins().bor(cb0, cb1);
 
-    let layout = fx.layout_of(Ty::new_tup(fx.tcx, &[fx.tcx.types.u8, fx.tcx.types.u64]));
-    let val = CValue::by_val_pair(cb_out, c, layout);
-    ret.write_cvalue(fx, val);
+    (cb_out, c)
 }
diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs
index da8ab361331..e3006b253b7 100644
--- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs
+++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs
@@ -647,12 +647,13 @@ fn codegen_regular_intrinsic_call<'tcx>(
             let val = CValue::by_ref(Pointer::new(ptr.load_scalar(fx)), inner_layout);
             ret.write_cvalue(fx, val);
         }
-        sym::volatile_store | sym::unaligned_volatile_store => {
+        sym::volatile_store | sym::unaligned_volatile_store | sym::nontemporal_store => {
             intrinsic_args!(fx, args => (ptr, val); intrinsic);
             let ptr = ptr.load_scalar(fx);
 
             // Cranelift treats stores as volatile by default
             // FIXME correctly handle unaligned_volatile_store
+            // FIXME actually do nontemporal stores if requested
             let dest = CPlace::for_ptr(Pointer::new(ptr), val.layout());
             dest.write_cvalue(fx, val);
         }
diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs
index 0de2dccda71..ebd153cb71d 100644
--- a/compiler/rustc_codegen_cranelift/src/lib.rs
+++ b/compiler/rustc_codegen_cranelift/src/lib.rs
@@ -268,8 +268,6 @@ fn build_isa(sess: &Session, backend_config: &BackendConfig) -> Arc<dyn isa::Tar
     };
     flags_builder.set("tls_model", tls_model).unwrap();
 
-    flags_builder.set("enable_simd", "true").unwrap();
-
     flags_builder.set("enable_llvm_abi_extensions", "true").unwrap();
 
     use rustc_session::config::OptLevel;
diff --git a/compiler/rustc_codegen_cranelift/src/value_and_place.rs b/compiler/rustc_codegen_cranelift/src/value_and_place.rs
index ec0b61a7ce5..ff95141ce90 100644
--- a/compiler/rustc_codegen_cranelift/src/value_and_place.rs
+++ b/compiler/rustc_codegen_cranelift/src/value_and_place.rs
@@ -2,6 +2,8 @@
 
 use crate::prelude::*;
 
+use rustc_middle::ty::FnSig;
+
 use cranelift_codegen::entity::EntityRef;
 use cranelift_codegen::ir::immediates::Offset32;
 
@@ -160,6 +162,7 @@ impl<'tcx> CValue<'tcx> {
     }
 
     /// Load a value with layout.abi of scalar
+    #[track_caller]
     pub(crate) fn load_scalar(self, fx: &mut FunctionCx<'_, '_, 'tcx>) -> Value {
         let layout = self.1;
         match self.0 {
@@ -182,6 +185,7 @@ impl<'tcx> CValue<'tcx> {
     }
 
     /// Load a value pair with layout.abi of scalar pair
+    #[track_caller]
     pub(crate) fn load_scalar_pair(self, fx: &mut FunctionCx<'_, '_, 'tcx>) -> (Value, Value) {
         let layout = self.1;
         match self.0 {
@@ -583,17 +587,25 @@ impl<'tcx> CPlace<'tcx> {
         let dst_layout = self.layout();
         match self.inner {
             CPlaceInner::Var(_local, var) => {
-                let data = CValue(from.0, dst_layout).load_scalar(fx);
+                let data = match from.1.abi {
+                    Abi::Scalar(_) => CValue(from.0, dst_layout).load_scalar(fx),
+                    _ => {
+                        let (ptr, meta) = from.force_stack(fx);
+                        assert!(meta.is_none());
+                        CValue(CValueInner::ByRef(ptr, None), dst_layout).load_scalar(fx)
+                    }
+                };
                 let dst_ty = fx.clif_type(self.layout().ty).unwrap();
                 transmute_scalar(fx, var, data, dst_ty);
             }
             CPlaceInner::VarPair(_local, var1, var2) => {
-                let (data1, data2) = if from.layout().ty == dst_layout.ty {
-                    CValue(from.0, dst_layout).load_scalar_pair(fx)
-                } else {
-                    let (ptr, meta) = from.force_stack(fx);
-                    assert!(meta.is_none());
-                    CValue(CValueInner::ByRef(ptr, None), dst_layout).load_scalar_pair(fx)
+                let (data1, data2) = match from.1.abi {
+                    Abi::ScalarPair(_, _) => CValue(from.0, dst_layout).load_scalar_pair(fx),
+                    _ => {
+                        let (ptr, meta) = from.force_stack(fx);
+                        assert!(meta.is_none());
+                        CValue(CValueInner::ByRef(ptr, None), dst_layout).load_scalar_pair(fx)
+                    }
                 };
                 let (dst_ty1, dst_ty2) = fx.clif_pair_type(self.layout().ty).unwrap();
                 transmute_scalar(fx, var1, data1, dst_ty1);
@@ -607,30 +619,38 @@ impl<'tcx> CPlace<'tcx> {
 
                 let mut flags = MemFlags::new();
                 flags.set_notrap();
-                match from.layout().abi {
-                    Abi::Scalar(_) => {
-                        let val = from.load_scalar(fx);
-                        to_ptr.store(fx, val, flags);
-                        return;
-                    }
-                    Abi::ScalarPair(a_scalar, b_scalar) => {
-                        let (value, extra) = from.load_scalar_pair(fx);
-                        let b_offset = scalar_pair_calculate_b_offset(fx.tcx, a_scalar, b_scalar);
-                        to_ptr.store(fx, value, flags);
-                        to_ptr.offset(fx, b_offset).store(fx, extra, flags);
-                        return;
-                    }
-                    _ => {}
-                }
 
                 match from.0 {
                     CValueInner::ByVal(val) => {
                         to_ptr.store(fx, val, flags);
                     }
-                    CValueInner::ByValPair(_, _) => {
-                        bug!("Non ScalarPair abi {:?} for ByValPair CValue", dst_layout.abi);
-                    }
+                    CValueInner::ByValPair(val1, val2) => match from.layout().abi {
+                        Abi::ScalarPair(a_scalar, b_scalar) => {
+                            let b_offset =
+                                scalar_pair_calculate_b_offset(fx.tcx, a_scalar, b_scalar);
+                            to_ptr.store(fx, val1, flags);
+                            to_ptr.offset(fx, b_offset).store(fx, val2, flags);
+                        }
+                        _ => bug!("Non ScalarPair abi {:?} for ByValPair CValue", dst_layout.abi),
+                    },
                     CValueInner::ByRef(from_ptr, None) => {
+                        match from.layout().abi {
+                            Abi::Scalar(_) => {
+                                let val = from.load_scalar(fx);
+                                to_ptr.store(fx, val, flags);
+                                return;
+                            }
+                            Abi::ScalarPair(a_scalar, b_scalar) => {
+                                let b_offset =
+                                    scalar_pair_calculate_b_offset(fx.tcx, a_scalar, b_scalar);
+                                let (val1, val2) = from.load_scalar_pair(fx);
+                                to_ptr.store(fx, val1, flags);
+                                to_ptr.offset(fx, b_offset).store(fx, val2, flags);
+                                return;
+                            }
+                            _ => {}
+                        }
+
                         let from_addr = from_ptr.get_addr(fx);
                         let to_addr = to_ptr.get_addr(fx);
                         let src_layout = from.1;
@@ -815,11 +835,42 @@ pub(crate) fn assert_assignable<'tcx>(
                 ParamEnv::reveal_all(),
                 from_ty.fn_sig(fx.tcx),
             );
+            let FnSig {
+                inputs_and_output: types_from,
+                c_variadic: c_variadic_from,
+                unsafety: unsafety_from,
+                abi: abi_from,
+            } = from_sig;
             let to_sig = fx
                 .tcx
                 .normalize_erasing_late_bound_regions(ParamEnv::reveal_all(), to_ty.fn_sig(fx.tcx));
+            let FnSig {
+                inputs_and_output: types_to,
+                c_variadic: c_variadic_to,
+                unsafety: unsafety_to,
+                abi: abi_to,
+            } = to_sig;
+            let mut types_from = types_from.iter();
+            let mut types_to = types_to.iter();
+            loop {
+                match (types_from.next(), types_to.next()) {
+                    (Some(a), Some(b)) => assert_assignable(fx, a, b, limit - 1),
+                    (None, None) => break,
+                    (Some(_), None) | (None, Some(_)) => panic!("{:#?}/{:#?}", from_ty, to_ty),
+                }
+            }
+            assert_eq!(
+                c_variadic_from, c_variadic_to,
+                "Can't write fn ptr with incompatible sig {:?} to place with sig {:?}\n\n{:#?}",
+                from_sig, to_sig, fx,
+            );
+            assert_eq!(
+                unsafety_from, unsafety_to,
+                "Can't write fn ptr with incompatible sig {:?} to place with sig {:?}\n\n{:#?}",
+                from_sig, to_sig, fx,
+            );
             assert_eq!(
-                from_sig, to_sig,
+                abi_from, abi_to,
                 "Can't write fn ptr with incompatible sig {:?} to place with sig {:?}\n\n{:#?}",
                 from_sig, to_sig, fx,
             );
diff --git a/compiler/rustc_codegen_gcc/messages.ftl b/compiler/rustc_codegen_gcc/messages.ftl
index 97bc8ef9d1b..2fd0daee3e7 100644
--- a/compiler/rustc_codegen_gcc/messages.ftl
+++ b/compiler/rustc_codegen_gcc/messages.ftl
@@ -1,63 +1,6 @@
 codegen_gcc_invalid_minimum_alignment =
     invalid minimum global alignment: {$err}
 
-codegen_gcc_invalid_monomorphization_basic_integer =
-    invalid monomorphization of `{$name}` intrinsic: expected basic integer type, found `{$ty}`
-
-codegen_gcc_invalid_monomorphization_expected_signed_unsigned =
-    invalid monomorphization of `{$name}` intrinsic: expected element type `{$elem_ty}` of vector type `{$vec_ty}` to be a signed or unsigned integer type
-
-codegen_gcc_invalid_monomorphization_expected_simd =
-    invalid monomorphization of `{$name}` intrinsic: expected SIMD {$expected_ty} type, found non-SIMD `{$found_ty}`
-
-codegen_gcc_invalid_monomorphization_inserted_type =
-    invalid monomorphization of `{$name}` intrinsic: expected inserted type `{$in_elem}` (element of input `{$in_ty}`), found `{$out_ty}`
-
-codegen_gcc_invalid_monomorphization_invalid_bitmask =
-    invalid monomorphization of `{$name}` intrinsic: invalid bitmask `{$ty}`, expected `u{$expected_int_bits}` or `[u8; {$expected_bytes}]`
-
-codegen_gcc_invalid_monomorphization_invalid_float_vector =
-    invalid monomorphization of `{$name}` intrinsic: unsupported element type `{$elem_ty}` of floating-point vector `{$vec_ty}`
-
-codegen_gcc_invalid_monomorphization_mask_type =
-    invalid monomorphization of `{$name}` intrinsic: mask element type is `{$ty}`, expected `i_`
-
-codegen_gcc_invalid_monomorphization_mismatched_lengths =
-    invalid monomorphization of `{$name}` intrinsic: mismatched lengths: mask length `{$m_len}` != other vector length `{$v_len}`
-
-codegen_gcc_invalid_monomorphization_not_float =
-    invalid monomorphization of `{$name}` intrinsic: `{$ty}` is not a floating-point type
-
-codegen_gcc_invalid_monomorphization_return_element =
-    invalid monomorphization of `{$name}` intrinsic: expected return element type `{$in_elem}` (element of input `{$in_ty}`), found `{$ret_ty}` with element type `{$out_ty}`
-
-codegen_gcc_invalid_monomorphization_return_integer_type =
-    invalid monomorphization of `{$name}` intrinsic: expected return type with integer elements, found `{$ret_ty}` with non-integer `{$out_ty}`
-
-codegen_gcc_invalid_monomorphization_return_length =
-    invalid monomorphization of `{$name}` intrinsic: expected return type of length {$in_len}, found `{$ret_ty}` with length {$out_len}
-
-codegen_gcc_invalid_monomorphization_return_length_input_type =
-    invalid monomorphization of `{$name}` intrinsic: expected return type with length {$in_len} (same as input type `{$in_ty}`), found `{$ret_ty}` with length {$out_len}
-
-codegen_gcc_invalid_monomorphization_return_type =
-    invalid monomorphization of `{$name}` intrinsic: expected return type `{$in_elem}` (element of input `{$in_ty}`), found `{$ret_ty}`
-
-codegen_gcc_invalid_monomorphization_simd_shuffle =
-    invalid monomorphization of `{$name}` intrinsic: simd_shuffle index must be an array of `u32`, got `{$ty}`
-
-codegen_gcc_invalid_monomorphization_unrecognized =
-    invalid monomorphization of `{$name}` intrinsic: unrecognized intrinsic `{$name}`
-
-codegen_gcc_invalid_monomorphization_unsupported_cast =
-    invalid monomorphization of `{$name}` intrinsic: unsupported cast from `{$in_ty}` with element `{$in_elem}` to `{$ret_ty}` with element `{$out_elem}`
-
-codegen_gcc_invalid_monomorphization_unsupported_element =
-    invalid monomorphization of `{$name}` intrinsic: unsupported {$name} from `{$in_ty}` with element `{$elem_ty}` to `{$ret_ty}`
-
-codegen_gcc_invalid_monomorphization_unsupported_operation =
-    invalid monomorphization of `{$name}` intrinsic: unsupported operation on `{$in_ty}` with element `{$in_elem}`
-
 codegen_gcc_lto_not_supported =
     LTO is not supported. You may get a linker error.
 
diff --git a/compiler/rustc_codegen_gcc/src/errors.rs b/compiler/rustc_codegen_gcc/src/errors.rs
index 9305bd1e043..693367192b1 100644
--- a/compiler/rustc_codegen_gcc/src/errors.rs
+++ b/compiler/rustc_codegen_gcc/src/errors.rs
@@ -1,7 +1,6 @@
 use rustc_errors::{DiagnosticArgValue, IntoDiagnosticArg};
 use rustc_macros::Diagnostic;
-use rustc_middle::ty::Ty;
-use rustc_span::{Span, Symbol};
+use rustc_span::Span;
 use std::borrow::Cow;
 
 struct ExitCode(Option<i32>);
@@ -17,201 +16,6 @@ impl IntoDiagnosticArg for ExitCode {
 }
 
 #[derive(Diagnostic)]
-#[diag(codegen_gcc_invalid_monomorphization_basic_integer, code = "E0511")]
-pub(crate) struct InvalidMonomorphizationBasicInteger<'a> {
-    #[primary_span]
-    pub span: Span,
-    pub name: Symbol,
-    pub ty: Ty<'a>,
-}
-
-#[derive(Diagnostic)]
-#[diag(codegen_gcc_invalid_monomorphization_invalid_float_vector, code = "E0511")]
-pub(crate) struct InvalidMonomorphizationInvalidFloatVector<'a> {
-    #[primary_span]
-    pub span: Span,
-    pub name: Symbol,
-    pub elem_ty: &'a str,
-    pub vec_ty: Ty<'a>,
-}
-
-#[derive(Diagnostic)]
-#[diag(codegen_gcc_invalid_monomorphization_not_float, code = "E0511")]
-pub(crate) struct InvalidMonomorphizationNotFloat<'a> {
-    #[primary_span]
-    pub span: Span,
-    pub name: Symbol,
-    pub ty: Ty<'a>,
-}
-
-#[derive(Diagnostic)]
-#[diag(codegen_gcc_invalid_monomorphization_unrecognized, code = "E0511")]
-pub(crate) struct InvalidMonomorphizationUnrecognized {
-    #[primary_span]
-    pub span: Span,
-    pub name: Symbol,
-}
-
-#[derive(Diagnostic)]
-#[diag(codegen_gcc_invalid_monomorphization_expected_signed_unsigned, code = "E0511")]
-pub(crate) struct InvalidMonomorphizationExpectedSignedUnsigned<'a> {
-    #[primary_span]
-    pub span: Span,
-    pub name: Symbol,
-    pub elem_ty: Ty<'a>,
-    pub vec_ty: Ty<'a>,
-}
-
-#[derive(Diagnostic)]
-#[diag(codegen_gcc_invalid_monomorphization_unsupported_element, code = "E0511")]
-pub(crate) struct InvalidMonomorphizationUnsupportedElement<'a> {
-    #[primary_span]
-    pub span: Span,
-    pub name: Symbol,
-    pub in_ty: Ty<'a>,
-    pub elem_ty: Ty<'a>,
-    pub ret_ty: Ty<'a>,
-}
-
-#[derive(Diagnostic)]
-#[diag(codegen_gcc_invalid_monomorphization_invalid_bitmask, code = "E0511")]
-pub(crate) struct InvalidMonomorphizationInvalidBitmask<'a> {
-    #[primary_span]
-    pub span: Span,
-    pub name: Symbol,
-    pub ty: Ty<'a>,
-    pub expected_int_bits: u64,
-    pub expected_bytes: u64,
-}
-
-#[derive(Diagnostic)]
-#[diag(codegen_gcc_invalid_monomorphization_simd_shuffle, code = "E0511")]
-pub(crate) struct InvalidMonomorphizationSimdShuffle<'a> {
-    #[primary_span]
-    pub span: Span,
-    pub name: Symbol,
-    pub ty: Ty<'a>,
-}
-
-#[derive(Diagnostic)]
-#[diag(codegen_gcc_invalid_monomorphization_expected_simd, code = "E0511")]
-pub(crate) struct InvalidMonomorphizationExpectedSimd<'a> {
-    #[primary_span]
-    pub span: Span,
-    pub name: Symbol,
-    pub position: &'a str,
-    pub found_ty: Ty<'a>,
-}
-
-#[derive(Diagnostic)]
-#[diag(codegen_gcc_invalid_monomorphization_mask_type, code = "E0511")]
-pub(crate) struct InvalidMonomorphizationMaskType<'a> {
-    #[primary_span]
-    pub span: Span,
-    pub name: Symbol,
-    pub ty: Ty<'a>,
-}
-
-#[derive(Diagnostic)]
-#[diag(codegen_gcc_invalid_monomorphization_return_length, code = "E0511")]
-pub(crate) struct InvalidMonomorphizationReturnLength<'a> {
-    #[primary_span]
-    pub span: Span,
-    pub name: Symbol,
-    pub in_len: u64,
-    pub ret_ty: Ty<'a>,
-    pub out_len: u64,
-}
-
-#[derive(Diagnostic)]
-#[diag(codegen_gcc_invalid_monomorphization_return_length_input_type, code = "E0511")]
-pub(crate) struct InvalidMonomorphizationReturnLengthInputType<'a> {
-    #[primary_span]
-    pub span: Span,
-    pub name: Symbol,
-    pub in_len: u64,
-    pub in_ty: Ty<'a>,
-    pub ret_ty: Ty<'a>,
-    pub out_len: u64,
-}
-
-#[derive(Diagnostic)]
-#[diag(codegen_gcc_invalid_monomorphization_return_element, code = "E0511")]
-pub(crate) struct InvalidMonomorphizationReturnElement<'a> {
-    #[primary_span]
-    pub span: Span,
-    pub name: Symbol,
-    pub in_elem: Ty<'a>,
-    pub in_ty: Ty<'a>,
-    pub ret_ty: Ty<'a>,
-    pub out_ty: Ty<'a>,
-}
-
-#[derive(Diagnostic)]
-#[diag(codegen_gcc_invalid_monomorphization_return_type, code = "E0511")]
-pub(crate) struct InvalidMonomorphizationReturnType<'a> {
-    #[primary_span]
-    pub span: Span,
-    pub name: Symbol,
-    pub in_elem: Ty<'a>,
-    pub in_ty: Ty<'a>,
-    pub ret_ty: Ty<'a>,
-}
-
-#[derive(Diagnostic)]
-#[diag(codegen_gcc_invalid_monomorphization_inserted_type, code = "E0511")]
-pub(crate) struct InvalidMonomorphizationInsertedType<'a> {
-    #[primary_span]
-    pub span: Span,
-    pub name: Symbol,
-    pub in_elem: Ty<'a>,
-    pub in_ty: Ty<'a>,
-    pub out_ty: Ty<'a>,
-}
-
-#[derive(Diagnostic)]
-#[diag(codegen_gcc_invalid_monomorphization_return_integer_type, code = "E0511")]
-pub(crate) struct InvalidMonomorphizationReturnIntegerType<'a> {
-    #[primary_span]
-    pub span: Span,
-    pub name: Symbol,
-    pub ret_ty: Ty<'a>,
-    pub out_ty: Ty<'a>,
-}
-
-#[derive(Diagnostic)]
-#[diag(codegen_gcc_invalid_monomorphization_mismatched_lengths, code = "E0511")]
-pub(crate) struct InvalidMonomorphizationMismatchedLengths {
-    #[primary_span]
-    pub span: Span,
-    pub name: Symbol,
-    pub m_len: u64,
-    pub v_len: u64,
-}
-
-#[derive(Diagnostic)]
-#[diag(codegen_gcc_invalid_monomorphization_unsupported_cast, code = "E0511")]
-pub(crate) struct InvalidMonomorphizationUnsupportedCast<'a> {
-    #[primary_span]
-    pub span: Span,
-    pub name: Symbol,
-    pub in_ty: Ty<'a>,
-    pub in_elem: Ty<'a>,
-    pub ret_ty: Ty<'a>,
-    pub out_elem: Ty<'a>,
-}
-
-#[derive(Diagnostic)]
-#[diag(codegen_gcc_invalid_monomorphization_unsupported_operation, code = "E0511")]
-pub(crate) struct InvalidMonomorphizationUnsupportedOperation<'a> {
-    #[primary_span]
-    pub span: Span,
-    pub name: Symbol,
-    pub in_ty: Ty<'a>,
-    pub in_elem: Ty<'a>,
-}
-
-#[derive(Diagnostic)]
 #[diag(codegen_gcc_lto_not_supported)]
 pub(crate) struct LTONotSupported;
 
diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs
index fe604be33b6..b75546447e3 100644
--- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs
+++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs
@@ -13,6 +13,7 @@ use rustc_codegen_ssa::mir::place::PlaceRef;
 use rustc_codegen_ssa::traits::{ArgAbiMethods, BaseTypeMethods, BuilderMethods, ConstMethods, IntrinsicCallMethods};
 #[cfg(feature="master")]
 use rustc_codegen_ssa::traits::{DerivedTypeMethods, MiscMethods};
+use rustc_codegen_ssa::errors::InvalidMonomorphization;
 use rustc_middle::bug;
 use rustc_middle::ty::{self, Instance, Ty};
 use rustc_middle::ty::layout::LayoutOf;
@@ -31,7 +32,6 @@ use crate::abi::FnAbiGccExt;
 use crate::builder::Builder;
 use crate::common::{SignType, TypeReflection};
 use crate::context::CodegenCx;
-use crate::errors::InvalidMonomorphizationBasicInteger;
 use crate::type_of::LayoutGccExt;
 use crate::intrinsic::simd::generic_simd_intrinsic;
 
@@ -256,7 +256,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
                                 _ => bug!(),
                             },
                             None => {
-                                tcx.sess.emit_err(InvalidMonomorphizationBasicInteger { span, name, ty });
+                                tcx.sess.emit_err(InvalidMonomorphization::BasicIntegerType { span, name, ty });
                                 return;
                             }
                         }
diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs
index 9115cf97119..f27de867d36 100644
--- a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs
+++ b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs
@@ -21,19 +21,6 @@ use rustc_target::abi::Align;
 use crate::builder::Builder;
 #[cfg(feature="master")]
 use crate::context::CodegenCx;
-#[cfg(feature="master")]
-use crate::errors::{InvalidMonomorphizationExpectedSignedUnsigned, InvalidMonomorphizationInsertedType};
-use crate::errors::{
-    InvalidMonomorphizationExpectedSimd,
-    InvalidMonomorphizationInvalidBitmask,
-    InvalidMonomorphizationInvalidFloatVector, InvalidMonomorphizationMaskType,
-    InvalidMonomorphizationMismatchedLengths, InvalidMonomorphizationNotFloat,
-    InvalidMonomorphizationReturnElement, InvalidMonomorphizationReturnIntegerType,
-    InvalidMonomorphizationReturnLength, InvalidMonomorphizationReturnLengthInputType,
-    InvalidMonomorphizationReturnType, InvalidMonomorphizationSimdShuffle,
-    InvalidMonomorphizationUnrecognized, InvalidMonomorphizationUnsupportedElement,
-    InvalidMonomorphizationUnsupportedOperation,
-};
 
 pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
     bx: &mut Builder<'a, 'gcc, 'tcx>,
@@ -59,16 +46,8 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
         };
     }
     macro_rules! require_simd {
-        ($ty: expr, $position: expr) => {
-            require!(
-                $ty.is_simd(),
-                InvalidMonomorphizationExpectedSimd {
-                    span,
-                    name,
-                    position: $position,
-                    found_ty: $ty
-                }
-            )
+        ($ty: expr, $diag: expr) => {
+            require!($ty.is_simd(), $diag)
         };
     }
 
@@ -78,7 +57,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
     let arg_tys = sig.inputs();
 
     if name == sym::simd_select_bitmask {
-        require_simd!(arg_tys[1], "argument");
+        require_simd!(arg_tys[1], InvalidMonomorphization::SimdArgument { span, name, ty: arg_tys[1] });
         let (len, _) = arg_tys[1].simd_size_and_type(bx.tcx());
 
         let expected_int_bits = (len.max(8) - 1).next_power_of_two();
@@ -99,10 +78,10 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
                 let ptr = bx.pointercast(place.llval, bx.cx.type_ptr_to(int_ty));
                 bx.load(int_ty, ptr, Align::ONE)
             }
-            _ => return_error!(InvalidMonomorphizationInvalidBitmask {
+            _ => return_error!(InvalidMonomorphization::InvalidBitmask {
                 span,
                 name,
-                ty: mask_ty,
+                mask_ty,
                 expected_int_bits,
                 expected_bytes
             }),
@@ -131,7 +110,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
     }
 
     // every intrinsic below takes a SIMD vector as its first argument
-    require_simd!(arg_tys[0], "input");
+    require_simd!(arg_tys[0], InvalidMonomorphization::SimdInput { span, name, ty: arg_tys[0] });
     let in_ty = arg_tys[0];
 
     let comparison = match name {
@@ -146,12 +125,12 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
 
     let (in_len, in_elem) = arg_tys[0].simd_size_and_type(bx.tcx());
     if let Some(cmp_op) = comparison {
-        require_simd!(ret_ty, "return");
+        require_simd!(ret_ty, InvalidMonomorphization::SimdReturn { span, name, ty: ret_ty });
 
         let (out_len, out_ty) = ret_ty.simd_size_and_type(bx.tcx());
         require!(
             in_len == out_len,
-            InvalidMonomorphizationReturnLengthInputType {
+            InvalidMonomorphization::ReturnLengthInputType {
                 span,
                 name,
                 in_len,
@@ -162,7 +141,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
         );
         require!(
             bx.type_kind(bx.element_type(llret_ty)) == TypeKind::Integer,
-            InvalidMonomorphizationReturnIntegerType { span, name, ret_ty, out_ty }
+            InvalidMonomorphization::ReturnIntegerType { span, name, ret_ty, out_ty }
         );
 
         let arg1 = args[0].immediate();
@@ -190,7 +169,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
                         || span_bug!(span, "could not evaluate shuffle index array length"),
                     )
                 }
-                _ => return_error!(InvalidMonomorphizationSimdShuffle {
+                _ => return_error!(InvalidMonomorphization::SimdShuffle {
                     span,
                     name,
                     ty: args[2].layout.ty
@@ -202,16 +181,16 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
             })
         };
 
-        require_simd!(ret_ty, "return");
+        require_simd!(ret_ty, InvalidMonomorphization::SimdReturn { span, name, ty: ret_ty });
 
         let (out_len, out_ty) = ret_ty.simd_size_and_type(bx.tcx());
         require!(
             out_len == n,
-            InvalidMonomorphizationReturnLength { span, name, in_len: n, ret_ty, out_len }
+            InvalidMonomorphization::ReturnLength { span, name, in_len: n, ret_ty, out_len }
         );
         require!(
             in_elem == out_ty,
-            InvalidMonomorphizationReturnElement { span, name, in_elem, in_ty, ret_ty, out_ty }
+            InvalidMonomorphization::ReturnElement { span, name, in_elem, in_ty, ret_ty, out_ty }
         );
 
         let vector = args[2].immediate();
@@ -223,7 +202,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
     if name == sym::simd_insert {
         require!(
             in_elem == arg_tys[2],
-            InvalidMonomorphizationInsertedType { span, name, in_elem, in_ty, out_ty: arg_tys[2] }
+            InvalidMonomorphization::InsertedType { span, name, in_elem, in_ty, out_ty: arg_tys[2] }
         );
         let vector = args[0].immediate();
         let index = args[1].immediate();
@@ -240,7 +219,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
     if name == sym::simd_extract {
         require!(
             ret_ty == in_elem,
-            InvalidMonomorphizationReturnType { span, name, in_elem, in_ty, ret_ty }
+            InvalidMonomorphization::ReturnType { span, name, in_elem, in_ty, ret_ty }
         );
         let vector = args[0].immediate();
         return Ok(bx.context.new_vector_access(None, vector, args[1].immediate()).to_rvalue());
@@ -249,26 +228,26 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
     if name == sym::simd_select {
         let m_elem_ty = in_elem;
         let m_len = in_len;
-        require_simd!(arg_tys[1], "argument");
+        require_simd!(arg_tys[1], InvalidMonomorphization::SimdArgument { span, name, ty: arg_tys[1] });
         let (v_len, _) = arg_tys[1].simd_size_and_type(bx.tcx());
         require!(
             m_len == v_len,
-            InvalidMonomorphizationMismatchedLengths { span, name, m_len, v_len }
+            InvalidMonomorphization::MismatchedLengths { span, name, m_len, v_len }
         );
         match m_elem_ty.kind() {
             ty::Int(_) => {}
-            _ => return_error!(InvalidMonomorphizationMaskType { span, name, ty: m_elem_ty }),
+            _ => return_error!(InvalidMonomorphization::MaskType { span, name, ty: m_elem_ty }),
         }
         return Ok(bx.vector_select(args[0].immediate(), args[1].immediate(), args[2].immediate()));
     }
 
     #[cfg(feature="master")]
     if name == sym::simd_cast || name == sym::simd_as {
-        require_simd!(ret_ty, "return");
+        require_simd!(ret_ty, InvalidMonomorphization::SimdReturn { span, name, ty: ret_ty });
         let (out_len, out_elem) = ret_ty.simd_size_and_type(bx.tcx());
         require!(
             in_len == out_len,
-            InvalidMonomorphizationReturnLengthInputType {
+            InvalidMonomorphization::ReturnLengthInputType {
                 span,
                 name,
                 in_len,
@@ -329,7 +308,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
                     })*
                     _ => {},
                 }
-                return_error!(InvalidMonomorphizationUnsupportedOperation { span, name, in_ty, in_elem })
+                return_error!(InvalidMonomorphization::UnsupportedOperation { span, name, in_ty, in_elem })
             })*
         }
     }
@@ -422,12 +401,12 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
                     32 => ("f", elem_ty),
                     64 => ("", elem_ty),
                     _ => {
-                        return_error!(InvalidMonomorphizationInvalidFloatVector { span, name, elem_ty: f.name_str(), vec_ty: in_ty });
+                        return_error!(InvalidMonomorphization::FloatingPointVector { span, name, f_ty: *f, in_ty });
                     }
                 }
             }
             else {
-                return_error!(InvalidMonomorphizationNotFloat { span, name, ty: in_ty });
+                return_error!(InvalidMonomorphization::FloatingPointType { span, name, in_ty });
             };
 
         let vec_ty = bx.cx.type_vector(elem_ty, in_len);
@@ -450,7 +429,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
                 sym::simd_fsqrt => "sqrt",
                 sym::simd_round => "round",
                 sym::simd_trunc => "trunc",
-                _ => return_error!(InvalidMonomorphizationUnrecognized { span, name })
+                _ => return_error!(InvalidMonomorphization::UnrecognizedIntrinsic { span, name })
             };
         let builtin_name = format!("{}{}", intr_name, elem_ty_str);
         let funcs = bx.cx.functions.borrow();
@@ -566,10 +545,10 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
         // * M: any integer width is supported, will be truncated to i1
 
         // All types must be simd vector types
-        require_simd!(in_ty, "first");
-        require_simd!(arg_tys[1], "second");
-        require_simd!(arg_tys[2], "third");
-        require_simd!(ret_ty, "return");
+        require_simd!(in_ty, InvalidMonomorphization::SimdFirst { span, name, ty: in_ty });
+        require_simd!(arg_tys[1], InvalidMonomorphization::SimdSecond { span, name, ty: arg_tys[1] });
+        require_simd!(arg_tys[2], InvalidMonomorphization::SimdThird { span, name, ty: arg_tys[2] });
+        require_simd!(ret_ty, InvalidMonomorphization::SimdReturn { span, name, ty: ret_ty });
 
         // Of the same length:
         let (out_len, _) = arg_tys[1].simd_size_and_type(bx.tcx());
@@ -674,9 +653,9 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
         // * M: any integer width is supported, will be truncated to i1
 
         // All types must be simd vector types
-        require_simd!(in_ty, "first");
-        require_simd!(arg_tys[1], "second");
-        require_simd!(arg_tys[2], "third");
+        require_simd!(in_ty, InvalidMonomorphization::SimdFirst { span, name, ty: in_ty });
+        require_simd!(arg_tys[1], InvalidMonomorphization::SimdSecond { span, name, ty: arg_tys[1] });
+        require_simd!(arg_tys[2], InvalidMonomorphization::SimdThird { span, name, ty: arg_tys[2] });
 
         // Of the same length:
         let (element_len1, _) = arg_tys[1].simd_size_and_type(bx.tcx());
@@ -815,7 +794,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
                     })*
                     _ => {},
                 }
-                return_error!(InvalidMonomorphizationUnsupportedOperation { span, name, in_ty, in_elem })
+                return_error!(InvalidMonomorphization::UnsupportedOperation { span, name, in_ty, in_elem })
             })*
         }
     }
@@ -835,11 +814,11 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
                 ty::Int(i) => (true, i.bit_width().unwrap_or(ptr_bits) / 8, bx.cx.type_int_from_ty(i)),
                 ty::Uint(i) => (false, i.bit_width().unwrap_or(ptr_bits) / 8, bx.cx.type_uint_from_ty(i)),
                 _ => {
-                return_error!(InvalidMonomorphizationExpectedSignedUnsigned {
+                return_error!(InvalidMonomorphization::ExpectedVectorElementType {
                     span,
                     name,
-                    elem_ty: arg_tys[0].simd_size_and_type(bx.tcx()).1,
-                    vec_ty: arg_tys[0],
+                    expected_element: arg_tys[0].simd_size_and_type(bx.tcx()).1,
+                    vector_type: arg_tys[0],
                 });
             }
         };
@@ -925,7 +904,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
             if name == sym::$name {
                 require!(
                     ret_ty == in_elem,
-                    InvalidMonomorphizationReturnType { span, name, in_elem, in_ty, ret_ty }
+                    InvalidMonomorphization::ReturnType { span, name, in_elem, in_ty, ret_ty }
                 );
                 return match in_elem.kind() {
                     ty::Int(_) | ty::Uint(_) => {
@@ -947,11 +926,12 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
                             Ok(bx.vector_reduce_op(args[0].immediate(), $vec_op))
                         }
                     }
-                    _ => return_error!(InvalidMonomorphizationUnsupportedElement {
+                    _ => return_error!(InvalidMonomorphization::UnsupportedSymbol {
                         span,
                         name,
+                        symbol: sym::$name,
                         in_ty,
-                        elem_ty: in_elem,
+                        in_elem,
                         ret_ty
                     }),
                 };
@@ -994,12 +974,19 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
             if name == sym::$name {
                 require!(
                     ret_ty == in_elem,
-                    InvalidMonomorphizationReturnType { span, name, in_elem, in_ty, ret_ty }
+                    InvalidMonomorphization::ReturnType { span, name, in_elem, in_ty, ret_ty }
                 );
                 return match in_elem.kind() {
                     ty::Int(_) | ty::Uint(_) => Ok(bx.$int_red(args[0].immediate())),
                     ty::Float(_) => Ok(bx.$float_red(args[0].immediate())),
-                    _ => return_error!(InvalidMonomorphizationUnsupportedElement { span, name, in_ty, elem_ty: in_elem, ret_ty }),
+                    _ => return_error!(InvalidMonomorphization::UnsupportedSymbol { 
+                        span, 
+                        name,
+                        symbol: sym::$name,
+                        in_ty,
+                        in_elem, 
+                        ret_ty 
+                    }),
                 };
             }
         };
@@ -1017,17 +1004,18 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
                 let input = if !$boolean {
                     require!(
                         ret_ty == in_elem,
-                        InvalidMonomorphizationReturnType { span, name, in_elem, in_ty, ret_ty }
+                        InvalidMonomorphization::ReturnType { span, name, in_elem, in_ty, ret_ty }
                     );
                     args[0].immediate()
                 } else {
                     match in_elem.kind() {
                         ty::Int(_) | ty::Uint(_) => {}
-                        _ => return_error!(InvalidMonomorphizationUnsupportedElement {
+                        _ => return_error!(InvalidMonomorphization::UnsupportedSymbol {
                             span,
                             name,
+                            symbol: sym::$name,
                             in_ty,
-                            elem_ty: in_elem,
+                            in_elem,
                             ret_ty
                         }),
                     }
@@ -1039,11 +1027,12 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
                         let r = bx.vector_reduce_op(input, $op);
                         Ok(if !$boolean { r } else { bx.icmp(IntPredicate::IntNE, r, bx.context.new_rvalue_zero(r.get_type())) })
                     }
-                    _ => return_error!(InvalidMonomorphizationUnsupportedElement {
+                    _ => return_error!(InvalidMonomorphization::UnsupportedSymbol {
                         span,
                         name,
+                        symbol: sym::$name,
                         in_ty,
-                        elem_ty: in_elem,
+                        in_elem,
                         ret_ty
                     }),
                 };
diff --git a/compiler/rustc_codegen_gcc/src/type_of.rs b/compiler/rustc_codegen_gcc/src/type_of.rs
index a30bce0a313..84d57838512 100644
--- a/compiler/rustc_codegen_gcc/src/type_of.rs
+++ b/compiler/rustc_codegen_gcc/src/type_of.rs
@@ -339,8 +339,7 @@ impl<'tcx> LayoutGccExt<'tcx> for TyAndLayout<'tcx> {
             return pointee;
         }
 
-        let assume_valid_ptr = true;
-        let result = Ty::ty_and_layout_pointee_info_at(*self, cx, offset, assume_valid_ptr);
+        let result = Ty::ty_and_layout_pointee_info_at(*self, cx, offset);
 
         cx.pointee_infos.borrow_mut().insert((self.ty, offset), result);
         result
diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs
index e1e0a442845..5bf641055c5 100644
--- a/compiler/rustc_codegen_llvm/src/context.rs
+++ b/compiler/rustc_codegen_llvm/src/context.rs
@@ -33,6 +33,7 @@ use rustc_target::abi::{
 use rustc_target::spec::{HasTargetSpec, RelocModel, Target, TlsModel};
 use smallvec::SmallVec;
 
+use libc::c_uint;
 use std::cell::{Cell, RefCell};
 use std::ffi::CStr;
 use std::str;
@@ -349,6 +350,23 @@ pub unsafe fn create_module<'ll>(
         );
     }
 
+    // Insert `llvm.ident` metadata.
+    //
+    // On the wasm targets it will get hooked up to the "producer" sections
+    // `processed-by` information.
+    let rustc_producer =
+        format!("rustc version {}", option_env!("CFG_VERSION").expect("CFG_VERSION"));
+    let name_metadata = llvm::LLVMMDStringInContext(
+        llcx,
+        rustc_producer.as_ptr().cast(),
+        rustc_producer.as_bytes().len() as c_uint,
+    );
+    llvm::LLVMAddNamedMetadataOperand(
+        llmod,
+        cstr!("llvm.ident").as_ptr(),
+        llvm::LLVMMDNodeInContext(llcx, &name_metadata, 1),
+    );
+
     llmod
 }
 
diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs
index 6e815ab3ff5..9149b06886b 100644
--- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs
+++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs
@@ -100,9 +100,11 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) {
     // Generate the LLVM IR representation of the coverage map and store it in a well-known global
     let cov_data_val = mapgen.generate_coverage_map(cx, version, filenames_size, filenames_val);
 
+    let covfun_section_name = coverageinfo::covfun_section_name(cx);
     for (mangled_function_name, source_hash, is_used, coverage_mapping_buffer) in function_data {
         save_function_record(
             cx,
+            &covfun_section_name,
             mangled_function_name,
             source_hash,
             filenames_ref,
@@ -228,6 +230,7 @@ impl CoverageMapGenerator {
 /// specific, well-known section and name.
 fn save_function_record(
     cx: &CodegenCx<'_, '_>,
+    covfun_section_name: &str,
     mangled_function_name: &str,
     source_hash: u64,
     filenames_ref: u64,
@@ -254,7 +257,13 @@ fn save_function_record(
         /*packed=*/ true,
     );
 
-    coverageinfo::save_func_record_to_mod(cx, func_name_hash, func_record_val, is_used);
+    coverageinfo::save_func_record_to_mod(
+        cx,
+        covfun_section_name,
+        func_name_hash,
+        func_record_val,
+        is_used,
+    );
 }
 
 /// When finalizing the coverage map, `FunctionCoverage` only has the `CodeRegion`s and counters for
diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs
index 1f8f4033931..60cbb3d3d9d 100644
--- a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs
+++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs
@@ -408,6 +408,7 @@ pub(crate) fn save_cov_data_to_mod<'ll, 'tcx>(
 
 pub(crate) fn save_func_record_to_mod<'ll, 'tcx>(
     cx: &CodegenCx<'ll, 'tcx>,
+    covfun_section_name: &str,
     func_name_hash: u64,
     func_record_val: &'ll llvm::Value,
     is_used: bool,
@@ -423,20 +424,33 @@ pub(crate) fn save_func_record_to_mod<'ll, 'tcx>(
     let func_record_var_name =
         format!("__covrec_{:X}{}", func_name_hash, if is_used { "u" } else { "" });
     debug!("function record var name: {:?}", func_record_var_name);
-
-    let func_record_section_name = llvm::build_string(|s| unsafe {
-        llvm::LLVMRustCoverageWriteFuncSectionNameToString(cx.llmod, s);
-    })
-    .expect("Rust Coverage function record section name failed UTF-8 conversion");
-    debug!("function record section name: {:?}", func_record_section_name);
+    debug!("function record section name: {:?}", covfun_section_name);
 
     let llglobal = llvm::add_global(cx.llmod, cx.val_ty(func_record_val), &func_record_var_name);
     llvm::set_initializer(llglobal, func_record_val);
     llvm::set_global_constant(llglobal, true);
     llvm::set_linkage(llglobal, llvm::Linkage::LinkOnceODRLinkage);
     llvm::set_visibility(llglobal, llvm::Visibility::Hidden);
-    llvm::set_section(llglobal, &func_record_section_name);
+    llvm::set_section(llglobal, covfun_section_name);
     llvm::set_alignment(llglobal, VAR_ALIGN_BYTES);
     llvm::set_comdat(cx.llmod, llglobal, &func_record_var_name);
     cx.add_used_global(llglobal);
 }
+
+/// Returns the section name string to pass through to the linker when embedding
+/// per-function coverage information in the object file, according to the target
+/// platform's object file format.
+///
+/// LLVM's coverage tools read coverage mapping details from this section when
+/// producing coverage reports.
+///
+/// Typical values are:
+/// - `__llvm_covfun` on Linux
+/// - `__LLVM_COV,__llvm_covfun` on macOS (includes `__LLVM_COV,` segment prefix)
+/// - `.lcovfun$M` on Windows (includes `$M` sorting suffix)
+pub(crate) fn covfun_section_name(cx: &CodegenCx<'_, '_>) -> String {
+    llvm::build_string(|s| unsafe {
+        llvm::LLVMRustCoverageWriteFuncSectionNameToString(cx.llmod, s);
+    })
+    .expect("Rust Coverage function record section name failed UTF-8 conversion")
+}
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
index c6996f2e16a..905e0e541a8 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
@@ -888,21 +888,6 @@ pub fn build_compile_unit_di_node<'ll, 'tcx>(
             llvm::LLVMAddNamedMetadataOperand(debug_context.llmod, llvm_gcov_ident.as_ptr(), val);
         }
 
-        // Insert `llvm.ident` metadata on the wasm targets since that will
-        // get hooked up to the "producer" sections `processed-by` information.
-        if tcx.sess.target.is_like_wasm {
-            let name_metadata = llvm::LLVMMDStringInContext(
-                debug_context.llcontext,
-                rustc_producer.as_ptr().cast(),
-                rustc_producer.as_bytes().len() as c_uint,
-            );
-            llvm::LLVMAddNamedMetadataOperand(
-                debug_context.llmod,
-                cstr!("llvm.ident").as_ptr(),
-                llvm::LLVMMDNodeInContext(debug_context.llcontext, &name_metadata, 1),
-            );
-        }
-
         return unit_metadata;
     };
 
diff --git a/compiler/rustc_codegen_llvm/src/type_of.rs b/compiler/rustc_codegen_llvm/src/type_of.rs
index 29dd53ff763..2dbd467cc84 100644
--- a/compiler/rustc_codegen_llvm/src/type_of.rs
+++ b/compiler/rustc_codegen_llvm/src/type_of.rs
@@ -411,8 +411,8 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyAndLayout<'tcx> {
         if let Some(&pointee) = cx.pointee_infos.borrow().get(&(self.ty, offset)) {
             return pointee;
         }
-        let assume_valid_ptr = true;
-        let result = Ty::ty_and_layout_pointee_info_at(*self, cx, offset, assume_valid_ptr);
+
+        let result = Ty::ty_and_layout_pointee_info_at(*self, cx, offset);
 
         cx.pointee_infos.borrow_mut().insert((self.ty, offset), result);
         result
diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs
index 6c781c6f082..0bbb6c9c338 100644
--- a/compiler/rustc_codegen_ssa/src/back/write.rs
+++ b/compiler/rustc_codegen_ssa/src/back/write.rs
@@ -362,7 +362,7 @@ pub struct CodegenContext<B: WriteBackendMethods> {
 
 impl<B: WriteBackendMethods> CodegenContext<B> {
     pub fn create_diag_handler(&self) -> Handler {
-        Handler::with_emitter(true, None, Box::new(self.diag_emitter.clone()), None)
+        Handler::with_emitter(Box::new(self.diag_emitter.clone()))
     }
 
     pub fn config(&self, kind: ModuleKind) -> &ModuleConfig {
diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs
index dc862803274..79c284ecfbf 100644
--- a/compiler/rustc_codegen_ssa/src/base.rs
+++ b/compiler/rustc_codegen_ssa/src/base.rs
@@ -38,6 +38,7 @@ use rustc_span::symbol::sym;
 use rustc_span::Symbol;
 use rustc_target::abi::{Align, FIRST_VARIANT};
 
+use std::cmp;
 use std::collections::BTreeSet;
 use std::time::{Duration, Instant};
 
@@ -682,10 +683,10 @@ pub fn codegen_crate<B: ExtraBackendMethods>(
     // are large size variations, this can reduce memory usage significantly.
     let codegen_units: Vec<_> = {
         let mut sorted_cgus = codegen_units.iter().collect::<Vec<_>>();
-        sorted_cgus.sort_by_cached_key(|cgu| cgu.size_estimate());
+        sorted_cgus.sort_by_key(|cgu| cmp::Reverse(cgu.size_estimate()));
 
         let (first_half, second_half) = sorted_cgus.split_at(sorted_cgus.len() / 2);
-        second_half.iter().rev().interleave(first_half).copied().collect()
+        first_half.iter().interleave(second_half.iter().rev()).copied().collect()
     };
 
     // Calculate the CGU reuse
diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
index 0c7b8a79612..92792ab6477 100644
--- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
+++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
@@ -501,7 +501,22 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
     });
 
     // #73631: closures inherit `#[target_feature]` annotations
-    if tcx.features().target_feature_11 && tcx.is_closure(did.to_def_id()) {
+    //
+    // If this closure is marked `#[inline(always)]`, simply skip adding `#[target_feature]`.
+    //
+    // At this point, `unsafe` has already been checked and `#[target_feature]` only affects codegen.
+    // Emitting both `#[inline(always)]` and `#[target_feature]` can potentially result in an
+    // ICE, because LLVM errors when the function fails to be inlined due to a target feature
+    // mismatch.
+    //
+    // Using `#[inline(always)]` implies that this closure will most likely be inlined into
+    // its parent function, which effectively inherits the features anyway. Boxing this closure
+    // would result in this closure being compiled without the inherited target features, but this
+    // is probably a poor usage of `#[inline(always)]` and easily avoided by not using the attribute.
+    if tcx.features().target_feature_11
+        && tcx.is_closure(did.to_def_id())
+        && codegen_fn_attrs.inline != InlineAttr::Always
+    {
         let owner_id = tcx.parent(did.to_def_id());
         if tcx.def_kind(owner_id).has_codegen_attrs() {
             codegen_fn_attrs
diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs
index c72d37be748..dbe1d6e8ba8 100644
--- a/compiler/rustc_codegen_ssa/src/errors.rs
+++ b/compiler/rustc_codegen_ssa/src/errors.rs
@@ -353,7 +353,7 @@ impl IntoDiagnostic<'_> for LinkingFailed<'_> {
 
         let contains_undefined_ref = self.escaped_output.contains("undefined reference to");
 
-        diag.note(format!("{:?}", self.command)).note(self.escaped_output.to_string());
+        diag.note(format!("{:?}", self.command)).note(self.escaped_output);
 
         // Trying to match an error from OS linkers
         // which by now we have no way to translate.
diff --git a/compiler/rustc_codegen_ssa/src/traits/consts.rs b/compiler/rustc_codegen_ssa/src/traits/consts.rs
index d6e9bfce1a4..822c19155e3 100644
--- a/compiler/rustc_codegen_ssa/src/traits/consts.rs
+++ b/compiler/rustc_codegen_ssa/src/traits/consts.rs
@@ -5,7 +5,13 @@ use rustc_target::abi;
 pub trait ConstMethods<'tcx>: BackendTypes {
     // Constant constructors
     fn const_null(&self, t: Self::Type) -> Self::Value;
+    /// Generate an uninitialized value (matching uninitialized memory in MIR).
+    /// Whether memory is initialized or not is tracked byte-for-byte.
     fn const_undef(&self, t: Self::Type) -> Self::Value;
+    /// Generate a fake value. Poison always affects the entire value, even if just a single byte is
+    /// poison. This can only be used in codepaths that are already UB, i.e., UB-free Rust code
+    /// (including code that e.g. copies uninit memory with `MaybeUninit`) can never encounter a
+    /// poison value.
     fn const_poison(&self, t: Self::Type) -> Self::Value;
     fn const_int(&self, t: Self::Type, i: i64) -> Self::Value;
     fn const_uint(&self, t: Self::Type, i: u64) -> Self::Value;
diff --git a/compiler/rustc_const_eval/Cargo.toml b/compiler/rustc_const_eval/Cargo.toml
index 74030a43c50..4e47fed8640 100644
--- a/compiler/rustc_const_eval/Cargo.toml
+++ b/compiler/rustc_const_eval/Cargo.toml
@@ -8,7 +8,7 @@ edition = "2021"
 [dependencies]
 tracing = "0.1"
 either = "1"
-rustc_apfloat = { path = "../rustc_apfloat" }
+rustc_apfloat = "0.2.0"
 rustc_ast = { path = "../rustc_ast" }
 rustc_attr = { path = "../rustc_attr" }
 rustc_data_structures = { path = "../rustc_data_structures" }
diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl
index 8833f55831c..671c2be1de9 100644
--- a/compiler/rustc_const_eval/messages.ftl
+++ b/compiler/rustc_const_eval/messages.ftl
@@ -244,6 +244,7 @@ const_eval_not_enough_caller_args =
 const_eval_null_box = {$front_matter}: encountered a null box
 const_eval_null_fn_ptr = {$front_matter}: encountered a null function pointer
 const_eval_null_ref = {$front_matter}: encountered a null reference
+const_eval_nullable_ptr_out_of_range = {$front_matter}: encountered a potentially null pointer, but expected something that cannot possibly fail to be {$in_range}
 const_eval_nullary_intrinsic_fail =
     could not evaluate nullary intrinsic
 
@@ -407,8 +408,11 @@ const_eval_undefined_behavior =
 const_eval_undefined_behavior_note =
     The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
 
+const_eval_uninhabited_enum_tag = {$front_matter}: encountered an uninhabited enum variant
+const_eval_uninhabited_enum_variant_read =
+    read discriminant of an uninhabited enum variant
 const_eval_uninhabited_enum_variant_written =
-    writing discriminant of an uninhabited enum
+    writing discriminant of an uninhabited enum variant
 const_eval_uninhabited_val = {$front_matter}: encountered a value of uninhabited type `{$ty}`
 const_eval_uninit = {$front_matter}: encountered uninitialized bytes
 const_eval_uninit_bool = {$front_matter}: encountered uninitialized memory, but expected a boolean
@@ -422,8 +426,6 @@ const_eval_uninit_int = {$front_matter}: encountered uninitialized memory, but e
 const_eval_uninit_raw_ptr = {$front_matter}: encountered uninitialized memory, but expected a raw pointer
 const_eval_uninit_ref = {$front_matter}: encountered uninitialized memory, but expected a reference
 const_eval_uninit_str = {$front_matter}: encountered uninitialized data in `str`
-const_eval_uninit_unsized_local =
-    unsized local is used while uninitialized
 const_eval_unreachable = entering unreachable code
 const_eval_unreachable_unwind =
     unwinding past a stack frame that does not allow unwinding
diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
index b21cb984de6..4c7e9194401 100644
--- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
+++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
@@ -52,13 +52,13 @@ fn eval_body_using_ecx<'mir, 'tcx>(
     trace!(
         "eval_body_using_ecx: pushing stack frame for global: {}{}",
         with_no_trimmed_paths!(ecx.tcx.def_path_str(cid.instance.def_id())),
-        cid.promoted.map_or_else(String::new, |p| format!("::promoted[{:?}]", p))
+        cid.promoted.map_or_else(String::new, |p| format!("::promoted[{p:?}]"))
     );
 
     ecx.push_stack_frame(
         cid.instance,
         body,
-        &ret.into(),
+        &ret.clone().into(),
         StackPopCleanup::Root { cleanup: false },
     )?;
 
@@ -228,7 +228,6 @@ pub fn eval_to_const_value_raw_provider<'tcx>(
     tcx: TyCtxt<'tcx>,
     key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>,
 ) -> ::rustc_middle::mir::interpret::EvalToConstValueResult<'tcx> {
-    assert!(key.param_env.is_const());
     // see comment in eval_to_allocation_raw_provider for what we're doing here
     if key.param_env.reveal() == Reveal::All {
         let mut key = key;
@@ -269,7 +268,6 @@ pub fn eval_to_allocation_raw_provider<'tcx>(
     tcx: TyCtxt<'tcx>,
     key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>,
 ) -> ::rustc_middle::mir::interpret::EvalToAllocationRawResult<'tcx> {
-    assert!(key.param_env.is_const());
     // Because the constant is computed twice (once per value of `Reveal`), we are at risk of
     // reporting the same error twice here. To resolve this, we check whether we can evaluate the
     // constant in the more restrictive `Reveal::UserFacing`, which most likely already was
@@ -356,7 +354,7 @@ pub fn eval_to_allocation_raw_provider<'tcx>(
             // Since evaluation had no errors, validate the resulting constant.
             // This is a separate `try` block to provide more targeted error reporting.
             let validation: Result<_, InterpErrorInfo<'_>> = try {
-                let mut ref_tracking = RefTracking::new(mplace);
+                let mut ref_tracking = RefTracking::new(mplace.clone());
                 let mut inner = false;
                 while let Some((mplace, path)) = ref_tracking.todo.pop() {
                     let mode = match tcx.static_mutability(cid.instance.def_id()) {
diff --git a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs
index fa8253d5e49..cc39387c41f 100644
--- a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs
+++ b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs
@@ -28,15 +28,18 @@ pub fn is_parent_const_impl_raw(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
         && tcx.constness(parent_id) == hir::Constness::Const
 }
 
-/// Checks whether an item is considered to be `const`. If it is a constructor, it is const. If
-/// it is a trait impl/function, return if it has a `const` modifier. If it is an intrinsic,
-/// report whether said intrinsic has a `rustc_const_{un,}stable` attribute. Otherwise, return
-/// `Constness::NotConst`.
+/// Checks whether an item is considered to be `const`. If it is a constructor, anonymous const,
+/// const block, const item or associated const, it is const. If it is a trait impl/function,
+/// return if it has a `const` modifier. If it is an intrinsic, report whether said intrinsic
+/// has a `rustc_const_{un,}stable` attribute. Otherwise, return `Constness::NotConst`.
 fn constness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::Constness {
     let node = tcx.hir().get_by_def_id(def_id);
 
     match node {
-        hir::Node::Ctor(_) => hir::Constness::Const,
+        hir::Node::Ctor(_)
+        | hir::Node::AnonConst(_)
+        | hir::Node::ConstBlock(_)
+        | hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Const(..), .. }) => hir::Constness::Const,
         hir::Node::Item(hir::Item { kind: hir::ItemKind::Impl(impl_), .. }) => impl_.constness,
         hir::Node::ForeignItem(hir::ForeignItem { kind: hir::ForeignItemKind::Fn(..), .. }) => {
             // Intrinsics use `rustc_const_{un,}stable` attributes to indicate constness. All other
diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs
index 0a9a47b2837..6630eeca27e 100644
--- a/compiler/rustc_const_eval/src/const_eval/machine.rs
+++ b/compiler/rustc_const_eval/src/const_eval/machine.rs
@@ -1,6 +1,7 @@
 use rustc_hir::def::DefKind;
 use rustc_hir::{LangItem, CRATE_HIR_ID};
 use rustc_middle::mir;
+use rustc_middle::mir::interpret::PointerArithmetic;
 use rustc_middle::ty::layout::{FnAbiOf, TyAndLayout};
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_session::lint::builtin::INVALID_ALIGNMENT;
@@ -16,7 +17,7 @@ use rustc_ast::Mutability;
 use rustc_hir::def_id::DefId;
 use rustc_middle::mir::AssertMessage;
 use rustc_span::symbol::{sym, Symbol};
-use rustc_target::abi::{Align, HasDataLayout as _, Size};
+use rustc_target::abi::{Align, Size};
 use rustc_target::spec::abi::Abi as CallAbi;
 
 use crate::errors::{LongRunning, LongRunningWarn};
@@ -215,7 +216,7 @@ impl<'mir, 'tcx: 'mir> CompileTimeEvalContext<'mir, 'tcx> {
 
             let mut msg_place = self.deref_operand(&args[0])?;
             while msg_place.layout.ty.is_ref() {
-                msg_place = self.deref_operand(&msg_place.into())?;
+                msg_place = self.deref_operand(&msg_place)?;
             }
 
             let msg = Symbol::intern(self.read_str(&msg_place)?);
@@ -303,8 +304,8 @@ impl<'mir, 'tcx: 'mir> CompileTimeEvalContext<'mir, 'tcx> {
                     Ok(ControlFlow::Break(()))
                 } else {
                     // Not alignable in const, return `usize::MAX`.
-                    let usize_max = self.data_layout().target_usize_max();
-                    self.write_scalar(Scalar::from_target_usize(usize_max, self), dest)?;
+                    let usize_max = Scalar::from_target_usize(self.target_usize_max(), self);
+                    self.write_scalar(usize_max, dest)?;
                     self.return_to_block(ret)?;
                     Ok(ControlFlow::Break(()))
                 }
@@ -332,7 +333,7 @@ impl<'mir, 'tcx: 'mir> CompileTimeEvalContext<'mir, 'tcx> {
             // Inequality with integers other than null can never be known for sure.
             (Scalar::Int(int), ptr @ Scalar::Ptr(..))
             | (ptr @ Scalar::Ptr(..), Scalar::Int(int))
-                if int.is_null() && !self.ptr_scalar_range(ptr)?.contains(&0) =>
+                if int.is_null() && !self.scalar_may_be_null(ptr)? =>
             {
                 0
             }
diff --git a/compiler/rustc_const_eval/src/const_eval/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs
index 0a3c3914ff9..ef31155215a 100644
--- a/compiler/rustc_const_eval/src/const_eval/mod.rs
+++ b/compiler/rustc_const_eval/src/const_eval/mod.rs
@@ -101,8 +101,8 @@ pub(crate) fn try_destructure_mir_constant_for_diagnostics<'tcx>(
             return None;
         }
         ty::Adt(def, _) => {
-            let variant = ecx.read_discriminant(&op).ok()?.1;
-            let down = ecx.operand_downcast(&op, variant).ok()?;
+            let variant = ecx.read_discriminant(&op).ok()?;
+            let down = ecx.project_downcast(&op, variant).ok()?;
             (def.variants()[variant].fields.len(), Some(variant), down)
         }
         ty::Tuple(args) => (args.len(), None, op),
@@ -111,7 +111,7 @@ pub(crate) fn try_destructure_mir_constant_for_diagnostics<'tcx>(
 
     let fields_iter = (0..field_count)
         .map(|i| {
-            let field_op = ecx.operand_field(&down, i).ok()?;
+            let field_op = ecx.project_field(&down, i).ok()?;
             let val = op_to_const(&ecx, &field_op);
             Some((val, field_op.layout.ty))
         })
diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs
index 9531d973eb3..7c1dbddfc26 100644
--- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs
+++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs
@@ -2,11 +2,11 @@ use super::eval_queries::{mk_eval_cx, op_to_const};
 use super::machine::CompileTimeEvalContext;
 use super::{ValTreeCreationError, ValTreeCreationResult, VALTREE_MAX_NODES};
 use crate::const_eval::CanAccessStatics;
+use crate::interpret::MPlaceTy;
 use crate::interpret::{
     intern_const_alloc_recursive, ConstValue, ImmTy, Immediate, InternKind, MemPlaceMeta,
-    MemoryKind, PlaceTy, Scalar,
+    MemoryKind, Place, Projectable, Scalar,
 };
-use crate::interpret::{MPlaceTy, Value};
 use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt};
 use rustc_span::source_map::DUMMY_SP;
 use rustc_target::abi::{Align, FieldIdx, VariantIdx, FIRST_VARIANT};
@@ -20,15 +20,15 @@ fn branches<'tcx>(
     num_nodes: &mut usize,
 ) -> ValTreeCreationResult<'tcx> {
     let place = match variant {
-        Some(variant) => ecx.mplace_downcast(&place, variant).unwrap(),
-        None => *place,
+        Some(variant) => ecx.project_downcast(place, variant).unwrap(),
+        None => place.clone(),
     };
     let variant = variant.map(|variant| Some(ty::ValTree::Leaf(ScalarInt::from(variant.as_u32()))));
     debug!(?place, ?variant);
 
     let mut fields = Vec::with_capacity(n);
     for i in 0..n {
-        let field = ecx.mplace_field(&place, i).unwrap();
+        let field = ecx.project_field(&place, i).unwrap();
         let valtree = const_to_valtree_inner(ecx, &field, num_nodes)?;
         fields.push(Some(valtree));
     }
@@ -55,13 +55,11 @@ fn slice_branches<'tcx>(
     place: &MPlaceTy<'tcx>,
     num_nodes: &mut usize,
 ) -> ValTreeCreationResult<'tcx> {
-    let n = place
-        .len(&ecx.tcx.tcx)
-        .unwrap_or_else(|_| panic!("expected to use len of place {:?}", place));
+    let n = place.len(ecx).unwrap_or_else(|_| panic!("expected to use len of place {place:?}"));
 
     let mut elems = Vec::with_capacity(n as usize);
     for i in 0..n {
-        let place_elem = ecx.mplace_index(place, i).unwrap();
+        let place_elem = ecx.project_index(place, i).unwrap();
         let valtree = const_to_valtree_inner(ecx, &place_elem, num_nodes)?;
         elems.push(valtree);
     }
@@ -88,7 +86,7 @@ pub(crate) fn const_to_valtree_inner<'tcx>(
             Ok(ty::ValTree::zst())
         }
         ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char => {
-            let Ok(val) = ecx.read_immediate(&place.into()) else {
+            let Ok(val) = ecx.read_immediate(place) else {
                 return Err(ValTreeCreationError::Other);
             };
             let val = val.to_scalar();
@@ -104,7 +102,7 @@ pub(crate) fn const_to_valtree_inner<'tcx>(
         ty::FnPtr(_) | ty::RawPtr(_) => Err(ValTreeCreationError::NonSupportedType),
 
         ty::Ref(_, _, _)  => {
-            let Ok(derefd_place)= ecx.deref_operand(&place.into()) else {
+            let Ok(derefd_place)= ecx.deref_operand(place) else {
                 return Err(ValTreeCreationError::Other);
             };
             debug!(?derefd_place);
@@ -132,7 +130,7 @@ pub(crate) fn const_to_valtree_inner<'tcx>(
                 bug!("uninhabited types should have errored and never gotten converted to valtree")
             }
 
-            let Ok((_, variant)) = ecx.read_discriminant(&place.into()) else {
+            let Ok(variant) = ecx.read_discriminant(place) else {
                 return Err(ValTreeCreationError::Other);
             };
             branches(ecx, place, def.variant(variant).fields.len(), def.is_enum().then_some(variant), num_nodes)
@@ -282,7 +280,7 @@ pub fn valtree_to_const_value<'tcx>(
             ),
         },
         ty::Ref(_, _, _) | ty::Tuple(_) | ty::Array(_, _) | ty::Adt(..) => {
-            let mut place = match ty.kind() {
+            let place = match ty.kind() {
                 ty::Ref(_, inner_ty, _) => {
                     // Need to create a place for the pointee to fill for Refs
                     create_pointee_place(&mut ecx, *inner_ty, valtree)
@@ -291,8 +289,8 @@ pub fn valtree_to_const_value<'tcx>(
             };
             debug!(?place);
 
-            valtree_into_mplace(&mut ecx, &mut place, valtree);
-            dump_place(&ecx, place.into());
+            valtree_into_mplace(&mut ecx, &place, valtree);
+            dump_place(&ecx, &place);
             intern_const_alloc_recursive(&mut ecx, InternKind::Constant, &place).unwrap();
 
             match ty.kind() {
@@ -331,7 +329,7 @@ pub fn valtree_to_const_value<'tcx>(
 #[instrument(skip(ecx), level = "debug")]
 fn valtree_into_mplace<'tcx>(
     ecx: &mut CompileTimeEvalContext<'tcx, 'tcx>,
-    place: &mut MPlaceTy<'tcx>,
+    place: &MPlaceTy<'tcx>,
     valtree: ty::ValTree<'tcx>,
 ) {
     // This will match on valtree and write the value(s) corresponding to the ValTree
@@ -347,14 +345,14 @@ fn valtree_into_mplace<'tcx>(
         ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char => {
             let scalar_int = valtree.unwrap_leaf();
             debug!("writing trivial valtree {:?} to place {:?}", scalar_int, place);
-            ecx.write_immediate(Immediate::Scalar(scalar_int.into()), &place.into()).unwrap();
+            ecx.write_immediate(Immediate::Scalar(scalar_int.into()), place).unwrap();
         }
         ty::Ref(_, inner_ty, _) => {
-            let mut pointee_place = create_pointee_place(ecx, *inner_ty, valtree);
+            let pointee_place = create_pointee_place(ecx, *inner_ty, valtree);
             debug!(?pointee_place);
 
-            valtree_into_mplace(ecx, &mut pointee_place, valtree);
-            dump_place(ecx, pointee_place.into());
+            valtree_into_mplace(ecx, &pointee_place, valtree);
+            dump_place(ecx, &pointee_place);
             intern_const_alloc_recursive(ecx, InternKind::Constant, &pointee_place).unwrap();
 
             let imm = match inner_ty.kind() {
@@ -371,7 +369,7 @@ fn valtree_into_mplace<'tcx>(
             };
             debug!(?imm);
 
-            ecx.write_immediate(imm, &place.into()).unwrap();
+            ecx.write_immediate(imm, place).unwrap();
         }
         ty::Adt(_, _) | ty::Tuple(_) | ty::Array(_, _) | ty::Str | ty::Slice(_) => {
             let branches = valtree.unwrap_branch();
@@ -386,12 +384,12 @@ fn valtree_into_mplace<'tcx>(
                     debug!(?variant);
 
                     (
-                        place.project_downcast(ecx, variant_idx).unwrap(),
+                        ecx.project_downcast(place, variant_idx).unwrap(),
                         &branches[1..],
                         Some(variant_idx),
                     )
                 }
-                _ => (*place, branches, None),
+                _ => (place.clone(), branches, None),
             };
             debug!(?place_adjusted, ?branches);
 
@@ -400,8 +398,8 @@ fn valtree_into_mplace<'tcx>(
             for (i, inner_valtree) in branches.iter().enumerate() {
                 debug!(?i, ?inner_valtree);
 
-                let mut place_inner = match ty.kind() {
-                    ty::Str | ty::Slice(_) => ecx.mplace_index(&place, i as u64).unwrap(),
+                let place_inner = match ty.kind() {
+                    ty::Str | ty::Slice(_) => ecx.project_index(place, i as u64).unwrap(),
                     _ if !ty.is_sized(*ecx.tcx, ty::ParamEnv::empty())
                         && i == branches.len() - 1 =>
                     {
@@ -441,29 +439,29 @@ fn valtree_into_mplace<'tcx>(
                             )
                             .unwrap()
                     }
-                    _ => ecx.mplace_field(&place_adjusted, i).unwrap(),
+                    _ => ecx.project_field(&place_adjusted, i).unwrap(),
                 };
 
                 debug!(?place_inner);
-                valtree_into_mplace(ecx, &mut place_inner, *inner_valtree);
-                dump_place(&ecx, place_inner.into());
+                valtree_into_mplace(ecx, &place_inner, *inner_valtree);
+                dump_place(&ecx, &place_inner);
             }
 
             debug!("dump of place_adjusted:");
-            dump_place(ecx, place_adjusted.into());
+            dump_place(ecx, &place_adjusted);
 
             if let Some(variant_idx) = variant_idx {
                 // don't forget filling the place with the discriminant of the enum
-                ecx.write_discriminant(variant_idx, &place.into()).unwrap();
+                ecx.write_discriminant(variant_idx, place).unwrap();
             }
 
             debug!("dump of place after writing discriminant:");
-            dump_place(ecx, place.into());
+            dump_place(ecx, place);
         }
         _ => bug!("shouldn't have created a ValTree for {:?}", ty),
     }
 }
 
-fn dump_place<'tcx>(ecx: &CompileTimeEvalContext<'tcx, 'tcx>, place: PlaceTy<'tcx>) {
-    trace!("{:?}", ecx.dump_place(*place));
+fn dump_place<'tcx>(ecx: &CompileTimeEvalContext<'tcx, 'tcx>, place: &MPlaceTy<'tcx>) {
+    trace!("{:?}", ecx.dump_place(Place::Ptr(**place)));
 }
diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs
index 61ce695ccd2..e1109e584b7 100644
--- a/compiler/rustc_const_eval/src/errors.rs
+++ b/compiler/rustc_const_eval/src/errors.rs
@@ -511,7 +511,8 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
             InvalidUninitBytes(Some(_)) => const_eval_invalid_uninit_bytes,
             DeadLocal => const_eval_dead_local,
             ScalarSizeMismatch(_) => const_eval_scalar_size_mismatch,
-            UninhabitedEnumVariantWritten => const_eval_uninhabited_enum_variant_written,
+            UninhabitedEnumVariantWritten(_) => const_eval_uninhabited_enum_variant_written,
+            UninhabitedEnumVariantRead(_) => const_eval_uninhabited_enum_variant_read,
             Validation(e) => e.diagnostic_message(),
             Custom(x) => (x.msg)(),
         }
@@ -535,7 +536,8 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
             | InvalidMeta(InvalidMetaKind::TooBig)
             | InvalidUninitBytes(None)
             | DeadLocal
-            | UninhabitedEnumVariantWritten => {}
+            | UninhabitedEnumVariantWritten(_)
+            | UninhabitedEnumVariantRead(_) => {}
             BoundsCheckFailed { len, index } => {
                 builder.set_arg("len", len);
                 builder.set_arg("index", index);
@@ -617,11 +619,13 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> {
             MutableRefInConst => const_eval_mutable_ref_in_const,
             NullFnPtr => const_eval_null_fn_ptr,
             NeverVal => const_eval_never_val,
+            NullablePtrOutOfRange { .. } => const_eval_nullable_ptr_out_of_range,
             PtrOutOfRange { .. } => const_eval_ptr_out_of_range,
             OutOfRange { .. } => const_eval_out_of_range,
             UnsafeCell => const_eval_unsafe_cell,
             UninhabitedVal { .. } => const_eval_uninhabited_val,
             InvalidEnumTag { .. } => const_eval_invalid_enum_tag,
+            UninhabitedEnumTag => const_eval_uninhabited_enum_tag,
             UninitEnumTag => const_eval_uninit_enum_tag,
             UninitStr => const_eval_uninit_str,
             Uninit { expected: ExpectedKind::Bool } => const_eval_uninit_bool,
@@ -731,7 +735,9 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> {
             | InvalidFnPtr { value } => {
                 err.set_arg("value", value);
             }
-            PtrOutOfRange { range, max_value } => add_range_arg(range, max_value, handler, err),
+            NullablePtrOutOfRange { range, max_value } | PtrOutOfRange { range, max_value } => {
+                add_range_arg(range, max_value, handler, err)
+            }
             OutOfRange { range, max_value, value } => {
                 err.set_arg("value", value);
                 add_range_arg(range, max_value, handler, err);
@@ -757,7 +763,8 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> {
             | InvalidMetaSliceTooLarge { .. }
             | InvalidMetaTooLarge { .. }
             | DanglingPtrUseAfterFree { .. }
-            | DanglingPtrOutOfBounds { .. } => {}
+            | DanglingPtrOutOfBounds { .. }
+            | UninhabitedEnumTag => {}
         }
     }
 }
@@ -832,7 +839,9 @@ impl<'tcx> ReportErrorExt for InvalidProgramInfo<'tcx> {
                 rustc_middle::error::middle_adjust_for_foreign_abi_error
             }
             InvalidProgramInfo::SizeOfUnsizedType(_) => const_eval_size_of_unsized,
-            InvalidProgramInfo::UninitUnsizedLocal => const_eval_uninit_unsized_local,
+            InvalidProgramInfo::ConstPropNonsense => {
+                panic!("We had const-prop nonsense, this should never be printed")
+            }
         }
     }
     fn add_args<G: EmissionGuarantee>(
@@ -843,7 +852,7 @@ impl<'tcx> ReportErrorExt for InvalidProgramInfo<'tcx> {
         match self {
             InvalidProgramInfo::TooGeneric
             | InvalidProgramInfo::AlreadyReported(_)
-            | InvalidProgramInfo::UninitUnsizedLocal => {}
+            | InvalidProgramInfo::ConstPropNonsense => {}
             InvalidProgramInfo::Layout(e) => {
                 let diag: DiagnosticBuilder<'_, ()> = e.into_diagnostic().into_diagnostic(handler);
                 for (name, val) in diag.args() {
diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs
index dd7a1fcc165..98e853dc4d9 100644
--- a/compiler/rustc_const_eval/src/interpret/cast.rs
+++ b/compiler/rustc_const_eval/src/interpret/cast.rs
@@ -56,7 +56,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             }
 
             CastKind::FnPtrToPtr | CastKind::PtrToPtr => {
-                let src = self.read_immediate(&src)?;
+                let src = self.read_immediate(src)?;
                 let res = self.ptr_to_ptr(&src, cast_ty)?;
                 self.write_immediate(res, dest)?;
             }
@@ -420,8 +420,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                     if cast_ty_field.is_zst() {
                         continue;
                     }
-                    let src_field = self.operand_field(src, i)?;
-                    let dst_field = self.place_field(dest, i)?;
+                    let src_field = self.project_field(src, i)?;
+                    let dst_field = self.project_field(dest, i)?;
                     if src_field.layout.ty == cast_ty_field.ty {
                         self.copy_op(&src_field, &dst_field, /*allow_transmute*/ false)?;
                     } else {
diff --git a/compiler/rustc_const_eval/src/interpret/discriminant.rs b/compiler/rustc_const_eval/src/interpret/discriminant.rs
index 99ea0ab18bc..6c35fb01a93 100644
--- a/compiler/rustc_const_eval/src/interpret/discriminant.rs
+++ b/compiler/rustc_const_eval/src/interpret/discriminant.rs
@@ -1,10 +1,11 @@
 //! Functions for reading and writing discriminants of multi-variant layouts (enums and generators).
 
-use rustc_middle::ty::layout::{LayoutOf, PrimitiveExt};
+use rustc_middle::ty::layout::{LayoutOf, PrimitiveExt, TyAndLayout};
 use rustc_middle::{mir, ty};
-use rustc_target::abi::{self, TagEncoding, VariantIdx, Variants, WrappingRange};
+use rustc_target::abi::{self, TagEncoding};
+use rustc_target::abi::{VariantIdx, Variants};
 
-use super::{ImmTy, InterpCx, InterpResult, Machine, OpTy, PlaceTy, Scalar};
+use super::{ImmTy, InterpCx, InterpResult, Machine, Readable, Scalar, Writeable};
 
 impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     /// Writes the discriminant of the given variant.
@@ -12,7 +13,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     pub fn write_discriminant(
         &mut self,
         variant_index: VariantIdx,
-        dest: &PlaceTy<'tcx, M::Provenance>,
+        dest: &impl Writeable<'tcx, M::Provenance>,
     ) -> InterpResult<'tcx> {
         // Layout computation excludes uninhabited variants from consideration
         // therefore there's no way to represent those variants in the given layout.
@@ -20,11 +21,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         // discriminant, so we cannot do anything here.
         // When evaluating we will always error before even getting here, but ConstProp 'executes'
         // dead code, so we cannot ICE here.
-        if dest.layout.for_variant(self, variant_index).abi.is_uninhabited() {
-            throw_ub!(UninhabitedEnumVariantWritten)
+        if dest.layout().for_variant(self, variant_index).abi.is_uninhabited() {
+            throw_ub!(UninhabitedEnumVariantWritten(variant_index))
         }
 
-        match dest.layout.variants {
+        match dest.layout().variants {
             abi::Variants::Single { index } => {
                 assert_eq!(index, variant_index);
             }
@@ -37,8 +38,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 // No need to validate that the discriminant here because the
                 // `TyAndLayout::for_variant()` call earlier already checks the variant is valid.
 
-                let discr_val =
-                    dest.layout.ty.discriminant_for_variant(*self.tcx, variant_index).unwrap().val;
+                let discr_val = dest
+                    .layout()
+                    .ty
+                    .discriminant_for_variant(*self.tcx, variant_index)
+                    .unwrap()
+                    .val;
 
                 // raw discriminants for enums are isize or bigger during
                 // their computation, but the in-memory tag is the smallest possible
@@ -46,7 +51,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 let size = tag_layout.size(self);
                 let tag_val = size.truncate(discr_val);
 
-                let tag_dest = self.place_field(dest, tag_field)?;
+                let tag_dest = self.project_field(dest, tag_field)?;
                 self.write_scalar(Scalar::from_uint(tag_val, size), &tag_dest)?;
             }
             abi::Variants::Multiple {
@@ -77,7 +82,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                         &niche_start_val,
                     )?;
                     // Write result.
-                    let niche_dest = self.place_field(dest, tag_field)?;
+                    let niche_dest = self.project_field(dest, tag_field)?;
                     self.write_immediate(*tag_val, &niche_dest)?;
                 }
             }
@@ -91,11 +96,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     #[instrument(skip(self), level = "trace")]
     pub fn read_discriminant(
         &self,
-        op: &OpTy<'tcx, M::Provenance>,
-    ) -> InterpResult<'tcx, (Scalar<M::Provenance>, VariantIdx)> {
-        trace!("read_discriminant_value {:#?}", op.layout);
+        op: &impl Readable<'tcx, M::Provenance>,
+    ) -> InterpResult<'tcx, VariantIdx> {
+        let ty = op.layout().ty;
+        trace!("read_discriminant_value {:#?}", op.layout());
         // Get type and layout of the discriminant.
-        let discr_layout = self.layout_of(op.layout.ty.discriminant_ty(*self.tcx))?;
+        let discr_layout = self.layout_of(ty.discriminant_ty(*self.tcx))?;
         trace!("discriminant type: {:?}", discr_layout.ty);
 
         // We use "discriminant" to refer to the value associated with a particular enum variant.
@@ -103,21 +109,23 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         // declared list of variants -- they can differ with explicitly assigned discriminants.
         // We use "tag" to refer to how the discriminant is encoded in memory, which can be either
         // straight-forward (`TagEncoding::Direct`) or with a niche (`TagEncoding::Niche`).
-        let (tag_scalar_layout, tag_encoding, tag_field) = match op.layout.variants {
+        let (tag_scalar_layout, tag_encoding, tag_field) = match op.layout().variants {
             Variants::Single { index } => {
-                let discr = match op.layout.ty.discriminant_for_variant(*self.tcx, index) {
-                    Some(discr) => {
-                        // This type actually has discriminants.
-                        assert_eq!(discr.ty, discr_layout.ty);
-                        Scalar::from_uint(discr.val, discr_layout.size)
+                // Do some extra checks on enums.
+                if ty.is_enum() {
+                    // Hilariously, `Single` is used even for 0-variant enums.
+                    // (See https://github.com/rust-lang/rust/issues/89765).
+                    if matches!(ty.kind(), ty::Adt(def, ..) if def.variants().is_empty()) {
+                        throw_ub!(UninhabitedEnumVariantRead(index))
                     }
-                    None => {
-                        // On a type without actual discriminants, variant is 0.
-                        assert_eq!(index.as_u32(), 0);
-                        Scalar::from_uint(index.as_u32(), discr_layout.size)
+                    // For consisteny with `write_discriminant`, and to make sure that
+                    // `project_downcast` cannot fail due to strange layouts, we declare immediate UB
+                    // for uninhabited variants.
+                    if op.layout().for_variant(self, index).abi.is_uninhabited() {
+                        throw_ub!(UninhabitedEnumVariantRead(index))
                     }
-                };
-                return Ok((discr, index));
+                }
+                return Ok(index);
             }
             Variants::Multiple { tag, ref tag_encoding, tag_field, .. } => {
                 (tag, tag_encoding, tag_field)
@@ -137,13 +145,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         let tag_layout = self.layout_of(tag_scalar_layout.primitive().to_int_ty(*self.tcx))?;
 
         // Read tag and sanity-check `tag_layout`.
-        let tag_val = self.read_immediate(&self.operand_field(op, tag_field)?)?;
+        let tag_val = self.read_immediate(&self.project_field(op, tag_field)?)?;
         assert_eq!(tag_layout.size, tag_val.layout.size);
         assert_eq!(tag_layout.abi.is_signed(), tag_val.layout.abi.is_signed());
         trace!("tag value: {}", tag_val);
 
         // Figure out which discriminant and variant this corresponds to.
-        Ok(match *tag_encoding {
+        let index = match *tag_encoding {
             TagEncoding::Direct => {
                 let scalar = tag_val.to_scalar();
                 // Generate a specific error if `tag_val` is not an integer.
@@ -159,7 +167,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                     self.cast_from_int_like(scalar, tag_val.layout, discr_layout.ty).unwrap();
                 let discr_bits = discr_val.assert_bits(discr_layout.size);
                 // Convert discriminant to variant index, and catch invalid discriminants.
-                let index = match *op.layout.ty.kind() {
+                let index = match *ty.kind() {
                     ty::Adt(adt, _) => {
                         adt.discriminants(*self.tcx).find(|(_, var)| var.val == discr_bits)
                     }
@@ -171,7 +179,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 }
                 .ok_or_else(|| err_ub!(InvalidTag(Scalar::from_uint(tag_bits, tag_layout.size))))?;
                 // Return the cast value, and the index.
-                (discr_val, index.0)
+                index.0
             }
             TagEncoding::Niche { untagged_variant, ref niche_variants, niche_start } => {
                 let tag_val = tag_val.to_scalar();
@@ -179,24 +187,19 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 // discriminant (encoded in niche/tag) and variant index are the same.
                 let variants_start = niche_variants.start().as_u32();
                 let variants_end = niche_variants.end().as_u32();
-                let variants_len = u128::from(variants_end - variants_start);
                 let variant = match tag_val.try_to_int() {
                     Err(dbg_val) => {
                         // So this is a pointer then, and casting to an int failed.
                         // Can only happen during CTFE.
-                        // The pointer and niches ranges must be disjoint, then we know
-                        // this is the untagged variant (as the value is not in the niche).
-                        // Everything else, we conservatively reject.
-                        let range = self.ptr_scalar_range(tag_val)?;
-                        let niches = WrappingRange {
-                            start: niche_start,
-                            end: niche_start.wrapping_add(variants_len),
-                        };
-                        if niches.overlaps_range(range) {
+                        // The niche must be just 0, and the ptr not null, then we know this is
+                        // okay. Everything else, we conservatively reject.
+                        let ptr_valid = niche_start == 0
+                            && variants_start == variants_end
+                            && !self.scalar_may_be_null(tag_val)?;
+                        if !ptr_valid {
                             throw_ub!(InvalidTag(dbg_val))
-                        } else {
-                            untagged_variant
                         }
+                        untagged_variant
                     }
                     Ok(tag_bits) => {
                         let tag_bits = tag_bits.assert_bits(tag_layout.size);
@@ -209,7 +212,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                         let variant_index_relative =
                             variant_index_relative_val.to_scalar().assert_bits(tag_val.layout.size);
                         // Check if this is in the range that indicates an actual discriminant.
-                        if variant_index_relative <= variants_len {
+                        if variant_index_relative <= u128::from(variants_end - variants_start) {
                             let variant_index_relative = u32::try_from(variant_index_relative)
                                 .expect("we checked that this fits into a u32");
                             // Then computing the absolute variant idx should not overflow any more.
@@ -218,12 +221,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                                     .checked_add(variant_index_relative)
                                     .expect("overflow computing absolute variant idx"),
                             );
-                            let variants = op
-                                .layout
-                                .ty
-                                .ty_adt_def()
-                                .expect("tagged layout for non adt")
-                                .variants();
+                            let variants =
+                                ty.ty_adt_def().expect("tagged layout for non adt").variants();
                             assert!(variant_index < variants.next_index());
                             variant_index
                         } else {
@@ -234,7 +233,32 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 // Compute the size of the scalar we need to return.
                 // No need to cast, because the variant index directly serves as discriminant and is
                 // encoded in the tag.
-                (Scalar::from_uint(variant.as_u32(), discr_layout.size), variant)
+                variant
+            }
+        };
+        // For consisteny with `write_discriminant`, and to make sure that `project_downcast` cannot fail due to strange layouts, we declare immediate UB for uninhabited variants.
+        if op.layout().for_variant(self, index).abi.is_uninhabited() {
+            throw_ub!(UninhabitedEnumVariantRead(index))
+        }
+        Ok(index)
+    }
+
+    pub fn discriminant_for_variant(
+        &self,
+        layout: TyAndLayout<'tcx>,
+        variant: VariantIdx,
+    ) -> InterpResult<'tcx, Scalar<M::Provenance>> {
+        let discr_layout = self.layout_of(layout.ty.discriminant_ty(*self.tcx))?;
+        Ok(match layout.ty.discriminant_for_variant(*self.tcx, variant) {
+            Some(discr) => {
+                // This type actually has discriminants.
+                assert_eq!(discr.ty, discr_layout.ty);
+                Scalar::from_uint(discr.val, discr_layout.size)
+            }
+            None => {
+                // On a type without actual discriminants, variant is 0.
+                assert_eq!(variant.as_u32(), 0);
+                Scalar::from_uint(variant.as_u32(), discr_layout.size)
             }
         })
     }
diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs
index 04e046fbda3..3ac6f07e8b7 100644
--- a/compiler/rustc_const_eval/src/interpret/eval_context.rs
+++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs
@@ -958,7 +958,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         } else {
             self.param_env
         };
-        let param_env = param_env.with_const();
         let val = self.ctfe_query(span, |tcx| tcx.eval_to_allocation_raw(param_env.and(gid)))?;
         self.raw_const_to_mplace(val)
     }
@@ -1014,9 +1013,12 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> std::fmt::Debug
 {
     fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         match self.place {
-            Place::Local { frame, local } => {
+            Place::Local { frame, local, offset } => {
                 let mut allocs = Vec::new();
-                write!(fmt, "{:?}", local)?;
+                write!(fmt, "{local:?}")?;
+                if let Some(offset) = offset {
+                    write!(fmt, "+{:#x}", offset.bytes())?;
+                }
                 if frame != self.ecx.frame_idx() {
                     write!(fmt, " ({} frames up)", self.ecx.frame_idx() - frame)?;
                 }
@@ -1032,7 +1034,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> std::fmt::Debug
                             fmt,
                             " by {} ref {:?}:",
                             match mplace.meta {
-                                MemPlaceMeta::Meta(meta) => format!(" meta({:?})", meta),
+                                MemPlaceMeta::Meta(meta) => format!(" meta({meta:?})"),
                                 MemPlaceMeta::None => String::new(),
                             },
                             mplace.ptr,
@@ -1040,13 +1042,13 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> std::fmt::Debug
                         allocs.extend(mplace.ptr.provenance.map(Provenance::get_alloc_id));
                     }
                     LocalValue::Live(Operand::Immediate(Immediate::Scalar(val))) => {
-                        write!(fmt, " {:?}", val)?;
+                        write!(fmt, " {val:?}")?;
                         if let Scalar::Ptr(ptr, _size) = val {
                             allocs.push(ptr.provenance.get_alloc_id());
                         }
                     }
                     LocalValue::Live(Operand::Immediate(Immediate::ScalarPair(val1, val2))) => {
-                        write!(fmt, " ({:?}, {:?})", val1, val2)?;
+                        write!(fmt, " ({val1:?}, {val2:?})")?;
                         if let Scalar::Ptr(ptr, _size) = val1 {
                             allocs.push(ptr.provenance.get_alloc_id());
                         }
@@ -1062,7 +1064,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> std::fmt::Debug
                 Some(alloc_id) => {
                     write!(fmt, "by ref {:?}: {:?}", mplace.ptr, self.ecx.dump_alloc(alloc_id))
                 }
-                ptr => write!(fmt, " integral by ref: {:?}", ptr),
+                ptr => write!(fmt, " integral by ref: {ptr:?}"),
             },
         }
     }
diff --git a/compiler/rustc_const_eval/src/interpret/intern.rs b/compiler/rustc_const_eval/src/interpret/intern.rs
index 107e5bec614..910c3ca5d0a 100644
--- a/compiler/rustc_const_eval/src/interpret/intern.rs
+++ b/compiler/rustc_const_eval/src/interpret/intern.rs
@@ -164,82 +164,13 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx, const_eval::Memory
         &self.ecx
     }
 
-    fn visit_aggregate(
-        &mut self,
-        mplace: &MPlaceTy<'tcx>,
-        fields: impl Iterator<Item = InterpResult<'tcx, Self::V>>,
-    ) -> InterpResult<'tcx> {
-        // We want to walk the aggregate to look for references to intern. While doing that we
-        // also need to take special care of interior mutability.
-        //
-        // As an optimization, however, if the allocation does not contain any references: we don't
-        // need to do the walk. It can be costly for big arrays for example (e.g. issue #93215).
-        let is_walk_needed = |mplace: &MPlaceTy<'tcx>| -> InterpResult<'tcx, bool> {
-            // ZSTs cannot contain pointers, we can avoid the interning walk.
-            if mplace.layout.is_zst() {
-                return Ok(false);
-            }
-
-            // Now, check whether this allocation could contain references.
-            //
-            // Note, this check may sometimes not be cheap, so we only do it when the walk we'd like
-            // to avoid could be expensive: on the potentially larger types, arrays and slices,
-            // rather than on all aggregates unconditionally.
-            if matches!(mplace.layout.ty.kind(), ty::Array(..) | ty::Slice(..)) {
-                let Some((size, align)) = self.ecx.size_and_align_of_mplace(&mplace)? else {
-                    // We do the walk if we can't determine the size of the mplace: we may be
-                    // dealing with extern types here in the future.
-                    return Ok(true);
-                };
-
-                // If there is no provenance in this allocation, it does not contain references
-                // that point to another allocation, and we can avoid the interning walk.
-                if let Some(alloc) = self.ecx.get_ptr_alloc(mplace.ptr, size, align)? {
-                    if !alloc.has_provenance() {
-                        return Ok(false);
-                    }
-                } else {
-                    // We're encountering a ZST here, and can avoid the walk as well.
-                    return Ok(false);
-                }
-            }
-
-            // In the general case, we do the walk.
-            Ok(true)
-        };
-
-        // If this allocation contains no references to intern, we avoid the potentially costly
-        // walk.
-        //
-        // We can do this before the checks for interior mutability below, because only references
-        // are relevant in that situation, and we're checking if there are any here.
-        if !is_walk_needed(mplace)? {
-            return Ok(());
-        }
-
-        if let Some(def) = mplace.layout.ty.ty_adt_def() {
-            if def.is_unsafe_cell() {
-                // We are crossing over an `UnsafeCell`, we can mutate again. This means that
-                // References we encounter inside here are interned as pointing to mutable
-                // allocations.
-                // Remember the `old` value to handle nested `UnsafeCell`.
-                let old = std::mem::replace(&mut self.inside_unsafe_cell, true);
-                let walked = self.walk_aggregate(mplace, fields);
-                self.inside_unsafe_cell = old;
-                return walked;
-            }
-        }
-
-        self.walk_aggregate(mplace, fields)
-    }
-
     fn visit_value(&mut self, mplace: &MPlaceTy<'tcx>) -> InterpResult<'tcx> {
         // Handle Reference types, as these are the only types with provenance supported by const eval.
         // Raw pointers (and boxes) are handled by the `leftover_allocations` logic.
         let tcx = self.ecx.tcx;
         let ty = mplace.layout.ty;
         if let ty::Ref(_, referenced_ty, ref_mutability) = *ty.kind() {
-            let value = self.ecx.read_immediate(&mplace.into())?;
+            let value = self.ecx.read_immediate(mplace)?;
             let mplace = self.ecx.ref_to_mplace(&value)?;
             assert_eq!(mplace.layout.ty, referenced_ty);
             // Handle trait object vtables.
@@ -315,7 +246,63 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx, const_eval::Memory
             }
             Ok(())
         } else {
-            // Not a reference -- proceed recursively.
+            // Not a reference. Check if we want to recurse.
+            let is_walk_needed = |mplace: &MPlaceTy<'tcx>| -> InterpResult<'tcx, bool> {
+                // ZSTs cannot contain pointers, we can avoid the interning walk.
+                if mplace.layout.is_zst() {
+                    return Ok(false);
+                }
+
+                // Now, check whether this allocation could contain references.
+                //
+                // Note, this check may sometimes not be cheap, so we only do it when the walk we'd like
+                // to avoid could be expensive: on the potentially larger types, arrays and slices,
+                // rather than on all aggregates unconditionally.
+                if matches!(mplace.layout.ty.kind(), ty::Array(..) | ty::Slice(..)) {
+                    let Some((size, align)) = self.ecx.size_and_align_of_mplace(&mplace)? else {
+                        // We do the walk if we can't determine the size of the mplace: we may be
+                        // dealing with extern types here in the future.
+                        return Ok(true);
+                    };
+
+                    // If there is no provenance in this allocation, it does not contain references
+                    // that point to another allocation, and we can avoid the interning walk.
+                    if let Some(alloc) = self.ecx.get_ptr_alloc(mplace.ptr, size, align)? {
+                        if !alloc.has_provenance() {
+                            return Ok(false);
+                        }
+                    } else {
+                        // We're encountering a ZST here, and can avoid the walk as well.
+                        return Ok(false);
+                    }
+                }
+
+                // In the general case, we do the walk.
+                Ok(true)
+            };
+
+            // If this allocation contains no references to intern, we avoid the potentially costly
+            // walk.
+            //
+            // We can do this before the checks for interior mutability below, because only references
+            // are relevant in that situation, and we're checking if there are any here.
+            if !is_walk_needed(mplace)? {
+                return Ok(());
+            }
+
+            if let Some(def) = mplace.layout.ty.ty_adt_def() {
+                if def.is_unsafe_cell() {
+                    // We are crossing over an `UnsafeCell`, we can mutate again. This means that
+                    // References we encounter inside here are interned as pointing to mutable
+                    // allocations.
+                    // Remember the `old` value to handle nested `UnsafeCell`.
+                    let old = std::mem::replace(&mut self.inside_unsafe_cell, true);
+                    let walked = self.walk_value(mplace);
+                    self.inside_unsafe_cell = old;
+                    return walked;
+                }
+            }
+
             self.walk_value(mplace)
         }
     }
@@ -371,7 +358,7 @@ pub fn intern_const_alloc_recursive<
         Some(ret.layout.ty),
     );
 
-    ref_tracking.track((*ret, base_intern_mode), || ());
+    ref_tracking.track((ret.clone(), base_intern_mode), || ());
 
     while let Some(((mplace, mode), _)) = ref_tracking.todo.pop() {
         let res = InternVisitor {
@@ -477,7 +464,7 @@ impl<'mir, 'tcx: 'mir, M: super::intern::CompileTimeMachine<'mir, 'tcx, !>>
         ) -> InterpResult<'tcx, ()>,
     ) -> InterpResult<'tcx, ConstAllocation<'tcx>> {
         let dest = self.allocate(layout, MemoryKind::Stack)?;
-        f(self, &dest.into())?;
+        f(self, &dest.clone().into())?;
         let mut alloc = self.memory.alloc_map.remove(&dest.ptr.provenance.unwrap()).unwrap().1;
         alloc.mutability = Mutability::Not;
         Ok(self.tcx.mk_const_alloc(alloc))
diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
index 8ec9a71bf3a..be7c14f33c2 100644
--- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs
+++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
@@ -5,7 +5,9 @@
 use rustc_hir::def_id::DefId;
 use rustc_middle::mir::{
     self,
-    interpret::{Allocation, ConstAllocation, ConstValue, GlobalId, InterpResult, Scalar},
+    interpret::{
+        Allocation, ConstAllocation, ConstValue, GlobalId, InterpResult, PointerArithmetic, Scalar,
+    },
     BinOp, NonDivergingIntrinsic,
 };
 use rustc_middle::ty;
@@ -13,7 +15,7 @@ use rustc_middle::ty::layout::{LayoutOf as _, ValidityRequirement};
 use rustc_middle::ty::GenericArgsRef;
 use rustc_middle::ty::{Ty, TyCtxt};
 use rustc_span::symbol::{sym, Symbol};
-use rustc_target::abi::{Abi, Align, HasDataLayout as _, Primitive, Size};
+use rustc_target::abi::{Abi, Align, Primitive, Size};
 
 use super::{
     util::ensure_monomorphic_enough, CheckInAllocMsg, ImmTy, InterpCx, Machine, OpTy, PlaceTy,
@@ -224,8 +226,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             }
             sym::discriminant_value => {
                 let place = self.deref_operand(&args[0])?;
-                let discr_val = self.read_discriminant(&place.into())?.0;
-                self.write_scalar(discr_val, dest)?;
+                let variant = self.read_discriminant(&place)?;
+                let discr = self.discriminant_for_variant(place.layout, variant)?;
+                self.write_scalar(discr, dest)?;
             }
             sym::exact_div => {
                 let l = self.read_immediate(&args[0])?;
@@ -359,12 +362,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 )?;
 
                 // Perform division by size to compute return value.
-                let dl = self.data_layout();
                 let ret_layout = if intrinsic_name == sym::ptr_offset_from_unsigned {
-                    assert!(0 <= dist && dist <= dl.target_isize_max());
+                    assert!(0 <= dist && dist <= self.target_isize_max());
                     usize_layout
                 } else {
-                    assert!(dl.target_isize_min() <= dist && dist <= dl.target_isize_max());
+                    assert!(self.target_isize_min() <= dist && dist <= self.target_isize_max());
                     isize_layout
                 };
                 let pointee_layout = self.layout_of(instance_args.type_at(0))?;
@@ -392,17 +394,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                         // For *all* intrinsics we first check `is_uninhabited` to give a more specific
                         // error message.
                         _ if layout.abi.is_uninhabited() => format!(
-                            "aborted execution: attempted to instantiate uninhabited type `{}`",
-                            ty
+                            "aborted execution: attempted to instantiate uninhabited type `{ty}`"
                         ),
                         ValidityRequirement::Inhabited => bug!("handled earlier"),
                         ValidityRequirement::Zero => format!(
-                            "aborted execution: attempted to zero-initialize type `{}`, which is invalid",
-                            ty
+                            "aborted execution: attempted to zero-initialize type `{ty}`, which is invalid"
                         ),
                         ValidityRequirement::UninitMitigated0x01Fill => format!(
-                            "aborted execution: attempted to leave type `{}` uninitialized, which is invalid",
-                            ty
+                            "aborted execution: attempted to leave type `{ty}` uninitialized, which is invalid"
                         ),
                         ValidityRequirement::Uninit => bug!("assert_uninit_valid doesn't exist"),
                     };
@@ -418,19 +417,17 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 assert_eq!(input_len, dest_len, "Return vector length must match input length");
                 assert!(
                     index < dest_len,
-                    "Index `{}` must be in bounds of vector with length {}",
-                    index,
-                    dest_len
+                    "Index `{index}` must be in bounds of vector with length {dest_len}"
                 );
 
                 for i in 0..dest_len {
-                    let place = self.mplace_index(&dest, i)?;
+                    let place = self.project_index(&dest, i)?;
                     let value = if i == index {
                         elem.clone()
                     } else {
-                        self.mplace_index(&input, i)?.into()
+                        self.project_index(&input, i)?.into()
                     };
-                    self.copy_op(&value, &place.into(), /*allow_transmute*/ false)?;
+                    self.copy_op(&value, &place, /*allow_transmute*/ false)?;
                 }
             }
             sym::simd_extract => {
@@ -438,12 +435,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 let (input, input_len) = self.operand_to_simd(&args[0])?;
                 assert!(
                     index < input_len,
-                    "index `{}` must be in bounds of vector with length {}",
-                    index,
-                    input_len
+                    "index `{index}` must be in bounds of vector with length {input_len}"
                 );
                 self.copy_op(
-                    &self.mplace_index(&input, index)?.into(),
+                    &self.project_index(&input, index)?,
                     dest,
                     /*allow_transmute*/ false,
                 )?;
@@ -608,7 +603,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         count: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::Provenance>,
         nonoverlapping: bool,
     ) -> InterpResult<'tcx> {
-        let count = self.read_target_usize(&count)?;
+        let count = self.read_target_usize(count)?;
         let layout = self.layout_of(src.layout.ty.builtin_deref(true).unwrap().ty)?;
         let (size, align) = (layout.size, layout.align.abi);
         // `checked_mul` enforces a too small bound (the correct one would probably be target_isize_max),
@@ -620,8 +615,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             )
         })?;
 
-        let src = self.read_pointer(&src)?;
-        let dst = self.read_pointer(&dst)?;
+        let src = self.read_pointer(src)?;
+        let dst = self.read_pointer(dst)?;
 
         self.mem_copy(src, align, dst, align, size, nonoverlapping)
     }
@@ -634,9 +629,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     ) -> InterpResult<'tcx> {
         let layout = self.layout_of(dst.layout.ty.builtin_deref(true).unwrap().ty)?;
 
-        let dst = self.read_pointer(&dst)?;
-        let byte = self.read_scalar(&byte)?.to_u8()?;
-        let count = self.read_target_usize(&count)?;
+        let dst = self.read_pointer(dst)?;
+        let byte = self.read_scalar(byte)?.to_u8()?;
+        let count = self.read_target_usize(count)?;
 
         // `checked_mul` enforces a too small bound (the correct one would probably be target_isize_max),
         // but no actual allocation can be big enough for the difference to be noticeable.
diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics/caller_location.rs b/compiler/rustc_const_eval/src/interpret/intrinsics/caller_location.rs
index c4fe293bfac..948bec7464a 100644
--- a/compiler/rustc_const_eval/src/interpret/intrinsics/caller_location.rs
+++ b/compiler/rustc_const_eval/src/interpret/intrinsics/caller_location.rs
@@ -101,11 +101,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         let location = self.allocate(loc_layout, MemoryKind::CallerLocation).unwrap();
 
         // Initialize fields.
-        self.write_immediate(file.to_ref(self), &self.mplace_field(&location, 0).unwrap().into())
+        self.write_immediate(file.to_ref(self), &self.project_field(&location, 0).unwrap())
             .expect("writing to memory we just allocated cannot fail");
-        self.write_scalar(line, &self.mplace_field(&location, 1).unwrap().into())
+        self.write_scalar(line, &self.project_field(&location, 1).unwrap())
             .expect("writing to memory we just allocated cannot fail");
-        self.write_scalar(col, &self.mplace_field(&location, 2).unwrap().into())
+        self.write_scalar(col, &self.project_field(&location, 2).unwrap())
             .expect("writing to memory we just allocated cannot fail");
 
         location
diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs
index 29fc5ffcfe7..02d022a2252 100644
--- a/compiler/rustc_const_eval/src/interpret/memory.rs
+++ b/compiler/rustc_const_eval/src/interpret/memory.rs
@@ -10,7 +10,6 @@ use std::assert_matches::assert_matches;
 use std::borrow::Cow;
 use std::collections::VecDeque;
 use std::fmt;
-use std::ops::RangeInclusive;
 use std::ptr;
 
 use rustc_ast::Mutability;
@@ -54,7 +53,7 @@ impl<T: fmt::Display> fmt::Display for MemoryKind<T> {
         match self {
             MemoryKind::Stack => write!(f, "stack variable"),
             MemoryKind::CallerLocation => write!(f, "caller location"),
-            MemoryKind::Machine(m) => write!(f, "{}", m),
+            MemoryKind::Machine(m) => write!(f, "{m}"),
         }
     }
 }
@@ -908,7 +907,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> std::fmt::Debug for DumpAllocs<'a,
             match self.ecx.memory.alloc_map.get(id) {
                 Some((kind, alloc)) => {
                     // normal alloc
-                    write!(fmt, " ({}, ", kind)?;
+                    write!(fmt, " ({kind}, ")?;
                     write_allocation_track_relocs(
                         &mut *fmt,
                         *self.ecx.tcx,
@@ -1223,34 +1222,24 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
 
 /// Machine pointer introspection.
 impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
-    /// Turn a pointer-sized scalar into a (non-empty) range of possible values.
+    /// Test if this value might be null.
     /// If the machine does not support ptr-to-int casts, this is conservative.
-    pub fn ptr_scalar_range(
-        &self,
-        scalar: Scalar<M::Provenance>,
-    ) -> InterpResult<'tcx, RangeInclusive<u64>> {
-        if let Ok(int) = scalar.to_target_usize(self) {
-            return Ok(int..=int);
-        }
-
-        let ptr = scalar.to_pointer(self)?;
-
-        // Can only happen during CTFE.
-        Ok(match self.ptr_try_get_alloc_id(ptr) {
-            Ok((alloc_id, offset, _)) => {
-                let offset = offset.bytes();
-                let (size, align, _) = self.get_alloc_info(alloc_id);
-                let dl = self.data_layout();
-                if offset > size.bytes() {
-                    // If the pointer is out-of-bounds, we do not have a
-                    // meaningful range to return.
-                    0..=dl.target_usize_max()
-                } else {
-                    let (min, max) = dl.address_range_for(size, align);
-                    (min + offset)..=(max + offset)
+    pub fn scalar_may_be_null(&self, scalar: Scalar<M::Provenance>) -> InterpResult<'tcx, bool> {
+        Ok(match scalar.try_to_int() {
+            Ok(int) => int.is_null(),
+            Err(_) => {
+                // Can only happen during CTFE.
+                let ptr = scalar.to_pointer(self)?;
+                match self.ptr_try_get_alloc_id(ptr) {
+                    Ok((alloc_id, offset, _)) => {
+                        let (size, _align, _kind) = self.get_alloc_info(alloc_id);
+                        // If the pointer is out-of-bounds, it may be null.
+                        // Note that one-past-the-end (offset == size) is still inbounds, and never null.
+                        offset > size
+                    }
+                    Err(_offset) => bug!("a non-int scalar is always a pointer"),
                 }
             }
-            Err(_offset) => bug!("a non-int scalar is always a pointer"),
         })
     }
 
diff --git a/compiler/rustc_const_eval/src/interpret/mod.rs b/compiler/rustc_const_eval/src/interpret/mod.rs
index f657f954f9c..b0b553c45d4 100644
--- a/compiler/rustc_const_eval/src/interpret/mod.rs
+++ b/compiler/rustc_const_eval/src/interpret/mod.rs
@@ -24,11 +24,12 @@ pub use self::eval_context::{Frame, FrameInfo, InterpCx, LocalState, LocalValue,
 pub use self::intern::{intern_const_alloc_recursive, InternKind};
 pub use self::machine::{compile_time_machine, AllocMap, Machine, MayLeak, StackPopJump};
 pub use self::memory::{AllocKind, AllocRef, AllocRefMut, FnVal, Memory, MemoryKind};
-pub use self::operand::{ImmTy, Immediate, OpTy, Operand};
-pub use self::place::{MPlaceTy, MemPlace, MemPlaceMeta, Place, PlaceTy};
+pub use self::operand::{ImmTy, Immediate, OpTy, Operand, Readable};
+pub use self::place::{MPlaceTy, MemPlace, MemPlaceMeta, Place, PlaceTy, Writeable};
+pub use self::projection::Projectable;
 pub use self::terminator::FnArg;
 pub use self::validity::{CtfeValidationMode, RefTracking};
-pub use self::visitor::{MutValueVisitor, Value, ValueVisitor};
+pub use self::visitor::ValueVisitor;
 
 pub(crate) use self::intrinsics::eval_nullary_intrinsic;
 use eval_context::{from_known_layout, mir_assign_valid_types};
diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs
index da45fdca1a1..6e57a56b445 100644
--- a/compiler/rustc_const_eval/src/interpret/operand.rs
+++ b/compiler/rustc_const_eval/src/interpret/operand.rs
@@ -1,6 +1,8 @@
 //! Functions concerning immediate values and operands, and reading from operands.
 //! All high-level functions to read from memory work on operands as sources.
 
+use std::assert_matches::assert_matches;
+
 use either::{Either, Left, Right};
 
 use rustc_hir::def::Namespace;
@@ -13,8 +15,8 @@ use rustc_target::abi::{self, Abi, Align, HasDataLayout, Size};
 
 use super::{
     alloc_range, from_known_layout, mir_assign_valid_types, AllocId, ConstValue, Frame, GlobalId,
-    InterpCx, InterpResult, MPlaceTy, Machine, MemPlace, MemPlaceMeta, Place, PlaceTy, Pointer,
-    Provenance, Scalar,
+    InterpCx, InterpResult, MPlaceTy, Machine, MemPlace, MemPlaceMeta, PlaceTy, Pointer,
+    Projectable, Provenance, Scalar,
 };
 
 /// An `Immediate` represents a single immediate self-contained Rust value.
@@ -31,7 +33,7 @@ pub enum Immediate<Prov: Provenance = AllocId> {
     /// A pair of two scalar value (must have `ScalarPair` ABI where both fields are
     /// `Scalar::Initialized`).
     ScalarPair(Scalar<Prov>, Scalar<Prov>),
-    /// A value of fully uninitialized memory. Can have and size and layout.
+    /// A value of fully uninitialized memory. Can have arbitrary size and layout.
     Uninit,
 }
 
@@ -178,20 +180,6 @@ impl<'tcx, Prov: Provenance> From<MPlaceTy<'tcx, Prov>> for OpTy<'tcx, Prov> {
     }
 }
 
-impl<'tcx, Prov: Provenance> From<&'_ MPlaceTy<'tcx, Prov>> for OpTy<'tcx, Prov> {
-    #[inline(always)]
-    fn from(mplace: &MPlaceTy<'tcx, Prov>) -> Self {
-        OpTy { op: Operand::Indirect(**mplace), layout: mplace.layout, align: Some(mplace.align) }
-    }
-}
-
-impl<'tcx, Prov: Provenance> From<&'_ mut MPlaceTy<'tcx, Prov>> for OpTy<'tcx, Prov> {
-    #[inline(always)]
-    fn from(mplace: &mut MPlaceTy<'tcx, Prov>) -> Self {
-        OpTy { op: Operand::Indirect(**mplace), layout: mplace.layout, align: Some(mplace.align) }
-    }
-}
-
 impl<'tcx, Prov: Provenance> From<ImmTy<'tcx, Prov>> for OpTy<'tcx, Prov> {
     #[inline(always)]
     fn from(val: ImmTy<'tcx, Prov>) -> Self {
@@ -240,43 +228,126 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> {
         let int = self.to_scalar().assert_int();
         ConstInt::new(int, self.layout.ty.is_signed(), self.layout.ty.is_ptr_sized_integral())
     }
+
+    /// Compute the "sub-immediate" that is located within the `base` at the given offset with the
+    /// given layout.
+    // Not called `offset` to avoid confusion with the trait method.
+    fn offset_(&self, offset: Size, layout: TyAndLayout<'tcx>, cx: &impl HasDataLayout) -> Self {
+        // This makes several assumptions about what layouts we will encounter; we match what
+        // codegen does as good as we can (see `extract_field` in `rustc_codegen_ssa/src/mir/operand.rs`).
+        let inner_val: Immediate<_> = match (**self, self.layout.abi) {
+            // if the entire value is uninit, then so is the field (can happen in ConstProp)
+            (Immediate::Uninit, _) => Immediate::Uninit,
+            // the field contains no information, can be left uninit
+            _ if layout.is_zst() => Immediate::Uninit,
+            // some fieldless enum variants can have non-zero size but still `Aggregate` ABI... try
+            // to detect those here and also give them no data
+            _ if matches!(layout.abi, Abi::Aggregate { .. })
+                && matches!(&layout.fields, abi::FieldsShape::Arbitrary { offsets, .. } if offsets.len() == 0) =>
+            {
+                Immediate::Uninit
+            }
+            // the field covers the entire type
+            _ if layout.size == self.layout.size => {
+                assert_eq!(offset.bytes(), 0);
+                assert!(
+                    match (self.layout.abi, layout.abi) {
+                        (Abi::Scalar(..), Abi::Scalar(..)) => true,
+                        (Abi::ScalarPair(..), Abi::ScalarPair(..)) => true,
+                        _ => false,
+                    },
+                    "cannot project into {} immediate with equally-sized field {}\nouter ABI: {:#?}\nfield ABI: {:#?}",
+                    self.layout.ty,
+                    layout.ty,
+                    self.layout.abi,
+                    layout.abi,
+                );
+                **self
+            }
+            // extract fields from types with `ScalarPair` ABI
+            (Immediate::ScalarPair(a_val, b_val), Abi::ScalarPair(a, b)) => {
+                assert!(matches!(layout.abi, Abi::Scalar(..)));
+                Immediate::from(if offset.bytes() == 0 {
+                    debug_assert_eq!(layout.size, a.size(cx));
+                    a_val
+                } else {
+                    debug_assert_eq!(offset, a.size(cx).align_to(b.align(cx).abi));
+                    debug_assert_eq!(layout.size, b.size(cx));
+                    b_val
+                })
+            }
+            // everything else is a bug
+            _ => bug!("invalid field access on immediate {}, layout {:#?}", self, self.layout),
+        };
+
+        ImmTy::from_immediate(inner_val, layout)
+    }
+}
+
+impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for ImmTy<'tcx, Prov> {
+    #[inline(always)]
+    fn layout(&self) -> TyAndLayout<'tcx> {
+        self.layout
+    }
+
+    fn meta<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
+        &self,
+        _ecx: &InterpCx<'mir, 'tcx, M>,
+    ) -> InterpResult<'tcx, MemPlaceMeta<M::Provenance>> {
+        assert!(self.layout.is_sized()); // unsized ImmTy can only exist temporarily and should never reach this here
+        Ok(MemPlaceMeta::None)
+    }
+
+    fn offset_with_meta(
+        &self,
+        offset: Size,
+        meta: MemPlaceMeta<Prov>,
+        layout: TyAndLayout<'tcx>,
+        cx: &impl HasDataLayout,
+    ) -> InterpResult<'tcx, Self> {
+        assert_matches!(meta, MemPlaceMeta::None); // we can't store this anywhere anyway
+        Ok(self.offset_(offset, layout, cx))
+    }
+
+    fn to_op<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
+        &self,
+        _ecx: &InterpCx<'mir, 'tcx, M>,
+    ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
+        Ok(self.clone().into())
+    }
 }
 
 impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> {
-    pub fn len(&self, cx: &impl HasDataLayout) -> InterpResult<'tcx, u64> {
-        if self.layout.is_unsized() {
-            if matches!(self.op, Operand::Immediate(Immediate::Uninit)) {
-                // Uninit unsized places shouldn't occur. In the interpreter we have them
-                // temporarily for unsized arguments before their value is put in; in ConstProp they
-                // remain uninit and this code can actually be reached.
-                throw_inval!(UninitUnsizedLocal);
+    // Provided as inherent method since it doesn't need the `ecx` of `Projectable::meta`.
+    pub fn meta(&self) -> InterpResult<'tcx, MemPlaceMeta<Prov>> {
+        Ok(if self.layout.is_unsized() {
+            if matches!(self.op, Operand::Immediate(_)) {
+                // Unsized immediate OpTy cannot occur. We create a MemPlace for all unsized locals during argument passing.
+                // However, ConstProp doesn't do that, so we can run into this nonsense situation.
+                throw_inval!(ConstPropNonsense);
             }
             // There are no unsized immediates.
-            self.assert_mem_place().len(cx)
+            self.assert_mem_place().meta
         } else {
-            match self.layout.fields {
-                abi::FieldsShape::Array { count, .. } => Ok(count),
-                _ => bug!("len not supported on sized type {:?}", self.layout.ty),
-            }
-        }
+            MemPlaceMeta::None
+        })
     }
+}
 
-    /// Replace the layout of this operand. There's basically no sanity check that this makes sense,
-    /// you better know what you are doing! If this is an immediate, applying the wrong layout can
-    /// not just lead to invalid data, it can actually *shift the data around* since the offsets of
-    /// a ScalarPair are entirely determined by the layout, not the data.
-    pub fn transmute(&self, layout: TyAndLayout<'tcx>) -> Self {
-        assert_eq!(
-            self.layout.size, layout.size,
-            "transmuting with a size change, that doesn't seem right"
-        );
-        OpTy { layout, ..*self }
+impl<'tcx, Prov: Provenance + 'static> Projectable<'tcx, Prov> for OpTy<'tcx, Prov> {
+    #[inline(always)]
+    fn layout(&self) -> TyAndLayout<'tcx> {
+        self.layout
     }
 
-    /// Offset the operand in memory (if possible) and change its metadata.
-    ///
-    /// This can go wrong very easily if you give the wrong layout for the new place!
-    pub(super) fn offset_with_meta(
+    fn meta<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
+        &self,
+        _ecx: &InterpCx<'mir, 'tcx, M>,
+    ) -> InterpResult<'tcx, MemPlaceMeta<M::Provenance>> {
+        self.meta()
+    }
+
+    fn offset_with_meta(
         &self,
         offset: Size,
         meta: MemPlaceMeta<Prov>,
@@ -286,28 +357,43 @@ impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> {
         match self.as_mplace_or_imm() {
             Left(mplace) => Ok(mplace.offset_with_meta(offset, meta, layout, cx)?.into()),
             Right(imm) => {
-                assert!(
-                    matches!(*imm, Immediate::Uninit),
-                    "Scalar/ScalarPair cannot be offset into"
-                );
                 assert!(!meta.has_meta()); // no place to store metadata here
                 // Every part of an uninit is uninit.
-                Ok(ImmTy::uninit(layout).into())
+                Ok(imm.offset(offset, layout, cx)?.into())
             }
         }
     }
 
-    /// Offset the operand in memory (if possible).
-    ///
-    /// This can go wrong very easily if you give the wrong layout for the new place!
-    pub fn offset(
+    fn to_op<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
         &self,
-        offset: Size,
-        layout: TyAndLayout<'tcx>,
-        cx: &impl HasDataLayout,
-    ) -> InterpResult<'tcx, Self> {
-        assert!(layout.is_sized());
-        self.offset_with_meta(offset, MemPlaceMeta::None, layout, cx)
+        _ecx: &InterpCx<'mir, 'tcx, M>,
+    ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
+        Ok(self.clone())
+    }
+}
+
+pub trait Readable<'tcx, Prov: Provenance>: Projectable<'tcx, Prov> {
+    fn as_mplace_or_imm(&self) -> Either<MPlaceTy<'tcx, Prov>, ImmTy<'tcx, Prov>>;
+}
+
+impl<'tcx, Prov: Provenance + 'static> Readable<'tcx, Prov> for OpTy<'tcx, Prov> {
+    #[inline(always)]
+    fn as_mplace_or_imm(&self) -> Either<MPlaceTy<'tcx, Prov>, ImmTy<'tcx, Prov>> {
+        self.as_mplace_or_imm()
+    }
+}
+
+impl<'tcx, Prov: Provenance + 'static> Readable<'tcx, Prov> for MPlaceTy<'tcx, Prov> {
+    #[inline(always)]
+    fn as_mplace_or_imm(&self) -> Either<MPlaceTy<'tcx, Prov>, ImmTy<'tcx, Prov>> {
+        Left(self.clone())
+    }
+}
+
+impl<'tcx, Prov: Provenance> Readable<'tcx, Prov> for ImmTy<'tcx, Prov> {
+    #[inline(always)]
+    fn as_mplace_or_imm(&self) -> Either<MPlaceTy<'tcx, Prov>, ImmTy<'tcx, Prov>> {
+        Right(self.clone())
     }
 }
 
@@ -383,14 +469,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     /// ConstProp needs it, though.
     pub fn read_immediate_raw(
         &self,
-        src: &OpTy<'tcx, M::Provenance>,
+        src: &impl Readable<'tcx, M::Provenance>,
     ) -> InterpResult<'tcx, Either<MPlaceTy<'tcx, M::Provenance>, ImmTy<'tcx, M::Provenance>>> {
         Ok(match src.as_mplace_or_imm() {
             Left(ref mplace) => {
                 if let Some(val) = self.read_immediate_from_mplace_raw(mplace)? {
                     Right(val)
                 } else {
-                    Left(*mplace)
+                    Left(mplace.clone())
                 }
             }
             Right(val) => Right(val),
@@ -403,14 +489,18 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     #[inline(always)]
     pub fn read_immediate(
         &self,
-        op: &OpTy<'tcx, M::Provenance>,
+        op: &impl Readable<'tcx, M::Provenance>,
     ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> {
         if !matches!(
-            op.layout.abi,
+            op.layout().abi,
             Abi::Scalar(abi::Scalar::Initialized { .. })
                 | Abi::ScalarPair(abi::Scalar::Initialized { .. }, abi::Scalar::Initialized { .. })
         ) {
-            span_bug!(self.cur_span(), "primitive read not possible for type: {:?}", op.layout.ty);
+            span_bug!(
+                self.cur_span(),
+                "primitive read not possible for type: {:?}",
+                op.layout().ty
+            );
         }
         let imm = self.read_immediate_raw(op)?.right().unwrap();
         if matches!(*imm, Immediate::Uninit) {
@@ -422,7 +512,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     /// Read a scalar from a place
     pub fn read_scalar(
         &self,
-        op: &OpTy<'tcx, M::Provenance>,
+        op: &impl Readable<'tcx, M::Provenance>,
     ) -> InterpResult<'tcx, Scalar<M::Provenance>> {
         Ok(self.read_immediate(op)?.to_scalar())
     }
@@ -433,16 +523,22 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     /// Read a pointer from a place.
     pub fn read_pointer(
         &self,
-        op: &OpTy<'tcx, M::Provenance>,
+        op: &impl Readable<'tcx, M::Provenance>,
     ) -> InterpResult<'tcx, Pointer<Option<M::Provenance>>> {
         self.read_scalar(op)?.to_pointer(self)
     }
     /// Read a pointer-sized unsigned integer from a place.
-    pub fn read_target_usize(&self, op: &OpTy<'tcx, M::Provenance>) -> InterpResult<'tcx, u64> {
+    pub fn read_target_usize(
+        &self,
+        op: &impl Readable<'tcx, M::Provenance>,
+    ) -> InterpResult<'tcx, u64> {
         self.read_scalar(op)?.to_target_usize(self)
     }
     /// Read a pointer-sized signed integer from a place.
-    pub fn read_target_isize(&self, op: &OpTy<'tcx, M::Provenance>) -> InterpResult<'tcx, i64> {
+    pub fn read_target_isize(
+        &self,
+        op: &impl Readable<'tcx, M::Provenance>,
+    ) -> InterpResult<'tcx, i64> {
         self.read_scalar(op)?.to_target_isize(self)
     }
 
@@ -497,18 +593,28 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     /// Every place can be read from, so we can turn them into an operand.
     /// This will definitely return `Indirect` if the place is a `Ptr`, i.e., this
     /// will never actually read from memory.
-    #[inline(always)]
     pub fn place_to_op(
         &self,
         place: &PlaceTy<'tcx, M::Provenance>,
     ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
-        let op = match **place {
-            Place::Ptr(mplace) => Operand::Indirect(mplace),
-            Place::Local { frame, local } => {
-                *self.local_to_op(&self.stack()[frame], local, None)?
+        match place.as_mplace_or_local() {
+            Left(mplace) => Ok(mplace.into()),
+            Right((frame, local, offset)) => {
+                let base = self.local_to_op(&self.stack()[frame], local, None)?;
+                let mut field = if let Some(offset) = offset {
+                    // This got offset. We can be sure that the field is sized.
+                    base.offset(offset, place.layout, self)?
+                } else {
+                    assert_eq!(place.layout, base.layout);
+                    // Unsized cases are possible here since an unsized local will be a
+                    // `Place::Local` until the first projection calls `place_to_op` to extract the
+                    // underlying mplace.
+                    base
+                };
+                field.align = Some(place.align);
+                Ok(field)
             }
-        };
-        Ok(OpTy { op, layout: place.layout, align: Some(place.align) })
+        }
     }
 
     /// Evaluate a place with the goal of reading from it. This lets us sometimes
@@ -525,7 +631,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         let mut op = self.local_to_op(self.frame(), mir_place.local, layout)?;
         // Using `try_fold` turned out to be bad for performance, hence the loop.
         for elem in mir_place.projection.iter() {
-            op = self.operand_projection(&op, elem)?
+            op = self.project(&op, elem)?
         }
 
         trace!("eval_place_to_op: got {:?}", *op);
@@ -592,10 +698,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             ty::ConstKind::Unevaluated(uv) => {
                 let instance = self.resolve(uv.def, uv.args)?;
                 let cid = GlobalId { instance, promoted: None };
-                self.ctfe_query(span, |tcx| {
-                    tcx.eval_to_valtree(self.param_env.with_const().and(cid))
-                })?
-                .unwrap_or_else(|| bug!("unable to create ValTree for {uv:?}"))
+                self.ctfe_query(span, |tcx| tcx.eval_to_valtree(self.param_env.and(cid)))?
+                    .unwrap_or_else(|| bug!("unable to create ValTree for {uv:?}"))
             }
             ty::ConstKind::Bound(..) | ty::ConstKind::Infer(..) => {
                 span_bug!(self.cur_span(), "unexpected ConstKind in ctfe: {val:?}")
diff --git a/compiler/rustc_const_eval/src/interpret/operator.rs b/compiler/rustc_const_eval/src/interpret/operator.rs
index e04764636cc..eb064578067 100644
--- a/compiler/rustc_const_eval/src/interpret/operator.rs
+++ b/compiler/rustc_const_eval/src/interpret/operator.rs
@@ -24,8 +24,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         debug_assert_eq!(
             Ty::new_tup(self.tcx.tcx, &[ty, self.tcx.types.bool]),
             dest.layout.ty,
-            "type mismatch for result of {:?}",
-            op,
+            "type mismatch for result of {op:?}",
         );
         // Write the result to `dest`.
         if let Abi::ScalarPair(..) = dest.layout.abi {
@@ -38,9 +37,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             // With randomized layout, `(int, bool)` might cease to be a `ScalarPair`, so we have to
             // do a component-wise write here. This code path is slower than the above because
             // `place_field` will have to `force_allocate` locals here.
-            let val_field = self.place_field(&dest, 0)?;
+            let val_field = self.project_field(dest, 0)?;
             self.write_scalar(val, &val_field)?;
-            let overflowed_field = self.place_field(&dest, 1)?;
+            let overflowed_field = self.project_field(dest, 1)?;
             self.write_scalar(Scalar::from_bool(overflowed), &overflowed_field)?;
         }
         Ok(())
@@ -56,7 +55,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         dest: &PlaceTy<'tcx, M::Provenance>,
     ) -> InterpResult<'tcx> {
         let (val, _overflowed, ty) = self.overflowing_binary_op(op, left, right)?;
-        assert_eq!(ty, dest.layout.ty, "type mismatch for result of {:?}", op);
+        assert_eq!(ty, dest.layout.ty, "type mismatch for result of {op:?}");
         self.write_scalar(val, dest)
     }
 }
diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs
index a9b2b43f1e6..96a960118ce 100644
--- a/compiler/rustc_const_eval/src/interpret/place.rs
+++ b/compiler/rustc_const_eval/src/interpret/place.rs
@@ -2,11 +2,14 @@
 //! into a place.
 //! All high-level functions to write to memory work on places as destinations.
 
+use std::assert_matches::assert_matches;
+
 use either::{Either, Left, Right};
 
 use rustc_ast::Mutability;
 use rustc_index::IndexSlice;
 use rustc_middle::mir;
+use rustc_middle::mir::interpret::PointerArithmetic;
 use rustc_middle::ty;
 use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
 use rustc_middle::ty::Ty;
@@ -15,7 +18,7 @@ use rustc_target::abi::{self, Abi, Align, FieldIdx, HasDataLayout, Size, FIRST_V
 use super::{
     alloc_range, mir_assign_valid_types, AllocId, AllocRef, AllocRefMut, CheckInAllocMsg,
     ConstAlloc, ImmTy, Immediate, InterpCx, InterpResult, Machine, MemoryKind, OpTy, Operand,
-    Pointer, Provenance, Scalar,
+    Pointer, Projectable, Provenance, Readable, Scalar,
 };
 
 #[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)]
@@ -44,6 +47,27 @@ impl<Prov: Provenance> MemPlaceMeta<Prov> {
             Self::None => false,
         }
     }
+
+    pub(crate) fn len<'tcx>(
+        &self,
+        layout: TyAndLayout<'tcx>,
+        cx: &impl HasDataLayout,
+    ) -> InterpResult<'tcx, u64> {
+        if layout.is_unsized() {
+            // We need to consult `meta` metadata
+            match layout.ty.kind() {
+                ty::Slice(..) | ty::Str => self.unwrap_meta().to_target_usize(cx),
+                _ => bug!("len not supported on unsized type {:?}", layout.ty),
+            }
+        } else {
+            // Go through the layout. There are lots of types that support a length,
+            // e.g., SIMD types. (But not all repr(simd) types even have FieldsShape::Array!)
+            match layout.fields {
+                abi::FieldsShape::Array { count, .. } => Ok(count),
+                _ => bug!("len not supported on sized type {:?}", layout.ty),
+            }
+        }
+    }
 }
 
 #[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)]
@@ -57,7 +81,7 @@ pub struct MemPlace<Prov: Provenance = AllocId> {
 }
 
 /// A MemPlace with its layout. Constructing it is only possible in this module.
-#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)]
+#[derive(Clone, Hash, Eq, PartialEq, Debug)]
 pub struct MPlaceTy<'tcx, Prov: Provenance = AllocId> {
     mplace: MemPlace<Prov>,
     pub layout: TyAndLayout<'tcx>,
@@ -68,14 +92,26 @@ pub struct MPlaceTy<'tcx, Prov: Provenance = AllocId> {
     pub align: Align,
 }
 
+impl<'tcx, Prov: Provenance> std::ops::Deref for MPlaceTy<'tcx, Prov> {
+    type Target = MemPlace<Prov>;
+    #[inline(always)]
+    fn deref(&self) -> &MemPlace<Prov> {
+        &self.mplace
+    }
+}
+
 #[derive(Copy, Clone, Debug)]
 pub enum Place<Prov: Provenance = AllocId> {
     /// A place referring to a value allocated in the `Memory` system.
     Ptr(MemPlace<Prov>),
 
-    /// To support alloc-free locals, we are able to write directly to a local.
+    /// To support alloc-free locals, we are able to write directly to a local. The offset indicates
+    /// where in the local this place is located; if it is `None`, no projection has been applied.
+    /// Such projections are meaningful even if the offset is 0, since they can change layouts.
     /// (Without that optimization, we'd just always be a `MemPlace`.)
-    Local { frame: usize, local: mir::Local },
+    /// Note that this only stores the frame index, not the thread this frame belongs to -- that is
+    /// implicit. This means a `Place` must never be moved across interpreter thread boundaries!
+    Local { frame: usize, local: mir::Local, offset: Option<Size> },
 }
 
 #[derive(Clone, Debug)]
@@ -97,14 +133,6 @@ impl<'tcx, Prov: Provenance> std::ops::Deref for PlaceTy<'tcx, Prov> {
     }
 }
 
-impl<'tcx, Prov: Provenance> std::ops::Deref for MPlaceTy<'tcx, Prov> {
-    type Target = MemPlace<Prov>;
-    #[inline(always)]
-    fn deref(&self) -> &MemPlace<Prov> {
-        &self.mplace
-    }
-}
-
 impl<'tcx, Prov: Provenance> From<MPlaceTy<'tcx, Prov>> for PlaceTy<'tcx, Prov> {
     #[inline(always)]
     fn from(mplace: MPlaceTy<'tcx, Prov>) -> Self {
@@ -112,26 +140,17 @@ impl<'tcx, Prov: Provenance> From<MPlaceTy<'tcx, Prov>> for PlaceTy<'tcx, Prov>
     }
 }
 
-impl<'tcx, Prov: Provenance> From<&'_ MPlaceTy<'tcx, Prov>> for PlaceTy<'tcx, Prov> {
-    #[inline(always)]
-    fn from(mplace: &MPlaceTy<'tcx, Prov>) -> Self {
-        PlaceTy { place: Place::Ptr(**mplace), layout: mplace.layout, align: mplace.align }
-    }
-}
-
-impl<'tcx, Prov: Provenance> From<&'_ mut MPlaceTy<'tcx, Prov>> for PlaceTy<'tcx, Prov> {
-    #[inline(always)]
-    fn from(mplace: &mut MPlaceTy<'tcx, Prov>) -> Self {
-        PlaceTy { place: Place::Ptr(**mplace), layout: mplace.layout, align: mplace.align }
-    }
-}
-
 impl<Prov: Provenance> MemPlace<Prov> {
     #[inline(always)]
     pub fn from_ptr(ptr: Pointer<Option<Prov>>) -> Self {
         MemPlace { ptr, meta: MemPlaceMeta::None }
     }
 
+    #[inline(always)]
+    pub fn from_ptr_with_meta(ptr: Pointer<Option<Prov>>, meta: MemPlaceMeta<Prov>) -> Self {
+        MemPlace { ptr, meta }
+    }
+
     /// Adjust the provenance of the main pointer (metadata is unaffected).
     pub fn map_provenance(self, f: impl FnOnce(Option<Prov>) -> Option<Prov>) -> Self {
         MemPlace { ptr: self.ptr.map_provenance(f), ..self }
@@ -150,7 +169,8 @@ impl<Prov: Provenance> MemPlace<Prov> {
     }
 
     #[inline]
-    pub(super) fn offset_with_meta<'tcx>(
+    // Not called `offset_with_meta` to avoid confusion with the trait method.
+    fn offset_with_meta_<'tcx>(
         self,
         offset: Size,
         meta: MemPlaceMeta<Prov>,
@@ -164,19 +184,6 @@ impl<Prov: Provenance> MemPlace<Prov> {
     }
 }
 
-impl<Prov: Provenance> Place<Prov> {
-    /// Asserts that this points to some local variable.
-    /// Returns the frame idx and the variable idx.
-    #[inline]
-    #[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980)
-    pub fn assert_local(&self) -> (usize, mir::Local) {
-        match self {
-            Place::Local { frame, local } => (*frame, *local),
-            _ => bug!("assert_local: expected Place::Local, got {:?}", self),
-        }
-    }
-}
-
 impl<'tcx, Prov: Provenance> MPlaceTy<'tcx, Prov> {
     /// Produces a MemPlace that works for ZST but nothing else.
     /// Conceptually this is a new allocation, but it doesn't actually create an allocation so you
@@ -189,11 +196,39 @@ impl<'tcx, Prov: Provenance> MPlaceTy<'tcx, Prov> {
         MPlaceTy { mplace: MemPlace { ptr, meta: MemPlaceMeta::None }, layout, align }
     }
 
-    /// Offset the place in memory and change its metadata.
-    ///
-    /// This can go wrong very easily if you give the wrong layout for the new place!
     #[inline]
-    pub(crate) fn offset_with_meta(
+    pub fn from_aligned_ptr(ptr: Pointer<Option<Prov>>, layout: TyAndLayout<'tcx>) -> Self {
+        MPlaceTy { mplace: MemPlace::from_ptr(ptr), layout, align: layout.align.abi }
+    }
+
+    #[inline]
+    pub fn from_aligned_ptr_with_meta(
+        ptr: Pointer<Option<Prov>>,
+        layout: TyAndLayout<'tcx>,
+        meta: MemPlaceMeta<Prov>,
+    ) -> Self {
+        MPlaceTy {
+            mplace: MemPlace::from_ptr_with_meta(ptr, meta),
+            layout,
+            align: layout.align.abi,
+        }
+    }
+}
+
+impl<'tcx, Prov: Provenance + 'static> Projectable<'tcx, Prov> for MPlaceTy<'tcx, Prov> {
+    #[inline(always)]
+    fn layout(&self) -> TyAndLayout<'tcx> {
+        self.layout
+    }
+
+    fn meta<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
+        &self,
+        _ecx: &InterpCx<'mir, 'tcx, M>,
+    ) -> InterpResult<'tcx, MemPlaceMeta<M::Provenance>> {
+        Ok(self.meta)
+    }
+
+    fn offset_with_meta(
         &self,
         offset: Size,
         meta: MemPlaceMeta<Prov>,
@@ -201,58 +236,65 @@ impl<'tcx, Prov: Provenance> MPlaceTy<'tcx, Prov> {
         cx: &impl HasDataLayout,
     ) -> InterpResult<'tcx, Self> {
         Ok(MPlaceTy {
-            mplace: self.mplace.offset_with_meta(offset, meta, cx)?,
+            mplace: self.mplace.offset_with_meta_(offset, meta, cx)?,
             align: self.align.restrict_for_offset(offset),
             layout,
         })
     }
 
-    /// Offset the place in memory.
-    ///
-    /// This can go wrong very easily if you give the wrong layout for the new place!
-    pub fn offset(
+    fn to_op<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
         &self,
-        offset: Size,
-        layout: TyAndLayout<'tcx>,
-        cx: &impl HasDataLayout,
-    ) -> InterpResult<'tcx, Self> {
-        assert!(layout.is_sized());
-        self.offset_with_meta(offset, MemPlaceMeta::None, layout, cx)
+        _ecx: &InterpCx<'mir, 'tcx, M>,
+    ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
+        Ok(self.clone().into())
     }
+}
 
-    #[inline]
-    pub fn from_aligned_ptr(ptr: Pointer<Option<Prov>>, layout: TyAndLayout<'tcx>) -> Self {
-        MPlaceTy { mplace: MemPlace::from_ptr(ptr), layout, align: layout.align.abi }
+impl<'tcx, Prov: Provenance + 'static> Projectable<'tcx, Prov> for PlaceTy<'tcx, Prov> {
+    #[inline(always)]
+    fn layout(&self) -> TyAndLayout<'tcx> {
+        self.layout
     }
 
-    #[inline]
-    pub fn from_aligned_ptr_with_meta(
-        ptr: Pointer<Option<Prov>>,
-        layout: TyAndLayout<'tcx>,
-        meta: MemPlaceMeta<Prov>,
-    ) -> Self {
-        let mut mplace = MemPlace::from_ptr(ptr);
-        mplace.meta = meta;
-
-        MPlaceTy { mplace, layout, align: layout.align.abi }
+    fn meta<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
+        &self,
+        ecx: &InterpCx<'mir, 'tcx, M>,
+    ) -> InterpResult<'tcx, MemPlaceMeta<M::Provenance>> {
+        ecx.place_meta(self)
     }
 
-    #[inline]
-    pub(crate) fn len(&self, cx: &impl HasDataLayout) -> InterpResult<'tcx, u64> {
-        if self.layout.is_unsized() {
-            // We need to consult `meta` metadata
-            match self.layout.ty.kind() {
-                ty::Slice(..) | ty::Str => self.mplace.meta.unwrap_meta().to_target_usize(cx),
-                _ => bug!("len not supported on unsized type {:?}", self.layout.ty),
-            }
-        } else {
-            // Go through the layout. There are lots of types that support a length,
-            // e.g., SIMD types. (But not all repr(simd) types even have FieldsShape::Array!)
-            match self.layout.fields {
-                abi::FieldsShape::Array { count, .. } => Ok(count),
-                _ => bug!("len not supported on sized type {:?}", self.layout.ty),
+    fn offset_with_meta(
+        &self,
+        offset: Size,
+        meta: MemPlaceMeta<Prov>,
+        layout: TyAndLayout<'tcx>,
+        cx: &impl HasDataLayout,
+    ) -> InterpResult<'tcx, Self> {
+        Ok(match self.as_mplace_or_local() {
+            Left(mplace) => mplace.offset_with_meta(offset, meta, layout, cx)?.into(),
+            Right((frame, local, old_offset)) => {
+                assert_matches!(meta, MemPlaceMeta::None); // we couldn't store it anyway...
+                let new_offset = cx
+                    .data_layout()
+                    .offset(old_offset.unwrap_or(Size::ZERO).bytes(), offset.bytes())?;
+                PlaceTy {
+                    place: Place::Local {
+                        frame,
+                        local,
+                        offset: Some(Size::from_bytes(new_offset)),
+                    },
+                    align: self.align.restrict_for_offset(offset),
+                    layout,
+                }
             }
-        }
+        })
+    }
+
+    fn to_op<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
+        &self,
+        ecx: &InterpCx<'mir, 'tcx, M>,
+    ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
+        ecx.place_to_op(self)
     }
 }
 
@@ -280,13 +322,15 @@ impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> {
     }
 }
 
-impl<'tcx, Prov: Provenance> PlaceTy<'tcx, Prov> {
+impl<'tcx, Prov: Provenance + 'static> PlaceTy<'tcx, Prov> {
     /// A place is either an mplace or some local.
     #[inline]
-    pub fn as_mplace_or_local(&self) -> Either<MPlaceTy<'tcx, Prov>, (usize, mir::Local)> {
+    pub fn as_mplace_or_local(
+        &self,
+    ) -> Either<MPlaceTy<'tcx, Prov>, (usize, mir::Local, Option<Size>)> {
         match **self {
             Place::Ptr(mplace) => Left(MPlaceTy { mplace, layout: self.layout, align: self.align }),
-            Place::Local { frame, local } => Right((frame, local)),
+            Place::Local { frame, local, offset } => Right((frame, local, offset)),
         }
     }
 
@@ -302,12 +346,74 @@ impl<'tcx, Prov: Provenance> PlaceTy<'tcx, Prov> {
     }
 }
 
+pub trait Writeable<'tcx, Prov: Provenance>: Projectable<'tcx, Prov> {
+    fn as_mplace_or_local(
+        &self,
+    ) -> Either<MPlaceTy<'tcx, Prov>, (usize, mir::Local, Option<Size>, Align, TyAndLayout<'tcx>)>;
+
+    fn force_mplace<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
+        &self,
+        ecx: &mut InterpCx<'mir, 'tcx, M>,
+    ) -> InterpResult<'tcx, MPlaceTy<'tcx, Prov>>;
+}
+
+impl<'tcx, Prov: Provenance + 'static> Writeable<'tcx, Prov> for PlaceTy<'tcx, Prov> {
+    #[inline(always)]
+    fn as_mplace_or_local(
+        &self,
+    ) -> Either<MPlaceTy<'tcx, Prov>, (usize, mir::Local, Option<Size>, Align, TyAndLayout<'tcx>)>
+    {
+        self.as_mplace_or_local()
+            .map_right(|(frame, local, offset)| (frame, local, offset, self.align, self.layout))
+    }
+
+    #[inline(always)]
+    fn force_mplace<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
+        &self,
+        ecx: &mut InterpCx<'mir, 'tcx, M>,
+    ) -> InterpResult<'tcx, MPlaceTy<'tcx, Prov>> {
+        ecx.force_allocation(self)
+    }
+}
+
+impl<'tcx, Prov: Provenance + 'static> Writeable<'tcx, Prov> for MPlaceTy<'tcx, Prov> {
+    #[inline(always)]
+    fn as_mplace_or_local(
+        &self,
+    ) -> Either<MPlaceTy<'tcx, Prov>, (usize, mir::Local, Option<Size>, Align, TyAndLayout<'tcx>)>
+    {
+        Left(self.clone())
+    }
+
+    #[inline(always)]
+    fn force_mplace<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
+        &self,
+        _ecx: &mut InterpCx<'mir, 'tcx, M>,
+    ) -> InterpResult<'tcx, MPlaceTy<'tcx, Prov>> {
+        Ok(self.clone())
+    }
+}
+
 // FIXME: Working around https://github.com/rust-lang/rust/issues/54385
 impl<'mir, 'tcx: 'mir, Prov, M> InterpCx<'mir, 'tcx, M>
 where
     Prov: Provenance + 'static,
     M: Machine<'mir, 'tcx, Provenance = Prov>,
 {
+    /// Get the metadata of the given place.
+    pub(super) fn place_meta(
+        &self,
+        place: &PlaceTy<'tcx, M::Provenance>,
+    ) -> InterpResult<'tcx, MemPlaceMeta<M::Provenance>> {
+        if place.layout.is_unsized() {
+            // For `Place::Local`, the metadata is stored with the local, not the place. So we have
+            // to look that up first.
+            self.place_to_op(place)?.meta()
+        } else {
+            Ok(MemPlaceMeta::None)
+        }
+    }
+
     /// Take a value, which represents a (thin or wide) reference, and make it a place.
     /// Alignment is just based on the type. This is the inverse of `MemPlace::to_ref()`.
     ///
@@ -327,18 +433,16 @@ where
             Immediate::Uninit => throw_ub!(InvalidUninitBytes(None)),
         };
 
-        let mplace = MemPlace { ptr: ptr.to_pointer(self)?, meta };
         // `ref_to_mplace` is called on raw pointers even if they don't actually get dereferenced;
         // we hence can't call `size_and_align_of` since that asserts more validity than we want.
-        let align = layout.align.abi;
-        Ok(MPlaceTy { mplace, layout, align })
+        Ok(MPlaceTy::from_aligned_ptr_with_meta(ptr.to_pointer(self)?, layout, meta))
     }
 
     /// Take an operand, representing a pointer, and dereference it to a place.
     #[instrument(skip(self), level = "debug")]
     pub fn deref_operand(
         &self,
-        src: &OpTy<'tcx, M::Provenance>,
+        src: &impl Readable<'tcx, M::Provenance>,
     ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
         let val = self.read_immediate(src)?;
         trace!("deref to {} on {:?}", val.layout.ty, *val);
@@ -348,7 +452,7 @@ where
         }
 
         let mplace = self.ref_to_mplace(&val)?;
-        self.check_mplace(mplace)?;
+        self.check_mplace(&mplace)?;
         Ok(mplace)
     }
 
@@ -379,7 +483,7 @@ where
     }
 
     /// Check if this mplace is dereferenceable and sufficiently aligned.
-    pub fn check_mplace(&self, mplace: MPlaceTy<'tcx, M::Provenance>) -> InterpResult<'tcx> {
+    pub fn check_mplace(&self, mplace: &MPlaceTy<'tcx, M::Provenance>) -> InterpResult<'tcx> {
         let (size, _align) = self
             .size_and_align_of_mplace(&mplace)?
             .unwrap_or((mplace.layout.size, mplace.layout.align.abi));
@@ -422,7 +526,7 @@ where
         local: mir::Local,
     ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> {
         let layout = self.layout_of_local(&self.stack()[frame], local, None)?;
-        let place = Place::Local { frame, local };
+        let place = Place::Local { frame, local, offset: None };
         Ok(PlaceTy { place, layout, align: layout.align.abi })
     }
 
@@ -430,13 +534,13 @@ where
     /// place; for reading, a more efficient alternative is `eval_place_to_op`.
     #[instrument(skip(self), level = "debug")]
     pub fn eval_place(
-        &mut self,
+        &self,
         mir_place: mir::Place<'tcx>,
     ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> {
         let mut place = self.local_to_place(self.frame_idx(), mir_place.local)?;
         // Using `try_fold` turned out to be bad for performance, hence the loop.
         for elem in mir_place.projection.iter() {
-            place = self.place_projection(&place, elem)?
+            place = self.project(&place, elem)?
         }
 
         trace!("{:?}", self.dump_place(place.place));
@@ -463,13 +567,13 @@ where
     pub fn write_immediate(
         &mut self,
         src: Immediate<M::Provenance>,
-        dest: &PlaceTy<'tcx, M::Provenance>,
+        dest: &impl Writeable<'tcx, M::Provenance>,
     ) -> InterpResult<'tcx> {
         self.write_immediate_no_validate(src, dest)?;
 
-        if M::enforce_validity(self, dest.layout) {
+        if M::enforce_validity(self, dest.layout()) {
             // Data got changed, better make sure it matches the type!
-            self.validate_operand(&self.place_to_op(dest)?)?;
+            self.validate_operand(&dest.to_op(self)?)?;
         }
 
         Ok(())
@@ -480,7 +584,7 @@ where
     pub fn write_scalar(
         &mut self,
         val: impl Into<Scalar<M::Provenance>>,
-        dest: &PlaceTy<'tcx, M::Provenance>,
+        dest: &impl Writeable<'tcx, M::Provenance>,
     ) -> InterpResult<'tcx> {
         self.write_immediate(Immediate::Scalar(val.into()), dest)
     }
@@ -490,7 +594,7 @@ where
     pub fn write_pointer(
         &mut self,
         ptr: impl Into<Pointer<Option<M::Provenance>>>,
-        dest: &PlaceTy<'tcx, M::Provenance>,
+        dest: &impl Writeable<'tcx, M::Provenance>,
     ) -> InterpResult<'tcx> {
         self.write_scalar(Scalar::from_maybe_pointer(ptr.into(), self), dest)
     }
@@ -501,32 +605,63 @@ where
     fn write_immediate_no_validate(
         &mut self,
         src: Immediate<M::Provenance>,
-        dest: &PlaceTy<'tcx, M::Provenance>,
+        dest: &impl Writeable<'tcx, M::Provenance>,
     ) -> InterpResult<'tcx> {
-        assert!(dest.layout.is_sized(), "Cannot write unsized data");
-        trace!("write_immediate: {:?} <- {:?}: {}", *dest, src, dest.layout.ty);
+        assert!(dest.layout().is_sized(), "Cannot write unsized immediate data");
 
         // See if we can avoid an allocation. This is the counterpart to `read_immediate_raw`,
         // but not factored as a separate function.
-        let mplace = match dest.place {
-            Place::Local { frame, local } => {
-                match M::access_local_mut(self, frame, local)? {
-                    Operand::Immediate(local) => {
-                        // Local can be updated in-place.
-                        *local = src;
-                        return Ok(());
-                    }
-                    Operand::Indirect(mplace) => {
-                        // The local is in memory, go on below.
-                        *mplace
+        let mplace = match dest.as_mplace_or_local() {
+            Right((frame, local, offset, align, layout)) => {
+                if offset.is_some() {
+                    // This has been projected to a part of this local. We could have complicated
+                    // logic to still keep this local as an `Operand`... but it's much easier to
+                    // just fall back to the indirect path.
+                    dest.force_mplace(self)?
+                } else {
+                    match M::access_local_mut(self, frame, local)? {
+                        Operand::Immediate(local_val) => {
+                            // Local can be updated in-place.
+                            *local_val = src;
+                            // Double-check that the value we are storing and the local fit to each other.
+                            // (*After* doing the update for borrow checker reasons.)
+                            if cfg!(debug_assertions) {
+                                let local_layout =
+                                    self.layout_of_local(&self.stack()[frame], local, None)?;
+                                match (src, local_layout.abi) {
+                                    (Immediate::Scalar(scalar), Abi::Scalar(s)) => {
+                                        assert_eq!(scalar.size(), s.size(self))
+                                    }
+                                    (
+                                        Immediate::ScalarPair(a_val, b_val),
+                                        Abi::ScalarPair(a, b),
+                                    ) => {
+                                        assert_eq!(a_val.size(), a.size(self));
+                                        assert_eq!(b_val.size(), b.size(self));
+                                    }
+                                    (Immediate::Uninit, _) => {}
+                                    (src, abi) => {
+                                        bug!(
+                                            "value {src:?} cannot be written into local with type {} (ABI {abi:?})",
+                                            local_layout.ty
+                                        )
+                                    }
+                                };
+                            }
+                            return Ok(());
+                        }
+                        Operand::Indirect(mplace) => {
+                            // The local is in memory, go on below.
+                            MPlaceTy { mplace: *mplace, align, layout }
+                        }
                     }
                 }
             }
-            Place::Ptr(mplace) => mplace, // already referring to memory
+            Left(mplace) => mplace, // already referring to memory
         };
 
         // This is already in memory, write there.
-        self.write_immediate_to_mplace_no_validate(src, dest.layout, dest.align, mplace)
+        self.write_immediate_to_mplace_no_validate(src, mplace.layout, mplace.align, mplace.mplace)
     }
 
     /// Write an immediate to memory.
@@ -590,18 +725,29 @@ where
         }
     }
 
-    pub fn write_uninit(&mut self, dest: &PlaceTy<'tcx, M::Provenance>) -> InterpResult<'tcx> {
+    pub fn write_uninit(
+        &mut self,
+        dest: &impl Writeable<'tcx, M::Provenance>,
+    ) -> InterpResult<'tcx> {
         let mplace = match dest.as_mplace_or_local() {
             Left(mplace) => mplace,
-            Right((frame, local)) => {
-                match M::access_local_mut(self, frame, local)? {
-                    Operand::Immediate(local) => {
-                        *local = Immediate::Uninit;
-                        return Ok(());
-                    }
-                    Operand::Indirect(mplace) => {
-                        // The local is in memory, go on below.
-                        MPlaceTy { mplace: *mplace, layout: dest.layout, align: dest.align }
+            Right((frame, local, offset, align, layout)) => {
+                if offset.is_some() {
+                    // This has been projected to a part of this local. We could have complicated
+                    // logic to still keep this local as an `Operand`... but it's much easier to
+                    // just fall back to the indirect path.
+                    // FIXME: share the logic with `write_immediate_no_validate`.
+                    dest.force_mplace(self)?
+                } else {
+                    match M::access_local_mut(self, frame, local)? {
+                        Operand::Immediate(local) => {
+                            *local = Immediate::Uninit;
+                            return Ok(());
+                        }
+                        Operand::Indirect(mplace) => {
+                            // The local is in memory, go on below.
+                            MPlaceTy { mplace: *mplace, layout, align }
+                        }
                     }
                 }
             }
@@ -620,15 +766,15 @@ where
     #[instrument(skip(self), level = "debug")]
     pub fn copy_op(
         &mut self,
-        src: &OpTy<'tcx, M::Provenance>,
-        dest: &PlaceTy<'tcx, M::Provenance>,
+        src: &impl Readable<'tcx, M::Provenance>,
+        dest: &impl Writeable<'tcx, M::Provenance>,
         allow_transmute: bool,
     ) -> InterpResult<'tcx> {
         self.copy_op_no_validate(src, dest, allow_transmute)?;
 
-        if M::enforce_validity(self, dest.layout) {
+        if M::enforce_validity(self, dest.layout()) {
             // Data got changed, better make sure it matches the type!
-            self.validate_operand(&self.place_to_op(dest)?)?;
+            self.validate_operand(&dest.to_op(self)?)?;
         }
 
         Ok(())
@@ -641,20 +787,20 @@ where
     #[instrument(skip(self), level = "debug")]
     fn copy_op_no_validate(
         &mut self,
-        src: &OpTy<'tcx, M::Provenance>,
-        dest: &PlaceTy<'tcx, M::Provenance>,
+        src: &impl Readable<'tcx, M::Provenance>,
+        dest: &impl Writeable<'tcx, M::Provenance>,
         allow_transmute: bool,
     ) -> InterpResult<'tcx> {
         // We do NOT compare the types for equality, because well-typed code can
         // actually "transmute" `&mut T` to `&T` in an assignment without a cast.
         let layout_compat =
-            mir_assign_valid_types(*self.tcx, self.param_env, src.layout, dest.layout);
+            mir_assign_valid_types(*self.tcx, self.param_env, src.layout(), dest.layout());
         if !allow_transmute && !layout_compat {
             span_bug!(
                 self.cur_span(),
                 "type mismatch when copying!\nsrc: {:?},\ndest: {:?}",
-                src.layout.ty,
-                dest.layout.ty,
+                src.layout().ty,
+                dest.layout().ty,
             );
         }
 
@@ -667,13 +813,13 @@ where
                 // actually sized, due to a trivially false where-clause
                 // predicate like `where Self: Sized` with `Self = dyn Trait`.
                 // See #102553 for an example of such a predicate.
-                if src.layout.is_unsized() {
-                    throw_inval!(SizeOfUnsizedType(src.layout.ty));
+                if src.layout().is_unsized() {
+                    throw_inval!(SizeOfUnsizedType(src.layout().ty));
                 }
-                if dest.layout.is_unsized() {
-                    throw_inval!(SizeOfUnsizedType(dest.layout.ty));
+                if dest.layout().is_unsized() {
+                    throw_inval!(SizeOfUnsizedType(dest.layout().ty));
                 }
-                assert_eq!(src.layout.size, dest.layout.size);
+                assert_eq!(src.layout().size, dest.layout().size);
                 // Yay, we got a value that we can write directly.
                 return if layout_compat {
                     self.write_immediate_no_validate(*src_val, dest)
@@ -682,10 +828,10 @@ where
                     // loaded using the offsets defined by `src.layout`. When we put this back into
                     // the destination, we have to use the same offsets! So (a) we make sure we
                     // write back to memory, and (b) we use `dest` *with the source layout*.
-                    let dest_mem = self.force_allocation(dest)?;
+                    let dest_mem = dest.force_mplace(self)?;
                     self.write_immediate_to_mplace_no_validate(
                         *src_val,
-                        src.layout,
+                        src.layout(),
                         dest_mem.align,
                         *dest_mem,
                     )
@@ -694,9 +840,9 @@ where
             Left(mplace) => mplace,
         };
         // Slow path, this does not fit into an immediate. Just memcpy.
-        trace!("copy_op: {:?} <- {:?}: {}", *dest, src, dest.layout.ty);
+        trace!("copy_op: {:?} <- {:?}: {}", *dest, src, dest.layout().ty);
 
-        let dest = self.force_allocation(&dest)?;
+        let dest = dest.force_mplace(self)?;
         let Some((dest_size, _)) = self.size_and_align_of_mplace(&dest)? else {
             span_bug!(self.cur_span(), "copy_op needs (dynamically) sized values")
         };
@@ -728,8 +874,8 @@ where
         place: &PlaceTy<'tcx, M::Provenance>,
     ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
         let mplace = match place.place {
-            Place::Local { frame, local } => {
-                match M::access_local_mut(self, frame, local)? {
+            Place::Local { frame, local, offset } => {
+                let whole_local = match M::access_local_mut(self, frame, local)? {
                     &mut Operand::Immediate(local_val) => {
                         // We need to make an allocation.
 
@@ -742,10 +888,11 @@ where
                             throw_unsup_format!("unsized locals are not supported");
                         }
                         let mplace = *self.allocate(local_layout, MemoryKind::Stack)?;
+                        // Preserve old value. (As an optimization, we can skip this if it was uninit.)
                         if !matches!(local_val, Immediate::Uninit) {
-                            // Preserve old value. (As an optimization, we can skip this if it was uninit.)
-                            // We don't have to validate as we can assume the local
-                            // was already valid for its type.
+                            // We don't have to validate as we can assume the local was already
+                            // valid for its type. We must not use any part of `place` here, that
+                            // could be a projection to a part of the local!
                             self.write_immediate_to_mplace_no_validate(
                                 local_val,
                                 local_layout,
@@ -753,18 +900,25 @@ where
                                 mplace,
                             )?;
                         }
-                        // Now we can call `access_mut` again, asserting it goes well,
-                        // and actually overwrite things.
+                        // Now we can call `access_mut` again, asserting it goes well, and actually
+                        // overwrite things. This points to the entire allocation, not just the part
+                        // the place refers to, i.e. we do this before we apply `offset`.
                         *M::access_local_mut(self, frame, local).unwrap() =
                             Operand::Indirect(mplace);
                         mplace
                     }
                     &mut Operand::Indirect(mplace) => mplace, // this already was an indirect local
+                };
+                if let Some(offset) = offset {
+                    whole_local.offset_with_meta_(offset, MemPlaceMeta::None, self)?
+                } else {
+                    // Preserve wide place metadata, do not call `offset`.
+                    whole_local
                 }
             }
             Place::Ptr(mplace) => mplace,
         };
-        // Return with the original layout, so that the caller can go on
+        // Return with the original layout and align, so that the caller can go on
         Ok(MPlaceTy { mplace, layout: place.layout, align: place.align })
     }
 
@@ -806,10 +960,10 @@ where
         operands: &IndexSlice<FieldIdx, mir::Operand<'tcx>>,
         dest: &PlaceTy<'tcx, M::Provenance>,
     ) -> InterpResult<'tcx> {
-        self.write_uninit(&dest)?;
+        self.write_uninit(dest)?;
         let (variant_index, variant_dest, active_field_index) = match *kind {
             mir::AggregateKind::Adt(_, variant_index, _, _, active_field_index) => {
-                let variant_dest = self.place_downcast(&dest, variant_index)?;
+                let variant_dest = self.project_downcast(dest, variant_index)?;
                 (variant_index, variant_dest, active_field_index)
             }
             _ => (FIRST_VARIANT, dest.clone(), None),
@@ -819,11 +973,11 @@ where
         }
         for (field_index, operand) in operands.iter_enumerated() {
             let field_index = active_field_index.unwrap_or(field_index);
-            let field_dest = self.place_field(&variant_dest, field_index.as_usize())?;
+            let field_dest = self.project_field(&variant_dest, field_index.as_usize())?;
             let op = self.eval_operand(operand, Some(field_dest.layout))?;
             self.copy_op(&op, &field_dest, /*allow_transmute*/ false)?;
         }
-        self.write_discriminant(variant_index, &dest)
+        self.write_discriminant(variant_index, dest)
     }
 
     pub fn raw_const_to_mplace(
@@ -859,22 +1013,24 @@ where
         Ok((mplace, vtable))
     }
 
-    /// Turn an operand with a `dyn* Trait` type into an operand with the actual dynamic type.
-    /// Aso returns the vtable.
-    pub(super) fn unpack_dyn_star(
+    /// Turn a `dyn* Trait` type into an value with the actual dynamic type.
+    /// Also returns the vtable.
+    pub(super) fn unpack_dyn_star<P: Projectable<'tcx, M::Provenance>>(
         &self,
-        op: &OpTy<'tcx, M::Provenance>,
-    ) -> InterpResult<'tcx, (OpTy<'tcx, M::Provenance>, Pointer<Option<M::Provenance>>)> {
+        val: &P,
+    ) -> InterpResult<'tcx, (P, Pointer<Option<M::Provenance>>)> {
         assert!(
-            matches!(op.layout.ty.kind(), ty::Dynamic(_, _, ty::DynStar)),
+            matches!(val.layout().ty.kind(), ty::Dynamic(_, _, ty::DynStar)),
             "`unpack_dyn_star` only makes sense on `dyn*` types"
         );
-        let data = self.operand_field(&op, 0)?;
-        let vtable = self.operand_field(&op, 1)?;
-        let vtable = self.read_pointer(&vtable)?;
+        let data = self.project_field(val, 0)?;
+        let vtable = self.project_field(val, 1)?;
+        let vtable = self.read_pointer(&vtable.to_op(self)?)?;
         let (ty, _) = self.get_ptr_vtable(vtable)?;
         let layout = self.layout_of(ty)?;
-        let data = data.transmute(layout);
+        // `data` is already the right thing but has the wrong type. So we transmute it, by
+        // projecting with offset 0.
+        let data = data.transmute(layout, self)?;
         Ok((data, vtable))
     }
 }
diff --git a/compiler/rustc_const_eval/src/interpret/projection.rs b/compiler/rustc_const_eval/src/interpret/projection.rs
index d7d31fe1887..bce43aedb69 100644
--- a/compiler/rustc_const_eval/src/interpret/projection.rs
+++ b/compiler/rustc_const_eval/src/interpret/projection.rs
@@ -7,18 +7,70 @@
 //! but we still need to do bounds checking and adjust the layout. To not duplicate that with MPlaceTy, we actually
 //! implement the logic on OpTy, and MPlaceTy calls that.
 
-use either::{Left, Right};
-
 use rustc_middle::mir;
 use rustc_middle::ty;
-use rustc_middle::ty::layout::LayoutOf;
+use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
 use rustc_middle::ty::Ty;
-use rustc_target::abi::{self, Abi, VariantIdx};
+use rustc_middle::ty::TyCtxt;
+use rustc_target::abi::HasDataLayout;
+use rustc_target::abi::Size;
+use rustc_target::abi::{self, VariantIdx};
+
+use super::{InterpCx, InterpResult, MPlaceTy, Machine, MemPlaceMeta, OpTy, Provenance, Scalar};
 
-use super::{
-    ImmTy, Immediate, InterpCx, InterpResult, MPlaceTy, Machine, MemPlaceMeta, OpTy, PlaceTy,
-    Provenance, Scalar,
-};
+/// A thing that we can project into, and that has a layout.
+pub trait Projectable<'tcx, Prov: Provenance>: Sized + std::fmt::Debug {
+    /// Get the layout.
+    fn layout(&self) -> TyAndLayout<'tcx>;
+
+    /// Get the metadata of a wide value.
+    fn meta<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
+        &self,
+        ecx: &InterpCx<'mir, 'tcx, M>,
+    ) -> InterpResult<'tcx, MemPlaceMeta<M::Provenance>>;
+
+    fn len<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
+        &self,
+        ecx: &InterpCx<'mir, 'tcx, M>,
+    ) -> InterpResult<'tcx, u64> {
+        self.meta(ecx)?.len(self.layout(), ecx)
+    }
+
+    /// Offset the value by the given amount, replacing the layout and metadata.
+    fn offset_with_meta(
+        &self,
+        offset: Size,
+        meta: MemPlaceMeta<Prov>,
+        layout: TyAndLayout<'tcx>,
+        cx: &impl HasDataLayout,
+    ) -> InterpResult<'tcx, Self>;
+
+    fn offset(
+        &self,
+        offset: Size,
+        layout: TyAndLayout<'tcx>,
+        cx: &impl HasDataLayout,
+    ) -> InterpResult<'tcx, Self> {
+        assert!(layout.is_sized());
+        self.offset_with_meta(offset, MemPlaceMeta::None, layout, cx)
+    }
+
+    fn transmute(
+        &self,
+        layout: TyAndLayout<'tcx>,
+        cx: &impl HasDataLayout,
+    ) -> InterpResult<'tcx, Self> {
+        assert_eq!(self.layout().size, layout.size);
+        self.offset_with_meta(Size::ZERO, MemPlaceMeta::None, layout, cx)
+    }
+
+    /// Convert this to an `OpTy`. This might be an irreversible transformation, but is useful for
+    /// reading from this thing.
+    fn to_op<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
+        &self,
+        ecx: &InterpCx<'mir, 'tcx, M>,
+    ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>>;
+}
 
 // FIXME: Working around https://github.com/rust-lang/rust/issues/54385
 impl<'mir, 'tcx: 'mir, Prov, M> InterpCx<'mir, 'tcx, M>
@@ -26,167 +78,83 @@ where
     Prov: Provenance + 'static,
     M: Machine<'mir, 'tcx, Provenance = Prov>,
 {
-    //# Field access
-
     /// Offset a pointer to project to a field of a struct/union. Unlike `place_field`, this is
     /// always possible without allocating, so it can take `&self`. Also return the field's layout.
-    /// This supports both struct and array fields.
+    /// This supports both struct and array fields, but not slices!
     ///
     /// This also works for arrays, but then the `usize` index type is restricting.
     /// For indexing into arrays, use `mplace_index`.
-    pub fn mplace_field(
+    pub fn project_field<P: Projectable<'tcx, M::Provenance>>(
         &self,
-        base: &MPlaceTy<'tcx, M::Provenance>,
+        base: &P,
         field: usize,
-    ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
-        let offset = base.layout.fields.offset(field);
-        let field_layout = base.layout.field(self, field);
+    ) -> InterpResult<'tcx, P> {
+        // Slices nominally have length 0, so they will panic somewhere in `fields.offset`.
+        debug_assert!(
+            !matches!(base.layout().ty.kind(), ty::Slice(..)),
+            "`field` projection called on a slice -- call `index` projection instead"
+        );
+        let offset = base.layout().fields.offset(field);
+        let field_layout = base.layout().field(self, field);
 
         // Offset may need adjustment for unsized fields.
         let (meta, offset) = if field_layout.is_unsized() {
+            if base.layout().is_sized() {
+                // An unsized field of a sized type? Sure...
+                // But const-prop actually feeds us such nonsense MIR!
+                throw_inval!(ConstPropNonsense);
+            }
+            let base_meta = base.meta(self)?;
             // Re-use parent metadata to determine dynamic field layout.
             // With custom DSTS, this *will* execute user-defined code, but the same
             // happens at run-time so that's okay.
-            match self.size_and_align_of(&base.meta, &field_layout)? {
-                Some((_, align)) => (base.meta, offset.align_to(align)),
+            match self.size_and_align_of(&base_meta, &field_layout)? {
+                Some((_, align)) => (base_meta, offset.align_to(align)),
                 None => {
                     // For unsized types with an extern type tail we perform no adjustments.
                     // NOTE: keep this in sync with `PlaceRef::project_field` in the codegen backend.
-                    assert!(matches!(base.meta, MemPlaceMeta::None));
-                    (base.meta, offset)
+                    assert!(matches!(base_meta, MemPlaceMeta::None));
+                    (base_meta, offset)
                 }
             }
         } else {
-            // base.meta could be present; we might be accessing a sized field of an unsized
+            // base_meta could be present; we might be accessing a sized field of an unsized
             // struct.
             (MemPlaceMeta::None, offset)
         };
 
-        // We do not look at `base.layout.align` nor `field_layout.align`, unlike
-        // codegen -- mostly to see if we can get away with that
         base.offset_with_meta(offset, meta, field_layout, self)
     }
 
-    /// Gets the place of a field inside the place, and also the field's type.
-    /// Just a convenience function, but used quite a bit.
-    /// This is the only projection that might have a side-effect: We cannot project
-    /// into the field of a local `ScalarPair`, we have to first allocate it.
-    pub fn place_field(
-        &mut self,
-        base: &PlaceTy<'tcx, M::Provenance>,
-        field: usize,
-    ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> {
-        // FIXME: We could try to be smarter and avoid allocation for fields that span the
-        // entire place.
-        let base = self.force_allocation(base)?;
-        Ok(self.mplace_field(&base, field)?.into())
-    }
-
-    pub fn operand_field(
+    /// Downcasting to an enum variant.
+    pub fn project_downcast<P: Projectable<'tcx, M::Provenance>>(
         &self,
-        base: &OpTy<'tcx, M::Provenance>,
-        field: usize,
-    ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
-        let base = match base.as_mplace_or_imm() {
-            Left(ref mplace) => {
-                // We can reuse the mplace field computation logic for indirect operands.
-                let field = self.mplace_field(mplace, field)?;
-                return Ok(field.into());
-            }
-            Right(value) => value,
-        };
-
-        let field_layout = base.layout.field(self, field);
-        let offset = base.layout.fields.offset(field);
-        // This makes several assumptions about what layouts we will encounter; we match what
-        // codegen does as good as we can (see `extract_field` in `rustc_codegen_ssa/src/mir/operand.rs`).
-        let field_val: Immediate<_> = match (*base, base.layout.abi) {
-            // if the entire value is uninit, then so is the field (can happen in ConstProp)
-            (Immediate::Uninit, _) => Immediate::Uninit,
-            // the field contains no information, can be left uninit
-            _ if field_layout.is_zst() => Immediate::Uninit,
-            // the field covers the entire type
-            _ if field_layout.size == base.layout.size => {
-                assert!(match (base.layout.abi, field_layout.abi) {
-                    (Abi::Scalar(..), Abi::Scalar(..)) => true,
-                    (Abi::ScalarPair(..), Abi::ScalarPair(..)) => true,
-                    _ => false,
-                });
-                assert!(offset.bytes() == 0);
-                *base
-            }
-            // extract fields from types with `ScalarPair` ABI
-            (Immediate::ScalarPair(a_val, b_val), Abi::ScalarPair(a, b)) => {
-                assert!(matches!(field_layout.abi, Abi::Scalar(..)));
-                Immediate::from(if offset.bytes() == 0 {
-                    debug_assert_eq!(field_layout.size, a.size(self));
-                    a_val
-                } else {
-                    debug_assert_eq!(offset, a.size(self).align_to(b.align(self).abi));
-                    debug_assert_eq!(field_layout.size, b.size(self));
-                    b_val
-                })
-            }
-            // everything else is a bug
-            _ => span_bug!(
-                self.cur_span(),
-                "invalid field access on immediate {}, layout {:#?}",
-                base,
-                base.layout
-            ),
-        };
-
-        Ok(ImmTy::from_immediate(field_val, field_layout).into())
-    }
-
-    //# Downcasting
-
-    pub fn mplace_downcast(
-        &self,
-        base: &MPlaceTy<'tcx, M::Provenance>,
+        base: &P,
         variant: VariantIdx,
-    ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
+    ) -> InterpResult<'tcx, P> {
+        assert!(!base.meta(self)?.has_meta());
         // Downcasts only change the layout.
         // (In particular, no check about whether this is even the active variant -- that's by design,
         // see https://github.com/rust-lang/rust/issues/93688#issuecomment-1032929496.)
-        assert!(!base.meta.has_meta());
-        let mut base = *base;
-        base.layout = base.layout.for_variant(self, variant);
-        Ok(base)
-    }
-
-    pub fn place_downcast(
-        &self,
-        base: &PlaceTy<'tcx, M::Provenance>,
-        variant: VariantIdx,
-    ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> {
-        // Downcast just changes the layout
-        let mut base = base.clone();
-        base.layout = base.layout.for_variant(self, variant);
-        Ok(base)
-    }
-
-    pub fn operand_downcast(
-        &self,
-        base: &OpTy<'tcx, M::Provenance>,
-        variant: VariantIdx,
-    ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
-        // Downcast just changes the layout
-        let mut base = base.clone();
-        base.layout = base.layout.for_variant(self, variant);
-        Ok(base)
+        // So we just "offset" by 0.
+        let layout = base.layout().for_variant(self, variant);
+        if layout.abi.is_uninhabited() {
+            // `read_discriminant` should have excluded uninhabited variants... but ConstProp calls
+            // us on dead code.
+            throw_inval!(ConstPropNonsense)
+        }
+        // This cannot be `transmute` as variants *can* have a smaller size than the entire enum.
+        base.offset(Size::ZERO, layout, self)
     }
 
-    //# Slice indexing
-
-    #[inline(always)]
-    pub fn operand_index(
+    /// Compute the offset and field layout for accessing the given index.
+    pub fn project_index<P: Projectable<'tcx, M::Provenance>>(
         &self,
-        base: &OpTy<'tcx, M::Provenance>,
+        base: &P,
         index: u64,
-    ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
+    ) -> InterpResult<'tcx, P> {
         // Not using the layout method because we want to compute on u64
-        match base.layout.fields {
+        let (offset, field_layout) = match base.layout().fields {
             abi::FieldsShape::Array { stride, count: _ } => {
                 // `count` is nonsense for slices, use the dynamic length instead.
                 let len = base.len(self)?;
@@ -196,63 +164,26 @@ where
                 }
                 let offset = stride * index; // `Size` multiplication
                 // All fields have the same layout.
-                let field_layout = base.layout.field(self, 0);
-                base.offset(offset, field_layout, self)
+                let field_layout = base.layout().field(self, 0);
+                (offset, field_layout)
             }
             _ => span_bug!(
                 self.cur_span(),
                 "`mplace_index` called on non-array type {:?}",
-                base.layout.ty
+                base.layout().ty
             ),
-        }
-    }
-
-    /// Iterates over all fields of an array. Much more efficient than doing the
-    /// same by repeatedly calling `operand_index`.
-    pub fn operand_array_fields<'a>(
-        &self,
-        base: &'a OpTy<'tcx, Prov>,
-    ) -> InterpResult<'tcx, impl Iterator<Item = InterpResult<'tcx, OpTy<'tcx, Prov>>> + 'a> {
-        let len = base.len(self)?; // also asserts that we have a type where this makes sense
-        let abi::FieldsShape::Array { stride, .. } = base.layout.fields else {
-            span_bug!(self.cur_span(), "operand_array_fields: expected an array layout");
         };
-        let field_layout = base.layout.field(self, 0);
-        let dl = &self.tcx.data_layout;
-        // `Size` multiplication
-        Ok((0..len).map(move |i| base.offset(stride * i, field_layout, dl)))
-    }
-
-    /// Index into an array.
-    pub fn mplace_index(
-        &self,
-        base: &MPlaceTy<'tcx, M::Provenance>,
-        index: u64,
-    ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
-        Ok(self.operand_index(&base.into(), index)?.assert_mem_place())
-    }
 
-    pub fn place_index(
-        &mut self,
-        base: &PlaceTy<'tcx, M::Provenance>,
-        index: u64,
-    ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> {
-        // There's not a lot we can do here, since we cannot have a place to a part of a local. If
-        // we are accessing the only element of a 1-element array, it's still the entire local...
-        // that doesn't seem worth it.
-        let base = self.force_allocation(base)?;
-        Ok(self.mplace_index(&base, index)?.into())
+        base.offset(offset, field_layout, self)
     }
 
-    //# ConstantIndex support
-
-    fn operand_constant_index(
+    fn project_constant_index<P: Projectable<'tcx, M::Provenance>>(
         &self,
-        base: &OpTy<'tcx, M::Provenance>,
+        base: &P,
         offset: u64,
         min_length: u64,
         from_end: bool,
-    ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
+    ) -> InterpResult<'tcx, P> {
         let n = base.len(self)?;
         if n < min_length {
             // This can only be reached in ConstProp and non-rustc-MIR.
@@ -267,32 +198,38 @@ where
             offset
         };
 
-        self.operand_index(base, index)
+        self.project_index(base, index)
     }
 
-    fn place_constant_index(
-        &mut self,
-        base: &PlaceTy<'tcx, M::Provenance>,
-        offset: u64,
-        min_length: u64,
-        from_end: bool,
-    ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> {
-        let base = self.force_allocation(base)?;
-        Ok(self
-            .operand_constant_index(&base.into(), offset, min_length, from_end)?
-            .assert_mem_place()
-            .into())
+    /// Iterates over all fields of an array. Much more efficient than doing the
+    /// same by repeatedly calling `operand_index`.
+    pub fn project_array_fields<'a, P: Projectable<'tcx, M::Provenance>>(
+        &self,
+        base: &'a P,
+    ) -> InterpResult<'tcx, impl Iterator<Item = InterpResult<'tcx, P>> + 'a>
+    where
+        'tcx: 'a,
+    {
+        let abi::FieldsShape::Array { stride, .. } = base.layout().fields else {
+            span_bug!(self.cur_span(), "operand_array_fields: expected an array layout");
+        };
+        let len = base.len(self)?;
+        let field_layout = base.layout().field(self, 0);
+        let tcx: TyCtxt<'tcx> = *self.tcx;
+        // `Size` multiplication
+        Ok((0..len).map(move |i| {
+            base.offset_with_meta(stride * i, MemPlaceMeta::None, field_layout, &tcx)
+        }))
     }
 
-    //# Subslicing
-
-    fn operand_subslice(
+    /// Subslicing
+    fn project_subslice<P: Projectable<'tcx, M::Provenance>>(
         &self,
-        base: &OpTy<'tcx, M::Provenance>,
+        base: &P,
         from: u64,
         to: u64,
         from_end: bool,
-    ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
+    ) -> InterpResult<'tcx, P> {
         let len = base.len(self)?; // also asserts that we have a type where this makes sense
         let actual_to = if from_end {
             if from.checked_add(to).map_or(true, |to| to > len) {
@@ -306,16 +243,20 @@ where
 
         // Not using layout method because that works with usize, and does not work with slices
         // (that have count 0 in their layout).
-        let from_offset = match base.layout.fields {
+        let from_offset = match base.layout().fields {
             abi::FieldsShape::Array { stride, .. } => stride * from, // `Size` multiplication is checked
             _ => {
-                span_bug!(self.cur_span(), "unexpected layout of index access: {:#?}", base.layout)
+                span_bug!(
+                    self.cur_span(),
+                    "unexpected layout of index access: {:#?}",
+                    base.layout()
+                )
             }
         };
 
         // Compute meta and new layout
         let inner_len = actual_to.checked_sub(from).unwrap();
-        let (meta, ty) = match base.layout.ty.kind() {
+        let (meta, ty) = match base.layout().ty.kind() {
             // It is not nice to match on the type, but that seems to be the only way to
             // implement this.
             ty::Array(inner, _) => {
@@ -323,85 +264,43 @@ where
             }
             ty::Slice(..) => {
                 let len = Scalar::from_target_usize(inner_len, self);
-                (MemPlaceMeta::Meta(len), base.layout.ty)
+                (MemPlaceMeta::Meta(len), base.layout().ty)
             }
             _ => {
-                span_bug!(self.cur_span(), "cannot subslice non-array type: `{:?}`", base.layout.ty)
+                span_bug!(
+                    self.cur_span(),
+                    "cannot subslice non-array type: `{:?}`",
+                    base.layout().ty
+                )
             }
         };
         let layout = self.layout_of(ty)?;
-        base.offset_with_meta(from_offset, meta, layout, self)
-    }
-
-    pub fn place_subslice(
-        &mut self,
-        base: &PlaceTy<'tcx, M::Provenance>,
-        from: u64,
-        to: u64,
-        from_end: bool,
-    ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> {
-        let base = self.force_allocation(base)?;
-        Ok(self.operand_subslice(&base.into(), from, to, from_end)?.assert_mem_place().into())
-    }
-
-    //# Applying a general projection
 
-    /// Projects into a place.
-    #[instrument(skip(self), level = "trace")]
-    pub fn place_projection(
-        &mut self,
-        base: &PlaceTy<'tcx, M::Provenance>,
-        proj_elem: mir::PlaceElem<'tcx>,
-    ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> {
-        use rustc_middle::mir::ProjectionElem::*;
-        Ok(match proj_elem {
-            OpaqueCast(ty) => {
-                let mut place = base.clone();
-                place.layout = self.layout_of(ty)?;
-                place
-            }
-            Field(field, _) => self.place_field(base, field.index())?,
-            Downcast(_, variant) => self.place_downcast(base, variant)?,
-            Deref => self.deref_operand(&self.place_to_op(base)?)?.into(),
-            Index(local) => {
-                let layout = self.layout_of(self.tcx.types.usize)?;
-                let n = self.local_to_op(self.frame(), local, Some(layout))?;
-                let n = self.read_target_usize(&n)?;
-                self.place_index(base, n)?
-            }
-            ConstantIndex { offset, min_length, from_end } => {
-                self.place_constant_index(base, offset, min_length, from_end)?
-            }
-            Subslice { from, to, from_end } => self.place_subslice(base, from, to, from_end)?,
-        })
+        base.offset_with_meta(from_offset, meta, layout, self)
     }
 
+    /// Applying a general projection
     #[instrument(skip(self), level = "trace")]
-    pub fn operand_projection(
-        &self,
-        base: &OpTy<'tcx, M::Provenance>,
-        proj_elem: mir::PlaceElem<'tcx>,
-    ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
+    pub fn project<P>(&self, base: &P, proj_elem: mir::PlaceElem<'tcx>) -> InterpResult<'tcx, P>
+    where
+        P: Projectable<'tcx, M::Provenance> + From<MPlaceTy<'tcx, M::Provenance>> + std::fmt::Debug,
+    {
         use rustc_middle::mir::ProjectionElem::*;
         Ok(match proj_elem {
-            OpaqueCast(ty) => {
-                let mut op = base.clone();
-                op.layout = self.layout_of(ty)?;
-                op
-            }
-            Field(field, _) => self.operand_field(base, field.index())?,
-            Downcast(_, variant) => self.operand_downcast(base, variant)?,
-            Deref => self.deref_operand(base)?.into(),
+            OpaqueCast(ty) => base.transmute(self.layout_of(ty)?, self)?,
+            Field(field, _) => self.project_field(base, field.index())?,
+            Downcast(_, variant) => self.project_downcast(base, variant)?,
+            Deref => self.deref_operand(&base.to_op(self)?)?.into(),
             Index(local) => {
                 let layout = self.layout_of(self.tcx.types.usize)?;
                 let n = self.local_to_op(self.frame(), local, Some(layout))?;
                 let n = self.read_target_usize(&n)?;
-                self.operand_index(base, n)?
+                self.project_index(base, n)?
             }
             ConstantIndex { offset, min_length, from_end } => {
-                self.operand_constant_index(base, offset, min_length, from_end)?
+                self.project_constant_index(base, offset, min_length, from_end)?
             }
-            Subslice { from, to, from_end } => self.operand_subslice(base, from, to, from_end)?,
+            Subslice { from, to, from_end } => self.project_subslice(base, from, to, from_end)?,
         })
     }
 }
diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs
index 619da8abb7d..91341ddacd1 100644
--- a/compiler/rustc_const_eval/src/interpret/step.rs
+++ b/compiler/rustc_const_eval/src/interpret/step.rs
@@ -8,7 +8,7 @@ use rustc_middle::mir;
 use rustc_middle::mir::interpret::{InterpResult, Scalar};
 use rustc_middle::ty::layout::LayoutOf;
 
-use super::{ImmTy, InterpCx, Machine};
+use super::{ImmTy, InterpCx, Machine, Projectable};
 use crate::util;
 
 impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
@@ -178,7 +178,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 // The operand always has the same type as the result.
                 let val = self.read_immediate(&self.eval_operand(operand, Some(dest.layout))?)?;
                 let val = self.unary_op(un_op, &val)?;
-                assert_eq!(val.layout, dest.layout, "layout mismatch for result of {:?}", un_op);
+                assert_eq!(val.layout, dest.layout, "layout mismatch for result of {un_op:?}");
                 self.write_immediate(*val, &dest)?;
             }
 
@@ -197,8 +197,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                     self.get_place_alloc_mut(&dest)?;
                 } else {
                     // Write the src to the first element.
-                    let first = self.mplace_field(&dest, 0)?;
-                    self.copy_op(&src, &first.into(), /*allow_transmute*/ false)?;
+                    let first = self.project_index(&dest, 0)?;
+                    self.copy_op(&src, &first, /*allow_transmute*/ false)?;
 
                     // This is performance-sensitive code for big static/const arrays! So we
                     // avoid writing each operand individually and instead just make many copies
@@ -302,8 +302,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
 
             Discriminant(place) => {
                 let op = self.eval_place_to_op(place, None)?;
-                let discr_val = self.read_discriminant(&op)?.0;
-                self.write_scalar(discr_val, &dest)?;
+                let variant = self.read_discriminant(&op)?;
+                let discr = self.discriminant_for_variant(op.layout, variant)?;
+                self.write_scalar(discr, &dest)?;
             }
         }
 
diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs
index 7964c6be008..d0191ea978a 100644
--- a/compiler/rustc_const_eval/src/interpret/terminator.rs
+++ b/compiler/rustc_const_eval/src/interpret/terminator.rs
@@ -60,13 +60,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     }
 
     pub fn fn_arg_field(
-        &mut self,
+        &self,
         arg: &FnArg<'tcx, M::Provenance>,
         field: usize,
     ) -> InterpResult<'tcx, FnArg<'tcx, M::Provenance>> {
         Ok(match arg {
-            FnArg::Copy(op) => FnArg::Copy(self.operand_field(op, field)?),
-            FnArg::InPlace(place) => FnArg::InPlace(self.place_field(place, field)?),
+            FnArg::Copy(op) => FnArg::Copy(self.project_field(op, field)?),
+            FnArg::InPlace(place) => FnArg::InPlace(self.project_field(place, field)?),
         })
     }
 
@@ -239,7 +239,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
 
     /// Evaluate the arguments of a function call
     pub(super) fn eval_fn_call_arguments(
-        &mut self,
+        &self,
         ops: &[mir::Operand<'tcx>],
     ) -> InterpResult<'tcx, Vec<FnArg<'tcx, M::Provenance>>> {
         ops.iter()
@@ -382,12 +382,16 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             // This all has to be in memory, there are no immediate unsized values.
             let src = caller_arg_copy.assert_mem_place();
             // The destination cannot be one of these "spread args".
-            let (dest_frame, dest_local) = callee_arg.assert_local();
+            let (dest_frame, dest_local, dest_offset) = callee_arg
+                .as_mplace_or_local()
+                .right()
+                .expect("callee fn arguments must be locals");
             // We are just initializing things, so there can't be anything here yet.
             assert!(matches!(
                 *self.local_to_op(&self.stack()[dest_frame], dest_local, None)?,
                 Operand::Immediate(Immediate::Uninit)
             ));
+            assert_eq!(dest_offset, None);
             // Allocate enough memory to hold `src`.
             let Some((size, align)) = self.size_and_align_of_mplace(&src)? else {
                 span_bug!(
@@ -595,7 +599,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                         if Some(local) == body.spread_arg {
                             // Must be a tuple
                             for i in 0..dest.layout.fields.count() {
-                                let dest = self.place_field(&dest, i)?;
+                                let dest = self.project_field(&dest, i)?;
                                 let callee_abi = callee_args_abis.next().unwrap();
                                 self.pass_argument(&mut caller_args, callee_abi, &dest)?;
                             }
@@ -630,7 +634,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                     // Ensure the return place is aligned and dereferenceable, and protect it for
                     // in-place return value passing.
                     if let Either::Left(mplace) = destination.as_mplace_or_local() {
-                        self.check_mplace(mplace)?;
+                        self.check_mplace(&mplace)?;
                     } else {
                         // Nothing to do for locals, they are always properly allocated and aligned.
                     }
@@ -677,7 +681,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                             // Not there yet, search for the only non-ZST field.
                             let mut non_zst_field = None;
                             for i in 0..receiver.layout.fields.count() {
-                                let field = self.operand_field(&receiver, i)?;
+                                let field = self.project_field(&receiver, i)?;
                                 let zst =
                                     field.layout.is_zst() && field.layout.align.abi.bytes() == 1;
                                 if !zst {
@@ -703,12 +707,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 let (vptr, dyn_ty, adjusted_receiver) = if let ty::Dynamic(data, _, ty::DynStar) =
                     receiver_place.layout.ty.kind()
                 {
-                    let (recv, vptr) = self.unpack_dyn_star(&receiver_place.into())?;
+                    let (recv, vptr) = self.unpack_dyn_star(&receiver_place)?;
                     let (dyn_ty, dyn_trait) = self.get_ptr_vtable(vptr)?;
                     if dyn_trait != data.principal() {
                         throw_ub_custom!(fluent::const_eval_dyn_star_call_vtable_mismatch);
                     }
-                    let recv = recv.assert_mem_place(); // we passed an MPlaceTy to `unpack_dyn_star` so we definitely still have one
 
                     (vptr, dyn_ty, recv.ptr)
                 } else {
@@ -836,7 +839,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             }
             ty::Dynamic(_, _, ty::DynStar) => {
                 // Dropping a `dyn*`. Need to find actual drop fn.
-                self.unpack_dyn_star(&place.into())?.0.assert_mem_place()
+                self.unpack_dyn_star(&place)?.0
             }
             _ => {
                 debug_assert_eq!(
diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs
index 108394d224b..aee95f70bc2 100644
--- a/compiler/rustc_const_eval/src/interpret/validity.rs
+++ b/compiler/rustc_const_eval/src/interpret/validity.rs
@@ -19,7 +19,9 @@ use rustc_middle::mir::interpret::{
 use rustc_middle::ty;
 use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
 use rustc_span::symbol::{sym, Symbol};
-use rustc_target::abi::{Abi, FieldIdx, Scalar as ScalarAbi, Size, VariantIdx, Variants};
+use rustc_target::abi::{
+    Abi, FieldIdx, Scalar as ScalarAbi, Size, VariantIdx, Variants, WrappingRange,
+};
 
 use std::hash::Hash;
 
@@ -27,7 +29,7 @@ use std::hash::Hash;
 use super::UndefinedBehaviorInfo::*;
 use super::{
     AllocId, CheckInAllocMsg, GlobalAlloc, ImmTy, Immediate, InterpCx, InterpResult, MPlaceTy,
-    Machine, MemPlaceMeta, OpTy, Pointer, Scalar, ValueVisitor,
+    Machine, MemPlaceMeta, OpTy, Pointer, Projectable, Scalar, ValueVisitor,
 };
 
 macro_rules! throw_validation_failure {
@@ -134,19 +136,19 @@ pub struct RefTracking<T, PATH = ()> {
     pub todo: Vec<(T, PATH)>,
 }
 
-impl<T: Copy + Eq + Hash + std::fmt::Debug, PATH: Default> RefTracking<T, PATH> {
+impl<T: Clone + Eq + Hash + std::fmt::Debug, PATH: Default> RefTracking<T, PATH> {
     pub fn empty() -> Self {
         RefTracking { seen: FxHashSet::default(), todo: vec![] }
     }
     pub fn new(op: T) -> Self {
         let mut ref_tracking_for_consts =
-            RefTracking { seen: FxHashSet::default(), todo: vec![(op, PATH::default())] };
+            RefTracking { seen: FxHashSet::default(), todo: vec![(op.clone(), PATH::default())] };
         ref_tracking_for_consts.seen.insert(op);
         ref_tracking_for_consts
     }
 
     pub fn track(&mut self, op: T, path: impl FnOnce() -> PATH) {
-        if self.seen.insert(op) {
+        if self.seen.insert(op.clone()) {
             trace!("Recursing below ptr {:#?}", op);
             let path = path();
             // Remember to come back to this later.
@@ -162,14 +164,14 @@ fn write_path(out: &mut String, path: &[PathElem]) {
 
     for elem in path.iter() {
         match elem {
-            Field(name) => write!(out, ".{}", name),
+            Field(name) => write!(out, ".{name}"),
             EnumTag => write!(out, ".<enum-tag>"),
-            Variant(name) => write!(out, ".<enum-variant({})>", name),
+            Variant(name) => write!(out, ".<enum-variant({name})>"),
             GeneratorTag => write!(out, ".<generator-tag>"),
             GeneratorState(idx) => write!(out, ".<generator-state({})>", idx.index()),
-            CapturedVar(name) => write!(out, ".<captured-var({})>", name),
-            TupleElem(idx) => write!(out, ".{}", idx),
-            ArrayElem(idx) => write!(out, "[{}]", idx),
+            CapturedVar(name) => write!(out, ".<captured-var({name})>"),
+            TupleElem(idx) => write!(out, ".{idx}"),
+            ArrayElem(idx) => write!(out, "[{idx}]"),
             // `.<deref>` does not match Rust syntax, but it is more readable for long paths -- and
             // some of the other items here also are not Rust syntax. Actually we can't
             // even use the usual syntax because we are just showing the projections,
@@ -460,6 +462,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
 
     /// Check if this is a value of primitive type, and if yes check the validity of the value
     /// at that type. Return `true` if the type is indeed primitive.
+    ///
+    /// Note that not all of these have `FieldsShape::Primitive`, e.g. wide references.
     fn try_visit_primitive(
         &mut self,
         value: &OpTy<'tcx, M::Provenance>,
@@ -552,7 +556,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
                     // FIXME: Check if the signature matches
                 } else {
                     // Otherwise (for standalone Miri), we have to still check it to be non-null.
-                    if self.ecx.ptr_scalar_range(value)?.contains(&0) {
+                    if self.ecx.scalar_may_be_null(value)? {
                         throw_validation_failure!(self.path, NullFnPtr);
                     }
                 }
@@ -593,36 +597,46 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
     ) -> InterpResult<'tcx> {
         let size = scalar_layout.size(self.ecx);
         let valid_range = scalar_layout.valid_range(self.ecx);
+        let WrappingRange { start, end } = valid_range;
         let max_value = size.unsigned_int_max();
-        assert!(valid_range.end <= max_value);
-        match scalar.try_to_int() {
-            Ok(int) => {
-                // We have an explicit int: check it against the valid range.
-                let bits = int.assert_bits(size);
-                if valid_range.contains(bits) {
-                    Ok(())
-                } else {
-                    throw_validation_failure!(
-                        self.path,
-                        OutOfRange { value: format!("{bits}"), range: valid_range, max_value }
-                    )
-                }
-            }
+        assert!(end <= max_value);
+        let bits = match scalar.try_to_int() {
+            Ok(int) => int.assert_bits(size),
             Err(_) => {
                 // So this is a pointer then, and casting to an int failed.
                 // Can only happen during CTFE.
-                // We check if the possible addresses are compatible with the valid range.
-                let range = self.ecx.ptr_scalar_range(scalar)?;
-                if valid_range.contains_range(range) {
-                    Ok(())
+                // We support 2 kinds of ranges here: full range, and excluding zero.
+                if start == 1 && end == max_value {
+                    // Only null is the niche. So make sure the ptr is NOT null.
+                    if self.ecx.scalar_may_be_null(scalar)? {
+                        throw_validation_failure!(
+                            self.path,
+                            NullablePtrOutOfRange { range: valid_range, max_value }
+                        )
+                    } else {
+                        return Ok(());
+                    }
+                } else if scalar_layout.is_always_valid(self.ecx) {
+                    // Easy. (This is reachable if `enforce_number_validity` is set.)
+                    return Ok(());
                 } else {
-                    // Reject conservatively, because the pointer *could* have a bad value.
+                    // Conservatively, we reject, because the pointer *could* have a bad
+                    // value.
                     throw_validation_failure!(
                         self.path,
                         PtrOutOfRange { range: valid_range, max_value }
                     )
                 }
             }
+        };
+        // Now compare.
+        if valid_range.contains(bits) {
+            Ok(())
+        } else {
+            throw_validation_failure!(
+                self.path,
+                OutOfRange { value: format!("{bits}"), range: valid_range, max_value }
+            )
         }
     }
 }
@@ -648,10 +662,9 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
                 InvalidTag(val) => InvalidEnumTag {
                     value: format!("{val:x}"),
                 },
-
+                UninhabitedEnumVariantRead(_) => UninhabitedEnumTag,
                 InvalidUninitBytes(None) => UninitEnumTag,
-            )
-            .1)
+            ))
         })
     }
 
@@ -721,60 +734,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
             }
         }
 
-        // Recursively walk the value at its type.
-        self.walk_value(op)?;
-
-        // *After* all of this, check the ABI. We need to check the ABI to handle
-        // types like `NonNull` where the `Scalar` info is more restrictive than what
-        // the fields say (`rustc_layout_scalar_valid_range_start`).
-        // But in most cases, this will just propagate what the fields say,
-        // and then we want the error to point at the field -- so, first recurse,
-        // then check ABI.
-        //
-        // FIXME: We could avoid some redundant checks here. For newtypes wrapping
-        // scalars, we do the same check on every "level" (e.g., first we check
-        // MyNewtype and then the scalar in there).
-        match op.layout.abi {
-            Abi::Uninhabited => {
-                let ty = op.layout.ty;
-                throw_validation_failure!(self.path, UninhabitedVal { ty });
-            }
-            Abi::Scalar(scalar_layout) => {
-                if !scalar_layout.is_uninit_valid() {
-                    // There is something to check here.
-                    let scalar = self.read_scalar(op, ExpectedKind::InitScalar)?;
-                    self.visit_scalar(scalar, scalar_layout)?;
-                }
-            }
-            Abi::ScalarPair(a_layout, b_layout) => {
-                // We can only proceed if *both* scalars need to be initialized.
-                // FIXME: find a way to also check ScalarPair when one side can be uninit but
-                // the other must be init.
-                if !a_layout.is_uninit_valid() && !b_layout.is_uninit_valid() {
-                    let (a, b) =
-                        self.read_immediate(op, ExpectedKind::InitScalar)?.to_scalar_pair();
-                    self.visit_scalar(a, a_layout)?;
-                    self.visit_scalar(b, b_layout)?;
-                }
-            }
-            Abi::Vector { .. } => {
-                // No checks here, we assume layout computation gets this right.
-                // (This is harder to check since Miri does not represent these as `Immediate`. We
-                // also cannot use field projections since this might be a newtype around a vector.)
-            }
-            Abi::Aggregate { .. } => {
-                // Nothing to do.
-            }
-        }
-
-        Ok(())
-    }
-
-    fn visit_aggregate(
-        &mut self,
-        op: &OpTy<'tcx, M::Provenance>,
-        fields: impl Iterator<Item = InterpResult<'tcx, Self::V>>,
-    ) -> InterpResult<'tcx> {
+        // Recursively walk the value at its type. Apply optimizations for some large types.
         match op.layout.ty.kind() {
             ty::Str => {
                 let mplace = op.assert_mem_place(); // strings are unsized and hence never immediate
@@ -862,12 +822,58 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
             // ZST type, so either validation fails for all elements or none.
             ty::Array(tys, ..) | ty::Slice(tys) if self.ecx.layout_of(*tys)?.is_zst() => {
                 // Validate just the first element (if any).
-                self.walk_aggregate(op, fields.take(1))?
+                if op.len(self.ecx)? > 0 {
+                    self.visit_field(op, 0, &self.ecx.project_index(op, 0)?)?;
+                }
             }
             _ => {
-                self.walk_aggregate(op, fields)? // default handler
+                self.walk_value(op)?; // default handler
+            }
+        }
+
+        // *After* all of this, check the ABI. We need to check the ABI to handle
+        // types like `NonNull` where the `Scalar` info is more restrictive than what
+        // the fields say (`rustc_layout_scalar_valid_range_start`).
+        // But in most cases, this will just propagate what the fields say,
+        // and then we want the error to point at the field -- so, first recurse,
+        // then check ABI.
+        //
+        // FIXME: We could avoid some redundant checks here. For newtypes wrapping
+        // scalars, we do the same check on every "level" (e.g., first we check
+        // MyNewtype and then the scalar in there).
+        match op.layout.abi {
+            Abi::Uninhabited => {
+                let ty = op.layout.ty;
+                throw_validation_failure!(self.path, UninhabitedVal { ty });
+            }
+            Abi::Scalar(scalar_layout) => {
+                if !scalar_layout.is_uninit_valid() {
+                    // There is something to check here.
+                    let scalar = self.read_scalar(op, ExpectedKind::InitScalar)?;
+                    self.visit_scalar(scalar, scalar_layout)?;
+                }
+            }
+            Abi::ScalarPair(a_layout, b_layout) => {
+                // We can only proceed if *both* scalars need to be initialized.
+                // FIXME: find a way to also check ScalarPair when one side can be uninit but
+                // the other must be init.
+                if !a_layout.is_uninit_valid() && !b_layout.is_uninit_valid() {
+                    let (a, b) =
+                        self.read_immediate(op, ExpectedKind::InitScalar)?.to_scalar_pair();
+                    self.visit_scalar(a, a_layout)?;
+                    self.visit_scalar(b, b_layout)?;
+                }
+            }
+            Abi::Vector { .. } => {
+                // No checks here, we assume layout computation gets this right.
+                // (This is harder to check since Miri does not represent these as `Immediate`. We
+                // also cannot use field projections since this might be a newtype around a vector.)
+            }
+            Abi::Aggregate { .. } => {
+                // Nothing to do.
             }
         }
+
         Ok(())
     }
 }
diff --git a/compiler/rustc_const_eval/src/interpret/visitor.rs b/compiler/rustc_const_eval/src/interpret/visitor.rs
index 879ae198f7e..531e2bd3ee0 100644
--- a/compiler/rustc_const_eval/src/interpret/visitor.rs
+++ b/compiler/rustc_const_eval/src/interpret/visitor.rs
@@ -1,544 +1,202 @@
 //! Visitor for a run-time value with a given layout: Traverse enums, structs and other compound
 //! types until we arrive at the leaves, with custom handling for primitive types.
 
+use rustc_index::IndexVec;
 use rustc_middle::mir::interpret::InterpResult;
 use rustc_middle::ty;
-use rustc_middle::ty::layout::TyAndLayout;
+use rustc_target::abi::FieldIdx;
 use rustc_target::abi::{FieldsShape, VariantIdx, Variants};
 
 use std::num::NonZeroUsize;
 
-use super::{InterpCx, MPlaceTy, Machine, OpTy, PlaceTy};
+use super::{InterpCx, MPlaceTy, Machine, Projectable};
 
-/// A thing that we can project into, and that has a layout.
-/// This wouldn't have to depend on `Machine` but with the current type inference,
-/// that's just more convenient to work with (avoids repeating all the `Machine` bounds).
-pub trait Value<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>>: Sized {
-    /// Gets this value's layout.
-    fn layout(&self) -> TyAndLayout<'tcx>;
+/// How to traverse a value and what to do when we are at the leaves.
+pub trait ValueVisitor<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>>: Sized {
+    type V: Projectable<'tcx, M::Provenance> + From<MPlaceTy<'tcx, M::Provenance>>;
 
-    /// Makes this into an `OpTy`, in a cheap way that is good for reading.
-    fn to_op_for_read(
-        &self,
-        ecx: &InterpCx<'mir, 'tcx, M>,
-    ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>>;
-
-    /// Makes this into an `OpTy`, in a potentially more expensive way that is good for projections.
-    fn to_op_for_proj(
-        &self,
-        ecx: &InterpCx<'mir, 'tcx, M>,
-    ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
-        self.to_op_for_read(ecx)
-    }
-
-    /// Creates this from an `OpTy`.
-    ///
-    /// If `to_op_for_proj` only ever produces `Indirect` operands, then this one is definitely `Indirect`.
-    fn from_op(op: &OpTy<'tcx, M::Provenance>) -> Self;
-
-    /// Projects to the given enum variant.
-    fn project_downcast(
-        &self,
-        ecx: &InterpCx<'mir, 'tcx, M>,
-        variant: VariantIdx,
-    ) -> InterpResult<'tcx, Self>;
-
-    /// Projects to the n-th field.
-    fn project_field(
-        &self,
-        ecx: &InterpCx<'mir, 'tcx, M>,
-        field: usize,
-    ) -> InterpResult<'tcx, Self>;
-}
-
-/// A thing that we can project into given *mutable* access to `ecx`, and that has a layout.
-/// This wouldn't have to depend on `Machine` but with the current type inference,
-/// that's just more convenient to work with (avoids repeating all the `Machine` bounds).
-pub trait ValueMut<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>>: Sized {
-    /// Gets this value's layout.
-    fn layout(&self) -> TyAndLayout<'tcx>;
-
-    /// Makes this into an `OpTy`, in a cheap way that is good for reading.
-    fn to_op_for_read(
-        &self,
-        ecx: &InterpCx<'mir, 'tcx, M>,
-    ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>>;
-
-    /// Makes this into an `OpTy`, in a potentially more expensive way that is good for projections.
-    fn to_op_for_proj(
-        &self,
-        ecx: &mut InterpCx<'mir, 'tcx, M>,
-    ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>>;
-
-    /// Creates this from an `OpTy`.
-    ///
-    /// If `to_op_for_proj` only ever produces `Indirect` operands, then this one is definitely `Indirect`.
-    fn from_op(op: &OpTy<'tcx, M::Provenance>) -> Self;
-
-    /// Projects to the given enum variant.
-    fn project_downcast(
-        &self,
-        ecx: &mut InterpCx<'mir, 'tcx, M>,
-        variant: VariantIdx,
-    ) -> InterpResult<'tcx, Self>;
-
-    /// Projects to the n-th field.
-    fn project_field(
-        &self,
-        ecx: &mut InterpCx<'mir, 'tcx, M>,
-        field: usize,
-    ) -> InterpResult<'tcx, Self>;
-}
-
-// We cannot have a general impl which shows that Value implies ValueMut. (When we do, it says we
-// cannot `impl ValueMut for PlaceTy` because some downstream crate could `impl Value for PlaceTy`.)
-// So we have some copy-paste here. (We could have a macro but since we only have 2 types with this
-// double-impl, that would barely make the code shorter, if at all.)
-
-impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M> for OpTy<'tcx, M::Provenance> {
-    #[inline(always)]
-    fn layout(&self) -> TyAndLayout<'tcx> {
-        self.layout
-    }
-
-    #[inline(always)]
-    fn to_op_for_read(
-        &self,
-        _ecx: &InterpCx<'mir, 'tcx, M>,
-    ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
-        Ok(self.clone())
-    }
-
-    #[inline(always)]
-    fn from_op(op: &OpTy<'tcx, M::Provenance>) -> Self {
-        op.clone()
-    }
-
-    #[inline(always)]
-    fn project_downcast(
-        &self,
-        ecx: &InterpCx<'mir, 'tcx, M>,
-        variant: VariantIdx,
-    ) -> InterpResult<'tcx, Self> {
-        ecx.operand_downcast(self, variant)
-    }
-
-    #[inline(always)]
-    fn project_field(
-        &self,
-        ecx: &InterpCx<'mir, 'tcx, M>,
-        field: usize,
-    ) -> InterpResult<'tcx, Self> {
-        ecx.operand_field(self, field)
-    }
-}
-
-impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueMut<'mir, 'tcx, M>
-    for OpTy<'tcx, M::Provenance>
-{
-    #[inline(always)]
-    fn layout(&self) -> TyAndLayout<'tcx> {
-        self.layout
-    }
-
-    #[inline(always)]
-    fn to_op_for_read(
-        &self,
-        _ecx: &InterpCx<'mir, 'tcx, M>,
-    ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
-        Ok(self.clone())
-    }
-
-    #[inline(always)]
-    fn to_op_for_proj(
-        &self,
-        _ecx: &mut InterpCx<'mir, 'tcx, M>,
-    ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
-        Ok(self.clone())
-    }
-
-    #[inline(always)]
-    fn from_op(op: &OpTy<'tcx, M::Provenance>) -> Self {
-        op.clone()
-    }
-
-    #[inline(always)]
-    fn project_downcast(
-        &self,
-        ecx: &mut InterpCx<'mir, 'tcx, M>,
-        variant: VariantIdx,
-    ) -> InterpResult<'tcx, Self> {
-        ecx.operand_downcast(self, variant)
-    }
-
-    #[inline(always)]
-    fn project_field(
-        &self,
-        ecx: &mut InterpCx<'mir, 'tcx, M>,
-        field: usize,
-    ) -> InterpResult<'tcx, Self> {
-        ecx.operand_field(self, field)
-    }
-}
-
-impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M>
-    for MPlaceTy<'tcx, M::Provenance>
-{
-    #[inline(always)]
-    fn layout(&self) -> TyAndLayout<'tcx> {
-        self.layout
-    }
-
-    #[inline(always)]
-    fn to_op_for_read(
-        &self,
-        _ecx: &InterpCx<'mir, 'tcx, M>,
-    ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
-        Ok(self.into())
-    }
-
-    #[inline(always)]
-    fn from_op(op: &OpTy<'tcx, M::Provenance>) -> Self {
-        // assert is justified because our `to_op_for_read` only ever produces `Indirect` operands.
-        op.assert_mem_place()
-    }
-
-    #[inline(always)]
-    fn project_downcast(
-        &self,
-        ecx: &InterpCx<'mir, 'tcx, M>,
-        variant: VariantIdx,
-    ) -> InterpResult<'tcx, Self> {
-        ecx.mplace_downcast(self, variant)
-    }
-
-    #[inline(always)]
-    fn project_field(
-        &self,
-        ecx: &InterpCx<'mir, 'tcx, M>,
-        field: usize,
-    ) -> InterpResult<'tcx, Self> {
-        ecx.mplace_field(self, field)
-    }
-}
-
-impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueMut<'mir, 'tcx, M>
-    for MPlaceTy<'tcx, M::Provenance>
-{
-    #[inline(always)]
-    fn layout(&self) -> TyAndLayout<'tcx> {
-        self.layout
-    }
-
-    #[inline(always)]
-    fn to_op_for_read(
-        &self,
-        _ecx: &InterpCx<'mir, 'tcx, M>,
-    ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
-        Ok(self.into())
-    }
-
-    #[inline(always)]
-    fn to_op_for_proj(
-        &self,
-        _ecx: &mut InterpCx<'mir, 'tcx, M>,
-    ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
-        Ok(self.into())
-    }
-
-    #[inline(always)]
-    fn from_op(op: &OpTy<'tcx, M::Provenance>) -> Self {
-        // assert is justified because our `to_op_for_proj` only ever produces `Indirect` operands.
-        op.assert_mem_place()
-    }
-
-    #[inline(always)]
-    fn project_downcast(
-        &self,
-        ecx: &mut InterpCx<'mir, 'tcx, M>,
-        variant: VariantIdx,
-    ) -> InterpResult<'tcx, Self> {
-        ecx.mplace_downcast(self, variant)
-    }
-
-    #[inline(always)]
-    fn project_field(
-        &self,
-        ecx: &mut InterpCx<'mir, 'tcx, M>,
-        field: usize,
-    ) -> InterpResult<'tcx, Self> {
-        ecx.mplace_field(self, field)
-    }
-}
-
-impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueMut<'mir, 'tcx, M>
-    for PlaceTy<'tcx, M::Provenance>
-{
-    #[inline(always)]
-    fn layout(&self) -> TyAndLayout<'tcx> {
-        self.layout
-    }
-
-    #[inline(always)]
-    fn to_op_for_read(
-        &self,
-        ecx: &InterpCx<'mir, 'tcx, M>,
-    ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
-        // No need for `force_allocation` since we are just going to read from this.
-        ecx.place_to_op(self)
-    }
+    /// The visitor must have an `InterpCx` in it.
+    fn ecx(&self) -> &InterpCx<'mir, 'tcx, M>;
 
+    /// `read_discriminant` can be hooked for better error messages.
     #[inline(always)]
-    fn to_op_for_proj(
-        &self,
-        ecx: &mut InterpCx<'mir, 'tcx, M>,
-    ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
-        // We `force_allocation` here so that `from_op` below can work.
-        Ok(ecx.force_allocation(self)?.into())
+    fn read_discriminant(&mut self, v: &Self::V) -> InterpResult<'tcx, VariantIdx> {
+        Ok(self.ecx().read_discriminant(&v.to_op(self.ecx())?)?)
     }
 
-    #[inline(always)]
-    fn from_op(op: &OpTy<'tcx, M::Provenance>) -> Self {
-        // assert is justified because our `to_op` only ever produces `Indirect` operands.
-        op.assert_mem_place().into()
-    }
-
-    #[inline(always)]
-    fn project_downcast(
-        &self,
-        ecx: &mut InterpCx<'mir, 'tcx, M>,
-        variant: VariantIdx,
-    ) -> InterpResult<'tcx, Self> {
-        ecx.place_downcast(self, variant)
-    }
-
-    #[inline(always)]
-    fn project_field(
-        &self,
-        ecx: &mut InterpCx<'mir, 'tcx, M>,
-        field: usize,
-    ) -> InterpResult<'tcx, Self> {
-        ecx.place_field(self, field)
-    }
-}
-
-macro_rules! make_value_visitor {
-    ($visitor_trait:ident, $value_trait:ident, $($mutability:ident)?) => {
-        /// How to traverse a value and what to do when we are at the leaves.
-        pub trait $visitor_trait<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>>: Sized {
-            type V: $value_trait<'mir, 'tcx, M>;
-
-            /// The visitor must have an `InterpCx` in it.
-            fn ecx(&$($mutability)? self)
-                -> &$($mutability)? InterpCx<'mir, 'tcx, M>;
-
-            /// `read_discriminant` can be hooked for better error messages.
-            #[inline(always)]
-            fn read_discriminant(
-                &mut self,
-                op: &OpTy<'tcx, M::Provenance>,
-            ) -> InterpResult<'tcx, VariantIdx> {
-                Ok(self.ecx().read_discriminant(op)?.1)
-            }
-
-            // Recursive actions, ready to be overloaded.
-            /// Visits the given value, dispatching as appropriate to more specialized visitors.
-            #[inline(always)]
-            fn visit_value(&mut self, v: &Self::V) -> InterpResult<'tcx>
-            {
-                self.walk_value(v)
-            }
-            /// Visits the given value as a union. No automatic recursion can happen here.
-            #[inline(always)]
-            fn visit_union(&mut self, _v: &Self::V, _fields: NonZeroUsize) -> InterpResult<'tcx>
-            {
-                Ok(())
-            }
-            /// Visits the given value as the pointer of a `Box`. There is nothing to recurse into.
-            /// The type of `v` will be a raw pointer, but this is a field of `Box<T>` and the
-            /// pointee type is the actual `T`.
-            #[inline(always)]
-            fn visit_box(&mut self, _v: &Self::V) -> InterpResult<'tcx>
-            {
-                Ok(())
+    /// This function provides the chance to reorder the order in which fields are visited for
+    /// `FieldsShape::Aggregate`: The order of fields will be
+    /// `(0..num_fields).map(aggregate_field_order)`.
+    ///
+    /// The default means we iterate in source declaration order; alternative this can do an inverse
+    /// lookup in `memory_index` to use memory field order instead.
+    #[inline(always)]
+    fn aggregate_field_order(_memory_index: &IndexVec<FieldIdx, u32>, idx: usize) -> usize {
+        idx
+    }
+
+    // Recursive actions, ready to be overloaded.
+    /// Visits the given value, dispatching as appropriate to more specialized visitors.
+    #[inline(always)]
+    fn visit_value(&mut self, v: &Self::V) -> InterpResult<'tcx> {
+        self.walk_value(v)
+    }
+    /// Visits the given value as a union. No automatic recursion can happen here.
+    #[inline(always)]
+    fn visit_union(&mut self, _v: &Self::V, _fields: NonZeroUsize) -> InterpResult<'tcx> {
+        Ok(())
+    }
+    /// Visits the given value as the pointer of a `Box`. There is nothing to recurse into.
+    /// The type of `v` will be a raw pointer, but this is a field of `Box<T>` and the
+    /// pointee type is the actual `T`.
+    #[inline(always)]
+    fn visit_box(&mut self, _v: &Self::V) -> InterpResult<'tcx> {
+        Ok(())
+    }
+
+    /// Called each time we recurse down to a field of a "product-like" aggregate
+    /// (structs, tuples, arrays and the like, but not enums), passing in old (outer)
+    /// and new (inner) value.
+    /// This gives the visitor the chance to track the stack of nested fields that
+    /// we are descending through.
+    #[inline(always)]
+    fn visit_field(
+        &mut self,
+        _old_val: &Self::V,
+        _field: usize,
+        new_val: &Self::V,
+    ) -> InterpResult<'tcx> {
+        self.visit_value(new_val)
+    }
+    /// Called when recursing into an enum variant.
+    /// This gives the visitor the chance to track the stack of nested fields that
+    /// we are descending through.
+    #[inline(always)]
+    fn visit_variant(
+        &mut self,
+        _old_val: &Self::V,
+        _variant: VariantIdx,
+        new_val: &Self::V,
+    ) -> InterpResult<'tcx> {
+        self.visit_value(new_val)
+    }
+
+    fn walk_value(&mut self, v: &Self::V) -> InterpResult<'tcx> {
+        let ty = v.layout().ty;
+        trace!("walk_value: type: {ty}");
+
+        // Special treatment for special types, where the (static) layout is not sufficient.
+        match *ty.kind() {
+            // If it is a trait object, switch to the real type that was used to create it.
+            ty::Dynamic(_, _, ty::Dyn) => {
+                // Dyn types. This is unsized, and the actual dynamic type of the data is given by the
+                // vtable stored in the place metadata.
+                // unsized values are never immediate, so we can assert_mem_place
+                let op = v.to_op(self.ecx())?;
+                let dest = op.assert_mem_place();
+                let inner_mplace = self.ecx().unpack_dyn_trait(&dest)?.0;
+                trace!("walk_value: dyn object layout: {:#?}", inner_mplace.layout);
+                // recurse with the inner type
+                return self.visit_field(&v, 0, &inner_mplace.into());
             }
-            /// Visits this value as an aggregate, you are getting an iterator yielding
-            /// all the fields (still in an `InterpResult`, you have to do error handling yourself).
-            /// Recurses into the fields.
-            #[inline(always)]
-            fn visit_aggregate(
-                &mut self,
-                v: &Self::V,
-                fields: impl Iterator<Item=InterpResult<'tcx, Self::V>>,
-            ) -> InterpResult<'tcx> {
-                self.walk_aggregate(v, fields)
+            ty::Dynamic(_, _, ty::DynStar) => {
+                // DynStar types. Very different from a dyn type (but strangely part of the
+                // same variant in `TyKind`): These are pairs where the 2nd component is the
+                // vtable, and the first component is the data (which must be ptr-sized).
+                let data = self.ecx().unpack_dyn_star(v)?.0;
+                return self.visit_field(&v, 0, &data);
             }
-
-            /// Called each time we recurse down to a field of a "product-like" aggregate
-            /// (structs, tuples, arrays and the like, but not enums), passing in old (outer)
-            /// and new (inner) value.
-            /// This gives the visitor the chance to track the stack of nested fields that
-            /// we are descending through.
-            #[inline(always)]
-            fn visit_field(
-                &mut self,
-                _old_val: &Self::V,
-                _field: usize,
-                new_val: &Self::V,
-            ) -> InterpResult<'tcx> {
-                self.visit_value(new_val)
+            // Slices do not need special handling here: they have `Array` field
+            // placement with length 0, so we enter the `Array` case below which
+            // indirectly uses the metadata to determine the actual length.
+
+            // However, `Box`... let's talk about `Box`.
+            ty::Adt(def, ..) if def.is_box() => {
+                // `Box` is a hybrid primitive-library-defined type that one the one hand is
+                // a dereferenceable pointer, on the other hand has *basically arbitrary
+                // user-defined layout* since the user controls the 'allocator' field. So it
+                // cannot be treated like a normal pointer, since it does not fit into an
+                // `Immediate`. Yeah, it is quite terrible. But many visitors want to do
+                // something with "all boxed pointers", so we handle this mess for them.
+                //
+                // When we hit a `Box`, we do not do the usual field recursion; instead,
+                // we (a) call `visit_box` on the pointer value, and (b) recurse on the
+                // allocator field. We also assert tons of things to ensure we do not miss
+                // any other fields.
+
+                // `Box` has two fields: the pointer we care about, and the allocator.
+                assert_eq!(v.layout().fields.count(), 2, "`Box` must have exactly 2 fields");
+                let (unique_ptr, alloc) =
+                    (self.ecx().project_field(v, 0)?, self.ecx().project_field(v, 1)?);
+                // Unfortunately there is some type junk in the way here: `unique_ptr` is a `Unique`...
+                // (which means another 2 fields, the second of which is a `PhantomData`)
+                assert_eq!(unique_ptr.layout().fields.count(), 2);
+                let (nonnull_ptr, phantom) = (
+                    self.ecx().project_field(&unique_ptr, 0)?,
+                    self.ecx().project_field(&unique_ptr, 1)?,
+                );
+                assert!(
+                    phantom.layout().ty.ty_adt_def().is_some_and(|adt| adt.is_phantom_data()),
+                    "2nd field of `Unique` should be PhantomData but is {:?}",
+                    phantom.layout().ty,
+                );
+                // ... that contains a `NonNull`... (gladly, only a single field here)
+                assert_eq!(nonnull_ptr.layout().fields.count(), 1);
+                let raw_ptr = self.ecx().project_field(&nonnull_ptr, 0)?; // the actual raw ptr
+                // ... whose only field finally is a raw ptr we can dereference.
+                self.visit_box(&raw_ptr)?;
+
+                // The second `Box` field is the allocator, which we recursively check for validity
+                // like in regular structs.
+                self.visit_field(v, 1, &alloc)?;
+
+                // We visited all parts of this one.
+                return Ok(());
             }
-            /// Called when recursing into an enum variant.
-            /// This gives the visitor the chance to track the stack of nested fields that
-            /// we are descending through.
-            #[inline(always)]
-            fn visit_variant(
-                &mut self,
-                _old_val: &Self::V,
-                _variant: VariantIdx,
-                new_val: &Self::V,
-            ) -> InterpResult<'tcx> {
-                self.visit_value(new_val)
+            _ => {}
+        };
+
+        // Visit the fields of this value.
+        match &v.layout().fields {
+            FieldsShape::Primitive => {}
+            &FieldsShape::Union(fields) => {
+                self.visit_union(v, fields)?;
             }
-
-            // Default recursors. Not meant to be overloaded.
-            fn walk_aggregate(
-                &mut self,
-                v: &Self::V,
-                fields: impl Iterator<Item=InterpResult<'tcx, Self::V>>,
-            ) -> InterpResult<'tcx> {
-                // Now iterate over it.
-                for (idx, field_val) in fields.enumerate() {
-                    self.visit_field(v, idx, &field_val?)?;
+            FieldsShape::Arbitrary { offsets, memory_index } => {
+                for idx in 0..offsets.len() {
+                    let idx = Self::aggregate_field_order(memory_index, idx);
+                    let field = self.ecx().project_field(v, idx)?;
+                    self.visit_field(v, idx, &field)?;
                 }
-                Ok(())
             }
-            fn walk_value(&mut self, v: &Self::V) -> InterpResult<'tcx>
-            {
-                let ty = v.layout().ty;
-                trace!("walk_value: type: {ty}");
-
-                // Special treatment for special types, where the (static) layout is not sufficient.
-                match *ty.kind() {
-                    // If it is a trait object, switch to the real type that was used to create it.
-                    ty::Dynamic(_, _, ty::Dyn) => {
-                        // Dyn types. This is unsized, and the actual dynamic type of the data is given by the
-                        // vtable stored in the place metadata.
-                        // unsized values are never immediate, so we can assert_mem_place
-                        let op = v.to_op_for_read(self.ecx())?;
-                        let dest = op.assert_mem_place();
-                        let inner_mplace = self.ecx().unpack_dyn_trait(&dest)?.0;
-                        trace!("walk_value: dyn object layout: {:#?}", inner_mplace.layout);
-                        // recurse with the inner type
-                        return self.visit_field(&v, 0, &$value_trait::from_op(&inner_mplace.into()));
-                    },
-                    ty::Dynamic(_, _, ty::DynStar) => {
-                        // DynStar types. Very different from a dyn type (but strangely part of the
-                        // same variant in `TyKind`): These are pairs where the 2nd component is the
-                        // vtable, and the first component is the data (which must be ptr-sized).
-                        let op = v.to_op_for_proj(self.ecx())?;
-                        let data = self.ecx().unpack_dyn_star(&op)?.0;
-                        return self.visit_field(&v, 0, &$value_trait::from_op(&data));
-                    }
-                    // Slices do not need special handling here: they have `Array` field
-                    // placement with length 0, so we enter the `Array` case below which
-                    // indirectly uses the metadata to determine the actual length.
-
-                    // However, `Box`... let's talk about `Box`.
-                    ty::Adt(def, ..) if def.is_box() => {
-                        // `Box` is a hybrid primitive-library-defined type that one the one hand is
-                        // a dereferenceable pointer, on the other hand has *basically arbitrary
-                        // user-defined layout* since the user controls the 'allocator' field. So it
-                        // cannot be treated like a normal pointer, since it does not fit into an
-                        // `Immediate`. Yeah, it is quite terrible. But many visitors want to do
-                        // something with "all boxed pointers", so we handle this mess for them.
-                        //
-                        // When we hit a `Box`, we do not do the usual `visit_aggregate`; instead,
-                        // we (a) call `visit_box` on the pointer value, and (b) recurse on the
-                        // allocator field. We also assert tons of things to ensure we do not miss
-                        // any other fields.
-
-                        // `Box` has two fields: the pointer we care about, and the allocator.
-                        assert_eq!(v.layout().fields.count(), 2, "`Box` must have exactly 2 fields");
-                        let (unique_ptr, alloc) =
-                            (v.project_field(self.ecx(), 0)?, v.project_field(self.ecx(), 1)?);
-                        // Unfortunately there is some type junk in the way here: `unique_ptr` is a `Unique`...
-                        // (which means another 2 fields, the second of which is a `PhantomData`)
-                        assert_eq!(unique_ptr.layout().fields.count(), 2);
-                        let (nonnull_ptr, phantom) = (
-                            unique_ptr.project_field(self.ecx(), 0)?,
-                            unique_ptr.project_field(self.ecx(), 1)?,
-                        );
-                        assert!(
-                            phantom.layout().ty.ty_adt_def().is_some_and(|adt| adt.is_phantom_data()),
-                            "2nd field of `Unique` should be PhantomData but is {:?}",
-                            phantom.layout().ty,
-                        );
-                        // ... that contains a `NonNull`... (gladly, only a single field here)
-                        assert_eq!(nonnull_ptr.layout().fields.count(), 1);
-                        let raw_ptr = nonnull_ptr.project_field(self.ecx(), 0)?; // the actual raw ptr
-                        // ... whose only field finally is a raw ptr we can dereference.
-                        self.visit_box(&raw_ptr)?;
-
-                        // The second `Box` field is the allocator, which we recursively check for validity
-                        // like in regular structs.
-                        self.visit_field(v, 1, &alloc)?;
-
-                        // We visited all parts of this one.
-                        return Ok(());
-                    }
-                    _ => {},
-                };
-
-                // Visit the fields of this value.
-                match &v.layout().fields {
-                    FieldsShape::Primitive => {}
-                    &FieldsShape::Union(fields) => {
-                        self.visit_union(v, fields)?;
-                    }
-                    FieldsShape::Arbitrary { offsets, .. } => {
-                        // FIXME: We collect in a vec because otherwise there are lifetime
-                        // errors: Projecting to a field needs access to `ecx`.
-                        let fields: Vec<InterpResult<'tcx, Self::V>> =
-                            (0..offsets.len()).map(|i| {
-                                v.project_field(self.ecx(), i)
-                            })
-                            .collect();
-                        self.visit_aggregate(v, fields.into_iter())?;
-                    }
-                    FieldsShape::Array { .. } => {
-                        // Let's get an mplace (or immediate) first.
-                        // This might `force_allocate` if `v` is a `PlaceTy`, but `place_index` does that anyway.
-                        let op = v.to_op_for_proj(self.ecx())?;
-                        // Now we can go over all the fields.
-                        // This uses the *run-time length*, i.e., if we are a slice,
-                        // the dynamic info from the metadata is used.
-                        let iter = self.ecx().operand_array_fields(&op)?
-                            .map(|f| f.and_then(|f| {
-                                Ok($value_trait::from_op(&f))
-                            }));
-                        self.visit_aggregate(v, iter)?;
-                    }
+            FieldsShape::Array { .. } => {
+                for (idx, field) in self.ecx().project_array_fields(v)?.enumerate() {
+                    self.visit_field(v, idx, &field?)?;
                 }
+            }
+        }
 
-                match v.layout().variants {
-                    // If this is a multi-variant layout, find the right variant and proceed
-                    // with *its* fields.
-                    Variants::Multiple { .. } => {
-                        let op = v.to_op_for_read(self.ecx())?;
-                        let idx = self.read_discriminant(&op)?;
-                        let inner = v.project_downcast(self.ecx(), idx)?;
-                        trace!("walk_value: variant layout: {:#?}", inner.layout());
-                        // recurse with the inner type
-                        self.visit_variant(v, idx, &inner)
-                    }
-                    // For single-variant layouts, we already did anything there is to do.
-                    Variants::Single { .. } => Ok(())
-                }
+        match v.layout().variants {
+            // If this is a multi-variant layout, find the right variant and proceed
+            // with *its* fields.
+            Variants::Multiple { .. } => {
+                let idx = self.read_discriminant(v)?;
+                // There are 3 cases where downcasts can turn a Scalar/ScalarPair into a different ABI which
+                // could be a problem for `ImmTy` (see layout_sanity_check):
+                // - variant.size == Size::ZERO: works fine because `ImmTy::offset` has a special case for
+                //   zero-sized layouts.
+                // - variant.fields.count() == 0: works fine because `ImmTy::offset` has a special case for
+                //   zero-field aggregates.
+                // - variant.abi.is_uninhabited(): triggers UB in `read_discriminant` so we never get here.
+                let inner = self.ecx().project_downcast(v, idx)?;
+                trace!("walk_value: variant layout: {:#?}", inner.layout());
+                // recurse with the inner type
+                self.visit_variant(v, idx, &inner)?;
             }
+            // For single-variant layouts, we already did anything there is to do.
+            Variants::Single { .. } => {}
         }
+
+        Ok(())
     }
 }
-
-make_value_visitor!(ValueVisitor, Value,);
-make_value_visitor!(MutValueVisitor, ValueMut, mut);
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs
index d8b8fa927c7..ad5ffa6511f 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs
@@ -8,6 +8,7 @@ use rustc_infer::infer::TyCtxtInferExt;
 use rustc_infer::traits::{ImplSource, Obligation, ObligationCause};
 use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
 use rustc_middle::mir::*;
+use rustc_middle::traits::BuiltinImplSource;
 use rustc_middle::ty::{self, adjustment::PointerCoercion, Instance, InstanceDef, Ty, TyCtxt};
 use rustc_middle::ty::{GenericArgKind, GenericArgs};
 use rustc_middle::ty::{TraitRef, TypeVisitableExt};
@@ -20,7 +21,7 @@ use std::mem;
 use std::ops::Deref;
 
 use super::ops::{self, NonConstOp, Status};
-use super::qualifs::{self, CustomEq, HasMutInterior, NeedsDrop, NeedsNonConstDrop};
+use super::qualifs::{self, CustomEq, HasMutInterior, NeedsDrop};
 use super::resolver::FlowSensitiveAnalysis;
 use super::{ConstCx, Qualif};
 use crate::const_eval::is_unstable_const_fn;
@@ -33,7 +34,7 @@ type QualifResults<'mir, 'tcx, Q> =
 pub struct Qualifs<'mir, 'tcx> {
     has_mut_interior: Option<QualifResults<'mir, 'tcx, HasMutInterior>>,
     needs_drop: Option<QualifResults<'mir, 'tcx, NeedsDrop>>,
-    needs_non_const_drop: Option<QualifResults<'mir, 'tcx, NeedsNonConstDrop>>,
+    // needs_non_const_drop: Option<QualifResults<'mir, 'tcx, NeedsNonConstDrop>>,
 }
 
 impl<'mir, 'tcx> Qualifs<'mir, 'tcx> {
@@ -76,15 +77,17 @@ impl<'mir, 'tcx> Qualifs<'mir, 'tcx> {
         local: Local,
         location: Location,
     ) -> bool {
+        // FIXME(effects) replace with `NeedsNonconstDrop` after const traits work again
+        /*
         let ty = ccx.body.local_decls[local].ty;
-        if !NeedsNonConstDrop::in_any_value_of_ty(ccx, ty) {
+        if !NeedsDrop::in_any_value_of_ty(ccx, ty) {
             return false;
         }
 
         let needs_non_const_drop = self.needs_non_const_drop.get_or_insert_with(|| {
             let ConstCx { tcx, body, .. } = *ccx;
 
-            FlowSensitiveAnalysis::new(NeedsNonConstDrop, ccx)
+            FlowSensitiveAnalysis::new(NeedsDrop, ccx)
                 .into_engine(tcx, &body)
                 .iterate_to_fixpoint()
                 .into_results_cursor(&body)
@@ -92,6 +95,9 @@ impl<'mir, 'tcx> Qualifs<'mir, 'tcx> {
 
         needs_non_const_drop.seek_before_primary_effect(location);
         needs_non_const_drop.get().contains(local)
+        */
+
+        self.needs_drop(ccx, local, location)
     }
 
     /// Returns `true` if `local` is `HasMutInterior` at the given `Location`.
@@ -766,7 +772,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
                     };
 
                     match implsrc {
-                        Ok(Some(ImplSource::Param(_, ty::BoundConstness::ConstIfConst))) => {
+                        Ok(Some(ImplSource::Param(ty::BoundConstness::ConstIfConst, _))) => {
                             debug!(
                                 "const_trait_impl: provided {:?} via where-clause in {:?}",
                                 trait_ref, param_env
@@ -774,7 +780,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
                             return;
                         }
                         // Closure: Fn{Once|Mut}
-                        Ok(Some(ImplSource::Builtin(_)))
+                        Ok(Some(ImplSource::Builtin(BuiltinImplSource::Misc, _)))
                             if trait_ref.self_ty().is_closure()
                                 && tcx.fn_trait_kind_from_def_id(trait_id).is_some() =>
                         {
@@ -797,16 +803,6 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
                         }
                         Ok(Some(ImplSource::UserDefined(data))) => {
                             let callee_name = tcx.item_name(callee);
-                            if let Some(&did) = tcx
-                                .associated_item_def_ids(data.impl_def_id)
-                                .iter()
-                                .find(|did| tcx.item_name(**did) == callee_name)
-                            {
-                                // using internal args is ok here, since this is only
-                                // used for the `resolve` call below
-                                fn_args = GenericArgs::identity_for_item(tcx, did);
-                                callee = did;
-                            }
 
                             if let hir::Constness::NotConst = tcx.constness(data.impl_def_id) {
                                 self.check_op(ops::FnCallNonConst {
@@ -819,6 +815,17 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
                                 });
                                 return;
                             }
+
+                            if let Some(&did) = tcx
+                                .associated_item_def_ids(data.impl_def_id)
+                                .iter()
+                                .find(|did| tcx.item_name(**did) == callee_name)
+                            {
+                                // using internal args is ok here, since this is only
+                                // used for the `resolve` call below
+                                fn_args = GenericArgs::identity_for_item(tcx, did);
+                                callee = did;
+                            }
                         }
                         _ if !tcx.is_const_fn_raw(callee) => {
                             // At this point, it is only legal when the caller is in a trait
@@ -995,8 +1002,9 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
                 let mut err_span = self.span;
                 let ty_of_dropped_place = dropped_place.ty(self.body, self.tcx).ty;
 
+                // FIXME(effects) replace with `NeedsNonConstDrop` once we fix const traits
                 let ty_needs_non_const_drop =
-                    qualifs::NeedsNonConstDrop::in_any_value_of_ty(self.ccx, ty_of_dropped_place);
+                    qualifs::NeedsDrop::in_any_value_of_ty(self.ccx, ty_of_dropped_place);
 
                 debug!(?ty_of_dropped_place, ?ty_needs_non_const_drop);
 
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs
index 81337079af2..e785196c744 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs
@@ -310,8 +310,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
 
         if let Some(feature) = feature && ccx.tcx.sess.is_nightly_build() {
             err.help(format!(
-                "add `#![feature({})]` to the crate attributes to enable",
-                feature,
+                "add `#![feature({feature})]` to the crate attributes to enable",
             ));
         }
 
@@ -346,10 +345,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallUnstable {
             err.help("const-stable functions can only call other const-stable functions");
         } else if ccx.tcx.sess.is_nightly_build() {
             if let Some(feature) = feature {
-                err.help(format!(
-                    "add `#![feature({})]` to the crate attributes to enable",
-                    feature
-                ));
+                err.help(format!("add `#![feature({feature})]` to the crate attributes to enable"));
             }
         }
 
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs b/compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs
index 1f1640fd80a..e3377bd10c6 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs
@@ -5,7 +5,7 @@ use rustc_span::{symbol::sym, Span};
 
 use super::check::Qualifs;
 use super::ops::{self, NonConstOp};
-use super::qualifs::{NeedsNonConstDrop, Qualif};
+use super::qualifs::{NeedsDrop, Qualif};
 use super::ConstCx;
 
 /// Returns `true` if we should use the more precise live drop checker that runs after drop
@@ -82,7 +82,9 @@ impl<'tcx> Visitor<'tcx> for CheckLiveDrops<'_, 'tcx> {
         match &terminator.kind {
             mir::TerminatorKind::Drop { place: dropped_place, .. } => {
                 let dropped_ty = dropped_place.ty(self.body, self.tcx).ty;
-                if !NeedsNonConstDrop::in_any_value_of_ty(self.ccx, dropped_ty) {
+
+                // FIXME(effects) use `NeedsNonConstDrop`
+                if !NeedsDrop::in_any_value_of_ty(self.ccx, dropped_ty) {
                     // Instead of throwing a bug, we just return here. This is because we have to
                     // run custom `const Drop` impls.
                     return;
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs
index 0ef7ace6965..b152644a551 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs
@@ -7,6 +7,7 @@ use rustc_hir::LangItem;
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_middle::mir;
 use rustc_middle::mir::*;
+use rustc_middle::traits::BuiltinImplSource;
 use rustc_middle::ty::{self, AdtDef, GenericArgsRef, Ty};
 use rustc_trait_selection::traits::{
     self, ImplSource, Obligation, ObligationCause, ObligationCtxt, SelectionContext,
@@ -22,7 +23,8 @@ pub fn in_any_value_of_ty<'tcx>(
     ConstQualifs {
         has_mut_interior: HasMutInterior::in_any_value_of_ty(cx, ty),
         needs_drop: NeedsDrop::in_any_value_of_ty(cx, ty),
-        needs_non_const_drop: NeedsNonConstDrop::in_any_value_of_ty(cx, ty),
+        // FIXME(effects)
+        needs_non_const_drop: NeedsDrop::in_any_value_of_ty(cx, ty),
         custom_eq: CustomEq::in_any_value_of_ty(cx, ty),
         tainted_by_errors,
     }
@@ -172,7 +174,8 @@ impl Qualif for NeedsNonConstDrop {
 
         if !matches!(
             impl_src,
-            ImplSource::Builtin(_) | ImplSource::Param(_, ty::BoundConstness::ConstIfConst)
+            ImplSource::Builtin(BuiltinImplSource::Misc, _)
+                | ImplSource::Param(ty::BoundConstness::ConstIfConst, _)
         ) {
             // If our const destruct candidate is not ConstDestruct or implied by the param env,
             // then it's bad
diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs
index ea0d90dbd51..31effadd2c2 100644
--- a/compiler/rustc_const_eval/src/transform/validate.rs
+++ b/compiler/rustc_const_eval/src/transform/validate.rs
@@ -58,11 +58,10 @@ impl<'tcx> MirPass<'tcx> for Validator {
             .iterate_to_fixpoint()
             .into_results_cursor(body);
 
-        let mut checker = TypeChecker {
+        let mut cfg_checker = CfgChecker {
             when: &self.when,
             body,
             tcx,
-            param_env,
             mir_phase,
             unwind_edge_count: 0,
             reachable_blocks: traversal::reachable_as_bitset(body),
@@ -70,13 +69,17 @@ impl<'tcx> MirPass<'tcx> for Validator {
             place_cache: FxHashSet::default(),
             value_cache: FxHashSet::default(),
         };
-        checker.visit_body(body);
-        checker.check_cleanup_control_flow();
+        cfg_checker.visit_body(body);
+        cfg_checker.check_cleanup_control_flow();
+
+        for (location, msg) in validate_types(tcx, self.mir_phase, param_env, body) {
+            cfg_checker.fail(location, msg);
+        }
 
         if let MirPhase::Runtime(_) = body.phase {
             if let ty::InstanceDef::Item(_) = body.source.instance {
                 if body.has_free_regions() {
-                    checker.fail(
+                    cfg_checker.fail(
                         Location::START,
                         format!("Free regions in optimized {} MIR", body.phase.name()),
                     );
@@ -86,11 +89,10 @@ impl<'tcx> MirPass<'tcx> for Validator {
     }
 }
 
-struct TypeChecker<'a, 'tcx> {
+struct CfgChecker<'a, 'tcx> {
     when: &'a str,
     body: &'a Body<'tcx>,
     tcx: TyCtxt<'tcx>,
-    param_env: ParamEnv<'tcx>,
     mir_phase: MirPhase,
     unwind_edge_count: usize,
     reachable_blocks: BitSet<BasicBlock>,
@@ -99,7 +101,7 @@ struct TypeChecker<'a, 'tcx> {
     value_cache: FxHashSet<u128>,
 }
 
-impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
+impl<'a, 'tcx> CfgChecker<'a, 'tcx> {
     #[track_caller]
     fn fail(&self, location: Location, msg: impl AsRef<str>) {
         let span = self.body.source_info(location).span;
@@ -147,7 +149,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                 }
             }
         } else {
-            self.fail(location, format!("encountered jump to invalid basic block {:?}", bb))
+            self.fail(location, format!("encountered jump to invalid basic block {bb:?}"))
         }
     }
 
@@ -220,8 +222,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                     self.fail(
                         Location { block: bb, statement_index: 0 },
                         format!(
-                            "Cleanup control flow violation: Cycle involving edge {:?} -> {:?}",
-                            bb, parent,
+                            "Cleanup control flow violation: Cycle involving edge {bb:?} -> {parent:?}",
                         ),
                     );
                     break;
@@ -248,35 +249,14 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
             UnwindAction::Unreachable | UnwindAction::Terminate => (),
         }
     }
-
-    /// Check if src can be assigned into dest.
-    /// This is not precise, it will accept some incorrect assignments.
-    fn mir_assign_valid_types(&self, src: Ty<'tcx>, dest: Ty<'tcx>) -> bool {
-        // Fast path before we normalize.
-        if src == dest {
-            // Equal types, all is good.
-            return true;
-        }
-
-        // We sometimes have to use `defining_opaque_types` for subtyping
-        // to succeed here and figuring out how exactly that should work
-        // is annoying. It is harmless enough to just not validate anything
-        // in that case. We still check this after analysis as all opaque
-        // types have been revealed at this point.
-        if (src, dest).has_opaque_types() {
-            return true;
-        }
-
-        crate::util::is_subtype(self.tcx, self.param_env, src, dest)
-    }
 }
 
-impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
+impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> {
     fn visit_local(&mut self, local: Local, context: PlaceContext, location: Location) {
         if self.body.local_decls.get(local).is_none() {
             self.fail(
                 location,
-                format!("local {:?} has no corresponding declaration in `body.local_decls`", local),
+                format!("local {local:?} has no corresponding declaration in `body.local_decls`"),
             );
         }
 
@@ -291,11 +271,280 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
             self.storage_liveness.seek_after_primary_effect(location);
             let locals_with_storage = self.storage_liveness.get();
             if !locals_with_storage.contains(local) {
-                self.fail(location, format!("use of local {:?}, which has no storage here", local));
+                self.fail(location, format!("use of local {local:?}, which has no storage here"));
             }
         }
     }
 
+    fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
+        match &statement.kind {
+            StatementKind::Assign(box (dest, rvalue)) => {
+                // FIXME(JakobDegen): Check this for all rvalues, not just this one.
+                if let Rvalue::Use(Operand::Copy(src) | Operand::Move(src)) = rvalue {
+                    // The sides of an assignment must not alias. Currently this just checks whether
+                    // the places are identical.
+                    if dest == src {
+                        self.fail(
+                            location,
+                            "encountered `Assign` statement with overlapping memory",
+                        );
+                    }
+                }
+            }
+            StatementKind::AscribeUserType(..) => {
+                if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
+                    self.fail(
+                        location,
+                        "`AscribeUserType` should have been removed after drop lowering phase",
+                    );
+                }
+            }
+            StatementKind::FakeRead(..) => {
+                if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
+                    self.fail(
+                        location,
+                        "`FakeRead` should have been removed after drop lowering phase",
+                    );
+                }
+            }
+            StatementKind::SetDiscriminant { .. } => {
+                if self.mir_phase < MirPhase::Runtime(RuntimePhase::Initial) {
+                    self.fail(location, "`SetDiscriminant`is not allowed until deaggregation");
+                }
+            }
+            StatementKind::Deinit(..) => {
+                if self.mir_phase < MirPhase::Runtime(RuntimePhase::Initial) {
+                    self.fail(location, "`Deinit`is not allowed until deaggregation");
+                }
+            }
+            StatementKind::Retag(kind, _) => {
+                // FIXME(JakobDegen) The validator should check that `self.mir_phase <
+                // DropsLowered`. However, this causes ICEs with generation of drop shims, which
+                // seem to fail to set their `MirPhase` correctly.
+                if matches!(kind, RetagKind::Raw | RetagKind::TwoPhase) {
+                    self.fail(location, format!("explicit `{kind:?}` is forbidden"));
+                }
+            }
+            StatementKind::StorageLive(local) => {
+                // We check that the local is not live when entering a `StorageLive` for it.
+                // Technically, violating this restriction is only UB and not actually indicative
+                // of not well-formed MIR. This means that an optimization which turns MIR that
+                // already has UB into MIR that fails this check is not necessarily wrong. However,
+                // we have no such optimizations at the moment, and so we include this check anyway
+                // to help us catch bugs. If you happen to write an optimization that might cause
+                // this to incorrectly fire, feel free to remove this check.
+                if self.reachable_blocks.contains(location.block) {
+                    self.storage_liveness.seek_before_primary_effect(location);
+                    let locals_with_storage = self.storage_liveness.get();
+                    if locals_with_storage.contains(*local) {
+                        self.fail(
+                            location,
+                            format!("StorageLive({local:?}) which already has storage here"),
+                        );
+                    }
+                }
+            }
+            StatementKind::StorageDead(_)
+            | StatementKind::Intrinsic(_)
+            | StatementKind::Coverage(_)
+            | StatementKind::ConstEvalCounter
+            | StatementKind::PlaceMention(..)
+            | StatementKind::Nop => {}
+        }
+
+        self.super_statement(statement, location);
+    }
+
+    fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
+        match &terminator.kind {
+            TerminatorKind::Goto { target } => {
+                self.check_edge(location, *target, EdgeKind::Normal);
+            }
+            TerminatorKind::SwitchInt { targets, discr: _ } => {
+                for (_, target) in targets.iter() {
+                    self.check_edge(location, target, EdgeKind::Normal);
+                }
+                self.check_edge(location, targets.otherwise(), EdgeKind::Normal);
+
+                self.value_cache.clear();
+                self.value_cache.extend(targets.iter().map(|(value, _)| value));
+                let has_duplicates = targets.iter().len() != self.value_cache.len();
+                if has_duplicates {
+                    self.fail(
+                        location,
+                        format!(
+                            "duplicated values in `SwitchInt` terminator: {:?}",
+                            terminator.kind,
+                        ),
+                    );
+                }
+            }
+            TerminatorKind::Drop { target, unwind, .. } => {
+                self.check_edge(location, *target, EdgeKind::Normal);
+                self.check_unwind_edge(location, *unwind);
+            }
+            TerminatorKind::Call { args, destination, target, unwind, .. } => {
+                if let Some(target) = target {
+                    self.check_edge(location, *target, EdgeKind::Normal);
+                }
+                self.check_unwind_edge(location, *unwind);
+
+                // The call destination place and Operand::Move place used as an argument might be
+                // passed by a reference to the callee. Consequently they must be non-overlapping.
+                // Currently this simply checks for duplicate places.
+                self.place_cache.clear();
+                self.place_cache.insert(destination.as_ref());
+                let mut has_duplicates = false;
+                for arg in args {
+                    if let Operand::Move(place) = arg {
+                        has_duplicates |= !self.place_cache.insert(place.as_ref());
+                    }
+                }
+
+                if has_duplicates {
+                    self.fail(
+                        location,
+                        format!(
+                            "encountered overlapping memory in `Call` terminator: {:?}",
+                            terminator.kind,
+                        ),
+                    );
+                }
+            }
+            TerminatorKind::Assert { target, unwind, .. } => {
+                self.check_edge(location, *target, EdgeKind::Normal);
+                self.check_unwind_edge(location, *unwind);
+            }
+            TerminatorKind::Yield { resume, drop, .. } => {
+                if self.body.generator.is_none() {
+                    self.fail(location, "`Yield` cannot appear outside generator bodies");
+                }
+                if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
+                    self.fail(location, "`Yield` should have been replaced by generator lowering");
+                }
+                self.check_edge(location, *resume, EdgeKind::Normal);
+                if let Some(drop) = drop {
+                    self.check_edge(location, *drop, EdgeKind::Normal);
+                }
+            }
+            TerminatorKind::FalseEdge { real_target, imaginary_target } => {
+                if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
+                    self.fail(
+                        location,
+                        "`FalseEdge` should have been removed after drop elaboration",
+                    );
+                }
+                self.check_edge(location, *real_target, EdgeKind::Normal);
+                self.check_edge(location, *imaginary_target, EdgeKind::Normal);
+            }
+            TerminatorKind::FalseUnwind { real_target, unwind } => {
+                if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
+                    self.fail(
+                        location,
+                        "`FalseUnwind` should have been removed after drop elaboration",
+                    );
+                }
+                self.check_edge(location, *real_target, EdgeKind::Normal);
+                self.check_unwind_edge(location, *unwind);
+            }
+            TerminatorKind::InlineAsm { destination, unwind, .. } => {
+                if let Some(destination) = destination {
+                    self.check_edge(location, *destination, EdgeKind::Normal);
+                }
+                self.check_unwind_edge(location, *unwind);
+            }
+            TerminatorKind::GeneratorDrop => {
+                if self.body.generator.is_none() {
+                    self.fail(location, "`GeneratorDrop` cannot appear outside generator bodies");
+                }
+                if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
+                    self.fail(
+                        location,
+                        "`GeneratorDrop` should have been replaced by generator lowering",
+                    );
+                }
+            }
+            TerminatorKind::Resume | TerminatorKind::Terminate => {
+                let bb = location.block;
+                if !self.body.basic_blocks[bb].is_cleanup {
+                    self.fail(
+                        location,
+                        "Cannot `Resume` or `Terminate` from non-cleanup basic block",
+                    )
+                }
+            }
+            TerminatorKind::Return => {
+                let bb = location.block;
+                if self.body.basic_blocks[bb].is_cleanup {
+                    self.fail(location, "Cannot `Return` from cleanup basic block")
+                }
+            }
+            TerminatorKind::Unreachable => {}
+        }
+
+        self.super_terminator(terminator, location);
+    }
+
+    fn visit_source_scope(&mut self, scope: SourceScope) {
+        if self.body.source_scopes.get(scope).is_none() {
+            self.tcx.sess.diagnostic().delay_span_bug(
+                self.body.span,
+                format!(
+                    "broken MIR in {:?} ({}):\ninvalid source scope {:?}",
+                    self.body.source.instance, self.when, scope,
+                ),
+            );
+        }
+    }
+}
+
+pub fn validate_types<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    mir_phase: MirPhase,
+    param_env: ty::ParamEnv<'tcx>,
+    body: &Body<'tcx>,
+) -> Vec<(Location, String)> {
+    let mut type_checker = TypeChecker { body, tcx, param_env, mir_phase, failures: Vec::new() };
+    type_checker.visit_body(body);
+    type_checker.failures
+}
+
+struct TypeChecker<'a, 'tcx> {
+    body: &'a Body<'tcx>,
+    tcx: TyCtxt<'tcx>,
+    param_env: ParamEnv<'tcx>,
+    mir_phase: MirPhase,
+    failures: Vec<(Location, String)>,
+}
+
+impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
+    fn fail(&mut self, location: Location, msg: impl Into<String>) {
+        self.failures.push((location, msg.into()));
+    }
+
+    /// Check if src can be assigned into dest.
+    /// This is not precise, it will accept some incorrect assignments.
+    fn mir_assign_valid_types(&self, src: Ty<'tcx>, dest: Ty<'tcx>) -> bool {
+        // Fast path before we normalize.
+        if src == dest {
+            // Equal types, all is good.
+            return true;
+        }
+
+        // We sometimes have to use `defining_opaque_types` for subtyping
+        // to succeed here and figuring out how exactly that should work
+        // is annoying. It is harmless enough to just not validate anything
+        // in that case. We still check this after analysis as all opaque
+        // types have been revealed at this point.
+        if (src, dest).has_opaque_types() {
+            return true;
+        }
+
+        crate::util::is_subtype(self.tcx, self.param_env, src, dest)
+    }
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
     fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
         // This check is somewhat expensive, so only run it when -Zvalidate-mir is passed.
         if self.tcx.sess.opts.unstable_opts.validate_mir
@@ -306,7 +555,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                 let ty = place.ty(&self.body.local_decls, self.tcx).ty;
 
                 if !ty.is_copy_modulo_regions(self.tcx, self.param_env) {
-                    self.fail(location, format!("`Operand::Copy` with non-`Copy` type {}", ty));
+                    self.fail(location, format!("`Operand::Copy` with non-`Copy` type {ty}"));
                 }
             }
         }
@@ -325,7 +574,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
             ProjectionElem::Index(index) => {
                 let index_ty = self.body.local_decls[index].ty;
                 if index_ty != self.tcx.types.usize {
-                    self.fail(location, format!("bad index ({:?} != usize)", index_ty))
+                    self.fail(location, format!("bad index ({index_ty:?} != usize)"))
                 }
             }
             ProjectionElem::Deref
@@ -336,22 +585,21 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                 if base_ty.is_box() {
                     self.fail(
                         location,
-                        format!("{:?} dereferenced after ElaborateBoxDerefs", base_ty),
+                        format!("{base_ty:?} dereferenced after ElaborateBoxDerefs"),
                     )
                 }
             }
             ProjectionElem::Field(f, ty) => {
                 let parent_ty = place_ref.ty(&self.body.local_decls, self.tcx);
-                let fail_out_of_bounds = |this: &Self, location| {
-                    this.fail(location, format!("Out of bounds field {:?} for {:?}", f, parent_ty));
+                let fail_out_of_bounds = |this: &mut Self, location| {
+                    this.fail(location, format!("Out of bounds field {f:?} for {parent_ty:?}"));
                 };
-                let check_equal = |this: &Self, location, f_ty| {
+                let check_equal = |this: &mut Self, location, f_ty| {
                     if !this.mir_assign_valid_types(ty, f_ty) {
                         this.fail(
                             location,
                             format!(
-                                "Field projection `{:?}.{:?}` specified type `{:?}`, but actual type is `{:?}`",
-                                place_ref, f, ty, f_ty
+                                "Field projection `{place_ref:?}.{f:?}` specified type `{ty:?}`, but actual type is `{f_ty:?}`"
                             )
                         )
                     }
@@ -399,7 +647,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                             let Some(layout) = gen_body.generator_layout() else {
                                 self.fail(
                                     location,
-                                    format!("No generator layout for {:?}", parent_ty),
+                                    format!("No generator layout for {parent_ty:?}"),
                                 );
                                 return;
                             };
@@ -412,7 +660,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                             let Some(f_ty) = layout.field_tys.get(local) else {
                                 self.fail(
                                     location,
-                                    format!("Out of bounds local {:?} for {:?}", local, parent_ty),
+                                    format!("Out of bounds local {local:?} for {parent_ty:?}"),
                                 );
                                 return;
                             };
@@ -440,9 +688,9 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
     }
 
     fn visit_var_debug_info(&mut self, debuginfo: &VarDebugInfo<'tcx>) {
-        let check_place = |place: Place<'_>| {
+        let check_place = |this: &mut Self, place: Place<'_>| {
             if place.projection.iter().any(|p| !p.can_use_in_debuginfo()) {
-                self.fail(
+                this.fail(
                     START_BLOCK.start_location(),
                     format!("illegal place {:?} in debuginfo for {:?}", place, debuginfo.name),
                 );
@@ -451,21 +699,21 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
         match debuginfo.value {
             VarDebugInfoContents::Const(_) => {}
             VarDebugInfoContents::Place(place) => {
-                check_place(place);
+                check_place(self, place);
                 if debuginfo.references != 0 && place.projection.last() == Some(&PlaceElem::Deref) {
                     self.fail(
                         START_BLOCK.start_location(),
-                        format!("debuginfo {:?}, has both ref and deref", debuginfo),
+                        format!("debuginfo {debuginfo:?}, has both ref and deref"),
                     );
                 }
             }
             VarDebugInfoContents::Composite { ty, ref fragments } => {
                 for f in fragments {
-                    check_place(f.contents);
+                    check_place(self, f.contents);
                     if ty.is_union() || ty.is_enum() {
                         self.fail(
                             START_BLOCK.start_location(),
-                            format!("invalid type {:?} for composite debuginfo", ty),
+                            format!("invalid type {ty:?} for composite debuginfo"),
                         );
                     }
                     if f.projection.iter().any(|p| !matches!(p, PlaceElem::Field(..))) {
@@ -492,7 +740,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
             && cntxt != PlaceContext::NonUse(NonUseContext::VarDebugInfo)
             && place.projection[1..].contains(&ProjectionElem::Deref)
         {
-            self.fail(location, format!("{:?}, has deref at the wrong place", place));
+            self.fail(location, format!("{place:?}, has deref at the wrong place"));
         }
 
         self.super_place(place, cntxt, location);
@@ -552,7 +800,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                     Offset => {
                         check_kinds!(a, "Cannot offset non-pointer type {:?}", ty::RawPtr(..));
                         if b != self.tcx.types.isize && b != self.tcx.types.usize {
-                            self.fail(location, format!("Cannot offset by non-isize type {:?}", b));
+                            self.fail(location, format!("Cannot offset by non-isize type {b:?}"));
                         }
                     }
                     Eq | Lt | Le | Ne | Ge | Gt => {
@@ -617,13 +865,12 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                             self.fail(
                                 location,
                                 format!(
-                                    "Cannot perform checked arithmetic on unequal types {:?} and {:?}",
-                                    a, b
+                                    "Cannot perform checked arithmetic on unequal types {a:?} and {b:?}"
                                 ),
                             );
                         }
                     }
-                    _ => self.fail(location, format!("There is no checked version of {:?}", op)),
+                    _ => self.fail(location, format!("There is no checked version of {op:?}")),
                 }
             }
             Rvalue::UnaryOp(op, operand) => {
@@ -718,7 +965,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                 }
             }
             Rvalue::NullaryOp(NullOp::OffsetOf(fields), container) => {
-                let fail_out_of_bounds = |this: &Self, location, field, ty| {
+                let fail_out_of_bounds = |this: &mut Self, location, field, ty| {
                     this.fail(location, format!("Out of bounds field {field:?} for {ty:?}"));
                 };
 
@@ -828,7 +1075,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                 if !ty.is_bool() {
                     self.fail(
                         location,
-                        format!("`assume` argument must be `bool`, but got: `{}`", ty),
+                        format!("`assume` argument must be `bool`, but got: `{ty}`"),
                     );
                 }
             }
@@ -841,7 +1088,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                 } else {
                     self.fail(
                         location,
-                        format!("Expected src to be ptr in copy_nonoverlapping, got: {}", src_ty),
+                        format!("Expected src to be ptr in copy_nonoverlapping, got: {src_ty}"),
                     );
                     return;
                 };
@@ -851,19 +1098,19 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                 } else {
                     self.fail(
                         location,
-                        format!("Expected dst to be ptr in copy_nonoverlapping, got: {}", dst_ty),
+                        format!("Expected dst to be ptr in copy_nonoverlapping, got: {dst_ty}"),
                     );
                     return;
                 };
                 // since CopyNonOverlapping is parametrized by 1 type,
                 // we only need to check that they are equal and not keep an extra parameter.
                 if !self.mir_assign_valid_types(op_src_ty, op_dst_ty) {
-                    self.fail(location, format!("bad arg ({:?} != {:?})", op_src_ty, op_dst_ty));
+                    self.fail(location, format!("bad arg ({op_src_ty:?} != {op_dst_ty:?})"));
                 }
 
                 let op_cnt_ty = count.ty(&self.body.local_decls, self.tcx);
                 if op_cnt_ty != self.tcx.types.usize {
-                    self.fail(location, format!("bad arg ({:?} != usize)", op_cnt_ty))
+                    self.fail(location, format!("bad arg ({op_cnt_ty:?} != usize)"))
                 }
             }
             StatementKind::SetDiscriminant { place, .. } => {
@@ -875,8 +1122,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                     self.fail(
                         location,
                         format!(
-                            "`SetDiscriminant` is only allowed on ADTs and generators, not {:?}",
-                            pty
+                            "`SetDiscriminant` is only allowed on ADTs and generators, not {pty:?}"
                         ),
                     );
                 }
@@ -891,29 +1137,11 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                 // DropsLowered`. However, this causes ICEs with generation of drop shims, which
                 // seem to fail to set their `MirPhase` correctly.
                 if matches!(kind, RetagKind::Raw | RetagKind::TwoPhase) {
-                    self.fail(location, format!("explicit `{:?}` is forbidden", kind));
+                    self.fail(location, format!("explicit `{kind:?}` is forbidden"));
                 }
             }
-            StatementKind::StorageLive(local) => {
-                // We check that the local is not live when entering a `StorageLive` for it.
-                // Technically, violating this restriction is only UB and not actually indicative
-                // of not well-formed MIR. This means that an optimization which turns MIR that
-                // already has UB into MIR that fails this check is not necessarily wrong. However,
-                // we have no such optimizations at the moment, and so we include this check anyway
-                // to help us catch bugs. If you happen to write an optimization that might cause
-                // this to incorrectly fire, feel free to remove this check.
-                if self.reachable_blocks.contains(location.block) {
-                    self.storage_liveness.seek_before_primary_effect(location);
-                    let locals_with_storage = self.storage_liveness.get();
-                    if locals_with_storage.contains(*local) {
-                        self.fail(
-                            location,
-                            format!("StorageLive({local:?}) which already has storage here"),
-                        );
-                    }
-                }
-            }
-            StatementKind::StorageDead(_)
+            StatementKind::StorageLive(_)
+            | StatementKind::StorageDead(_)
             | StatementKind::Coverage(_)
             | StatementKind::ConstEvalCounter
             | StatementKind::PlaceMention(..)
@@ -925,9 +1153,6 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
 
     fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
         match &terminator.kind {
-            TerminatorKind::Goto { target } => {
-                self.check_edge(location, *target, EdgeKind::Normal);
-            }
             TerminatorKind::SwitchInt { targets, discr } => {
                 let switch_ty = discr.ty(&self.body.local_decls, self.tcx);
 
@@ -941,164 +1166,49 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                     other => bug!("unhandled type: {:?}", other),
                 });
 
-                for (value, target) in targets.iter() {
+                for (value, _) in targets.iter() {
                     if Scalar::<()>::try_from_uint(value, size).is_none() {
                         self.fail(
                             location,
-                            format!("the value {:#x} is not a proper {:?}", value, switch_ty),
+                            format!("the value {value:#x} is not a proper {switch_ty:?}"),
                         )
                     }
-
-                    self.check_edge(location, target, EdgeKind::Normal);
-                }
-                self.check_edge(location, targets.otherwise(), EdgeKind::Normal);
-
-                self.value_cache.clear();
-                self.value_cache.extend(targets.iter().map(|(value, _)| value));
-                let has_duplicates = targets.iter().len() != self.value_cache.len();
-                if has_duplicates {
-                    self.fail(
-                        location,
-                        format!(
-                            "duplicated values in `SwitchInt` terminator: {:?}",
-                            terminator.kind,
-                        ),
-                    );
                 }
             }
-            TerminatorKind::Drop { target, unwind, .. } => {
-                self.check_edge(location, *target, EdgeKind::Normal);
-                self.check_unwind_edge(location, *unwind);
-            }
-            TerminatorKind::Call { func, args, destination, target, unwind, .. } => {
+            TerminatorKind::Call { func, .. } => {
                 let func_ty = func.ty(&self.body.local_decls, self.tcx);
                 match func_ty.kind() {
                     ty::FnPtr(..) | ty::FnDef(..) => {}
                     _ => self.fail(
                         location,
-                        format!("encountered non-callable type {} in `Call` terminator", func_ty),
+                        format!("encountered non-callable type {func_ty} in `Call` terminator"),
                     ),
                 }
-                if let Some(target) = target {
-                    self.check_edge(location, *target, EdgeKind::Normal);
-                }
-                self.check_unwind_edge(location, *unwind);
-
-                // The call destination place and Operand::Move place used as an argument might be
-                // passed by a reference to the callee. Consequently they must be non-overlapping.
-                // Currently this simply checks for duplicate places.
-                self.place_cache.clear();
-                self.place_cache.insert(destination.as_ref());
-                let mut has_duplicates = false;
-                for arg in args {
-                    if let Operand::Move(place) = arg {
-                        has_duplicates |= !self.place_cache.insert(place.as_ref());
-                    }
-                }
-
-                if has_duplicates {
-                    self.fail(
-                        location,
-                        format!(
-                            "encountered overlapping memory in `Call` terminator: {:?}",
-                            terminator.kind,
-                        ),
-                    );
-                }
             }
-            TerminatorKind::Assert { cond, target, unwind, .. } => {
+            TerminatorKind::Assert { cond, .. } => {
                 let cond_ty = cond.ty(&self.body.local_decls, self.tcx);
                 if cond_ty != self.tcx.types.bool {
                     self.fail(
                         location,
                         format!(
-                            "encountered non-boolean condition of type {} in `Assert` terminator",
-                            cond_ty
+                            "encountered non-boolean condition of type {cond_ty} in `Assert` terminator"
                         ),
                     );
                 }
-                self.check_edge(location, *target, EdgeKind::Normal);
-                self.check_unwind_edge(location, *unwind);
-            }
-            TerminatorKind::Yield { resume, drop, .. } => {
-                if self.body.generator.is_none() {
-                    self.fail(location, "`Yield` cannot appear outside generator bodies");
-                }
-                if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
-                    self.fail(location, "`Yield` should have been replaced by generator lowering");
-                }
-                self.check_edge(location, *resume, EdgeKind::Normal);
-                if let Some(drop) = drop {
-                    self.check_edge(location, *drop, EdgeKind::Normal);
-                }
-            }
-            TerminatorKind::FalseEdge { real_target, imaginary_target } => {
-                if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
-                    self.fail(
-                        location,
-                        "`FalseEdge` should have been removed after drop elaboration",
-                    );
-                }
-                self.check_edge(location, *real_target, EdgeKind::Normal);
-                self.check_edge(location, *imaginary_target, EdgeKind::Normal);
-            }
-            TerminatorKind::FalseUnwind { real_target, unwind } => {
-                if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
-                    self.fail(
-                        location,
-                        "`FalseUnwind` should have been removed after drop elaboration",
-                    );
-                }
-                self.check_edge(location, *real_target, EdgeKind::Normal);
-                self.check_unwind_edge(location, *unwind);
-            }
-            TerminatorKind::InlineAsm { destination, unwind, .. } => {
-                if let Some(destination) = destination {
-                    self.check_edge(location, *destination, EdgeKind::Normal);
-                }
-                self.check_unwind_edge(location, *unwind);
             }
-            TerminatorKind::GeneratorDrop => {
-                if self.body.generator.is_none() {
-                    self.fail(location, "`GeneratorDrop` cannot appear outside generator bodies");
-                }
-                if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
-                    self.fail(
-                        location,
-                        "`GeneratorDrop` should have been replaced by generator lowering",
-                    );
-                }
-            }
-            TerminatorKind::Resume | TerminatorKind::Terminate => {
-                let bb = location.block;
-                if !self.body.basic_blocks[bb].is_cleanup {
-                    self.fail(
-                        location,
-                        "Cannot `Resume` or `Terminate` from non-cleanup basic block",
-                    )
-                }
-            }
-            TerminatorKind::Return => {
-                let bb = location.block;
-                if self.body.basic_blocks[bb].is_cleanup {
-                    self.fail(location, "Cannot `Return` from cleanup basic block")
-                }
-            }
-            TerminatorKind::Unreachable => {}
+            TerminatorKind::Goto { .. }
+            | TerminatorKind::Drop { .. }
+            | TerminatorKind::Yield { .. }
+            | TerminatorKind::FalseEdge { .. }
+            | TerminatorKind::FalseUnwind { .. }
+            | TerminatorKind::InlineAsm { .. }
+            | TerminatorKind::GeneratorDrop
+            | TerminatorKind::Resume
+            | TerminatorKind::Terminate
+            | TerminatorKind::Return
+            | TerminatorKind::Unreachable => {}
         }
 
         self.super_terminator(terminator, location);
     }
-
-    fn visit_source_scope(&mut self, scope: SourceScope) {
-        if self.body.source_scopes.get(scope).is_none() {
-            self.tcx.sess.diagnostic().delay_span_bug(
-                self.body.span,
-                format!(
-                    "broken MIR in {:?} ({}):\ninvalid source scope {:?}",
-                    self.body.source.instance, self.when, scope,
-                ),
-            );
-        }
-    }
 }
diff --git a/compiler/rustc_const_eval/src/util/compare_types.rs b/compiler/rustc_const_eval/src/util/compare_types.rs
index d6a2ffb7511..83376c8e992 100644
--- a/compiler/rustc_const_eval/src/util/compare_types.rs
+++ b/compiler/rustc_const_eval/src/util/compare_types.rs
@@ -56,8 +56,16 @@ pub fn is_subtype<'tcx>(
     // With `Reveal::All`, opaque types get normalized away, with `Reveal::UserFacing`
     // we would get unification errors because we're unable to look into opaque types,
     // even if they're constrained in our current function.
-    //
-    // It seems very unlikely that this hides any bugs.
-    let _ = infcx.take_opaque_types();
+    for (key, ty) in infcx.take_opaque_types() {
+        let hidden_ty = tcx.type_of(key.def_id).instantiate(tcx, key.args);
+        if hidden_ty != ty.hidden_type.ty {
+            span_bug!(
+                ty.hidden_type.span,
+                "{}, {}",
+                tcx.type_of(key.def_id).instantiate(tcx, key.args),
+                ty.hidden_type.ty
+            );
+        }
+    }
     errors.is_empty()
 }
diff --git a/compiler/rustc_data_structures/src/sso/map.rs b/compiler/rustc_data_structures/src/sso/map.rs
index 99581ed2375..04e359a5470 100644
--- a/compiler/rustc_data_structures/src/sso/map.rs
+++ b/compiler/rustc_data_structures/src/sso/map.rs
@@ -268,11 +268,7 @@ impl<K: Eq + Hash, V> SsoHashMap<K, V> {
     pub fn remove_entry(&mut self, key: &K) -> Option<(K, V)> {
         match self {
             SsoHashMap::Array(array) => {
-                if let Some(index) = array.iter().position(|(k, _v)| k == key) {
-                    Some(array.swap_remove(index))
-                } else {
-                    None
-                }
+                array.iter().position(|(k, _v)| k == key).map(|index| array.swap_remove(index))
             }
             SsoHashMap::Map(map) => map.remove_entry(key),
         }
diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs
index 8722979cb5e..8f8b9eaa274 100644
--- a/compiler/rustc_driver_impl/src/lib.rs
+++ b/compiler/rustc_driver_impl/src/lib.rs
@@ -1415,7 +1415,7 @@ pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str, extra_info:
         false,
         TerminalUrl::No,
     ));
-    let handler = rustc_errors::Handler::with_emitter(true, None, emitter, None);
+    let handler = rustc_errors::Handler::with_emitter(emitter);
 
     // a .span_bug or .bug call has already printed what
     // it wants to print.
diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs
index 1879ece59e3..a49b842d9ff 100644
--- a/compiler/rustc_error_messages/src/lib.rs
+++ b/compiler/rustc_error_messages/src/lib.rs
@@ -533,6 +533,14 @@ impl MultiSpan {
     pub fn has_span_labels(&self) -> bool {
         self.span_labels.iter().any(|(sp, _)| !sp.is_dummy())
     }
+
+    /// Clone this `MultiSpan` without keeping any of the span labels - sometimes a `MultiSpan` is
+    /// to be re-used in another diagnostic, but includes `span_labels` which have translated
+    /// messages. These translated messages would fail to translate without their diagnostic
+    /// arguments which are unlikely to be cloned alongside the `Span`.
+    pub fn clone_ignoring_labels(&self) -> Self {
+        Self { primary_spans: self.primary_spans.clone(), ..MultiSpan::new() }
+    }
 }
 
 impl From<Span> for MultiSpan {
diff --git a/compiler/rustc_errors/messages.ftl b/compiler/rustc_errors/messages.ftl
index 8e8223c3cf8..d68dba0be5e 100644
--- a/compiler/rustc_errors/messages.ftl
+++ b/compiler/rustc_errors/messages.ftl
@@ -1,3 +1,25 @@
+errors_delayed_at_with_newline =
+    delayed at {$emitted_at}
+    {$note}
+
+errors_delayed_at_without_newline =
+    delayed at {$emitted_at} - {$note}
+
+errors_expected_lifetime_parameter =
+    expected lifetime {$count ->
+        [1] parameter
+        *[other] parameters
+    }
+
+errors_indicate_anonymous_lifetime =
+    indicate the anonymous {$count ->
+        [1] lifetime
+        *[other] lifetimes
+    }
+
+errors_invalid_flushed_delayed_diagnostic_level =
+    `flushed_delayed` got diagnostic with level {$level}, instead of the expected `DelayedBug`
+
 errors_target_inconsistent_architecture =
     inconsistent target specification: "data-layout" claims architecture is {$dl}-endian, while "target-endian" is `{$target}`
 
diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs
index ed0d06ed0ff..a96e317df55 100644
--- a/compiler/rustc_errors/src/diagnostic.rs
+++ b/compiler/rustc_errors/src/diagnostic.rs
@@ -420,13 +420,13 @@ impl Diagnostic {
         let expected_label = if expected_label.is_empty() {
             "expected".to_string()
         } else {
-            format!("expected {}", expected_label)
+            format!("expected {expected_label}")
         };
         let found_label = found_label.to_string();
         let found_label = if found_label.is_empty() {
             "found".to_string()
         } else {
-            format!("found {}", found_label)
+            format!("found {found_label}")
         };
         let (found_padding, expected_padding) = if expected_label.len() > found_label.len() {
             (expected_label.len() - found_label.len(), 0)
@@ -439,13 +439,13 @@ impl Diagnostic {
             StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle),
             StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight),
         }));
-        msg.push((format!("`{}\n", expected_extra), Style::NoStyle));
+        msg.push((format!("`{expected_extra}\n"), Style::NoStyle));
         msg.push((format!("{}{} `", " ".repeat(found_padding), found_label), Style::NoStyle));
         msg.extend(found.0.iter().map(|x| match *x {
             StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle),
             StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight),
         }));
-        msg.push((format!("`{}", found_extra), Style::NoStyle));
+        msg.push((format!("`{found_extra}"), Style::NoStyle));
 
         // For now, just attach these as notes.
         self.highlighted_note(msg);
@@ -454,7 +454,7 @@ impl Diagnostic {
 
     pub fn note_trait_signature(&mut self, name: Symbol, signature: String) -> &mut Self {
         self.highlighted_note(vec![
-            (format!("`{}` from trait: `", name), Style::NoStyle),
+            (format!("`{name}` from trait: `"), Style::NoStyle),
             (signature, Style::Highlight),
             ("`".to_string(), Style::NoStyle),
         ]);
diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs
index 08ff2cfba5c..5e23ae655fe 100644
--- a/compiler/rustc_errors/src/diagnostic_builder.rs
+++ b/compiler/rustc_errors/src/diagnostic_builder.rs
@@ -536,7 +536,9 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
             }
         };
 
-        if handler.flags.dont_buffer_diagnostics || handler.flags.treat_err_as_bug.is_some() {
+        if handler.inner.lock().flags.dont_buffer_diagnostics
+            || handler.inner.lock().flags.treat_err_as_bug.is_some()
+        {
             self.emit();
             return None;
         }
diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs
index 10fe7fc74a8..a170e3a8943 100644
--- a/compiler/rustc_errors/src/diagnostic_impls.rs
+++ b/compiler/rustc_errors/src/diagnostic_impls.rs
@@ -1,3 +1,4 @@
+use crate::diagnostic::DiagnosticLocation;
 use crate::{fluent_generated as fluent, AddToDiagnostic};
 use crate::{DiagnosticArgValue, DiagnosticBuilder, Handler, IntoDiagnostic, IntoDiagnosticArg};
 use rustc_ast as ast;
@@ -10,6 +11,7 @@ use rustc_span::Span;
 use rustc_target::abi::TargetDataLayoutErrors;
 use rustc_target::spec::{PanicStrategy, SplitDebuginfo, StackProtector, TargetTriple};
 use rustc_type_ir as type_ir;
+use std::backtrace::Backtrace;
 use std::borrow::Cow;
 use std::fmt;
 use std::num::ParseIntError;
@@ -102,7 +104,7 @@ impl IntoDiagnosticArg for bool {
 
 impl IntoDiagnosticArg for char {
     fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
-        DiagnosticArgValue::Str(Cow::Owned(format!("{:?}", self)))
+        DiagnosticArgValue::Str(Cow::Owned(format!("{self:?}")))
     }
 }
 
@@ -164,6 +166,12 @@ impl IntoDiagnosticArg for hir::ConstContext {
     }
 }
 
+impl IntoDiagnosticArg for ast::Expr {
+    fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
+        DiagnosticArgValue::Str(Cow::Owned(pprust::expr_to_string(&self)))
+    }
+}
+
 impl IntoDiagnosticArg for ast::Path {
     fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
         DiagnosticArgValue::Str(Cow::Owned(pprust::path_to_string(&self)))
@@ -311,3 +319,62 @@ pub enum LabelKind {
     Label,
     Help,
 }
+
+#[derive(Subdiagnostic)]
+#[label(errors_expected_lifetime_parameter)]
+pub struct ExpectedLifetimeParameter {
+    #[primary_span]
+    pub span: Span,
+    pub count: usize,
+}
+
+#[derive(Subdiagnostic)]
+#[note(errors_delayed_at_with_newline)]
+pub struct DelayedAtWithNewline {
+    #[primary_span]
+    pub span: Span,
+    pub emitted_at: DiagnosticLocation,
+    pub note: Backtrace,
+}
+#[derive(Subdiagnostic)]
+#[note(errors_delayed_at_without_newline)]
+pub struct DelayedAtWithoutNewline {
+    #[primary_span]
+    pub span: Span,
+    pub emitted_at: DiagnosticLocation,
+    pub note: Backtrace,
+}
+
+impl IntoDiagnosticArg for DiagnosticLocation {
+    fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
+        DiagnosticArgValue::Str(Cow::from(self.to_string()))
+    }
+}
+
+impl IntoDiagnosticArg for Backtrace {
+    fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
+        DiagnosticArgValue::Str(Cow::from(self.to_string()))
+    }
+}
+
+#[derive(Subdiagnostic)]
+#[note(errors_invalid_flushed_delayed_diagnostic_level)]
+pub struct InvalidFlushedDelayedDiagnosticLevel {
+    #[primary_span]
+    pub span: Span,
+    pub level: rustc_errors::Level,
+}
+impl IntoDiagnosticArg for rustc_errors::Level {
+    fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
+        DiagnosticArgValue::Str(Cow::from(self.to_string()))
+    }
+}
+
+#[derive(Subdiagnostic)]
+#[suggestion(errors_indicate_anonymous_lifetime, code = "{suggestion}", style = "verbose")]
+pub struct IndicateAnonymousLifetime {
+    #[primary_span]
+    pub span: Span,
+    pub count: usize,
+    pub suggestion: String,
+}
diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs
index a0fa4115c3e..961feba3250 100644
--- a/compiler/rustc_errors/src/emitter.rs
+++ b/compiler/rustc_errors/src/emitter.rs
@@ -279,12 +279,12 @@ pub trait Emitter: Translate {
                 let msg = if substitution.is_empty() || sugg.style.hide_inline() {
                     // This substitution is only removal OR we explicitly don't want to show the
                     // code inline (`hide_inline`). Therefore, we don't show the substitution.
-                    format!("help: {}", &msg)
+                    format!("help: {msg}")
                 } else {
                     // Show the default suggestion text with the substitution
                     format!(
                         "help: {}{}: `{}`",
-                        &msg,
+                        msg,
                         if self.source_map().is_some_and(|sm| is_case_difference(
                             sm,
                             substitution,
diff --git a/compiler/rustc_errors/src/json/tests.rs b/compiler/rustc_errors/src/json/tests.rs
index db0dd4ffe8e..1f9a2981e02 100644
--- a/compiler/rustc_errors/src/json/tests.rs
+++ b/compiler/rustc_errors/src/json/tests.rs
@@ -64,7 +64,7 @@ fn test_positions(code: &str, span: (u32, u32), expected_output: SpanTestData) {
         );
 
         let span = Span::with_root_ctxt(BytePos(span.0), BytePos(span.1));
-        let handler = Handler::with_emitter(true, None, Box::new(je), None);
+        let handler = Handler::with_emitter(Box::new(je));
         handler.span_err(span, "foo");
 
         let bytes = output.lock().unwrap();
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs
index 1da02e1bb01..e01e80939ca 100644
--- a/compiler/rustc_errors/src/lib.rs
+++ b/compiler/rustc_errors/src/lib.rs
@@ -22,6 +22,8 @@ extern crate rustc_macros;
 #[macro_use]
 extern crate tracing;
 
+extern crate self as rustc_errors;
+
 pub use emitter::ColorConfig;
 
 use rustc_lint_defs::LintExpectationId;
@@ -377,13 +379,16 @@ pub struct ExplicitBug;
 /// rather than a failed assertion, etc.
 pub struct DelayedBugPanic;
 
+use crate::diagnostic_impls::{DelayedAtWithNewline, DelayedAtWithoutNewline};
 pub use diagnostic::{
     AddToDiagnostic, DecorateLint, Diagnostic, DiagnosticArg, DiagnosticArgValue, DiagnosticId,
     DiagnosticStyledString, IntoDiagnosticArg, SubDiagnostic,
 };
 pub use diagnostic_builder::{DiagnosticBuilder, EmissionGuarantee, Noted};
 pub use diagnostic_impls::{
-    DiagnosticArgFromDisplay, DiagnosticSymbolList, LabelKind, SingleLabelManySpans,
+    DiagnosticArgFromDisplay, DiagnosticSymbolList, ExpectedLifetimeParameter,
+    IndicateAnonymousLifetime, InvalidFlushedDelayedDiagnosticLevel, LabelKind,
+    SingleLabelManySpans,
 };
 use std::backtrace::{Backtrace, BacktraceStatus};
 
@@ -391,7 +396,6 @@ use std::backtrace::{Backtrace, BacktraceStatus};
 /// Certain errors (fatal, bug, unimpl) may cause immediate exit,
 /// others log errors for later reporting.
 pub struct Handler {
-    flags: HandlerFlags,
     inner: Lock<HandlerInner>,
 }
 
@@ -549,69 +553,47 @@ impl Drop for HandlerInner {
 
 impl Handler {
     pub fn with_tty_emitter(
-        color_config: ColorConfig,
-        can_emit_warnings: bool,
-        treat_err_as_bug: Option<NonZeroUsize>,
-        sm: Option<Lrc<SourceMap>>,
-        fluent_bundle: Option<Lrc<FluentBundle>>,
-        fallback_bundle: LazyFallbackBundle,
-        ice_file: Option<PathBuf>,
-    ) -> Self {
-        Self::with_tty_emitter_and_flags(
-            color_config,
-            sm,
-            fluent_bundle,
-            fallback_bundle,
-            HandlerFlags { can_emit_warnings, treat_err_as_bug, ..Default::default() },
-            ice_file,
-        )
-    }
-
-    pub fn with_tty_emitter_and_flags(
-        color_config: ColorConfig,
         sm: Option<Lrc<SourceMap>>,
-        fluent_bundle: Option<Lrc<FluentBundle>>,
         fallback_bundle: LazyFallbackBundle,
-        flags: HandlerFlags,
-        ice_file: Option<PathBuf>,
     ) -> Self {
         let emitter = Box::new(EmitterWriter::stderr(
-            color_config,
+            ColorConfig::Auto,
             sm,
-            fluent_bundle,
+            None,
             fallback_bundle,
             false,
             false,
             None,
-            flags.macro_backtrace,
-            flags.track_diagnostics,
+            false,
+            false,
             TerminalUrl::No,
         ));
-        Self::with_emitter_and_flags(emitter, flags, ice_file)
+        Self::with_emitter(emitter)
+    }
+    pub fn disable_warnings(mut self) -> Self {
+        self.inner.get_mut().flags.can_emit_warnings = false;
+        self
     }
 
-    pub fn with_emitter(
-        can_emit_warnings: bool,
-        treat_err_as_bug: Option<NonZeroUsize>,
-        emitter: Box<dyn Emitter + sync::Send>,
-        ice_file: Option<PathBuf>,
-    ) -> Self {
-        Handler::with_emitter_and_flags(
-            emitter,
-            HandlerFlags { can_emit_warnings, treat_err_as_bug, ..Default::default() },
-            ice_file,
-        )
+    pub fn treat_err_as_bug(mut self, treat_err_as_bug: NonZeroUsize) -> Self {
+        self.inner.get_mut().flags.treat_err_as_bug = Some(treat_err_as_bug);
+        self
     }
 
-    pub fn with_emitter_and_flags(
-        emitter: Box<dyn Emitter + sync::Send>,
-        flags: HandlerFlags,
-        ice_file: Option<PathBuf>,
-    ) -> Self {
+    pub fn with_flags(mut self, flags: HandlerFlags) -> Self {
+        self.inner.get_mut().flags = flags;
+        self
+    }
+
+    pub fn with_ice_file(mut self, ice_file: PathBuf) -> Self {
+        self.inner.get_mut().ice_file = Some(ice_file);
+        self
+    }
+
+    pub fn with_emitter(emitter: Box<dyn Emitter + sync::Send>) -> Self {
         Self {
-            flags,
             inner: Lock::new(HandlerInner {
-                flags,
+                flags: HandlerFlags { can_emit_warnings: true, ..Default::default() },
                 lint_err_count: 0,
                 err_count: 0,
                 warn_count: 0,
@@ -629,7 +611,7 @@ impl Handler {
                 check_unstable_expect_diagnostics: false,
                 unstable_expect_diagnostics: Vec::new(),
                 fulfilled_expectations: Default::default(),
-                ice_file,
+                ice_file: None,
             }),
         }
     }
@@ -657,7 +639,7 @@ impl Handler {
     // This is here to not allow mutation of flags;
     // as of this writing it's only used in tests in librustc_middle.
     pub fn can_emit_warnings(&self) -> bool {
-        self.flags.can_emit_warnings
+        self.inner.lock().flags.can_emit_warnings
     }
 
     /// Resets the diagnostic error count as well as the cached emitted diagnostics.
@@ -1485,7 +1467,7 @@ impl HandlerInner {
                 let _ = self.fatal(errors);
             }
             (_, _) => {
-                let _ = self.fatal(format!("{}; {}", &errors, &warnings));
+                let _ = self.fatal(format!("{errors}; {warnings}"));
             }
         }
 
@@ -1696,11 +1678,10 @@ impl HandlerInner {
             if bug.level != Level::DelayedBug {
                 // NOTE(eddyb) not panicking here because we're already producing
                 // an ICE, and the more information the merrier.
-                bug.note(format!(
-                    "`flushed_delayed` got diagnostic with level {:?}, \
-                     instead of the expected `DelayedBug`",
-                    bug.level,
-                ));
+                bug.subdiagnostic(InvalidFlushedDelayedDiagnosticLevel {
+                    span: bug.span.primary_span().unwrap(),
+                    level: bug.level,
+                });
             }
             bug.level = Level::Bug;
 
@@ -1767,12 +1748,22 @@ impl DelayedDiagnostic {
     fn decorate(mut self) -> Diagnostic {
         match self.note.status() {
             BacktraceStatus::Captured => {
-                self.inner.note(format!("delayed at {}\n{}", self.inner.emitted_at, self.note));
+                let inner = &self.inner;
+                self.inner.subdiagnostic(DelayedAtWithNewline {
+                    span: inner.span.primary_span().unwrap(),
+                    emitted_at: inner.emitted_at.clone(),
+                    note: self.note,
+                });
             }
             // Avoid the needless newline when no backtrace has been captured,
             // the display impl should just be a single line.
             _ => {
-                self.inner.note(format!("delayed at {} - {}", self.inner.emitted_at, self.note));
+                let inner = &self.inner;
+                self.inner.subdiagnostic(DelayedAtWithoutNewline {
+                    span: inner.span.primary_span().unwrap(),
+                    emitted_at: inner.emitted_at.clone(),
+                    note: self.note,
+                });
             }
         }
 
@@ -1864,7 +1855,7 @@ pub fn add_elided_lifetime_in_path_suggestion(
     incl_angl_brckt: bool,
     insertion_span: Span,
 ) {
-    diag.span_label(path_span, format!("expected lifetime parameter{}", pluralize!(n)));
+    diag.subdiagnostic(ExpectedLifetimeParameter { span: path_span, count: n });
     if !source_map.is_span_accessible(insertion_span) {
         // Do not try to suggest anything if generated by a proc-macro.
         return;
@@ -1872,12 +1863,12 @@ pub fn add_elided_lifetime_in_path_suggestion(
     let anon_lts = vec!["'_"; n].join(", ");
     let suggestion =
         if incl_angl_brckt { format!("<{}>", anon_lts) } else { format!("{}, ", anon_lts) };
-    diag.span_suggestion_verbose(
-        insertion_span.shrink_to_hi(),
-        format!("indicate the anonymous lifetime{}", pluralize!(n)),
+
+    diag.subdiagnostic(IndicateAnonymousLifetime {
+        span: insertion_span.shrink_to_hi(),
+        count: n,
         suggestion,
-        Applicability::MachineApplicable,
-    );
+    });
 }
 
 #[derive(Clone, Copy, PartialEq, Hash, Debug)]
diff --git a/compiler/rustc_errors/src/markdown/tests/term.rs b/compiler/rustc_errors/src/markdown/tests/term.rs
index 3b31c6d6295..6f68fb25a58 100644
--- a/compiler/rustc_errors/src/markdown/tests/term.rs
+++ b/compiler/rustc_errors/src/markdown/tests/term.rs
@@ -63,7 +63,7 @@ fn test_wrapping_write() {
 #[test]
 fn test_output() {
     // Capture `--bless` when run via ./x
-    let bless = std::env::var("RUSTC_BLESS").unwrap_or_default() == "1";
+    let bless = std::env::var_os("RUSTC_BLESS").is_some_and(|v| v != "0");
     let ast = MdStream::parse_str(INPUT);
     let bufwtr = BufferWriter::stderr(ColorChoice::Always);
     let mut buffer = bufwtr.buffer();
diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs
index 4ec5ac22e90..aeb4f6e861b 100644
--- a/compiler/rustc_expand/src/config.rs
+++ b/compiler/rustc_expand/src/config.rs
@@ -365,9 +365,9 @@ impl<'a> StripUnconfigured<'a> {
 
         // Use the `#` in `#[cfg_attr(pred, attr)]` as the `#` token
         // for `attr` when we expand it to `#[attr]`
-        let mut orig_trees = orig_tokens.into_trees();
+        let mut orig_trees = orig_tokens.trees();
         let TokenTree::Token(pound_token @ Token { kind: TokenKind::Pound, .. }, _) =
-            orig_trees.next().unwrap()
+            orig_trees.next().unwrap().clone()
         else {
             panic!("Bad tokens for attribute {:?}", attr);
         };
@@ -377,7 +377,7 @@ impl<'a> StripUnconfigured<'a> {
         if attr.style == AttrStyle::Inner {
             // For inner attributes, we do the same thing for the `!` in `#![some_attr]`
             let TokenTree::Token(bang_token @ Token { kind: TokenKind::Not, .. }, _) =
-                orig_trees.next().unwrap()
+                orig_trees.next().unwrap().clone()
             else {
                 panic!("Bad tokens for attribute {:?}", attr);
             };
diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs
index 102bae2a744..7398a124fdb 100644
--- a/compiler/rustc_expand/src/mbe/macro_rules.rs
+++ b/compiler/rustc_expand/src/mbe/macro_rules.rs
@@ -500,7 +500,7 @@ pub fn compile_declarative_macro(
             .map(|m| {
                 if let MatchedTokenTree(tt) = m {
                     let tt = mbe::quoted::parse(
-                        TokenStream::new(vec![tt.clone()]),
+                        &TokenStream::new(vec![tt.clone()]),
                         true,
                         &sess.parse_sess,
                         def.id,
@@ -524,7 +524,7 @@ pub fn compile_declarative_macro(
             .map(|m| {
                 if let MatchedTokenTree(tt) = m {
                     return mbe::quoted::parse(
-                        TokenStream::new(vec![tt.clone()]),
+                        &TokenStream::new(vec![tt.clone()]),
                         false,
                         &sess.parse_sess,
                         def.id,
diff --git a/compiler/rustc_expand/src/mbe/quoted.rs b/compiler/rustc_expand/src/mbe/quoted.rs
index 40bfa3715be..ac862ae8c4f 100644
--- a/compiler/rustc_expand/src/mbe/quoted.rs
+++ b/compiler/rustc_expand/src/mbe/quoted.rs
@@ -36,7 +36,7 @@ const VALID_FRAGMENT_NAMES_MSG: &str = "valid fragment specifiers are \
 ///
 /// A collection of `self::TokenTree`. There may also be some errors emitted to `sess`.
 pub(super) fn parse(
-    input: tokenstream::TokenStream,
+    input: &tokenstream::TokenStream,
     parsing_patterns: bool,
     sess: &ParseSess,
     node_id: NodeId,
@@ -48,7 +48,7 @@ pub(super) fn parse(
 
     // For each token tree in `input`, parse the token into a `self::TokenTree`, consuming
     // additional trees if need be.
-    let mut trees = input.into_trees();
+    let mut trees = input.trees();
     while let Some(tree) = trees.next() {
         // Given the parsed tree, if there is a metavar and we are expecting matchers, actually
         // parse out the matcher (i.e., in `$id:ident` this would parse the `:` and `ident`).
@@ -56,7 +56,7 @@ pub(super) fn parse(
         match tree {
             TokenTree::MetaVar(start_sp, ident) if parsing_patterns => {
                 let span = match trees.next() {
-                    Some(tokenstream::TokenTree::Token(Token { kind: token::Colon, span }, _)) => {
+                    Some(&tokenstream::TokenTree::Token(Token { kind: token::Colon, span }, _)) => {
                         match trees.next() {
                             Some(tokenstream::TokenTree::Token(token, _)) => match token.ident() {
                                 Some((frag, _)) => {
@@ -96,10 +96,10 @@ pub(super) fn parse(
                                 }
                                 _ => token.span,
                             },
-                            tree => tree.as_ref().map_or(span, tokenstream::TokenTree::span),
+                            tree => tree.map_or(span, tokenstream::TokenTree::span),
                         }
                     }
-                    tree => tree.as_ref().map_or(start_sp, tokenstream::TokenTree::span),
+                    tree => tree.map_or(start_sp, tokenstream::TokenTree::span),
                 };
 
                 result.push(TokenTree::MetaVarDecl(span, ident, None));
@@ -134,9 +134,9 @@ fn maybe_emit_macro_metavar_expr_feature(features: &Features, sess: &ParseSess,
 /// - `parsing_patterns`: same as [parse].
 /// - `sess`: the parsing session. Any errors will be emitted to this session.
 /// - `features`: language features so we can do feature gating.
-fn parse_tree(
-    tree: tokenstream::TokenTree,
-    outer_trees: &mut impl Iterator<Item = tokenstream::TokenTree>,
+fn parse_tree<'a>(
+    tree: &'a tokenstream::TokenTree,
+    outer_trees: &mut impl Iterator<Item = &'a tokenstream::TokenTree>,
     parsing_patterns: bool,
     sess: &ParseSess,
     node_id: NodeId,
@@ -146,13 +146,13 @@ fn parse_tree(
     // Depending on what `tree` is, we could be parsing different parts of a macro
     match tree {
         // `tree` is a `$` token. Look at the next token in `trees`
-        tokenstream::TokenTree::Token(Token { kind: token::Dollar, span }, _) => {
+        &tokenstream::TokenTree::Token(Token { kind: token::Dollar, span }, _) => {
             // FIXME: Handle `Invisible`-delimited groups in a more systematic way
             // during parsing.
             let mut next = outer_trees.next();
-            let mut trees: Box<dyn Iterator<Item = tokenstream::TokenTree>>;
+            let mut trees: Box<dyn Iterator<Item = &tokenstream::TokenTree>>;
             if let Some(tokenstream::TokenTree::Delimited(_, Delimiter::Invisible, tts)) = next {
-                trees = Box::new(tts.into_trees());
+                trees = Box::new(tts.trees());
                 next = trees.next();
             } else {
                 trees = Box::new(outer_trees);
@@ -160,7 +160,7 @@ fn parse_tree(
 
             match next {
                 // `tree` is followed by a delimited set of token trees.
-                Some(tokenstream::TokenTree::Delimited(delim_span, delim, tts)) => {
+                Some(&tokenstream::TokenTree::Delimited(delim_span, delim, ref tts)) => {
                     if parsing_patterns {
                         if delim != Delimiter::Parenthesis {
                             span_dollar_dollar_or_metavar_in_the_lhs_err(
@@ -228,7 +228,7 @@ fn parse_tree(
                 }
 
                 // `tree` is followed by another `$`. This is an escaped `$`.
-                Some(tokenstream::TokenTree::Token(Token { kind: token::Dollar, span }, _)) => {
+                Some(&tokenstream::TokenTree::Token(Token { kind: token::Dollar, span }, _)) => {
                     if parsing_patterns {
                         span_dollar_dollar_or_metavar_in_the_lhs_err(
                             sess,
@@ -256,11 +256,11 @@ fn parse_tree(
         }
 
         // `tree` is an arbitrary token. Keep it.
-        tokenstream::TokenTree::Token(token, _) => TokenTree::Token(token),
+        tokenstream::TokenTree::Token(token, _) => TokenTree::Token(token.clone()),
 
         // `tree` is the beginning of a delimited set of tokens (e.g., `(` or `{`). We need to
         // descend into the delimited set and further parse it.
-        tokenstream::TokenTree::Delimited(span, delim, tts) => TokenTree::Delimited(
+        &tokenstream::TokenTree::Delimited(span, delim, ref tts) => TokenTree::Delimited(
             span,
             Delimited {
                 delim,
@@ -286,16 +286,16 @@ fn kleene_op(token: &Token) -> Option<KleeneOp> {
 /// - Ok(Ok((op, span))) if the next token tree is a KleeneOp
 /// - Ok(Err(tok, span)) if the next token tree is a token but not a KleeneOp
 /// - Err(span) if the next token tree is not a token
-fn parse_kleene_op(
-    input: &mut impl Iterator<Item = tokenstream::TokenTree>,
+fn parse_kleene_op<'a>(
+    input: &mut impl Iterator<Item = &'a tokenstream::TokenTree>,
     span: Span,
 ) -> Result<Result<(KleeneOp, Span), Token>, Span> {
     match input.next() {
         Some(tokenstream::TokenTree::Token(token, _)) => match kleene_op(&token) {
             Some(op) => Ok(Ok((op, token.span))),
-            None => Ok(Err(token)),
+            None => Ok(Err(token.clone())),
         },
-        tree => Err(tree.as_ref().map_or(span, tokenstream::TokenTree::span)),
+        tree => Err(tree.map_or(span, tokenstream::TokenTree::span)),
     }
 }
 
@@ -311,8 +311,8 @@ fn parse_kleene_op(
 /// session `sess`. If the next one (or possibly two) tokens in `input` correspond to a Kleene
 /// operator and separator, then a tuple with `(separator, KleeneOp)` is returned. Otherwise, an
 /// error with the appropriate span is emitted to `sess` and a dummy value is returned.
-fn parse_sep_and_kleene_op(
-    input: &mut impl Iterator<Item = tokenstream::TokenTree>,
+fn parse_sep_and_kleene_op<'a>(
+    input: &mut impl Iterator<Item = &'a tokenstream::TokenTree>,
     span: Span,
     sess: &ParseSess,
 ) -> (Option<Token>, KleeneToken) {
diff --git a/compiler/rustc_expand/src/parse/tests.rs b/compiler/rustc_expand/src/parse/tests.rs
index 8b37728b60f..bdc20882a9d 100644
--- a/compiler/rustc_expand/src/parse/tests.rs
+++ b/compiler/rustc_expand/src/parse/tests.rs
@@ -1,4 +1,6 @@
-use crate::tests::{matches_codepattern, string_to_stream, with_error_checking_parse};
+use crate::tests::{
+    matches_codepattern, string_to_stream, with_error_checking_parse, with_expected_parse_error,
+};
 
 use rustc_ast::ptr::P;
 use rustc_ast::token::{self, Delimiter, Token};
@@ -51,11 +53,15 @@ fn string_to_item(source_str: String) -> Option<P<ast::Item>> {
     with_error_checking_parse(source_str, &sess(), |p| p.parse_item(ForceCollect::No))
 }
 
-#[should_panic]
 #[test]
 fn bad_path_expr_1() {
+    // This should trigger error: expected identifier, found keyword `return`
     create_default_session_globals_then(|| {
-        string_to_expr("::abc::def::return".to_string());
+        with_expected_parse_error(
+            "::abc::def::return",
+            "expected identifier, found keyword `return`",
+            |p| p.parse_expr(),
+        );
     })
 }
 
@@ -63,9 +69,8 @@ fn bad_path_expr_1() {
 #[test]
 fn string_to_tts_macro() {
     create_default_session_globals_then(|| {
-        let tts: Vec<_> =
-            string_to_stream("macro_rules! zip (($a)=>($a))".to_string()).into_trees().collect();
-        let tts: &[TokenTree] = &tts[..];
+        let stream = string_to_stream("macro_rules! zip (($a)=>($a))".to_string());
+        let tts = &stream.trees().collect::<Vec<_>>()[..];
 
         match tts {
             [
@@ -294,9 +299,7 @@ fn ttdelim_span() {
         .unwrap();
 
         let ast::ExprKind::MacCall(mac) = &expr.kind else { panic!("not a macro") };
-        let tts: Vec<_> = mac.args.tokens.clone().into_trees().collect();
-
-        let span = tts.iter().rev().next().unwrap().span();
+        let span = mac.args.tokens.trees().last().unwrap().span();
 
         match sess.source_map().span_to_snippet(span) {
             Ok(s) => assert_eq!(&s[..], "{ body }"),
diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs
index ecd2315112a..ac73b5d72b7 100644
--- a/compiler/rustc_expand/src/proc_macro_server.rs
+++ b/compiler/rustc_expand/src/proc_macro_server.rs
@@ -94,10 +94,10 @@ impl FromInternal<(TokenStream, &mut Rustc<'_, '_>)> for Vec<TokenTree<TokenStre
         // Estimate the capacity as `stream.len()` rounded up to the next power
         // of two to limit the number of required reallocations.
         let mut trees = Vec::with_capacity(stream.len().next_power_of_two());
-        let mut cursor = stream.into_trees();
+        let mut cursor = stream.trees();
 
         while let Some(tree) = cursor.next() {
-            let (Token { kind, span }, joint) = match tree {
+            let (Token { kind, span }, joint) = match tree.clone() {
                 tokenstream::TokenTree::Delimited(span, delim, tts) => {
                     let delimiter = pm::Delimiter::from_internal(delim);
                     trees.push(TokenTree::Group(Group {
diff --git a/compiler/rustc_expand/src/tests.rs b/compiler/rustc_expand/src/tests.rs
index 6490e52955d..30fa5fea407 100644
--- a/compiler/rustc_expand/src/tests.rs
+++ b/compiler/rustc_expand/src/tests.rs
@@ -22,6 +22,33 @@ fn string_to_parser(ps: &ParseSess, source_str: String) -> Parser<'_> {
     new_parser_from_source_str(ps, PathBuf::from("bogofile").into(), source_str)
 }
 
+fn create_test_handler() -> (Handler, Lrc<SourceMap>, Arc<Mutex<Vec<u8>>>) {
+    let output = Arc::new(Mutex::new(Vec::new()));
+    let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty()));
+    let fallback_bundle = rustc_errors::fallback_fluent_bundle(
+        vec![crate::DEFAULT_LOCALE_RESOURCE, rustc_parse::DEFAULT_LOCALE_RESOURCE],
+        false,
+    );
+    let emitter = EmitterWriter::new(
+        Box::new(Shared { data: output.clone() }),
+        Some(source_map.clone()),
+        None,
+        fallback_bundle,
+        false,
+        false,
+        false,
+        Some(140),
+        false,
+        false,
+        TerminalUrl::No,
+    );
+    let handler = Handler::with_emitter(Box::new(emitter));
+    (handler, source_map, output)
+}
+
+/// Returns the result of parsing the given string via the given callback.
+///
+/// If there are any errors, this will panic.
 pub(crate) fn with_error_checking_parse<'a, T, F>(s: String, ps: &'a ParseSess, f: F) -> T
 where
     F: FnOnce(&mut Parser<'a>) -> PResult<'a, T>,
@@ -32,6 +59,26 @@ where
     x
 }
 
+/// Verifies that parsing the given string using the given callback will
+/// generate an error that contains the given text.
+pub(crate) fn with_expected_parse_error<T, F>(source_str: &str, expected_output: &str, f: F)
+where
+    F: for<'a> FnOnce(&mut Parser<'a>) -> PResult<'a, T>,
+{
+    let (handler, source_map, output) = create_test_handler();
+    let ps = ParseSess::with_span_handler(handler, source_map);
+    let mut p = string_to_parser(&ps, source_str.to_string());
+    let result = f(&mut p);
+    assert!(result.is_ok());
+
+    let bytes = output.lock().unwrap();
+    let actual_output = str::from_utf8(&bytes).unwrap();
+    println!("expected output:\n------\n{}------", expected_output);
+    println!("actual output:\n------\n{}------", actual_output);
+
+    assert!(actual_output.contains(expected_output))
+}
+
 /// Maps a string to tts, using a made-up filename.
 pub(crate) fn string_to_stream(source_str: String) -> TokenStream {
     let ps = ParseSess::new(
@@ -130,13 +177,7 @@ impl<T: Write> Write for Shared<T> {
 
 fn test_harness(file_text: &str, span_labels: Vec<SpanLabel>, expected_output: &str) {
     create_default_session_if_not_set_then(|_| {
-        let output = Arc::new(Mutex::new(Vec::new()));
-
-        let fallback_bundle = rustc_errors::fallback_fluent_bundle(
-            vec![crate::DEFAULT_LOCALE_RESOURCE, rustc_parse::DEFAULT_LOCALE_RESOURCE],
-            false,
-        );
-        let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty()));
+        let (handler, source_map, output) = create_test_handler();
         source_map.new_source_file(Path::new("test.rs").to_owned().into(), file_text.to_owned());
 
         let primary_span = make_span(&file_text, &span_labels[0].start, &span_labels[0].end);
@@ -148,20 +189,6 @@ fn test_harness(file_text: &str, span_labels: Vec<SpanLabel>, expected_output: &
             println!("text: {:?}", source_map.span_to_snippet(span));
         }
 
-        let emitter = EmitterWriter::new(
-            Box::new(Shared { data: output.clone() }),
-            Some(source_map.clone()),
-            None,
-            fallback_bundle,
-            false,
-            false,
-            false,
-            None,
-            false,
-            false,
-            TerminalUrl::No,
-        );
-        let handler = Handler::with_emitter(true, None, Box::new(emitter), None);
         #[allow(rustc::untranslatable_diagnostic)]
         handler.span_err(msp, "foo");
 
diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs
index 56a2c5eff3d..c03789f500a 100644
--- a/compiler/rustc_feature/src/active.rs
+++ b/compiler/rustc_feature/src/active.rs
@@ -379,6 +379,8 @@ declare_features! (
     (active, deprecated_safe, "1.61.0", Some(94978), None),
     /// Allows having using `suggestion` in the `#[deprecated]` attribute.
     (active, deprecated_suggestion, "1.61.0", Some(94785), None),
+    /// Allows using the `#[diagnostic]` attribute tool namespace
+    (active, diagnostic_namespace, "CURRENT_RUSTC_VERSION", Some(94785), None),
     /// Controls errors in trait implementations.
     (active, do_not_recommend, "1.67.0", Some(51992), None),
     /// Tells rustdoc to automatically generate `#[doc(cfg(...))]`.
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index 68f1559ea22..3663c450ba6 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -3013,8 +3013,7 @@ pub struct FieldDef<'hir> {
 impl FieldDef<'_> {
     // Still necessary in couple of places
     pub fn is_positional(&self) -> bool {
-        let first = self.ident.as_str().as_bytes()[0];
-        (b'0'..=b'9').contains(&first)
+        self.ident.as_str().as_bytes()[0].is_ascii_digit()
     }
 }
 
diff --git a/compiler/rustc_hir_analysis/src/astconv/errors.rs b/compiler/rustc_hir_analysis/src/astconv/errors.rs
index 7f4927bbb98..bd311c98fac 100644
--- a/compiler/rustc_hir_analysis/src/astconv/errors.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/errors.rs
@@ -197,7 +197,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
             }
         }
 
-        err.span_label(span, format!("associated type `{}` not found", assoc_name));
+        err.span_label(span, format!("associated type `{assoc_name}` not found"));
         err.emit()
     }
 
@@ -393,7 +393,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
             .into_iter()
             .map(|error| error.root_obligation.predicate)
             .filter_map(format_pred)
-            .map(|(p, _)| format!("`{}`", p))
+            .map(|(p, _)| format!("`{p}`"))
             .collect();
         bounds.sort();
         bounds.dedup();
@@ -652,7 +652,7 @@ pub(crate) fn fn_trait_to_string(
             }
             .map(|s| {
                 // `s.empty()` checks to see if the type is the unit tuple, if so we don't want a comma
-                if parenthesized || s.is_empty() { format!("({})", s) } else { format!("({},)", s) }
+                if parenthesized || s.is_empty() { format!("({s})") } else { format!("({s},)") }
             })
             .ok(),
             _ => None,
diff --git a/compiler/rustc_hir_analysis/src/astconv/generics.rs b/compiler/rustc_hir_analysis/src/astconv/generics.rs
index e81c61d80ed..1372cc896be 100644
--- a/compiler/rustc_hir_analysis/src/astconv/generics.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/generics.rs
@@ -81,7 +81,7 @@ fn generic_arg_mismatch_err(
                         err.span_suggestion(
                             tcx.def_span(src_def_id),
                             "consider changing this type parameter to a const parameter",
-                            format!("const {}: {}", param_name, param_type),
+                            format!("const {param_name}: {param_type}"),
                             Applicability::MaybeIncorrect,
                         );
                     };
@@ -102,7 +102,7 @@ fn generic_arg_mismatch_err(
                 err.span_suggestion(
                     arg.span(),
                     "array type provided where a `usize` was expected, try",
-                    format!("{{ {} }}", snippet),
+                    format!("{{ {snippet} }}"),
                     Applicability::MaybeIncorrect,
                 );
             }
@@ -130,7 +130,7 @@ fn generic_arg_mismatch_err(
         } else {
             (arg.descr(), param.kind.descr())
         };
-        err.note(format!("{} arguments must be provided before {} arguments", first, last));
+        err.note(format!("{first} arguments must be provided before {last} arguments"));
         if let Some(help) = help {
             err.help(help);
         }
@@ -304,7 +304,7 @@ pub fn create_args_for_parent_generic_args<'tcx, 'a>(
                                         "reorder the arguments: {}: `<{}>`",
                                         param_types_present
                                             .into_iter()
-                                            .map(|ord| format!("{}s", ord))
+                                            .map(|ord| format!("{ord}s"))
                                             .collect::<Vec<String>>()
                                             .join(", then "),
                                         ordered_params
diff --git a/compiler/rustc_hir_analysis/src/astconv/lint.rs b/compiler/rustc_hir_analysis/src/astconv/lint.rs
index ff55174f97a..1bd1270beaf 100644
--- a/compiler/rustc_hir_analysis/src/astconv/lint.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/lint.rs
@@ -34,9 +34,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
             let param_name = generics.params.next_type_param_name(None);
 
             let add_generic_sugg = if let Some(span) = generics.span_for_param_suggestion() {
-                (span, format!(", {}: {}", param_name, impl_trait_name))
+                (span, format!(", {param_name}: {impl_trait_name}"))
             } else {
-                (generics.span, format!("<{}: {}>", param_name, impl_trait_name))
+                (generics.span, format!("<{param_name}: {impl_trait_name}>"))
             };
             diag.multipart_suggestion(
             format!("alternatively use a blanket \
diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs
index ecbbfd92539..3235a9ceba1 100644
--- a/compiler/rustc_hir_analysis/src/astconv/mod.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs
@@ -1128,7 +1128,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                     ty_param_name
                 )
             };
-            err.span_label(span, format!("ambiguous associated type `{}`", assoc_name));
+            err.span_label(span, format!("ambiguous associated type `{assoc_name}`"));
 
             let mut where_bounds = vec![];
             for bound in bounds {
@@ -1407,7 +1407,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
             _ => {
                 let reported = if variant_resolution.is_some() {
                     // Variant in type position
-                    let msg = format!("expected type, found variant `{}`", assoc_ident);
+                    let msg = format!("expected type, found variant `{assoc_ident}`");
                     tcx.sess.span_err(span, msg)
                 } else if qself_ty.is_enum() {
                     let mut err = struct_span_err!(
@@ -1438,12 +1438,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                     } else {
                         err.span_label(
                             assoc_ident.span,
-                            format!("variant not found in `{}`", qself_ty),
+                            format!("variant not found in `{qself_ty}`"),
                         );
                     }
 
                     if let Some(sp) = tcx.hir().span_if_local(adt_def.did()) {
-                        err.span_label(sp, format!("variant `{}` not found here", assoc_ident));
+                        err.span_label(sp, format!("variant `{assoc_ident}` not found here"));
                     }
 
                     err.emit()
@@ -2750,7 +2750,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                 ty::BrNamed(_, kw::UnderscoreLifetime) | ty::BrAnon(..) | ty::BrEnv => {
                     "an anonymous lifetime".to_string()
                 }
-                ty::BrNamed(_, name) => format!("lifetime `{}`", name),
+                ty::BrNamed(_, name) => format!("lifetime `{name}`"),
             };
 
             let mut err = generate_err(&br_name);
diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index e0698a72335..84452979aa5 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -19,11 +19,13 @@ use rustc_lint_defs::builtin::REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS;
 use rustc_middle::hir::nested_filter;
 use rustc_middle::middle::stability::EvalResult;
 use rustc_middle::traits::DefiningAnchor;
+use rustc_middle::ty::fold::BottomUpFolder;
 use rustc_middle::ty::layout::{LayoutError, MAX_SIMD_LANES};
 use rustc_middle::ty::util::{Discr, IntTypeExt};
 use rustc_middle::ty::GenericArgKind;
 use rustc_middle::ty::{
-    self, AdtDef, ParamEnv, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
+    self, AdtDef, ParamEnv, RegionKind, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable,
+    TypeVisitableExt,
 };
 use rustc_session::lint::builtin::{UNINHABITED_STATIC, UNSUPPORTED_CALLING_CONVENTIONS};
 use rustc_span::symbol::sym;
@@ -34,6 +36,7 @@ use rustc_trait_selection::traits::error_reporting::on_unimplemented::OnUnimplem
 use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
 use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _;
 use rustc_trait_selection::traits::{self, ObligationCtxt, TraitEngine, TraitEngineExt as _};
+use rustc_type_ir::fold::TypeFoldable;
 
 use std::ops::ControlFlow;
 
@@ -342,7 +345,7 @@ pub(super) fn check_opaque_for_inheriting_lifetimes(
                 err.span_suggestion(
                     span,
                     "consider spelling out the type instead",
-                    name.unwrap_or_else(|| format!("{:?}", ty)),
+                    name.unwrap_or_else(|| format!("{ty:?}")),
                     Applicability::MaybeIncorrect,
                 );
             }
@@ -437,7 +440,7 @@ fn check_opaque_meets_bounds<'tcx>(
     // hidden type is well formed even without those bounds.
     let predicate =
         ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(hidden_ty.into())));
-    ocx.register_obligation(Obligation::new(tcx, misc_cause, param_env, predicate));
+    ocx.register_obligation(Obligation::new(tcx, misc_cause.clone(), param_env, predicate));
 
     // Check that all obligations are satisfied by the implementation's
     // version.
@@ -464,11 +467,179 @@ fn check_opaque_meets_bounds<'tcx>(
             ocx.resolve_regions_and_report_errors(defining_use_anchor, &outlives_env)?;
         }
     }
-    // Clean up after ourselves
-    let _ = infcx.take_opaque_types();
+    // Check that any hidden types found during wf checking match the hidden types that `type_of` sees.
+    for (key, mut ty) in infcx.take_opaque_types() {
+        ty.hidden_type.ty = infcx.resolve_vars_if_possible(ty.hidden_type.ty);
+        sanity_check_found_hidden_type(tcx, key, ty.hidden_type, defining_use_anchor, origin)?;
+    }
     Ok(())
 }
 
+fn sanity_check_found_hidden_type<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    key: ty::OpaqueTypeKey<'tcx>,
+    mut ty: ty::OpaqueHiddenType<'tcx>,
+    defining_use_anchor: LocalDefId,
+    origin: &hir::OpaqueTyOrigin,
+) -> Result<(), ErrorGuaranteed> {
+    if ty.ty.is_ty_var() {
+        // Nothing was actually constrained.
+        return Ok(());
+    }
+    if let ty::Alias(ty::Opaque, alias) = ty.ty.kind() {
+        if alias.def_id == key.def_id.to_def_id() && alias.args == key.args {
+            // Nothing was actually constrained, this is an opaque usage that was
+            // only discovered to be opaque after inference vars resolved.
+            return Ok(());
+        }
+    }
+    // Closures frequently end up containing erased lifetimes in their final representation.
+    // These correspond to lifetime variables that never got resolved, so we patch this up here.
+    ty.ty = ty.ty.fold_with(&mut BottomUpFolder {
+        tcx,
+        ty_op: |t| t,
+        ct_op: |c| c,
+        lt_op: |l| match l.kind() {
+            RegionKind::ReVar(_) => tcx.lifetimes.re_erased,
+            _ => l,
+        },
+    });
+    // Get the hidden type.
+    let mut hidden_ty = tcx.type_of(key.def_id).instantiate(tcx, key.args);
+    if let hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) = origin {
+        if hidden_ty != ty.ty {
+            hidden_ty = find_and_apply_rpit_args(
+                tcx,
+                hidden_ty,
+                defining_use_anchor.to_def_id(),
+                key.def_id.to_def_id(),
+            )?;
+        }
+    }
+
+    // If the hidden types differ, emit a type mismatch diagnostic.
+    if hidden_ty == ty.ty {
+        Ok(())
+    } else {
+        let span = tcx.def_span(key.def_id);
+        let other = ty::OpaqueHiddenType { ty: hidden_ty, span };
+        Err(ty.report_mismatch(&other, key.def_id, tcx).emit())
+    }
+}
+
+/// In case it is in a nested opaque type, find that opaque type's
+/// usage in the function signature and use the generic arguments from the usage site.
+/// We need to do because RPITs ignore the lifetimes of the function,
+/// as they have their own copies of all the lifetimes they capture.
+/// So the only way to get the lifetimes represented in terms of the function,
+/// is to look how they are used in the function signature (or do some other fancy
+/// recording of this mapping at ast -> hir lowering time).
+///
+/// As an example:
+/// ```text
+/// trait Id {
+///     type Assoc;
+/// }
+/// impl<'a> Id for &'a () {
+///     type Assoc = &'a ();
+/// }
+/// fn func<'a>(x: &'a ()) -> impl Id<Assoc = impl Sized + 'a> { x }
+/// // desugared to
+/// fn func<'a>(x: &'a () -> Outer<'a> where <Outer<'a> as Id>::Assoc = Inner<'a> {
+///     // Note that in contrast to other nested items, RPIT type aliases can
+///     // access their parents' generics.
+///
+///     // hidden type is `&'aDupOuter ()`
+///     // During wfcheck the hidden type of `Inner<'aDupOuter>` is `&'a ()`, but
+///     // `typeof(Inner<'aDupOuter>) = &'aDupOuter ()`.
+///     // So we walk the signature of `func` to find the use of `Inner<'a>`
+///     // and then use that to replace the lifetimes in the hidden type, obtaining
+///     // `&'a ()`.
+///     type Outer<'aDupOuter> = impl Id<Assoc = Inner<'aDupOuter>>;
+///
+///     // hidden type is `&'aDupInner ()`
+///     type Inner<'aDupInner> = impl Sized + 'aDupInner;
+///
+///     x
+/// }
+/// ```
+fn find_and_apply_rpit_args<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    mut hidden_ty: Ty<'tcx>,
+    function: DefId,
+    opaque: DefId,
+) -> Result<Ty<'tcx>, ErrorGuaranteed> {
+    // Find use of the RPIT in the function signature and thus find the right args to
+    // convert it into the parameter space of the function signature. This is needed,
+    // because that's what `type_of` returns, against which we compare later.
+    let ret = tcx.fn_sig(function).instantiate_identity().output();
+    struct Visitor<'tcx> {
+        tcx: TyCtxt<'tcx>,
+        opaque: DefId,
+        function: DefId,
+        seen: FxHashSet<DefId>,
+    }
+    impl<'tcx> ty::TypeVisitor<TyCtxt<'tcx>> for Visitor<'tcx> {
+        type BreakTy = GenericArgsRef<'tcx>;
+
+        #[instrument(level = "trace", skip(self), ret)]
+        fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+            trace!("{:#?}", t.kind());
+            match t.kind() {
+                ty::Alias(ty::Opaque, alias) => {
+                    trace!(?alias.def_id);
+                    if alias.def_id == self.opaque {
+                        return ControlFlow::Break(alias.args);
+                    } else if self.seen.insert(alias.def_id) {
+                        for clause in self
+                            .tcx
+                            .explicit_item_bounds(alias.def_id)
+                            .iter_instantiated_copied(self.tcx, alias.args)
+                        {
+                            trace!(?clause);
+                            clause.visit_with(self)?;
+                        }
+                    }
+                }
+                ty::Alias(ty::Projection, alias) => {
+                    if self.tcx.is_impl_trait_in_trait(alias.def_id)
+                        && self.tcx.impl_trait_in_trait_parent_fn(alias.def_id) == self.function
+                    {
+                        // If we're lowering to associated item, install the opaque type which is just
+                        // the `type_of` of the trait's associated item. If we're using the old lowering
+                        // strategy, then just reinterpret the associated type like an opaque :^)
+                        self.tcx
+                            .type_of(alias.def_id)
+                            .instantiate(self.tcx, alias.args)
+                            .visit_with(self)?;
+                    }
+                }
+                ty::Alias(ty::Weak, alias) => {
+                    self.tcx
+                        .type_of(alias.def_id)
+                        .instantiate(self.tcx, alias.args)
+                        .visit_with(self)?;
+                }
+                _ => (),
+            }
+
+            t.super_visit_with(self)
+        }
+    }
+    if let ControlFlow::Break(args) =
+        ret.visit_with(&mut Visitor { tcx, function, opaque, seen: Default::default() })
+    {
+        trace!(?args);
+        trace!("expected: {hidden_ty:#?}");
+        hidden_ty = ty::EarlyBinder::bind(hidden_ty).instantiate(tcx, args);
+        trace!("expected: {hidden_ty:#?}");
+    } else {
+        tcx.sess
+            .delay_span_bug(tcx.def_span(function), format!("{ret:?} does not contain {opaque:?}"));
+    }
+    Ok(hidden_ty)
+}
+
 fn is_enum_of_nonnullable_ptr<'tcx>(
     tcx: TyCtxt<'tcx>,
     adt_def: AdtDef<'tcx>,
@@ -626,7 +797,7 @@ fn check_item_type(tcx: TyCtxt<'_>, id: hir::ItemId) {
                                     "replace the {} parameters with concrete {}{}",
                                     kinds,
                                     kinds_pl,
-                                    egs.map(|egs| format!(" like `{}`", egs)).unwrap_or_default(),
+                                    egs.map(|egs| format!(" like `{egs}`")).unwrap_or_default(),
                                 ),
                             )
                             .emit();
@@ -711,7 +882,7 @@ pub(super) fn check_specialization_validity<'tcx>(
         } else {
             tcx.sess.delay_span_bug(
                 DUMMY_SP,
-                format!("parent item: {:?} not marked as default", parent_impl),
+                format!("parent item: {parent_impl:?} not marked as default"),
             );
         }
     }
diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
index 89877280a73..919092ecb16 100644
--- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
+++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
@@ -219,11 +219,7 @@ fn compare_method_predicate_entailment<'tcx>(
     // The key step here is to update the caller_bounds's predicates to be
     // the new hybrid bounds we computed.
     let normalize_cause = traits::ObligationCause::misc(impl_m_span, impl_m_def_id);
-    let param_env = ty::ParamEnv::new(
-        tcx.mk_clauses(&hybrid_preds.predicates),
-        Reveal::UserFacing,
-        hir::Constness::NotConst,
-    );
+    let param_env = ty::ParamEnv::new(tcx.mk_clauses(&hybrid_preds.predicates), Reveal::UserFacing);
     let param_env = traits::normalize_param_env_or_error(tcx, param_env, normalize_cause);
 
     let infcx = &tcx.infer_ctxt().build();
@@ -1744,7 +1740,7 @@ fn compare_generic_param_kinds<'tcx>(
                         tcx.type_of(param.def_id).instantiate_identity()
                     )
                 }
-                Type { .. } => format!("{} type parameter", prefix),
+                Type { .. } => format!("{prefix} type parameter"),
                 Lifetime { .. } => unreachable!(),
             };
 
@@ -1923,11 +1919,7 @@ fn compare_type_predicate_entailment<'tcx>(
 
     let impl_ty_span = tcx.def_span(impl_ty_def_id);
     let normalize_cause = traits::ObligationCause::misc(impl_ty_span, impl_ty_def_id);
-    let param_env = ty::ParamEnv::new(
-        tcx.mk_clauses(&hybrid_preds.predicates),
-        Reveal::UserFacing,
-        hir::Constness::NotConst,
-    );
+    let param_env = ty::ParamEnv::new(tcx.mk_clauses(&hybrid_preds.predicates), Reveal::UserFacing);
     let param_env = traits::normalize_param_env_or_error(tcx, param_env, normalize_cause);
     let infcx = tcx.infer_ctxt().build();
     let ocx = ObligationCtxt::new(&infcx);
@@ -2102,7 +2094,7 @@ pub(super) fn check_type_bounds<'tcx>(
                 .to_predicate(tcx),
             ),
         };
-        ty::ParamEnv::new(tcx.mk_clauses(&predicates), Reveal::UserFacing, param_env.constness())
+        ty::ParamEnv::new(tcx.mk_clauses(&predicates), Reveal::UserFacing)
     };
     debug!(?normalize_param_env);
 
diff --git a/compiler/rustc_hir_analysis/src/check/dropck.rs b/compiler/rustc_hir_analysis/src/check/dropck.rs
index 4ac3a7c23ae..dda3f742569 100644
--- a/compiler/rustc_hir_analysis/src/check/dropck.rs
+++ b/compiler/rustc_hir_analysis/src/check/dropck.rs
@@ -129,9 +129,8 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>(
     // We don't need to normalize this param-env or anything, since we're only
     // substituting it with free params, so no additional param-env normalization
     // can occur on top of what has been done in the param_env query itself.
-    let param_env = ty::EarlyBinder::bind(tcx.param_env(adt_def_id))
-        .instantiate(tcx, adt_to_impl_args)
-        .with_constness(tcx.constness(drop_impl_def_id));
+    let param_env =
+        ty::EarlyBinder::bind(tcx.param_env(adt_def_id)).instantiate(tcx, adt_to_impl_args);
 
     for (pred, span) in tcx.predicates_of(drop_impl_def_id).instantiate_identity(tcx) {
         let normalize_cause = traits::ObligationCause::misc(span, adt_def_id);
diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs
index d5c9840887a..e076b1fc68c 100644
--- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs
+++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs
@@ -134,7 +134,7 @@ pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: DefId) -> hir
 /// Remember to add all intrinsics here, in `compiler/rustc_codegen_llvm/src/intrinsic.rs`,
 /// and in `library/core/src/intrinsics.rs`.
 pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
-    let param = |n| Ty::new_param(tcx, n, Symbol::intern(&format!("P{}", n)));
+    let param = |n| Ty::new_param(tcx, n, Symbol::intern(&format!("P{n}")));
     let intrinsic_id = it.owner_id.to_def_id();
     let intrinsic_name = tcx.item_name(intrinsic_id);
     let name_str = intrinsic_name.as_str();
@@ -494,7 +494,7 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
 /// Type-check `extern "platform-intrinsic" { ... }` functions.
 pub fn check_platform_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
     let param = |n| {
-        let name = Symbol::intern(&format!("P{}", n));
+        let name = Symbol::intern(&format!("P{n}"));
         Ty::new_param(tcx, n, name)
     };
 
diff --git a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs
index 8423a9550ba..b0dd5e5787d 100644
--- a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs
+++ b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs
@@ -211,7 +211,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
         // register class is usable at all.
         if let Some(feature) = feature {
             if !target_features.contains(feature) {
-                let msg = format!("`{}` target feature is not enabled", feature);
+                let msg = format!("`{feature}` target feature is not enabled");
                 let mut err = self.tcx.sess.struct_span_err(expr.span, msg);
                 err.note(format!(
                     "this is required to use type `{}` with register class `{}`",
diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs
index def7a3a9d88..4cf3587327d 100644
--- a/compiler/rustc_hir_analysis/src/check/mod.rs
+++ b/compiler/rustc_hir_analysis/src/check/mod.rs
@@ -214,7 +214,7 @@ fn missing_items_err(
             trait_item,
             tcx.impl_trait_ref(impl_def_id).unwrap().instantiate_identity(),
         );
-        let code = format!("{}{}\n{}", padding, snippet, padding);
+        let code = format!("{padding}{snippet}\n{padding}");
         if let Some(span) = tcx.hir().span_if_local(trait_item.def_id) {
             missing_trait_item_label
                 .push(errors::MissingTraitItemLabel { span, item: trait_item.name });
diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
index 4e194f1c381..0e99df4f581 100644
--- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs
+++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
@@ -75,12 +75,10 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> {
             self.body_def_id,
             ObligationCauseCode::WellFormed(loc),
         );
-        // for a type to be WF, we do not need to check if const trait predicates satisfy.
-        let param_env = self.param_env.without_const();
         self.ocx.register_obligation(traits::Obligation::new(
             self.tcx(),
             cause,
-            param_env,
+            self.param_env,
             ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg))),
         ));
     }
@@ -472,8 +470,7 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe
             let bound =
                 if unsatisfied_bounds.len() > 1 { "these bounds are" } else { "this bound is" };
             err.note(format!(
-                "{} currently required to ensure that impls have maximum flexibility",
-                bound
+                "{bound} currently required to ensure that impls have maximum flexibility"
             ));
             err.note(
                 "we are soliciting feedback, see issue #87479 \
@@ -505,7 +502,7 @@ fn augment_param_env<'tcx>(
     );
     // FIXME(compiler-errors): Perhaps there is a case where we need to normalize this
     // i.e. traits::normalize_param_env_or_error
-    ty::ParamEnv::new(bounds, param_env.reveal(), param_env.constness())
+    ty::ParamEnv::new(bounds, param_env.reveal())
 }
 
 /// We use the following trait as an example throughout this function.
@@ -989,7 +986,7 @@ fn check_type_defn<'tcx>(tcx: TyCtxt<'tcx>, item: &hir::Item<'tcx>, all_sized: b
                     let ty = tcx.erase_regions(ty);
                     if ty.has_infer() {
                         tcx.sess
-                            .delay_span_bug(item.span, format!("inference variables in {:?}", ty));
+                            .delay_span_bug(item.span, format!("inference variables in {ty:?}"));
                         // Just treat unresolved type expression as if it needs drop.
                         true
                     } else {
@@ -1416,7 +1413,7 @@ fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, span: Span, def_id
     let wf_obligations = predicates.into_iter().flat_map(|(p, sp)| {
         traits::wf::predicate_obligations(
             infcx,
-            wfcx.param_env.without_const(),
+            wfcx.param_env,
             wfcx.body_def_id,
             p.as_predicate(),
             sp,
@@ -1863,8 +1860,7 @@ fn report_bivariance(
 
     if matches!(param.kind, hir::GenericParamKind::Type { .. }) && !has_explicit_bounds {
         err.help(format!(
-            "if you intended `{0}` to be a const parameter, use `const {0}: usize` instead",
-            param_name
+            "if you intended `{param_name}` to be a const parameter, use `const {param_name}: usize` instead"
         ));
     }
     err.emit()
diff --git a/compiler/rustc_hir_analysis/src/check_unused.rs b/compiler/rustc_hir_analysis/src/check_unused.rs
index d10bc5b34ea..9ad73eeffc6 100644
--- a/compiler/rustc_hir_analysis/src/check_unused.rs
+++ b/compiler/rustc_hir_analysis/src/check_unused.rs
@@ -36,7 +36,7 @@ fn check_unused_traits(tcx: TyCtxt<'_>, (): ()) {
         }
         let (path, _) = item.expect_use();
         let msg = if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(path.span) {
-            format!("unused import: `{}`", snippet)
+            format!("unused import: `{snippet}`")
         } else {
             "unused import".to_owned()
         };
diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs
index 3f65adcd36e..c930537d4ae 100644
--- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs
@@ -171,8 +171,7 @@ fn visit_implementation_of_dispatch_from_dyn(tcx: TyCtxt<'_>, impl_did: LocalDef
                 create_err(&format!(
                     "the trait `DispatchFromDyn` may only be implemented \
                             for a coercion between structures with the same \
-                            definition; expected `{}`, found `{}`",
-                    source_path, target_path,
+                            definition; expected `{source_path}`, found `{target_path}`",
                 ))
                 .emit();
 
diff --git a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs
index f5326e50614..a94c75f918a 100644
--- a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs
@@ -148,8 +148,7 @@ impl<'tcx> InherentCollect<'tcx> {
                 if let ty::Ref(_, subty, _) = ty.kind() {
                     err.note(format!(
                         "you could also try moving the reference to \
-                            uses of `{}` (such as `self`) within the implementation",
-                        subty
+                            uses of `{subty}` (such as `self`) within the implementation"
                     ));
                 }
                 err.emit();
diff --git a/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs b/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs
index 3bd2931265c..7205b7a21a8 100644
--- a/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs
@@ -77,8 +77,8 @@ impl<'tcx> InherentOverlapChecker<'tcx> {
                         "duplicate definitions with name `{}`",
                         ident,
                     );
-                    err.span_label(span, format!("duplicate definitions for `{}`", ident));
-                    err.span_label(*former, format!("other definition for `{}`", ident));
+                    err.span_label(span, format!("duplicate definitions for `{ident}`"));
+                    err.span_label(*former, format!("other definition for `{ident}`"));
 
                     err.emit();
                 }
@@ -114,11 +114,11 @@ impl<'tcx> InherentOverlapChecker<'tcx> {
                 );
                 err.span_label(
                     self.tcx.def_span(item1.def_id),
-                    format!("duplicate definitions for `{}`", name),
+                    format!("duplicate definitions for `{name}`"),
                 );
                 err.span_label(
                     self.tcx.def_span(item2.def_id),
-                    format!("other definition for `{}`", name),
+                    format!("other definition for `{name}`"),
                 );
 
                 for cause in &overlap.intercrate_ambiguity_causes {
diff --git a/compiler/rustc_hir_analysis/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs
index 21ffbefcd08..bbdb108c59b 100644
--- a/compiler/rustc_hir_analysis/src/coherence/orphan.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs
@@ -352,7 +352,7 @@ fn emit_orphan_check_error<'tcx>(
 
                 let this = |name: &str| {
                     if !trait_ref.def_id.is_local() && !is_target_ty {
-                        msg("this", &format!(" because this is a foreign trait"))
+                        msg("this", " because this is a foreign trait")
                     } else {
                         msg("this", &format!(" because {name} are always foreign"))
                     }
@@ -412,9 +412,8 @@ fn emit_orphan_check_error<'tcx>(
                 .span_label(
                     sp,
                     format!(
-                        "type parameter `{}` must be covered by another type \
-                    when it appears before the first local type (`{}`)",
-                        param_ty, local_type
+                        "type parameter `{param_ty}` must be covered by another type \
+                    when it appears before the first local type (`{local_type}`)"
                     ),
                 )
                 .note(
@@ -441,9 +440,8 @@ fn emit_orphan_check_error<'tcx>(
                 .span_label(
                     sp,
                     format!(
-                        "type parameter `{}` must be used as the type parameter for some \
+                        "type parameter `{param_ty}` must be used as the type parameter for some \
                     local type",
-                        param_ty,
                     ),
                 )
                 .note(
@@ -541,17 +539,16 @@ fn lint_auto_trait_impl<'tcx>(
             let self_descr = tcx.def_descr(self_type_did);
             match arg {
                 ty::util::NotUniqueParam::DuplicateParam(arg) => {
-                    lint.note(format!("`{}` is mentioned multiple times", arg));
+                    lint.note(format!("`{arg}` is mentioned multiple times"));
                 }
                 ty::util::NotUniqueParam::NotParam(arg) => {
-                    lint.note(format!("`{}` is not a generic parameter", arg));
+                    lint.note(format!("`{arg}` is not a generic parameter"));
                 }
             }
             lint.span_note(
                 item_span,
                 format!(
-                    "try using the same sequence of generic parameters as the {} definition",
-                    self_descr,
+                    "try using the same sequence of generic parameters as the {self_descr} definition",
                 ),
             )
         },
diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs
index c160cf2df6e..f568b751951 100644
--- a/compiler/rustc_hir_analysis/src/collect.rs
+++ b/compiler/rustc_hir_analysis/src/collect.rs
@@ -195,9 +195,9 @@ pub(crate) fn placeholder_type_error_diag<'tcx>(
             sugg.push((arg.span, (*type_name).to_string()));
         } else if let Some(span) = generics.span_for_param_suggestion() {
             // Account for bounds, we want `fn foo<T: E, K>(_: K)` not `fn foo<T, K: E>(_: K)`.
-            sugg.push((span, format!(", {}", type_name)));
+            sugg.push((span, format!(", {type_name}")));
         } else {
-            sugg.push((generics.span, format!("<{}>", type_name)));
+            sugg.push((generics.span, format!("<{type_name}>")));
         }
     }
 
@@ -329,7 +329,7 @@ fn bad_placeholder<'tcx>(
     mut spans: Vec<Span>,
     kind: &'static str,
 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
-    let kind = if kind.ends_with('s') { format!("{}es", kind) } else { format!("{}s", kind) };
+    let kind = if kind.ends_with('s') { format!("{kind}es") } else { format!("{kind}s") };
 
     spans.sort();
     tcx.sess.create_err(errors::PlaceholderNotAllowedItemSignatures { spans, kind })
@@ -425,10 +425,8 @@ impl<'tcx> AstConv<'tcx> for ItemCtxt<'tcx> {
                         | hir::ItemKind::Union(_, generics) => {
                             let lt_name = get_new_lifetime_name(self.tcx, poly_trait_ref, generics);
                             let (lt_sp, sugg) = match generics.params {
-                                [] => (generics.span, format!("<{}>", lt_name)),
-                                [bound, ..] => {
-                                    (bound.span.shrink_to_lo(), format!("{}, ", lt_name))
-                                }
+                                [] => (generics.span, format!("<{lt_name}>")),
+                                [bound, ..] => (bound.span.shrink_to_lo(), format!("{lt_name}, ")),
                             };
                             mpart_sugg = Some(errors::AssociatedTypeTraitUninferredGenericParamsMultipartSuggestion {
                                 fspan: lt_sp,
@@ -1027,7 +1025,7 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef {
             } else {
                 tcx.sess.span_err(
                     meta.span(),
-                    format!("unknown meta item passed to `rustc_deny_explicit_impl` {:?}", meta),
+                    format!("unknown meta item passed to `rustc_deny_explicit_impl` {meta:?}"),
                 );
             }
         }
@@ -1505,7 +1503,7 @@ fn compute_sig_of_foreign_fn_decl<'tcx>(
                     .sess
                     .source_map()
                     .span_to_snippet(ast_ty.span)
-                    .map_or_else(|_| String::new(), |s| format!(" `{}`", s));
+                    .map_or_else(|_| String::new(), |s| format!(" `{s}`"));
                 tcx.sess.emit_err(errors::SIMDFFIHighlyExperimental { span: ast_ty.span, snip });
             }
         };
diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
index 44f116ef2da..eb93817e823 100644
--- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
+++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
@@ -137,12 +137,6 @@ enum Scope<'a> {
         s: ScopeRef<'a>,
     },
 
-    /// A scope which either determines unspecified lifetimes or errors
-    /// on them (e.g., due to ambiguity).
-    Elision {
-        s: ScopeRef<'a>,
-    },
-
     /// Use a specific lifetime (if `Some`) or leave it unset (to be
     /// inferred in a function body or potentially error outside one),
     /// for the default choice of lifetime in a trait object type.
@@ -211,7 +205,6 @@ impl<'a> fmt::Debug for TruncatedScopeDebug<'a> {
             Scope::Body { id, s: _ } => {
                 f.debug_struct("Body").field("id", id).field("s", &"..").finish()
             }
-            Scope::Elision { s: _ } => f.debug_struct("Elision").field("s", &"..").finish(),
             Scope::ObjectLifetimeDefault { lifetime, s: _ } => f
                 .debug_struct("ObjectLifetimeDefault")
                 .field("lifetime", lifetime)
@@ -325,9 +318,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
                     break (vec![], BinderScopeType::Normal);
                 }
 
-                Scope::Elision { s, .. }
-                | Scope::ObjectLifetimeDefault { s, .. }
-                | Scope::AnonConstBoundary { s } => {
+                Scope::ObjectLifetimeDefault { s, .. } | Scope::AnonConstBoundary { s } => {
                     scope = s;
                 }
 
@@ -526,16 +517,12 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
             | hir::ItemKind::Macro(..)
             | hir::ItemKind::Mod(..)
             | hir::ItemKind::ForeignMod { .. }
+            | hir::ItemKind::Static(..)
+            | hir::ItemKind::Const(..)
             | hir::ItemKind::GlobalAsm(..) => {
                 // These sorts of items have no lifetime parameters at all.
                 intravisit::walk_item(self, item);
             }
-            hir::ItemKind::Static(..) | hir::ItemKind::Const(..) => {
-                // No lifetime parameters, but implied 'static.
-                self.with(Scope::Elision { s: self.scope }, |this| {
-                    intravisit::walk_item(this, item)
-                });
-            }
             hir::ItemKind::OpaqueTy(hir::OpaqueTy {
                 origin: hir::OpaqueTyOrigin::TyAlias { .. },
                 ..
@@ -727,12 +714,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
                         // Elided lifetimes are not allowed in non-return
                         // position impl Trait
                         let scope = Scope::TraitRefBoundary { s: self.scope };
-                        self.with(scope, |this| {
-                            let scope = Scope::Elision { s: this.scope };
-                            this.with(scope, |this| {
-                                intravisit::walk_item(this, opaque_ty);
-                            })
-                        });
+                        self.with(scope, |this| intravisit::walk_item(this, opaque_ty));
 
                         return;
                     }
@@ -1293,8 +1275,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
                     scope = s;
                 }
 
-                Scope::Elision { s, .. }
-                | Scope::ObjectLifetimeDefault { s, .. }
+                Scope::ObjectLifetimeDefault { s, .. }
                 | Scope::Supertrait { s, .. }
                 | Scope::TraitRefBoundary { s, .. }
                 | Scope::AnonConstBoundary { s } => {
@@ -1357,7 +1338,6 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
                 Scope::Root { .. } => break,
                 Scope::Binder { s, .. }
                 | Scope::Body { s, .. }
-                | Scope::Elision { s, .. }
                 | Scope::ObjectLifetimeDefault { s, .. }
                 | Scope::Supertrait { s, .. }
                 | Scope::TraitRefBoundary { s, .. }
@@ -1409,8 +1389,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
                     scope = s;
                 }
 
-                Scope::Elision { s, .. }
-                | Scope::ObjectLifetimeDefault { s, .. }
+                Scope::ObjectLifetimeDefault { s, .. }
                 | Scope::Supertrait { s, .. }
                 | Scope::TraitRefBoundary { s, .. } => {
                     scope = s;
@@ -1483,7 +1462,6 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
                 Scope::Root { .. } => break,
                 Scope::Binder { s, .. }
                 | Scope::Body { s, .. }
-                | Scope::Elision { s, .. }
                 | Scope::ObjectLifetimeDefault { s, .. }
                 | Scope::Supertrait { s, .. }
                 | Scope::TraitRefBoundary { s, .. }
@@ -1564,7 +1542,6 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
                         Scope::Body { .. } => break true,
 
                         Scope::Binder { s, .. }
-                        | Scope::Elision { s, .. }
                         | Scope::ObjectLifetimeDefault { s, .. }
                         | Scope::Supertrait { s, .. }
                         | Scope::TraitRefBoundary { s, .. }
@@ -1832,14 +1809,20 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
         output: Option<&'tcx hir::Ty<'tcx>>,
         in_closure: bool,
     ) {
-        self.with(Scope::Elision { s: self.scope }, |this| {
-            for input in inputs {
-                this.visit_ty(input);
-            }
-            if !in_closure && let Some(output) = output {
-                this.visit_ty(output);
-            }
-        });
+        self.with(
+            Scope::ObjectLifetimeDefault {
+                lifetime: Some(ResolvedArg::StaticLifetime),
+                s: self.scope,
+            },
+            |this| {
+                for input in inputs {
+                    this.visit_ty(input);
+                }
+                if !in_closure && let Some(output) = output {
+                    this.visit_ty(output);
+                }
+            },
+        );
         if in_closure && let Some(output) = output {
             self.visit_ty(output);
         }
@@ -1859,7 +1842,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
                     scope = s;
                 }
 
-                Scope::Root { .. } | Scope::Elision { .. } => break ResolvedArg::StaticLifetime,
+                Scope::Root { .. } => break ResolvedArg::StaticLifetime,
 
                 Scope::Body { .. } | Scope::ObjectLifetimeDefault { lifetime: None, .. } => return,
 
@@ -2057,8 +2040,7 @@ fn is_late_bound_map(
                                             tcx.sess.delay_span_bug(
                                                 *span,
                                                 format!(
-                                                    "Incorrect generic arg count for alias {:?}",
-                                                    alias_def
+                                                    "Incorrect generic arg count for alias {alias_def:?}"
                                                 ),
                                             );
                                             None
diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs
index c39edaa1577..a9ef791077e 100644
--- a/compiler/rustc_hir_analysis/src/collect/type_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs
@@ -156,7 +156,7 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> {
             let Some(type_dependent_def) = tables.type_dependent_def_id(parent_node_id) else {
                 return Ty::new_error_with_message(tcx,
                     tcx.def_span(def_id),
-                    format!("unable to find type-dependent def for {:?}", parent_node_id),
+                    format!("unable to find type-dependent def for {parent_node_id:?}"),
                 );
             };
             let idx = segment
@@ -197,14 +197,14 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> {
                     } else {
                         return Ty::new_error_with_message(tcx,
                             tcx.def_span(def_id),
-                            format!("unable to find const parent for {} in pat {:?}", hir_id, pat),
+                            format!("unable to find const parent for {hir_id} in pat {pat:?}"),
                         );
                     }
                 }
                 _ => {
                     return Ty::new_error_with_message(tcx,
                         tcx.def_span(def_id),
-                        format!("unexpected const parent path {:?}", parent_node),
+                        format!("unexpected const parent path {parent_node:?}"),
                     );
                 }
             };
@@ -544,7 +544,7 @@ fn infer_placeholder_type<'a>(
                 if let Some(ty) = ty.make_suggestable(tcx, false) {
                     err.span_suggestion(
                         span,
-                        format!("provide a type for the {item}", item = kind),
+                        format!("provide a type for the {kind}"),
                         format!("{colon} {ty}"),
                         Applicability::MachineApplicable,
                     );
diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs
index c2d2e5f7e50..0babdf7e5b3 100644
--- a/compiler/rustc_hir_analysis/src/errors.rs
+++ b/compiler/rustc_hir_analysis/src/errors.rs
@@ -216,7 +216,7 @@ impl<'a> IntoDiagnostic<'a> for MissingTypeParams {
             "parameters",
             self.missing_type_params
                 .iter()
-                .map(|n| format!("`{}`", n))
+                .map(|n| format!("`{n}`"))
                 .collect::<Vec<_>>()
                 .join(", "),
         );
diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check.rs b/compiler/rustc_hir_analysis/src/impl_wf_check.rs
index 62f22bda9f9..4f705eaf10a 100644
--- a/compiler/rustc_hir_analysis/src/impl_wf_check.rs
+++ b/compiler/rustc_hir_analysis/src/impl_wf_check.rs
@@ -77,8 +77,7 @@ fn enforce_impl_params_are_constrained(tcx: TyCtxt<'_>, impl_def_id: LocalDefId)
         tcx.sess.delay_span_bug(
             tcx.def_span(impl_def_id),
             format!(
-                "potentially unconstrained type parameters weren't evaluated: {:?}",
-                impl_self_ty,
+                "potentially unconstrained type parameters weren't evaluated: {impl_self_ty:?}",
             ),
         );
         return;
@@ -180,7 +179,7 @@ fn report_unused_parameter(tcx: TyCtxt<'_>, span: Span, kind: &str, name: Symbol
         kind,
         name
     );
-    err.span_label(span, format!("unconstrained {} parameter", kind));
+    err.span_label(span, format!("unconstrained {kind} parameter"));
     if kind == "const" {
         err.note(
             "expressions using a const parameter must map each value to a distinct output value",
diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs
index 81993789bcf..8b2c93d8fd3 100644
--- a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs
+++ b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs
@@ -294,7 +294,7 @@ fn check_duplicate_params<'tcx>(
     if let (_, [duplicate, ..]) = base_params.partition_dedup() {
         let param = impl1_args[duplicate.0 as usize];
         tcx.sess
-            .struct_span_err(span, format!("specializing impl repeats parameter `{}`", param))
+            .struct_span_err(span, format!("specializing impl repeats parameter `{param}`"))
             .emit();
     }
 }
@@ -523,7 +523,7 @@ fn check_specialization_on<'tcx>(tcx: TyCtxt<'tcx>, predicate: ty::Predicate<'tc
         }
         _ => {
             tcx.sess
-                .struct_span_err(span, format!("cannot specialize on predicate `{}`", predicate))
+                .struct_span_err(span, format!("cannot specialize on predicate `{predicate}`"))
                 .emit();
         }
     }
diff --git a/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs b/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs
index 0828fe9e0f2..6be8d72aed2 100644
--- a/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs
+++ b/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs
@@ -474,7 +474,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
                 verb
             )
         } else {
-            format!("missing generics for {} `{}`", def_kind, def_path)
+            format!("missing generics for {def_kind} `{def_path}`")
         }
     }
 
@@ -599,7 +599,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
                 let span = self.path_segment.ident.span;
 
                 // insert a suggestion of the form "Y<'a, 'b>"
-                let sugg = format!("<{}>", suggested_args);
+                let sugg = format!("<{suggested_args}>");
                 debug!("sugg: {:?}", sugg);
 
                 err.span_suggestion_verbose(
@@ -624,7 +624,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
                 let sugg_suffix =
                     if is_first && (has_non_lt_args || has_bindings) { ", " } else { "" };
 
-                let sugg = format!("{}{}{}", sugg_prefix, suggested_args, sugg_suffix);
+                let sugg = format!("{sugg_prefix}{suggested_args}{sugg_suffix}");
                 debug!("sugg: {:?}", sugg);
 
                 err.span_suggestion_verbose(sugg_span, msg, sugg, Applicability::HasPlaceholders);
@@ -649,7 +649,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
                 let span = self.path_segment.ident.span;
 
                 // insert a suggestion of the form "Y<T, U>"
-                let sugg = format!("<{}>", suggested_args);
+                let sugg = format!("<{suggested_args}>");
                 debug!("sugg: {:?}", sugg);
 
                 err.span_suggestion_verbose(
@@ -682,7 +682,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
                 let sugg_suffix =
                     if is_first && !self.gen_args.bindings.is_empty() { ", " } else { "" };
 
-                let sugg = format!("{}{}{}", sugg_prefix, suggested_args, sugg_suffix);
+                let sugg = format!("{sugg_prefix}{suggested_args}{sugg_suffix}");
                 debug!("sugg: {:?}", sugg);
 
                 err.span_suggestion_verbose(sugg_span, msg, sugg, Applicability::HasPlaceholders);
@@ -1024,7 +1024,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
                     .collect::<Vec<_>>()
                     .join(", ");
 
-                format!(": {}", params)
+                format!(": {params}")
             };
 
             format!(
diff --git a/compiler/rustc_hir_analysis/src/variance/terms.rs b/compiler/rustc_hir_analysis/src/variance/terms.rs
index 3b286bb9c93..ed03c5da26f 100644
--- a/compiler/rustc_hir_analysis/src/variance/terms.rs
+++ b/compiler/rustc_hir_analysis/src/variance/terms.rs
@@ -32,8 +32,8 @@ pub enum VarianceTerm<'a> {
 impl<'a> fmt::Debug for VarianceTerm<'a> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match *self {
-            ConstantTerm(c1) => write!(f, "{:?}", c1),
-            TransformTerm(v1, v2) => write!(f, "({:?} \u{00D7} {:?})", v1, v2),
+            ConstantTerm(c1) => write!(f, "{c1:?}"),
+            TransformTerm(v1, v2) => write!(f, "({v1:?} \u{00D7} {v2:?})"),
             InferredTerm(id) => write!(f, "[{}]", {
                 let InferredIndex(i) = id;
                 i
diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs
index a24d1ff077f..a4759722199 100644
--- a/compiler/rustc_hir_typeck/src/callee.rs
+++ b/compiler/rustc_hir_typeck/src/callee.rs
@@ -402,7 +402,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             .sess
                             .struct_span_err(
                                 callee_expr.span,
-                                format!("evaluate({:?}) = {:?}", predicate, result),
+                                format!("evaluate({predicate:?}) = {result:?}"),
                             )
                             .span_label(predicate_span, "predicate")
                             .emit();
@@ -581,6 +581,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         callee_ty: Ty<'tcx>,
         arg_exprs: &'tcx [hir::Expr<'tcx>],
     ) -> ErrorGuaranteed {
+        // Callee probe fails when APIT references errors, so suppress those
+        // errors here.
+        if let Some((_, _, args)) = self.extract_callable_info(callee_ty)
+            && let Err(err) = args.error_reported()
+        {
+            return err;
+        }
+
         let mut unit_variant = None;
         if let hir::ExprKind::Path(qpath) = &callee_expr.kind
             && let Res::Def(def::DefKind::Ctor(kind, CtorKind::Const), _)
diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs
index 7597ce88f93..5bc0e2ee86c 100644
--- a/compiler/rustc_hir_typeck/src/cast.rs
+++ b/compiler/rustc_hir_typeck/src/cast.rs
@@ -144,7 +144,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 let reported = self
                     .tcx
                     .sess
-                    .delay_span_bug(span, format!("`{:?}` should be sized but is not?", t));
+                    .delay_span_bug(span, format!("`{t:?}` should be sized but is not?"));
                 return Err(reported);
             }
         })
@@ -644,12 +644,12 @@ impl<'a, 'tcx> CastCheck<'tcx> {
                             err.span_suggestion(
                                 self.cast_span,
                                 "try casting to a reference instead",
-                                format!("&{}{}", mtstr, s),
+                                format!("&{mtstr}{s}"),
                                 Applicability::MachineApplicable,
                             );
                         }
                         Err(_) => {
-                            let msg = format!("did you mean `&{}{}`?", mtstr, tstr);
+                            let msg = format!("did you mean `&{mtstr}{tstr}`?");
                             err.span_help(self.cast_span, msg);
                         }
                     }
@@ -705,10 +705,10 @@ impl<'a, 'tcx> CastCheck<'tcx> {
                 )
             }),
             |lint| {
-                lint.help(format!(
+                lint.help(
                     "cast can be replaced by coercion; this might \
-                     require a temporary variable"
-                ))
+                     require a temporary variable",
+                )
             },
         );
     }
diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs
index affeee55e79..ca3b595d238 100644
--- a/compiler/rustc_hir_typeck/src/closure.rs
+++ b/compiler/rustc_hir_typeck/src/closure.rs
@@ -81,7 +81,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         debug!(?bound_sig, ?liberated_sig);
 
-        let mut fcx = FnCtxt::new(self, self.param_env.without_const(), closure.def_id);
+        let mut fcx = FnCtxt::new(self, self.param_env, closure.def_id);
         let generator_types = check_fn(
             &mut fcx,
             liberated_sig,
diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs
index 3b1d9c8905b..4fdfc51bc86 100644
--- a/compiler/rustc_hir_typeck/src/coercion.rs
+++ b/compiler/rustc_hir_typeck/src/coercion.rs
@@ -46,6 +46,7 @@ use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKi
 use rustc_infer::infer::{Coercion, DefineOpaqueTypes, InferOk, InferResult};
 use rustc_infer::traits::{Obligation, PredicateObligation};
 use rustc_middle::lint::in_external_macro;
+use rustc_middle::traits::BuiltinImplSource;
 use rustc_middle::ty::adjustment::{
     Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCoercion,
 };
@@ -636,22 +637,6 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
                 Some(ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred)))
                     if traits.contains(&trait_pred.def_id()) =>
                 {
-                    let trait_pred = self.resolve_vars_if_possible(trait_pred);
-                    if unsize_did == trait_pred.def_id() {
-                        let self_ty = trait_pred.self_ty();
-                        let unsize_ty = trait_pred.trait_ref.args[1].expect_ty();
-                        if let (ty::Dynamic(ref data_a, ..), ty::Dynamic(ref data_b, ..)) =
-                            (self_ty.kind(), unsize_ty.kind())
-                            && data_a.principal_def_id() != data_b.principal_def_id()
-                        {
-                            debug!("coerce_unsized: found trait upcasting coercion");
-                            has_trait_upcasting_coercion = Some((self_ty, unsize_ty));
-                        }
-                        if let ty::Tuple(..) = unsize_ty.kind() {
-                            debug!("coerce_unsized: found unsized tuple coercion");
-                            has_unsized_tuple_coercion = true;
-                        }
-                    }
                     trait_pred
                 }
                 _ => {
@@ -659,6 +644,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
                     continue;
                 }
             };
+            let trait_pred = self.resolve_vars_if_possible(trait_pred);
             match selcx.select(&obligation.with(selcx.tcx(), trait_pred)) {
                 // Uncertain or unimplemented.
                 Ok(None) => {
@@ -701,20 +687,28 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
                     // be silent, as it causes a type mismatch later.
                 }
 
-                Ok(Some(impl_source)) => queue.extend(impl_source.nested_obligations()),
+                Ok(Some(impl_source)) => {
+                    // Some builtin coercions are still unstable so we detect
+                    // these here and emit a feature error if coercion doesn't fail
+                    // due to another reason.
+                    match impl_source {
+                        traits::ImplSource::Builtin(
+                            BuiltinImplSource::TraitUpcasting { .. },
+                            _,
+                        ) => {
+                            has_trait_upcasting_coercion =
+                                Some((trait_pred.self_ty(), trait_pred.trait_ref.args.type_at(1)));
+                        }
+                        traits::ImplSource::Builtin(BuiltinImplSource::TupleUnsizing, _) => {
+                            has_unsized_tuple_coercion = true;
+                        }
+                        _ => {}
+                    }
+                    queue.extend(impl_source.nested_obligations())
+                }
             }
         }
 
-        if has_unsized_tuple_coercion && !self.tcx.features().unsized_tuple_coercion {
-            feature_err(
-                &self.tcx.sess.parse_sess,
-                sym::unsized_tuple_coercion,
-                self.cause.span,
-                "unsized tuple coercion is not stable enough for use and is subject to change",
-            )
-            .emit();
-        }
-
         if let Some((sub, sup)) = has_trait_upcasting_coercion
             && !self.tcx().features().trait_upcasting
         {
@@ -730,6 +724,16 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
             err.emit();
         }
 
+        if has_unsized_tuple_coercion && !self.tcx.features().unsized_tuple_coercion {
+            feature_err(
+                &self.tcx.sess.parse_sess,
+                sym::unsized_tuple_coercion,
+                self.cause.span,
+                "unsized tuple coercion is not stable enough for use and is subject to change",
+            )
+            .emit();
+        }
+
         Ok(coercion)
     }
 
@@ -1793,8 +1797,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
             err.span_note(
                 sp,
                 format!(
-                    "return type inferred to be `{}` here",
-                    expected
+                    "return type inferred to be `{expected}` here"
                 ),
             );
         }
diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs
index 429c3366fed..26fa3d80d55 100644
--- a/compiler/rustc_hir_typeck/src/demand.rs
+++ b/compiler/rustc_hir_typeck/src/demand.rs
@@ -53,7 +53,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             || self.suggest_no_capture_closure(err, expected, expr_ty)
             || self.suggest_boxing_when_appropriate(err, expr.span, expr.hir_id, expected, expr_ty)
             || self.suggest_block_to_brackets_peeling_refs(err, expr, expr_ty, expected)
-            || self.suggest_copied_or_cloned(err, expr, expr_ty, expected)
+            || self.suggest_copied_cloned_or_as_ref(err, expr, expr_ty, expected, expected_ty_expr)
             || self.suggest_clone_for_ref(err, expr, expr_ty, expected)
             || self.suggest_into(err, expr, expr_ty, expected)
             || self.suggest_floating_point_literal(err, expr, expected)
@@ -621,7 +621,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     // is in a different line, so we point at both.
                     err.span_label(secondary_span, "expected due to the type of this binding");
                     err.span_label(primary_span, format!("expected due to this{post_message}"));
-                } else if post_message == "" {
+                } else if post_message.is_empty() {
                     // We are pointing at either the assignment lhs or the binding def pattern.
                     err.span_label(primary_span, "expected due to the type of this binding");
                 } else {
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index 29488c9011a..3f1f10f2f00 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -1334,7 +1334,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 t_cast,
                 t.span,
                 expr.span,
-                self.param_env.constness(),
+                hir::Constness::NotConst,
             ) {
                 Ok(cast_check) => {
                     debug!(
@@ -1428,7 +1428,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         // Create a new function context.
         let def_id = block.def_id;
-        let fcx = FnCtxt::new(self, self.param_env.with_const(), def_id);
+        let fcx = FnCtxt::new(self, self.param_env, def_id);
         crate::GatherLocalsVisitor::new(&fcx).visit_body(body);
 
         let ty = fcx.check_expr_with_expectation(&body.value, expected);
@@ -1890,7 +1890,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         let mut truncated_fields_error = String::new();
         let remaining_fields_names = match &displayable_field_names[..] {
-            [field1] => format!("`{}`", field1),
+            [field1] => format!("`{field1}`"),
             [field1, field2] => format!("`{field1}` and `{field2}`"),
             [field1, field2, field3] => format!("`{field1}`, `{field2}` and `{field3}`"),
             _ => {
@@ -2117,16 +2117,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     );
                 }
                 _ => {
-                    err.span_label(variant_ident_span, format!("`{adt}` defined here", adt = ty));
+                    err.span_label(variant_ident_span, format!("`{ty}` defined here"));
                     err.span_label(field.ident.span, "field does not exist");
                     err.span_suggestion_verbose(
                         expr_span,
-                        format!(
-                            "`{adt}` is a tuple {kind_name}, use the appropriate syntax",
-                            adt = ty,
-                            kind_name = kind_name,
-                        ),
-                        format!("{adt}(/* fields */)", adt = ty),
+                        format!("`{ty}` is a tuple {kind_name}, use the appropriate syntax",),
+                        format!("{ty}(/* fields */)"),
                         Applicability::HasPlaceholders,
                     );
                 }
@@ -2243,7 +2239,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // dynamic limit, to never omit just one field
         let limit = if names.len() == 6 { 6 } else { 5 };
         let mut display =
-            names.iter().take(limit).map(|n| format!("`{}`", n)).collect::<Vec<_>>().join(", ");
+            names.iter().take(limit).map(|n| format!("`{n}`")).collect::<Vec<_>>().join(", ");
         if names.len() > limit {
             display = format!("{} ... and {} others", display, names.len() - limit);
         }
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
index 94f64a1ffdb..ecafb50f420 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
@@ -61,7 +61,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
                 debug!("warn_if_unreachable: id={:?} span={:?} kind={}", id, span, kind);
 
-                let msg = format!("unreachable {}", kind);
+                let msg = format!("unreachable {kind}");
                 self.tcx().struct_span_lint_hir(
                     lint::builtin::UNREACHABLE_CODE,
                     id,
@@ -134,7 +134,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     }
 
     pub fn tag(&self) -> String {
-        format!("{:p}", self)
+        format!("{self:p}")
     }
 
     pub fn local_ty(&self, span: Span, nid: hir::HirId) -> Ty<'tcx> {
@@ -1069,7 +1069,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         } else if let ExprKind::MethodCall(..) = rcvr.kind {
             err.span_note(
                 sp,
-                modifies_rcvr_note.clone() + ", it is not meant to be used in method chains.",
+                modifies_rcvr_note + ", it is not meant to be used in method chains.",
             );
         } else {
             err.span_note(sp, modifies_rcvr_note);
@@ -1412,9 +1412,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     self.tcx.sess.delay_span_bug(
                         span,
                         format!(
-                        "instantiate_value_path: (UFCS) {:?} was a subtype of {:?} but now is not?",
-                        self_ty,
-                        impl_ty,
+                        "instantiate_value_path: (UFCS) {self_ty:?} was a subtype of {impl_ty:?} but now is not?",
                     ),
                     );
                 }
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
index a9610009db1..e102c51c7ee 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
@@ -45,12 +45,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         debug!("FnCtxt::check_casts: {} deferred checks", deferred_cast_checks.len());
         for cast in deferred_cast_checks.drain(..) {
-            let prev_env = self.param_env;
-            self.param_env = self.param_env.with_constness(cast.constness);
-
             cast.check(self);
-
-            self.param_env = prev_env;
         }
 
         *self.deferred_cast_checks.borrow_mut() = deferred_cast_checks;
@@ -689,7 +684,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         );
                         err.span_label(
                             full_call_span,
-                            format!("arguments to this {} are incorrect", call_name),
+                            format!("arguments to this {call_name} are incorrect"),
                         );
                     } else {
                         err = tcx.sess.struct_span_err_with_code(
@@ -796,10 +791,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 None,
                 None,
             );
-            err.span_label(
-                full_call_span,
-                format!("arguments to this {} are incorrect", call_name),
-            );
+            err.span_label(full_call_span, format!("arguments to this {call_name} are incorrect"));
 
             if let hir::ExprKind::MethodCall(_, rcvr, _, _) = call_expr.kind
                 && provided_idx.as_usize() == expected_idx.as_usize()
@@ -874,7 +866,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             if ty.is_unit() {
                 "()".to_string()
             } else if ty.is_suggestable(tcx, false) {
-                format!("/* {} */", ty)
+                format!("/* {ty} */")
             } else if let Some(fn_def_id) = fn_def_id
                 && self.tcx.def_kind(fn_def_id).is_fn_like()
                 && let self_implicit =
@@ -931,12 +923,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     let (provided_ty, provided_span) = provided_arg_tys[arg_idx];
                     let provided_ty_name = if !has_error_or_infer([provided_ty]) {
                         // FIXME: not suggestable, use something else
-                        format!(" of type `{}`", provided_ty)
+                        format!(" of type `{provided_ty}`")
                     } else {
                         "".to_string()
                     };
-                    labels
-                        .push((provided_span, format!("unexpected argument{}", provided_ty_name)));
+                    labels.push((provided_span, format!("unexpected argument{provided_ty_name}")));
                     let mut span = provided_span;
                     if span.can_be_used_for_suggestions() {
                         if arg_idx.index() > 0
@@ -1009,11 +1000,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                 args_span
                             };
                             let rendered = if !has_error_or_infer([input_ty]) {
-                                format!(" of type `{}`", input_ty)
+                                format!(" of type `{input_ty}`")
                             } else {
                                 "".to_string()
                             };
-                            labels.push((span, format!("an argument{} is missing", rendered)));
+                            labels.push((span, format!("an argument{rendered} is missing")));
                             suggestion_text = match suggestion_text {
                                 SuggestionText::None => SuggestionText::Provide(false),
                                 SuggestionText::Provide(_) => SuggestionText::Provide(true),
@@ -1034,13 +1025,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             let rendered =
                                 if !has_error_or_infer([first_expected_ty, second_expected_ty]) {
                                     format!(
-                                        " of type `{}` and `{}`",
-                                        first_expected_ty, second_expected_ty
+                                        " of type `{first_expected_ty}` and `{second_expected_ty}`"
                                     )
                                 } else {
                                     "".to_string()
                                 };
-                            labels.push((span, format!("two arguments{} are missing", rendered)));
+                            labels.push((span, format!("two arguments{rendered} are missing")));
                             suggestion_text = match suggestion_text {
                                 SuggestionText::None | SuggestionText::Provide(_) => {
                                     SuggestionText::Provide(true)
@@ -1066,13 +1056,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                 third_expected_ty,
                             ]) {
                                 format!(
-                                    " of type `{}`, `{}`, and `{}`",
-                                    first_expected_ty, second_expected_ty, third_expected_ty
+                                    " of type `{first_expected_ty}`, `{second_expected_ty}`, and `{third_expected_ty}`"
                                 )
                             } else {
                                 "".to_string()
                             };
-                            labels.push((span, format!("three arguments{} are missing", rendered)));
+                            labels.push((span, format!("three arguments{rendered} are missing")));
                             suggestion_text = match suggestion_text {
                                 SuggestionText::None | SuggestionText::Provide(_) => {
                                     SuggestionText::Provide(true)
@@ -1113,25 +1102,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     let (first_provided_ty, first_span) = provided_arg_tys[first_provided_idx];
                     let (_, first_expected_ty) = formal_and_expected_inputs[first_expected_idx];
                     let first_provided_ty_name = if !has_error_or_infer([first_provided_ty]) {
-                        format!(", found `{}`", first_provided_ty)
+                        format!(", found `{first_provided_ty}`")
                     } else {
                         String::new()
                     };
                     labels.push((
                         first_span,
-                        format!("expected `{}`{}", first_expected_ty, first_provided_ty_name),
+                        format!("expected `{first_expected_ty}`{first_provided_ty_name}"),
                     ));
 
                     let (second_provided_ty, second_span) = provided_arg_tys[second_provided_idx];
                     let (_, second_expected_ty) = formal_and_expected_inputs[second_expected_idx];
                     let second_provided_ty_name = if !has_error_or_infer([second_provided_ty]) {
-                        format!(", found `{}`", second_provided_ty)
+                        format!(", found `{second_provided_ty}`")
                     } else {
                         String::new()
                     };
                     labels.push((
                         second_span,
-                        format!("expected `{}`{}", second_expected_ty, second_provided_ty_name),
+                        format!("expected `{second_expected_ty}`{second_provided_ty_name}"),
                     ));
 
                     suggestion_text = match suggestion_text {
@@ -1144,13 +1133,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         let (_, expected_ty) = formal_and_expected_inputs[dst_arg];
                         let (provided_ty, provided_span) = provided_arg_tys[dest_input];
                         let provided_ty_name = if !has_error_or_infer([provided_ty]) {
-                            format!(", found `{}`", provided_ty)
+                            format!(", found `{provided_ty}`")
                         } else {
                             String::new()
                         };
                         labels.push((
                             provided_span,
-                            format!("expected `{}`{}", expected_ty, provided_ty_name),
+                            format!("expected `{expected_ty}`{provided_ty_name}"),
                         ));
                     }
 
@@ -2031,7 +2020,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             } else {
                 ("closure", self.tcx.def_span(def_id))
             };
-            err.span_note(span, format!("{} defined here", kind));
+            err.span_note(span, format!("{kind} defined here"));
         } else {
             err.span_note(
                 self.tcx.def_span(def_id),
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
index ec19d017c25..31b51dadcf4 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
@@ -397,7 +397,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     let struct_pat_shorthand_field =
                         self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr);
                     if let Some(name) = struct_pat_shorthand_field {
-                        sugg.insert(0, (expr.span.shrink_to_lo(), format!("{}: ", name)));
+                        sugg.insert(0, (expr.span.shrink_to_lo(), format!("{name}: ")));
                     }
                     Some(sugg)
                 })
@@ -558,7 +558,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     .take(4)
                     .map(|(var_hir_id, upvar)| {
                         let var_name = self.tcx.hir().name(*var_hir_id).to_string();
-                        let msg = format!("`{}` captured here", var_name);
+                        let msg = format!("`{var_name}` captured here");
                         (upvar.span, msg)
                     })
                     .collect::<Vec<_>>();
@@ -931,7 +931,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         err.span_suggestion(
             fn_return.span(),
             "consider using an impl return type",
-            format!("impl {}", all_bounds_str),
+            format!("impl {all_bounds_str}"),
             Applicability::MaybeIncorrect,
         );
     }
@@ -1070,7 +1070,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 .must_apply_modulo_regions()
           {
             let suggestion = match self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) {
-                Some(ident) => format!(": {}.clone()", ident),
+                Some(ident) => format!(": {ident}.clone()"),
                 None => ".clone()".to_string()
             };
 
@@ -1085,12 +1085,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         false
     }
 
-    pub(crate) fn suggest_copied_or_cloned(
+    pub(crate) fn suggest_copied_cloned_or_as_ref(
         &self,
         diag: &mut Diagnostic,
         expr: &hir::Expr<'_>,
         expr_ty: Ty<'tcx>,
         expected_ty: Ty<'tcx>,
+        expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
     ) -> bool {
         let ty::Adt(adt_def, args) = expr_ty.kind() else {
             return false;
@@ -1102,7 +1103,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             return false;
         }
 
-        let mut suggest_copied_or_cloned = || {
+        let mut suggest_copied_cloned_or_as_ref = || {
             let expr_inner_ty = args.type_at(0);
             let expected_inner_ty = expected_args.type_at(0);
             if let &ty::Ref(_, ty, hir::Mutability::Not) = expr_inner_ty.kind()
@@ -1119,6 +1120,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         Applicability::MachineApplicable,
                     );
                     return true;
+                } else if let Some(expected_ty_expr) = expected_ty_expr {
+                    diag.span_suggestion_verbose(
+                        expected_ty_expr.span.shrink_to_hi(),
+                        format!(
+                            "use `{def_path}::as_ref()` to convert `{expected_ty}` to `{expr_ty}`"
+                        ),
+                        ".as_ref()",
+                        Applicability::MachineApplicable,
+                    );
+                    return true;
                 } else if let Some(clone_did) = self.tcx.lang_items().clone_trait()
                     && rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions(
                         self,
@@ -1146,11 +1157,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             // Check that the error types are equal
             && self.can_eq(self.param_env, args.type_at(1), expected_args.type_at(1))
         {
-            return suggest_copied_or_cloned();
+            return suggest_copied_cloned_or_as_ref();
         } else if let Some(option_did) = self.tcx.get_diagnostic_item(sym::Option)
             && adt_def.did() == option_did
         {
-            return suggest_copied_or_cloned();
+            return suggest_copied_cloned_or_as_ref();
         }
 
         false
@@ -1248,7 +1259,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
 
         let suggestion = match self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) {
-            Some(ident) => format!(": {}.is_some()", ident),
+            Some(ident) => format!(": {ident}.is_some()"),
             None => ".is_some()".to_string(),
         };
 
@@ -1512,9 +1523,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         found_ty: Ty<'tcx>,
         expr: &hir::Expr<'_>,
     ) {
+        // When `expr` is `x` in something like `let x = foo.clone(); x`, need to recurse up to get
+        // `foo` and `clone`.
+        let expr = self.note_type_is_not_clone_inner_expr(expr);
+
+        // If we've recursed to an `expr` of `foo.clone()`, get `foo` and `clone`.
         let hir::ExprKind::MethodCall(segment, callee_expr, &[], _) = expr.kind else {
             return;
         };
+
         let Some(clone_trait_did) = self.tcx.lang_items().clone_trait() else {
             return;
         };
@@ -1567,6 +1584,84 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
     }
 
+    /// Given a type mismatch error caused by `&T` being cloned instead of `T`, and
+    /// the `expr` as the source of this type mismatch, try to find the method call
+    /// as the source of this error and return that instead. Otherwise, return the
+    /// original expression.
+    fn note_type_is_not_clone_inner_expr<'b>(
+        &'b self,
+        expr: &'b hir::Expr<'b>,
+    ) -> &'b hir::Expr<'b> {
+        match expr.peel_blocks().kind {
+            hir::ExprKind::Path(hir::QPath::Resolved(
+                None,
+                hir::Path { segments: [_], res: crate::Res::Local(binding), .. },
+            )) => {
+                let Some(hir::Node::Pat(hir::Pat { hir_id, .. })) = self.tcx.hir().find(*binding)
+                else {
+                    return expr;
+                };
+                let Some(parent) = self.tcx.hir().find(self.tcx.hir().parent_id(*hir_id)) else {
+                    return expr;
+                };
+
+                match parent {
+                    // foo.clone()
+                    hir::Node::Local(hir::Local { init: Some(init), .. }) => {
+                        self.note_type_is_not_clone_inner_expr(init)
+                    }
+                    // When `expr` is more complex like a tuple
+                    hir::Node::Pat(hir::Pat {
+                        hir_id: pat_hir_id,
+                        kind: hir::PatKind::Tuple(pats, ..),
+                        ..
+                    }) => {
+                        let Some(hir::Node::Local(hir::Local { init: Some(init), .. })) =
+                                self.tcx.hir().find(self.tcx.hir().parent_id(*pat_hir_id)) else {
+                            return expr;
+                        };
+
+                        match init.peel_blocks().kind {
+                            ExprKind::Tup(init_tup) => {
+                                if let Some(init) = pats
+                                    .iter()
+                                    .enumerate()
+                                    .filter(|x| x.1.hir_id == *hir_id)
+                                    .map(|(i, _)| init_tup.get(i).unwrap())
+                                    .next()
+                                {
+                                    self.note_type_is_not_clone_inner_expr(init)
+                                } else {
+                                    expr
+                                }
+                            }
+                            _ => expr,
+                        }
+                    }
+                    _ => expr,
+                }
+            }
+            // If we're calling into a closure that may not be typed recurse into that call. no need
+            // to worry if it's a call to a typed function or closure as this would ne handled
+            // previously.
+            hir::ExprKind::Call(Expr { kind: call_expr_kind, .. }, _) => {
+                if let hir::ExprKind::Path(hir::QPath::Resolved(None, call_expr_path)) = call_expr_kind
+                    && let hir::Path { segments: [_], res: crate::Res::Local(binding), .. } = call_expr_path
+                    && let Some(hir::Node::Pat(hir::Pat { hir_id, .. })) = self.tcx.hir().find(*binding)
+                    && let Some(closure) = self.tcx.hir().find(self.tcx.hir().parent_id(*hir_id))
+                    && let hir::Node::Local(hir::Local { init: Some(init), .. }) = closure
+                    && let Expr { kind: hir::ExprKind::Closure(hir::Closure { body: body_id, .. }), ..} = init
+                {
+                    let hir::Body { value: body_expr, .. } = self.tcx.hir().body(*body_id);
+                    self.note_type_is_not_clone_inner_expr(body_expr)
+                } else {
+                    expr
+                }
+            }
+            _ => expr,
+        }
+    }
+
     /// A common error is to add an extra semicolon:
     ///
     /// ```compile_fail,E0308
diff --git a/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/mod.rs b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/mod.rs
index e01dcf83a38..e563bd40b65 100644
--- a/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/mod.rs
+++ b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/mod.rs
@@ -125,8 +125,8 @@ impl Debug for TrackedValue {
                 write!(f, "{}", tcx.hir().node_to_string(self.hir_id()))
             } else {
                 match self {
-                    Self::Variable(hir_id) => write!(f, "Variable({:?})", hir_id),
-                    Self::Temporary(hir_id) => write!(f, "Temporary({:?})", hir_id),
+                    Self::Variable(hir_id) => write!(f, "Variable({hir_id:?})"),
+                    Self::Temporary(hir_id) => write!(f, "Temporary({hir_id:?})"),
                 }
             }
         })
diff --git a/compiler/rustc_hir_typeck/src/generator_interior/mod.rs b/compiler/rustc_hir_typeck/src/generator_interior/mod.rs
index 86ea092bc40..6a817122491 100644
--- a/compiler/rustc_hir_typeck/src/generator_interior/mod.rs
+++ b/compiler/rustc_hir_typeck/src/generator_interior/mod.rs
@@ -112,7 +112,7 @@ impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> {
                     self.fcx
                         .tcx
                         .sess
-                        .delay_span_bug(span, format!("Encountered var {:?}", unresolved_term));
+                        .delay_span_bug(span, format!("Encountered var {unresolved_term:?}"));
                 } else {
                     let note = format!(
                         "the type is part of the {} because of this {}",
diff --git a/compiler/rustc_hir_typeck/src/intrinsicck.rs b/compiler/rustc_hir_typeck/src/intrinsicck.rs
index 2d85451c9e1..2cd18c4c3fc 100644
--- a/compiler/rustc_hir_typeck/src/intrinsicck.rs
+++ b/compiler/rustc_hir_typeck/src/intrinsicck.rs
@@ -85,7 +85,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             Ok(SizeSkeleton::Pointer { tail, .. }) => format!("pointer to `{tail}`"),
             Ok(SizeSkeleton::Known(size)) => {
                 if let Some(v) = u128::from(size.bytes()).checked_mul(8) {
-                    format!("{} bits", v)
+                    format!("{v} bits")
                 } else {
                     // `u128` should definitely be able to hold the size of different architectures
                     // larger sizes should be reported as error `are too big for the current architecture`
diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs
index 28e7f290737..864308267bb 100644
--- a/compiler/rustc_hir_typeck/src/lib.rs
+++ b/compiler/rustc_hir_typeck/src/lib.rs
@@ -71,7 +71,7 @@ use rustc_middle::traits;
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_session::config;
 use rustc_span::def_id::{DefId, LocalDefId};
-use rustc_span::{sym, Span};
+use rustc_span::Span;
 
 fluent_messages! { "../messages.ftl" }
 
@@ -182,11 +182,7 @@ fn typeck_with_fallback<'tcx>(
     let body = tcx.hir().body(body_id);
 
     let param_env = tcx.param_env(def_id);
-    let param_env = if tcx.has_attr(def_id, sym::rustc_do_not_const_check) {
-        param_env.without_const()
-    } else {
-        param_env
-    };
+
     let inh = Inherited::new(tcx, def_id);
     let mut fcx = FnCtxt::new(&inh, param_env, def_id);
 
@@ -263,11 +259,7 @@ fn typeck_with_fallback<'tcx>(
 
     // Closure and generator analysis may run after fallback
     // because they don't constrain other type variables.
-    // Closure analysis only runs on closures. Therefore they only need to fulfill non-const predicates (as of now)
-    let prev_constness = fcx.param_env.constness();
-    fcx.param_env = fcx.param_env.without_const();
     fcx.closure_analyze(body);
-    fcx.param_env = fcx.param_env.with_constness(prev_constness);
     assert!(fcx.deferred_call_resolutions.borrow().is_empty());
     // Before the generator analysis, temporary scopes shall be marked to provide more
     // precise information on types to be captured.
diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs
index 6835782b5bd..7c73f6a89cd 100644
--- a/compiler/rustc_hir_typeck/src/method/confirm.rs
+++ b/compiler/rustc_hir_typeck/src/method/confirm.rs
@@ -225,7 +225,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
                         assert!(mutbl.is_mut());
                         Ty::new_ptr(self.tcx, ty::TypeAndMut { mutbl: hir::Mutability::Not, ty })
                     }
-                    other => panic!("Cannot adjust receiver type {:?} to const ptr", other),
+                    other => panic!("Cannot adjust receiver type {other:?} to const ptr"),
                 };
 
                 adjustments.push(Adjustment {
@@ -262,8 +262,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
                 let impl_def_id = pick.item.container_id(self.tcx);
                 assert!(
                     self.tcx.impl_trait_ref(impl_def_id).is_none(),
-                    "impl {:?} is not an inherent impl",
-                    impl_def_id
+                    "impl {impl_def_id:?} is not an inherent impl"
                 );
                 self.fresh_args_for_item(self.span, impl_def_id)
             }
diff --git a/compiler/rustc_hir_typeck/src/method/prelude2021.rs b/compiler/rustc_hir_typeck/src/method/prelude2021.rs
index 4efe95c4dc5..5b19a4c525f 100644
--- a/compiler/rustc_hir_typeck/src/method/prelude2021.rs
+++ b/compiler/rustc_hir_typeck/src/method/prelude2021.rs
@@ -97,28 +97,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         let self_adjusted = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) =
                             pick.autoref_or_ptr_adjustment
                         {
-                            format!("{}{} as *const _", derefs, self_expr)
+                            format!("{derefs}{self_expr} as *const _")
                         } else {
-                            format!("{}{}{}", autoref, derefs, self_expr)
+                            format!("{autoref}{derefs}{self_expr}")
                         };
 
                         lint.span_suggestion(
                             sp,
                             "disambiguate the method call",
-                            format!("({})", self_adjusted),
+                            format!("({self_adjusted})"),
                             Applicability::MachineApplicable,
                         );
                     } else {
                         let self_adjusted = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) =
                             pick.autoref_or_ptr_adjustment
                         {
-                            format!("{}(...) as *const _", derefs)
+                            format!("{derefs}(...) as *const _")
                         } else {
-                            format!("{}{}...", autoref, derefs)
+                            format!("{autoref}{derefs}...")
                         };
                         lint.span_help(
                             sp,
-                            format!("disambiguate the method call with `({})`", self_adjusted,),
+                            format!("disambiguate the method call with `({self_adjusted})`",),
                         );
                     }
 
@@ -168,7 +168,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                     .ok())
                                 {
                                     // Keep turbofish.
-                                    format!("::{}", args)
+                                    format!("::{args}")
                                 } else {
                                     String::new()
                                 },
@@ -347,7 +347,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 // Glob import, so just use its name.
                 return None;
             } else {
-                return Some(format!("{}", any_id));
+                return Some(format!("{any_id}"));
             }
         }
 
@@ -396,9 +396,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let adjusted_text = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) =
             pick.autoref_or_ptr_adjustment
         {
-            format!("{}{} as *const _", derefs, expr_text)
+            format!("{derefs}{expr_text} as *const _")
         } else {
-            format!("{}{}{}", autoref, derefs, expr_text)
+            format!("{autoref}{derefs}{expr_text}")
         };
 
         (adjusted_text, precise)
diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs
index 3d7187cb16f..f6c07931023 100644
--- a/compiler/rustc_hir_typeck/src/method/suggest.rs
+++ b/compiler/rustc_hir_typeck/src/method/suggest.rs
@@ -153,7 +153,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     E0034,
                     "multiple applicable items in scope"
                 );
-                err.span_label(item_name.span, format!("multiple `{}` found", item_name));
+                err.span_label(item_name.span, format!("multiple `{item_name}` found"));
 
                 self.note_candidates_on_method_error(
                     rcvr_ty,
@@ -177,13 +177,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     kind,
                     item_name
                 );
-                err.span_label(item_name.span, format!("private {}", kind));
+                err.span_label(item_name.span, format!("private {kind}"));
                 let sp = self
                     .tcx
                     .hir()
                     .span_if_local(def_id)
                     .unwrap_or_else(|| self.tcx.def_span(def_id));
-                err.span_label(sp, format!("private {} defined here", kind));
+                err.span_label(sp, format!("private {kind} defined here"));
                 self.suggest_valid_traits(&mut err, out_of_scope_traits);
                 err.emit();
             }
@@ -218,7 +218,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             *region,
                             ty::TypeAndMut { ty: *t_type, mutbl: mutability.invert() },
                         );
-                        let msg = format!("you need `{}` instead of `{}`", trait_type, rcvr_ty);
+                        let msg = format!("you need `{trait_type}` instead of `{rcvr_ty}`");
                         let mut kind = &self_expr.kind;
                         while let hir::ExprKind::AddrOf(_, _, expr)
                         | hir::ExprKind::Unary(hir::UnOp::Deref, expr) = kind
@@ -637,7 +637,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     }
                     // Point at the closure that couldn't satisfy the bound.
                     ty::Closure(def_id, _) => bound_spans
-                        .push((tcx.def_span(*def_id), format!("doesn't satisfy `{}`", quiet))),
+                        .push((tcx.def_span(*def_id), format!("doesn't satisfy `{quiet}`"))),
                     _ => {}
                 }
             };
@@ -659,7 +659,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
                         let term = pred.skip_binder().term;
 
-                        let obligation = format!("{} = {}", projection_ty, term);
+                        let obligation = format!("{projection_ty} = {term}");
                         let quiet = with_forced_trimmed_paths!(format!(
                             "{} = {}",
                             quiet_projection_ty, term
@@ -672,7 +672,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         let p = poly_trait_ref.trait_ref;
                         let self_ty = p.self_ty();
                         let path = p.print_only_trait_path();
-                        let obligation = format!("{}: {}", self_ty, path);
+                        let obligation = format!("{self_ty}: {path}");
                         let quiet = with_forced_trimmed_paths!(format!("_: {}", path));
                         bound_span_label(self_ty, &obligation, &quiet);
                         Some((obligation, self_ty))
@@ -825,12 +825,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 let mut preds: Vec<_> = predicates
                     .iter()
                     .filter_map(|pred| format_pred(**pred))
-                    .map(|(p, _)| format!("`{}`", p))
+                    .map(|(p, _)| format!("`{p}`"))
                     .collect();
                 preds.sort();
                 preds.dedup();
                 let msg = if let [pred] = &preds[..] {
-                    format!("trait bound {} was not satisfied", pred)
+                    format!("trait bound {pred} was not satisfied")
                 } else {
                     format!("the following trait bounds were not satisfied:\n{}", preds.join("\n"),)
                 };
@@ -875,7 +875,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                                 suggested_bounds.insert(pred);
                                             }
                                         }
-                                        format!("`{}`\nwhich is required by `{}`", p, parent_p)
+                                        format!("`{p}`\nwhich is required by `{parent_p}`")
                                     }
                                 },
                             },
@@ -1034,8 +1034,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             "".to_string()
                         };
                         err.note(format!(
-                            "the {item_kind} was found for\n{}{}",
-                            type_candidates, additional_types
+                            "the {item_kind} was found for\n{type_candidates}{additional_types}"
                         ));
                     } else {
                         'outer: for inherent_impl_did in self.tcx.inherent_impls(adt.did()) {
@@ -1249,8 +1248,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     } else {
                         (
                             format!(
-                                "the candidate is defined in an impl{} for the type `{}`",
-                                insertion, impl_ty,
+                                "the candidate is defined in an impl{insertion} for the type `{impl_ty}`",
                             ),
                             None,
                         )
@@ -1452,11 +1450,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             err.span_suggestion(
                 sugg_span,
                 "use associated function syntax instead",
-                format!("{}::{}{}", ty_str, item_name, args),
+                format!("{ty_str}::{item_name}{args}"),
                 applicability,
             );
         } else {
-            err.help(format!("try with `{}::{}`", ty_str, item_name,));
+            err.help(format!("try with `{ty_str}::{item_name}`",));
         }
     }
 
@@ -1491,9 +1489,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     let expr_span = expr.span.to(item_name.span);
                     err.multipart_suggestion(
                         format!(
-                            "to call the function stored in `{}`, \
+                            "to call the function stored in `{item_name}`, \
                                          surround the field access with parentheses",
-                            item_name,
                         ),
                         vec![
                             (expr_span.shrink_to_lo(), '('.to_string()),
@@ -1516,7 +1513,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             }
 
             let field_kind = if is_accessible { "field" } else { "private field" };
-            err.span_label(item_name.span, format!("{}, not a method", field_kind));
+            err.span_label(item_name.span, format!("{field_kind}, not a method"));
             return true;
         }
         false
@@ -1669,8 +1666,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         lit.span,
                         format!(
                             "you must specify a concrete type for this numeric value, \
-                                         like `{}`",
-                            concrete_type
+                                         like `{concrete_type}`"
                         ),
                         format!("{snippet}_{concrete_type}"),
                         Applicability::MaybeIncorrect,
@@ -1685,8 +1681,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         let parent_node =
                             self.tcx.hir().get_parent(hir_id);
                         let msg = format!(
-                            "you must specify a type for this binding, like `{}`",
-                            concrete_type,
+                            "you must specify a type for this binding, like `{concrete_type}`",
                         );
 
                         match (filename, parent_node) {
@@ -2194,7 +2189,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             if let Some((last_self_name, _, ref mut last_trait_names)) = derives_grouped.last_mut()
             {
                 if last_self_name == &self_name {
-                    last_trait_names.push_str(format!(", {}", trait_name).as_str());
+                    last_trait_names.push_str(format!(", {trait_name}").as_str());
                     continue;
                 }
             }
@@ -2226,8 +2221,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         for (self_name, self_span, traits) in &derives_grouped {
             err.span_suggestion_verbose(
                 self_span.shrink_to_lo(),
-                format!("consider annotating `{}` with `#[derive({})]`", self_name, traits),
-                format!("#[derive({})]\n", traits),
+                format!("consider annotating `{self_name}` with `#[derive({traits})]`"),
+                format!("#[derive({traits})]\n"),
                 Applicability::MaybeIncorrect,
             );
         }
@@ -2475,7 +2470,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         if pick.autoderefs == 0 && !skip {
                             err.span_label(
                                 pick.item.ident(self.tcx).span,
-                                format!("the method is available for `{}` here", rcvr_ty),
+                                format!("the method is available for `{rcvr_ty}` here"),
                             );
                         }
                         break;
@@ -2521,13 +2516,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         if pick.autoderefs == 0 && !skip {
                             err.span_label(
                                 pick.item.ident(self.tcx).span,
-                                format!("the method is available for `{}` here", new_rcvr_t),
+                                format!("the method is available for `{new_rcvr_t}` here"),
                             );
                             err.multipart_suggestion(
                                 "consider wrapping the receiver expression with the \
                                     appropriate type",
                                 vec![
-                                    (rcvr.span.shrink_to_lo(), format!("{}({}", pre, post)),
+                                    (rcvr.span.shrink_to_lo(), format!("{pre}({post}")),
                                     (rcvr.span.shrink_to_hi(), ")".to_string()),
                                 ],
                                 Applicability::MaybeIncorrect,
@@ -2767,7 +2762,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             };
                             err.span_suggestions(
                                 sp,
-                                message(format!("add {} supertrait for", article)),
+                                message(format!("add {article} supertrait for")),
                                 candidates.iter().map(|t| {
                                     format!("{} {}", sep, self.tcx.def_path_str(t.def_id),)
                                 }),
@@ -2836,7 +2831,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 trait_infos => {
                     let mut msg = message(param_type.map_or_else(
                         || "implement".to_string(), // FIXME: it might only need to be imported into scope, not implemented.
-                        |param| format!("restrict type parameter `{}` with", param),
+                        |param| format!("restrict type parameter `{param}` with"),
                     ));
                     for (i, trait_info) in trait_infos.iter().enumerate() {
                         msg.push_str(&format!(
@@ -2860,8 +2855,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 }
                 trait_infos => {
                     let mut msg = format!(
-                        "the following traits define an item `{}`, but are explicitly unimplemented:",
-                        item_name
+                        "the following traits define an item `{item_name}`, but are explicitly unimplemented:"
                     );
                     for trait_info in trait_infos {
                         msg.push_str(&format!("\n{}", self.tcx.def_path_str(trait_info.def_id)));
@@ -3027,13 +3021,13 @@ fn print_disambiguation_help<'tcx>(
                 .join(", "),
         );
         let trait_name = if !fn_has_self_parameter {
-            format!("<{} as {}>", rcvr_ty, trait_name)
+            format!("<{rcvr_ty} as {trait_name}>")
         } else {
             trait_name
         };
-        (span, format!("{}::{}{}", trait_name, item_name, args))
+        (span, format!("{trait_name}::{item_name}{args}"))
     } else {
-        (span.with_hi(item_name.span.lo()), format!("<{} as {}>::", rcvr_ty, trait_name))
+        (span.with_hi(item_name.span.lo()), format!("<{rcvr_ty} as {trait_name}>::"))
     };
     err.span_suggestion_verbose(
         span,
@@ -3041,7 +3035,7 @@ fn print_disambiguation_help<'tcx>(
             "disambiguate the {} for {}",
             def_kind_descr,
             if let Some(candidate) = candidate {
-                format!("candidate #{}", candidate)
+                format!("candidate #{candidate}")
             } else {
                 "the candidate".to_string()
             },
diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index 8bf95d4bf9a..6ffa3b0a0b3 100644
--- a/compiler/rustc_hir_typeck/src/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -516,7 +516,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     fn endpoint_has_type(&self, err: &mut Diagnostic, span: Span, ty: Ty<'_>) {
         if !ty.references_error() {
-            err.span_label(span, format!("this is of type `{}`", ty));
+            err.span_label(span, format!("this is of type `{ty}`"));
         }
     }
 
@@ -540,7 +540,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         );
         let msg = |ty| {
             let ty = self.resolve_vars_if_possible(ty);
-            format!("this is of type `{}` but it should be `char` or numeric", ty)
+            format!("this is of type `{ty}` but it should be `char` or numeric")
         };
         let mut one_side_err = |first_span, first_ty, second: Option<(bool, Ty<'tcx>, Span)>| {
             err.span_label(first_span, msg(first_ty));
@@ -653,7 +653,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 )
             });
             let pre = if in_match { "in the same arm, " } else { "" };
-            err.note(format!("{}a binding must have the same type in all alternatives", pre));
+            err.note(format!("{pre}a binding must have the same type in all alternatives"));
             self.suggest_adding_missing_ref_or_removing_ref(
                 &mut err,
                 span,
@@ -1710,7 +1710,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             err.span_suggestion_verbose(
                 qpath.span().shrink_to_hi().to(pat.span.shrink_to_hi()),
                 "use the tuple variant pattern syntax instead",
-                format!("({})", sugg),
+                format!("({sugg})"),
                 appl,
             );
             return Some(err);
@@ -1812,7 +1812,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             const LIMIT: usize = 3;
             match witnesses {
                 [] => bug!(),
-                [witness] => format!("`{}`", witness),
+                [witness] => format!("`{witness}`"),
                 [head @ .., tail] if head.len() < LIMIT => {
                     let head: Vec<_> = head.iter().map(<_>::to_string).collect();
                     format!("`{}` and `{}`", head.join("`, `"), tail)
@@ -1834,8 +1834,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             "ensure that all fields are mentioned explicitly by adding the suggested fields",
         );
         lint.note(format!(
-            "the pattern is of type `{}` and the `non_exhaustive_omitted_patterns` attribute was found",
-            ty,
+            "the pattern is of type `{ty}` and the `non_exhaustive_omitted_patterns` attribute was found",
         ));
 
         lint
@@ -1864,10 +1863,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         } else {
             let fields = unmentioned_fields
                 .iter()
-                .map(|(_, name)| format!("`{}`", name))
+                .map(|(_, name)| format!("`{name}`"))
                 .collect::<Vec<String>>()
                 .join(", ");
-            format!("fields {}{}", fields, inaccessible)
+            format!("fields {fields}{inaccessible}")
         };
         let mut err = struct_span_err!(
             self.tcx.sess,
@@ -1876,7 +1875,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             "pattern does not mention {}",
             field_names
         );
-        err.span_label(pat.span, format!("missing {}", field_names));
+        err.span_label(pat.span, format!("missing {field_names}"));
         let len = unmentioned_fields.len();
         let (prefix, postfix, sp) = match fields {
             [] => match &pat.kind {
@@ -1909,11 +1908,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     .iter()
                     .map(|(_, name)| {
                         let field_name = name.to_string();
-                        if is_number(&field_name) {
-                            format!("{}: _", field_name)
-                        } else {
-                            field_name
-                        }
+                        if is_number(&field_name) { format!("{field_name}: _") } else { field_name }
                     })
                     .collect::<Vec<_>>()
                     .join(", "),
@@ -1930,7 +1925,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 s = pluralize!(len),
                 them = if len == 1 { "it" } else { "them" },
             ),
-            format!("{}..{}", prefix, postfix),
+            format!("{prefix}..{postfix}"),
             Applicability::MachineApplicable,
         );
         err
diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs
index fb81a8395d7..be939560c45 100644
--- a/compiler/rustc_hir_typeck/src/upvar.rs
+++ b/compiler/rustc_hir_typeck/src/upvar.rs
@@ -109,11 +109,11 @@ impl MigrationWarningReason {
     fn migration_message(&self) -> String {
         let base = "changes to closure capture in Rust 2021 will affect";
         if !self.auto_traits.is_empty() && self.drop_order {
-            format!("{} drop order and which traits the closure implements", base)
+            format!("{base} drop order and which traits the closure implements")
         } else if self.drop_order {
-            format!("{} drop order", base)
+            format!("{base} drop order")
         } else {
-            format!("{} which traits the closure implements", base)
+            format!("{base} which traits the closure implements")
         }
     }
 }
@@ -824,8 +824,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     lint.note("for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>");
 
                     let diagnostic_msg = format!(
-                        "add a dummy let to cause {} to be fully captured",
-                        migrated_variables_concat
+                        "add a dummy let to cause {migrated_variables_concat} to be fully captured"
                     );
 
                     let closure_span = self.tcx.hir().span_with_body(closure_hir_id);
@@ -932,7 +931,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         var_hir_id: hir::HirId,
         closure_clause: hir::CaptureBy,
     ) -> Option<FxIndexMap<UpvarMigrationInfo, UnordSet<&'static str>>> {
-        let auto_traits_def_id = vec![
+        let auto_traits_def_id = [
             self.tcx.lang_items().clone_trait(),
             self.tcx.lang_items().sync_trait(),
             self.tcx.get_diagnostic_item(sym::Send),
@@ -1943,7 +1942,7 @@ fn construct_place_string<'tcx>(tcx: TyCtxt<'_>, place: &Place<'tcx>) -> String
     let mut projections_str = String::new();
     for (i, item) in place.projections.iter().enumerate() {
         let proj = match item.kind {
-            ProjectionKind::Field(a, b) => format!("({:?}, {:?})", a, b),
+            ProjectionKind::Field(a, b) => format!("({a:?}, {b:?})"),
             ProjectionKind::Deref => String::from("Deref"),
             ProjectionKind::Index => String::from("Index"),
             ProjectionKind::Subslice => String::from("Subslice"),
@@ -1966,7 +1965,7 @@ fn construct_capture_kind_reason_string<'tcx>(
 
     let capture_kind_str = match capture_info.capture_kind {
         ty::UpvarCapture::ByValue => "ByValue".into(),
-        ty::UpvarCapture::ByRef(kind) => format!("{:?}", kind),
+        ty::UpvarCapture::ByRef(kind) => format!("{kind:?}"),
     };
 
     format!("{place_str} captured as {capture_kind_str} here")
@@ -1987,7 +1986,7 @@ fn construct_capture_info_string<'tcx>(
 
     let capture_kind_str = match capture_info.capture_kind {
         ty::UpvarCapture::ByValue => "ByValue".into(),
-        ty::UpvarCapture::ByRef(kind) => format!("{:?}", kind),
+        ty::UpvarCapture::ByRef(kind) => format!("{kind:?}"),
     };
     format!("{place_str} -> {capture_kind_str}")
 }
diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs
index 2329a1f63ce..6f47623ec43 100644
--- a/compiler/rustc_hir_typeck/src/writeback.rs
+++ b/compiler/rustc_hir_typeck/src/writeback.rs
@@ -217,7 +217,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
                 // When encountering `return [0][0]` outside of a `fn` body we can encounter a base
                 // that isn't in the type table. We assume more relevant errors have already been
                 // emitted, so we delay an ICE if none have. (#64638)
-                self.tcx().sess.delay_span_bug(e.span, format!("bad base: `{:?}`", base));
+                self.tcx().sess.delay_span_bug(e.span, format!("bad base: `{base:?}`"));
             }
             if let Some(base_ty) = base_ty
                 && let ty::Ref(_, base_ty_inner, _) = *base_ty.kind()
@@ -231,7 +231,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
                         Ty::new_error_with_message(
                             self.fcx.tcx,
                             e.span,
-                            format!("bad index {:?} for base: `{:?}`", index, base),
+                            format!("bad index {index:?} for base: `{base:?}`"),
                         )
                     });
                 if self.is_builtin_index(e, base_ty_inner, index_ty) {
@@ -488,10 +488,8 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
                     let span = self.tcx().hir().span(hir_id);
                     // We need to buffer the errors in order to guarantee a consistent
                     // order when emitting them.
-                    let err = self
-                        .tcx()
-                        .sess
-                        .struct_span_err(span, format!("user args: {:?}", user_args));
+                    let err =
+                        self.tcx().sess.struct_span_err(span, format!("user args: {user_args:?}"));
                     err.buffer(&mut errors_buffer);
                 }
             }
diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index 8e3c76d6a4b..d3978e242a8 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -264,11 +264,11 @@ fn msg_span_from_named_region<'tcx>(
         ty::RePlaceholder(ty::PlaceholderRegion {
             bound: ty::BoundRegion { kind: ty::BoundRegionKind::BrAnon(Some(span)), .. },
             ..
-        }) => (format!("the anonymous lifetime defined here"), Some(span)),
+        }) => ("the anonymous lifetime defined here".to_owned(), Some(span)),
         ty::RePlaceholder(ty::PlaceholderRegion {
             bound: ty::BoundRegion { kind: ty::BoundRegionKind::BrAnon(None), .. },
             ..
-        }) => (format!("an anonymous lifetime"), None),
+        }) => ("an anonymous lifetime".to_owned(), None),
         _ => bug!("{:?}", region),
     }
 }
@@ -2354,7 +2354,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                                     if let Ok(snip) = self.tcx.sess.source_map().span_to_next_source(p.span)
                                         && snip.starts_with(' ')
                                     {
-                                        format!("{new_lt}")
+                                        new_lt.to_string()
                                     } else {
                                         format!("{new_lt} ")
                                     }
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs
index cc92bfbfdfe..40dc6497611 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs
@@ -333,11 +333,7 @@ pub fn suggest_new_region_bound(
                     } else {
                         None
                     };
-                    let name = if let Some(name) = &existing_lt_name {
-                        format!("{}", name)
-                    } else {
-                        format!("'a")
-                    };
+                    let name = if let Some(name) = &existing_lt_name { name } else { "'a" };
                     // if there are more than one elided lifetimes in inputs, the explicit `'_` lifetime cannot be used.
                     // introducing a new lifetime `'a` or making use of one from existing named lifetimes if any
                     if let Some(id) = scope_def_id
@@ -350,7 +346,7 @@ pub fn suggest_new_region_bound(
                                   if p.span.hi() - p.span.lo() == rustc_span::BytePos(1) { // Ampersand (elided without '_)
                                       (p.span.shrink_to_hi(),format!("{name} "))
                                   } else { // Underscore (elided with '_)
-                                      (p.span, format!("{name}"))
+                                      (p.span, name.to_string())
                                   }
                             )
                             .collect::<Vec<_>>()
diff --git a/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs b/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs
index dd65f66ccd1..b6ff8f2f512 100644
--- a/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs
+++ b/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs
@@ -425,9 +425,11 @@ impl<'tcx> MiniGraph<'tcx> {
                 }
             }
         } else {
-            for (constraint, _origin) in &region_constraints.data().constraints {
-                each_constraint(constraint)
-            }
+            region_constraints
+                .data()
+                .constraints
+                .keys()
+                .for_each(|constraint| each_constraint(constraint));
         }
     }
 
diff --git a/compiler/rustc_infer/src/traits/mod.rs b/compiler/rustc_infer/src/traits/mod.rs
index 626dd9359a1..a9da6104b38 100644
--- a/compiler/rustc_infer/src/traits/mod.rs
+++ b/compiler/rustc_infer/src/traits/mod.rs
@@ -79,7 +79,6 @@ impl<'tcx> PredicateObligation<'tcx> {
     }
 
     pub fn without_const(mut self, tcx: TyCtxt<'tcx>) -> PredicateObligation<'tcx> {
-        self.param_env = self.param_env.without_const();
         if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred)) = self.predicate.kind().skip_binder() && trait_pred.is_const_if_const() {
             self.predicate = tcx.mk_predicate(self.predicate.kind().map_bound(|_| ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred.without_const()))));
         }
@@ -88,14 +87,6 @@ impl<'tcx> PredicateObligation<'tcx> {
 }
 
 impl<'tcx> PolyTraitObligation<'tcx> {
-    /// Returns `true` if the trait predicate is considered `const` in its ParamEnv.
-    pub fn is_const(&self) -> bool {
-        matches!(
-            (self.predicate.skip_binder().constness, self.param_env.constness()),
-            (ty::BoundConstness::ConstIfConst, hir::Constness::Const)
-        )
-    }
-
     pub fn derived_cause(
         &self,
         variant: impl FnOnce(DerivedObligationCause<'tcx>) -> ObligationCauseCode<'tcx>,
diff --git a/compiler/rustc_interface/Cargo.toml b/compiler/rustc_interface/Cargo.toml
index 7826d42dcb2..2c7438ed9db 100644
--- a/compiler/rustc_interface/Cargo.toml
+++ b/compiler/rustc_interface/Cargo.toml
@@ -6,7 +6,6 @@ edition = "2021"
 [lib]
 
 [dependencies]
-atty = "0.2.13"
 libloading = "0.7.1"
 tracing = "0.1"
 rustc-rayon-core = { version = "0.5.0", optional = true }
diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs
index aedc662b067..e3d66d18388 100644
--- a/compiler/rustc_interface/src/tests.rs
+++ b/compiler/rustc_interface/src/tests.rs
@@ -28,7 +28,6 @@ use rustc_span::edition::{Edition, DEFAULT_EDITION};
 use rustc_span::symbol::sym;
 use rustc_span::FileName;
 use rustc_span::SourceFileHashAlgorithm;
-use rustc_target::abi::ReferenceNichePolicy;
 use rustc_target::spec::{CodeModel, LinkerFlavorCli, MergeFunctions, PanicStrategy, RelocModel};
 use rustc_target::spec::{RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, TlsModel};
 
@@ -741,6 +740,7 @@ fn test_unstable_options_tracking_hash() {
     untracked!(unstable_options, true);
     untracked!(validate_mir, true);
     untracked!(verbose, true);
+    untracked!(write_long_types_to_disk, false);
     // tidy-alphabetical-end
 
     macro_rules! tracked {
@@ -821,7 +821,6 @@ fn test_unstable_options_tracking_hash() {
     tracked!(profile_emit, Some(PathBuf::from("abc")));
     tracked!(profile_sample_use, Some(PathBuf::from("abc")));
     tracked!(profiler_runtime, "abc".to_string());
-    tracked!(reference_niches, Some(ReferenceNichePolicy { size: true, align: false }));
     tracked!(relax_elf_relocations, Some(true));
     tracked!(relro_level, Some(RelroLevel::Full));
     tracked!(remap_cwd_prefix, Some(PathBuf::from("abc")));
diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs
index 12d33f06309..ad35dbbc8f9 100644
--- a/compiler/rustc_interface/src/util.rs
+++ b/compiler/rustc_interface/src/util.rs
@@ -519,7 +519,8 @@ fn multiple_output_types_to_stdout(
     output_types: &OutputTypes,
     single_output_file_is_stdout: bool,
 ) -> bool {
-    if atty::is(atty::Stream::Stdout) {
+    use std::io::IsTerminal;
+    if std::io::stdout().is_terminal() {
         // If stdout is a tty, check if multiple text output types are
         // specified by `--emit foo=- --emit bar=-` or `-o - --emit foo,bar`
         let named_text_types = output_types
diff --git a/compiler/rustc_lexer/src/cursor.rs b/compiler/rustc_lexer/src/cursor.rs
index eceef59802e..aba7f95487e 100644
--- a/compiler/rustc_lexer/src/cursor.rs
+++ b/compiler/rustc_lexer/src/cursor.rs
@@ -24,6 +24,10 @@ impl<'a> Cursor<'a> {
         }
     }
 
+    pub fn as_str(&self) -> &'a str {
+        self.chars.as_str()
+    }
+
     /// Returns the last eaten symbol (or `'\0'` in release builds).
     /// (For debug assertions only.)
     pub(crate) fn prev(&self) -> char {
diff --git a/compiler/rustc_lexer/src/lib.rs b/compiler/rustc_lexer/src/lib.rs
index 29335a8c0f4..d511d2b1280 100644
--- a/compiler/rustc_lexer/src/lib.rs
+++ b/compiler/rustc_lexer/src/lib.rs
@@ -367,6 +367,13 @@ impl Cursor<'_> {
                 Some(|terminated| Byte { terminated }),
             ),
 
+            // c-string literal, raw c-string literal or identifier.
+            'c' => self.c_or_byte_string(
+                |terminated| CStr { terminated },
+                |n_hashes| RawCStr { n_hashes },
+                None,
+            ),
+
             // Identifier (this should be checked after other variant that can
             // start as identifier).
             c if is_id_start(c) => self.ident_or_unknown_prefix(),
diff --git a/compiler/rustc_lexer/src/unescape.rs b/compiler/rustc_lexer/src/unescape.rs
index c9ad54d8d98..717b042fbda 100644
--- a/compiler/rustc_lexer/src/unescape.rs
+++ b/compiler/rustc_lexer/src/unescape.rs
@@ -372,7 +372,7 @@ where
         callback(start..end, EscapeError::MultipleSkippedLinesWarning);
     }
     let tail = &tail[first_non_space..];
-    if let Some(c) = tail.chars().nth(0) {
+    if let Some(c) = tail.chars().next() {
         if c.is_whitespace() {
             // For error reporting, we would like the span to contain the character that was not
             // skipped. The +1 is necessary to account for the leading \ that started the escape.
diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl
index 2c92277b50d..252177932e4 100644
--- a/compiler/rustc_lint/messages.ftl
+++ b/compiler/rustc_lint/messages.ftl
@@ -267,8 +267,6 @@ lint_improper_ctypes_char_help = consider using `u32` or `libc::wchar_t` instead
 lint_improper_ctypes_char_reason = the `char` type has no C equivalent
 lint_improper_ctypes_dyn = trait objects have no C equivalent
 
-lint_improper_ctypes_enum_phantomdata = this enum contains a PhantomData field
-
 lint_improper_ctypes_enum_repr_help =
     consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
 
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index 84aca80b0de..cf6e7f16f07 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -1716,7 +1716,7 @@ impl EarlyLintPass for EllipsisInclusiveRangePatterns {
                 let end = expr_to_string(&end);
                 let replace = match start {
                     Some(start) => format!("&({}..={})", expr_to_string(&start), end),
-                    None => format!("&(..={})", end),
+                    None => format!("&(..={end})"),
                 };
                 if join.edition() >= Edition::Edition2021 {
                     cx.sess().emit_err(BuiltinEllipsisInclusiveRangePatterns {
diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs
index 7c701fd4fe1..65c56bff841 100644
--- a/compiler/rustc_lint/src/context.rs
+++ b/compiler/rustc_lint/src/context.rs
@@ -411,7 +411,7 @@ impl LintStore {
         }
 
         let complete_name = if let Some(tool_name) = tool_name {
-            format!("{}::{}", tool_name, lint_name)
+            format!("{tool_name}::{lint_name}")
         } else {
             lint_name.to_string()
         };
@@ -424,7 +424,7 @@ impl LintStore {
                         // 1. The tool is currently running, so this lint really doesn't exist.
                         // FIXME: should this handle tools that never register a lint, like rustfmt?
                         debug!("lints={:?}", self.by_name.keys().collect::<Vec<_>>());
-                        let tool_prefix = format!("{}::", tool_name);
+                        let tool_prefix = format!("{tool_name}::");
                         return if self.by_name.keys().any(|lint| lint.starts_with(&tool_prefix)) {
                             self.no_lint_suggestion(&complete_name)
                         } else {
@@ -445,11 +445,11 @@ impl LintStore {
         }
         match self.by_name.get(&complete_name) {
             Some(Renamed(new_name, _)) => CheckLintNameResult::Warning(
-                format!("lint `{}` has been renamed to `{}`", complete_name, new_name),
+                format!("lint `{complete_name}` has been renamed to `{new_name}`"),
                 Some(new_name.to_owned()),
             ),
             Some(Removed(reason)) => CheckLintNameResult::Warning(
-                format!("lint `{}` has been removed: {}", complete_name, reason),
+                format!("lint `{complete_name}` has been removed: {reason}"),
                 None,
             ),
             None => match self.lint_groups.get(&*complete_name) {
@@ -503,7 +503,7 @@ impl LintStore {
         lint_name: &str,
         tool_name: &str,
     ) -> CheckLintNameResult<'_> {
-        let complete_name = format!("{}::{}", tool_name, lint_name);
+        let complete_name = format!("{tool_name}::{lint_name}");
         match self.by_name.get(&complete_name) {
             None => match self.lint_groups.get(&*complete_name) {
                 // Now we are sure, that this lint exists nowhere
@@ -618,12 +618,10 @@ pub trait LintContext: Sized {
                         _ => ("", "s"),
                     };
                     db.span_label(span, format!(
-                        "this comment contains {}invisible unicode text flow control codepoint{}",
-                        an,
-                        s,
+                        "this comment contains {an}invisible unicode text flow control codepoint{s}",
                     ));
                     for (c, span) in &spans {
-                        db.span_label(*span, format!("{:?}", c));
+                        db.span_label(*span, format!("{c:?}"));
                     }
                     db.note(
                         "these kind of unicode codepoints change the way text flows on \
@@ -648,7 +646,7 @@ pub trait LintContext: Sized {
                             let opt_colon =
                                 if s.trim_start().starts_with("::") { "" } else { "::" };
 
-                            (format!("crate{}{}", opt_colon, s), Applicability::MachineApplicable)
+                            (format!("crate{opt_colon}{s}"), Applicability::MachineApplicable)
                         }
                         Err(_) => ("crate::<path>".to_string(), Applicability::HasPlaceholders),
                     };
@@ -704,7 +702,7 @@ pub trait LintContext: Sized {
                         let introduced = if is_imported { "imported" } else { "defined" };
                         db.span_label(
                             span,
-                            format!("the item `{}` is already {} here", ident, introduced),
+                            format!("the item `{ident}` is already {introduced} here"),
                         );
                     }
                 }
@@ -908,7 +906,7 @@ pub trait LintContext: Sized {
                 BuiltinLintDiagnostics::NamedArgumentUsedPositionally{ position_sp_to_replace, position_sp_for_msg, named_arg_sp, named_arg_name, is_formatting_arg} => {
                     db.span_label(named_arg_sp, "this named argument is referred to by position in formatting string");
                     if let Some(positional_arg_for_msg) = position_sp_for_msg {
-                        let msg = format!("this formatting argument uses named argument `{}` by position", named_arg_name);
+                        let msg = format!("this formatting argument uses named argument `{named_arg_name}` by position");
                         db.span_label(positional_arg_for_msg, msg);
                     }
 
@@ -949,11 +947,11 @@ pub trait LintContext: Sized {
                     );
                 }
                 BuiltinLintDiagnostics::AmbiguousGlobReexports { name, namespace, first_reexport_span, duplicate_reexport_span } => {
-                    db.span_label(first_reexport_span, format!("the name `{}` in the {} namespace is first re-exported here", name, namespace));
-                    db.span_label(duplicate_reexport_span, format!("but the name `{}` in the {} namespace is also re-exported here", name, namespace));
+                    db.span_label(first_reexport_span, format!("the name `{name}` in the {namespace} namespace is first re-exported here"));
+                    db.span_label(duplicate_reexport_span, format!("but the name `{name}` in the {namespace} namespace is also re-exported here"));
                 }
                 BuiltinLintDiagnostics::HiddenGlobReexports { name, namespace, glob_reexport_span, private_item_span } => {
-                    db.span_note(glob_reexport_span, format!("the name `{}` in the {} namespace is supposed to be publicly re-exported here", name, namespace));
+                    db.span_note(glob_reexport_span, format!("the name `{name}` in the {namespace} namespace is supposed to be publicly re-exported here"));
                     db.span_note(private_item_span, "but the private item here shadows it".to_owned());
                 }
                 BuiltinLintDiagnostics::UnusedQualifications { removal_span } => {
@@ -1281,8 +1279,8 @@ impl<'tcx> LateContext<'tcx> {
                 // This shouldn't ever be needed, but just in case:
                 with_no_trimmed_paths!({
                     Ok(vec![match trait_ref {
-                        Some(trait_ref) => Symbol::intern(&format!("{:?}", trait_ref)),
-                        None => Symbol::intern(&format!("<{}>", self_ty)),
+                        Some(trait_ref) => Symbol::intern(&format!("{trait_ref:?}")),
+                        None => Symbol::intern(&format!("<{self_ty}>")),
                     }])
                 })
             }
@@ -1306,7 +1304,7 @@ impl<'tcx> LateContext<'tcx> {
                         )))
                     }
                     None => {
-                        with_no_trimmed_paths!(Symbol::intern(&format!("<impl {}>", self_ty)))
+                        with_no_trimmed_paths!(Symbol::intern(&format!("<impl {self_ty}>")))
                     }
                 });
 
diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs
index 0c80141a756..fb407be1f02 100644
--- a/compiler/rustc_lint/src/levels.rs
+++ b/compiler/rustc_lint/src/levels.rs
@@ -945,7 +945,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
                             );
                         }
                     } else {
-                        panic!("renamed lint does not exist: {}", new_name);
+                        panic!("renamed lint does not exist: {new_name}");
                     }
                 }
             }
diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs
index 1dea758bb29..2f2a5a93347 100644
--- a/compiler/rustc_lint/src/lints.rs
+++ b/compiler/rustc_lint/src/lints.rs
@@ -776,7 +776,7 @@ impl AddToDiagnostic for HiddenUnicodeCodepointsDiagLabels {
         ) -> rustc_errors::SubdiagnosticMessage,
     {
         for (c, span) in self.spans {
-            diag.span_label(span, format!("{:?}", c));
+            diag.span_label(span, format!("{c:?}"));
         }
     }
 }
@@ -808,7 +808,7 @@ impl AddToDiagnostic for HiddenUnicodeCodepointsDiagSub {
                     spans
                         .into_iter()
                         .map(|(c, span)| {
-                            let c = format!("{:?}", c);
+                            let c = format!("{c:?}");
                             (span, c[1..c.len() - 1].to_string())
                         })
                         .collect(),
@@ -823,7 +823,7 @@ impl AddToDiagnostic for HiddenUnicodeCodepointsDiagSub {
                     "escaped",
                     spans
                         .into_iter()
-                        .map(|(c, _)| format!("{:?}", c))
+                        .map(|(c, _)| format!("{c:?}"))
                         .collect::<Vec<String>>()
                         .join(", "),
                 );
diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs
index cc8a3408004..226d01b79a8 100644
--- a/compiler/rustc_lint/src/types.rs
+++ b/compiler/rustc_lint/src/types.rs
@@ -966,12 +966,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
         args: GenericArgsRef<'tcx>,
     ) -> FfiResult<'tcx> {
         let field_ty = field.ty(self.cx.tcx, args);
-        if field_ty.has_opaque_types() {
-            self.check_type_for_ffi(cache, field_ty)
-        } else {
-            let field_ty = self.cx.tcx.normalize_erasing_regions(self.cx.param_env, field_ty);
-            self.check_type_for_ffi(cache, field_ty)
-        }
+        let field_ty = self
+            .cx
+            .tcx
+            .try_normalize_erasing_regions(self.cx.param_env, field_ty)
+            .unwrap_or(field_ty);
+        self.check_type_for_ffi(cache, field_ty)
     }
 
     /// Checks if the given `VariantDef`'s field types are "ffi-safe".
@@ -985,39 +985,43 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
     ) -> FfiResult<'tcx> {
         use FfiResult::*;
 
-        let transparent_safety = def.repr().transparent().then(|| {
-            // Can assume that at most one field is not a ZST, so only check
-            // that field's type for FFI-safety.
+        let transparent_with_all_zst_fields = if def.repr().transparent() {
             if let Some(field) = transparent_newtype_field(self.cx.tcx, variant) {
-                return self.check_field_type_for_ffi(cache, field, args);
+                // Transparent newtypes have at most one non-ZST field which needs to be checked..
+                match self.check_field_type_for_ffi(cache, field, args) {
+                    FfiUnsafe { ty, .. } if ty.is_unit() => (),
+                    r => return r,
+                }
+
+                false
             } else {
-                // All fields are ZSTs; this means that the type should behave
-                // like (), which is FFI-unsafe... except if all fields are PhantomData,
-                // which is tested for below
-                FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_struct_zst, help: None }
+                // ..or have only ZST fields, which is FFI-unsafe (unless those fields are all
+                // `PhantomData`).
+                true
             }
-        });
-        // We can't completely trust repr(C) markings; make sure the fields are
-        // actually safe.
+        } else {
+            false
+        };
+
+        // We can't completely trust `repr(C)` markings, so make sure the fields are actually safe.
         let mut all_phantom = !variant.fields.is_empty();
         for field in &variant.fields {
-            match self.check_field_type_for_ffi(cache, &field, args) {
-                FfiSafe => {
-                    all_phantom = false;
-                }
-                FfiPhantom(..) if !def.repr().transparent() && def.is_enum() => {
-                    return FfiUnsafe {
-                        ty,
-                        reason: fluent::lint_improper_ctypes_enum_phantomdata,
-                        help: None,
-                    };
-                }
-                FfiPhantom(..) => {}
-                r => return transparent_safety.unwrap_or(r),
+            all_phantom &= match self.check_field_type_for_ffi(cache, &field, args) {
+                FfiSafe => false,
+                // `()` fields are FFI-safe!
+                FfiUnsafe { ty, .. } if ty.is_unit() => false,
+                FfiPhantom(..) => true,
+                r @ FfiUnsafe { .. } => return r,
             }
         }
 
-        if all_phantom { FfiPhantom(ty) } else { transparent_safety.unwrap_or(FfiSafe) }
+        if all_phantom {
+            FfiPhantom(ty)
+        } else if transparent_with_all_zst_fields {
+            FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_struct_zst, help: None }
+        } else {
+            FfiSafe
+        }
     }
 
     /// Checks if the given type is "ffi-safe" (has a stable, well-defined
@@ -1220,25 +1224,19 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
                 }
 
                 let sig = tcx.erase_late_bound_regions(sig);
-                if !sig.output().is_unit() {
-                    let r = self.check_type_for_ffi(cache, sig.output());
-                    match r {
-                        FfiSafe => {}
-                        _ => {
-                            return r;
-                        }
-                    }
-                }
                 for arg in sig.inputs() {
-                    let r = self.check_type_for_ffi(cache, *arg);
-                    match r {
+                    match self.check_type_for_ffi(cache, *arg) {
                         FfiSafe => {}
-                        _ => {
-                            return r;
-                        }
+                        r => return r,
                     }
                 }
-                FfiSafe
+
+                let ret_ty = sig.output();
+                if ret_ty.is_unit() {
+                    return FfiSafe;
+                }
+
+                self.check_type_for_ffi(cache, ret_ty)
             }
 
             ty::Foreign(..) => FfiSafe,
@@ -1320,7 +1318,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
         if let Some(ty) = self
             .cx
             .tcx
-            .normalize_erasing_regions(self.cx.param_env, ty)
+            .try_normalize_erasing_regions(self.cx.param_env, ty)
+            .unwrap_or(ty)
             .visit_with(&mut ProhibitOpaqueTypes)
             .break_value()
         {
@@ -1338,16 +1337,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
         is_static: bool,
         is_return_type: bool,
     ) {
-        // We have to check for opaque types before `normalize_erasing_regions`,
-        // which will replace opaque types with their underlying concrete type.
         if self.check_for_opaque_ty(sp, ty) {
             // We've already emitted an error due to an opaque type.
             return;
         }
 
-        // it is only OK to use this function because extern fns cannot have
-        // any generic types right now:
-        let ty = self.cx.tcx.normalize_erasing_regions(self.cx.param_env, ty);
+        let ty = self.cx.tcx.try_normalize_erasing_regions(self.cx.param_env, ty).unwrap_or(ty);
 
         // C doesn't really support passing arrays by value - the only way to pass an array by value
         // is through a struct. So, first test that the top level isn't an array, and then
@@ -1357,7 +1352,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
         }
 
         // Don't report FFI errors for unit return types. This check exists here, and not in
-        // `check_foreign_fn` (where it would make more sense) so that normalization has definitely
+        // the caller (where it would make more sense) so that normalization has definitely
         // happened.
         if is_return_type && ty.is_unit() {
             return;
@@ -1373,9 +1368,6 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
                     None,
                 );
             }
-            // If `ty` is a `repr(transparent)` newtype, and the non-zero-sized type is a generic
-            // argument, which after substitution, is `()`, then this branch can be hit.
-            FfiResult::FfiUnsafe { ty, .. } if is_return_type && ty.is_unit() => {}
             FfiResult::FfiUnsafe { ty, reason, help } => {
                 self.emit_ffi_unsafe_type_lint(ty, sp, reason, help);
             }
diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs
index f78da284a3a..a3c5ca4cda1 100644
--- a/compiler/rustc_lint/src/unused.rs
+++ b/compiler/rustc_lint/src/unused.rs
@@ -414,7 +414,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
             match path {
                 MustUsePath::Suppressed => {}
                 MustUsePath::Boxed(path) => {
-                    let descr_pre = &format!("{}boxed ", descr_pre);
+                    let descr_pre = &format!("{descr_pre}boxed ");
                     emit_must_use_untranslated(
                         cx,
                         path,
@@ -426,7 +426,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
                     );
                 }
                 MustUsePath::Opaque(path) => {
-                    let descr_pre = &format!("{}implementer{} of ", descr_pre, plural_suffix);
+                    let descr_pre = &format!("{descr_pre}implementer{plural_suffix} of ");
                     emit_must_use_untranslated(
                         cx,
                         path,
@@ -438,7 +438,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
                     );
                 }
                 MustUsePath::TraitObject(path) => {
-                    let descr_post = &format!(" trait object{}{}", plural_suffix, descr_post);
+                    let descr_post = &format!(" trait object{plural_suffix}{descr_post}");
                     emit_must_use_untranslated(
                         cx,
                         path,
@@ -451,7 +451,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
                 }
                 MustUsePath::TupleElement(elems) => {
                     for (index, path) in elems {
-                        let descr_post = &format!(" in tuple element {}", index);
+                        let descr_post = &format!(" in tuple element {index}");
                         emit_must_use_untranslated(
                             cx,
                             path,
@@ -464,7 +464,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
                     }
                 }
                 MustUsePath::Array(path, len) => {
-                    let descr_pre = &format!("{}array{} of ", descr_pre, plural_suffix);
+                    let descr_pre = &format!("{descr_pre}array{plural_suffix} of ");
                     emit_must_use_untranslated(
                         cx,
                         path,
diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs
index a0f2e9aed81..f1765d653b0 100644
--- a/compiler/rustc_lint_defs/src/builtin.rs
+++ b/compiler/rustc_lint_defs/src/builtin.rs
@@ -3400,6 +3400,7 @@ declare_lint_pass! {
         UNFULFILLED_LINT_EXPECTATIONS,
         UNINHABITED_STATIC,
         UNKNOWN_CRATE_TYPES,
+        UNKNOWN_DIAGNOSTIC_ATTRIBUTES,
         UNKNOWN_LINTS,
         UNNAMEABLE_TYPES,
         UNREACHABLE_CODE,
@@ -4052,12 +4053,12 @@ declare_lint! {
     ///
     /// The compiler disables the automatic implementation if an explicit one
     /// exists for given type constructor. The exact rules governing this
-    /// are currently unsound, quite subtle, and will be modified in the future.
-    /// This change will cause the automatic implementation to be disabled in more
+    /// were previously unsound, quite subtle, and have been recently modified.
+    /// This change caused the automatic implementation to be disabled in more
     /// cases, potentially breaking some code.
     pub SUSPICIOUS_AUTO_TRAIT_IMPLS,
     Warn,
-    "the rules governing auto traits will change in the future",
+    "the rules governing auto traits have recently changed resulting in potential breakage",
     @future_incompatible = FutureIncompatibleInfo {
         reason: FutureIncompatibilityReason::FutureReleaseSemanticsChange,
         reference: "issue #93367 <https://github.com/rust-lang/rust/issues/93367>",
@@ -4380,3 +4381,27 @@ declare_lint! {
     "effective visibility of a type is larger than the area in which it can be named",
     @feature_gate = sym::type_privacy_lints;
 }
+
+declare_lint! {
+    /// The `unknown_diagnostic_attributes` lint detects unrecognized diagnostic attributes.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// #![feature(diagnostic_namespace)]
+    /// #[diagnostic::does_not_exist]
+    /// struct Foo;
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// It is usually a mistake to specify a diagnostic attribute that does not exist. Check
+    /// the spelling, and check the diagnostic attribute listing for the correct name. Also
+    /// consider if you are using an old version of the compiler, and the attribute
+    /// is only available in a newer version.
+    pub UNKNOWN_DIAGNOSTIC_ATTRIBUTES,
+    Warn,
+    "unrecognized diagnostic attribute"
+}
diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
index e5fb6b0953f..71df17c9ce7 100644
--- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
+++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
@@ -1366,6 +1366,11 @@ LLVMRustPrepareThinLTOImport(const LLVMRustThinLTOData *Data, LLVMModuleRef M,
     if (WasmCustomSections)
       WasmCustomSections->eraseFromParent();
 
+    // `llvm.ident` named metadata also gets duplicated.
+    auto *llvmIdent = (*MOrErr)->getNamedMetadata("llvm.ident");
+    if (llvmIdent)
+      llvmIdent->eraseFromParent();
+
     return MOrErr;
   };
   bool ClearDSOLocal = clearDSOLocalOnDeclarations(Mod, Target);
diff --git a/compiler/rustc_macros/Cargo.toml b/compiler/rustc_macros/Cargo.toml
index 16c4a850012..17651ce9598 100644
--- a/compiler/rustc_macros/Cargo.toml
+++ b/compiler/rustc_macros/Cargo.toml
@@ -8,7 +8,6 @@ proc-macro = true
 
 [dependencies]
 synstructure = "0.13.0"
-# FIXME(Nilstrieb): Updating this causes changes in the diagnostics output.
-syn = { version = "=2.0.8", features = ["full"] }
+syn = { version = "2.0.9", features = ["full"] }
 proc-macro2 = "1"
 quote = "1"
diff --git a/compiler/rustc_metadata/src/fs.rs b/compiler/rustc_metadata/src/fs.rs
index 238f963ed46..69a77e82f98 100644
--- a/compiler/rustc_metadata/src/fs.rs
+++ b/compiler/rustc_metadata/src/fs.rs
@@ -104,8 +104,8 @@ pub fn encode_and_write_metadata(tcx: TyCtxt<'_>) -> (EncodedMetadata, bool) {
     };
 
     // Load metadata back to memory: codegen may need to include it in object files.
-    let metadata = EncodedMetadata::from_path(metadata_filename.clone(), metadata_tmpdir)
-        .unwrap_or_else(|err| {
+    let metadata =
+        EncodedMetadata::from_path(metadata_filename, metadata_tmpdir).unwrap_or_else(|err| {
             tcx.sess.emit_fatal(FailedCreateEncodedMetadata { err });
         });
 
diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
index 77c33336dff..a8815ee0908 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
@@ -301,7 +301,6 @@ provide! { tcx, def_id, other, cdata,
     is_profiler_runtime => { cdata.root.profiler_runtime }
     required_panic_strategy => { cdata.root.required_panic_strategy }
     panic_in_drop_strategy => { cdata.root.panic_in_drop_strategy }
-    reference_niches_policy => { cdata.root.reference_niches_policy }
     extern_crate => {
         let r = *cdata.extern_crate.lock();
         r.map(|c| &*tcx.arena.alloc(c))
diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs
index 46571e7796d..8f065dd6b58 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -673,7 +673,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
                 stable_crate_id: tcx.def_path_hash(LOCAL_CRATE.as_def_id()).stable_crate_id(),
                 required_panic_strategy: tcx.required_panic_strategy(LOCAL_CRATE),
                 panic_in_drop_strategy: tcx.sess.opts.unstable_opts.panic_in_drop,
-                reference_niches_policy: tcx.reference_niches_policy(LOCAL_CRATE),
                 edition: tcx.sess.edition(),
                 has_global_allocator: tcx.has_global_allocator(LOCAL_CRATE),
                 has_alloc_error_handler: tcx.has_alloc_error_handler(LOCAL_CRATE),
@@ -2245,13 +2244,12 @@ pub fn provide(providers: &mut Providers) {
             tcx.resolutions(())
                 .doc_link_resolutions
                 .get(&def_id)
-                .expect("no resolutions for a doc link")
+                .unwrap_or_else(|| span_bug!(tcx.def_span(def_id), "no resolutions for a doc link"))
         },
         doc_link_traits_in_scope: |tcx, def_id| {
-            tcx.resolutions(())
-                .doc_link_traits_in_scope
-                .get(&def_id)
-                .expect("no traits in scope for a doc link")
+            tcx.resolutions(()).doc_link_traits_in_scope.get(&def_id).unwrap_or_else(|| {
+                span_bug!(tcx.def_span(def_id), "no traits in scope for a doc link")
+            })
         },
         traits: |tcx, LocalCrate| {
             let mut traits = Vec::new();
diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs
index 8bc2e0aa5a9..0bc16fc64ff 100644
--- a/compiler/rustc_metadata/src/rmeta/mod.rs
+++ b/compiler/rustc_metadata/src/rmeta/mod.rs
@@ -32,7 +32,7 @@ use rustc_span::edition::Edition;
 use rustc_span::hygiene::{ExpnIndex, MacroKind};
 use rustc_span::symbol::{Ident, Symbol};
 use rustc_span::{self, ExpnData, ExpnHash, ExpnId, Span};
-use rustc_target::abi::{FieldIdx, ReferenceNichePolicy, VariantIdx};
+use rustc_target::abi::{FieldIdx, VariantIdx};
 use rustc_target::spec::{PanicStrategy, TargetTriple};
 
 use std::marker::PhantomData;
@@ -251,7 +251,6 @@ pub(crate) struct CrateRoot {
     stable_crate_id: StableCrateId,
     required_panic_strategy: Option<PanicStrategy>,
     panic_in_drop_strategy: PanicStrategy,
-    reference_niches_policy: ReferenceNichePolicy,
     edition: Edition,
     has_global_allocator: bool,
     has_alloc_error_handler: bool,
diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml
index 4c238308fe8..bb8e774cea3 100644
--- a/compiler/rustc_middle/Cargo.toml
+++ b/compiler/rustc_middle/Cargo.toml
@@ -13,7 +13,7 @@ gsgdt = "0.1.2"
 field-offset = "0.3.5"
 measureme = "10.0.0"
 polonius-engine = "0.13.0"
-rustc_apfloat = { path = "../rustc_apfloat" }
+rustc_apfloat = "0.2.0"
 rustc_arena = { path = "../rustc_arena" }
 rustc_ast = { path = "../rustc_ast" }
 rustc_attr = { path = "../rustc_attr" }
diff --git a/compiler/rustc_middle/src/macros.rs b/compiler/rustc_middle/src/macros.rs
index cd1c6c330bc..fca16d8e509 100644
--- a/compiler/rustc_middle/src/macros.rs
+++ b/compiler/rustc_middle/src/macros.rs
@@ -43,7 +43,7 @@ macro_rules! span_bug {
 
 #[macro_export]
 macro_rules! CloneLiftImpls {
-    ($($ty:ty,)+) => {
+    ($($ty:ty),+ $(,)?) => {
         $(
             impl<'tcx> $crate::ty::Lift<'tcx> for $ty {
                 type Lifted = Self;
@@ -59,7 +59,7 @@ macro_rules! CloneLiftImpls {
 /// allocated data** (i.e., don't need to be folded).
 #[macro_export]
 macro_rules! TrivialTypeTraversalImpls {
-    ($($ty:ty,)+) => {
+    ($($ty:ty),+ $(,)?) => {
         $(
             impl<'tcx> $crate::ty::fold::TypeFoldable<$crate::ty::TyCtxt<'tcx>> for $ty {
                 fn try_fold_with<F: $crate::ty::fold::FallibleTypeFolder<$crate::ty::TyCtxt<'tcx>>>(
diff --git a/compiler/rustc_middle/src/mir/basic_blocks.rs b/compiler/rustc_middle/src/mir/basic_blocks.rs
index 7722e7b47cf..0ad17e819c7 100644
--- a/compiler/rustc_middle/src/mir/basic_blocks.rs
+++ b/compiler/rustc_middle/src/mir/basic_blocks.rs
@@ -178,9 +178,7 @@ impl<'tcx> graph::WithPredecessors for BasicBlocks<'tcx> {
     }
 }
 
-TrivialTypeTraversalAndLiftImpls! {
-    Cache,
-}
+TrivialTypeTraversalAndLiftImpls! { Cache }
 
 impl<S: Encoder> Encodable<S> for Cache {
     #[inline]
diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs
index b8030d9db13..c1cb2f2e497 100644
--- a/compiler/rustc_middle/src/mir/interpret/allocation.rs
+++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs
@@ -571,7 +571,7 @@ impl<Prov: Provenance, Extra, Bytes: AllocBytes> Allocation<Prov, Extra, Bytes>
         assert!(self.mutability == Mutability::Mut);
 
         // `to_bits_or_ptr_internal` is the right method because we just want to store this data
-        // as-is into memory.
+        // as-is into memory. This also double-checks that `val.size()` matches `range.size`.
         let (bytes, provenance) = match val.to_bits_or_ptr_internal(range.size)? {
             Right(ptr) => {
                 let (provenance, offset) = ptr.into_parts();
diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs
index 1bcef17d73b..6161b16fc46 100644
--- a/compiler/rustc_middle/src/mir/interpret/error.rs
+++ b/compiler/rustc_middle/src/mir/interpret/error.rs
@@ -12,7 +12,8 @@ use rustc_errors::{
 use rustc_macros::HashStable;
 use rustc_session::CtfeBacktrace;
 use rustc_span::def_id::DefId;
-use rustc_target::abi::{call, Align, Size, WrappingRange};
+use rustc_target::abi::{call, Align, Size, VariantIdx, WrappingRange};
+
 use std::borrow::Cow;
 use std::{any::Any, backtrace::Backtrace, fmt};
 
@@ -66,9 +67,7 @@ impl Into<ErrorGuaranteed> for ReportedErrorInfo {
     }
 }
 
-TrivialTypeTraversalAndLiftImpls! {
-    ErrorHandled,
-}
+TrivialTypeTraversalAndLiftImpls! { ErrorHandled }
 
 pub type EvalToAllocationRawResult<'tcx> = Result<ConstAlloc<'tcx>, ErrorHandled>;
 pub type EvalToConstValueResult<'tcx> = Result<ConstValue<'tcx>, ErrorHandled>;
@@ -191,9 +190,8 @@ pub enum InvalidProgramInfo<'tcx> {
     FnAbiAdjustForForeignAbi(call::AdjustForForeignAbiError),
     /// SizeOf of unsized type was requested.
     SizeOfUnsizedType(Ty<'tcx>),
-    /// An unsized local was accessed without having been initialized.
-    /// This is not meaningful as we can't even have backing memory for such locals.
-    UninitUnsizedLocal,
+    /// We are runnning into a nonsense situation due to ConstProp violating our invariants.
+    ConstPropNonsense,
 }
 
 /// Details of why a pointer had to be in-bounds.
@@ -324,7 +322,9 @@ pub enum UndefinedBehaviorInfo<'a> {
     /// Data size is not equal to target size.
     ScalarSizeMismatch(ScalarSizeMismatch),
     /// A discriminant of an uninhabited enum variant is written.
-    UninhabitedEnumVariantWritten,
+    UninhabitedEnumVariantWritten(VariantIdx),
+    /// An uninhabited enum variant is projected.
+    UninhabitedEnumVariantRead(VariantIdx),
     /// Validation error.
     Validation(ValidationErrorInfo<'a>),
     // FIXME(fee1-dead) these should all be actual variants of the enum instead of dynamically
@@ -388,11 +388,13 @@ pub enum ValidationErrorKind<'tcx> {
     MutableRefInConst,
     NullFnPtr,
     NeverVal,
+    NullablePtrOutOfRange { range: WrappingRange, max_value: u128 },
     PtrOutOfRange { range: WrappingRange, max_value: u128 },
     OutOfRange { value: String, range: WrappingRange, max_value: u128 },
     UnsafeCell,
     UninhabitedVal { ty: Ty<'tcx> },
     InvalidEnumTag { value: String },
+    UninhabitedEnumTag,
     UninitEnumTag,
     UninitStr,
     Uninit { expected: ExpectedKind },
diff --git a/compiler/rustc_middle/src/mir/interpret/pointer.rs b/compiler/rustc_middle/src/mir/interpret/pointer.rs
index c8133bcc387..65d04919357 100644
--- a/compiler/rustc_middle/src/mir/interpret/pointer.rs
+++ b/compiler/rustc_middle/src/mir/interpret/pointer.rs
@@ -19,19 +19,33 @@ pub trait PointerArithmetic: HasDataLayout {
 
     #[inline(always)]
     fn max_size_of_val(&self) -> Size {
-        Size::from_bytes(self.data_layout().target_isize_max())
+        Size::from_bytes(self.target_isize_max())
+    }
+
+    #[inline]
+    fn target_usize_max(&self) -> u64 {
+        self.pointer_size().unsigned_int_max().try_into().unwrap()
+    }
+
+    #[inline]
+    fn target_isize_min(&self) -> i64 {
+        self.pointer_size().signed_int_min().try_into().unwrap()
+    }
+
+    #[inline]
+    fn target_isize_max(&self) -> i64 {
+        self.pointer_size().signed_int_max().try_into().unwrap()
     }
 
     #[inline]
     fn target_usize_to_isize(&self, val: u64) -> i64 {
-        let dl = self.data_layout();
         let val = val as i64;
         // Now wrap-around into the machine_isize range.
-        if val > dl.target_isize_max() {
+        if val > self.target_isize_max() {
             // This can only happen if the ptr size is < 64, so we know max_usize_plus_1 fits into
             // i64.
-            debug_assert!(dl.pointer_size.bits() < 64);
-            let max_usize_plus_1 = 1u128 << dl.pointer_size.bits();
+            debug_assert!(self.pointer_size().bits() < 64);
+            let max_usize_plus_1 = 1u128 << self.pointer_size().bits();
             val - i64::try_from(max_usize_plus_1).unwrap()
         } else {
             val
@@ -44,7 +58,7 @@ pub trait PointerArithmetic: HasDataLayout {
     #[inline]
     fn truncate_to_ptr(&self, (val, over): (u64, bool)) -> (u64, bool) {
         let val = u128::from(val);
-        let max_ptr_plus_1 = 1u128 << self.data_layout().pointer_size.bits();
+        let max_ptr_plus_1 = 1u128 << self.pointer_size().bits();
         (u64::try_from(val % max_ptr_plus_1).unwrap(), over || val >= max_ptr_plus_1)
     }
 
@@ -62,11 +76,11 @@ pub trait PointerArithmetic: HasDataLayout {
         let n = i.unsigned_abs();
         if i >= 0 {
             let (val, over) = self.overflowing_offset(val, n);
-            (val, over || i > self.data_layout().target_isize_max())
+            (val, over || i > self.target_isize_max())
         } else {
             let res = val.overflowing_sub(n);
             let (val, over) = self.truncate_to_ptr(res);
-            (val, over || i < self.data_layout().target_isize_min())
+            (val, over || i < self.target_isize_min())
         }
     }
 
diff --git a/compiler/rustc_middle/src/mir/interpret/queries.rs b/compiler/rustc_middle/src/mir/interpret/queries.rs
index c9db0e7c11d..fc659ce18a4 100644
--- a/compiler/rustc_middle/src/mir/interpret/queries.rs
+++ b/compiler/rustc_middle/src/mir/interpret/queries.rs
@@ -139,7 +139,6 @@ impl<'tcx> TyCtxt<'tcx> {
         cid: GlobalId<'tcx>,
         span: Option<Span>,
     ) -> EvalToConstValueResult<'tcx> {
-        let param_env = param_env.with_const();
         // Const-eval shouldn't depend on lifetimes at all, so we can erase them, which should
         // improve caching of queries.
         let inputs = self.erase_regions(param_env.and(cid));
@@ -158,8 +157,6 @@ impl<'tcx> TyCtxt<'tcx> {
         cid: GlobalId<'tcx>,
         span: Option<Span>,
     ) -> EvalToValTreeResult<'tcx> {
-        let param_env = param_env.with_const();
-        debug!(?param_env);
         // Const-eval shouldn't depend on lifetimes at all, so we can erase them, which should
         // improve caching of queries.
         let inputs = self.erase_regions(param_env.and(cid));
@@ -204,7 +201,6 @@ impl<'tcx> TyCtxtAt<'tcx> {
         gid: GlobalId<'tcx>,
         param_env: ty::ParamEnv<'tcx>,
     ) -> Result<mir::ConstAllocation<'tcx>, ErrorHandled> {
-        let param_env = param_env.with_const();
         trace!("eval_to_allocation: Need to compute {:?}", gid);
         let raw_const = self.eval_to_allocation_raw(param_env.and(gid))?;
         Ok(self.global_alloc(raw_const.alloc_id).unwrap_memory())
@@ -224,8 +220,7 @@ impl<'tcx> TyCtxtEnsure<'tcx> {
         let args = GenericArgs::identity_for_item(self.tcx, def_id);
         let instance = ty::Instance::new(def_id, args);
         let cid = GlobalId { instance, promoted: None };
-        let param_env =
-            self.tcx.param_env(def_id).with_reveal_all_normalized(self.tcx).with_const();
+        let param_env = self.tcx.param_env(def_id).with_reveal_all_normalized(self.tcx);
         // Const-eval shouldn't depend on lifetimes at all, so we can erase them, which should
         // improve caching of queries.
         let inputs = self.tcx.erase_regions(param_env.and(cid));
@@ -238,7 +233,7 @@ impl<'tcx> TyCtxtEnsure<'tcx> {
         assert!(self.tcx.is_static(def_id));
         let instance = ty::Instance::mono(self.tcx, def_id);
         let gid = GlobalId { instance, promoted: None };
-        let param_env = ty::ParamEnv::reveal_all().with_const();
+        let param_env = ty::ParamEnv::reveal_all();
         trace!("eval_to_allocation: Need to compute {:?}", gid);
         self.eval_to_allocation_raw(param_env.and(gid))
     }
diff --git a/compiler/rustc_middle/src/mir/interpret/value.rs b/compiler/rustc_middle/src/mir/interpret/value.rs
index 0416411dfe1..47421d0f037 100644
--- a/compiler/rustc_middle/src/mir/interpret/value.rs
+++ b/compiler/rustc_middle/src/mir/interpret/value.rs
@@ -320,6 +320,14 @@ impl<Prov> Scalar<Prov> {
             }
         })
     }
+
+    #[inline]
+    pub fn size(self) -> Size {
+        match self {
+            Scalar::Int(int) => int.size(),
+            Scalar::Ptr(_ptr, sz) => Size::from_bytes(sz),
+        }
+    }
 }
 
 impl<'tcx, Prov: Provenance> Scalar<Prov> {
diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs
index 97f53a59fd6..3a958548515 100644
--- a/compiler/rustc_middle/src/mir/mod.rs
+++ b/compiler/rustc_middle/src/mir/mod.rs
@@ -706,9 +706,7 @@ pub enum BindingForm<'tcx> {
     RefForGuard,
 }
 
-TrivialTypeTraversalAndLiftImpls! {
-    BindingForm<'tcx>,
-}
+TrivialTypeTraversalAndLiftImpls! { BindingForm<'tcx> }
 
 mod binding_form_impl {
     use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs
index 9bf02267005..2c481745d98 100644
--- a/compiler/rustc_middle/src/query/erase.rs
+++ b/compiler/rustc_middle/src/query/erase.rs
@@ -111,11 +111,6 @@ impl EraseType
     >()];
 }
 
-impl EraseType for Result<ty::layout::TyAndNaiveLayout<'_>, &ty::layout::LayoutError<'_>> {
-    type Result =
-        [u8; size_of::<Result<ty::layout::TyAndNaiveLayout<'_>, &ty::layout::LayoutError<'_>>>()];
-}
-
 impl EraseType for Result<ty::Const<'_>, mir::interpret::LitToConstError> {
     type Result = [u8; size_of::<Result<ty::Const<'static>, mir::interpret::LitToConstError>>()];
 }
@@ -296,7 +291,6 @@ trivial! {
     rustc_span::Symbol,
     rustc_span::symbol::Ident,
     rustc_target::spec::PanicStrategy,
-    rustc_target::abi::ReferenceNichePolicy,
     rustc_type_ir::Variance,
     u32,
     usize,
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index b5b00b7b640..fb796587352 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -1394,18 +1394,6 @@ rustc_queries! {
         desc { "computing layout of `{}`", key.value }
     }
 
-    /// Computes the naive layout approximation of a type. Note that this implicitly
-    /// executes in "reveal all" mode, and will normalize the input type.
-    ///
-    /// Unlike `layout_of`, this doesn't look past references (beyond the `Pointee::Metadata`
-    /// projection), and as such can be called on generic types like `Option<&T>`.
-    query naive_layout_of(
-        key: ty::ParamEnvAnd<'tcx, Ty<'tcx>>
-    ) -> Result<ty::layout::TyAndNaiveLayout<'tcx>, &'tcx ty::layout::LayoutError<'tcx>> {
-        depth_limit
-        desc { "computing layout (naive) of `{}`", key.value }
-    }
-
     /// Compute a `FnAbi` suitable for indirect calls, i.e. to `fn` pointers.
     ///
     /// NB: this doesn't handle virtual calls - those should use `fn_abi_of_instance`
@@ -1481,11 +1469,6 @@ rustc_queries! {
         desc { "getting a crate's configured panic-in-drop strategy" }
         separate_provide_extern
     }
-    query reference_niches_policy(_: CrateNum) -> abi::ReferenceNichePolicy {
-        fatal_cycle
-        desc { "getting a crate's policy for size and alignment niches of references" }
-        separate_provide_extern
-    }
     query is_no_builtins(_: CrateNum) -> bool {
         fatal_cycle
         desc { "getting whether a crate has `#![no_builtins]`" }
@@ -2081,9 +2064,9 @@ rustc_queries! {
         }
     }
 
-    query is_impossible_method(key: (DefId, DefId)) -> bool {
+    query is_impossible_associated_item(key: (DefId, DefId)) -> bool {
         desc { |tcx|
-            "checking if `{}` is impossible to call within `{}`",
+            "checking if `{}` is impossible to reference within `{}`",
             tcx.def_path_str(key.1),
             tcx.def_path_str(key.0),
         }
diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs
index b7ffed57a0b..a2b33bb2737 100644
--- a/compiler/rustc_middle/src/traits/mod.rs
+++ b/compiler/rustc_middle/src/traits/mod.rs
@@ -649,43 +649,31 @@ pub enum ImplSource<'tcx, N> {
     /// for some type parameter. The `Vec<N>` represents the
     /// obligations incurred from normalizing the where-clause (if
     /// any).
-    Param(Vec<N>, ty::BoundConstness),
+    Param(ty::BoundConstness, Vec<N>),
 
-    /// Virtual calls through an object.
-    Object(ImplSourceObjectData<N>),
-
-    /// Successful resolution for a builtin trait.
-    Builtin(Vec<N>),
-
-    /// ImplSource for trait upcasting coercion
-    TraitUpcasting(ImplSourceTraitUpcastingData<N>),
+    /// Successful resolution for a builtin impl.
+    Builtin(BuiltinImplSource, Vec<N>),
 }
 
 impl<'tcx, N> ImplSource<'tcx, N> {
     pub fn nested_obligations(self) -> Vec<N> {
         match self {
             ImplSource::UserDefined(i) => i.nested,
-            ImplSource::Param(n, _) | ImplSource::Builtin(n) => n,
-            ImplSource::Object(d) => d.nested,
-            ImplSource::TraitUpcasting(d) => d.nested,
+            ImplSource::Param(_, n) | ImplSource::Builtin(_, n) => n,
         }
     }
 
     pub fn borrow_nested_obligations(&self) -> &[N] {
         match self {
             ImplSource::UserDefined(i) => &i.nested,
-            ImplSource::Param(n, _) | ImplSource::Builtin(n) => &n,
-            ImplSource::Object(d) => &d.nested,
-            ImplSource::TraitUpcasting(d) => &d.nested,
+            ImplSource::Param(_, n) | ImplSource::Builtin(_, n) => &n,
         }
     }
 
     pub fn borrow_nested_obligations_mut(&mut self) -> &mut [N] {
         match self {
             ImplSource::UserDefined(i) => &mut i.nested,
-            ImplSource::Param(n, _) | ImplSource::Builtin(n) => n,
-            ImplSource::Object(d) => &mut d.nested,
-            ImplSource::TraitUpcasting(d) => &mut d.nested,
+            ImplSource::Param(_, n) | ImplSource::Builtin(_, n) => n,
         }
     }
 
@@ -699,17 +687,9 @@ impl<'tcx, N> ImplSource<'tcx, N> {
                 args: i.args,
                 nested: i.nested.into_iter().map(f).collect(),
             }),
-            ImplSource::Param(n, ct) => ImplSource::Param(n.into_iter().map(f).collect(), ct),
-            ImplSource::Builtin(n) => ImplSource::Builtin(n.into_iter().map(f).collect()),
-            ImplSource::Object(o) => ImplSource::Object(ImplSourceObjectData {
-                vtable_base: o.vtable_base,
-                nested: o.nested.into_iter().map(f).collect(),
-            }),
-            ImplSource::TraitUpcasting(d) => {
-                ImplSource::TraitUpcasting(ImplSourceTraitUpcastingData {
-                    vtable_vptr_slot: d.vtable_vptr_slot,
-                    nested: d.nested.into_iter().map(f).collect(),
-                })
+            ImplSource::Param(ct, n) => ImplSource::Param(ct, n.into_iter().map(f).collect()),
+            ImplSource::Builtin(source, n) => {
+                ImplSource::Builtin(source, n.into_iter().map(f).collect())
             }
         }
     }
@@ -733,29 +713,31 @@ pub struct ImplSourceUserDefinedData<'tcx, N> {
     pub nested: Vec<N>,
 }
 
-#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, Lift)]
-#[derive(TypeFoldable, TypeVisitable)]
-pub struct ImplSourceTraitUpcastingData<N> {
+#[derive(Copy, Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, Debug)]
+pub enum BuiltinImplSource {
+    /// Some builtin impl we don't need to differentiate. This should be used
+    /// unless more specific information is necessary.
+    Misc,
+    /// A builtin impl for trait objects.
+    ///
+    /// The vtable is formed by concatenating together the method lists of
+    /// the base object trait and all supertraits, pointers to supertrait vtable will
+    /// be provided when necessary; this is the start of `upcast_trait_ref`'s methods
+    /// in that vtable.
+    Object { vtable_base: usize },
     /// The vtable is formed by concatenating together the method lists of
     /// the base object trait and all supertraits, pointers to supertrait vtable will
     /// be provided when necessary; this is the position of `upcast_trait_ref`'s vtable
     /// within that vtable.
-    pub vtable_vptr_slot: Option<usize>,
-
-    pub nested: Vec<N>,
+    TraitUpcasting { vtable_vptr_slot: Option<usize> },
+    /// Unsizing a tuple like `(A, B, ..., X)` to `(A, B, ..., Y)` if `X` unsizes to `Y`.
+    ///
+    /// This needs to be a separate variant as it is still unstable and we need to emit
+    /// a feature error when using it on stable.
+    TupleUnsizing,
 }
 
-#[derive(PartialEq, Eq, Clone, TyEncodable, TyDecodable, HashStable, Lift)]
-#[derive(TypeFoldable, TypeVisitable)]
-pub struct ImplSourceObjectData<N> {
-    /// The vtable is formed by concatenating together the method lists of
-    /// the base object trait and all supertraits, pointers to supertrait vtable will
-    /// be provided when necessary; this is the start of `upcast_trait_ref`'s methods
-    /// in that vtable.
-    pub vtable_base: usize,
-
-    pub nested: Vec<N>,
-}
+TrivialTypeTraversalAndLiftImpls! { BuiltinImplSource }
 
 #[derive(Clone, Debug, PartialEq, Eq, Hash, HashStable, PartialOrd, Ord)]
 pub enum ObjectSafetyViolation {
diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs
index f2dda003b99..a90d58f5fc1 100644
--- a/compiler/rustc_middle/src/traits/select.rs
+++ b/compiler/rustc_middle/src/traits/select.rs
@@ -304,9 +304,7 @@ impl From<ErrorGuaranteed> for OverflowError {
     }
 }
 
-TrivialTypeTraversalAndLiftImpls! {
-    OverflowError,
-}
+TrivialTypeTraversalAndLiftImpls! { OverflowError }
 
 impl<'tcx> From<OverflowError> for SelectionError<'tcx> {
     fn from(overflow_error: OverflowError) -> SelectionError<'tcx> {
diff --git a/compiler/rustc_middle/src/traits/solve/inspect.rs b/compiler/rustc_middle/src/traits/solve/inspect.rs
index 8698cf86022..e793f481995 100644
--- a/compiler/rustc_middle/src/traits/solve/inspect.rs
+++ b/compiler/rustc_middle/src/traits/solve/inspect.rs
@@ -73,8 +73,12 @@ pub struct GoalCandidate<'tcx> {
 pub enum CandidateKind<'tcx> {
     /// Probe entered when normalizing the self ty during candidate assembly
     NormalizedSelfTyAssembly,
+    DynUpcastingAssembly,
     /// A normal candidate for proving a goal
-    Candidate { name: String, result: QueryResult<'tcx> },
+    Candidate {
+        name: String,
+        result: QueryResult<'tcx>,
+    },
 }
 impl Debug for GoalCandidate<'_> {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
diff --git a/compiler/rustc_middle/src/traits/solve/inspect/format.rs b/compiler/rustc_middle/src/traits/solve/inspect/format.rs
index f19f1189e44..39f84279259 100644
--- a/compiler/rustc_middle/src/traits/solve/inspect/format.rs
+++ b/compiler/rustc_middle/src/traits/solve/inspect/format.rs
@@ -15,11 +15,11 @@ struct Indentor<'a, 'b> {
 
 impl Write for Indentor<'_, '_> {
     fn write_str(&mut self, s: &str) -> std::fmt::Result {
-        for line in s.split_inclusive("\n") {
+        for line in s.split_inclusive('\n') {
             if self.on_newline {
                 self.f.write_str("    ")?;
             }
-            self.on_newline = line.ends_with("\n");
+            self.on_newline = line.ends_with('\n');
             self.f.write_str(line)?;
         }
 
@@ -100,6 +100,9 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> {
             CandidateKind::NormalizedSelfTyAssembly => {
                 writeln!(self.f, "NORMALIZING SELF TY FOR ASSEMBLY:")
             }
+            CandidateKind::DynUpcastingAssembly => {
+                writeln!(self.f, "ASSEMBLING CANDIDATES FOR DYN UPCASTING:")
+            }
             CandidateKind::Candidate { name, result } => {
                 writeln!(self.f, "CANDIDATE {}: {:?}", name, result)
             }
diff --git a/compiler/rustc_middle/src/traits/structural_impls.rs b/compiler/rustc_middle/src/traits/structural_impls.rs
index e2cd118500b..530d1ec5d35 100644
--- a/compiler/rustc_middle/src/traits/structural_impls.rs
+++ b/compiler/rustc_middle/src/traits/structural_impls.rs
@@ -6,18 +6,16 @@ use std::fmt;
 
 impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSource<'tcx, N> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match *self {
-            super::ImplSource::UserDefined(ref v) => write!(f, "{:?}", v),
+        match self {
+            super::ImplSource::UserDefined(v) => write!(f, "{:?}", v),
 
-            super::ImplSource::Builtin(ref d) => write!(f, "{:?}", d),
-
-            super::ImplSource::Object(ref d) => write!(f, "{:?}", d),
+            super::ImplSource::Builtin(source, d) => {
+                write!(f, "Builtin({source:?}, {d:?})")
+            }
 
-            super::ImplSource::Param(ref n, ct) => {
+            super::ImplSource::Param(ct, n) => {
                 write!(f, "ImplSourceParamData({:?}, {:?})", n, ct)
             }
-
-            super::ImplSource::TraitUpcasting(ref d) => write!(f, "{:?}", d),
         }
     }
 }
@@ -31,23 +29,3 @@ impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSourceUserDefinedData<'tcx,
         )
     }
 }
-
-impl<N: fmt::Debug> fmt::Debug for traits::ImplSourceTraitUpcastingData<N> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(
-            f,
-            "ImplSourceTraitUpcastingData(vtable_vptr_slot={:?}, nested={:?})",
-            self.vtable_vptr_slot, self.nested
-        )
-    }
-}
-
-impl<N: fmt::Debug> fmt::Debug for traits::ImplSourceObjectData<N> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(
-            f,
-            "ImplSourceObjectData(vtable_base={}, nested={:?})",
-            self.vtable_base, self.nested
-        )
-    }
-}
diff --git a/compiler/rustc_middle/src/ty/abstract_const.rs b/compiler/rustc_middle/src/ty/abstract_const.rs
index 0364a620810..cdd8351499b 100644
--- a/compiler/rustc_middle/src/ty/abstract_const.rs
+++ b/compiler/rustc_middle/src/ty/abstract_const.rs
@@ -27,9 +27,7 @@ impl From<ErrorGuaranteed> for NotConstEvaluatable {
     }
 }
 
-TrivialTypeTraversalAndLiftImpls! {
-    NotConstEvaluatable,
-}
+TrivialTypeTraversalAndLiftImpls! { NotConstEvaluatable }
 
 pub type BoundAbstractConst<'tcx> = Result<Option<EarlyBinder<ty::Const<'tcx>>>, ErrorGuaranteed>;
 
diff --git a/compiler/rustc_middle/src/ty/binding.rs b/compiler/rustc_middle/src/ty/binding.rs
index a5b05a4f9b5..2fec8ac9095 100644
--- a/compiler/rustc_middle/src/ty/binding.rs
+++ b/compiler/rustc_middle/src/ty/binding.rs
@@ -6,7 +6,7 @@ pub enum BindingMode {
     BindByValue(Mutability),
 }
 
-TrivialTypeTraversalAndLiftImpls! { BindingMode, }
+TrivialTypeTraversalAndLiftImpls! { BindingMode }
 
 impl BindingMode {
     pub fn convert(BindingAnnotation(by_ref, mutbl): BindingAnnotation) -> BindingMode {
diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs
index b4f4f9bef8e..7c05deae90a 100644
--- a/compiler/rustc_middle/src/ty/codec.rs
+++ b/compiler/rustc_middle/src/ty/codec.rs
@@ -168,7 +168,6 @@ impl<'tcx, E: TyEncoder<I = TyCtxt<'tcx>>> Encodable<E> for ty::ParamEnv<'tcx> {
     fn encode(&self, e: &mut E) {
         self.caller_bounds().encode(e);
         self.reveal().encode(e);
-        self.constness().encode(e);
     }
 }
 
@@ -306,8 +305,7 @@ impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> Decodable<D> for ty::ParamEnv<'tcx> {
     fn decode(d: &mut D) -> Self {
         let caller_bounds = Decodable::decode(d);
         let reveal = Decodable::decode(d);
-        let constness = Decodable::decode(d);
-        ty::ParamEnv::new(caller_bounds, reveal, constness)
+        ty::ParamEnv::new(caller_bounds, reveal)
     }
 }
 
diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs
index c794c3faded..3fdbaf5e947 100644
--- a/compiler/rustc_middle/src/ty/error.rs
+++ b/compiler/rustc_middle/src/ty/error.rs
@@ -339,12 +339,17 @@ impl<'tcx> TyCtxt<'tcx> {
     }
 
     pub fn short_ty_string(self, ty: Ty<'tcx>) -> (String, Option<PathBuf>) {
-        let width = self.sess.diagnostic_width();
-        let length_limit = width.saturating_sub(30);
         let regular = FmtPrinter::new(self, hir::def::Namespace::TypeNS)
             .pretty_print_type(ty)
             .expect("could not write to `String`")
             .into_buffer();
+
+        if !self.sess.opts.unstable_opts.write_long_types_to_disk {
+            return (regular, None);
+        }
+
+        let width = self.sess.diagnostic_width();
+        let length_limit = width.saturating_sub(30);
         if regular.len() <= width {
             return (regular, None);
         }
diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs
index 26137e86fa0..81e7dc3728a 100644
--- a/compiler/rustc_middle/src/ty/layout.rs
+++ b/compiler/rustc_middle/src/ty/layout.rs
@@ -313,16 +313,7 @@ impl<'tcx> SizeSkeleton<'tcx> {
     ) -> Result<SizeSkeleton<'tcx>, &'tcx LayoutError<'tcx>> {
         debug_assert!(!ty.has_non_region_infer());
 
-        // First, try computing an exact naive layout (this covers simple types with generic
-        // references, where a full static layout would fail).
-        if let Ok(layout) = tcx.naive_layout_of(param_env.and(ty)) {
-            if layout.exact {
-                return Ok(SizeSkeleton::Known(layout.size));
-            }
-        }
-
-        // Second, try computing a full static layout (this covers cases when the naive layout
-        // wasn't smart enough, but cannot deal with generic references).
+        // First try computing a static layout.
         let err = match tcx.layout_of(param_env.and(ty)) {
             Ok(layout) => {
                 return Ok(SizeSkeleton::Known(layout.size));
@@ -336,7 +327,6 @@ impl<'tcx> SizeSkeleton<'tcx> {
             ) => return Err(e),
         };
 
-        // Third, fall back to ad-hoc cases.
         match *ty.kind() {
             ty::Ref(_, pointee, _) | ty::RawPtr(ty::TypeAndMut { ty: pointee, .. }) => {
                 let non_zero = !ty.is_unsafe_ptr();
@@ -631,219 +621,6 @@ impl<T, E> MaybeResult<T> for Result<T, E> {
 
 pub type TyAndLayout<'tcx> = rustc_target::abi::TyAndLayout<'tcx, Ty<'tcx>>;
 
-#[derive(Copy, Clone, Debug, HashStable)]
-pub struct TyAndNaiveLayout<'tcx> {
-    pub ty: Ty<'tcx>,
-    pub layout: NaiveLayout,
-}
-
-impl std::ops::Deref for TyAndNaiveLayout<'_> {
-    type Target = NaiveLayout;
-    fn deref(&self) -> &Self::Target {
-        &self.layout
-    }
-}
-
-impl std::ops::DerefMut for TyAndNaiveLayout<'_> {
-    fn deref_mut(&mut self) -> &mut Self::Target {
-        &mut self.layout
-    }
-}
-
-/// Extremely simplified approximation of a type's layout returned by the
-/// `naive_layout_of` query.
-#[derive(Copy, Clone, Debug, HashStable)]
-pub struct NaiveLayout {
-    pub abi: NaiveAbi,
-    /// Niche information, required for tracking non-null enum optimizations.
-    pub niches: NaiveNiches,
-    /// An underestimate of the layout's size.
-    pub size: Size,
-    /// An underestimate of the layout's required alignment.
-    pub align: Align,
-    /// If `true`, `size` and `align` must be exact values.
-    pub exact: bool,
-}
-
-#[derive(Copy, Clone, Debug, Eq, PartialEq, HashStable)]
-pub enum NaiveNiches {
-    None,
-    Some,
-    Maybe,
-}
-
-#[derive(Copy, Clone, Debug, Eq, PartialEq, HashStable)]
-pub enum NaiveAbi {
-    /// A scalar layout, always implies `exact` and a non-zero `size`.
-    Scalar(Primitive),
-    /// An uninhabited layout. (needed to properly track `Scalar` and niches)
-    Uninhabited,
-    /// An unsized aggregate. (needed to properly track `Scalar` and niches)
-    Unsized,
-    /// Any other sized layout.
-    Sized,
-}
-
-impl NaiveAbi {
-    #[inline]
-    pub fn as_aggregate(self) -> Self {
-        match self {
-            NaiveAbi::Scalar(_) => NaiveAbi::Sized,
-            _ => self,
-        }
-    }
-}
-
-impl NaiveLayout {
-    /// The layout of an empty aggregate, e.g. `()`.
-    pub const EMPTY: Self = Self {
-        size: Size::ZERO,
-        align: Align::ONE,
-        exact: true,
-        abi: NaiveAbi::Sized,
-        niches: NaiveNiches::None,
-    };
-
-    /// Returns whether `self` is a valid approximation of the given full `layout`.
-    ///
-    /// This should always return `true` when both layouts are computed from the same type.
-    pub fn is_refined_by(&self, layout: Layout<'_>) -> bool {
-        if self.size > layout.size() || self.align > layout.align().abi {
-            return false;
-        }
-
-        if let NaiveAbi::Scalar(prim) = self.abi {
-            if !self.exact
-                || self.size == Size::ZERO
-                || !matches!(layout.abi(), Abi::Scalar(s) if s.primitive() == prim)
-            {
-                return false;
-            }
-        }
-
-        match (self.niches, layout.largest_niche()) {
-            (NaiveNiches::None, Some(_)) => return false,
-            (NaiveNiches::Some, None) => return false,
-            _ => (),
-        }
-
-        !self.exact || (self.size, self.align) == (layout.size(), layout.align().abi)
-    }
-
-    /// Returns if this layout is known to be pointer-like (`None` if uncertain)
-    ///
-    /// See the corresponding `Layout::is_pointer_like` method.
-    pub fn is_pointer_like(&self, dl: &TargetDataLayout) -> Option<bool> {
-        match self.abi {
-            NaiveAbi::Scalar(_) => {
-                assert!(self.exact);
-                Some(self.size == dl.pointer_size && self.align == dl.pointer_align.abi)
-            }
-            NaiveAbi::Uninhabited | NaiveAbi::Unsized => Some(false),
-            NaiveAbi::Sized if self.exact => Some(false),
-            NaiveAbi::Sized => None,
-        }
-    }
-
-    /// Artificially lowers the alignment of this layout.
-    #[must_use]
-    #[inline]
-    pub fn packed(mut self, align: Align) -> Self {
-        if self.align > align {
-            self.align = align;
-            self.abi = self.abi.as_aggregate();
-        }
-        self
-    }
-
-    /// Artificially raises the alignment of this layout.
-    #[must_use]
-    #[inline]
-    pub fn align_to(mut self, align: Align) -> Self {
-        if align > self.align {
-            self.align = align;
-            self.abi = self.abi.as_aggregate();
-        }
-        self
-    }
-
-    /// Artificially makes this layout inexact.
-    #[must_use]
-    #[inline]
-    pub fn inexact(mut self) -> Self {
-        self.abi = self.abi.as_aggregate();
-        self.exact = false;
-        self
-    }
-
-    /// Pads this layout so that its size is a multiple of `align`.
-    #[must_use]
-    #[inline]
-    pub fn pad_to_align(mut self, align: Align) -> Self {
-        let new_size = self.size.align_to(align);
-        if new_size > self.size {
-            self.abi = self.abi.as_aggregate();
-            self.size = new_size;
-        }
-        self
-    }
-
-    /// Returns the layout of `self` immediately followed by `other`, without any
-    /// padding between them, as in a packed `struct` or tuple.
-    #[must_use]
-    #[inline]
-    pub fn concat(&self, other: &Self, dl: &TargetDataLayout) -> Option<Self> {
-        use NaiveAbi::*;
-
-        let size = self.size.checked_add(other.size, dl)?;
-        let align = cmp::max(self.align, other.align);
-        let exact = self.exact && other.exact;
-        let abi = match (self.abi, other.abi) {
-            // The uninhabited and unsized ABIs override everything.
-            (Uninhabited, _) | (_, Uninhabited) => Uninhabited,
-            (Unsized, _) | (_, Unsized) => Unsized,
-            // A scalar struct must have a single non ZST-field.
-            (_, s @ Scalar(_)) if exact && self.size == Size::ZERO => s,
-            (s @ Scalar(_), _) if exact && other.size == Size::ZERO => s,
-            // Default case.
-            (_, _) => Sized,
-        };
-        let niches = match (self.niches, other.niches) {
-            (NaiveNiches::Some, _) | (_, NaiveNiches::Some) => NaiveNiches::Some,
-            (NaiveNiches::None, NaiveNiches::None) => NaiveNiches::None,
-            (_, _) => NaiveNiches::Maybe,
-        };
-        Some(Self { abi, size, align, exact, niches })
-    }
-
-    /// Returns the layout of `self` superposed with `other`, as in an `enum`
-    /// or an `union`.
-    ///
-    /// Note: This always ignore niche information from `other`.
-    #[must_use]
-    #[inline]
-    pub fn union(&self, other: &Self) -> Self {
-        use NaiveAbi::*;
-
-        let size = cmp::max(self.size, other.size);
-        let align = cmp::max(self.align, other.align);
-        let exact = self.exact && other.exact;
-        let abi = match (self.abi, other.abi) {
-            // The unsized ABI overrides everything.
-            (Unsized, _) | (_, Unsized) => Unsized,
-            // A scalar union must have a single non ZST-field...
-            (_, s @ Scalar(_)) if exact && self.size == Size::ZERO => s,
-            (s @ Scalar(_), _) if exact && other.size == Size::ZERO => s,
-            // ...or identical scalar fields.
-            (Scalar(s1), Scalar(s2)) if s1 == s2 => Scalar(s1),
-            // Default cases.
-            (Uninhabited, Uninhabited) => Uninhabited,
-            (_, _) => Sized,
-        };
-        Self { abi, size, align, exact, niches: self.niches }
-    }
-}
-
 /// Trait for contexts that want to be able to compute layouts of types.
 /// This automatically gives access to `LayoutOf`, through a blanket `impl`.
 pub trait LayoutOfHelpers<'tcx>: HasDataLayout + HasTyCtxt<'tcx> + HasParamEnv<'tcx> {
@@ -896,19 +673,6 @@ pub trait LayoutOf<'tcx>: LayoutOfHelpers<'tcx> {
                 .map_err(|err| self.handle_layout_err(*err, span, ty)),
         )
     }
-
-    /// Computes the naive layout estimate of a type. Note that this implicitly
-    /// executes in "reveal all" mode, and will normalize the input type.
-    ///
-    /// Unlike `layout_of`, this doesn't look past references (beyond the `Pointee::Metadata`
-    /// projection), and as such can be called on generic types like `Option<&T>`.
-    #[inline]
-    fn naive_layout_of(
-        &self,
-        ty: Ty<'tcx>,
-    ) -> Result<TyAndNaiveLayout<'tcx>, &'tcx LayoutError<'tcx>> {
-        self.tcx().naive_layout_of(self.param_env().and(ty))
-    }
 }
 
 impl<'tcx, C: LayoutOfHelpers<'tcx>> LayoutOf<'tcx> for C {}
@@ -977,9 +741,9 @@ where
 
                 let fields = match this.ty.kind() {
                     ty::Adt(def, _) if def.variants().is_empty() =>
-                        bug!("for_variant called on zero-variant enum"),
+                        bug!("for_variant called on zero-variant enum {}", this.ty),
                     ty::Adt(def, _) => def.variant(variant_index).fields.len(),
-                    _ => bug!(),
+                    _ => bug!("`ty_and_layout_for_variant` on unexpected type {}", this.ty),
                 };
                 tcx.mk_layout(LayoutS {
                     variants: Variants::Single { index: variant_index },
@@ -1205,9 +969,6 @@ where
         this: TyAndLayout<'tcx>,
         cx: &C,
         offset: Size,
-        // If true, assume that pointers are either null or valid (according to their type),
-        // enabling extra optimizations.
-        mut assume_valid_ptr: bool,
     ) -> Option<PointeeInfo> {
         let tcx = cx.tcx();
         let param_env = cx.param_env();
@@ -1230,19 +991,19 @@ where
                 // Freeze/Unpin queries, and can save time in the codegen backend (noalias
                 // attributes in LLVM have compile-time cost even in unoptimized builds).
                 let optimize = tcx.sess.opts.optimize != OptLevel::No;
-                let safe = match (assume_valid_ptr, mt) {
-                    (true, hir::Mutability::Not) => Some(PointerKind::SharedRef {
+                let kind = match mt {
+                    hir::Mutability::Not => PointerKind::SharedRef {
                         frozen: optimize && ty.is_freeze(tcx, cx.param_env()),
-                    }),
-                    (true, hir::Mutability::Mut) => Some(PointerKind::MutableRef {
+                    },
+                    hir::Mutability::Mut => PointerKind::MutableRef {
                         unpin: optimize && ty.is_unpin(tcx, cx.param_env()),
-                    }),
-                    (false, _) => None,
+                    },
                 };
+
                 tcx.layout_of(param_env.and(ty)).ok().map(|layout| PointeeInfo {
                     size: layout.size,
                     align: layout.align.abi,
-                    safe,
+                    safe: Some(kind),
                 })
             }
 
@@ -1251,21 +1012,19 @@ where
                     // Within the discriminant field, only the niche itself is
                     // always initialized, so we only check for a pointer at its
                     // offset.
+                    //
+                    // If the niche is a pointer, it's either valid (according
+                    // to its type), or null (which the niche field's scalar
+                    // validity range encodes). This allows using
+                    // `dereferenceable_or_null` for e.g., `Option<&T>`, and
+                    // this will continue to work as long as we don't start
+                    // using more niches than just null (e.g., the first page of
+                    // the address space, or unaligned pointers).
                     Variants::Multiple {
-                        tag_encoding:
-                            TagEncoding::Niche {
-                                untagged_variant,
-                                niche_variants: ref variants,
-                                niche_start,
-                            },
+                        tag_encoding: TagEncoding::Niche { untagged_variant, .. },
                         tag_field,
                         ..
                     } if this.fields.offset(tag_field) == offset => {
-                        // We can only continue assuming pointer validity if the only possible
-                        // discriminant value is null. The null special-case is permitted by LLVM's
-                        // `dereferenceable_or_null`, and allow types like `Option<&T>` to benefit
-                        // from optimizations.
-                        assume_valid_ptr &= niche_start == 0 && variants.start() == variants.end();
                         Some(this.for_variant(cx, untagged_variant))
                     }
                     _ => Some(this),
@@ -1291,12 +1050,9 @@ where
                             result = field.to_result().ok().and_then(|field| {
                                 if ptr_end <= field_start + field.size {
                                     // We found the right field, look inside it.
-                                    Self::ty_and_layout_pointee_info_at(
-                                        field,
-                                        cx,
-                                        offset - field_start,
-                                        assume_valid_ptr,
-                                    )
+                                    let field_info =
+                                        field.pointee_info_at(cx, offset - field_start);
+                                    field_info
                                 } else {
                                     None
                                 }
@@ -1311,7 +1067,7 @@ where
                 // FIXME(eddyb) This should be for `ptr::Unique<T>`, not `Box<T>`.
                 if let Some(ref mut pointee) = result {
                     if let ty::Adt(def, _) = this.ty.kind() {
-                        if assume_valid_ptr && def.is_box() && offset.bytes() == 0 {
+                        if def.is_box() && offset.bytes() == 0 {
                             let optimize = tcx.sess.opts.optimize != OptLevel::No;
                             pointee.safe = Some(PointerKind::Box {
                                 unpin: optimize && this.ty.boxed_ty().is_unpin(tcx, cx.param_env()),
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index 0411890ab51..f9c0fbb23c3 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -867,20 +867,6 @@ pub struct TraitPredicate<'tcx> {
 pub type PolyTraitPredicate<'tcx> = ty::Binder<'tcx, TraitPredicate<'tcx>>;
 
 impl<'tcx> TraitPredicate<'tcx> {
-    pub fn remap_constness(&mut self, param_env: &mut ParamEnv<'tcx>) {
-        *param_env = param_env.with_constness(self.constness.and(param_env.constness()))
-    }
-
-    /// Remap the constness of this predicate before emitting it for diagnostics.
-    pub fn remap_constness_diag(&mut self, param_env: ParamEnv<'tcx>) {
-        // this is different to `remap_constness` that callees want to print this predicate
-        // in case of selection errors. `T: ~const Drop` bounds cannot end up here when the
-        // param_env is not const because it is always satisfied in non-const contexts.
-        if let hir::Constness::NotConst = param_env.constness() {
-            self.constness = ty::BoundConstness::NotConst;
-        }
-    }
-
     pub fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self {
         Self { trait_ref: self.trait_ref.with_self_ty(tcx, self_ty), ..self }
     }
@@ -922,14 +908,6 @@ impl<'tcx> PolyTraitPredicate<'tcx> {
         self.map_bound(|trait_ref| trait_ref.self_ty())
     }
 
-    /// Remap the constness of this predicate before emitting it for diagnostics.
-    pub fn remap_constness_diag(&mut self, param_env: ParamEnv<'tcx>) {
-        *self = self.map_bound(|mut p| {
-            p.remap_constness_diag(param_env);
-            p
-        });
-    }
-
     #[inline]
     pub fn is_const_if_const(self) -> bool {
         self.skip_binder().is_const_if_const()
@@ -1700,15 +1678,12 @@ pub struct ParamEnv<'tcx> {
 #[derive(Copy, Clone)]
 struct ParamTag {
     reveal: traits::Reveal,
-    constness: hir::Constness,
 }
 
 impl_tag! {
     impl Tag for ParamTag;
-    ParamTag { reveal: traits::Reveal::UserFacing, constness: hir::Constness::NotConst },
-    ParamTag { reveal: traits::Reveal::All,        constness: hir::Constness::NotConst },
-    ParamTag { reveal: traits::Reveal::UserFacing, constness: hir::Constness::Const    },
-    ParamTag { reveal: traits::Reveal::All,        constness: hir::Constness::Const    },
+    ParamTag { reveal: traits::Reveal::UserFacing },
+    ParamTag { reveal: traits::Reveal::All },
 }
 
 impl<'tcx> fmt::Debug for ParamEnv<'tcx> {
@@ -1716,7 +1691,6 @@ impl<'tcx> fmt::Debug for ParamEnv<'tcx> {
         f.debug_struct("ParamEnv")
             .field("caller_bounds", &self.caller_bounds())
             .field("reveal", &self.reveal())
-            .field("constness", &self.constness())
             .finish()
     }
 }
@@ -1725,7 +1699,6 @@ impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for ParamEnv<'tcx> {
     fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
         self.caller_bounds().hash_stable(hcx, hasher);
         self.reveal().hash_stable(hcx, hasher);
-        self.constness().hash_stable(hcx, hasher);
     }
 }
 
@@ -1737,7 +1710,6 @@ impl<'tcx> TypeFoldable<TyCtxt<'tcx>> for ParamEnv<'tcx> {
         Ok(ParamEnv::new(
             self.caller_bounds().try_fold_with(folder)?,
             self.reveal().try_fold_with(folder)?,
-            self.constness(),
         ))
     }
 }
@@ -1756,7 +1728,7 @@ impl<'tcx> ParamEnv<'tcx> {
     /// type-checking.
     #[inline]
     pub fn empty() -> Self {
-        Self::new(List::empty(), Reveal::UserFacing, hir::Constness::NotConst)
+        Self::new(List::empty(), Reveal::UserFacing)
     }
 
     #[inline]
@@ -1769,16 +1741,6 @@ impl<'tcx> ParamEnv<'tcx> {
         self.packed.tag().reveal
     }
 
-    #[inline]
-    pub fn constness(self) -> hir::Constness {
-        self.packed.tag().constness
-    }
-
-    #[inline]
-    pub fn is_const(self) -> bool {
-        self.packed.tag().constness == hir::Constness::Const
-    }
-
     /// Construct a trait environment with no where-clauses in scope
     /// where the values of all `impl Trait` and other hidden types
     /// are revealed. This is suitable for monomorphized, post-typeck
@@ -1788,17 +1750,13 @@ impl<'tcx> ParamEnv<'tcx> {
     /// or invoke `param_env.with_reveal_all()`.
     #[inline]
     pub fn reveal_all() -> Self {
-        Self::new(List::empty(), Reveal::All, hir::Constness::NotConst)
+        Self::new(List::empty(), Reveal::All)
     }
 
     /// Construct a trait environment with the given set of predicates.
     #[inline]
-    pub fn new(
-        caller_bounds: &'tcx List<Clause<'tcx>>,
-        reveal: Reveal,
-        constness: hir::Constness,
-    ) -> Self {
-        ty::ParamEnv { packed: CopyTaggedPtr::new(caller_bounds, ParamTag { reveal, constness }) }
+    pub fn new(caller_bounds: &'tcx List<Clause<'tcx>>, reveal: Reveal) -> Self {
+        ty::ParamEnv { packed: CopyTaggedPtr::new(caller_bounds, ParamTag { reveal }) }
     }
 
     pub fn with_user_facing(mut self) -> Self {
@@ -1806,29 +1764,6 @@ impl<'tcx> ParamEnv<'tcx> {
         self
     }
 
-    #[inline]
-    pub fn with_constness(mut self, constness: hir::Constness) -> Self {
-        self.packed.set_tag(ParamTag { constness, ..self.packed.tag() });
-        self
-    }
-
-    #[inline]
-    pub fn with_const(mut self) -> Self {
-        self.packed.set_tag(ParamTag { constness: hir::Constness::Const, ..self.packed.tag() });
-        self
-    }
-
-    #[inline]
-    pub fn without_const(mut self) -> Self {
-        self.packed.set_tag(ParamTag { constness: hir::Constness::NotConst, ..self.packed.tag() });
-        self
-    }
-
-    #[inline]
-    pub fn remap_constness_with(&mut self, mut constness: ty::BoundConstness) {
-        *self = self.with_constness(constness.and(self.constness()))
-    }
-
     /// Returns a new parameter environment with the same clauses, but
     /// which "reveals" the true results of projections in all cases
     /// (even for associated types that are specializable). This is
@@ -1843,17 +1778,13 @@ impl<'tcx> ParamEnv<'tcx> {
             return self;
         }
 
-        ParamEnv::new(
-            tcx.reveal_opaque_types_in_bounds(self.caller_bounds()),
-            Reveal::All,
-            self.constness(),
-        )
+        ParamEnv::new(tcx.reveal_opaque_types_in_bounds(self.caller_bounds()), Reveal::All)
     }
 
     /// Returns this same environment but with no caller bounds.
     #[inline]
     pub fn without_caller_bounds(self) -> Self {
-        Self::new(List::empty(), self.reveal(), self.constness())
+        Self::new(List::empty(), self.reveal())
     }
 
     /// Creates a suitable environment in which to perform trait
diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs
index 9d380a58d9f..50bf9afa531 100644
--- a/compiler/rustc_middle/src/ty/structural_impls.rs
+++ b/compiler/rustc_middle/src/ty/structural_impls.rs
@@ -602,7 +602,7 @@ impl<'a, 'tcx> Lift<'tcx> for ty::ParamEnv<'a> {
     type Lifted = ty::ParamEnv<'tcx>;
     fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted> {
         tcx.lift(self.caller_bounds())
-            .map(|caller_bounds| ty::ParamEnv::new(caller_bounds, self.reveal(), self.constness()))
+            .map(|caller_bounds| ty::ParamEnv::new(caller_bounds, self.reveal()))
     }
 }
 
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index f9c1ca9a8b1..3e023ccdead 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -2670,11 +2670,6 @@ impl<'tcx> Ty<'tcx> {
         variant_index: VariantIdx,
     ) -> Option<Discr<'tcx>> {
         match self.kind() {
-            TyKind::Adt(adt, _) if adt.variants().is_empty() => {
-                // This can actually happen during CTFE, see
-                // https://github.com/rust-lang/rust/issues/89765.
-                None
-            }
             TyKind::Adt(adt, _) if adt.is_enum() => {
                 Some(adt.discriminant_for_variant(tcx, variant_index))
             }
diff --git a/compiler/rustc_mir_build/Cargo.toml b/compiler/rustc_mir_build/Cargo.toml
index 58449ee9eb4..c7e2c625ce5 100644
--- a/compiler/rustc_mir_build/Cargo.toml
+++ b/compiler/rustc_mir_build/Cargo.toml
@@ -10,7 +10,7 @@ rustc_arena = { path = "../rustc_arena" }
 tracing = "0.1"
 either = "1"
 rustc_middle = { path = "../rustc_middle" }
-rustc_apfloat = { path = "../rustc_apfloat" }
+rustc_apfloat = "0.2.0"
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_index = { path = "../rustc_index" }
 rustc_errors = { path = "../rustc_errors" }
diff --git a/compiler/rustc_mir_build/src/build/custom/parse.rs b/compiler/rustc_mir_build/src/build/custom/parse.rs
index c494929cbff..60c4a041696 100644
--- a/compiler/rustc_mir_build/src/build/custom/parse.rs
+++ b/compiler/rustc_mir_build/src/build/custom/parse.rs
@@ -74,7 +74,7 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
             kind @ StmtKind::Let { pattern, .. } => {
                 return Err(ParseError {
                     span: pattern.span,
-                    item_description: format!("{:?}", kind),
+                    item_description: format!("{kind:?}"),
                     expected: "expression".to_string(),
                 });
             }
diff --git a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs
index 7817c3d0d80..3220a184d49 100644
--- a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs
+++ b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs
@@ -774,8 +774,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                         // Not in a closure
                         debug_assert!(
                             local == ty::CAPTURE_STRUCT_LOCAL,
-                            "Expected local to be Local(1), found {:?}",
-                            local
+                            "Expected local to be Local(1), found {local:?}"
                         );
                         // Not in a closure
                         debug_assert!(
diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs
index 6f14891cb78..ed3ac7cb3ec 100644
--- a/compiler/rustc_mir_build/src/build/matches/mod.rs
+++ b/compiler/rustc_mir_build/src/build/matches/mod.rs
@@ -1627,9 +1627,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         // at least the first candidate ought to be tested
         assert!(
             total_candidate_count > candidates.len(),
-            "{}, {:#?}",
-            total_candidate_count,
-            candidates
+            "{total_candidate_count}, {candidates:#?}"
         );
         debug!("tested_candidates: {}", total_candidate_count - candidates.len());
         debug!("untested_candidates: {}", candidates.len());
diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs
index 275878a3919..484e8490919 100644
--- a/compiler/rustc_mir_build/src/build/matches/test.rs
+++ b/compiler/rustc_mir_build/src/build/matches/test.rs
@@ -175,16 +175,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                             debug_assert_ne!(
                                 target_blocks[idx.index()],
                                 otherwise_block,
-                                "no candidates for tested discriminant: {:?}",
-                                discr,
+                                "no candidates for tested discriminant: {discr:?}",
                             );
                             Some((discr.val, target_blocks[idx.index()]))
                         } else {
                             debug_assert_eq!(
                                 target_blocks[idx.index()],
                                 otherwise_block,
-                                "found candidates for untested discriminant: {:?}",
-                                discr,
+                                "found candidates for untested discriminant: {discr:?}",
                             );
                             None
                         }
diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs
index 17295fe1d34..1ae8031ef3c 100644
--- a/compiler/rustc_mir_build/src/build/mod.rs
+++ b/compiler/rustc_mir_build/src/build/mod.rs
@@ -94,8 +94,7 @@ fn mir_build(tcx: TyCtxt<'_>, def: LocalDefId) -> Body<'_> {
             || body.basic_blocks.has_free_regions()
             || body.var_debug_info.has_free_regions()
             || body.yield_ty().has_free_regions()),
-        "Unexpected free regions in MIR: {:?}",
-        body,
+        "Unexpected free regions in MIR: {body:?}",
     );
 
     body
@@ -977,9 +976,9 @@ pub(crate) fn parse_float_into_scalar(
     match float_ty {
         ty::FloatTy::F32 => {
             let Ok(rust_f) = num.parse::<f32>() else { return None };
-            let mut f = num.parse::<Single>().unwrap_or_else(|e| {
-                panic!("apfloat::ieee::Single failed to parse `{}`: {:?}", num, e)
-            });
+            let mut f = num
+                .parse::<Single>()
+                .unwrap_or_else(|e| panic!("apfloat::ieee::Single failed to parse `{num}`: {e:?}"));
 
             assert!(
                 u128::from(rust_f.to_bits()) == f.to_bits(),
@@ -1000,9 +999,9 @@ pub(crate) fn parse_float_into_scalar(
         }
         ty::FloatTy::F64 => {
             let Ok(rust_f) = num.parse::<f64>() else { return None };
-            let mut f = num.parse::<Double>().unwrap_or_else(|e| {
-                panic!("apfloat::ieee::Double failed to parse `{}`: {:?}", num, e)
-            });
+            let mut f = num
+                .parse::<Double>()
+                .unwrap_or_else(|e| panic!("apfloat::ieee::Double failed to parse `{num}`: {e:?}"));
 
             assert!(
                 u128::from(rust_f.to_bits()) == f.to_bits(),
diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs
index 820ab93851e..3ff3387a781 100644
--- a/compiler/rustc_mir_build/src/errors.rs
+++ b/compiler/rustc_mir_build/src/errors.rs
@@ -454,17 +454,13 @@ impl<'a> IntoDiagnostic<'a> for NonExhaustivePatternsTypeNotEmpty<'_, '_, '_> {
         if self.span.eq_ctxt(self.expr_span) {
             // Get the span for the empty match body `{}`.
             let (indentation, more) = if let Some(snippet) = sm.indentation_before(self.span) {
-                (format!("\n{}", snippet), "    ")
+                (format!("\n{snippet}"), "    ")
             } else {
                 (" ".to_string(), "")
             };
             suggestion = Some((
                 self.span.shrink_to_hi().with_hi(self.expr_span.hi()),
-                format!(
-                    " {{{indentation}{more}_ => todo!(),{indentation}}}",
-                    indentation = indentation,
-                    more = more,
-                ),
+                format!(" {{{indentation}{more}_ => todo!(),{indentation}}}",),
             ));
         }
 
diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs
index 711a9126c04..ff4620948fa 100644
--- a/compiler/rustc_mir_build/src/thir/cx/expr.rs
+++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs
@@ -229,9 +229,7 @@ impl<'tcx> Cx<'tcx> {
             let param_env_ty = self.param_env.and(discr_ty);
             let size = tcx
                 .layout_of(param_env_ty)
-                .unwrap_or_else(|e| {
-                    panic!("could not compute layout for {:?}: {:?}", param_env_ty, e)
-                })
+                .unwrap_or_else(|e| panic!("could not compute layout for {param_env_ty:?}: {e:?}"))
                 .size;
 
             let lit = ScalarInt::try_from_uint(discr_offset as u128, size).unwrap();
diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
index 38c866c50a2..a786e659664 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
@@ -691,7 +691,7 @@ fn non_exhaustive_match<'p, 'tcx>(
         err = create_e0004(
             cx.tcx.sess,
             sp,
-            format!("non-exhaustive patterns: {} not covered", joined_patterns),
+            format!("non-exhaustive patterns: {joined_patterns} not covered"),
         );
         err.span_label(sp, pattern_not_covered_label(&witnesses, &joined_patterns));
         patterns_len = witnesses.len();
@@ -721,15 +721,13 @@ fn non_exhaustive_match<'p, 'tcx>(
         && matches!(witnesses[0].ctor(), Constructor::NonExhaustive)
     {
         err.note(format!(
-            "`{}` does not have a fixed maximum value, so a wildcard `_` is necessary to match \
+            "`{scrut_ty}` does not have a fixed maximum value, so a wildcard `_` is necessary to match \
              exhaustively",
-            scrut_ty,
         ));
         if cx.tcx.sess.is_nightly_build() {
             err.help(format!(
                 "add `#![feature(precise_pointer_size_matching)]` to the crate attributes to \
-                 enable precise `{}` matching",
-                scrut_ty,
+                 enable precise `{scrut_ty}` matching",
             ));
         }
     }
@@ -745,18 +743,13 @@ fn non_exhaustive_match<'p, 'tcx>(
         [] if sp.eq_ctxt(expr_span) => {
             // Get the span for the empty match body `{}`.
             let (indentation, more) = if let Some(snippet) = sm.indentation_before(sp) {
-                (format!("\n{}", snippet), "    ")
+                (format!("\n{snippet}"), "    ")
             } else {
                 (" ".to_string(), "")
             };
             suggestion = Some((
                 sp.shrink_to_hi().with_hi(expr_span.hi()),
-                format!(
-                    " {{{indentation}{more}{pattern} => todo!(),{indentation}}}",
-                    indentation = indentation,
-                    more = more,
-                    pattern = pattern,
-                ),
+                format!(" {{{indentation}{more}{pattern} => todo!(),{indentation}}}",),
             ));
         }
         [only] => {
@@ -765,7 +758,7 @@ fn non_exhaustive_match<'p, 'tcx>(
                 && let Ok(with_trailing) = sm.span_extend_while(only.span, |c| c.is_whitespace() || c == ',')
                 && sm.is_multiline(with_trailing)
             {
-                (format!("\n{}", snippet), true)
+                (format!("\n{snippet}"), true)
             } else {
                 (" ".to_string(), false)
             };
@@ -780,7 +773,7 @@ fn non_exhaustive_match<'p, 'tcx>(
             };
             suggestion = Some((
                 only.span.shrink_to_hi(),
-                format!("{}{}{} => todo!()", comma, pre_indentation, pattern),
+                format!("{comma}{pre_indentation}{pattern} => todo!()"),
             ));
         }
         [.., prev, last] => {
@@ -803,7 +796,7 @@ fn non_exhaustive_match<'p, 'tcx>(
                 if let Some(spacing) = spacing {
                     suggestion = Some((
                         last.span.shrink_to_hi(),
-                        format!("{}{}{} => todo!()", comma, spacing, pattern),
+                        format!("{comma}{spacing}{pattern} => todo!()"),
                     ));
                 }
             }
@@ -900,7 +893,7 @@ fn adt_defined_here<'p, 'tcx>(
         for pat in spans {
             span.push_span_label(pat, "not covered");
         }
-        err.span_note(span, format!("`{}` defined here", ty));
+        err.span_note(span, format!("`{ty}` defined here"));
     }
 }
 
diff --git a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs
index 4228597cdc9..bee1c4e4614 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs
@@ -306,9 +306,9 @@ impl fmt::Debug for IntRange {
         let (lo, hi) = self.boundaries();
         let bias = self.bias;
         let (lo, hi) = (lo ^ bias, hi ^ bias);
-        write!(f, "{}", lo)?;
+        write!(f, "{lo}")?;
         write!(f, "{}", RangeEnd::Included)?;
-        write!(f, "{}", hi)
+        write!(f, "{hi}")
     }
 }
 
@@ -1619,7 +1619,7 @@ impl<'p, 'tcx> fmt::Debug for DeconstructedPat<'p, 'tcx> {
                     // of `std`). So this branch is only reachable when the feature is enabled and
                     // the pattern is a box pattern.
                     let subpattern = self.iter_fields().next().unwrap();
-                    write!(f, "box {:?}", subpattern)
+                    write!(f, "box {subpattern:?}")
                 }
                 ty::Adt(..) | ty::Tuple(..) => {
                     let variant = match self.ty.kind() {
@@ -1638,7 +1638,7 @@ impl<'p, 'tcx> fmt::Debug for DeconstructedPat<'p, 'tcx> {
                     write!(f, "(")?;
                     for p in self.iter_fields() {
                         write!(f, "{}", start_or_comma())?;
-                        write!(f, "{:?}", p)?;
+                        write!(f, "{p:?}")?;
                     }
                     write!(f, ")")
                 }
@@ -1674,11 +1674,11 @@ impl<'p, 'tcx> fmt::Debug for DeconstructedPat<'p, 'tcx> {
                 write!(f, "]")
             }
             &FloatRange(lo, hi, end) => {
-                write!(f, "{}", lo)?;
-                write!(f, "{}", end)?;
-                write!(f, "{}", hi)
+                write!(f, "{lo}")?;
+                write!(f, "{end}")?;
+                write!(f, "{hi}")
             }
-            IntRange(range) => write!(f, "{:?}", range), // Best-effort, will render e.g. `false` as `0..=0`
+            IntRange(range) => write!(f, "{range:?}"), // Best-effort, will render e.g. `false` as `0..=0`
             Wildcard | Missing { .. } | NonExhaustive => write!(f, "_ : {:?}", self.ty),
             Or => {
                 for pat in self.iter_fields() {
@@ -1686,7 +1686,7 @@ impl<'p, 'tcx> fmt::Debug for DeconstructedPat<'p, 'tcx> {
                 }
                 Ok(())
             }
-            Str(value) => write!(f, "{}", value),
+            Str(value) => write!(f, "{value}"),
             Opaque => write!(f, "<constant pattern>"),
         }
     }
diff --git a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs
index e5b63506906..08cfe98bb68 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs
@@ -459,7 +459,7 @@ impl<'p, 'tcx> fmt::Debug for PatStack<'p, 'tcx> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         write!(f, "+")?;
         for pat in self.iter() {
-            write!(f, " {:?} +", pat)?;
+            write!(f, " {pat:?} +")?;
         }
         Ok(())
     }
@@ -530,7 +530,7 @@ impl<'p, 'tcx> fmt::Debug for Matrix<'p, 'tcx> {
 
         let Matrix { patterns: m, .. } = self;
         let pretty_printed_matrix: Vec<Vec<String>> =
-            m.iter().map(|row| row.iter().map(|pat| format!("{:?}", pat)).collect()).collect();
+            m.iter().map(|row| row.iter().map(|pat| format!("{pat:?}")).collect()).collect();
 
         let column_count = m.iter().map(|row| row.len()).next().unwrap_or(0);
         assert!(m.iter().all(|row| row.len() == column_count));
diff --git a/compiler/rustc_mir_transform/src/const_prop_lint.rs b/compiler/rustc_mir_transform/src/const_prop_lint.rs
index 01b945afda6..ac07c25763b 100644
--- a/compiler/rustc_mir_transform/src/const_prop_lint.rs
+++ b/compiler/rustc_mir_transform/src/const_prop_lint.rs
@@ -494,7 +494,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
         trace!("assertion on {:?} should be {:?}", value, expected);
 
         let expected = Scalar::from_bool(expected);
-        let value_const = self.use_ecx(location, |this| this.ecx.read_scalar(&value))?;
+        let value_const = self.use_ecx(location, |this| this.ecx.read_scalar(value))?;
 
         if expected != value_const {
             // Poison all places this operand references so that further code
@@ -664,7 +664,7 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> {
             }
             TerminatorKind::SwitchInt { ref discr, ref targets } => {
                 if let Some(ref value) = self.eval_operand(&discr, location)
-                  && let Some(value_const) = self.use_ecx(location, |this| this.ecx.read_scalar(&value))
+                  && let Some(value_const) = self.use_ecx(location, |this| this.ecx.read_scalar(value))
                   && let Ok(constant) = value_const.try_to_int()
                   && let Ok(constant) = constant.to_bits(constant.size())
                 {
diff --git a/compiler/rustc_mir_transform/src/coverage/counters.rs b/compiler/rustc_mir_transform/src/coverage/counters.rs
index 658e01d9310..e9c5f856d35 100644
--- a/compiler/rustc_mir_transform/src/coverage/counters.rs
+++ b/compiler/rustc_mir_transform/src/coverage/counters.rs
@@ -282,7 +282,7 @@ impl<'a> BcbCounters<'a> {
             branching_counter_operand,
             Op::Subtract,
             sumup_counter_operand,
-            || Some(format!("{:?}", expression_branch)),
+            || Some(format!("{expression_branch:?}")),
         );
         debug!("{:?} gets an expression: {}", expression_branch, self.format_counter(&expression));
         let bcb = expression_branch.target_bcb;
@@ -324,7 +324,7 @@ impl<'a> BcbCounters<'a> {
         // program results in a tight infinite loop, but it should still compile.
         let one_path_to_target = self.bcb_has_one_path_to_target(bcb);
         if one_path_to_target || self.bcb_predecessors(bcb).contains(&bcb) {
-            let counter_kind = self.coverage_counters.make_counter(|| Some(format!("{:?}", bcb)));
+            let counter_kind = self.coverage_counters.make_counter(|| Some(format!("{bcb:?}")));
             if one_path_to_target {
                 debug!(
                     "{}{:?} gets a new counter: {}",
@@ -392,7 +392,7 @@ impl<'a> BcbCounters<'a> {
             first_edge_counter_operand,
             Op::Add,
             some_sumup_edge_counter_operand.unwrap(),
-            || Some(format!("{:?}", bcb)),
+            || Some(format!("{bcb:?}")),
         );
         debug!(
             "{}{:?} gets a new counter (sum of predecessor counters): {}",
@@ -449,7 +449,7 @@ impl<'a> BcbCounters<'a> {
 
         // Make a new counter to count this edge.
         let counter_kind =
-            self.coverage_counters.make_counter(|| Some(format!("{:?}->{:?}", from_bcb, to_bcb)));
+            self.coverage_counters.make_counter(|| Some(format!("{from_bcb:?}->{to_bcb:?}")));
         debug!(
             "{}Edge {:?}->{:?} gets a new counter: {}",
             NESTED_INDENT.repeat(debug_indent_level),
diff --git a/compiler/rustc_mir_transform/src/coverage/debug.rs b/compiler/rustc_mir_transform/src/coverage/debug.rs
index 7ad98144159..c9914eb9f82 100644
--- a/compiler/rustc_mir_transform/src/coverage/debug.rs
+++ b/compiler/rustc_mir_transform/src/coverage/debug.rs
@@ -630,7 +630,7 @@ pub(super) fn dump_coverage_spanview<'tcx>(
         .expect("Unexpected error creating MIR spanview HTML file");
     let crate_name = tcx.crate_name(def_id.krate);
     let item_name = tcx.def_path(def_id).to_filename_friendly_no_crate();
-    let title = format!("{}.{} - Coverage Spans", crate_name, item_name);
+    let title = format!("{crate_name}.{item_name} - Coverage Spans");
     spanview::write_document(tcx, body_span, span_viewables, &title, &mut file)
         .expect("Unexpected IO error dumping coverage spans as HTML");
 }
@@ -779,7 +779,7 @@ fn bcb_to_string_sections<'tcx>(
         ));
     }
     if let Some(counter_kind) = &bcb_data.counter_kind {
-        sections.push(format!("{:?}", counter_kind));
+        sections.push(format!("{counter_kind:?}"));
     }
     let non_term_blocks = bcb_data.basic_blocks[0..len - 1]
         .iter()
diff --git a/compiler/rustc_mir_transform/src/coverage/graph.rs b/compiler/rustc_mir_transform/src/coverage/graph.rs
index d2a854b2675..5d843f4ade0 100644
--- a/compiler/rustc_mir_transform/src/coverage/graph.rs
+++ b/compiler/rustc_mir_transform/src/coverage/graph.rs
@@ -360,8 +360,7 @@ impl BasicCoverageBlockData {
         if let Some(replaced) = self.counter_kind.replace(counter_kind) {
             Error::from_string(format!(
                 "attempt to set a BasicCoverageBlock coverage counter more than once; \
-                {:?} already had counter {:?}",
-                self, replaced,
+                {self:?} already had counter {replaced:?}",
             ))
         } else {
             Ok(operand)
@@ -389,9 +388,8 @@ impl BasicCoverageBlockData {
             // `BasicCoverageBlock`).
             if self.counter_kind.as_ref().is_some_and(|c| !c.is_expression()) {
                 return Error::from_string(format!(
-                    "attempt to add an incoming edge counter from {:?} when the target BCB already \
-                    has a `Counter`",
-                    from_bcb
+                    "attempt to add an incoming edge counter from {from_bcb:?} when the target BCB already \
+                    has a `Counter`"
                 ));
             }
         }
@@ -401,8 +399,7 @@ impl BasicCoverageBlockData {
         {
             Error::from_string(format!(
                 "attempt to set an edge counter more than once; from_bcb: \
-                {:?} already had counter {:?}",
-                from_bcb, replaced,
+                {from_bcb:?} already had counter {replaced:?}",
             ))
         } else {
             Ok(operand)
@@ -612,7 +609,7 @@ impl TraverseCoverageGraphWithLoops {
                             the {}",
                             successor_to_add,
                             if let Some(loop_header) = some_loop_header {
-                                format!("worklist for the loop headed by {:?}", loop_header)
+                                format!("worklist for the loop headed by {loop_header:?}")
                             } else {
                                 String::from("non-loop worklist")
                             },
@@ -623,7 +620,7 @@ impl TraverseCoverageGraphWithLoops {
                             "{:?} successor is non-branching. Defer it to the end of the {}",
                             successor_to_add,
                             if let Some(loop_header) = some_loop_header {
-                                format!("worklist for the loop headed by {:?}", loop_header)
+                                format!("worklist for the loop headed by {loop_header:?}")
                             } else {
                                 String::from("non-loop worklist")
                             },
diff --git a/compiler/rustc_mir_transform/src/elaborate_drops.rs b/compiler/rustc_mir_transform/src/elaborate_drops.rs
index d5664e2b40a..43757a9ea35 100644
--- a/compiler/rustc_mir_transform/src/elaborate_drops.rs
+++ b/compiler/rustc_mir_transform/src/elaborate_drops.rs
@@ -358,8 +358,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
                         self.tcx.sess.delay_span_bug(
                             terminator.source_info.span,
                             format!(
-                                "drop of untracked, uninitialized value {:?}, place {:?} ({:?})",
-                                bb, place, path
+                                "drop of untracked, uninitialized value {bb:?}, place {place:?} ({path:?})"
                             ),
                         );
                     }
@@ -424,7 +423,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
                             if !replace {
                                 self.tcx.sess.delay_span_bug(
                                     terminator.source_info.span,
-                                    format!("drop of untracked value {:?}", bb),
+                                    format!("drop of untracked value {bb:?}"),
                                 );
                             }
                             // A drop and replace behind a pointer/array/whatever.
diff --git a/compiler/rustc_mir_transform/src/function_item_references.rs b/compiler/rustc_mir_transform/src/function_item_references.rs
index fdf6ab33c82..a42eacbf22b 100644
--- a/compiler/rustc_mir_transform/src/function_item_references.rs
+++ b/compiler/rustc_mir_transform/src/function_item_references.rs
@@ -168,15 +168,15 @@ impl<'tcx> FunctionItemRefChecker<'_, 'tcx> {
             }
         };
         let ident = self.tcx.item_name(fn_id).to_ident_string();
-        let ty_params = fn_args.types().map(|ty| format!("{}", ty));
-        let const_params = fn_args.consts().map(|c| format!("{}", c));
+        let ty_params = fn_args.types().map(|ty| format!("{ty}"));
+        let const_params = fn_args.consts().map(|c| format!("{c}"));
         let params = ty_params.chain(const_params).join(", ");
         let num_args = fn_sig.inputs().map_bound(|inputs| inputs.len()).skip_binder();
         let variadic = if fn_sig.c_variadic() { ", ..." } else { "" };
         let ret = if fn_sig.output().skip_binder().is_unit() { "" } else { " -> _" };
         let sugg = format!(
             "{} as {}{}fn({}{}){}",
-            if params.is_empty() { ident.clone() } else { format!("{}::<{}>", ident, params) },
+            if params.is_empty() { ident.clone() } else { format!("{ident}::<{params}>") },
             unsafety,
             abi,
             vec!["_"; num_args].join(", "),
diff --git a/compiler/rustc_mir_transform/src/generator.rs b/compiler/rustc_mir_transform/src/generator.rs
index eaeaff69cf3..669135f80bc 100644
--- a/compiler/rustc_mir_transform/src/generator.rs
+++ b/compiler/rustc_mir_transform/src/generator.rs
@@ -1477,7 +1477,7 @@ impl<'tcx> MirPass<'tcx> for StateTransform {
                 )
             }
             _ => {
-                tcx.sess.delay_span_bug(body.span, format!("unexpected generator type {}", gen_ty));
+                tcx.sess.delay_span_bug(body.span, format!("unexpected generator type {gen_ty}"));
                 return;
             }
         };
diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs
index e08edfe143a..f13c8214af1 100644
--- a/compiler/rustc_mir_transform/src/inline.rs
+++ b/compiler/rustc_mir_transform/src/inline.rs
@@ -1,6 +1,7 @@
 //! Inlining pass for MIR functions
 use crate::deref_separator::deref_finder;
 use rustc_attr::InlineAttr;
+use rustc_const_eval::transform::validate::validate_types;
 use rustc_hir::def_id::DefId;
 use rustc_index::bit_set::BitSet;
 use rustc_index::Idx;
@@ -10,7 +11,7 @@ use rustc_middle::mir::*;
 use rustc_middle::ty::TypeVisitableExt;
 use rustc_middle::ty::{self, Instance, InstanceDef, ParamEnv, Ty, TyCtxt};
 use rustc_session::config::OptLevel;
-use rustc_target::abi::{FieldIdx, FIRST_VARIANT};
+use rustc_target::abi::FieldIdx;
 use rustc_target::spec::abi::Abi;
 
 use crate::simplify::{remove_dead_blocks, CfgSimplifier};
@@ -200,6 +201,19 @@ impl<'tcx> Inliner<'tcx> {
             return Err("failed to normalize callee body");
         };
 
+        // Normally, this shouldn't be required, but trait normalization failure can create a
+        // validation ICE.
+        if !validate_types(
+            self.tcx,
+            MirPhase::Runtime(RuntimePhase::Optimized),
+            self.param_env,
+            &callee_body,
+        )
+        .is_empty()
+        {
+            return Err("failed to validate callee body");
+        }
+
         // Check call signature compatibility.
         // Normally, this shouldn't be required, but trait normalization failure can create a
         // validation ICE.
@@ -437,13 +451,8 @@ impl<'tcx> Inliner<'tcx> {
             instance: callsite.callee,
             callee_body,
             cost: 0,
-            validation: Ok(()),
         };
 
-        for var_debug_info in callee_body.var_debug_info.iter() {
-            checker.visit_var_debug_info(var_debug_info);
-        }
-
         // Traverse the MIR manually so we can account for the effects of inlining on the CFG.
         let mut work_list = vec![START_BLOCK];
         let mut visited = BitSet::new_empty(callee_body.basic_blocks.len());
@@ -480,9 +489,6 @@ impl<'tcx> Inliner<'tcx> {
             }
         }
 
-        // Abort if type validation found anything fishy.
-        checker.validation?;
-
         // N.B. We still apply our cost threshold to #[inline(always)] functions.
         // That attribute is often applied to very large functions that exceed LLVM's (very
         // generous) inlining threshold. Such functions are very poor MIR inlining candidates.
@@ -774,11 +780,10 @@ struct CostChecker<'b, 'tcx> {
     cost: usize,
     callee_body: &'b Body<'tcx>,
     instance: ty::Instance<'tcx>,
-    validation: Result<(), &'static str>,
 }
 
 impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> {
-    fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
+    fn visit_statement(&mut self, statement: &Statement<'tcx>, _: Location) {
         // Don't count StorageLive/StorageDead in the inlining cost.
         match statement.kind {
             StatementKind::StorageLive(_)
@@ -787,11 +792,9 @@ impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> {
             | StatementKind::Nop => {}
             _ => self.cost += INSTR_COST,
         }
-
-        self.super_statement(statement, location);
     }
 
-    fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
+    fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, _: Location) {
         let tcx = self.tcx;
         match terminator.kind {
             TerminatorKind::Drop { ref place, unwind, .. } => {
@@ -835,109 +838,6 @@ impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> {
             }
             _ => self.cost += INSTR_COST,
         }
-
-        self.super_terminator(terminator, location);
-    }
-
-    /// This method duplicates code from MIR validation in an attempt to detect type mismatches due
-    /// to normalization failure.
-    fn visit_projection_elem(
-        &mut self,
-        place_ref: PlaceRef<'tcx>,
-        elem: PlaceElem<'tcx>,
-        context: PlaceContext,
-        location: Location,
-    ) {
-        if let ProjectionElem::Field(f, ty) = elem {
-            let parent_ty = place_ref.ty(&self.callee_body.local_decls, self.tcx);
-            let check_equal = |this: &mut Self, f_ty| {
-                // Fast path if there is nothing to substitute.
-                if ty == f_ty {
-                    return;
-                }
-                let ty = this.instance.subst_mir(this.tcx, ty::EarlyBinder::bind(&ty));
-                let f_ty = this.instance.subst_mir(this.tcx, ty::EarlyBinder::bind(&f_ty));
-                if ty == f_ty {
-                    return;
-                }
-                if !util::is_subtype(this.tcx, this.param_env, ty, f_ty) {
-                    trace!(?ty, ?f_ty);
-                    this.validation = Err("failed to normalize projection type");
-                    return;
-                }
-            };
-
-            let kind = match parent_ty.ty.kind() {
-                &ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => {
-                    self.tcx.type_of(def_id).instantiate(self.tcx, args).kind()
-                }
-                kind => kind,
-            };
-
-            match kind {
-                ty::Tuple(fields) => {
-                    let Some(f_ty) = fields.get(f.as_usize()) else {
-                        self.validation = Err("malformed MIR");
-                        return;
-                    };
-                    check_equal(self, *f_ty);
-                }
-                ty::Adt(adt_def, args) => {
-                    let var = parent_ty.variant_index.unwrap_or(FIRST_VARIANT);
-                    let Some(field) = adt_def.variant(var).fields.get(f) else {
-                        self.validation = Err("malformed MIR");
-                        return;
-                    };
-                    check_equal(self, field.ty(self.tcx, args));
-                }
-                ty::Closure(_, args) => {
-                    let args = args.as_closure();
-                    let Some(f_ty) = args.upvar_tys().nth(f.as_usize()) else {
-                        self.validation = Err("malformed MIR");
-                        return;
-                    };
-                    check_equal(self, f_ty);
-                }
-                &ty::Generator(def_id, args, _) => {
-                    let f_ty = if let Some(var) = parent_ty.variant_index {
-                        let gen_body = if def_id == self.callee_body.source.def_id() {
-                            self.callee_body
-                        } else {
-                            self.tcx.optimized_mir(def_id)
-                        };
-
-                        let Some(layout) = gen_body.generator_layout() else {
-                            self.validation = Err("malformed MIR");
-                            return;
-                        };
-
-                        let Some(&local) = layout.variant_fields[var].get(f) else {
-                            self.validation = Err("malformed MIR");
-                            return;
-                        };
-
-                        let Some(f_ty) = layout.field_tys.get(local) else {
-                            self.validation = Err("malformed MIR");
-                            return;
-                        };
-
-                        f_ty.ty
-                    } else {
-                        let Some(f_ty) = args.as_generator().prefix_tys().nth(f.index()) else {
-                            self.validation = Err("malformed MIR");
-                            return;
-                        };
-
-                        f_ty
-                    };
-
-                    check_equal(self, f_ty);
-                }
-                _ => self.validation = Err("malformed MIR"),
-            }
-        }
-
-        self.super_projection_elem(place_ref, elem, context, location);
     }
 }
 
diff --git a/compiler/rustc_mir_transform/src/instsimplify.rs b/compiler/rustc_mir_transform/src/instsimplify.rs
index a430f8da35c..8b0a0903d18 100644
--- a/compiler/rustc_mir_transform/src/instsimplify.rs
+++ b/compiler/rustc_mir_transform/src/instsimplify.rs
@@ -57,7 +57,7 @@ struct InstSimplifyContext<'tcx, 'a> {
 impl<'tcx> InstSimplifyContext<'tcx, '_> {
     fn should_simplify(&self, source_info: &SourceInfo, rvalue: &Rvalue<'tcx>) -> bool {
         self.tcx.consider_optimizing(|| {
-            format!("InstSimplify - Rvalue: {:?} SourceInfo: {:?}", rvalue, source_info)
+            format!("InstSimplify - Rvalue: {rvalue:?} SourceInfo: {source_info:?}")
         })
     }
 
diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs
index d419329f2d6..734321e97d8 100644
--- a/compiler/rustc_mir_transform/src/lib.rs
+++ b/compiler/rustc_mir_transform/src/lib.rs
@@ -615,7 +615,7 @@ fn inner_optimized_mir(tcx: TyCtxt<'_>, did: LocalDefId) -> Body<'_> {
         // computes and caches its result.
         Some(hir::ConstContext::ConstFn) => tcx.ensure_with_value().mir_for_ctfe(did),
         None => {}
-        Some(other) => panic!("do not use `optimized_mir` for constants: {:?}", other),
+        Some(other) => panic!("do not use `optimized_mir` for constants: {other:?}"),
     }
     debug!("about to call mir_drops_elaborated...");
     let body = tcx.mir_drops_elaborated_and_const_checked(did).steal();
diff --git a/compiler/rustc_mir_transform/src/match_branches.rs b/compiler/rustc_mir_transform/src/match_branches.rs
index 6eb48498274..bc29fb8ded1 100644
--- a/compiler/rustc_mir_transform/src/match_branches.rs
+++ b/compiler/rustc_mir_transform/src/match_branches.rs
@@ -51,7 +51,7 @@ impl<'tcx> MirPass<'tcx> for MatchBranchSimplification {
         let bbs = body.basic_blocks.as_mut();
         let mut should_cleanup = false;
         'outer: for bb_idx in bbs.indices() {
-            if !tcx.consider_optimizing(|| format!("MatchBranchSimplification {:?} ", def_id)) {
+            if !tcx.consider_optimizing(|| format!("MatchBranchSimplification {def_id:?} ")) {
                 continue;
             }
 
diff --git a/compiler/rustc_mir_transform/src/multiple_return_terminators.rs b/compiler/rustc_mir_transform/src/multiple_return_terminators.rs
index 3957cd92c4e..c97d034544a 100644
--- a/compiler/rustc_mir_transform/src/multiple_return_terminators.rs
+++ b/compiler/rustc_mir_transform/src/multiple_return_terminators.rs
@@ -27,7 +27,7 @@ impl<'tcx> MirPass<'tcx> for MultipleReturnTerminators {
         }
 
         for bb in bbs {
-            if !tcx.consider_optimizing(|| format!("MultipleReturnTerminators {:?} ", def_id)) {
+            if !tcx.consider_optimizing(|| format!("MultipleReturnTerminators {def_id:?} ")) {
                 break;
             }
 
diff --git a/compiler/rustc_mir_transform/src/nrvo.rs b/compiler/rustc_mir_transform/src/nrvo.rs
index 5ce96012b90..e1298b0654f 100644
--- a/compiler/rustc_mir_transform/src/nrvo.rs
+++ b/compiler/rustc_mir_transform/src/nrvo.rs
@@ -45,7 +45,7 @@ impl<'tcx> MirPass<'tcx> for RenameReturnPlace {
             return;
         };
 
-        if !tcx.consider_optimizing(|| format!("RenameReturnPlace {:?}", def_id)) {
+        if !tcx.consider_optimizing(|| format!("RenameReturnPlace {def_id:?}")) {
             return;
         }
 
diff --git a/compiler/rustc_mir_transform/src/pass_manager.rs b/compiler/rustc_mir_transform/src/pass_manager.rs
index 710eed3ed38..057f5fe8293 100644
--- a/compiler/rustc_mir_transform/src/pass_manager.rs
+++ b/compiler/rustc_mir_transform/src/pass_manager.rs
@@ -118,7 +118,7 @@ fn run_passes_inner<'tcx>(
                 dump_mir_for_pass(tcx, body, &name, false);
             }
             if validate {
-                validate_body(tcx, body, format!("before pass {}", name));
+                validate_body(tcx, body, format!("before pass {name}"));
             }
 
             tcx.sess.time(name, || pass.run_pass(tcx, body));
@@ -127,7 +127,7 @@ fn run_passes_inner<'tcx>(
                 dump_mir_for_pass(tcx, body, &name, true);
             }
             if validate {
-                validate_body(tcx, body, format!("after pass {}", name));
+                validate_body(tcx, body, format!("after pass {name}"));
             }
 
             body.pass_count += 1;
diff --git a/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs b/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs
index 84ccf6e1f61..08b2a6537e9 100644
--- a/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs
+++ b/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs
@@ -27,7 +27,7 @@ impl<'tcx> MirPass<'tcx> for RemoveUnneededDrops {
                 if ty.ty.needs_drop(tcx, param_env) {
                     continue;
                 }
-                if !tcx.consider_optimizing(|| format!("RemoveUnneededDrops {:?} ", did)) {
+                if !tcx.consider_optimizing(|| format!("RemoveUnneededDrops {did:?} ")) {
                     continue;
                 }
                 debug!("SUCCESS: replacing `drop` with goto({:?})", target);
diff --git a/compiler/rustc_mir_transform/src/remove_zsts.rs b/compiler/rustc_mir_transform/src/remove_zsts.rs
index 1ba9ad87537..9c6c55b0811 100644
--- a/compiler/rustc_mir_transform/src/remove_zsts.rs
+++ b/compiler/rustc_mir_transform/src/remove_zsts.rs
@@ -102,7 +102,7 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'_, 'tcx> {
         let op_ty = operand.ty(self.local_decls, self.tcx);
         if self.known_to_be_zst(op_ty)
             && self.tcx.consider_optimizing(|| {
-                format!("RemoveZsts - Operand: {:?} Location: {:?}", operand, loc)
+                format!("RemoveZsts - Operand: {operand:?} Location: {loc:?}")
             })
         {
             *operand = Operand::Constant(Box::new(self.make_zst(op_ty)))
diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs
index 71aef53192f..de6db8ae6ae 100644
--- a/compiler/rustc_monomorphize/src/partitioning.rs
+++ b/compiler/rustc_monomorphize/src/partitioning.rs
@@ -394,7 +394,7 @@ fn merge_codegen_units<'tcx>(
         && codegen_units.iter().any(|cgu| cgu.size_estimate() < NON_INCR_MIN_CGU_SIZE)
     {
         // Sort small cgus to the back.
-        codegen_units.sort_by_cached_key(|cgu| cmp::Reverse(cgu.size_estimate()));
+        codegen_units.sort_by_key(|cgu| cmp::Reverse(cgu.size_estimate()));
 
         let mut smallest = codegen_units.pop().unwrap();
         let second_smallest = codegen_units.last_mut().unwrap();
@@ -1041,10 +1041,7 @@ fn debug_dump<'a, 'tcx: 'a>(tcx: TyCtxt<'tcx>, label: &str, cgus: &[CodegenUnit<
             }
             elem(curr, curr_count);
 
-            let mut s = "[".to_string();
-            s.push_str(&v.join(", "));
-            s.push_str("]");
-            s
+            format!("[{}]", v.join(", "))
         }
     };
 
@@ -1229,12 +1226,13 @@ fn dump_mono_items_stats<'tcx>(
     // Gather instantiated mono items grouped by def_id
     let mut items_per_def_id: FxHashMap<_, Vec<_>> = Default::default();
     for cgu in codegen_units {
-        for (&mono_item, _) in cgu.items() {
+        cgu.items()
+            .keys()
             // Avoid variable-sized compiler-generated shims
-            if mono_item.is_user_defined() {
+            .filter(|mono_item| mono_item.is_user_defined())
+            .for_each(|mono_item| {
                 items_per_def_id.entry(mono_item.def_id()).or_default().push(mono_item);
-            }
-        }
+            });
     }
 
     #[derive(serde::Serialize)]
@@ -1287,7 +1285,7 @@ fn codegened_and_inlined_items(tcx: TyCtxt<'_>, (): ()) -> &DefIdSet {
     let mut result = items.clone();
 
     for cgu in cgus {
-        for (item, _) in cgu.items() {
+        for item in cgu.items().keys() {
             if let MonoItem::Fn(ref instance) = item {
                 let did = instance.def_id();
                 if !visited.insert(did) {
diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs
index c6e6b46e455..1931ee5e528 100644
--- a/compiler/rustc_parse/src/lexer/mod.rs
+++ b/compiler/rustc_parse/src/lexer/mod.rs
@@ -9,8 +9,8 @@ use rustc_ast::tokenstream::TokenStream;
 use rustc_ast::util::unicode::contains_text_flow_control_chars;
 use rustc_errors::{error_code, Applicability, Diagnostic, DiagnosticBuilder, StashKey};
 use rustc_lexer::unescape::{self, EscapeError, Mode};
-use rustc_lexer::Cursor;
 use rustc_lexer::{Base, DocStyle, RawStrError};
+use rustc_lexer::{Cursor, LiteralKind};
 use rustc_session::lint::builtin::{
     RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX, TEXT_DIRECTION_CODEPOINT_IN_COMMENT,
 };
@@ -118,6 +118,7 @@ impl<'a> StringReader<'a> {
         let mut swallow_next_invalid = 0;
         // Skip trivial (whitespace & comments) tokens
         loop {
+            let str_before = self.cursor.as_str();
             let token = self.cursor.advance_token();
             let start = self.pos;
             self.pos = self.pos + BytePos(token.len);
@@ -165,10 +166,7 @@ impl<'a> StringReader<'a> {
                     continue;
                 }
                 rustc_lexer::TokenKind::Ident => {
-                    let sym = nfc_normalize(self.str_from(start));
-                    let span = self.mk_sp(start, self.pos);
-                    self.sess.symbol_gallery.insert(sym, span);
-                    token::Ident(sym, false)
+                    self.ident(start)
                 }
                 rustc_lexer::TokenKind::RawIdent => {
                     let sym = nfc_normalize(self.str_from(start + BytePos(2)));
@@ -182,10 +180,7 @@ impl<'a> StringReader<'a> {
                 }
                 rustc_lexer::TokenKind::UnknownPrefix => {
                     self.report_unknown_prefix(start);
-                    let sym = nfc_normalize(self.str_from(start));
-                    let span = self.mk_sp(start, self.pos);
-                    self.sess.symbol_gallery.insert(sym, span);
-                    token::Ident(sym, false)
+                    self.ident(start)
                 }
                 rustc_lexer::TokenKind::InvalidIdent
                     // Do not recover an identifier with emoji if the codepoint is a confusable
@@ -203,6 +198,27 @@ impl<'a> StringReader<'a> {
                         .push(span);
                     token::Ident(sym, false)
                 }
+                // split up (raw) c string literals to an ident and a string literal when edition < 2021.
+                rustc_lexer::TokenKind::Literal {
+                    kind: kind @ (LiteralKind::CStr { .. } | LiteralKind::RawCStr { .. }),
+                    suffix_start: _,
+                } if !self.mk_sp(start, self.pos).edition().at_least_rust_2021() => {
+                    let prefix_len = match kind {
+                        LiteralKind::CStr { .. } => 1,
+                        LiteralKind::RawCStr { .. } => 2,
+                        _ => unreachable!(),
+                    };
+
+                    // reset the state so that only the prefix ("c" or "cr")
+                    // was consumed.
+                    let lit_start = start + BytePos(prefix_len);
+                    self.pos = lit_start;
+                    self.cursor = Cursor::new(&str_before[prefix_len as usize..]);
+
+                    self.report_unknown_prefix(start);
+                    let prefix_span = self.mk_sp(start, lit_start);
+                    return (Token::new(self.ident(start), prefix_span), preceded_by_whitespace);
+                }
                 rustc_lexer::TokenKind::Literal { kind, suffix_start } => {
                     let suffix_start = start + BytePos(suffix_start);
                     let (kind, symbol) = self.cook_lexer_literal(start, suffix_start, kind);
@@ -317,6 +333,13 @@ impl<'a> StringReader<'a> {
         }
     }
 
+    fn ident(&self, start: BytePos) -> TokenKind {
+        let sym = nfc_normalize(self.str_from(start));
+        let span = self.mk_sp(start, self.pos);
+        self.sess.symbol_gallery.insert(sym, span);
+        token::Ident(sym, false)
+    }
+
     fn struct_fatal_span_char(
         &self,
         from_pos: BytePos,
diff --git a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs
index 461a34b67db..d1f852b1a40 100644
--- a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs
+++ b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs
@@ -27,7 +27,7 @@ pub(crate) fn emit_unescape_error(
         lit, span_with_quotes, mode, range, error
     );
     let last_char = || {
-        let c = lit[range.clone()].chars().rev().next().unwrap();
+        let c = lit[range.clone()].chars().next_back().unwrap();
         let span = span.with_lo(span.hi() - BytePos(c.len_utf8() as u32));
         (c, span)
     };
diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs
index b579da098d8..4cc03664b47 100644
--- a/compiler/rustc_parse/src/parser/attr_wrapper.rs
+++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs
@@ -107,7 +107,7 @@ impl ToAttrTokenStream for LazyAttrTokenStreamImpl {
         let tokens =
             std::iter::once((FlatToken::Token(self.start_token.0.clone()), self.start_token.1))
                 .chain((0..self.num_calls).map(|_| {
-                    let token = cursor_snapshot.next(cursor_snapshot.desugar_doc_comments);
+                    let token = cursor_snapshot.next();
                     (FlatToken::Token(token.0), token.1)
                 }))
                 .take(self.num_calls);
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 3ecdbc36248..b54cb8c5a0c 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -238,7 +238,7 @@ impl<'a> Parser<'a> {
                     _ => unreachable!(),
                 }
                 .into();
-                let invalid = format!("{}=", &sugg);
+                let invalid = format!("{sugg}=");
                 self.sess.emit_err(errors::InvalidComparisonOperator {
                     span: sp,
                     invalid: invalid.clone(),
@@ -3003,7 +3003,8 @@ impl<'a> Parser<'a> {
     fn is_do_catch_block(&self) -> bool {
         self.token.is_keyword(kw::Do)
             && self.is_keyword_ahead(1, &[kw::Catch])
-            && self.look_ahead(2, |t| *t == token::OpenDelim(Delimiter::Brace))
+            && self
+                .look_ahead(2, |t| *t == token::OpenDelim(Delimiter::Brace) || t.is_whole_block())
             && !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL)
     }
 
@@ -3013,7 +3014,8 @@ impl<'a> Parser<'a> {
 
     fn is_try_block(&self) -> bool {
         self.token.is_keyword(kw::Try)
-            && self.look_ahead(1, |t| *t == token::OpenDelim(Delimiter::Brace))
+            && self
+                .look_ahead(1, |t| *t == token::OpenDelim(Delimiter::Brace) || t.is_whole_block())
             && self.token.uninterpolated_span().at_least_rust_2018()
     }
 
@@ -3032,10 +3034,14 @@ impl<'a> Parser<'a> {
             && ((
                 // `async move {`
                 self.is_keyword_ahead(1, &[kw::Move])
-                    && self.look_ahead(2, |t| *t == token::OpenDelim(Delimiter::Brace))
+                    && self.look_ahead(2, |t| {
+                        *t == token::OpenDelim(Delimiter::Brace) || t.is_whole_block()
+                    })
             ) || (
                 // `async {`
-                self.look_ahead(1, |t| *t == token::OpenDelim(Delimiter::Brace))
+                self.look_ahead(1, |t| {
+                    *t == token::OpenDelim(Delimiter::Brace) || t.is_whole_block()
+                })
             ))
     }
 
diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs
index 8ab38c4fb8b..242c9d332bb 100644
--- a/compiler/rustc_parse/src/parser/generics.rs
+++ b/compiler/rustc_parse/src/parser/generics.rs
@@ -49,7 +49,7 @@ impl<'a> Parser<'a> {
             && self.check_ident()
         // `Const` followed by IDENT
         {
-            return Ok(self.recover_const_param_with_mistyped_const(preceding_attrs, ident)?);
+            return self.recover_const_param_with_mistyped_const(preceding_attrs, ident);
         }
 
         // Parse optional colon and param bounds.
diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs
index 2e1a61e634e..37b4c371c94 100644
--- a/compiler/rustc_parse/src/parser/mod.rs
+++ b/compiler/rustc_parse/src/parser/mod.rs
@@ -138,7 +138,6 @@ pub struct Parser<'a> {
     // Important: This must only be advanced from `bump` to ensure that
     // `token_cursor.num_next_calls` is updated properly.
     token_cursor: TokenCursor,
-    desugar_doc_comments: bool,
     /// This field is used to keep track of how many left angle brackets we have seen. This is
     /// required in order to detect extra leading left angle brackets (`<` characters) and error
     /// appropriately.
@@ -225,6 +224,9 @@ struct TokenCursor {
     // because it's the outermost token stream which never has delimiters.
     stack: Vec<(TokenTreeCursor, Delimiter, DelimSpan)>,
 
+    // We need to desugar doc comments from `/// foo` form into `#[doc =
+    // r"foo"]` form when parsing declarative macro inputs in `parse_tt`,
+    // because some declarative macros look for `doc` attributes.
     desugar_doc_comments: bool,
 
     // Counts the number of calls to `{,inlined_}next`.
@@ -255,33 +257,38 @@ struct TokenCursor {
 }
 
 impl TokenCursor {
-    fn next(&mut self, desugar_doc_comments: bool) -> (Token, Spacing) {
-        self.inlined_next(desugar_doc_comments)
+    fn next(&mut self) -> (Token, Spacing) {
+        self.inlined_next()
     }
 
     /// This always-inlined version should only be used on hot code paths.
     #[inline(always)]
-    fn inlined_next(&mut self, desugar_doc_comments: bool) -> (Token, Spacing) {
+    fn inlined_next(&mut self) -> (Token, Spacing) {
         loop {
             // FIXME: we currently don't return `Delimiter` open/close delims. To fix #67062 we will
             // need to, whereupon the `delim != Delimiter::Invisible` conditions below can be
             // removed.
             if let Some(tree) = self.tree_cursor.next_ref() {
                 match tree {
-                    &TokenTree::Token(ref token, spacing) => match (desugar_doc_comments, token) {
-                        (true, &Token { kind: token::DocComment(_, attr_style, data), span }) => {
-                            let desugared = self.desugar(attr_style, data, span);
-                            self.tree_cursor.replace_prev_and_rewind(desugared);
-                            // Continue to get the first token of the desugared doc comment.
-                        }
-                        _ => {
-                            debug_assert!(!matches!(
-                                token.kind,
-                                token::OpenDelim(_) | token::CloseDelim(_)
-                            ));
-                            return (token.clone(), spacing);
+                    &TokenTree::Token(ref token, spacing) => {
+                        match (self.desugar_doc_comments, token) {
+                            (
+                                true,
+                                &Token { kind: token::DocComment(_, attr_style, data), span },
+                            ) => {
+                                let desugared = self.desugar(attr_style, data, span);
+                                self.tree_cursor.replace_prev_and_rewind(desugared);
+                                // Continue to get the first token of the desugared doc comment.
+                            }
+                            _ => {
+                                debug_assert!(!matches!(
+                                    token.kind,
+                                    token::OpenDelim(_) | token::CloseDelim(_)
+                                ));
+                                return (token.clone(), spacing);
+                            }
                         }
-                    },
+                    }
                     &TokenTree::Delimited(sp, delim, ref tts) => {
                         let trees = tts.clone().into_trees();
                         self.stack.push((mem::replace(&mut self.tree_cursor, trees), delim, sp));
@@ -463,7 +470,6 @@ impl<'a> Parser<'a> {
                 desugar_doc_comments,
                 break_last_token: false,
             },
-            desugar_doc_comments,
             unmatched_angle_bracket_count: 0,
             max_angle_bracket_count: 0,
             last_unexpected_token_span: None,
@@ -1107,7 +1113,7 @@ impl<'a> Parser<'a> {
     pub fn bump(&mut self) {
         // Note: destructuring here would give nicer code, but it was found in #96210 to be slower
         // than `.0`/`.1` access.
-        let mut next = self.token_cursor.inlined_next(self.desugar_doc_comments);
+        let mut next = self.token_cursor.inlined_next();
         self.token_cursor.num_next_calls += 1;
         // We've retrieved an token from the underlying
         // cursor, so we no longer need to worry about
@@ -1157,7 +1163,7 @@ impl<'a> Parser<'a> {
         let mut i = 0;
         let mut token = Token::dummy();
         while i < dist {
-            token = cursor.next(/* desugar_doc_comments */ false).0;
+            token = cursor.next().0;
             if matches!(
                 token.kind,
                 token::OpenDelim(Delimiter::Invisible) | token::CloseDelim(Delimiter::Invisible)
diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs
index 928fdce313d..2011083019c 100644
--- a/compiler/rustc_parse/src/validate_attr.rs
+++ b/compiler/rustc_parse/src/validate_attr.rs
@@ -157,15 +157,15 @@ fn emit_malformed_attribute(
         matches!(name, sym::doc | sym::ignore | sym::inline | sym::link | sym::test | sym::bench)
     };
 
-    let error_msg = format!("malformed `{}` attribute input", name);
+    let error_msg = format!("malformed `{name}` attribute input");
     let mut msg = "attribute must be of the form ".to_owned();
     let mut suggestions = vec![];
     let mut first = true;
     let inner = if style == ast::AttrStyle::Inner { "!" } else { "" };
     if template.word {
         first = false;
-        let code = format!("#{}[{}]", inner, name);
-        msg.push_str(&format!("`{}`", &code));
+        let code = format!("#{inner}[{name}]");
+        msg.push_str(&format!("`{code}`"));
         suggestions.push(code);
     }
     if let Some(descr) = template.list {
@@ -173,16 +173,16 @@ fn emit_malformed_attribute(
             msg.push_str(" or ");
         }
         first = false;
-        let code = format!("#{}[{}({})]", inner, name, descr);
-        msg.push_str(&format!("`{}`", &code));
+        let code = format!("#{inner}[{name}({descr})]");
+        msg.push_str(&format!("`{code}`"));
         suggestions.push(code);
     }
     if let Some(descr) = template.name_value_str {
         if !first {
             msg.push_str(" or ");
         }
-        let code = format!("#{}[{} = \"{}\"]", inner, name, descr);
-        msg.push_str(&format!("`{}`", &code));
+        let code = format!("#{inner}[{name} = \"{descr}\"]");
+        msg.push_str(&format!("`{code}`"));
         suggestions.push(code);
     }
     if should_warn(name) {
diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl
index 0aa3d6265dc..5b3cc5d99cc 100644
--- a/compiler/rustc_passes/messages.ftl
+++ b/compiler/rustc_passes/messages.ftl
@@ -211,6 +211,17 @@ passes_doc_keyword_not_mod =
 passes_doc_keyword_only_impl =
     `#[doc(keyword = "...")]` should be used on impl blocks
 
+passes_doc_masked_not_extern_crate_self =
+    this attribute cannot be applied to an `extern crate self` item
+    .label = not applicable on `extern crate self` items
+    .extern_crate_self_label = `extern crate self` defined here
+
+passes_doc_masked_only_extern_crate =
+    this attribute can only be applied to an `extern crate` item
+    .label = only applicable on `extern crate` items
+    .not_an_extern_crate_label = not an `extern crate` item
+    .note = read <https://doc.rust-lang.org/unstable-book/language-features/doc-masked.html> for more information
+
 passes_doc_test_literal = `#![doc(test(...)]` does not take a literal
 
 passes_doc_test_takes_list =
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index 4d7ebe3fefe..cbb030958c6 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -878,6 +878,44 @@ impl CheckAttrVisitor<'_> {
         }
     }
 
+    fn check_doc_masked(
+        &self,
+        attr: &Attribute,
+        meta: &NestedMetaItem,
+        hir_id: HirId,
+        target: Target,
+    ) -> bool {
+        if target != Target::ExternCrate {
+            self.tcx.emit_spanned_lint(
+                INVALID_DOC_ATTRIBUTES,
+                hir_id,
+                meta.span(),
+                errors::DocMaskedOnlyExternCrate {
+                    attr_span: meta.span(),
+                    item_span: (attr.style == AttrStyle::Outer)
+                        .then(|| self.tcx.hir().span(hir_id)),
+                },
+            );
+            return false;
+        }
+
+        if self.tcx.extern_mod_stmt_cnum(hir_id.owner).is_none() {
+            self.tcx.emit_spanned_lint(
+                INVALID_DOC_ATTRIBUTES,
+                hir_id,
+                meta.span(),
+                errors::DocMaskedNotExternCrateSelf {
+                    attr_span: meta.span(),
+                    item_span: (attr.style == AttrStyle::Outer)
+                        .then(|| self.tcx.hir().span(hir_id)),
+                },
+            );
+            return false;
+        }
+
+        true
+    }
+
     /// Checks that an attribute is *not* used at the crate level. Returns `true` if valid.
     fn check_attr_not_crate_level(
         &self,
@@ -1048,6 +1086,17 @@ impl CheckAttrVisitor<'_> {
                             is_valid = false;
                         }
 
+                        sym::masked
+                            if !self.check_doc_masked(
+                                attr,
+                                meta,
+                                hir_id,
+                                target,
+                            ) =>
+                        {
+                            is_valid = false;
+                        }
+
                         // no_default_passes: deprecated
                         // passes: deprecated
                         // plugins: removed, but rustdoc warns about it itself
diff --git a/compiler/rustc_passes/src/check_const.rs b/compiler/rustc_passes/src/check_const.rs
index fc437c429fb..e70817d7b7c 100644
--- a/compiler/rustc_passes/src/check_const.rs
+++ b/compiler/rustc_passes/src/check_const.rs
@@ -157,10 +157,8 @@ impl<'tcx> CheckConstVisitor<'tcx> {
                 // is a pretty narrow case, however.
                 if tcx.sess.is_nightly_build() {
                     for gate in missing_secondary {
-                        let note = format!(
-                            "add `#![feature({})]` to the crate attributes to enable",
-                            gate,
-                        );
+                        let note =
+                            format!("add `#![feature({gate})]` to the crate attributes to enable",);
                         err.help(note);
                     }
                 }
diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs
index eae13f86049..4f5514372d1 100644
--- a/compiler/rustc_passes/src/errors.rs
+++ b/compiler/rustc_passes/src/errors.rs
@@ -267,6 +267,25 @@ pub struct DocInlineOnlyUse {
     pub item_span: Option<Span>,
 }
 
+#[derive(LintDiagnostic)]
+#[diag(passes_doc_masked_only_extern_crate)]
+#[note]
+pub struct DocMaskedOnlyExternCrate {
+    #[label]
+    pub attr_span: Span,
+    #[label(passes_not_an_extern_crate_label)]
+    pub item_span: Option<Span>,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(passes_doc_masked_not_extern_crate_self)]
+pub struct DocMaskedNotExternCrateSelf {
+    #[label]
+    pub attr_span: Span,
+    #[label(passes_extern_crate_self_label)]
+    pub item_span: Option<Span>,
+}
+
 #[derive(Diagnostic)]
 #[diag(passes_doc_attr_not_crate_level)]
 pub struct DocAttrNotCrateLevel<'a> {
diff --git a/compiler/rustc_passes/src/hir_id_validator.rs b/compiler/rustc_passes/src/hir_id_validator.rs
index 363e1743677..f825363ae39 100644
--- a/compiler/rustc_passes/src/hir_id_validator.rs
+++ b/compiler/rustc_passes/src/hir_id_validator.rs
@@ -89,9 +89,8 @@ impl<'a, 'hir> HirIdValidator<'a, 'hir> {
 
             self.error(|| {
                 format!(
-                    "ItemLocalIds not assigned densely in {}. \
-                Max ItemLocalId = {}, missing IDs = {:#?}; seen IDs = {:#?}",
-                    pretty_owner, max, missing_items, seen_items
+                    "ItemLocalIds not assigned densely in {pretty_owner}. \
+                Max ItemLocalId = {max}, missing IDs = {missing_items:#?}; seen IDs = {seen_items:#?}"
                 )
             });
         }
diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs
index 6c748147abe..5aa8aef6a85 100644
--- a/compiler/rustc_passes/src/hir_stats.rs
+++ b/compiler/rustc_passes/src/hir_stats.rs
@@ -126,12 +126,12 @@ impl<'k> StatCollector<'k> {
 
         let total_size = nodes.iter().map(|(_, node)| node.stats.count * node.stats.size).sum();
 
-        eprintln!("{} {}", prefix, title);
+        eprintln!("{prefix} {title}");
         eprintln!(
             "{} {:<18}{:>18}{:>14}{:>14}",
             prefix, "Name", "Accumulated Size", "Count", "Item Size"
         );
-        eprintln!("{} ----------------------------------------------------------------", prefix);
+        eprintln!("{prefix} ----------------------------------------------------------------");
 
         let percent = |m, n| (m * 100) as f64 / n as f64;
 
@@ -163,9 +163,9 @@ impl<'k> StatCollector<'k> {
                 }
             }
         }
-        eprintln!("{} ----------------------------------------------------------------", prefix);
+        eprintln!("{prefix} ----------------------------------------------------------------");
         eprintln!("{} {:<18}{:>10}", prefix, "Total", to_readable_str(total_size));
-        eprintln!("{}", prefix);
+        eprintln!("{prefix}");
     }
 }
 
diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs
index 89c6704e416..15757a0f1ad 100644
--- a/compiler/rustc_passes/src/liveness.rs
+++ b/compiler/rustc_passes/src/liveness.rs
@@ -605,7 +605,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
         for var_idx in 0..self.ir.var_kinds.len() {
             let var = Variable::from(var_idx);
             if test(var) {
-                write!(wr, " {:?}", var)?;
+                write!(wr, " {var:?}")?;
             }
         }
         Ok(())
diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs
index 12a3f2ac802..def6ac280b8 100644
--- a/compiler/rustc_query_impl/src/plumbing.rs
+++ b/compiler/rustc_query_impl/src/plumbing.rs
@@ -183,7 +183,7 @@ pub(super) fn encode_all_query_results<'tcx>(
     encoder: &mut CacheEncoder<'_, 'tcx>,
     query_result_index: &mut EncodedDepNodeIndex,
 ) {
-    for encode in super::ENCODE_QUERY_RESULTS.iter().copied().filter_map(|e| e) {
+    for encode in super::ENCODE_QUERY_RESULTS.iter().copied().flatten() {
         encode(tcx, encoder, query_result_index);
     }
 }
diff --git a/compiler/rustc_query_system/src/query/job.rs b/compiler/rustc_query_system/src/query/job.rs
index a53d1fcc69e..d2140161f1d 100644
--- a/compiler/rustc_query_system/src/query/job.rs
+++ b/compiler/rustc_query_system/src/query/job.rs
@@ -176,8 +176,7 @@ impl QueryJobId {
         while let Some(id) = current_id {
             let info = query_map.get(&id).unwrap();
             // FIXME: This string comparison should probably not be done.
-            let query_name = format!("{:?}", info.query.dep_kind);
-            if query_name == "layout_of" || query_name == "naive_layout_of" {
+            if format!("{:?}", info.query.dep_kind) == "layout_of" {
                 depth += 1;
                 last_layout = Some((info.clone(), depth));
             }
diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs
index 2cfde2f62d8..73cd596c076 100644
--- a/compiler/rustc_resolve/src/diagnostics.rs
+++ b/compiler/rustc_resolve/src/diagnostics.rs
@@ -1395,7 +1395,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
             let head_span = source_map.guess_head_span(span);
             err.subdiagnostic(ConsiderAddingADerive {
                 span: head_span.shrink_to_lo(),
-                suggestion: format!("#[derive(Default)]\n")
+                suggestion: "#[derive(Default)]\n".to_string(),
             });
         }
         for ns in [Namespace::MacroNS, Namespace::TypeNS, Namespace::ValueNS] {
@@ -1718,7 +1718,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
             if next_binding.is_none() && let Some(span) = non_exhaustive {
                 note_span.push_span_label(
                     span,
-                    format!("cannot be constructed because it is `#[non_exhaustive]`"),
+                    "cannot be constructed because it is `#[non_exhaustive]`",
                 );
             }
             err.span_note(note_span, msg);
diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs
index 526fc9c3aa5..f3cf61c5b93 100644
--- a/compiler/rustc_resolve/src/imports.rs
+++ b/compiler/rustc_resolve/src/imports.rs
@@ -989,14 +989,17 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                             initial_binding.res()
                         });
                         let res = binding.res();
+                        if res == Res::Err || !this.ambiguity_errors.is_empty() {
+                            this.tcx
+                                .sess
+                                .delay_span_bug(import.span, "some error happened for an import");
+                            return;
+                        }
                         if let Ok(initial_res) = initial_res {
-                            if res != initial_res && this.ambiguity_errors.is_empty() {
+                            if res != initial_res {
                                 span_bug!(import.span, "inconsistent resolution for an import");
                             }
-                        } else if res != Res::Err
-                            && this.ambiguity_errors.is_empty()
-                            && this.privacy_errors.is_empty()
-                        {
+                        } else if this.privacy_errors.is_empty() {
                             this.tcx
                                 .sess
                                 .create_err(CannotDetermineImportResolution { span: import.span })
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index 05128a51016..aab71494fd3 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -549,6 +549,7 @@ enum MaybeExported<'a> {
     Ok(NodeId),
     Impl(Option<DefId>),
     ImplItem(Result<DefId, &'a Visibility>),
+    NestedUse(&'a Visibility),
 }
 
 impl MaybeExported<'_> {
@@ -559,7 +560,9 @@ impl MaybeExported<'_> {
                 trait_def_id.as_local()
             }
             MaybeExported::Impl(None) => return true,
-            MaybeExported::ImplItem(Err(vis)) => return vis.kind.is_pub(),
+            MaybeExported::ImplItem(Err(vis)) | MaybeExported::NestedUse(vis) => {
+                return vis.kind.is_pub();
+            }
         };
         def_id.map_or(true, |def_id| r.effective_visibilities.is_exported(def_id))
     }
@@ -2284,7 +2287,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
     fn resolve_item(&mut self, item: &'ast Item) {
         let mod_inner_docs =
             matches!(item.kind, ItemKind::Mod(..)) && rustdoc::inner_docs(&item.attrs);
-        if !mod_inner_docs && !matches!(item.kind, ItemKind::Impl(..)) {
+        if !mod_inner_docs && !matches!(item.kind, ItemKind::Impl(..) | ItemKind::Use(..)) {
             self.resolve_doc_links(&item.attrs, MaybeExported::Ok(item.id));
         }
 
@@ -2428,6 +2431,12 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
             }
 
             ItemKind::Use(ref use_tree) => {
+                let maybe_exported = match use_tree.kind {
+                    UseTreeKind::Simple(_) | UseTreeKind::Glob => MaybeExported::Ok(item.id),
+                    UseTreeKind::Nested(_) => MaybeExported::NestedUse(&item.vis),
+                };
+                self.resolve_doc_links(&item.attrs, maybe_exported);
+
                 self.future_proof_import(use_tree);
             }
 
@@ -2569,7 +2578,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
             let res = match kind {
                 RibKind::Item(..) | RibKind::AssocItem => Res::Def(def_kind, def_id.to_def_id()),
                 RibKind::Normal => {
-                    if self.r.tcx.sess.features_untracked().non_lifetime_binders {
+                    if self.r.tcx.features().non_lifetime_binders {
                         Res::Def(def_kind, def_id.to_def_id())
                     } else {
                         Res::Err
diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs
index d16b7902f60..d456cc9a9a0 100644
--- a/compiler/rustc_resolve/src/macros.rs
+++ b/compiler/rustc_resolve/src/macros.rs
@@ -24,7 +24,9 @@ use rustc_hir::def_id::{CrateNum, LocalDefId};
 use rustc_middle::middle::stability;
 use rustc_middle::ty::RegisteredTools;
 use rustc_middle::ty::TyCtxt;
-use rustc_session::lint::builtin::{LEGACY_DERIVE_HELPERS, SOFT_UNSTABLE};
+use rustc_session::lint::builtin::{
+    LEGACY_DERIVE_HELPERS, SOFT_UNSTABLE, UNKNOWN_DIAGNOSTIC_ATTRIBUTES,
+};
 use rustc_session::lint::builtin::{UNUSED_MACROS, UNUSED_MACRO_RULES};
 use rustc_session::lint::BuiltinLintDiagnostics;
 use rustc_session::parse::feature_err;
@@ -140,9 +142,9 @@ pub(crate) fn registered_tools(tcx: TyCtxt<'_>, (): ()) -> RegisteredTools {
             }
         }
     }
-    // We implicitly add `rustfmt` and `clippy` to known tools,
+    // We implicitly add `rustfmt`, `clippy`, `diagnostic` to known tools,
     // but it's not an error to register them explicitly.
-    let predefined_tools = [sym::clippy, sym::rustfmt];
+    let predefined_tools = [sym::clippy, sym::rustfmt, sym::diagnostic];
     registered_tools.extend(predefined_tools.iter().cloned().map(Ident::with_dummy_span));
     registered_tools
 }
@@ -576,10 +578,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         }
 
         // We are trying to avoid reporting this error if other related errors were reported.
-        if res != Res::Err
-            && inner_attr
-            && !self.tcx.sess.features_untracked().custom_inner_attributes
-        {
+        if res != Res::Err && inner_attr && !self.tcx.features().custom_inner_attributes {
             let msg = match res {
                 Res::Def(..) => "inner macro attributes are unstable",
                 Res::NonMacroAttr(..) => "custom inner attributes are unstable",
@@ -598,6 +597,18 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
             }
         }
 
+        if res == Res::NonMacroAttr(NonMacroAttrKind::Tool)
+            && path.segments.len() >= 2
+            && path.segments[0].ident.name == sym::diagnostic
+        {
+            self.tcx.sess.parse_sess.buffer_lint(
+                UNKNOWN_DIAGNOSTIC_ATTRIBUTES,
+                path.segments[1].span(),
+                node_id,
+                "unknown diagnostic attribute",
+            );
+        }
+
         Ok((ext, res))
     }
 
diff --git a/compiler/rustc_session/Cargo.toml b/compiler/rustc_session/Cargo.toml
index 1291d1454a6..e26d25d9a41 100644
--- a/compiler/rustc_session/Cargo.toml
+++ b/compiler/rustc_session/Cargo.toml
@@ -4,7 +4,6 @@ version = "0.0.0"
 edition = "2021"
 
 [dependencies]
-atty = "0.2.13"
 bitflags = "1.2.1"
 getopts = "0.2"
 rustc_macros = { path = "../rustc_macros" }
diff --git a/compiler/rustc_session/messages.ftl b/compiler/rustc_session/messages.ftl
index ee24c6d902f..d4042a2e61a 100644
--- a/compiler/rustc_session/messages.ftl
+++ b/compiler/rustc_session/messages.ftl
@@ -45,6 +45,7 @@ session_int_literal_too_large = integer literal is too large
     .note = value exceeds limit of `{$limit}`
 
 session_invalid_character_in_create_name = invalid character `{$character}` in crate name: `{$crate_name}`
+session_invalid_character_in_create_name_help = you can either pass `--crate-name` on the command line or add `#![crate_name="…"]` to set the crate name
 
 session_invalid_float_literal_suffix = invalid suffix `{$suffix}` for float literal
     .label = invalid suffix `{$suffix}`
diff --git a/compiler/rustc_session/src/code_stats.rs b/compiler/rustc_session/src/code_stats.rs
index cabe1c96bf7..df81e1f8305 100644
--- a/compiler/rustc_session/src/code_stats.rs
+++ b/compiler/rustc_session/src/code_stats.rs
@@ -227,10 +227,8 @@ impl CodeStats {
     }
 
     pub fn print_vtable_sizes(&self, crate_name: &str) {
-        let mut infos = std::mem::take(&mut *self.vtable_sizes.lock())
-            .into_iter()
-            .map(|(_did, stats)| stats)
-            .collect::<Vec<_>>();
+        let mut infos =
+            std::mem::take(&mut *self.vtable_sizes.lock()).into_values().collect::<Vec<_>>();
 
         // Primary sort: cost % in reverse order (from largest to smallest)
         // Secondary sort: trait_name
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index 1766e97b67d..88bbadefe26 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -279,11 +279,11 @@ impl LinkSelfContained {
         // set of all values like `y` or `n` used to be. Therefore, if this flag had previously been
         // set in bulk with its historical values, then manually setting a component clears that
         // `explicitly_set` state.
-        if let Some(component_to_enable) = component.strip_prefix("+") {
+        if let Some(component_to_enable) = component.strip_prefix('+') {
             self.explicitly_set = None;
             self.components.insert(component_to_enable.parse()?);
             Ok(())
-        } else if let Some(component_to_disable) = component.strip_prefix("-") {
+        } else if let Some(component_to_disable) = component.strip_prefix('-') {
             self.explicitly_set = None;
             self.components.remove(component_to_disable.parse()?);
             Ok(())
@@ -834,9 +834,10 @@ impl OutFileName {
     }
 
     pub fn is_tty(&self) -> bool {
+        use std::io::IsTerminal;
         match *self {
             OutFileName::Real(_) => false,
-            OutFileName::Stdout => atty::is(atty::Stream::Stdout),
+            OutFileName::Stdout => std::io::stdout().is_terminal(),
         }
     }
 
@@ -2151,6 +2152,12 @@ fn collect_print_requests(
     prints.extend(matches.opt_strs("print").into_iter().map(|req| {
         let (req, out) = split_out_file_name(&req);
 
+        if out.is_some() && !unstable_opts.unstable_options {
+            handler.early_error(
+                "the `-Z unstable-options` flag must also be passed to \
+                 enable the path print option",
+            );
+        }
         let kind = match PRINT_KINDS.iter().find(|&&(name, _)| name == req) {
             Some((_, PrintKind::TargetSpec)) => {
                 if unstable_opts.unstable_options {
@@ -3117,7 +3124,6 @@ pub(crate) mod dep_tracking {
     use rustc_feature::UnstableFeatures;
     use rustc_span::edition::Edition;
     use rustc_span::RealFileName;
-    use rustc_target::abi::ReferenceNichePolicy;
     use rustc_target::spec::{CodeModel, MergeFunctions, PanicStrategy, RelocModel};
     use rustc_target::spec::{
         RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, TargetTriple, TlsModel,
@@ -3213,7 +3219,6 @@ pub(crate) mod dep_tracking {
         OomStrategy,
         LanguageIdentifier,
         TraitSolver,
-        ReferenceNichePolicy,
     );
 
     impl<T1, T2> DepTrackingHash for (T1, T2)
diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs
index dd15ad45145..1ffee01b2f1 100644
--- a/compiler/rustc_session/src/errors.rs
+++ b/compiler/rustc_session/src/errors.rs
@@ -199,6 +199,14 @@ pub struct InvalidCharacterInCrateName {
     pub span: Option<Span>,
     pub character: char,
     pub crate_name: Symbol,
+    #[subdiagnostic]
+    pub crate_name_help: Option<InvalidCrateNameHelp>,
+}
+
+#[derive(Subdiagnostic)]
+pub enum InvalidCrateNameHelp {
+    #[help(session_invalid_character_in_create_name_help)]
+    AddCrateName,
 }
 
 #[derive(Subdiagnostic)]
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index 0c66121c72f..4a535f80b8a 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -6,7 +6,6 @@ use crate::{lint, EarlyErrorHandler};
 use rustc_data_structures::profiling::TimePassesFormat;
 use rustc_errors::ColorConfig;
 use rustc_errors::{LanguageIdentifier, TerminalUrl};
-use rustc_target::abi::ReferenceNichePolicy;
 use rustc_target::spec::{CodeModel, LinkerFlavorCli, MergeFunctions, PanicStrategy, SanitizerSet};
 use rustc_target::spec::{
     RelocModel, RelroLevel, SplitDebuginfo, StackProtector, TargetTriple, TlsModel,
@@ -422,8 +421,6 @@ mod desc {
     pub const parse_proc_macro_execution_strategy: &str =
         "one of supported execution strategies (`same-thread`, or `cross-thread`)";
     pub const parse_dump_solver_proof_tree: &str = "one of: `always`, `on-request`, `on-error`";
-    pub const parse_opt_reference_niches: &str =
-        "`null`, or a `,` separated combination of `size` or `align`";
 }
 
 mod parse {
@@ -1148,7 +1145,7 @@ mod parse {
         }
 
         // 2. Parse a list of enabled and disabled components.
-        for comp in s.split(",") {
+        for comp in s.split(',') {
             if slot.handle_cli_component(comp).is_err() {
                 return false;
             }
@@ -1256,31 +1253,6 @@ mod parse {
         };
         true
     }
-
-    pub(crate) fn parse_opt_reference_niches(
-        slot: &mut Option<ReferenceNichePolicy>,
-        v: Option<&str>,
-    ) -> bool {
-        let Some(s) = v else {
-            return false;
-        };
-
-        let slot = slot.get_or_insert_default();
-
-        if s == "null" {
-            return true;
-        }
-
-        for opt in s.split(",") {
-            match opt {
-                "size" => slot.size = true,
-                "align" => slot.align = true,
-                _ => return false,
-            }
-        }
-
-        true
-    }
 }
 
 options! {
@@ -1461,8 +1433,6 @@ options! {
     dep_tasks: bool = (false, parse_bool, [UNTRACKED],
         "print tasks that execute and the color their dep node gets (requires debug build) \
         (default: no)"),
-    diagnostic_width: Option<usize> = (None, parse_opt_number, [UNTRACKED],
-        "set the current output width for diagnostic truncation"),
     dont_buffer_diagnostics: bool = (false, parse_bool, [UNTRACKED],
         "emit diagnostics rather than buffering (breaks NLL error downgrading, sorting) \
         (default: no)"),
@@ -1729,8 +1699,6 @@ options! {
         "enable queries of the dependency graph for regression testing (default: no)"),
     randomize_layout: bool = (false, parse_bool, [TRACKED],
         "randomize the layout of types (default: no)"),
-    reference_niches: Option<ReferenceNichePolicy> = (None, parse_opt_reference_niches, [TRACKED],
-        "override the set of discriminant niches that may be exposed by references"),
     relax_elf_relocations: Option<bool> = (None, parse_opt_bool, [TRACKED],
         "whether ELF relocations can be relaxed"),
     relro_level: Option<RelroLevel> = (None, parse_relro_level, [TRACKED],
@@ -1908,6 +1876,8 @@ written to standard error output)"),
         Requires `-Clto[=[fat,yes]]`"),
     wasi_exec_model: Option<WasiExecModel> = (None, parse_wasi_exec_model, [TRACKED],
         "whether to build a wasi command or reactor"),
+    write_long_types_to_disk: bool = (true, parse_bool, [UNTRACKED],
+        "whether long type names should be written to files instead of being printed in errors"),
     // tidy-alphabetical-end
 
     // If you add a new option, please update:
diff --git a/compiler/rustc_session/src/output.rs b/compiler/rustc_session/src/output.rs
index 2088744bc5b..c0884fb21cd 100644
--- a/compiler/rustc_session/src/output.rs
+++ b/compiler/rustc_session/src/output.rs
@@ -2,7 +2,7 @@
 use crate::config::{CrateType, Input, OutFileName, OutputFilenames, OutputType};
 use crate::errors::{
     CrateNameDoesNotMatch, CrateNameEmpty, CrateNameInvalid, FileIsNotWriteable,
-    InvalidCharacterInCrateName,
+    InvalidCharacterInCrateName, InvalidCrateNameHelp,
 };
 use crate::Session;
 use rustc_ast::{self as ast, attr};
@@ -101,7 +101,16 @@ pub fn validate_crate_name(sess: &Session, s: Symbol, sp: Option<Span>) {
                 continue;
             }
             err_count += 1;
-            sess.emit_err(InvalidCharacterInCrateName { span: sp, character: c, crate_name: s });
+            sess.emit_err(InvalidCharacterInCrateName {
+                span: sp,
+                character: c,
+                crate_name: s,
+                crate_name_help: if sp.is_none() {
+                    Some(InvalidCrateNameHelp::AddCrateName)
+                } else {
+                    None
+                },
+            });
         }
     }
 
diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs
index b0a67c564ce..bca49981668 100644
--- a/compiler/rustc_session/src/parse.rs
+++ b/compiler/rustc_session/src/parse.rs
@@ -9,7 +9,7 @@ use crate::lint::{
 use rustc_ast::node_id::NodeId;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
 use rustc_data_structures::sync::{AppendOnlyVec, AtomicBool, Lock, Lrc};
-use rustc_errors::{emitter::SilentEmitter, ColorConfig, Handler};
+use rustc_errors::{emitter::SilentEmitter, Handler};
 use rustc_errors::{
     fallback_fluent_bundle, Diagnostic, DiagnosticBuilder, DiagnosticId, DiagnosticMessage,
     EmissionGuarantee, ErrorGuaranteed, IntoDiagnostic, MultiSpan, Noted, StashKey,
@@ -224,15 +224,7 @@ impl ParseSess {
     pub fn new(locale_resources: Vec<&'static str>, file_path_mapping: FilePathMapping) -> Self {
         let fallback_bundle = fallback_fluent_bundle(locale_resources, false);
         let sm = Lrc::new(SourceMap::new(file_path_mapping));
-        let handler = Handler::with_tty_emitter(
-            ColorConfig::Auto,
-            true,
-            None,
-            Some(sm.clone()),
-            None,
-            fallback_bundle,
-            None,
-        );
+        let handler = Handler::with_tty_emitter(Some(sm.clone()), fallback_bundle);
         ParseSess::with_span_handler(handler, sm)
     }
 
@@ -262,21 +254,9 @@ impl ParseSess {
     pub fn with_silent_emitter(fatal_note: Option<String>) -> Self {
         let fallback_bundle = fallback_fluent_bundle(Vec::new(), false);
         let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
-        let fatal_handler = Handler::with_tty_emitter(
-            ColorConfig::Auto,
-            false,
-            None,
-            None,
-            None,
-            fallback_bundle,
-            None,
-        );
-        let handler = Handler::with_emitter(
-            false,
-            None,
-            Box::new(SilentEmitter { fatal_handler, fatal_note }),
-            None,
-        );
+        let fatal_handler = Handler::with_tty_emitter(None, fallback_bundle).disable_warnings();
+        let handler = Handler::with_emitter(Box::new(SilentEmitter { fatal_handler, fatal_note }))
+            .disable_warnings();
         ParseSess::with_span_handler(handler, sm)
     }
 
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index 3c8be439111..ad22e7c703d 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -1442,11 +1442,11 @@ pub fn build_session(
     );
     let emitter = default_emitter(&sopts, registry, source_map.clone(), bundle, fallback_bundle);
 
-    let span_diagnostic = rustc_errors::Handler::with_emitter_and_flags(
-        emitter,
-        sopts.unstable_opts.diagnostic_handler_flags(can_emit_warnings),
-        ice_file,
-    );
+    let mut span_diagnostic = rustc_errors::Handler::with_emitter(emitter)
+        .with_flags(sopts.unstable_opts.diagnostic_handler_flags(can_emit_warnings));
+    if let Some(ice_file) = ice_file {
+        span_diagnostic = span_diagnostic.with_ice_file(ice_file);
+    }
 
     let self_profiler = if let SwitchWithOptPath::Enabled(ref d) = sopts.unstable_opts.self_profile
     {
@@ -1737,7 +1737,7 @@ pub struct EarlyErrorHandler {
 impl EarlyErrorHandler {
     pub fn new(output: ErrorOutputType) -> Self {
         let emitter = mk_emitter(output);
-        Self { handler: rustc_errors::Handler::with_emitter(true, None, emitter, None) }
+        Self { handler: rustc_errors::Handler::with_emitter(emitter) }
     }
 
     pub fn abort_if_errors(&self) {
@@ -1751,7 +1751,7 @@ impl EarlyErrorHandler {
         self.handler.abort_if_errors();
 
         let emitter = mk_emitter(output);
-        self.handler = Handler::with_emitter(true, None, emitter, None);
+        self.handler = Handler::with_emitter(emitter);
     }
 
     #[allow(rustc::untranslatable_diagnostic)]
diff --git a/compiler/rustc_session/src/utils.rs b/compiler/rustc_session/src/utils.rs
index 1d15e2c28d8..71f2591fe66 100644
--- a/compiler/rustc_session/src/utils.rs
+++ b/compiler/rustc_session/src/utils.rs
@@ -7,6 +7,7 @@ impl Session {
     pub fn timer(&self, what: &'static str) -> VerboseTimingGuard<'_> {
         self.prof.verbose_generic_activity(what)
     }
+    /// Used by `-Z self-profile`.
     pub fn time<R>(&self, what: &'static str, f: impl FnOnce() -> R) -> R {
         self.prof.verbose_generic_activity(what).run(f)
     }
diff --git a/compiler/rustc_smir/src/rustc_internal/mod.rs b/compiler/rustc_smir/src/rustc_internal/mod.rs
index a918bc981c0..6a26a4a22ed 100644
--- a/compiler/rustc_smir/src/rustc_internal/mod.rs
+++ b/compiler/rustc_smir/src/rustc_internal/mod.rs
@@ -47,6 +47,10 @@ pub fn generator_def(did: DefId) -> stable_mir::ty::GeneratorDef {
     with_tables(|t| t.generator_def(did))
 }
 
+pub fn alias_def(did: DefId) -> stable_mir::ty::AliasDef {
+    with_tables(|t| t.alias_def(did))
+}
+
 pub fn param_def(did: DefId) -> stable_mir::ty::ParamDef {
     with_tables(|t| t.param_def(did))
 }
@@ -55,6 +59,10 @@ pub fn br_named_def(did: DefId) -> stable_mir::ty::BrNamedDef {
     with_tables(|t| t.br_named_def(did))
 }
 
+pub fn trait_def(did: DefId) -> stable_mir::ty::TraitDef {
+    with_tables(|t| t.trait_def(did))
+}
+
 impl<'tcx> Tables<'tcx> {
     pub fn item_def_id(&self, item: &stable_mir::CrateItem) -> DefId {
         self.def_ids[item.0]
@@ -84,6 +92,10 @@ impl<'tcx> Tables<'tcx> {
         stable_mir::ty::GeneratorDef(self.create_def_id(did))
     }
 
+    pub fn alias_def(&mut self, did: DefId) -> stable_mir::ty::AliasDef {
+        stable_mir::ty::AliasDef(self.create_def_id(did))
+    }
+
     pub fn param_def(&mut self, did: DefId) -> stable_mir::ty::ParamDef {
         stable_mir::ty::ParamDef(self.create_def_id(did))
     }
@@ -92,6 +104,10 @@ impl<'tcx> Tables<'tcx> {
         stable_mir::ty::BrNamedDef(self.create_def_id(did))
     }
 
+    pub fn trait_def(&mut self, did: DefId) -> stable_mir::ty::TraitDef {
+        stable_mir::ty::TraitDef(self.create_def_id(did))
+    }
+
     fn create_def_id(&mut self, did: DefId) -> stable_mir::DefId {
         // FIXME: this becomes inefficient when we have too many ids
         for (i, &d) in self.def_ids.iter().enumerate() {
diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs
index c9da752ab77..0a33030c603 100644
--- a/compiler/rustc_smir/src/rustc_smir/mod.rs
+++ b/compiler/rustc_smir/src/rustc_smir/mod.rs
@@ -156,7 +156,9 @@ impl<'tcx> Stable<'tcx> for mir::Rvalue<'tcx> {
                 ops.0.stable(tables),
                 ops.1.stable(tables),
             ),
-            NullaryOp(_, _) => todo!(),
+            NullaryOp(null_op, ty) => {
+                stable_mir::mir::Rvalue::NullaryOp(null_op.stable(tables), tables.intern_ty(*ty))
+            }
             UnaryOp(un_op, op) => {
                 stable_mir::mir::Rvalue::UnaryOp(un_op.stable(tables), op.stable(tables))
             }
@@ -237,6 +239,93 @@ impl<'tcx> Stable<'tcx> for mir::CastKind {
     }
 }
 
+impl<'tcx> Stable<'tcx> for ty::AliasKind {
+    type T = stable_mir::ty::AliasKind;
+    fn stable(&self, _: &mut Tables<'tcx>) -> Self::T {
+        use ty::AliasKind::*;
+        match self {
+            Projection => stable_mir::ty::AliasKind::Projection,
+            Inherent => stable_mir::ty::AliasKind::Inherent,
+            Opaque => stable_mir::ty::AliasKind::Opaque,
+            Weak => stable_mir::ty::AliasKind::Weak,
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::AliasTy<'tcx> {
+    type T = stable_mir::ty::AliasTy;
+    fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
+        let ty::AliasTy { args, def_id, .. } = self;
+        stable_mir::ty::AliasTy { def_id: tables.alias_def(*def_id), args: args.stable(tables) }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::DynKind {
+    type T = stable_mir::ty::DynKind;
+
+    fn stable(&self, _: &mut Tables<'tcx>) -> Self::T {
+        use ty::DynKind;
+        match self {
+            DynKind::Dyn => stable_mir::ty::DynKind::Dyn,
+            DynKind::DynStar => stable_mir::ty::DynKind::DynStar,
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::ExistentialPredicate<'tcx> {
+    type T = stable_mir::ty::ExistentialPredicate;
+
+    fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
+        use stable_mir::ty::ExistentialPredicate::*;
+        match self {
+            ty::ExistentialPredicate::Trait(existential_trait_ref) => {
+                Trait(existential_trait_ref.stable(tables))
+            }
+            ty::ExistentialPredicate::Projection(existential_projection) => {
+                Projection(existential_projection.stable(tables))
+            }
+            ty::ExistentialPredicate::AutoTrait(def_id) => AutoTrait(tables.trait_def(*def_id)),
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::ExistentialTraitRef<'tcx> {
+    type T = stable_mir::ty::ExistentialTraitRef;
+
+    fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
+        let ty::ExistentialTraitRef { def_id, args } = self;
+        stable_mir::ty::ExistentialTraitRef {
+            def_id: tables.trait_def(*def_id),
+            generic_args: args.stable(tables),
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::TermKind<'tcx> {
+    type T = stable_mir::ty::TermKind;
+
+    fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
+        use stable_mir::ty::TermKind;
+        match self {
+            ty::TermKind::Ty(ty) => TermKind::Type(tables.intern_ty(*ty)),
+            ty::TermKind::Const(const_) => TermKind::Const(opaque(const_)),
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::ExistentialProjection<'tcx> {
+    type T = stable_mir::ty::ExistentialProjection;
+
+    fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
+        let ty::ExistentialProjection { def_id, args, term } = self;
+        stable_mir::ty::ExistentialProjection {
+            def_id: tables.trait_def(*def_id),
+            generic_args: args.stable(tables),
+            term: term.unpack().stable(tables),
+        }
+    }
+}
+
 impl<'tcx> Stable<'tcx> for ty::adjustment::PointerCoercion {
     type T = stable_mir::mir::PointerCoercion;
     fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
@@ -488,29 +577,36 @@ impl<'tcx> Stable<'tcx> for mir::Terminator<'tcx> {
 impl<'tcx> Stable<'tcx> for ty::GenericArgs<'tcx> {
     type T = stable_mir::ty::GenericArgs;
     fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
-        use stable_mir::ty::{GenericArgKind, GenericArgs};
-
-        GenericArgs(
-            self.iter()
-                .map(|arg| match arg.unpack() {
-                    ty::GenericArgKind::Lifetime(region) => {
-                        GenericArgKind::Lifetime(opaque(&region))
-                    }
-                    ty::GenericArgKind::Type(ty) => GenericArgKind::Type(tables.intern_ty(ty)),
-                    ty::GenericArgKind::Const(const_) => GenericArgKind::Const(opaque(&const_)),
-                })
-                .collect(),
-        )
+        use stable_mir::ty::GenericArgs;
+
+        GenericArgs(self.iter().map(|arg| arg.unpack().stable(tables)).collect())
     }
 }
 
-impl<'tcx> Stable<'tcx> for ty::PolyFnSig<'tcx> {
-    type T = stable_mir::ty::PolyFnSig;
+impl<'tcx> Stable<'tcx> for ty::GenericArgKind<'tcx> {
+    type T = stable_mir::ty::GenericArgKind;
+
+    fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
+        use stable_mir::ty::GenericArgKind;
+        match self {
+            ty::GenericArgKind::Lifetime(region) => GenericArgKind::Lifetime(opaque(region)),
+            ty::GenericArgKind::Type(ty) => GenericArgKind::Type(tables.intern_ty(*ty)),
+            ty::GenericArgKind::Const(const_) => GenericArgKind::Const(opaque(&const_)),
+        }
+    }
+}
+
+impl<'tcx, S, V> Stable<'tcx> for ty::Binder<'tcx, S>
+where
+    S: Stable<'tcx, T = V>,
+{
+    type T = stable_mir::ty::Binder<V>;
+
     fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
         use stable_mir::ty::Binder;
 
         Binder {
-            value: self.skip_binder().stable(tables),
+            value: self.as_ref().skip_binder().stable(tables),
             bound_vars: self
                 .bound_vars()
                 .iter()
@@ -568,63 +664,118 @@ impl<'tcx> Stable<'tcx> for ty::FnSig<'tcx> {
     }
 }
 
+impl<'tcx> Stable<'tcx> for ty::BoundTyKind {
+    type T = stable_mir::ty::BoundTyKind;
+
+    fn stable(&self, _: &mut Tables<'tcx>) -> Self::T {
+        use stable_mir::ty::BoundTyKind;
+
+        match self {
+            ty::BoundTyKind::Anon => BoundTyKind::Anon,
+            ty::BoundTyKind::Param(def_id, symbol) => {
+                BoundTyKind::Param(rustc_internal::param_def(*def_id), symbol.to_string())
+            }
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::BoundRegionKind {
+    type T = stable_mir::ty::BoundRegionKind;
+
+    fn stable(&self, _: &mut Tables<'tcx>) -> Self::T {
+        use stable_mir::ty::BoundRegionKind;
+
+        match self {
+            ty::BoundRegionKind::BrAnon(option_span) => {
+                BoundRegionKind::BrAnon(option_span.map(|span| opaque(&span)))
+            }
+            ty::BoundRegionKind::BrNamed(def_id, symbol) => {
+                BoundRegionKind::BrNamed(rustc_internal::br_named_def(*def_id), symbol.to_string())
+            }
+            ty::BoundRegionKind::BrEnv => BoundRegionKind::BrEnv,
+        }
+    }
+}
+
 impl<'tcx> Stable<'tcx> for ty::BoundVariableKind {
     type T = stable_mir::ty::BoundVariableKind;
-    fn stable(&self, _: &mut Tables<'tcx>) -> Self::T {
-        use stable_mir::ty::{BoundRegionKind, BoundTyKind, BoundVariableKind};
+
+    fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
+        use stable_mir::ty::BoundVariableKind;
 
         match self {
             ty::BoundVariableKind::Ty(bound_ty_kind) => {
-                BoundVariableKind::Ty(match bound_ty_kind {
-                    ty::BoundTyKind::Anon => BoundTyKind::Anon,
-                    ty::BoundTyKind::Param(def_id, symbol) => {
-                        BoundTyKind::Param(rustc_internal::param_def(*def_id), symbol.to_string())
-                    }
-                })
+                BoundVariableKind::Ty(bound_ty_kind.stable(tables))
             }
             ty::BoundVariableKind::Region(bound_region_kind) => {
-                BoundVariableKind::Region(match bound_region_kind {
-                    ty::BoundRegionKind::BrAnon(option_span) => {
-                        BoundRegionKind::BrAnon(option_span.map(|span| opaque(&span)))
-                    }
-                    ty::BoundRegionKind::BrNamed(def_id, symbol) => BoundRegionKind::BrNamed(
-                        rustc_internal::br_named_def(*def_id),
-                        symbol.to_string(),
-                    ),
-                    ty::BoundRegionKind::BrEnv => BoundRegionKind::BrEnv,
-                })
+                BoundVariableKind::Region(bound_region_kind.stable(tables))
             }
             ty::BoundVariableKind::Const => BoundVariableKind::Const,
         }
     }
 }
 
+impl<'tcx> Stable<'tcx> for ty::IntTy {
+    type T = IntTy;
+
+    fn stable(&self, _: &mut Tables<'tcx>) -> Self::T {
+        match self {
+            ty::IntTy::Isize => IntTy::Isize,
+            ty::IntTy::I8 => IntTy::I8,
+            ty::IntTy::I16 => IntTy::I16,
+            ty::IntTy::I32 => IntTy::I32,
+            ty::IntTy::I64 => IntTy::I64,
+            ty::IntTy::I128 => IntTy::I128,
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::UintTy {
+    type T = UintTy;
+
+    fn stable(&self, _: &mut Tables<'tcx>) -> Self::T {
+        match self {
+            ty::UintTy::Usize => UintTy::Usize,
+            ty::UintTy::U8 => UintTy::U8,
+            ty::UintTy::U16 => UintTy::U16,
+            ty::UintTy::U32 => UintTy::U32,
+            ty::UintTy::U64 => UintTy::U64,
+            ty::UintTy::U128 => UintTy::U128,
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::FloatTy {
+    type T = FloatTy;
+
+    fn stable(&self, _: &mut Tables<'tcx>) -> Self::T {
+        match self {
+            ty::FloatTy::F32 => FloatTy::F32,
+            ty::FloatTy::F64 => FloatTy::F64,
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for hir::Movability {
+    type T = Movability;
+
+    fn stable(&self, _: &mut Tables<'tcx>) -> Self::T {
+        match self {
+            hir::Movability::Static => Movability::Static,
+            hir::Movability::Movable => Movability::Movable,
+        }
+    }
+}
+
 impl<'tcx> Stable<'tcx> for Ty<'tcx> {
     type T = stable_mir::ty::TyKind;
     fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
         match self.kind() {
             ty::Bool => TyKind::RigidTy(RigidTy::Bool),
             ty::Char => TyKind::RigidTy(RigidTy::Char),
-            ty::Int(int_ty) => match int_ty {
-                ty::IntTy::Isize => TyKind::RigidTy(RigidTy::Int(IntTy::Isize)),
-                ty::IntTy::I8 => TyKind::RigidTy(RigidTy::Int(IntTy::I8)),
-                ty::IntTy::I16 => TyKind::RigidTy(RigidTy::Int(IntTy::I16)),
-                ty::IntTy::I32 => TyKind::RigidTy(RigidTy::Int(IntTy::I32)),
-                ty::IntTy::I64 => TyKind::RigidTy(RigidTy::Int(IntTy::I64)),
-                ty::IntTy::I128 => TyKind::RigidTy(RigidTy::Int(IntTy::I128)),
-            },
-            ty::Uint(uint_ty) => match uint_ty {
-                ty::UintTy::Usize => TyKind::RigidTy(RigidTy::Uint(UintTy::Usize)),
-                ty::UintTy::U8 => TyKind::RigidTy(RigidTy::Uint(UintTy::U8)),
-                ty::UintTy::U16 => TyKind::RigidTy(RigidTy::Uint(UintTy::U16)),
-                ty::UintTy::U32 => TyKind::RigidTy(RigidTy::Uint(UintTy::U32)),
-                ty::UintTy::U64 => TyKind::RigidTy(RigidTy::Uint(UintTy::U64)),
-                ty::UintTy::U128 => TyKind::RigidTy(RigidTy::Uint(UintTy::U128)),
-            },
-            ty::Float(float_ty) => match float_ty {
-                ty::FloatTy::F32 => TyKind::RigidTy(RigidTy::Float(FloatTy::F32)),
-                ty::FloatTy::F64 => TyKind::RigidTy(RigidTy::Float(FloatTy::F64)),
-            },
+            ty::Int(int_ty) => TyKind::RigidTy(RigidTy::Int(int_ty.stable(tables))),
+            ty::Uint(uint_ty) => TyKind::RigidTy(RigidTy::Uint(uint_ty.stable(tables))),
+            ty::Float(float_ty) => TyKind::RigidTy(RigidTy::Float(float_ty.stable(tables))),
             ty::Adt(adt_def, generic_args) => TyKind::RigidTy(RigidTy::Adt(
                 rustc_internal::adt_def(adt_def.did()),
                 generic_args.stable(tables),
@@ -650,7 +801,16 @@ impl<'tcx> Stable<'tcx> for Ty<'tcx> {
                 generic_args.stable(tables),
             )),
             ty::FnPtr(poly_fn_sig) => TyKind::RigidTy(RigidTy::FnPtr(poly_fn_sig.stable(tables))),
-            ty::Dynamic(_, _, _) => todo!(),
+            ty::Dynamic(existential_predicates, region, dyn_kind) => {
+                TyKind::RigidTy(RigidTy::Dynamic(
+                    existential_predicates
+                        .iter()
+                        .map(|existential_predicate| existential_predicate.stable(tables))
+                        .collect(),
+                    opaque(region),
+                    dyn_kind.stable(tables),
+                ))
+            }
             ty::Closure(def_id, generic_args) => TyKind::RigidTy(RigidTy::Closure(
                 rustc_internal::closure_def(*def_id),
                 generic_args.stable(tables),
@@ -658,18 +818,19 @@ impl<'tcx> Stable<'tcx> for Ty<'tcx> {
             ty::Generator(def_id, generic_args, movability) => TyKind::RigidTy(RigidTy::Generator(
                 rustc_internal::generator_def(*def_id),
                 generic_args.stable(tables),
-                match movability {
-                    hir::Movability::Static => Movability::Static,
-                    hir::Movability::Movable => Movability::Movable,
-                },
+                movability.stable(tables),
             )),
             ty::Never => TyKind::RigidTy(RigidTy::Never),
             ty::Tuple(fields) => TyKind::RigidTy(RigidTy::Tuple(
                 fields.iter().map(|ty| tables.intern_ty(ty)).collect(),
             )),
-            ty::Alias(_, _) => todo!(),
-            ty::Param(_) => todo!(),
-            ty::Bound(_, _) => todo!(),
+            ty::Alias(alias_kind, alias_ty) => {
+                TyKind::Alias(alias_kind.stable(tables), alias_ty.stable(tables))
+            }
+            ty::Param(param_ty) => TyKind::Param(param_ty.stable(tables)),
+            ty::Bound(debruijn_idx, bound_ty) => {
+                TyKind::Bound(debruijn_idx.as_usize(), bound_ty.stable(tables))
+            }
             ty::Placeholder(..)
             | ty::GeneratorWitness(_)
             | ty::GeneratorWitnessMIR(_, _)
@@ -680,3 +841,19 @@ impl<'tcx> Stable<'tcx> for Ty<'tcx> {
         }
     }
 }
+
+impl<'tcx> Stable<'tcx> for rustc_middle::ty::ParamTy {
+    type T = stable_mir::ty::ParamTy;
+    fn stable(&self, _: &mut Tables<'tcx>) -> Self::T {
+        use stable_mir::ty::ParamTy;
+        ParamTy { index: self.index, name: self.name.to_string() }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for rustc_middle::ty::BoundTy {
+    type T = stable_mir::ty::BoundTy;
+    fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
+        use stable_mir::ty::BoundTy;
+        BoundTy { var: self.var.as_usize(), kind: self.kind.stable(tables) }
+    }
+}
diff --git a/compiler/rustc_smir/src/stable_mir/mir/body.rs b/compiler/rustc_smir/src/stable_mir/mir/body.rs
index 831eb6589e4..1b1871bcd2a 100644
--- a/compiler/rustc_smir/src/stable_mir/mir/body.rs
+++ b/compiler/rustc_smir/src/stable_mir/mir/body.rs
@@ -218,6 +218,9 @@ pub enum Rvalue {
     /// nature of this operation?
     ThreadLocalRef(stable_mir::CrateItem),
 
+    /// Computes a value as described by the operation.
+    NullaryOp(NullOp, Ty),
+
     /// Exactly like `BinaryOp`, but less operands.
     ///
     /// Also does two's-complement arithmetic. Negation requires a signed integer or a float;
diff --git a/compiler/rustc_smir/src/stable_mir/ty.rs b/compiler/rustc_smir/src/stable_mir/ty.rs
index 2b762eab5ef..7a72afd666c 100644
--- a/compiler/rustc_smir/src/stable_mir/ty.rs
+++ b/compiler/rustc_smir/src/stable_mir/ty.rs
@@ -17,6 +17,9 @@ type Span = Opaque;
 #[derive(Clone, Debug)]
 pub enum TyKind {
     RigidTy(RigidTy),
+    Alias(AliasKind, AliasTy),
+    Param(ParamTy),
+    Bound(usize, BoundTy),
 }
 
 #[derive(Clone, Debug)]
@@ -37,6 +40,7 @@ pub enum RigidTy {
     FnPtr(PolyFnSig),
     Closure(ClosureDef, GenericArgs),
     Generator(GeneratorDef, GenericArgs, Movability),
+    Dynamic(Vec<Binder<ExistentialPredicate>>, Region, DynKind),
     Never,
     Tuple(Vec<Ty>),
 }
@@ -94,6 +98,12 @@ pub struct BrNamedDef(pub(crate) DefId);
 #[derive(Clone, PartialEq, Eq, Debug)]
 pub struct AdtDef(pub(crate) DefId);
 
+#[derive(Clone, PartialEq, Eq, Debug)]
+pub struct AliasDef(pub(crate) DefId);
+
+#[derive(Clone, PartialEq, Eq, Debug)]
+pub struct TraitDef(pub(crate) DefId);
+
 #[derive(Clone, Debug)]
 pub struct GenericArgs(pub Vec<GenericArgKind>);
 
@@ -104,6 +114,26 @@ pub enum GenericArgKind {
     Const(Const),
 }
 
+#[derive(Clone, Debug)]
+pub enum TermKind {
+    Type(Ty),
+    Const(Const),
+}
+
+#[derive(Clone, Debug)]
+pub enum AliasKind {
+    Projection,
+    Inherent,
+    Opaque,
+    Weak,
+}
+
+#[derive(Clone, Debug)]
+pub struct AliasTy {
+    pub def_id: AliasDef,
+    pub args: GenericArgs,
+}
+
 pub type PolyFnSig = Binder<FnSig>;
 
 #[derive(Clone, Debug)]
@@ -174,3 +204,41 @@ pub enum BoundRegionKind {
     BrNamed(BrNamedDef, String),
     BrEnv,
 }
+
+#[derive(Clone, Debug)]
+pub enum DynKind {
+    Dyn,
+    DynStar,
+}
+
+#[derive(Clone, Debug)]
+pub enum ExistentialPredicate {
+    Trait(ExistentialTraitRef),
+    Projection(ExistentialProjection),
+    AutoTrait(TraitDef),
+}
+
+#[derive(Clone, Debug)]
+pub struct ExistentialTraitRef {
+    pub def_id: TraitDef,
+    pub generic_args: GenericArgs,
+}
+
+#[derive(Clone, Debug)]
+pub struct ExistentialProjection {
+    pub def_id: TraitDef,
+    pub generic_args: GenericArgs,
+    pub term: TermKind,
+}
+
+#[derive(Clone, Debug)]
+pub struct ParamTy {
+    pub index: u32,
+    pub name: String,
+}
+
+#[derive(Clone, Debug)]
+pub struct BoundTy {
+    pub var: usize,
+    pub kind: BoundTyKind,
+}
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 08925761b39..1b426ef2048 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -372,6 +372,7 @@ symbols! {
         arm_target_feature,
         array,
         arrays,
+        as_mut_ptr,
         as_ptr,
         as_ref,
         as_str,
@@ -619,6 +620,7 @@ symbols! {
         destruct,
         destructuring_assignment,
         diagnostic,
+        diagnostic_namespace,
         direct,
         discriminant_kind,
         discriminant_type,
@@ -858,6 +860,7 @@ symbols! {
         item,
         item_like_imports,
         iter,
+        iter_mut,
         iter_repeat,
         iterator_collect_fn,
         kcfi,
diff --git a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs
index 4a938ff938a..845b5791161 100644
--- a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs
+++ b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs
@@ -7,10 +7,10 @@
 ///
 /// For more information about LLVM CFI and cross-language LLVM CFI support for the Rust compiler,
 /// see design document in the tracking issue #89653.
-use core::fmt::Display;
 use rustc_data_structures::base_n;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_hir as hir;
+use rustc_middle::ty::layout::IntegerExt;
 use rustc_middle::ty::{
     self, Const, ExistentialPredicate, FloatTy, FnSig, Instance, IntTy, List, Region, RegionKind,
     TermKind, Ty, TyCtxt, UintTy,
@@ -19,6 +19,7 @@ use rustc_middle::ty::{GenericArg, GenericArgKind, GenericArgsRef};
 use rustc_span::def_id::DefId;
 use rustc_span::sym;
 use rustc_target::abi::call::{Conv, FnAbi};
+use rustc_target::abi::Integer;
 use rustc_target::spec::abi::Abi;
 use std::fmt::Write as _;
 
@@ -93,44 +94,54 @@ fn encode_const<'tcx>(
     dict: &mut FxHashMap<DictKey<'tcx>, usize>,
     options: EncodeTyOptions,
 ) -> String {
-    // L<element-type>[n]<element-value>E as literal argument
+    // L<element-type>[n][<element-value>]E as literal argument
     let mut s = String::from('L');
 
-    // Element type
-    s.push_str(&encode_ty(tcx, c.ty(), dict, options));
+    match c.kind() {
+        // Const parameters
+        ty::ConstKind::Param(..) => {
+            // L<element-type>E as literal argument
 
-    // The only allowed types of const parameters are bool, u8, u16, u32, u64, u128, usize i8, i16,
-    // i32, i64, i128, isize, and char. The bool value false is encoded as 0 and true as 1.
-    fn push_signed_value<T: Display + PartialOrd>(s: &mut String, value: T, zero: T) {
-        if value < zero {
-            s.push('n')
-        };
-        let _ = write!(s, "{value}");
-    }
-
-    fn push_unsigned_value<T: Display>(s: &mut String, value: T) {
-        let _ = write!(s, "{value}");
-    }
+            // Element type
+            s.push_str(&encode_ty(tcx, c.ty(), dict, options));
+        }
 
-    if let Some(scalar_int) = c.try_to_scalar_int() {
-        let signed = c.ty().is_signed();
-        match scalar_int.size().bits() {
-            8 if signed => push_signed_value(&mut s, scalar_int.try_to_i8().unwrap(), 0),
-            16 if signed => push_signed_value(&mut s, scalar_int.try_to_i16().unwrap(), 0),
-            32 if signed => push_signed_value(&mut s, scalar_int.try_to_i32().unwrap(), 0),
-            64 if signed => push_signed_value(&mut s, scalar_int.try_to_i64().unwrap(), 0),
-            128 if signed => push_signed_value(&mut s, scalar_int.try_to_i128().unwrap(), 0),
-            8 => push_unsigned_value(&mut s, scalar_int.try_to_u8().unwrap()),
-            16 => push_unsigned_value(&mut s, scalar_int.try_to_u16().unwrap()),
-            32 => push_unsigned_value(&mut s, scalar_int.try_to_u32().unwrap()),
-            64 => push_unsigned_value(&mut s, scalar_int.try_to_u64().unwrap()),
-            128 => push_unsigned_value(&mut s, scalar_int.try_to_u128().unwrap()),
-            _ => {
-                bug!("encode_const: unexpected size `{:?}`", scalar_int.size().bits());
+        // Literal arguments
+        ty::ConstKind::Value(..) => {
+            // L<element-type>[n]<element-value>E as literal argument
+
+            // Element type
+            s.push_str(&encode_ty(tcx, c.ty(), dict, options));
+
+            // The only allowed types of const values are bool, u8, u16, u32,
+            // u64, u128, usize i8, i16, i32, i64, i128, isize, and char. The
+            // bool value false is encoded as 0 and true as 1.
+            match c.ty().kind() {
+                ty::Int(ity) => {
+                    let bits = c.eval_bits(tcx, ty::ParamEnv::reveal_all(), c.ty());
+                    let val = Integer::from_int_ty(&tcx, *ity).size().sign_extend(bits) as i128;
+                    if val < 0 {
+                        s.push('n');
+                    }
+                    let _ = write!(s, "{val}");
+                }
+                ty::Uint(_) => {
+                    let val = c.eval_bits(tcx, ty::ParamEnv::reveal_all(), c.ty());
+                    let _ = write!(s, "{val}");
+                }
+                ty::Bool => {
+                    let val = c.try_eval_bool(tcx, ty::ParamEnv::reveal_all()).unwrap();
+                    let _ = write!(s, "{val}");
+                }
+                _ => {
+                    bug!("encode_const: unexpected type `{:?}`", c.ty());
+                }
             }
-        };
-    } else {
-        bug!("encode_const: unexpected type `{:?}`", c.ty());
+        }
+
+        _ => {
+            bug!("encode_const: unexpected kind `{:?}`", c.kind());
+        }
     }
 
     // Close the "L..E" pair
@@ -400,7 +411,7 @@ fn encode_ty_name(tcx: TyCtxt<'_>, def_id: DefId) -> String {
         let _ = write!(s, "{}", name.len());
 
         // Prepend a '_' if name starts with a digit or '_'
-        if let Some(first) = name.as_bytes().get(0) {
+        if let Some(first) = name.as_bytes().first() {
             if first.is_ascii_digit() || *first == b'_' {
                 s.push('_');
             }
diff --git a/compiler/rustc_target/src/abi/mod.rs b/compiler/rustc_target/src/abi/mod.rs
index 11ba551dccc..084c917cc31 100644
--- a/compiler/rustc_target/src/abi/mod.rs
+++ b/compiler/rustc_target/src/abi/mod.rs
@@ -50,9 +50,6 @@ pub trait TyAbiInterface<'a, C>: Sized {
         this: TyAndLayout<'a, Self>,
         cx: &C,
         offset: Size,
-        // If true, assume that pointers are either null or valid (according to their type),
-        // enabling extra optimizations.
-        assume_valid_ptr: bool,
     ) -> Option<PointeeInfo>;
     fn is_adt(this: TyAndLayout<'a, Self>) -> bool;
     fn is_never(this: TyAndLayout<'a, Self>) -> bool;
@@ -79,8 +76,7 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
     where
         Ty: TyAbiInterface<'a, C>,
     {
-        let assume_valid_ptr = true;
-        Ty::ty_and_layout_pointee_info_at(self, cx, offset, assume_valid_ptr)
+        Ty::ty_and_layout_pointee_info_at(self, cx, offset)
     }
 
     pub fn is_single_fp_element<C>(self, cx: &C) -> bool
diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_hermit.rs b/compiler/rustc_target/src/spec/aarch64_unknown_hermit.rs
index 87e8d627026..2414867be55 100644
--- a/compiler/rustc_target/src/spec/aarch64_unknown_hermit.rs
+++ b/compiler/rustc_target/src/spec/aarch64_unknown_hermit.rs
@@ -1,15 +1,15 @@
-use crate::spec::Target;
+use crate::spec::{Target, TargetOptions};
 
 pub fn target() -> Target {
-    let mut base = super::hermit_base::opts();
-    base.max_atomic_width = Some(128);
-    base.features = "+v8a,+strict-align,+neon,+fp-armv8".into();
-
     Target {
         llvm_target: "aarch64-unknown-hermit".into(),
         pointer_width: 64,
-        data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".into(),
         arch: "aarch64".into(),
-        options: base,
+        data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".into(),
+        options: TargetOptions {
+            features: "+v8a,+strict-align,+neon,+fp-armv8".into(),
+            max_atomic_width: Some(128),
+            ..super::hermit_base::opts()
+        },
     }
 }
diff --git a/compiler/rustc_target/src/spec/armebv7r_none_eabi.rs b/compiler/rustc_target/src/spec/armebv7r_none_eabi.rs
index 5632bcfcefe..446efa90d09 100644
--- a/compiler/rustc_target/src/spec/armebv7r_none_eabi.rs
+++ b/compiler/rustc_target/src/spec/armebv7r_none_eabi.rs
@@ -5,7 +5,7 @@ use crate::spec::{Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, Targ
 
 pub fn target() -> Target {
     Target {
-        llvm_target: "armebv7r-unknown-none-eabi".into(),
+        llvm_target: "armebv7r-none-eabi".into(),
         pointer_width: 32,
         data_layout: "E-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(),
         arch: "arm".into(),
@@ -18,7 +18,7 @@ pub fn target() -> Target {
             panic_strategy: PanicStrategy::Abort,
             max_atomic_width: Some(64),
             emit_debug_gdb_scripts: false,
-            // GCC and Clang default to 8 for arm-none here
+            // GCC defaults to 8 for arm-none here.
             c_enum_min_bits: Some(8),
             ..Default::default()
         },
diff --git a/compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs b/compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs
index 2815de3589d..0c9e99ff84b 100644
--- a/compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs
+++ b/compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs
@@ -5,7 +5,7 @@ use crate::spec::{Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, Targ
 
 pub fn target() -> Target {
     Target {
-        llvm_target: "armebv7r-unknown-none-eabihf".into(),
+        llvm_target: "armebv7r-none-eabihf".into(),
         pointer_width: 32,
         data_layout: "E-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(),
         arch: "arm".into(),
@@ -19,7 +19,7 @@ pub fn target() -> Target {
             features: "+vfp3,-d32,-fp16".into(),
             max_atomic_width: Some(64),
             emit_debug_gdb_scripts: false,
-            // GCC and Clang default to 8 for arm-none here
+            // GCC defaults to 8 for arm-none here.
             c_enum_min_bits: Some(8),
             ..Default::default()
         },
diff --git a/compiler/rustc_target/src/spec/armv4t_none_eabi.rs b/compiler/rustc_target/src/spec/armv4t_none_eabi.rs
index d0f988b278f..44fdd3178af 100644
--- a/compiler/rustc_target/src/spec/armv4t_none_eabi.rs
+++ b/compiler/rustc_target/src/spec/armv4t_none_eabi.rs
@@ -39,9 +39,9 @@ pub fn target() -> Target {
             has_thumb_interworking: true,
             relocation_model: RelocModel::Static,
             panic_strategy: PanicStrategy::Abort,
-            // from thumb_base, rust-lang/rust#44993.
+            // From thumb_base, rust-lang/rust#44993.
             emit_debug_gdb_scripts: false,
-            // from thumb_base, apparently gcc/clang give enums a minimum of 8 bits on no-os targets
+            // From thumb_base, GCC gives enums a minimum of 8 bits on no-os targets.
             c_enum_min_bits: Some(8),
             ..Default::default()
         },
diff --git a/compiler/rustc_target/src/spec/armv7a_none_eabihf.rs b/compiler/rustc_target/src/spec/armv7a_none_eabihf.rs
index 8cdf3c36ba2..c134f3e0907 100644
--- a/compiler/rustc_target/src/spec/armv7a_none_eabihf.rs
+++ b/compiler/rustc_target/src/spec/armv7a_none_eabihf.rs
@@ -18,7 +18,7 @@ pub fn target() -> Target {
         max_atomic_width: Some(64),
         panic_strategy: PanicStrategy::Abort,
         emit_debug_gdb_scripts: false,
-        // GCC and Clang default to 8 for arm-none here
+        // GCC defaults to 8 for arm-none here.
         c_enum_min_bits: Some(8),
         ..Default::default()
     };
diff --git a/compiler/rustc_target/src/spec/armv7r_none_eabi.rs b/compiler/rustc_target/src/spec/armv7r_none_eabi.rs
index 74905ed5a4e..68b2527985a 100644
--- a/compiler/rustc_target/src/spec/armv7r_none_eabi.rs
+++ b/compiler/rustc_target/src/spec/armv7r_none_eabi.rs
@@ -4,7 +4,7 @@ use crate::spec::{Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, Targ
 
 pub fn target() -> Target {
     Target {
-        llvm_target: "armv7r-unknown-none-eabi".into(),
+        llvm_target: "armv7r-none-eabi".into(),
         pointer_width: 32,
         data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(),
         arch: "arm".into(),
@@ -17,7 +17,7 @@ pub fn target() -> Target {
             panic_strategy: PanicStrategy::Abort,
             max_atomic_width: Some(64),
             emit_debug_gdb_scripts: false,
-            // GCC and Clang default to 8 for arm-none here
+            // GCC defaults to 8 for arm-none here.
             c_enum_min_bits: Some(8),
             ..Default::default()
         },
diff --git a/compiler/rustc_target/src/spec/armv7r_none_eabihf.rs b/compiler/rustc_target/src/spec/armv7r_none_eabihf.rs
index 516b3f5c17e..909765a310f 100644
--- a/compiler/rustc_target/src/spec/armv7r_none_eabihf.rs
+++ b/compiler/rustc_target/src/spec/armv7r_none_eabihf.rs
@@ -4,7 +4,7 @@ use crate::spec::{Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, Targ
 
 pub fn target() -> Target {
     Target {
-        llvm_target: "armv7r-unknown-none-eabihf".into(),
+        llvm_target: "armv7r-none-eabihf".into(),
         pointer_width: 32,
         data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(),
         arch: "arm".into(),
@@ -18,7 +18,7 @@ pub fn target() -> Target {
             features: "+vfp3,-d32,-fp16".into(),
             max_atomic_width: Some(64),
             emit_debug_gdb_scripts: false,
-            // GCC and Clang default to 8 for arm-none here
+            // GCC defaults to 8 for arm-none here.
             c_enum_min_bits: Some(8),
             ..Default::default()
         },
diff --git a/compiler/rustc_target/src/spec/hermit_base.rs b/compiler/rustc_target/src/spec/hermit_base.rs
index dd9991381e7..c6e98fc1a11 100644
--- a/compiler/rustc_target/src/spec/hermit_base.rs
+++ b/compiler/rustc_target/src/spec/hermit_base.rs
@@ -1,21 +1,15 @@
 use crate::spec::{Cc, LinkerFlavor, Lld, PanicStrategy, TargetOptions, TlsModel};
 
 pub fn opts() -> TargetOptions {
-    let pre_link_args = TargetOptions::link_args(
-        LinkerFlavor::Gnu(Cc::No, Lld::No),
-        &["--build-id", "--hash-style=gnu", "--Bstatic"],
-    );
-
     TargetOptions {
         os: "hermit".into(),
-        linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes),
         linker: Some("rust-lld".into()),
-        has_thread_local: true,
-        pre_link_args,
-        panic_strategy: PanicStrategy::Abort,
+        linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes),
+        tls_model: TlsModel::InitialExec,
         position_independent_executables: true,
         static_position_independent_executables: true,
-        tls_model: TlsModel::InitialExec,
+        has_thread_local: true,
+        panic_strategy: PanicStrategy::Abort,
         ..Default::default()
     }
 }
diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs
index 6ae07f45f4a..7114c243ea1 100644
--- a/compiler/rustc_target/src/spec/mod.rs
+++ b/compiler/rustc_target/src/spec/mod.rs
@@ -85,6 +85,7 @@ mod solaris_base;
 mod solid_base;
 mod thumb_base;
 mod uefi_msvc_base;
+mod unikraft_linux_musl_base;
 mod vxworks_base;
 mod wasm_base;
 mod windows_gnu_base;
@@ -1418,8 +1419,11 @@ supported_targets! {
     ("msp430-none-elf", msp430_none_elf),
 
     ("aarch64-unknown-hermit", aarch64_unknown_hermit),
+    ("riscv64gc-unknown-hermit", riscv64gc_unknown_hermit),
     ("x86_64-unknown-hermit", x86_64_unknown_hermit),
 
+    ("x86_64-unikraft-linux-musl", x86_64_unikraft_linux_musl),
+
     ("riscv32i-unknown-none-elf", riscv32i_unknown_none_elf),
     ("riscv32im-unknown-none-elf", riscv32im_unknown_none_elf),
     ("riscv32imc-unknown-none-elf", riscv32imc_unknown_none_elf),
diff --git a/compiler/rustc_target/src/spec/riscv64gc_unknown_hermit.rs b/compiler/rustc_target/src/spec/riscv64gc_unknown_hermit.rs
new file mode 100644
index 00000000000..1f6a34c0cac
--- /dev/null
+++ b/compiler/rustc_target/src/spec/riscv64gc_unknown_hermit.rs
@@ -0,0 +1,20 @@
+use crate::spec::{CodeModel, RelocModel, Target, TargetOptions, TlsModel};
+
+pub fn target() -> Target {
+    Target {
+        llvm_target: "riscv64-unknown-hermit".into(),
+        pointer_width: 64,
+        arch: "riscv64".into(),
+        data_layout: "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128".into(),
+        options: TargetOptions {
+            cpu: "generic-rv64".into(),
+            features: "+m,+a,+f,+d,+c".into(),
+            relocation_model: RelocModel::Pic,
+            code_model: Some(CodeModel::Medium),
+            tls_model: TlsModel::LocalExec,
+            max_atomic_width: Some(64),
+            llvm_abiname: "lp64d".into(),
+            ..super::hermit_base::opts()
+        },
+    }
+}
diff --git a/compiler/rustc_target/src/spec/thumb_base.rs b/compiler/rustc_target/src/spec/thumb_base.rs
index 2220b9326c9..0decfecb4cd 100644
--- a/compiler/rustc_target/src/spec/thumb_base.rs
+++ b/compiler/rustc_target/src/spec/thumb_base.rs
@@ -52,7 +52,7 @@ pub fn opts() -> TargetOptions {
         // breaks debugging. Preserve LR by default to prevent that from happening.
         frame_pointer: FramePointer::Always,
         // ARM supports multiple ABIs for enums, the linux one matches the default of 32 here
-        // but any arm-none or thumb-none target will be defaulted to 8 on GCC and clang
+        // but any arm-none or thumb-none target will be defaulted to 8 on GCC.
         c_enum_min_bits: Some(8),
         ..Default::default()
     }
diff --git a/compiler/rustc_target/src/spec/thumbv4t_none_eabi.rs b/compiler/rustc_target/src/spec/thumbv4t_none_eabi.rs
index 9c59bb9114e..88a76f49acd 100644
--- a/compiler/rustc_target/src/spec/thumbv4t_none_eabi.rs
+++ b/compiler/rustc_target/src/spec/thumbv4t_none_eabi.rs
@@ -45,8 +45,6 @@ pub fn target() -> Target {
             relocation_model: RelocModel::Static,
             // suggested from thumb_base, rust-lang/rust#44993.
             emit_debug_gdb_scripts: false,
-            // suggested from thumb_base, with no-os gcc/clang use 8-bit enums
-            c_enum_min_bits: Some(8),
             frame_pointer: FramePointer::MayOmit,
 
             main_needs_argc_argv: false,
diff --git a/compiler/rustc_target/src/spec/unikraft_linux_musl_base.rs b/compiler/rustc_target/src/spec/unikraft_linux_musl_base.rs
new file mode 100644
index 00000000000..9ccd0a1e7ca
--- /dev/null
+++ b/compiler/rustc_target/src/spec/unikraft_linux_musl_base.rs
@@ -0,0 +1,15 @@
+use crate::spec::{cvs, PanicStrategy, RelocModel, TargetOptions};
+
+pub fn opts() -> TargetOptions {
+    TargetOptions {
+        os: "linux".into(),
+        env: "musl".into(),
+        vendor: "unikraft".into(),
+        linker: Some("kraftld".into()),
+        relocation_model: RelocModel::Static,
+        families: cvs!["unix"],
+        has_thread_local: true,
+        panic_strategy: PanicStrategy::Abort,
+        ..Default::default()
+    }
+}
diff --git a/compiler/rustc_target/src/spec/x86_64_unikraft_linux_musl.rs b/compiler/rustc_target/src/spec/x86_64_unikraft_linux_musl.rs
new file mode 100644
index 00000000000..2aa093b131f
--- /dev/null
+++ b/compiler/rustc_target/src/spec/x86_64_unikraft_linux_musl.rs
@@ -0,0 +1,19 @@
+use crate::spec::{Cc, LinkerFlavor, Lld, StackProbeType, Target, TargetOptions};
+
+pub fn target() -> Target {
+    Target {
+        llvm_target: "x86_64-unknown-linux-musl".into(),
+        pointer_width: 64,
+        arch: "x86_64".into(),
+        data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+            .into(),
+        options: TargetOptions {
+            cpu: "x86-64".into(),
+            plt_by_default: false,
+            pre_link_args: TargetOptions::link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64"]),
+            max_atomic_width: Some(64),
+            stack_probes: StackProbeType::X86,
+            ..super::unikraft_linux_musl_base::opts()
+        },
+    }
+}
diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_hermit.rs b/compiler/rustc_target/src/spec/x86_64_unknown_hermit.rs
index 74ef2527c36..1eb06930111 100644
--- a/compiler/rustc_target/src/spec/x86_64_unknown_hermit.rs
+++ b/compiler/rustc_target/src/spec/x86_64_unknown_hermit.rs
@@ -1,19 +1,19 @@
-use crate::spec::{StackProbeType, Target};
+use crate::spec::{StackProbeType, Target, TargetOptions};
 
 pub fn target() -> Target {
-    let mut base = super::hermit_base::opts();
-    base.cpu = "x86-64".into();
-    base.plt_by_default = false;
-    base.max_atomic_width = Some(64);
-    base.features = "+rdrnd,+rdseed".into();
-    base.stack_probes = StackProbeType::X86;
-
     Target {
         llvm_target: "x86_64-unknown-hermit".into(),
         pointer_width: 64,
+        arch: "x86_64".into(),
         data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
             .into(),
-        arch: "x86_64".into(),
-        options: base,
+        options: TargetOptions {
+            cpu: "x86-64".into(),
+            features: "+rdrnd,+rdseed".into(),
+            plt_by_default: false,
+            max_atomic_width: Some(64),
+            stack_probes: StackProbeType::X86,
+            ..super::hermit_base::opts()
+        },
     }
 }
diff --git a/compiler/rustc_trait_selection/messages.ftl b/compiler/rustc_trait_selection/messages.ftl
index 217ba71b631..f57f1bad15d 100644
--- a/compiler/rustc_trait_selection/messages.ftl
+++ b/compiler/rustc_trait_selection/messages.ftl
@@ -1,3 +1,13 @@
+trait_selection_adjust_signature_borrow = consider adjusting the signature so it borrows its {$len ->
+        [one] argument
+        *[other] arguments
+    }
+
+trait_selection_adjust_signature_remove_borrow = consider adjusting the signature so it does not borrow its {$len ->
+        [one] argument
+        *[other] arguments
+    }
+
 trait_selection_dump_vtable_entries = vtable entries for `{$trait_ref}`: {$entries}
 
 trait_selection_empty_on_clause_in_rustc_on_unimplemented = empty `on`-clause in `#[rustc_on_unimplemented]`
diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs
index 54e22cc3d7f..dde9e9c9ac6 100644
--- a/compiler/rustc_trait_selection/src/errors.rs
+++ b/compiler/rustc_trait_selection/src/errors.rs
@@ -1,5 +1,8 @@
 use crate::fluent_generated as fluent;
-use rustc_errors::{ErrorGuaranteed, Handler, IntoDiagnostic};
+use rustc_errors::{
+    AddToDiagnostic, Applicability, Diagnostic, ErrorGuaranteed, Handler, IntoDiagnostic,
+    SubdiagnosticMessage,
+};
 use rustc_macros::Diagnostic;
 use rustc_middle::ty::{self, PolyTraitRef, Ty};
 use rustc_span::{Span, Symbol};
@@ -97,3 +100,34 @@ pub struct InherentProjectionNormalizationOverflow {
     pub span: Span,
     pub ty: String,
 }
+
+pub enum AdjustSignatureBorrow {
+    Borrow { to_borrow: Vec<(Span, String)> },
+    RemoveBorrow { remove_borrow: Vec<(Span, String)> },
+}
+
+impl AddToDiagnostic for AdjustSignatureBorrow {
+    fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
+    where
+        F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
+    {
+        match self {
+            AdjustSignatureBorrow::Borrow { to_borrow } => {
+                diag.set_arg("len", to_borrow.len());
+                diag.multipart_suggestion_verbose(
+                    fluent::trait_selection_adjust_signature_borrow,
+                    to_borrow,
+                    Applicability::MaybeIncorrect,
+                );
+            }
+            AdjustSignatureBorrow::RemoveBorrow { remove_borrow } => {
+                diag.set_arg("len", remove_borrow.len());
+                diag.multipart_suggestion_verbose(
+                    fluent::trait_selection_adjust_signature_remove_borrow,
+                    remove_borrow,
+                    Applicability::MaybeIncorrect,
+                );
+            }
+        }
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/solve/alias_relate.rs b/compiler/rustc_trait_selection/src/solve/alias_relate.rs
index 73362d82306..1b4af95cb8a 100644
--- a/compiler/rustc_trait_selection/src/solve/alias_relate.rs
+++ b/compiler/rustc_trait_selection/src/solve/alias_relate.rs
@@ -1,3 +1,16 @@
+//! Implements the `AliasRelate` goal, which is used when unifying aliases.
+//! Doing this via a separate goal is called "deferred alias relation" and part
+//! of our more general approach to "lazy normalization".
+//!
+//! This goal, e.g. `A alias-relate B`, may be satisfied by one of three branches:
+//! * normalizes-to: If `A` is a projection, we can prove the equivalent
+//!   projection predicate with B as the right-hand side of the projection.
+//!   This goal is computed in both directions, if both are aliases.
+//! * subst-relate: Equate `A` and `B` by their substs, if they're both
+//!   aliases with the same def-id.
+//! * bidirectional-normalizes-to: If `A` and `B` are both projections, and both
+//!   may apply, then we can compute the "intersection" of both normalizes-to by
+//!   performing them together. This is used specifically to resolve ambiguities.
 use super::{EvalCtxt, SolverMode};
 use rustc_infer::traits::query::NoSolution;
 use rustc_middle::traits::solve::{Certainty, Goal, QueryResult};
@@ -66,24 +79,27 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
                     Invert::Yes,
                 ));
                 // Relate via args
-                let subst_relate_response = self
-                    .assemble_subst_relate_candidate(param_env, alias_lhs, alias_rhs, direction);
-                candidates.extend(subst_relate_response);
+                candidates.extend(
+                    self.assemble_subst_relate_candidate(
+                        param_env, alias_lhs, alias_rhs, direction,
+                    ),
+                );
                 debug!(?candidates);
 
                 if let Some(merged) = self.try_merge_responses(&candidates) {
                     Ok(merged)
                 } else {
-                    // When relating two aliases and we have ambiguity, we prefer
-                    // relating the generic arguments of the aliases over normalizing
-                    // them. This is necessary for inference during typeck.
+                    // When relating two aliases and we have ambiguity, if both
+                    // aliases can be normalized to something, we prefer
+                    // "bidirectionally normalizing" both of them within the same
+                    // candidate.
+                    //
+                    // See <https://github.com/rust-lang/trait-system-refactor-initiative/issues/25>.
                     //
                     // As this is incomplete, we must not do so during coherence.
                     match self.solver_mode() {
                         SolverMode::Normal => {
-                            if let Ok(subst_relate_response) = subst_relate_response {
-                                Ok(subst_relate_response)
-                            } else if let Ok(bidirectional_normalizes_to_response) = self
+                            if let Ok(bidirectional_normalizes_to_response) = self
                                 .assemble_bidirectional_normalizes_to_candidate(
                                     param_env, lhs, rhs, direction,
                                 )
@@ -115,6 +131,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         })
     }
 
+    // Computes the normalizes-to branch, with side-effects. This must be performed
+    // in a probe in order to not taint the evaluation context.
     fn normalizes_to_inner(
         &mut self,
         param_env: ty::ParamEnv<'tcx>,
@@ -124,9 +142,13 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         invert: Invert,
     ) -> Result<(), NoSolution> {
         let other = match direction {
-            // This is purely an optimization.
+            // This is purely an optimization. No need to instantiate a new
+            // infer var and equate the RHS to it.
             ty::AliasRelationDirection::Equate => other,
 
+            // Instantiate an infer var and subtype our RHS to it, so that we
+            // properly represent a subtype relation between the LHS and RHS
+            // of the goal.
             ty::AliasRelationDirection::Subtype => {
                 let fresh = self.next_term_infer_of_kind(other);
                 let (sub, sup) = match invert {
diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
index 6920e790e71..ab90db6ff58 100644
--- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
@@ -3,17 +3,16 @@
 use super::search_graph::OverflowHandler;
 use super::{EvalCtxt, SolverMode};
 use crate::traits::coherence;
-use rustc_data_structures::fx::FxIndexSet;
 use rustc_hir::def_id::DefId;
 use rustc_infer::traits::query::NoSolution;
-use rustc_infer::traits::util::elaborate;
 use rustc_infer::traits::Reveal;
 use rustc_middle::traits::solve::inspect::CandidateKind;
 use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, MaybeCause, QueryResult};
+use rustc_middle::traits::BuiltinImplSource;
 use rustc_middle::ty::fast_reject::{SimplifiedType, TreatParams};
-use rustc_middle::ty::TypeVisitableExt;
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_middle::ty::{fast_reject, TypeFoldable};
+use rustc_middle::ty::{ToPredicate, TypeVisitableExt};
 use rustc_span::ErrorGuaranteed;
 use std::fmt::Debug;
 
@@ -89,16 +88,6 @@ pub(super) enum CandidateSource {
     AliasBound,
 }
 
-/// Records additional information about what kind of built-in impl this is.
-/// This should only be used by selection.
-#[derive(Debug, Clone, Copy)]
-pub(super) enum BuiltinImplSource {
-    TraitUpcasting,
-    Object,
-    Misc,
-    Ambiguity,
-}
-
 /// Methods used to assemble candidates for either trait or projection goals.
 pub(super) trait GoalKind<'tcx>:
     TypeFoldable<TyCtxt<'tcx>> + Copy + Eq + std::fmt::Display
@@ -281,21 +270,6 @@ pub(super) trait GoalKind<'tcx>:
         goal: Goal<'tcx, Self>,
     ) -> QueryResult<'tcx>;
 
-    /// The most common forms of unsizing are array to slice, and concrete (Sized)
-    /// type into a `dyn Trait`. ADTs and Tuples can also have their final field
-    /// unsized if it's generic.
-    fn consider_builtin_unsize_candidate(
-        ecx: &mut EvalCtxt<'_, 'tcx>,
-        goal: Goal<'tcx, Self>,
-    ) -> QueryResult<'tcx>;
-
-    /// `dyn Trait1` can be unsized to `dyn Trait2` if they are the same trait, or
-    /// if `Trait2` is a (transitive) supertrait of `Trait2`.
-    fn consider_builtin_dyn_upcast_candidates(
-        ecx: &mut EvalCtxt<'_, 'tcx>,
-        goal: Goal<'tcx, Self>,
-    ) -> Vec<CanonicalResponse<'tcx>>;
-
     fn consider_builtin_discriminant_kind_candidate(
         ecx: &mut EvalCtxt<'_, 'tcx>,
         goal: Goal<'tcx, Self>,
@@ -310,6 +284,25 @@ pub(super) trait GoalKind<'tcx>:
         ecx: &mut EvalCtxt<'_, 'tcx>,
         goal: Goal<'tcx, Self>,
     ) -> QueryResult<'tcx>;
+
+    /// Consider (possibly several) candidates to upcast or unsize a type to another
+    /// type.
+    ///
+    /// The most common forms of unsizing are array to slice, and concrete (Sized)
+    /// type into a `dyn Trait`. ADTs and Tuples can also have their final field
+    /// unsized if it's generic.
+    ///
+    /// `dyn Trait1` can be unsized to `dyn Trait2` if they are the same trait, or
+    /// if `Trait2` is a (transitive) supertrait of `Trait2`.
+    ///
+    /// We return the `BuiltinImplSource` for each candidate as it is needed
+    /// for unsize coercion in hir typeck and because it is difficult to
+    /// otherwise recompute this for codegen. This is a bit of a mess but the
+    /// easiest way to maintain the existing behavior for now.
+    fn consider_builtin_unsize_and_upcast_candidates(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)>;
 }
 
 impl<'tcx> EvalCtxt<'_, 'tcx> {
@@ -343,7 +336,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
     ) -> Option<Vec<Candidate<'tcx>>> {
         goal.predicate.self_ty().is_ty_var().then(|| {
             vec![Candidate {
-                source: CandidateSource::BuiltinImpl(BuiltinImplSource::Ambiguity),
+                source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
                 result: self
                     .evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
                     .unwrap(),
@@ -412,7 +405,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
                             Certainty::Maybe(MaybeCause::Overflow),
                         )?;
                         Ok(vec![Candidate {
-                            source: CandidateSource::BuiltinImpl(BuiltinImplSource::Ambiguity),
+                            source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
                             result,
                         }])
                     },
@@ -610,8 +603,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
             G::consider_builtin_future_candidate(self, goal)
         } else if lang_items.gen_trait() == Some(trait_def_id) {
             G::consider_builtin_generator_candidate(self, goal)
-        } else if lang_items.unsize_trait() == Some(trait_def_id) {
-            G::consider_builtin_unsize_candidate(self, goal)
         } else if lang_items.discriminant_kind_trait() == Some(trait_def_id) {
             G::consider_builtin_discriminant_kind_candidate(self, goal)
         } else if lang_items.destruct_trait() == Some(trait_def_id) {
@@ -633,11 +624,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         // 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 lang_items.unsize_trait() == Some(trait_def_id) {
-            for result in G::consider_builtin_dyn_upcast_candidates(self, goal) {
-                candidates.push(Candidate {
-                    source: CandidateSource::BuiltinImpl(BuiltinImplSource::TraitUpcasting),
-                    result,
-                });
+            for (result, source) in G::consider_builtin_unsize_and_upcast_candidates(self, goal) {
+                candidates.push(Candidate { source: CandidateSource::BuiltinImpl(source), result });
             }
         }
     }
@@ -853,29 +841,47 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
             ty::Dynamic(bounds, ..) => bounds,
         };
 
-        let own_bounds: FxIndexSet<_> =
-            bounds.iter().map(|bound| bound.with_self_ty(tcx, self_ty)).collect();
-        for assumption in elaborate(tcx, own_bounds.iter().copied())
-            // we only care about bounds that match the `Self` type
-            .filter_only_self()
-        {
-            // FIXME: Predicates are fully elaborated in the object type's existential bounds
-            // list. We want to only consider these pre-elaborated projections, and not other
-            // projection predicates that we reach by elaborating the principal trait ref,
-            // since that'll cause ambiguity.
-            //
-            // We can remove this when we have implemented lifetime intersections in responses.
-            if assumption.as_projection_clause().is_some() && !own_bounds.contains(&assumption) {
-                continue;
+        // Consider all of the auto-trait and projection bounds, which don't
+        // need to be recorded as a `BuiltinImplSource::Object` since they don't
+        // really have a vtable base...
+        for bound in bounds {
+            match bound.skip_binder() {
+                ty::ExistentialPredicate::Trait(_) => {
+                    // Skip principal
+                }
+                ty::ExistentialPredicate::Projection(_)
+                | ty::ExistentialPredicate::AutoTrait(_) => {
+                    match G::consider_object_bound_candidate(
+                        self,
+                        goal,
+                        bound.with_self_ty(tcx, self_ty),
+                    ) {
+                        Ok(result) => candidates.push(Candidate {
+                            source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
+                            result,
+                        }),
+                        Err(NoSolution) => (),
+                    }
+                }
             }
+        }
 
-            match G::consider_object_bound_candidate(self, goal, assumption) {
-                Ok(result) => candidates.push(Candidate {
-                    source: CandidateSource::BuiltinImpl(BuiltinImplSource::Object),
-                    result,
-                }),
-                Err(NoSolution) => (),
-            }
+        // FIXME: We only need to do *any* of this if we're considering a trait goal,
+        // 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);
+            self.walk_vtable(principal_trait_ref, |ecx, assumption, vtable_base, _| {
+                match G::consider_object_bound_candidate(ecx, goal, assumption.to_predicate(tcx)) {
+                    Ok(result) => candidates.push(Candidate {
+                        source: CandidateSource::BuiltinImpl(BuiltinImplSource::Object {
+                            vtable_base,
+                        }),
+                        result,
+                    }),
+                    Err(NoSolution) => (),
+                }
+            });
         }
     }
 
@@ -895,7 +901,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
                         .evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
                     {
                         Ok(result) => candidates.push(Candidate {
-                            source: CandidateSource::BuiltinImpl(BuiltinImplSource::Ambiguity),
+                            source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
                             result,
                         }),
                         // FIXME: This will be reachable at some point if we're in
diff --git a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs
index a8ba98bef6d..a2db35e069e 100644
--- a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs
+++ b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs
@@ -1,3 +1,5 @@
+//! Code which is used by built-in goals that match "structurally", such a auto
+//! traits, `Copy`/`Clone`.
 use rustc_data_structures::fx::FxHashMap;
 use rustc_hir::{def_id::DefId, Movability, Mutability};
 use rustc_infer::traits::query::NoSolution;
diff --git a/compiler/rustc_trait_selection/src/solve/canonicalize.rs b/compiler/rustc_trait_selection/src/solve/canonicalize.rs
index 255620489ff..88771f90756 100644
--- a/compiler/rustc_trait_selection/src/solve/canonicalize.rs
+++ b/compiler/rustc_trait_selection/src/solve/canonicalize.rs
@@ -125,9 +125,8 @@ impl<'a, 'tcx> Canonicalizer<'a, 'tcx> {
         // - var_infos: [E0, U1, E1, U1, E1, E6, U6], curr_compressed_uv: 1, next_orig_uv: 6
         // - var_infos: [E0, U1, E1, U1, E1, E2, U2], curr_compressed_uv: 2, next_orig_uv: -
         //
-        // This algorithm runs in `O(nm)` where `n` is the number of different universe
-        // indices in the input and `m` is the number of canonical variables.
-        // This should be fine as both `n` and `m` are expected to be small.
+        // This algorithm runs in `O(n²)` where `n` is the number of different universe
+        // indices in the input. This should be fine as `n` is expected to be small.
         let mut curr_compressed_uv = ty::UniverseIndex::ROOT;
         let mut existential_in_new_uv = false;
         let mut next_orig_uv = Some(ty::UniverseIndex::ROOT);
@@ -263,14 +262,18 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'_, 'tcx> {
             ty::ReError(_) => return r,
         };
 
-        let var = ty::BoundVar::from(
-            self.variables.iter().position(|&v| v == r.into()).unwrap_or_else(|| {
-                let var = self.variables.len();
-                self.variables.push(r.into());
-                self.primitive_var_infos.push(CanonicalVarInfo { kind });
-                var
-            }),
-        );
+        let existing_bound_var = match self.canonicalize_mode {
+            CanonicalizeMode::Input => None,
+            CanonicalizeMode::Response { .. } => {
+                self.variables.iter().position(|&v| v == r.into()).map(ty::BoundVar::from)
+            }
+        };
+        let var = existing_bound_var.unwrap_or_else(|| {
+            let var = ty::BoundVar::from(self.variables.len());
+            self.variables.push(r.into());
+            self.primitive_var_infos.push(CanonicalVarInfo { kind });
+            var
+        });
         let br = ty::BoundRegion { var, kind: BrAnon(None) };
         ty::Region::new_late_bound(self.interner(), self.binder_index, br)
     }
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs
index 6e0aa08c307..9e3b0bbc4fb 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs
@@ -25,6 +25,7 @@ use std::io::Write;
 use std::ops::ControlFlow;
 
 use crate::traits::specialization_graph;
+use crate::traits::vtable::{count_own_vtable_entries, prepare_vtable_segments, VtblSegment};
 
 use super::inspect::ProofTreeBuilder;
 use super::search_graph::{self, OverflowHandler};
@@ -271,6 +272,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
         // assertions against dropping an `InferCtxt` without taking opaques.
         // FIXME: Once we remove support for the old impl we can remove this.
         if input.anchor != DefiningAnchor::Error {
+            // This seems ok, but fragile.
             let _ = infcx.take_opaque_types();
         }
 
@@ -342,7 +344,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
             Ok(response) => response,
         };
 
-        let has_changed = !canonical_response.value.var_values.is_identity()
+        let has_changed = !canonical_response.value.var_values.is_identity_modulo_regions()
             || !canonical_response.value.external_constraints.opaque_types.is_empty();
         let (certainty, nested_goals) = match self.instantiate_and_apply_query_response(
             goal.param_env,
@@ -920,4 +922,39 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
             Err(ErrorHandled::TooGeneric) => None,
         }
     }
+
+    /// Walk through the vtable of a principal trait ref, executing a `supertrait_visitor`
+    /// for every trait ref encountered (including the principal). Passes both the vtable
+    /// base and the (optional) vptr slot.
+    pub(super) fn walk_vtable(
+        &mut self,
+        principal: ty::PolyTraitRef<'tcx>,
+        mut supertrait_visitor: impl FnMut(&mut Self, ty::PolyTraitRef<'tcx>, usize, Option<usize>),
+    ) {
+        let tcx = self.tcx();
+        let mut offset = 0;
+        prepare_vtable_segments::<()>(tcx, principal, |segment| {
+            match segment {
+                VtblSegment::MetadataDSA => {
+                    offset += TyCtxt::COMMON_VTABLE_ENTRIES.len();
+                }
+                VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => {
+                    let own_vtable_entries = count_own_vtable_entries(tcx, trait_ref);
+
+                    supertrait_visitor(
+                        self,
+                        trait_ref,
+                        offset,
+                        emit_vptr.then(|| offset + own_vtable_entries),
+                    );
+
+                    offset += own_vtable_entries;
+                    if emit_vptr {
+                        offset += 1;
+                    }
+                }
+            }
+            ControlFlow::Continue(())
+        });
+    }
 }
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs
index 9f58e66d04e..7323b98b8ce 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs
@@ -1,13 +1,13 @@
-/// Canonicalization is used to separate some goal from its context,
-/// throwing away unnecessary information in the process.
-///
-/// This is necessary to cache goals containing inference variables
-/// and placeholders without restricting them to the current `InferCtxt`.
-///
-/// Canonicalization is fairly involved, for more details see the relevant
-/// section of the [rustc-dev-guide][c].
-///
-/// [c]: https://rustc-dev-guide.rust-lang.org/solve/canonicalization.html
+//! Canonicalization is used to separate some goal from its context,
+//! throwing away unnecessary information in the process.
+//!
+//! This is necessary to cache goals containing inference variables
+//! and placeholders without restricting them to the current `InferCtxt`.
+//!
+//! Canonicalization is fairly involved, for more details see the relevant
+//! section of the [rustc-dev-guide][c].
+//!
+//! [c]: https://rustc-dev-guide.rust-lang.org/solve/canonicalization.html
 use super::{CanonicalInput, Certainty, EvalCtxt, Goal};
 use crate::solve::canonicalize::{CanonicalizeMode, Canonicalizer};
 use crate::solve::{CanonicalResponse, QueryResult, Response};
@@ -135,6 +135,13 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         )
     }
 
+    /// Computes the region constraints and *new* opaque types registered when
+    /// proving a goal.
+    ///
+    /// If an opaque was already constrained before proving this goal, then the
+    /// external constraints do not need to record that opaque, since if it is
+    /// further constrained by inference, that will be passed back in the var
+    /// values.
     #[instrument(level = "debug", skip(self), ret)]
     fn compute_external_query_constraints(&self) -> Result<ExternalConstraints<'tcx>, NoSolution> {
         // We only check for leaks from universes which were entered inside
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs
index a6d118d8cc2..6045001510e 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs
@@ -1,25 +1,20 @@
-use std::ops::ControlFlow;
-
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
-use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk};
-use rustc_infer::traits::util::supertraits;
+use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt};
 use rustc_infer::traits::{
     Obligation, PolyTraitObligation, PredicateObligation, Selection, SelectionResult, TraitEngine,
 };
 use rustc_middle::traits::solve::{CanonicalInput, Certainty, Goal};
 use rustc_middle::traits::{
-    ImplSource, ImplSourceObjectData, ImplSourceTraitUpcastingData, ImplSourceUserDefinedData,
-    ObligationCause, SelectionError,
+    BuiltinImplSource, ImplSource, ImplSourceUserDefinedData, ObligationCause, SelectionError,
 };
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_span::DUMMY_SP;
 
-use crate::solve::assembly::{BuiltinImplSource, Candidate, CandidateSource};
+use crate::solve::assembly::{Candidate, CandidateSource};
 use crate::solve::eval_ctxt::{EvalCtxt, GenerateProofTree};
 use crate::solve::inspect::ProofTreeBuilder;
 use crate::solve::search_graph::OverflowHandler;
-use crate::traits::vtable::{count_own_vtable_entries, prepare_vtable_segments, VtblSegment};
 use crate::traits::StructurallyNormalizeExt;
 use crate::traits::TraitEngineExt;
 
@@ -105,38 +100,26 @@ impl<'tcx> InferCtxtSelectExt<'tcx> for InferCtxt<'tcx> {
                 rematch_impl(self, goal, def_id, nested_obligations)
             }
 
-            // Rematching the dyn upcast or object goal will instantiate the same nested
-            // goals that would have caused the ambiguity, so we can still make progress here
-            // regardless.
-            // FIXME: This doesn't actually check the object bounds hold here.
-            (
-                _,
-                CandidateSource::BuiltinImpl(
-                    BuiltinImplSource::Object | BuiltinImplSource::TraitUpcasting,
-                ),
-            ) => rematch_object(self, goal, nested_obligations),
-
-            (Certainty::Maybe(_), CandidateSource::BuiltinImpl(BuiltinImplSource::Misc))
+            (Certainty::Maybe(_), CandidateSource::BuiltinImpl(src))
                 if self.tcx.lang_items().unsize_trait() == Some(goal.predicate.def_id()) =>
             {
-                rematch_unsize(self, goal, nested_obligations)
+                rematch_unsize(self, goal, nested_obligations, src)
             }
 
             // Technically some builtin impls have nested obligations, but if
             // `Certainty::Yes`, then they should've all been verified and don't
             // need re-checking.
-            (Certainty::Yes, CandidateSource::BuiltinImpl(BuiltinImplSource::Misc)) => {
-                Ok(Some(ImplSource::Builtin(nested_obligations)))
+            (Certainty::Yes, CandidateSource::BuiltinImpl(src)) => {
+                Ok(Some(ImplSource::Builtin(src, nested_obligations)))
             }
 
             // It's fine not to do anything to rematch these, since there are no
             // nested obligations.
             (Certainty::Yes, CandidateSource::ParamEnv(_) | CandidateSource::AliasBound) => {
-                Ok(Some(ImplSource::Param(nested_obligations, ty::BoundConstness::NotConst)))
+                Ok(Some(ImplSource::Param(ty::BoundConstness::NotConst, nested_obligations)))
             }
 
-            (_, CandidateSource::BuiltinImpl(BuiltinImplSource::Ambiguity))
-            | (Certainty::Maybe(_), _) => Ok(None),
+            (Certainty::Maybe(_), _) => Ok(None),
         }
     }
 }
@@ -183,11 +166,12 @@ fn candidate_should_be_dropped_in_favor_of<'tcx>(
         }
         (_, CandidateSource::ParamEnv(_)) => true,
 
+        // FIXME: we could prefer earlier vtable bases perhaps...
         (
-            CandidateSource::BuiltinImpl(BuiltinImplSource::Object),
-            CandidateSource::BuiltinImpl(BuiltinImplSource::Object),
+            CandidateSource::BuiltinImpl(BuiltinImplSource::Object { .. }),
+            CandidateSource::BuiltinImpl(BuiltinImplSource::Object { .. }),
         ) => false,
-        (_, CandidateSource::BuiltinImpl(BuiltinImplSource::Object)) => true,
+        (_, CandidateSource::BuiltinImpl(BuiltinImplSource::Object { .. })) => true,
 
         (CandidateSource::Impl(victim_def_id), CandidateSource::Impl(other_def_id)) => {
             tcx.specializes((other_def_id, victim_def_id))
@@ -225,102 +209,6 @@ fn rematch_impl<'tcx>(
     Ok(Some(ImplSource::UserDefined(ImplSourceUserDefinedData { impl_def_id, args, nested })))
 }
 
-fn rematch_object<'tcx>(
-    infcx: &InferCtxt<'tcx>,
-    goal: Goal<'tcx, ty::TraitPredicate<'tcx>>,
-    mut nested: Vec<PredicateObligation<'tcx>>,
-) -> SelectionResult<'tcx, Selection<'tcx>> {
-    let a_ty = structurally_normalize(goal.predicate.self_ty(), infcx, goal.param_env, &mut nested);
-    let ty::Dynamic(data, _, source_kind) = *a_ty.kind() else { bug!() };
-    let source_trait_ref = data.principal().unwrap().with_self_ty(infcx.tcx, a_ty);
-
-    let (is_upcasting, target_trait_ref_unnormalized) =
-        if Some(goal.predicate.def_id()) == infcx.tcx.lang_items().unsize_trait() {
-            assert_eq!(source_kind, ty::Dyn, "cannot upcast dyn*");
-            let b_ty = structurally_normalize(
-                goal.predicate.trait_ref.args.type_at(1),
-                infcx,
-                goal.param_env,
-                &mut nested,
-            );
-            if let ty::Dynamic(data, _, ty::Dyn) = *b_ty.kind() {
-                // FIXME: We also need to ensure that the source lifetime outlives the
-                // target lifetime. This doesn't matter for codegen, though, and only
-                // *really* matters if the goal's certainty is ambiguous.
-                (true, data.principal().unwrap().with_self_ty(infcx.tcx, a_ty))
-            } else {
-                bug!()
-            }
-        } else {
-            (false, ty::Binder::dummy(goal.predicate.trait_ref))
-        };
-
-    let mut target_trait_ref = None;
-    for candidate_trait_ref in supertraits(infcx.tcx, source_trait_ref) {
-        let result = infcx.commit_if_ok(|_| {
-            infcx.at(&ObligationCause::dummy(), goal.param_env).eq(
-                DefineOpaqueTypes::No,
-                target_trait_ref_unnormalized,
-                candidate_trait_ref,
-            )
-
-            // FIXME: We probably should at least shallowly verify these...
-        });
-
-        match result {
-            Ok(InferOk { value: (), obligations }) => {
-                target_trait_ref = Some(candidate_trait_ref);
-                nested.extend(obligations);
-                break;
-            }
-            Err(_) => continue,
-        }
-    }
-
-    let target_trait_ref = target_trait_ref.unwrap();
-
-    let mut offset = 0;
-    let Some((vtable_base, vtable_vptr_slot)) =
-        prepare_vtable_segments(infcx.tcx, source_trait_ref, |segment| {
-            match segment {
-                VtblSegment::MetadataDSA => {
-                    offset += TyCtxt::COMMON_VTABLE_ENTRIES.len();
-                }
-                VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => {
-                    let own_vtable_entries = count_own_vtable_entries(infcx.tcx, trait_ref);
-
-                    if trait_ref == target_trait_ref {
-                        if emit_vptr {
-                            return ControlFlow::Break((
-                                offset,
-                                Some(offset + count_own_vtable_entries(infcx.tcx, trait_ref)),
-                            ));
-                        } else {
-                            return ControlFlow::Break((offset, None));
-                        }
-                    }
-
-                    offset += own_vtable_entries;
-                    if emit_vptr {
-                        offset += 1;
-                    }
-                }
-            }
-            ControlFlow::Continue(())
-        })
-    else {
-        bug!();
-    };
-
-    // If we're upcasting, get the offset of the vtable pointer, otherwise get
-    // the base of the vtable.
-    Ok(Some(if is_upcasting {
-        ImplSource::TraitUpcasting(ImplSourceTraitUpcastingData { vtable_vptr_slot, nested })
-    } else {
-        ImplSource::Object(ImplSourceObjectData { vtable_base, nested })
-    }))
-}
-
 /// The `Unsize` trait is particularly important to coercion, so we try rematch it.
 /// NOTE: This must stay in sync with `consider_builtin_unsize_candidate` in trait
 /// goal assembly in the solver, both for soundness and in order to avoid ICEs.
@@ -328,11 +216,16 @@ fn rematch_unsize<'tcx>(
     infcx: &InferCtxt<'tcx>,
     goal: Goal<'tcx, ty::TraitPredicate<'tcx>>,
     mut nested: Vec<PredicateObligation<'tcx>>,
+    source: BuiltinImplSource,
 ) -> SelectionResult<'tcx, Selection<'tcx>> {
     let tcx = infcx.tcx;
-    let a_ty = goal.predicate.self_ty();
-    let b_ty = goal.predicate.trait_ref.args.type_at(1);
-
+    let a_ty = structurally_normalize(goal.predicate.self_ty(), infcx, goal.param_env, &mut nested);
+    let b_ty = structurally_normalize(
+        goal.predicate.trait_ref.args.type_at(1),
+        infcx,
+        goal.param_env,
+        &mut nested,
+    );
     match (a_ty.kind(), b_ty.kind()) {
         (_, &ty::Dynamic(data, region, ty::Dyn)) => {
             // Check that the type implements all of the predicates of the def-id.
@@ -360,6 +253,8 @@ fn rematch_unsize<'tcx>(
                 goal.param_env,
                 ty::Binder::dummy(ty::OutlivesPredicate(a_ty, region)),
             ));
+
+            Ok(Some(ImplSource::Builtin(source, nested)))
         }
         // `[T; n]` -> `[T]` unsizing
         (&ty::Array(a_elem_ty, ..), &ty::Slice(b_elem_ty)) => {
@@ -370,6 +265,8 @@ fn rematch_unsize<'tcx>(
                     .expect("expected rematch to succeed")
                     .into_obligations(),
             );
+
+            Ok(Some(ImplSource::Builtin(source, nested)))
         }
         // Struct unsizing `Struct<T>` -> `Struct<U>` where `T: Unsize<U>`
         (&ty::Adt(a_def, a_args), &ty::Adt(b_def, b_args))
@@ -420,6 +317,8 @@ fn rematch_unsize<'tcx>(
                 goal.param_env,
                 ty::TraitRef::new(tcx, goal.predicate.def_id(), [a_tail_ty, b_tail_ty]),
             ));
+
+            Ok(Some(ImplSource::Builtin(source, nested)))
         }
         // Tuple unsizing `(.., T)` -> `(.., U)` where `T: Unsize<U>`
         (&ty::Tuple(a_tys), &ty::Tuple(b_tys))
@@ -446,14 +345,20 @@ fn rematch_unsize<'tcx>(
                 goal.param_env,
                 ty::TraitRef::new(tcx, goal.predicate.def_id(), [*a_last_ty, *b_last_ty]),
             ));
+
+            // We need to be able to detect tuple unsizing to require its feature gate.
+            assert_eq!(
+                source,
+                BuiltinImplSource::TupleUnsizing,
+                "compiler-errors wants to know if this can ever be triggered..."
+            );
+            Ok(Some(ImplSource::Builtin(source, nested)))
         }
         // FIXME: We *could* ICE here if either:
         // 1. the certainty is `Certainty::Yes`,
         // 2. we're in codegen (which should mean `Certainty::Yes`).
-        _ => return Ok(None),
+        _ => Ok(None),
     }
-
-    Ok(Some(ImplSource::Builtin(nested)))
 }
 
 fn structurally_normalize<'tcx>(
diff --git a/compiler/rustc_trait_selection/src/solve/inherent_projection.rs b/compiler/rustc_trait_selection/src/solve/inherent_projection.rs
index d10a14ff742..28fe59b7f6a 100644
--- a/compiler/rustc_trait_selection/src/solve/inherent_projection.rs
+++ b/compiler/rustc_trait_selection/src/solve/inherent_projection.rs
@@ -1,3 +1,9 @@
+//! Computes a normalizes-to (projection) goal for inherent associated types,
+//! `#![feature(inherent_associated_type)]`. Since astconv already determines
+//! which impl the IAT is being projected from, we just:
+//! 1. instantiate substs,
+//! 2. equate the self type, and
+//! 3. instantiate and register where clauses.
 use rustc_middle::traits::solve::{Certainty, Goal, QueryResult};
 use rustc_middle::ty;
 
diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs
index 7c15c3c0e8b..63e48c94a86 100644
--- a/compiler/rustc_trait_selection/src/solve/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/mod.rs
@@ -1,14 +1,19 @@
-//! The new trait solver, currently still WIP.
+//! The next-generation trait solver, currently still WIP.
 //!
-//! As a user of the trait system, you can use `TyCtxt::evaluate_goal` to
-//! interact with this solver.
+//! As a user of rust, you can use `-Ztrait-solver=next` or `next-coherence`
+//! to enable the new trait solver always, or just within coherence, respectively.
+//!
+//! As a developer of rustc, you shouldn't be using the new trait
+//! solver without asking the trait-system-refactor-initiative, but it can
+//! be enabled with `InferCtxtBuilder::with_next_trait_solver`. This will
+//! ensure that trait solving using that inference context will be routed
+//! to the new trait solver.
 //!
 //! For a high-level overview of how this solver works, check out the relevant
 //! section of the rustc-dev-guide.
 //!
 //! FIXME(@lcnr): Write that section. If you read this before then ask me
 //! about it on zulip.
-
 use rustc_hir::def_id::DefId;
 use rustc_infer::infer::canonical::{Canonical, CanonicalVarValues};
 use rustc_infer::traits::query::NoSolution;
diff --git a/compiler/rustc_trait_selection/src/solve/normalize.rs b/compiler/rustc_trait_selection/src/solve/normalize.rs
index f51f4edb933..091b7f33834 100644
--- a/compiler/rustc_trait_selection/src/solve/normalize.rs
+++ b/compiler/rustc_trait_selection/src/solve/normalize.rs
@@ -28,7 +28,8 @@ pub(crate) fn deeply_normalize<'tcx, T: TypeFoldable<TyCtxt<'tcx>>>(
 /// its input to be already fully resolved.
 ///
 /// Additionally takes a list of universes which represents the binders which have been
-/// entered before passing `value` to the function.
+/// entered before passing `value` to the function. This is currently needed for
+/// `normalize_erasing_regions`, which skips binders as it walks through a type.
 pub(crate) fn deeply_normalize_with_skipped_universes<'tcx, T: TypeFoldable<TyCtxt<'tcx>>>(
     at: At<'_, 'tcx>,
     value: T,
@@ -194,7 +195,7 @@ impl<'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for NormalizationFolder<'_, 'tcx> {
                 mapped_regions,
                 mapped_types,
                 mapped_consts,
-                &mut self.universes,
+                &self.universes,
                 result,
             ))
         } else {
@@ -224,7 +225,7 @@ impl<'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for NormalizationFolder<'_, 'tcx> {
                 mapped_regions,
                 mapped_types,
                 mapped_consts,
-                &mut self.universes,
+                &self.universes,
                 result,
             ))
         } else {
diff --git a/compiler/rustc_trait_selection/src/solve/opaques.rs b/compiler/rustc_trait_selection/src/solve/opaques.rs
index 2e537d1c358..f08adc0208b 100644
--- a/compiler/rustc_trait_selection/src/solve/opaques.rs
+++ b/compiler/rustc_trait_selection/src/solve/opaques.rs
@@ -1,3 +1,6 @@
+//! Computes a normalizes-to (projection) goal for opaque types. This goal
+//! behaves differently depending on the param-env's reveal mode and whether
+//! the opaque is in a defining scope.
 use rustc_middle::traits::query::NoSolution;
 use rustc_middle::traits::solve::{Certainty, Goal, QueryResult};
 use rustc_middle::traits::Reveal;
diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs
index 222ed9939ba..03ade738bb6 100644
--- a/compiler/rustc_trait_selection/src/solve/project_goals.rs
+++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs
@@ -10,6 +10,7 @@ use rustc_infer::traits::specialization_graph::LeafDef;
 use rustc_infer::traits::Reveal;
 use rustc_middle::traits::solve::inspect::CandidateKind;
 use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, QueryResult};
+use rustc_middle::traits::BuiltinImplSource;
 use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
 use rustc_middle::ty::ProjectionPredicate;
 use rustc_middle::ty::{self, Ty, TyCtxt};
@@ -502,17 +503,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
         )
     }
 
-    fn consider_builtin_unsize_candidate(
+    fn consider_builtin_unsize_and_upcast_candidates(
         _ecx: &mut EvalCtxt<'_, 'tcx>,
         goal: Goal<'tcx, Self>,
-    ) -> QueryResult<'tcx> {
-        bug!("`Unsize` does not have an associated type: {:?}", goal);
-    }
-
-    fn consider_builtin_dyn_upcast_candidates(
-        _ecx: &mut EvalCtxt<'_, 'tcx>,
-        goal: Goal<'tcx, Self>,
-    ) -> Vec<CanonicalResponse<'tcx>> {
+    ) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)> {
         bug!("`Unsize` does not have an associated type: {:?}", goal);
     }
 
diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs
index 761f5327f6d..40ce4a3b3e4 100644
--- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs
+++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs
@@ -1,13 +1,14 @@
 //! Dealing with trait goals, i.e. `T: Trait<'a, U>`.
 
 use super::assembly::{self, structural_traits};
+use super::search_graph::OverflowHandler;
 use super::{EvalCtxt, SolverMode};
 use rustc_hir::def_id::DefId;
 use rustc_hir::{LangItem, Movability};
 use rustc_infer::traits::query::NoSolution;
-use rustc_infer::traits::util::supertraits;
-use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, QueryResult};
-use rustc_middle::traits::Reveal;
+use rustc_middle::traits::solve::inspect::CandidateKind;
+use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, MaybeCause, QueryResult};
+use rustc_middle::traits::{BuiltinImplSource, Reveal};
 use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams, TreatProjections};
 use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt};
 use rustc_middle::ty::{TraitPredicate, TypeVisitableExt};
@@ -223,20 +224,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
             return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
         }
 
-        // First, try computing an exact naive layout in case the type is generic.
-        let is_pointer_like = if let Ok(layout) = tcx.naive_layout_of(key) {
-            layout.is_pointer_like(&tcx.data_layout).unwrap_or_else(|| {
-                // Second, we fall back to full layout computation.
-                tcx.layout_of(key)
-                    .ok()
-                    .filter(|l| l.layout.is_pointer_like(&tcx.data_layout))
-                    .is_some()
-            })
-        } else {
-            false
-        };
-
-        if is_pointer_like {
+        if let Ok(layout) = tcx.layout_of(key)
+            && layout.layout.is_pointer_like(&tcx.data_layout)
+        {
             // FIXME: We could make this faster by making a no-constraints response
             ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
         } else {
@@ -376,7 +366,70 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
         )
     }
 
-    fn consider_builtin_unsize_candidate(
+    fn consider_builtin_unsize_and_upcast_candidates(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)> {
+        if goal.predicate.polarity != ty::ImplPolarity::Positive {
+            return vec![];
+        }
+
+        ecx.probe(|_| CandidateKind::DynUpcastingAssembly).enter(|ecx| {
+            let a_ty = goal.predicate.self_ty();
+            // We need to normalize the b_ty since it's matched structurally
+            // in the other functions below.
+            let b_ty = match ecx
+                .normalize_non_self_ty(goal.predicate.trait_ref.args.type_at(1), goal.param_env)
+            {
+                Ok(Some(b_ty)) => {
+                    // If we have a type var, then bail with ambiguity.
+                    if b_ty.is_ty_var() {
+                        return vec![(
+                            ecx.evaluate_added_goals_and_make_canonical_response(
+                                Certainty::AMBIGUOUS,
+                            )
+                            .unwrap(),
+                            BuiltinImplSource::Misc,
+                        )];
+                    } else {
+                        b_ty
+                    }
+                }
+                Ok(None) => {
+                    return vec![(
+                        ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Maybe(
+                            MaybeCause::Overflow,
+                        ))
+                        .unwrap(),
+                        BuiltinImplSource::Misc,
+                    )];
+                }
+                Err(_) => return vec![],
+            };
+
+            let mut results = vec![];
+            results.extend(ecx.consider_builtin_dyn_upcast_candidates(goal.param_env, a_ty, b_ty));
+            results.extend(
+                ecx.consider_builtin_unsize_candidate(goal.with(ecx.tcx(), (a_ty, b_ty)))
+                    .into_iter()
+                    .map(|resp| {
+                        // If we're unsizing from tuple -> tuple, detect
+                        let source =
+                            if matches!((a_ty.kind(), b_ty.kind()), (ty::Tuple(..), ty::Tuple(..)))
+                            {
+                                BuiltinImplSource::TupleUnsizing
+                            } else {
+                                BuiltinImplSource::Misc
+                            };
+                        (resp, source)
+                    }),
+            );
+
+            results
+        })
+    }
+
+    fn consider_builtin_discriminant_kind_candidate(
         ecx: &mut EvalCtxt<'_, 'tcx>,
         goal: Goal<'tcx, Self>,
     ) -> QueryResult<'tcx> {
@@ -384,14 +437,69 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
             return Err(NoSolution);
         }
 
-        let tcx = ecx.tcx();
-        let a_ty = goal.predicate.self_ty();
-        let b_ty = goal.predicate.trait_ref.args.type_at(1);
-        if b_ty.is_ty_var() {
-            return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
+        // `DiscriminantKind` is automatically implemented for every type.
+        ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+    }
+
+    fn consider_builtin_destruct_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx> {
+        if goal.predicate.polarity != ty::ImplPolarity::Positive {
+            return Err(NoSolution);
+        }
+
+        // FIXME(-Ztrait-solver=next): Implement this when we get const working in the new solver
+
+        // `Destruct` is automatically implemented for every type in
+        // non-const environments.
+        ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+    }
+
+    fn consider_builtin_transmute_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx> {
+        if goal.predicate.polarity != ty::ImplPolarity::Positive {
+            return Err(NoSolution);
+        }
+
+        // `rustc_transmute` does not have support for type or const params
+        if goal.has_non_region_placeholders() {
+            return Err(NoSolution);
         }
-        ecx.probe_candidate("builtin unsize").enter(|ecx| {
+
+        // Erase regions because we compute layouts in `rustc_transmute`,
+        // which will ICE for region vars.
+        let args = ecx.tcx().erase_regions(goal.predicate.trait_ref.args);
+
+        let Some(assume) =
+            rustc_transmute::Assume::from_const(ecx.tcx(), goal.param_env, args.const_at(3))
+        else {
+            return Err(NoSolution);
+        };
+
+        let certainty = ecx.is_transmutable(
+            rustc_transmute::Types { dst: args.type_at(0), src: args.type_at(1) },
+            args.type_at(2),
+            assume,
+        )?;
+        ecx.evaluate_added_goals_and_make_canonical_response(certainty)
+    }
+}
+
+impl<'tcx> EvalCtxt<'_, 'tcx> {
+    fn consider_builtin_unsize_candidate(
+        &mut self,
+        goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>,
+    ) -> QueryResult<'tcx> {
+        let Goal { param_env, predicate: (a_ty, b_ty) } = goal;
+        self.probe_candidate("builtin unsize").enter(|ecx| {
+            let tcx = ecx.tcx();
             match (a_ty.kind(), b_ty.kind()) {
+                (ty::Infer(ty::TyVar(_)), _) | (_, ty::Infer(ty::TyVar(_))) => {
+                    bug!("unexpected type variable in unsize goal")
+                }
                 // Trait upcasting, or `dyn Trait + Auto + 'a` -> `dyn Trait + 'b`
                 (&ty::Dynamic(_, _, ty::Dyn), &ty::Dynamic(_, _, ty::Dyn)) => {
                     // Dyn upcasting is handled separately, since due to upcasting,
@@ -415,20 +523,27 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
                     // Check that the type implements all of the predicates of the def-id.
                     // (i.e. the principal, all of the associated types match, and any auto traits)
                     ecx.add_goals(
-                        data.iter().map(|pred| goal.with(tcx, pred.with_self_ty(tcx, a_ty))),
+                        data.iter()
+                            .map(|pred| Goal::new(tcx, param_env, pred.with_self_ty(tcx, a_ty))),
                     );
                     // The type must be Sized to be unsized.
-                    ecx.add_goal(goal.with(tcx, ty::TraitRef::new(tcx, sized_def_id, [a_ty])));
+                    ecx.add_goal(Goal::new(
+                        tcx,
+                        param_env,
+                        ty::TraitRef::new(tcx, sized_def_id, [a_ty]),
+                    ));
                     // The type must outlive the lifetime of the `dyn` we're unsizing into.
-                    ecx.add_goal(
-                        goal.with(tcx, ty::Binder::dummy(ty::OutlivesPredicate(a_ty, region))),
-                    );
+                    ecx.add_goal(Goal::new(
+                        tcx,
+                        param_env,
+                        ty::Binder::dummy(ty::OutlivesPredicate(a_ty, region)),
+                    ));
                     ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
                 }
                 // `[T; n]` -> `[T]` unsizing
                 (&ty::Array(a_elem_ty, ..), &ty::Slice(b_elem_ty)) => {
                     // We just require that the element type stays the same
-                    ecx.eq(goal.param_env, a_elem_ty, b_elem_ty)?;
+                    ecx.eq(param_env, a_elem_ty, b_elem_ty)?;
                     ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
                 }
                 // Struct unsizing `Struct<T>` -> `Struct<U>` where `T: Unsize<U>`
@@ -459,10 +574,15 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
 
                     // Finally, we require that `TailA: Unsize<TailB>` for the tail field
                     // types.
-                    ecx.eq(goal.param_env, unsized_a_ty, b_ty)?;
-                    ecx.add_goal(goal.with(
+                    ecx.eq(param_env, unsized_a_ty, b_ty)?;
+                    ecx.add_goal(Goal::new(
                         tcx,
-                        ty::TraitRef::new(tcx, goal.predicate.def_id(), [a_tail_ty, b_tail_ty]),
+                        param_env,
+                        ty::TraitRef::new(
+                            tcx,
+                            tcx.lang_items().unsize_trait().unwrap(),
+                            [a_tail_ty, b_tail_ty],
+                        ),
                     ));
                     ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
                 }
@@ -476,12 +596,17 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
                     // Substitute 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().chain([b_last_ty]).copied());
-                    ecx.eq(goal.param_env, unsized_a_ty, b_ty)?;
+                    ecx.eq(param_env, unsized_a_ty, b_ty)?;
 
                     // Similar to ADTs, require that the rest of the fields are equal.
-                    ecx.add_goal(goal.with(
+                    ecx.add_goal(Goal::new(
                         tcx,
-                        ty::TraitRef::new(tcx, goal.predicate.def_id(), [*a_last_ty, *b_last_ty]),
+                        param_env,
+                        ty::TraitRef::new(
+                            tcx,
+                            tcx.lang_items().unsize_trait().unwrap(),
+                            [*a_last_ty, *b_last_ty],
+                        ),
                     ));
                     ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
                 }
@@ -491,17 +616,15 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     }
 
     fn consider_builtin_dyn_upcast_candidates(
-        ecx: &mut EvalCtxt<'_, 'tcx>,
-        goal: Goal<'tcx, Self>,
-    ) -> Vec<CanonicalResponse<'tcx>> {
-        if goal.predicate.polarity != ty::ImplPolarity::Positive {
-            return vec![];
+        &mut self,
+        param_env: ty::ParamEnv<'tcx>,
+        a_ty: Ty<'tcx>,
+        b_ty: Ty<'tcx>,
+    ) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)> {
+        if a_ty.is_ty_var() || b_ty.is_ty_var() {
+            bug!("unexpected type variable in unsize goal")
         }
 
-        let tcx = ecx.tcx();
-
-        let a_ty = goal.predicate.self_ty();
-        let b_ty = goal.predicate.trait_ref.args.type_at(1);
         let ty::Dynamic(a_data, a_region, ty::Dyn) = *a_ty.kind() else {
             return vec![];
         };
@@ -509,6 +632,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
             return vec![];
         };
 
+        let tcx = self.tcx();
         // All of a's auto traits need to be in b's auto traits.
         let auto_traits_compatible =
             b_data.auto_traits().all(|b| a_data.auto_traits().any(|a| a == b));
@@ -516,123 +640,69 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
             return vec![];
         }
 
-        let mut unsize_dyn_to_principal = |principal: Option<ty::PolyExistentialTraitRef<'tcx>>| {
-            ecx.probe_candidate("upcast dyn to principle").enter(|ecx| -> Result<_, NoSolution> {
-                // Require that all of the trait predicates from A match B, except for
-                // the auto traits. We do this by constructing a new A type with B's
-                // auto traits, and equating these types.
-                let new_a_data = principal
-                    .into_iter()
-                    .map(|trait_ref| trait_ref.map_bound(ty::ExistentialPredicate::Trait))
-                    .chain(a_data.iter().filter(|a| {
-                        matches!(a.skip_binder(), ty::ExistentialPredicate::Projection(_))
-                    }))
-                    .chain(
-                        b_data
-                            .auto_traits()
-                            .map(ty::ExistentialPredicate::AutoTrait)
-                            .map(ty::Binder::dummy),
-                    );
-                let new_a_data = tcx.mk_poly_existential_predicates_from_iter(new_a_data);
-                let new_a_ty = Ty::new_dynamic(tcx, new_a_data, b_region, ty::Dyn);
-
-                // We also require that A's lifetime outlives B's lifetime.
-                ecx.eq(goal.param_env, new_a_ty, b_ty)?;
-                ecx.add_goal(
-                    goal.with(tcx, ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region))),
-                );
-                ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
-            })
-        };
+        // Try to match `a_ty` against `b_ty`, replacing `a_ty`'s principal trait ref with
+        // the supertrait principal and subtyping the types.
+        let unsize_dyn_to_principal =
+            |ecx: &mut Self, principal: Option<ty::PolyExistentialTraitRef<'tcx>>| {
+                ecx.probe_candidate("upcast dyn to principle").enter(
+                    |ecx| -> Result<_, NoSolution> {
+                        // Require that all of the trait predicates from A match B, except for
+                        // the auto traits. We do this by constructing a new A type with B's
+                        // auto traits, and equating these types.
+                        let new_a_data = principal
+                            .into_iter()
+                            .map(|trait_ref| trait_ref.map_bound(ty::ExistentialPredicate::Trait))
+                            .chain(a_data.iter().filter(|a| {
+                                matches!(a.skip_binder(), ty::ExistentialPredicate::Projection(_))
+                            }))
+                            .chain(
+                                b_data
+                                    .auto_traits()
+                                    .map(ty::ExistentialPredicate::AutoTrait)
+                                    .map(ty::Binder::dummy),
+                            );
+                        let new_a_data = tcx.mk_poly_existential_predicates_from_iter(new_a_data);
+                        let new_a_ty = Ty::new_dynamic(tcx, new_a_data, b_region, ty::Dyn);
+
+                        // We also require that A's lifetime outlives B's lifetime.
+                        ecx.eq(param_env, new_a_ty, b_ty)?;
+                        ecx.add_goal(Goal::new(
+                            tcx,
+                            param_env,
+                            ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region)),
+                        ));
+                        ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+                    },
+                )
+            };
 
         let mut responses = vec![];
         // If the principal def ids match (or are both none), then we're not doing
         // trait upcasting. We're just removing auto traits (or shortening the lifetime).
         if a_data.principal_def_id() == b_data.principal_def_id() {
-            if let Ok(response) = unsize_dyn_to_principal(a_data.principal()) {
-                responses.push(response);
-            }
-        } else if let Some(a_principal) = a_data.principal()
-            && let Some(b_principal) = b_data.principal()
-        {
-            for super_trait_ref in supertraits(tcx, a_principal.with_self_ty(tcx, a_ty)) {
-                if super_trait_ref.def_id() != b_principal.def_id() {
-                    continue;
-                }
-                let erased_trait_ref = super_trait_ref
-                    .map_bound(|trait_ref| ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref));
-                if let Ok(response) = unsize_dyn_to_principal(Some(erased_trait_ref)) {
-                    responses.push(response);
-                }
+            if let Ok(resp) = unsize_dyn_to_principal(self, a_data.principal()) {
+                responses.push((resp, BuiltinImplSource::Misc));
             }
+        } else if let Some(a_principal) = a_data.principal() {
+            self.walk_vtable(
+                a_principal.with_self_ty(tcx, a_ty),
+                |ecx, new_a_principal, _, vtable_vptr_slot| {
+                    if let Ok(resp) = unsize_dyn_to_principal(
+                        ecx,
+                        Some(new_a_principal.map_bound(|trait_ref| {
+                            ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)
+                        })),
+                    ) {
+                        responses
+                            .push((resp, BuiltinImplSource::TraitUpcasting { vtable_vptr_slot }));
+                    }
+                },
+            );
         }
 
         responses
     }
 
-    fn consider_builtin_discriminant_kind_candidate(
-        ecx: &mut EvalCtxt<'_, 'tcx>,
-        goal: Goal<'tcx, Self>,
-    ) -> QueryResult<'tcx> {
-        if goal.predicate.polarity != ty::ImplPolarity::Positive {
-            return Err(NoSolution);
-        }
-
-        // `DiscriminantKind` is automatically implemented for every type.
-        ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
-    }
-
-    fn consider_builtin_destruct_candidate(
-        ecx: &mut EvalCtxt<'_, 'tcx>,
-        goal: Goal<'tcx, Self>,
-    ) -> QueryResult<'tcx> {
-        if goal.predicate.polarity != ty::ImplPolarity::Positive {
-            return Err(NoSolution);
-        }
-
-        if !goal.param_env.is_const() {
-            // `Destruct` is automatically implemented for every type in
-            // non-const environments.
-            ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
-        } else {
-            // FIXME(-Ztrait-solver=next): Implement this when we get const working in the new solver
-            Err(NoSolution)
-        }
-    }
-
-    fn consider_builtin_transmute_candidate(
-        ecx: &mut EvalCtxt<'_, 'tcx>,
-        goal: Goal<'tcx, Self>,
-    ) -> QueryResult<'tcx> {
-        if goal.predicate.polarity != ty::ImplPolarity::Positive {
-            return Err(NoSolution);
-        }
-
-        // `rustc_transmute` does not have support for type or const params
-        if goal.has_non_region_placeholders() {
-            return Err(NoSolution);
-        }
-
-        // Erase regions because we compute layouts in `rustc_transmute`,
-        // which will ICE for region vars.
-        let args = ecx.tcx().erase_regions(goal.predicate.trait_ref.args);
-
-        let Some(assume) =
-            rustc_transmute::Assume::from_const(ecx.tcx(), goal.param_env, args.const_at(3))
-        else {
-            return Err(NoSolution);
-        };
-
-        let certainty = ecx.is_transmutable(
-            rustc_transmute::Types { dst: args.type_at(0), src: args.type_at(1) },
-            args.type_at(2),
-            assume,
-        )?;
-        ecx.evaluate_added_goals_and_make_canonical_response(certainty)
-    }
-}
-
-impl<'tcx> EvalCtxt<'_, 'tcx> {
     // Return `Some` if there is an impl (built-in or user provided) that may
     // hold for the self type of the goal, which for coherence and soundness
     // purposes must disqualify the built-in auto impl assembled by considering
@@ -761,4 +831,47 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         let candidates = self.assemble_and_evaluate_candidates(goal);
         self.merge_candidates(candidates)
     }
+
+    /// Normalize a non-self type when it is structually matched on when solving
+    /// a built-in goal. This is handled already through `assemble_candidates_after_normalizing_self_ty`
+    /// for the self type, but for other goals, additional normalization of other
+    /// arguments may be needed to completely implement the semantics of the trait.
+    ///
+    /// This is required when structurally matching on any trait argument that is
+    /// not the self type.
+    fn normalize_non_self_ty(
+        &mut self,
+        mut ty: Ty<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+    ) -> Result<Option<Ty<'tcx>>, NoSolution> {
+        if !matches!(ty.kind(), ty::Alias(..)) {
+            return Ok(Some(ty));
+        }
+
+        self.repeat_while_none(
+            |_| Ok(None),
+            |ecx| {
+                let ty::Alias(_, projection_ty) = *ty.kind() else {
+                    return Some(Ok(Some(ty)));
+                };
+
+                let normalized_ty = ecx.next_ty_infer();
+                let normalizes_to_goal = Goal::new(
+                    ecx.tcx(),
+                    param_env,
+                    ty::Binder::dummy(ty::ProjectionPredicate {
+                        projection_ty,
+                        term: normalized_ty.into(),
+                    }),
+                );
+                ecx.add_goal(normalizes_to_goal);
+                if let Err(err) = ecx.try_evaluate_added_goals() {
+                    return Some(Err(err));
+                }
+
+                ty = ecx.resolve_vars_if_possible(normalized_ty);
+                None
+            },
+        )
+    }
 }
diff --git a/compiler/rustc_trait_selection/src/solve/weak_types.rs b/compiler/rustc_trait_selection/src/solve/weak_types.rs
index c7717879a4a..54de32cf618 100644
--- a/compiler/rustc_trait_selection/src/solve/weak_types.rs
+++ b/compiler/rustc_trait_selection/src/solve/weak_types.rs
@@ -1,3 +1,8 @@
+//! Computes a normalizes-to (projection) goal for inherent associated types,
+//! `#![feature(lazy_type_alias)]` and `#![feature(type_alias_impl_trait)]`.
+//!
+//! Since a weak alias is not ambiguous, this just computes the `type_of` of
+//! the alias and registers the where-clauses of the type alias.
 use rustc_middle::traits::solve::{Certainty, Goal, QueryResult};
 use rustc_middle::ty;
 
diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs
index 27cd0f99f34..c26849d484e 100644
--- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs
+++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs
@@ -347,14 +347,12 @@ impl<'tcx> AutoTraitFinder<'tcx> {
             new_env = ty::ParamEnv::new(
                 tcx.mk_clauses_from_iter(normalized_preds.filter_map(|p| p.as_clause())),
                 param_env.reveal(),
-                param_env.constness(),
             );
         }
 
         let final_user_env = ty::ParamEnv::new(
             tcx.mk_clauses_from_iter(user_computed_preds.into_iter().filter_map(|p| p.as_clause())),
             user_env.reveal(),
-            user_env.constness(),
         );
         debug!(
             "evaluate_nested_obligations(ty={:?}, trait_did={:?}): succeeded with '{:?}' \
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
index c14839fe9be..cbd81cae989 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
@@ -687,9 +687,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                 match bound_predicate.skip_binder() {
                     ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_predicate)) => {
                         let trait_predicate = bound_predicate.rebind(trait_predicate);
-                        let mut trait_predicate = self.resolve_vars_if_possible(trait_predicate);
+                        let trait_predicate = self.resolve_vars_if_possible(trait_predicate);
 
-                        trait_predicate.remap_constness_diag(obligation.param_env);
                         let predicate_is_const = ty::BoundConstness::ConstIfConst
                             == trait_predicate.skip_binder().constness;
 
@@ -3108,11 +3107,11 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
         span: Span,
     ) -> UnsatisfiedConst {
         let mut unsatisfied_const = UnsatisfiedConst(false);
-        if trait_predicate.is_const_if_const() && obligation.param_env.is_const() {
+        if trait_predicate.is_const_if_const() {
             let non_const_predicate = trait_ref.without_const();
             let non_const_obligation = Obligation {
                 cause: obligation.cause.clone(),
-                param_env: obligation.param_env.without_const(),
+                param_env: obligation.param_env,
                 predicate: non_const_predicate.to_predicate(self.tcx),
                 recursion_depth: obligation.recursion_depth,
             };
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs
index fbe8d4a6680..a530b27bc91 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs
@@ -329,18 +329,13 @@ pub struct OnUnimplementedNote {
 }
 
 /// Append a message for `~const Trait` errors.
-#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
 pub enum AppendConstMessage {
+    #[default]
     Default,
     Custom(Symbol),
 }
 
-impl Default for AppendConstMessage {
-    fn default() -> Self {
-        AppendConstMessage::Default
-    }
-}
-
 impl<'tcx> OnUnimplementedDirective {
     fn parse(
         tcx: TyCtxt<'tcx>,
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
index 073a2a2b1a0..ff7854d51d9 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -5,6 +5,7 @@ use super::{
     PredicateObligation,
 };
 
+use crate::errors;
 use crate::infer::InferCtxt;
 use crate::traits::{NormalizeExt, ObligationCtxt};
 
@@ -776,18 +777,14 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                 real_trait_pred = parent_trait_pred;
             }
 
-            let real_ty = real_trait_pred.self_ty();
             // We `erase_late_bound_regions` here because `make_subregion` does not handle
             // `ReLateBound`, and we don't particularly care about the regions.
-            if !self.can_eq(
-                obligation.param_env,
-                self.tcx.erase_late_bound_regions(real_ty),
-                arg_ty,
-            ) {
+            let real_ty = self.tcx.erase_late_bound_regions(real_trait_pred.self_ty());
+            if !self.can_eq(obligation.param_env, real_ty, arg_ty) {
                 continue;
             }
 
-            if let ty::Ref(region, base_ty, mutbl) = *real_ty.skip_binder().kind() {
+            if let ty::Ref(region, base_ty, mutbl) = *real_ty.kind() {
                 let autoderef = (self.autoderef_steps)(base_ty);
                 if let Some(steps) =
                     autoderef.into_iter().enumerate().find_map(|(steps, (ty, obligations))| {
@@ -1470,7 +1467,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
 
                         let span = if needs_parens { span } else { span.shrink_to_lo() };
                         let suggestions = if !needs_parens {
-                            vec![(span.shrink_to_lo(), format!("{}", sugg_prefix))]
+                            vec![(span.shrink_to_lo(), sugg_prefix)]
                         } else {
                             vec![
                                 (span.shrink_to_lo(), format!("{}(", sugg_prefix)),
@@ -3139,7 +3136,6 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
             ObligationCauseCode::ImplDerivedObligation(ref data) => {
                 let mut parent_trait_pred =
                     self.resolve_vars_if_possible(data.derived.parent_trait_pred);
-                parent_trait_pred.remap_constness_diag(param_env);
                 let parent_def_id = parent_trait_pred.def_id();
                 let (self_ty, file) =
                     self.tcx.short_ty_string(parent_trait_pred.skip_binder().self_ty());
@@ -4031,6 +4027,10 @@ fn hint_missing_borrow<'tcx>(
     found_node: Node<'_>,
     err: &mut Diagnostic,
 ) {
+    if matches!(found_node, Node::TraitItem(..)) {
+        return;
+    }
+
     let found_args = match found.kind() {
         ty::FnPtr(f) => infcx.instantiate_binder_with_placeholders(*f).inputs().iter(),
         kind => {
@@ -4102,19 +4102,11 @@ fn hint_missing_borrow<'tcx>(
     }
 
     if !to_borrow.is_empty() {
-        err.multipart_suggestion_verbose(
-            "consider borrowing the argument",
-            to_borrow,
-            Applicability::MaybeIncorrect,
-        );
+        err.subdiagnostic(errors::AdjustSignatureBorrow::Borrow { to_borrow });
     }
 
     if !remove_borrow.is_empty() {
-        err.multipart_suggestion_verbose(
-            "do not borrow the argument",
-            remove_borrow,
-            Applicability::MaybeIncorrect,
-        );
+        err.subdiagnostic(errors::AdjustSignatureBorrow::RemoveBorrow { remove_borrow });
     }
 }
 
diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs
index 1c145999184..593f669e9bb 100644
--- a/compiler/rustc_trait_selection/src/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/mod.rs
@@ -328,11 +328,7 @@ pub fn normalize_param_env_or_error<'tcx>(
 
     debug!("normalize_param_env_or_error: elaborated-predicates={:?}", predicates);
 
-    let elaborated_env = ty::ParamEnv::new(
-        tcx.mk_clauses(&predicates),
-        unnormalized_env.reveal(),
-        unnormalized_env.constness(),
-    );
+    let elaborated_env = ty::ParamEnv::new(tcx.mk_clauses(&predicates), unnormalized_env.reveal());
 
     // HACK: we are trying to normalize the param-env inside *itself*. The problem is that
     // normalization expects its param-env to be already normalized, which means we have
@@ -376,11 +372,8 @@ pub fn normalize_param_env_or_error<'tcx>(
     // here. I believe they should not matter, because we are ignoring TypeOutlives param-env
     // predicates here anyway. Keeping them here anyway because it seems safer.
     let outlives_env = non_outlives_predicates.iter().chain(&outlives_predicates).cloned();
-    let outlives_env = ty::ParamEnv::new(
-        tcx.mk_clauses_from_iter(outlives_env),
-        unnormalized_env.reveal(),
-        unnormalized_env.constness(),
-    );
+    let outlives_env =
+        ty::ParamEnv::new(tcx.mk_clauses_from_iter(outlives_env), unnormalized_env.reveal());
     let Ok(outlives_predicates) =
         do_normalize_predicates(tcx, cause, outlives_env, outlives_predicates)
     else {
@@ -393,11 +386,7 @@ pub fn normalize_param_env_or_error<'tcx>(
     let mut predicates = non_outlives_predicates;
     predicates.extend(outlives_predicates);
     debug!("normalize_param_env_or_error: final predicates={:?}", predicates);
-    ty::ParamEnv::new(
-        tcx.mk_clauses(&predicates),
-        unnormalized_env.reveal(),
-        unnormalized_env.constness(),
-    )
+    ty::ParamEnv::new(tcx.mk_clauses(&predicates), unnormalized_env.reveal())
 }
 
 /// Normalize a type and process all resulting obligations, returning any errors.
@@ -474,11 +463,14 @@ fn subst_and_check_impossible_predicates<'tcx>(
     result
 }
 
-/// Checks whether a trait's method is impossible to call on a given impl.
+/// Checks whether a trait's associated item is impossible to reference on a given impl.
 ///
 /// This only considers predicates that reference the impl's generics, and not
 /// those that reference the method's generics.
-fn is_impossible_method(tcx: TyCtxt<'_>, (impl_def_id, trait_item_def_id): (DefId, DefId)) -> bool {
+fn is_impossible_associated_item(
+    tcx: TyCtxt<'_>,
+    (impl_def_id, trait_item_def_id): (DefId, DefId),
+) -> bool {
     struct ReferencesOnlyParentGenerics<'tcx> {
         tcx: TyCtxt<'tcx>,
         generics: &'tcx ty::Generics,
@@ -556,7 +548,7 @@ pub fn provide(providers: &mut Providers) {
         specializes: specialize::specializes,
         subst_and_check_impossible_predicates,
         check_tys_might_be_eq: misc::check_tys_might_be_eq,
-        is_impossible_method,
+        is_impossible_associated_item,
         ..*providers
     };
 }
diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs
index 143e8412967..abb05be80e9 100644
--- a/compiler/rustc_trait_selection/src/traits/object_safety.rs
+++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs
@@ -761,11 +761,7 @@ fn receiver_is_dispatchable<'tcx>(
         let caller_bounds =
             param_env.caller_bounds().iter().chain([unsize_predicate, trait_predicate]);
 
-        ty::ParamEnv::new(
-            tcx.mk_clauses_from_iter(caller_bounds),
-            param_env.reveal(),
-            param_env.constness(),
-        )
+        ty::ParamEnv::new(tcx.mk_clauses_from_iter(caller_bounds), param_env.reveal())
     };
 
     // Receiver: DispatchFromDyn<Receiver[Self => U]>
diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs
index a39fc1f1771..a35dd1f1a59 100644
--- a/compiler/rustc_trait_selection/src/traits/project.rs
+++ b/compiler/rustc_trait_selection/src/traits/project.rs
@@ -4,7 +4,6 @@ use super::check_args_compatible;
 use super::specialization_graph;
 use super::translate_args;
 use super::util;
-use super::ImplSourceUserDefinedData;
 use super::MismatchedProjectionTypes;
 use super::Obligation;
 use super::ObligationCause;
@@ -13,6 +12,9 @@ use super::Selection;
 use super::SelectionContext;
 use super::SelectionError;
 use super::{Normalized, NormalizedTy, ProjectionCacheEntry, ProjectionCacheKey};
+use rustc_middle::traits::BuiltinImplSource;
+use rustc_middle::traits::ImplSource;
+use rustc_middle::traits::ImplSourceUserDefinedData;
 
 use crate::errors::InherentProjectionNormalizationOverflow;
 use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
@@ -1717,7 +1719,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
         };
 
         let eligible = match &impl_source {
-            super::ImplSource::UserDefined(impl_data) => {
+            ImplSource::UserDefined(impl_data) => {
                 // We have to be careful when projecting out of an
                 // impl because of specialization. If we are not in
                 // codegen (i.e., projection mode is not "any"), and the
@@ -1767,7 +1769,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
                     }
                 }
             }
-            super::ImplSource::Builtin(..) => {
+            ImplSource::Builtin(BuiltinImplSource::Misc, _) => {
                 // While a builtin impl may be known to exist, the associated type may not yet
                 // be known. Any type with multiple potential associated types is therefore
                 // not eligible.
@@ -1891,7 +1893,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
                     bug!("unexpected builtin trait with associated type: {trait_ref:?}")
                 }
             }
-            super::ImplSource::Param(..) => {
+            ImplSource::Param(..) => {
                 // This case tell us nothing about the value of an
                 // associated type. Consider:
                 //
@@ -1919,13 +1921,14 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
                 // in `assemble_candidates_from_param_env`.
                 false
             }
-            super::ImplSource::Object(_) => {
+            ImplSource::Builtin(BuiltinImplSource::Object { .. }, _) => {
                 // Handled by the `Object` projection candidate. See
                 // `assemble_candidates_from_object_ty` for an explanation of
                 // why we special case object types.
                 false
             }
-            | super::ImplSource::TraitUpcasting(_) => {
+            ImplSource::Builtin(BuiltinImplSource::TraitUpcasting { .. }, _)
+            | ImplSource::Builtin(BuiltinImplSource::TupleUnsizing, _) => {
                 // These traits have no associated types.
                 selcx.tcx().sess.delay_span_bug(
                     obligation.cause.span,
@@ -1985,8 +1988,8 @@ fn confirm_select_candidate<'cx, 'tcx>(
     impl_source: Selection<'tcx>,
 ) -> Progress<'tcx> {
     match impl_source {
-        super::ImplSource::UserDefined(data) => confirm_impl_candidate(selcx, obligation, data),
-        super::ImplSource::Builtin(data) => {
+        ImplSource::UserDefined(data) => confirm_impl_candidate(selcx, obligation, data),
+        ImplSource::Builtin(BuiltinImplSource::Misc, data) => {
             let trait_def_id = obligation.predicate.trait_def_id(selcx.tcx());
             let lang_items = selcx.tcx().lang_items();
             if lang_items.gen_trait() == Some(trait_def_id) {
@@ -2003,9 +2006,10 @@ fn confirm_select_candidate<'cx, 'tcx>(
                 confirm_builtin_candidate(selcx, obligation, data)
             }
         }
-        super::ImplSource::Object(_)
-        | super::ImplSource::Param(..)
-        | super::ImplSource::TraitUpcasting(_) => {
+        ImplSource::Builtin(BuiltinImplSource::Object { .. }, _)
+        | ImplSource::Param(..)
+        | ImplSource::Builtin(BuiltinImplSource::TraitUpcasting { .. }, _)
+        | ImplSource::Builtin(BuiltinImplSource::TupleUnsizing, _) => {
             // we don't create Select candidates with this kind of resolution
             span_bug!(
                 obligation.cause.span,
diff --git a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs
index a50644bb709..65f32b1c48a 100644
--- a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs
@@ -1,5 +1,4 @@
 use rustc_infer::traits::{TraitEngine, TraitEngineExt};
-use rustc_middle::ty;
 
 use crate::infer::canonical::OriginalQueryValues;
 use crate::infer::InferCtxt;
@@ -66,17 +65,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
     ) -> Result<EvaluationResult, OverflowError> {
         let mut _orig_values = OriginalQueryValues::default();
 
-        let param_env = match obligation.predicate.kind().skip_binder() {
-            ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => {
-                // we ignore the value set to it.
-                let mut _constness = pred.constness;
-                obligation
-                    .param_env
-                    .with_constness(_constness.and(obligation.param_env.constness()))
-            }
-            // constness has no effect on the given predicate.
-            _ => obligation.param_env.without_const(),
-        };
+        let param_env = obligation.param_env;
 
         if self.next_trait_solver() {
             self.probe(|snapshot| {
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs
index 420d6326553..302b6016e5e 100644
--- a/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs
@@ -82,7 +82,6 @@ fn relate_mir_and_user_args<'tcx>(
     def_id: DefId,
     user_args: UserArgs<'tcx>,
 ) -> Result<(), NoSolution> {
-    let param_env = param_env.without_const();
     let UserArgs { user_self_ty, args } = user_args;
     let tcx = ocx.infcx.tcx;
     let cause = ObligationCause::dummy_with_span(span);
diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
index f1d870269a6..b9f31be25b1 100644
--- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
@@ -124,11 +124,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
 
             self.assemble_candidates_from_projected_tys(obligation, &mut candidates);
             self.assemble_candidates_from_caller_bounds(stack, &mut candidates)?;
-            // Auto implementations have lower priority, so we only
-            // consider triggering a default if there is no other impl that can apply.
-            if candidates.vec.is_empty() {
-                self.assemble_candidates_from_auto_impls(obligation, &mut candidates);
-            }
+            self.assemble_candidates_from_auto_impls(obligation, &mut candidates);
         }
         debug!("candidate list size: {}", candidates.vec.len());
         Ok(candidates)
@@ -513,7 +509,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                     // for an example of a test case that exercises
                     // this path.
                 }
-                ty::Infer(ty::TyVar(_)) => {
+                ty::Infer(ty::TyVar(_) | ty::IntVar(_) | ty::FloatVar(_)) => {
                     // The auto impl might apply; we don't know.
                     candidates.ambiguous = true;
                 }
@@ -533,7 +529,63 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                     }
                 }
 
-                _ => candidates.vec.push(AutoImplCandidate),
+                ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
+                    bug!(
+                        "asked to assemble auto trait candidates of unexpected type: {:?}",
+                        self_ty
+                    );
+                }
+
+                ty::Alias(_, _)
+                    if candidates.vec.iter().any(|c| matches!(c, ProjectionCandidate(..))) =>
+                {
+                    // We do not generate an auto impl candidate for `impl Trait`s which already
+                    // reference our auto trait.
+                    //
+                    // For example during candidate assembly for `impl Send: Send`, we don't have
+                    // to look at the constituent types for this opaque types to figure out that this
+                    // trivially holds.
+                    //
+                    // Note that this is only sound as projection candidates of opaque types
+                    // are always applicable for auto traits.
+                }
+                ty::Alias(_, _) => candidates.vec.push(AutoImplCandidate),
+
+                ty::Bool
+                | ty::Char
+                | ty::Int(_)
+                | ty::Uint(_)
+                | ty::Float(_)
+                | ty::Str
+                | ty::Array(_, _)
+                | ty::Slice(_)
+                | ty::Adt(..)
+                | ty::RawPtr(_)
+                | ty::Ref(..)
+                | ty::FnDef(..)
+                | ty::FnPtr(_)
+                | ty::Closure(_, _)
+                | ty::Generator(..)
+                | ty::Never
+                | ty::Tuple(_)
+                | ty::GeneratorWitness(_)
+                | ty::GeneratorWitnessMIR(..) => {
+                    // Only consider auto impls if there are no manual impls for the root of `self_ty`.
+                    //
+                    // For example, we only consider auto candidates for `&i32: Auto` if no explicit impl
+                    // for `&SomeType: Auto` exists. Due to E0321 the only crate where impls
+                    // for `&SomeType: Auto` can be defined is the crate where `Auto` has been defined.
+                    //
+                    // Generally, we have to guarantee that for all `SimplifiedType`s the only crate
+                    // which may define impls for that type is either the crate defining the type
+                    // or the trait. This should be guaranteed by the orphan check.
+                    let mut has_impl = false;
+                    self.tcx().for_each_relevant_impl(def_id, self_ty, |_| has_impl = true);
+                    if !has_impl {
+                        candidates.vec.push(AutoImplCandidate)
+                    }
+                }
+                ty::Error(_) => {} // do not add an auto trait impl for `ty::Error` for now.
             }
         }
     }
@@ -839,7 +891,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
     ) {
         // If the predicate is `~const Destruct` in a non-const environment, we don't actually need
         // to check anything. We'll short-circuit checking any obligations in confirmation, too.
-        if !obligation.is_const() {
+        // FIXME(effects)
+        if true {
             candidates.vec.push(ConstDestructCandidate(None));
             return;
         }
@@ -979,20 +1032,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             return;
         }
 
-        // First, try computing an exact naive layout in case the type is generic.
-        let is_pointer_like = if let Ok(layout) = tcx.naive_layout_of(key) {
-            layout.is_pointer_like(&tcx.data_layout).unwrap_or_else(|| {
-                // Second, we fall back to full layout computation.
-                tcx.layout_of(key)
-                    .ok()
-                    .filter(|l| l.layout.is_pointer_like(&tcx.data_layout))
-                    .is_some()
-            })
-        } else {
-            false
-        };
-
-        if is_pointer_like {
+        if let Ok(layout) = tcx.layout_of(key)
+            && layout.layout.is_pointer_like(&tcx.data_layout)
+        {
             candidates.vec.push(BuiltinCandidate { has_nested: false });
         }
     }
diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
index 2cb2895b476..7140fedb74a 100644
--- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
@@ -11,10 +11,10 @@ use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_hir::lang_items::LangItem;
 use rustc_infer::infer::LateBoundRegionConversionTime::HigherRankedType;
 use rustc_infer::infer::{DefineOpaqueTypes, InferOk};
-use rustc_middle::traits::SelectionOutputTypeParameterMismatch;
+use rustc_middle::traits::{BuiltinImplSource, SelectionOutputTypeParameterMismatch};
 use rustc_middle::ty::{
-    self, Binder, GenericArgs, GenericArgsRef, GenericParamDefKind, ToPolyTraitRef, ToPredicate,
-    TraitPredicate, TraitRef, Ty, TyCtxt, TypeVisitableExt,
+    self, GenericArgs, GenericArgsRef, GenericParamDefKind, ToPolyTraitRef, ToPredicate,
+    TraitPredicate, Ty, TyCtxt, TypeVisitableExt,
 };
 use rustc_span::def_id::DefId;
 
@@ -26,9 +26,9 @@ use crate::traits::vtable::{
 };
 use crate::traits::{
     BuiltinDerivedObligation, ImplDerivedObligation, ImplDerivedObligationCause, ImplSource,
-    ImplSourceObjectData, ImplSourceTraitUpcastingData, ImplSourceUserDefinedData, Normalized,
-    Obligation, ObligationCause, OutputTypeParameterMismatch, PolyTraitObligation,
-    PredicateObligation, Selection, SelectionError, TraitNotObjectSafe, Unimplemented,
+    ImplSourceUserDefinedData, Normalized, Obligation, ObligationCause,
+    OutputTypeParameterMismatch, PolyTraitObligation, PredicateObligation, Selection,
+    SelectionError, TraitNotObjectSafe, Unimplemented,
 };
 
 use super::BuiltinImplConditions;
@@ -48,18 +48,18 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         let mut impl_src = match candidate {
             BuiltinCandidate { has_nested } => {
                 let data = self.confirm_builtin_candidate(obligation, has_nested);
-                ImplSource::Builtin(data)
+                ImplSource::Builtin(BuiltinImplSource::Misc, data)
             }
 
             TransmutabilityCandidate => {
                 let data = self.confirm_transmutability_candidate(obligation)?;
-                ImplSource::Builtin(data)
+                ImplSource::Builtin(BuiltinImplSource::Misc, data)
             }
 
             ParamCandidate(param) => {
                 let obligations =
                     self.confirm_param_candidate(obligation, param.map_bound(|t| t.trait_ref));
-                ImplSource::Param(obligations, param.skip_binder().constness)
+                ImplSource::Param(param.skip_binder().constness, obligations)
             }
 
             ImplCandidate(impl_def_id) => {
@@ -68,64 +68,57 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
 
             AutoImplCandidate => {
                 let data = self.confirm_auto_impl_candidate(obligation)?;
-                ImplSource::Builtin(data)
+                ImplSource::Builtin(BuiltinImplSource::Misc, data)
             }
 
             ProjectionCandidate(idx, constness) => {
                 let obligations = self.confirm_projection_candidate(obligation, idx)?;
-                ImplSource::Param(obligations, constness)
+                ImplSource::Param(constness, obligations)
             }
 
-            ObjectCandidate(idx) => {
-                let data = self.confirm_object_candidate(obligation, idx)?;
-                ImplSource::Object(data)
-            }
+            ObjectCandidate(idx) => self.confirm_object_candidate(obligation, idx)?,
 
             ClosureCandidate { .. } => {
                 let vtable_closure = self.confirm_closure_candidate(obligation)?;
-                ImplSource::Builtin(vtable_closure)
+                ImplSource::Builtin(BuiltinImplSource::Misc, vtable_closure)
             }
 
             GeneratorCandidate => {
                 let vtable_generator = self.confirm_generator_candidate(obligation)?;
-                ImplSource::Builtin(vtable_generator)
+                ImplSource::Builtin(BuiltinImplSource::Misc, vtable_generator)
             }
 
             FutureCandidate => {
                 let vtable_future = self.confirm_future_candidate(obligation)?;
-                ImplSource::Builtin(vtable_future)
+                ImplSource::Builtin(BuiltinImplSource::Misc, vtable_future)
             }
 
             FnPointerCandidate { is_const } => {
                 let data = self.confirm_fn_pointer_candidate(obligation, is_const)?;
-                ImplSource::Builtin(data)
+                ImplSource::Builtin(BuiltinImplSource::Misc, data)
             }
 
             TraitAliasCandidate => {
                 let data = self.confirm_trait_alias_candidate(obligation);
-                ImplSource::Builtin(data)
+                ImplSource::Builtin(BuiltinImplSource::Misc, data)
             }
 
             BuiltinObjectCandidate => {
                 // This indicates something like `Trait + Send: Send`. In this case, we know that
                 // this holds because that's what the object type is telling us, and there's really
                 // no additional obligations to prove and no types in particular to unify, etc.
-                ImplSource::Builtin(Vec::new())
+                ImplSource::Builtin(BuiltinImplSource::Misc, Vec::new())
             }
 
-            BuiltinUnsizeCandidate => {
-                let data = self.confirm_builtin_unsize_candidate(obligation)?;
-                ImplSource::Builtin(data)
-            }
+            BuiltinUnsizeCandidate => self.confirm_builtin_unsize_candidate(obligation)?,
 
             TraitUpcastingUnsizeCandidate(idx) => {
-                let data = self.confirm_trait_upcasting_unsize_candidate(obligation, idx)?;
-                ImplSource::TraitUpcasting(data)
+                self.confirm_trait_upcasting_unsize_candidate(obligation, idx)?
             }
 
             ConstDestructCandidate(def_id) => {
                 let data = self.confirm_const_destruct_candidate(obligation, def_id)?;
-                ImplSource::Builtin(data)
+                ImplSource::Builtin(BuiltinImplSource::Misc, data)
             }
         };
 
@@ -484,7 +477,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         &mut self,
         obligation: &PolyTraitObligation<'tcx>,
         index: usize,
-    ) -> Result<ImplSourceObjectData<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
+    ) -> Result<ImplSource<'tcx, PredicateObligation<'tcx>>, SelectionError<'tcx>> {
         let tcx = self.tcx();
         debug!(?obligation, ?index, "confirm_object_candidate");
 
@@ -648,13 +641,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             (unnormalized_upcast_trait_ref, ty::Binder::dummy(object_trait_ref)),
         );
 
-        Ok(ImplSourceObjectData { vtable_base, nested })
+        Ok(ImplSource::Builtin(BuiltinImplSource::Object { vtable_base: vtable_base }, nested))
     }
 
     fn confirm_fn_pointer_candidate(
         &mut self,
         obligation: &PolyTraitObligation<'tcx>,
-        is_const: bool,
+        // FIXME(effects)
+        _is_const: bool,
     ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
         debug!(?obligation, "confirm_fn_pointer_candidate");
 
@@ -681,16 +675,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         let mut nested = self.confirm_poly_trait_refs(obligation, trait_ref)?;
         let cause = obligation.derived_cause(BuiltinDerivedObligation);
 
-        if obligation.is_const() && !is_const {
-            // function is a trait method
-            if let ty::FnDef(def_id, args) = self_ty.kind() && let Some(trait_id) = tcx.trait_of_item(*def_id) {
-                let trait_ref = TraitRef::from_method(tcx, trait_id, *args);
-                let poly_trait_pred = Binder::dummy(trait_ref).with_constness(ty::BoundConstness::ConstIfConst);
-                let obligation = Obligation::new(tcx, cause.clone(), obligation.param_env, poly_trait_pred);
-                nested.push(obligation);
-            }
-        }
-
         // Confirm the `type Output: Sized;` bound that is present on `FnOnce`
         let output_ty = self.infcx.instantiate_binder_with_placeholders(sig.output());
         let output_ty = normalize_with_depth_to(
@@ -897,7 +881,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         &mut self,
         obligation: &PolyTraitObligation<'tcx>,
         idx: usize,
-    ) -> Result<ImplSourceTraitUpcastingData<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
+    ) -> Result<ImplSource<'tcx, PredicateObligation<'tcx>>, SelectionError<'tcx>> {
         let tcx = self.tcx();
 
         // `assemble_candidates_for_unsizing` should ensure there are no late-bound
@@ -994,13 +978,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         let vtable_vptr_slot =
             prepare_vtable_segments(tcx, source_trait_ref, vtable_segment_callback).unwrap();
 
-        Ok(ImplSourceTraitUpcastingData { vtable_vptr_slot, nested })
+        Ok(ImplSource::Builtin(BuiltinImplSource::TraitUpcasting { vtable_vptr_slot }, nested))
     }
 
     fn confirm_builtin_unsize_candidate(
         &mut self,
         obligation: &PolyTraitObligation<'tcx>,
-    ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
+    ) -> Result<ImplSource<'tcx, PredicateObligation<'tcx>>, SelectionError<'tcx>> {
         let tcx = self.tcx();
 
         // `assemble_candidates_for_unsizing` should ensure there are no late-bound
@@ -1008,10 +992,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         let source = self.infcx.shallow_resolve(obligation.self_ty().no_bound_vars().unwrap());
         let target = obligation.predicate.skip_binder().trait_ref.args.type_at(1);
         let target = self.infcx.shallow_resolve(target);
-
         debug!(?source, ?target, "confirm_builtin_unsize_candidate");
 
         let mut nested = vec![];
+        let src;
         match (source.kind(), target.kind()) {
             // Trait+Kx+'a -> Trait+Ky+'b (auto traits and lifetime subtyping).
             (&ty::Dynamic(ref data_a, r_a, dyn_a), &ty::Dynamic(ref data_b, r_b, dyn_b))
@@ -1055,6 +1039,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                     obligation.param_env,
                     obligation.predicate.rebind(outlives),
                 ));
+
+                src = BuiltinImplSource::Misc;
             }
 
             // `T` -> `Trait`
@@ -1101,6 +1087,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 nested.push(predicate_to_obligation(
                     ty::Binder::dummy(ty::ClauseKind::TypeOutlives(outlives)).to_predicate(tcx),
                 ));
+
+                src = BuiltinImplSource::Misc;
             }
 
             // `[T; n]` -> `[T]`
@@ -1111,6 +1099,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                     .eq(DefineOpaqueTypes::No, b, a)
                     .map_err(|_| Unimplemented)?;
                 nested.extend(obligations);
+
+                src = BuiltinImplSource::Misc;
             }
 
             // `Struct<T>` -> `Struct<U>`
@@ -1167,6 +1157,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                     ),
                 );
                 nested.push(tail_unsize_obligation);
+
+                src = BuiltinImplSource::Misc;
             }
 
             // `(.., T)` -> `(.., U)`
@@ -1194,12 +1186,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                     ty::TraitRef::new(tcx, obligation.predicate.def_id(), [a_last, b_last]),
                 );
                 nested.push(last_unsize_obligation);
+
+                src = BuiltinImplSource::TupleUnsizing;
             }
 
             _ => bug!("source: {source}, target: {target}"),
         };
 
-        Ok(nested)
+        Ok(ImplSource::Builtin(src, nested))
     }
 
     fn confirm_const_destruct_candidate(
@@ -1208,7 +1202,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         impl_def_id: Option<DefId>,
     ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
         // `~const Destruct` in a non-const environment is always trivially true, since our type is `Drop`
-        if !obligation.is_const() {
+        // FIXME(effects)
+        if true {
             return Ok(vec![]);
         }
 
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index e086489b1bc..b4875ec0ea3 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -1000,13 +1000,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         }
 
         let stack = self.push_stack(previous_stack, &obligation);
-        let mut fresh_trait_pred = stack.fresh_trait_pred;
-        let mut param_env = obligation.param_env;
-
-        fresh_trait_pred = fresh_trait_pred.map_bound(|mut pred| {
-            pred.remap_constness(&mut param_env);
-            pred
-        });
+        let fresh_trait_pred = stack.fresh_trait_pred;
+        let param_env = obligation.param_env;
 
         debug!(?fresh_trait_pred);
 
@@ -1386,8 +1381,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         (result, dep_node)
     }
 
-    /// filter_impls filters constant trait obligations and candidates that have a positive impl
-    /// for a negative goal and a negative impl for a positive goal
+    /// filter_impls filters candidates that have a positive impl for a negative
+    /// goal and a negative impl for a positive goal
     #[instrument(level = "debug", skip(self, candidates))]
     fn filter_impls(
         &mut self,
@@ -1399,42 +1394,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         let mut result = Vec::with_capacity(candidates.len());
 
         for candidate in candidates {
-            // Respect const trait obligations
-            if obligation.is_const() {
-                match candidate {
-                    // const impl
-                    ImplCandidate(def_id) if tcx.constness(def_id) == hir::Constness::Const => {}
-                    // const param
-                    ParamCandidate(trait_pred) if trait_pred.is_const_if_const() => {}
-                    // const projection
-                    ProjectionCandidate(_, ty::BoundConstness::ConstIfConst)
-                    // auto trait impl
-                    | AutoImplCandidate
-                    // generator / future, this will raise error in other places
-                    // or ignore error with const_async_blocks feature
-                    | GeneratorCandidate
-                    | FutureCandidate
-                    // FnDef where the function is const
-                    | FnPointerCandidate { is_const: true }
-                    | ConstDestructCandidate(_)
-                    | ClosureCandidate { is_const: true } => {}
-
-                    FnPointerCandidate { is_const: false } => {
-                        if let ty::FnDef(def_id, _) = obligation.self_ty().skip_binder().kind() && tcx.trait_of_item(*def_id).is_some() {
-                            // Trait methods are not seen as const unless the trait is implemented as const.
-                            // We do not filter that out in here, but nested obligations will be needed to confirm this.
-                        } else {
-                            continue
-                        }
-                    }
-
-                    _ => {
-                        // reject all other types of candidates
-                        continue;
-                    }
-                }
-            }
-
             if let ImplCandidate(def_id) = candidate {
                 if ty::ImplPolarity::Reservation == tcx.impl_polarity(def_id)
                     || obligation.polarity() == tcx.impl_polarity(def_id)
@@ -1487,7 +1446,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
     fn is_knowable<'o>(&mut self, stack: &TraitObligationStack<'o, 'tcx>) -> Result<(), Conflict> {
         debug!("is_knowable(intercrate={:?})", self.is_intercrate());
 
-        if !self.is_intercrate() || stack.obligation.polarity() == ty::ImplPolarity::Negative {
+        if !self.is_intercrate() {
             return Ok(());
         }
 
@@ -1528,7 +1487,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
 
     fn check_candidate_cache(
         &mut self,
-        mut param_env: ty::ParamEnv<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
         cache_fresh_trait_pred: ty::PolyTraitPredicate<'tcx>,
     ) -> Option<SelectionResult<'tcx, SelectionCandidate<'tcx>>> {
         // Neither the global nor local cache is aware of intercrate
@@ -1539,8 +1498,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             return None;
         }
         let tcx = self.tcx();
-        let mut pred = cache_fresh_trait_pred.skip_binder();
-        pred.remap_constness(&mut param_env);
+        let pred = cache_fresh_trait_pred.skip_binder();
 
         if self.can_use_global_caches(param_env) {
             if let Some(res) = tcx.selection_cache.get(&(param_env, pred), tcx) {
@@ -1586,15 +1544,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
     #[instrument(skip(self, param_env, cache_fresh_trait_pred, dep_node), level = "debug")]
     fn insert_candidate_cache(
         &mut self,
-        mut param_env: ty::ParamEnv<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
         cache_fresh_trait_pred: ty::PolyTraitPredicate<'tcx>,
         dep_node: DepNodeIndex,
         candidate: SelectionResult<'tcx, SelectionCandidate<'tcx>>,
     ) {
         let tcx = self.tcx();
-        let mut pred = cache_fresh_trait_pred.skip_binder();
-
-        pred.remap_constness(&mut param_env);
+        let pred = cache_fresh_trait_pred.skip_binder();
 
         if !self.can_cache_candidate(&candidate) {
             debug!(?pred, ?candidate, "insert_candidate_cache - candidate is not cacheable");
@@ -1827,6 +1783,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
     /// candidates and prefer where-clause candidates.
     ///
     /// See the comment for "SelectionCandidate" for more details.
+    #[instrument(level = "debug", skip(self))]
     fn candidate_should_be_dropped_in_favor_of(
         &mut self,
         victim: &EvaluatedCandidate<'tcx>,
@@ -1850,13 +1807,6 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
         // This is a fix for #53123 and prevents winnowing from accidentally extending the
         // lifetime of a variable.
         match (&other.candidate, &victim.candidate) {
-            (_, AutoImplCandidate) | (AutoImplCandidate, _) => {
-                bug!(
-                    "default implementations shouldn't be recorded \
-                    when there are other valid candidates"
-                );
-            }
-
             // FIXME(@jswrenn): this should probably be more sophisticated
             (TransmutabilityCandidate, _) | (_, TransmutabilityCandidate) => DropVictim::No,
 
@@ -1898,6 +1848,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
             (
                 ParamCandidate(ref other_cand),
                 ImplCandidate(..)
+                | AutoImplCandidate
                 | ClosureCandidate { .. }
                 | GeneratorCandidate
                 | FutureCandidate
@@ -1925,6 +1876,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
             }
             (
                 ImplCandidate(_)
+                | AutoImplCandidate
                 | ClosureCandidate { .. }
                 | GeneratorCandidate
                 | FutureCandidate
@@ -1958,6 +1910,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
             (
                 ObjectCandidate(_) | ProjectionCandidate(..),
                 ImplCandidate(..)
+                | AutoImplCandidate
                 | ClosureCandidate { .. }
                 | GeneratorCandidate
                 | FutureCandidate
@@ -1971,6 +1924,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
 
             (
                 ImplCandidate(..)
+                | AutoImplCandidate
                 | ClosureCandidate { .. }
                 | GeneratorCandidate
                 | FutureCandidate
@@ -2061,6 +2015,19 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
                 }
             }
 
+            (AutoImplCandidate, ImplCandidate(_)) | (ImplCandidate(_), AutoImplCandidate) => {
+                DropVictim::No
+            }
+
+            (AutoImplCandidate, _) | (_, AutoImplCandidate) => {
+                bug!(
+                    "default implementations shouldn't be recorded \
+                    when there are other global candidates: {:?} {:?}",
+                    other,
+                    victim
+                );
+            }
+
             // Everything else is ambiguous
             (
                 ImplCandidate(_)
diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs
index 7b34908c4be..3eab885a089 100644
--- a/compiler/rustc_trait_selection/src/traits/util.rs
+++ b/compiler/rustc_trait_selection/src/traits/util.rs
@@ -241,9 +241,9 @@ pub fn upcast_choices<'tcx>(
 /// Given an upcast trait object described by `object`, returns the
 /// index of the method `method_def_id` (which should be part of
 /// `object.upcast_trait_ref`) within the vtable for `object`.
-pub fn get_vtable_index_of_object_method<'tcx, N>(
+pub fn get_vtable_index_of_object_method<'tcx>(
     tcx: TyCtxt<'tcx>,
-    object: &super::ImplSourceObjectData<N>,
+    vtable_base: usize,
     method_def_id: DefId,
 ) -> Option<usize> {
     // Count number of methods preceding the one we are selecting and
@@ -252,7 +252,7 @@ pub fn get_vtable_index_of_object_method<'tcx, N>(
         .iter()
         .copied()
         .position(|def_id| def_id == method_def_id)
-        .map(|index| object.vtable_base + index)
+        .map(|index| vtable_base + index)
 }
 
 pub fn closure_trait_ref_and_return_type<'tcx>(
diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs
index b4f614e3e22..c4cc89aa907 100644
--- a/compiler/rustc_trait_selection/src/traits/vtable.rs
+++ b/compiler/rustc_trait_selection/src/traits/vtable.rs
@@ -5,6 +5,7 @@ use rustc_hir::lang_items::LangItem;
 use rustc_infer::traits::util::PredicateSet;
 use rustc_infer::traits::ImplSource;
 use rustc_middle::query::Providers;
+use rustc_middle::traits::BuiltinImplSource;
 use rustc_middle::ty::visit::TypeVisitableExt;
 use rustc_middle::ty::GenericArgs;
 use rustc_middle::ty::{self, GenericParamDefKind, ToPredicate, Ty, TyCtxt, VtblEntry};
@@ -384,8 +385,8 @@ pub(crate) fn vtable_trait_upcasting_coercion_new_vptr_slot<'tcx>(
     let trait_ref = ty::TraitRef::new(tcx, unsize_trait_did, [source, target]);
 
     match tcx.codegen_select_candidate((ty::ParamEnv::reveal_all(), trait_ref)) {
-        Ok(ImplSource::TraitUpcasting(implsrc_traitcasting)) => {
-            implsrc_traitcasting.vtable_vptr_slot
+        Ok(ImplSource::Builtin(BuiltinImplSource::TraitUpcasting { vtable_vptr_slot }, _)) => {
+            *vtable_vptr_slot
         }
         otherwise => bug!("expected TraitUpcasting candidate, got {otherwise:?}"),
     }
diff --git a/compiler/rustc_ty_utils/src/implied_bounds.rs b/compiler/rustc_ty_utils/src/implied_bounds.rs
index be063a097c0..fa1976510cf 100644
--- a/compiler/rustc_ty_utils/src/implied_bounds.rs
+++ b/compiler/rustc_ty_utils/src/implied_bounds.rs
@@ -51,7 +51,7 @@ fn assumed_wf_types<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx [(Ty<'
             // assumed_wf_types should include those of `Opaque<T>`, `Opaque<T>` itself
             // and `&'static T`.
             DefKind::OpaqueTy => bug!("unimplemented implied bounds for nested opaque types"),
-            def_kind @ _ => {
+            def_kind => {
                 bug!("unimplemented implied bounds for opaque types with parent {def_kind:?}")
             }
         },
diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs
index 16b1836ba9f..a21b5ef05e6 100644
--- a/compiler/rustc_ty_utils/src/instance.rs
+++ b/compiler/rustc_ty_utils/src/instance.rs
@@ -2,7 +2,7 @@ use rustc_errors::ErrorGuaranteed;
 use rustc_hir::def_id::DefId;
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_middle::query::Providers;
-use rustc_middle::traits::CodegenObligationError;
+use rustc_middle::traits::{BuiltinImplSource, CodegenObligationError};
 use rustc_middle::ty::GenericArgsRef;
 use rustc_middle::ty::{self, Instance, TyCtxt, TypeVisitableExt};
 use rustc_span::sym;
@@ -177,12 +177,15 @@ fn resolve_associated_item<'tcx>(
 
             Some(ty::Instance::new(leaf_def.item.def_id, args))
         }
-        traits::ImplSource::Object(ref data) => {
-            traits::get_vtable_index_of_object_method(tcx, data, trait_item_id).map(|index| {
-                Instance { def: ty::InstanceDef::Virtual(trait_item_id, index), args: rcvr_args }
-            })
+        traits::ImplSource::Builtin(BuiltinImplSource::Object { vtable_base }, _) => {
+            traits::get_vtable_index_of_object_method(tcx, *vtable_base, trait_item_id).map(
+                |index| Instance {
+                    def: ty::InstanceDef::Virtual(trait_item_id, index),
+                    args: rcvr_args,
+                },
+            )
         }
-        traits::ImplSource::Builtin(..) => {
+        traits::ImplSource::Builtin(BuiltinImplSource::Misc, _) => {
             let lang_items = tcx.lang_items();
             if Some(trait_ref.def_id) == lang_items.clone_trait() {
                 // FIXME(eddyb) use lang items for methods instead of names.
@@ -290,7 +293,9 @@ fn resolve_associated_item<'tcx>(
                 None
             }
         }
-        traits::ImplSource::Param(..) | traits::ImplSource::TraitUpcasting(_) => None,
+        traits::ImplSource::Param(..)
+        | traits::ImplSource::Builtin(BuiltinImplSource::TraitUpcasting { .. }, _)
+        | traits::ImplSource::Builtin(BuiltinImplSource::TupleUnsizing, _) => None,
     })
 }
 
diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs
index da1eba68d53..b840ff184e0 100644
--- a/compiler/rustc_ty_utils/src/layout.rs
+++ b/compiler/rustc_ty_utils/src/layout.rs
@@ -3,7 +3,7 @@ use rustc_hir as hir;
 use rustc_index::bit_set::BitSet;
 use rustc_index::{IndexSlice, IndexVec};
 use rustc_middle::mir::{GeneratorLayout, GeneratorSavedLocal};
-use rustc_middle::query::{LocalCrate, Providers};
+use rustc_middle::query::Providers;
 use rustc_middle::ty::layout::{
     IntegerExt, LayoutCx, LayoutError, LayoutOf, TyAndLayout, MAX_SIMD_LANES,
 };
@@ -24,28 +24,32 @@ use crate::errors::{
 use crate::layout_sanity_check::sanity_check_layout;
 
 pub fn provide(providers: &mut Providers) {
-    *providers = Providers { layout_of, reference_niches_policy, ..*providers };
+    *providers = Providers { layout_of, ..*providers };
 }
 
-#[instrument(skip(tcx), level = "debug")]
-fn reference_niches_policy<'tcx>(tcx: TyCtxt<'tcx>, _: LocalCrate) -> ReferenceNichePolicy {
-    tcx.sess.opts.unstable_opts.reference_niches.unwrap_or(DEFAULT_REF_NICHES)
-}
-
-/// The reference niche policy for builtin types, and for types in
-/// crates not specifying `-Z reference-niches`.
-const DEFAULT_REF_NICHES: ReferenceNichePolicy = ReferenceNichePolicy { size: false, align: false };
-
 #[instrument(skip(tcx, query), level = "debug")]
 fn layout_of<'tcx>(
     tcx: TyCtxt<'tcx>,
     query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
 ) -> Result<TyAndLayout<'tcx>, &'tcx LayoutError<'tcx>> {
-    let (param_env, unnormalized_ty) = query.into_parts();
+    let (param_env, ty) = query.into_parts();
+    debug!(?ty);
+
     let param_env = param_env.with_reveal_all_normalized(tcx);
-    // `naive_layout_of` takes care of normalizing the type.
-    let naive = tcx.naive_layout_of(query)?;
-    let ty = naive.ty;
+    let unnormalized_ty = ty;
+
+    // FIXME: We might want to have two different versions of `layout_of`:
+    // One that can be called after typecheck has completed and can use
+    // `normalize_erasing_regions` here and another one that can be called
+    // before typecheck has completed and uses `try_normalize_erasing_regions`.
+    let ty = match tcx.try_normalize_erasing_regions(param_env, ty) {
+        Ok(t) => t,
+        Err(normalization_error) => {
+            return Err(tcx
+                .arena
+                .alloc(LayoutError::NormalizationFailure(ty, normalization_error)));
+        }
+    };
 
     if ty != unnormalized_ty {
         // Ensure this layout is also cached for the normalized type.
@@ -53,11 +57,13 @@ fn layout_of<'tcx>(
     }
 
     let cx = LayoutCx { tcx, param_env };
-    let layout = layout_of_uncached(&cx, ty)?;
 
+    let layout = layout_of_uncached(&cx, ty)?;
     let layout = TyAndLayout { ty, layout };
+
     record_layout_for_printing(&cx, layout);
-    sanity_check_layout(&cx, &layout, &naive);
+
+    sanity_check_layout(&cx, &layout);
 
     Ok(layout)
 }
@@ -77,10 +83,12 @@ fn univariant_uninterned<'tcx>(
     kind: StructKind,
 ) -> Result<LayoutS, &'tcx LayoutError<'tcx>> {
     let dl = cx.data_layout();
-    assert!(
-        !(repr.pack.is_some() && repr.align.is_some()),
-        "already rejected by `naive_layout_of`"
-    );
+    let pack = repr.pack;
+    if pack.is_some() && repr.align.is_some() {
+        cx.tcx.sess.delay_span_bug(DUMMY_SP, "struct cannot be packed and aligned");
+        return Err(cx.tcx.arena.alloc(LayoutError::Unknown(ty)));
+    }
+
     cx.univariant(dl, fields, repr, kind).ok_or_else(|| error(cx, LayoutError::SizeOverflow(ty)))
 }
 
@@ -138,35 +146,75 @@ fn layout_of_uncached<'tcx>(
         ty::Ref(_, pointee, _) | ty::RawPtr(ty::TypeAndMut { ty: pointee, .. }) => {
             let mut data_ptr = scalar_unit(Pointer(AddressSpace::DATA));
             if !ty.is_unsafe_ptr() {
-                // Calling `layout_of` here would cause a query cycle for recursive types;
-                // so use a conservative estimate that doesn't look past references.
-                let naive = cx.naive_layout_of(pointee)?.layout;
-
-                let niches = match *pointee.kind() {
-                    ty::FnDef(def, ..)
-                    | ty::Foreign(def)
-                    | ty::Generator(def, ..)
-                    | ty::Closure(def, ..) => tcx.reference_niches_policy(def.krate),
-                    ty::Adt(def, _) => tcx.reference_niches_policy(def.did().krate),
-                    _ => DEFAULT_REF_NICHES,
+                data_ptr.valid_range_mut().start = 1;
+            }
+
+            let pointee = tcx.normalize_erasing_regions(param_env, pointee);
+            if pointee.is_sized(tcx, param_env) {
+                return Ok(tcx.mk_layout(LayoutS::scalar(cx, data_ptr)));
+            }
+
+            let metadata = if let Some(metadata_def_id) = tcx.lang_items().metadata_type()
+                // Projection eagerly bails out when the pointee references errors,
+                // fall back to structurally deducing metadata.
+                && !pointee.references_error()
+            {
+                let pointee_metadata = Ty::new_projection(tcx,metadata_def_id, [pointee]);
+                let metadata_ty = match tcx.try_normalize_erasing_regions(
+                    param_env,
+                    pointee_metadata,
+                ) {
+                    Ok(metadata_ty) => metadata_ty,
+                    Err(mut err) => {
+                        // Usually `<Ty as Pointee>::Metadata` can't be normalized because
+                        // its struct tail cannot be normalized either, so try to get a
+                        // more descriptive layout error here, which will lead to less confusing
+                        // diagnostics.
+                        match tcx.try_normalize_erasing_regions(
+                            param_env,
+                            tcx.struct_tail_without_normalization(pointee),
+                        ) {
+                            Ok(_) => {},
+                            Err(better_err) => {
+                                err = better_err;
+                            }
+                        }
+                        return Err(error(cx, LayoutError::NormalizationFailure(pointee, err)));
+                    },
                 };
 
-                let (min_addr, max_addr) = dl.address_range_for(
-                    if niches.size { naive.size } else { Size::ZERO },
-                    if niches.align { naive.align } else { Align::ONE },
-                );
+                let metadata_layout = cx.layout_of(metadata_ty)?;
+                // If the metadata is a 1-zst, then the pointer is thin.
+                if metadata_layout.is_zst() && metadata_layout.align.abi.bytes() == 1 {
+                    return Ok(tcx.mk_layout(LayoutS::scalar(cx, data_ptr)));
+                }
 
-                *data_ptr.valid_range_mut() =
-                    WrappingRange { start: min_addr.into(), end: max_addr.into() };
-            }
+                let Abi::Scalar(metadata) = metadata_layout.abi else {
+                    return Err(error(cx, LayoutError::Unknown(pointee)));
+                };
 
-            if let Some(metadata) = ptr_metadata_scalar(cx, pointee)? {
-                // Effectively a (ptr, meta) tuple.
-                tcx.mk_layout(cx.scalar_pair(data_ptr, metadata))
+                metadata
             } else {
-                // No metadata, this is a thin pointer.
-                tcx.mk_layout(LayoutS::scalar(cx, data_ptr))
-            }
+                let unsized_part = tcx.struct_tail_erasing_lifetimes(pointee, param_env);
+
+                match unsized_part.kind() {
+                    ty::Foreign(..) => {
+                        return Ok(tcx.mk_layout(LayoutS::scalar(cx, data_ptr)));
+                    }
+                    ty::Slice(_) | ty::Str => scalar_unit(Int(dl.ptr_sized_integer(), false)),
+                    ty::Dynamic(..) => {
+                        let mut vtable = scalar_unit(Pointer(AddressSpace::DATA));
+                        vtable.valid_range_mut().start = 1;
+                        vtable
+                    }
+                    _ => {
+                        return Err(error(cx, LayoutError::Unknown(pointee)));
+                    }
+                }
+            };
+
+            // Effectively a (ptr, meta) tuple.
+            tcx.mk_layout(cx.scalar_pair(data_ptr, metadata))
         }
 
         ty::Dynamic(_, _, ty::DynStar) => {
@@ -178,8 +226,16 @@ fn layout_of_uncached<'tcx>(
         }
 
         // Arrays and slices.
-        ty::Array(element, count) => {
-            let count = compute_array_count(cx, count)
+        ty::Array(element, mut count) => {
+            if count.has_projections() {
+                count = tcx.normalize_erasing_regions(param_env, count);
+                if count.has_projections() {
+                    return Err(error(cx, LayoutError::Unknown(ty)));
+                }
+            }
+
+            let count = count
+                .try_eval_target_usize(tcx, param_env)
                 .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?;
             let element = cx.layout_of(element)?;
             let size = element
@@ -502,104 +558,20 @@ fn layout_of_uncached<'tcx>(
         }
 
         // Types with no meaningful known layout.
-        ty::Alias(..)
-        | ty::Bound(..)
-        | ty::GeneratorWitness(..)
-        | ty::GeneratorWitnessMIR(..)
-        | ty::Infer(_)
-        | ty::Placeholder(..)
-        | ty::Param(_)
-        | ty::Error(_) => {
-            unreachable!("already rejected by `naive_layout_of`");
+        ty::Alias(..) => {
+            // NOTE(eddyb) `layout_of` query should've normalized these away,
+            // if that was possible, so there's no reason to try again here.
+            return Err(error(cx, LayoutError::Unknown(ty)));
         }
-    })
-}
 
-pub(crate) fn compute_array_count<'tcx>(
-    cx: &LayoutCx<'tcx, TyCtxt<'tcx>>,
-    mut count: ty::Const<'tcx>,
-) -> Option<u64> {
-    let LayoutCx { tcx, param_env } = *cx;
-    if count.has_projections() {
-        count = tcx.normalize_erasing_regions(param_env, count);
-        if count.has_projections() {
-            return None;
+        ty::Bound(..) | ty::GeneratorWitness(..) | ty::GeneratorWitnessMIR(..) | ty::Infer(_) => {
+            bug!("Layout::compute: unexpected type `{}`", ty)
         }
-    }
-
-    count.try_eval_target_usize(tcx, param_env)
-}
-
-pub(crate) fn ptr_metadata_scalar<'tcx>(
-    cx: &LayoutCx<'tcx, TyCtxt<'tcx>>,
-    pointee: Ty<'tcx>,
-) -> Result<Option<Scalar>, &'tcx LayoutError<'tcx>> {
-    let dl = cx.data_layout();
-    let scalar_unit = |value: Primitive| {
-        let size = value.size(dl);
-        assert!(size.bits() <= 128);
-        Scalar::Initialized { value, valid_range: WrappingRange::full(size) }
-    };
 
-    let LayoutCx { tcx, param_env } = *cx;
-
-    let pointee = tcx.normalize_erasing_regions(param_env, pointee);
-    if pointee.is_sized(tcx, param_env) {
-        return Ok(None);
-    }
-
-    if let Some(metadata_def_id) = tcx.lang_items().metadata_type()
-        // Projection eagerly bails out when the pointee references errors,
-        // fall back to structurally deducing metadata.
-        && !pointee.references_error()
-    {
-        let pointee_metadata = Ty::new_projection(tcx,metadata_def_id, [pointee]);
-        let metadata_ty = match tcx.try_normalize_erasing_regions(
-            param_env,
-            pointee_metadata,
-        ) {
-            Ok(metadata_ty) => metadata_ty,
-            Err(mut err) => {
-                // Usually `<Ty as Pointee>::Metadata` can't be normalized because
-                // its struct tail cannot be normalized either, so try to get a
-                // more descriptive layout error here, which will lead to less confusing
-                // diagnostics.
-                match tcx.try_normalize_erasing_regions(
-                    param_env,
-                    tcx.struct_tail_without_normalization(pointee),
-                ) {
-                    Ok(_) => {},
-                    Err(better_err) => {
-                        err = better_err;
-                    }
-                }
-                return Err(error(cx, LayoutError::NormalizationFailure(pointee, err)));
-            },
-        };
-
-        let metadata_layout = cx.layout_of(metadata_ty)?;
-
-        if metadata_layout.is_zst() && metadata_layout.align.abi.bytes() == 1 {
-            Ok(None) // If the metadata is a 1-zst, then the pointer is thin.
-        } else if let Abi::Scalar(metadata) = metadata_layout.abi {
-            Ok(Some(metadata))
-        } else {
-            Err(error(cx, LayoutError::Unknown(pointee)))
-        }
-    } else {
-        let unsized_part = tcx.struct_tail_erasing_lifetimes(pointee, param_env);
-
-        match unsized_part.kind() {
-            ty::Foreign(..) => Ok(None),
-            ty::Slice(_) | ty::Str => Ok(Some(scalar_unit(Int(dl.ptr_sized_integer(), false)))),
-            ty::Dynamic(..) => {
-                let mut vtable = scalar_unit(Pointer(AddressSpace::DATA));
-                vtable.valid_range_mut().start = 1;
-                Ok(Some(vtable))
-            }
-            _ => Err(error(cx, LayoutError::Unknown(pointee))),
+        ty::Placeholder(..) | ty::Param(_) | ty::Error(_) => {
+            return Err(error(cx, LayoutError::Unknown(ty)));
         }
-    }
+    })
 }
 
 /// Overlap eligibility and variant assignment for each GeneratorSavedLocal.
diff --git a/compiler/rustc_ty_utils/src/layout_naive.rs b/compiler/rustc_ty_utils/src/layout_naive.rs
deleted file mode 100644
index 3070ab59d53..00000000000
--- a/compiler/rustc_ty_utils/src/layout_naive.rs
+++ /dev/null
@@ -1,322 +0,0 @@
-use rustc_middle::query::Providers;
-use rustc_middle::ty::layout::{
-    IntegerExt, LayoutCx, LayoutError, LayoutOf, NaiveAbi, NaiveLayout, NaiveNiches,
-    TyAndNaiveLayout,
-};
-use rustc_middle::ty::{self, ReprOptions, Ty, TyCtxt, TypeVisitableExt};
-use rustc_span::DUMMY_SP;
-use rustc_target::abi::*;
-
-use std::ops::Bound;
-
-use crate::layout::{compute_array_count, ptr_metadata_scalar};
-
-pub fn provide(providers: &mut Providers) {
-    *providers = Providers { naive_layout_of, ..*providers };
-}
-
-#[instrument(skip(tcx, query), level = "debug")]
-fn naive_layout_of<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
-) -> Result<TyAndNaiveLayout<'tcx>, &'tcx LayoutError<'tcx>> {
-    let (param_env, ty) = query.into_parts();
-    debug!(?ty);
-
-    let param_env = param_env.with_reveal_all_normalized(tcx);
-    let unnormalized_ty = ty;
-
-    // FIXME: We might want to have two different versions of `layout_of`:
-    // One that can be called after typecheck has completed and can use
-    // `normalize_erasing_regions` here and another one that can be called
-    // before typecheck has completed and uses `try_normalize_erasing_regions`.
-    let ty = match tcx.try_normalize_erasing_regions(param_env, ty) {
-        Ok(t) => t,
-        Err(normalization_error) => {
-            return Err(tcx
-                .arena
-                .alloc(LayoutError::NormalizationFailure(ty, normalization_error)));
-        }
-    };
-
-    if ty != unnormalized_ty {
-        // Ensure this layout is also cached for the normalized type.
-        return tcx.naive_layout_of(param_env.and(ty));
-    }
-
-    let cx = LayoutCx { tcx, param_env };
-    let layout = naive_layout_of_uncached(&cx, ty)?;
-    Ok(TyAndNaiveLayout { ty, layout })
-}
-
-fn error<'tcx>(
-    cx: &LayoutCx<'tcx, TyCtxt<'tcx>>,
-    err: LayoutError<'tcx>,
-) -> &'tcx LayoutError<'tcx> {
-    cx.tcx.arena.alloc(err)
-}
-
-fn naive_layout_of_uncached<'tcx>(
-    cx: &LayoutCx<'tcx, TyCtxt<'tcx>>,
-    ty: Ty<'tcx>,
-) -> Result<NaiveLayout, &'tcx LayoutError<'tcx>> {
-    let tcx = cx.tcx;
-    let dl = cx.data_layout();
-
-    let scalar = |niched: bool, value: Primitive| NaiveLayout {
-        abi: NaiveAbi::Scalar(value),
-        niches: if niched { NaiveNiches::Some } else { NaiveNiches::None },
-        size: value.size(dl),
-        align: value.align(dl).abi,
-        exact: true,
-    };
-
-    let univariant = |fields: &mut dyn Iterator<Item = Ty<'tcx>>,
-                      repr: &ReprOptions|
-     -> Result<NaiveLayout, &'tcx LayoutError<'tcx>> {
-        if repr.pack.is_some() && repr.align.is_some() {
-            cx.tcx.sess.delay_span_bug(DUMMY_SP, "struct cannot be packed and aligned");
-            return Err(error(cx, LayoutError::Unknown(ty)));
-        }
-
-        let linear = repr.inhibit_struct_field_reordering_opt();
-        let pack = repr.pack.unwrap_or(Align::MAX);
-        let mut layout = NaiveLayout::EMPTY;
-
-        for field in fields {
-            let field = cx.naive_layout_of(field)?.packed(pack);
-            if linear {
-                layout = layout.pad_to_align(field.align);
-            }
-            layout = layout
-                .concat(&field, dl)
-                .ok_or_else(|| error(cx, LayoutError::SizeOverflow(ty)))?;
-        }
-
-        if let Some(align) = repr.align {
-            layout = layout.align_to(align);
-        }
-
-        if linear {
-            layout.abi = layout.abi.as_aggregate();
-        }
-
-        Ok(layout.pad_to_align(layout.align))
-    };
-
-    debug_assert!(!ty.has_non_region_infer());
-
-    Ok(match *ty.kind() {
-        // Basic scalars
-        ty::Bool => scalar(true, Int(I8, false)),
-        ty::Char => scalar(true, Int(I32, false)),
-        ty::Int(ity) => scalar(false, Int(Integer::from_int_ty(dl, ity), true)),
-        ty::Uint(ity) => scalar(false, Int(Integer::from_uint_ty(dl, ity), false)),
-        ty::Float(fty) => scalar(
-            false,
-            match fty {
-                ty::FloatTy::F32 => F32,
-                ty::FloatTy::F64 => F64,
-            },
-        ),
-        ty::FnPtr(_) => scalar(true, Pointer(dl.instruction_address_space)),
-
-        // The never type.
-        ty::Never => NaiveLayout { abi: NaiveAbi::Uninhabited, ..NaiveLayout::EMPTY },
-
-        // Potentially-wide pointers.
-        ty::Ref(_, pointee, _) | ty::RawPtr(ty::TypeAndMut { ty: pointee, .. }) => {
-            let data_ptr = scalar(!ty.is_unsafe_ptr(), Pointer(AddressSpace::DATA));
-            if let Some(metadata) = ptr_metadata_scalar(cx, pointee)? {
-                // Effectively a (ptr, meta) tuple.
-                let meta = scalar(!metadata.is_always_valid(dl), metadata.primitive());
-                let l = data_ptr
-                    .concat(&meta, dl)
-                    .ok_or_else(|| error(cx, LayoutError::SizeOverflow(ty)))?;
-                l.pad_to_align(l.align)
-            } else {
-                // No metadata, this is a thin pointer.
-                data_ptr
-            }
-        }
-
-        ty::Dynamic(_, _, ty::DynStar) => {
-            let ptr = scalar(false, Pointer(AddressSpace::DATA));
-            let vtable = scalar(true, Pointer(AddressSpace::DATA));
-            ptr.concat(&vtable, dl).ok_or_else(|| error(cx, LayoutError::SizeOverflow(ty)))?
-        }
-
-        // Arrays and slices.
-        ty::Array(element, count) => {
-            let count = compute_array_count(cx, count)
-                .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?;
-            let element = cx.naive_layout_of(element)?;
-            NaiveLayout {
-                abi: element.abi.as_aggregate(),
-                size: element
-                    .size
-                    .checked_mul(count, cx)
-                    .ok_or_else(|| error(cx, LayoutError::SizeOverflow(ty)))?,
-                niches: if count == 0 { NaiveNiches::None } else { element.niches },
-                ..*element
-            }
-        }
-        ty::Slice(element) => NaiveLayout {
-            abi: NaiveAbi::Unsized,
-            size: Size::ZERO,
-            niches: NaiveNiches::None,
-            ..*cx.naive_layout_of(element)?
-        },
-
-        ty::FnDef(..) => NaiveLayout::EMPTY,
-
-        // Unsized types.
-        ty::Str | ty::Dynamic(_, _, ty::Dyn) | ty::Foreign(..) => {
-            NaiveLayout { abi: NaiveAbi::Unsized, ..NaiveLayout::EMPTY }
-        }
-
-        // FIXME(reference_niches): try to actually compute a reasonable layout estimate,
-        // without duplicating too much code from `generator_layout`.
-        ty::Generator(..) => {
-            NaiveLayout { exact: false, niches: NaiveNiches::Maybe, ..NaiveLayout::EMPTY }
-        }
-
-        ty::Closure(_, ref substs) => {
-            univariant(&mut substs.as_closure().upvar_tys(), &ReprOptions::default())?
-        }
-
-        ty::Tuple(tys) => univariant(&mut tys.iter(), &ReprOptions::default())?,
-
-        ty::Adt(def, substs) if def.is_union() => {
-            assert_eq!(def.variants().len(), 1, "union should have a single variant");
-            let repr = def.repr();
-            let pack = repr.pack.unwrap_or(Align::MAX);
-            if repr.pack.is_some() && repr.align.is_some() {
-                cx.tcx.sess.delay_span_bug(DUMMY_SP, "union cannot be packed and aligned");
-                return Err(error(cx, LayoutError::Unknown(ty)));
-            }
-
-            let mut layout = NaiveLayout {
-                // Unions never have niches.
-                niches: NaiveNiches::None,
-                ..NaiveLayout::EMPTY
-            };
-
-            for f in &def.variants()[FIRST_VARIANT].fields {
-                let field = cx.naive_layout_of(f.ty(tcx, substs))?;
-                layout = layout.union(&field.packed(pack));
-            }
-
-            // Unions are always inhabited, and never scalar if `repr(C)`.
-            if !matches!(layout.abi, NaiveAbi::Scalar(_)) || repr.inhibit_enum_layout_opt() {
-                layout.abi = NaiveAbi::Sized;
-            }
-
-            if let Some(align) = repr.align {
-                layout = layout.align_to(align);
-            }
-            layout.pad_to_align(layout.align)
-        }
-
-        ty::Adt(def, substs) => {
-            let repr = def.repr();
-            let mut layout = NaiveLayout {
-                // An ADT with no inhabited variants should have an uninhabited ABI.
-                abi: NaiveAbi::Uninhabited,
-                ..NaiveLayout::EMPTY
-            };
-
-            let mut empty_variants = 0;
-            for v in def.variants() {
-                let mut fields = v.fields.iter().map(|f| f.ty(tcx, substs));
-                let vlayout = univariant(&mut fields, &repr)?;
-
-                if vlayout.size == Size::ZERO && vlayout.exact {
-                    empty_variants += 1;
-                } else {
-                    // Remember the niches of the last seen variant.
-                    layout.niches = vlayout.niches;
-                }
-
-                layout = layout.union(&vlayout);
-            }
-
-            if def.is_enum() {
-                let may_need_discr = match def.variants().len() {
-                    0 | 1 => false,
-                    // Simple Option-like niche optimization.
-                    // Handling this special case allows enums like `Option<&T>`
-                    // to be recognized as `PointerLike` and to be transmutable
-                    // in generic contexts.
-                    2 if empty_variants == 1 && layout.niches == NaiveNiches::Some => {
-                        layout.niches = NaiveNiches::Maybe; // fill up the niche.
-                        false
-                    }
-                    _ => true,
-                };
-
-                if may_need_discr || repr.inhibit_enum_layout_opt() {
-                    // For simplicity, assume that the discriminant always get niched.
-                    // This will be wrong in many cases, which will cause the size (and
-                    // sometimes the alignment) to be underestimated.
-                    // FIXME(reference_niches): Be smarter here.
-                    layout.niches = NaiveNiches::Maybe;
-                    layout = layout.inexact();
-                }
-            } else {
-                assert_eq!(def.variants().len(), 1, "struct should have a single variant");
-
-                // We don't compute exact alignment for SIMD structs.
-                if repr.simd() {
-                    layout = layout.inexact();
-                }
-
-                // `UnsafeCell` hides all niches.
-                if def.is_unsafe_cell() {
-                    layout.niches = NaiveNiches::None;
-                }
-            }
-
-            let valid_range = tcx.layout_scalar_valid_range(def.did());
-            if valid_range != (Bound::Unbounded, Bound::Unbounded) {
-                let get = |bound, default| match bound {
-                    Bound::Unbounded => default,
-                    Bound::Included(v) => v,
-                    Bound::Excluded(_) => bug!("exclusive `layout_scalar_valid_range` bound"),
-                };
-
-                let valid_range = WrappingRange {
-                    start: get(valid_range.0, 0),
-                    // FIXME: this is wrong for scalar-pair ABIs. Fortunately, the
-                    // only type this could currently affect is`NonNull<T: !Sized>`,
-                    // and the `NaiveNiches` result still ends up correct.
-                    end: get(valid_range.1, layout.size.unsigned_int_max()),
-                };
-                assert!(
-                    valid_range.is_in_range_for(layout.size),
-                    "`layout_scalar_valid_range` values are out of bounds",
-                );
-                if !valid_range.is_full_for(layout.size) {
-                    layout.niches = NaiveNiches::Some;
-                }
-            }
-
-            layout.pad_to_align(layout.align)
-        }
-
-        // Types with no meaningful known layout.
-        ty::Alias(..) => {
-            // NOTE(eddyb) `layout_of` query should've normalized these away,
-            // if that was possible, so there's no reason to try again here.
-            return Err(error(cx, LayoutError::Unknown(ty)));
-        }
-
-        ty::Bound(..) | ty::GeneratorWitness(..) | ty::GeneratorWitnessMIR(..) | ty::Infer(_) => {
-            bug!("Layout::compute: unexpected type `{}`", ty)
-        }
-
-        ty::Placeholder(..) | ty::Param(_) | ty::Error(_) => {
-            return Err(error(cx, LayoutError::Unknown(ty)));
-        }
-    })
-}
diff --git a/compiler/rustc_ty_utils/src/layout_sanity_check.rs b/compiler/rustc_ty_utils/src/layout_sanity_check.rs
index 2e3fe4e7fb8..8633334381a 100644
--- a/compiler/rustc_ty_utils/src/layout_sanity_check.rs
+++ b/compiler/rustc_ty_utils/src/layout_sanity_check.rs
@@ -1,5 +1,5 @@
 use rustc_middle::ty::{
-    layout::{LayoutCx, NaiveLayout, TyAndLayout},
+    layout::{LayoutCx, TyAndLayout},
     TyCtxt,
 };
 use rustc_target::abi::*;
@@ -10,7 +10,6 @@ use std::assert_matches::assert_matches;
 pub(super) fn sanity_check_layout<'tcx>(
     cx: &LayoutCx<'tcx, TyCtxt<'tcx>>,
     layout: &TyAndLayout<'tcx>,
-    naive: &NaiveLayout,
 ) {
     // Type-level uninhabitedness should always imply ABI uninhabitedness.
     if layout.ty.is_privately_uninhabited(cx.tcx, cx.param_env) {
@@ -21,10 +20,6 @@ pub(super) fn sanity_check_layout<'tcx>(
         bug!("size is not a multiple of align, in the following layout:\n{layout:#?}");
     }
 
-    if !naive.is_refined_by(layout.layout) {
-        bug!("the naive layout isn't refined by the actual layout:\n{:#?}\n{:#?}", naive, layout);
-    }
-
     if !cfg!(debug_assertions) {
         // Stop here, the rest is kind of expensive.
         return;
diff --git a/compiler/rustc_ty_utils/src/lib.rs b/compiler/rustc_ty_utils/src/lib.rs
index e2db6a6993f..55b8857ed39 100644
--- a/compiler/rustc_ty_utils/src/lib.rs
+++ b/compiler/rustc_ty_utils/src/lib.rs
@@ -31,7 +31,6 @@ mod errors;
 mod implied_bounds;
 pub mod instance;
 mod layout;
-mod layout_naive;
 mod layout_sanity_check;
 mod needs_drop;
 mod opaque_types;
@@ -48,7 +47,6 @@ pub fn provide(providers: &mut Providers) {
     consts::provide(providers);
     implied_bounds::provide(providers);
     layout::provide(providers);
-    layout_naive::provide(providers);
     needs_drop::provide(providers);
     opaque_types::provide(providers);
     representability::provide(providers);
diff --git a/compiler/rustc_ty_utils/src/needs_drop.rs b/compiler/rustc_ty_utils/src/needs_drop.rs
index e173bba49be..f89558a4599 100644
--- a/compiler/rustc_ty_utils/src/needs_drop.rs
+++ b/compiler/rustc_ty_utils/src/needs_drop.rs
@@ -133,7 +133,7 @@ where
                             _ => {
                                 tcx.sess.delay_span_bug(
                                     tcx.hir().span_if_local(def_id).unwrap_or(DUMMY_SP),
-                                    format!("unexpected generator witness type {:?}", witness),
+                                    format!("unexpected generator witness type {witness:?}"),
                                 );
                                 return Some(Err(AlwaysRequiresDrop));
                             }
diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs
index 505f78d0e5f..2055852eda0 100644
--- a/compiler/rustc_ty_utils/src/ty.rs
+++ b/compiler/rustc_ty_utils/src/ty.rs
@@ -144,85 +144,9 @@ fn param_env(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> {
     }
 
     let local_did = def_id.as_local();
-    // FIXME(-Zlower-impl-trait-in-trait-to-assoc-ty): This isn't correct for
-    // RPITITs in const trait fn.
-    let hir_id = local_did.and_then(|def_id| tcx.opt_local_def_id_to_hir_id(def_id));
-
-    // FIXME(consts): This is not exactly in line with the constness query.
-    let constness = match hir_id {
-        Some(hir_id) => match tcx.hir().get(hir_id) {
-            hir::Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Fn(..), .. })
-                if tcx.is_const_default_method(def_id) =>
-            {
-                hir::Constness::Const
-            }
-
-            hir::Node::Item(hir::Item { kind: hir::ItemKind::Const(..), .. })
-            | hir::Node::Item(hir::Item { kind: hir::ItemKind::Static(..), .. })
-            | hir::Node::TraitItem(hir::TraitItem {
-                kind: hir::TraitItemKind::Const(..), ..
-            })
-            | hir::Node::AnonConst(_)
-            | hir::Node::ConstBlock(_)
-            | hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Const(..), .. })
-            | hir::Node::ImplItem(hir::ImplItem {
-                kind:
-                    hir::ImplItemKind::Fn(
-                        hir::FnSig {
-                            header: hir::FnHeader { constness: hir::Constness::Const, .. },
-                            ..
-                        },
-                        ..,
-                    ),
-                ..
-            }) => hir::Constness::Const,
-
-            hir::Node::ImplItem(hir::ImplItem {
-                kind: hir::ImplItemKind::Type(..) | hir::ImplItemKind::Fn(..),
-                ..
-            }) => {
-                let parent_hir_id = tcx.hir().parent_id(hir_id);
-                match tcx.hir().get(parent_hir_id) {
-                    hir::Node::Item(hir::Item {
-                        kind: hir::ItemKind::Impl(hir::Impl { constness, .. }),
-                        ..
-                    }) => *constness,
-                    _ => span_bug!(
-                        tcx.def_span(parent_hir_id.owner),
-                        "impl item's parent node is not an impl",
-                    ),
-                }
-            }
-
-            hir::Node::Item(hir::Item {
-                kind:
-                    hir::ItemKind::Fn(hir::FnSig { header: hir::FnHeader { constness, .. }, .. }, ..),
-                ..
-            })
-            | hir::Node::TraitItem(hir::TraitItem {
-                kind:
-                    hir::TraitItemKind::Fn(
-                        hir::FnSig { header: hir::FnHeader { constness, .. }, .. },
-                        ..,
-                    ),
-                ..
-            })
-            | hir::Node::Item(hir::Item {
-                kind: hir::ItemKind::Impl(hir::Impl { constness, .. }),
-                ..
-            }) => *constness,
-
-            _ => hir::Constness::NotConst,
-        },
-        // FIXME(consts): It's suspicious that a param-env for a foreign item
-        // will always have NotConst param-env, though we don't typically use
-        // that param-env for anything meaningful right now, so it's likely
-        // not an issue.
-        None => hir::Constness::NotConst,
-    };
 
     let unnormalized_env =
-        ty::ParamEnv::new(tcx.mk_clauses(&predicates), traits::Reveal::UserFacing, constness);
+        ty::ParamEnv::new(tcx.mk_clauses(&predicates), traits::Reveal::UserFacing);
 
     let body_id = local_did.unwrap_or(CRATE_DEF_ID);
     let cause = traits::ObligationCause::misc(tcx.def_span(def_id), body_id);
diff --git a/compiler/rustc_type_ir/src/sty.rs b/compiler/rustc_type_ir/src/sty.rs
index b134845dab0..ec0dbffc22f 100644
--- a/compiler/rustc_type_ir/src/sty.rs
+++ b/compiler/rustc_type_ir/src/sty.rs
@@ -39,6 +39,7 @@ pub enum AliasKind {
     /// A projection `<Type as Trait>::AssocType`.
     /// Can get normalized away if monomorphic enough.
     Projection,
+    /// An associated type in an inherent `impl`
     Inherent,
     /// An opaque type (usually from `impl Trait` in type aliases or function return types)
     /// Can only be normalized away in RevealAll mode