about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock1
-rw-r--r--compiler/rustc_abi/src/lib.rs108
-rw-r--r--compiler/rustc_ast_lowering/src/item.rs14
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs10
-rw-r--r--compiler/rustc_codegen_gcc/src/lib.rs4
-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/debuginfo/metadata.rs15
-rw-r--r--compiler/rustc_codegen_llvm/src/lib.rs61
-rw-r--r--compiler/rustc_codegen_llvm/src/llvm/ffi.rs12
-rw-r--r--compiler/rustc_codegen_llvm/src/llvm_util.rs51
-rw-r--r--compiler/rustc_codegen_llvm/src/type_of.rs4
-rw-r--r--compiler/rustc_codegen_ssa/Cargo.toml1
-rw-r--r--compiler/rustc_codegen_ssa/messages.ftl2
-rw-r--r--compiler/rustc_codegen_ssa/src/back/link.rs39
-rw-r--r--compiler/rustc_codegen_ssa/src/back/metadata.rs20
-rw-r--r--compiler/rustc_codegen_ssa/src/back/write.rs4
-rw-r--r--compiler/rustc_codegen_ssa/src/errors.rs6
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/constant.rs16
-rw-r--r--compiler/rustc_codegen_ssa/src/traits/backend.rs20
-rw-r--r--compiler/rustc_codegen_ssa/src/traits/consts.rs6
-rw-r--r--compiler/rustc_codegen_ssa/src/traits/mod.rs4
-rw-r--r--compiler/rustc_codegen_ssa/src/traits/write.rs1
-rw-r--r--compiler/rustc_const_eval/messages.ftl1
-rw-r--r--compiler/rustc_const_eval/src/const_eval/machine.rs9
-rw-r--r--compiler/rustc_const_eval/src/errors.rs5
-rw-r--r--compiler/rustc_const_eval/src/interpret/discriminant.rs24
-rw-r--r--compiler/rustc_const_eval/src/interpret/intrinsics.rs11
-rw-r--r--compiler/rustc_const_eval/src/interpret/memory.rs41
-rw-r--r--compiler/rustc_const_eval/src/interpret/operand.rs2
-rw-r--r--compiler/rustc_const_eval/src/interpret/validity.rs54
-rw-r--r--compiler/rustc_driver_impl/messages.ftl2
-rw-r--r--compiler/rustc_driver_impl/src/lib.rs67
-rw-r--r--compiler/rustc_driver_impl/src/pretty.rs13
-rw-r--r--compiler/rustc_driver_impl/src/session_diagnostics.rs7
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0691.md3
-rw-r--r--compiler/rustc_hir_analysis/src/check/check.rs29
-rw-r--r--compiler/rustc_hir_analysis/src/check_unused.rs9
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/orphan.rs10
-rw-r--r--compiler/rustc_hir_analysis/src/lib.rs3
-rw-r--r--compiler/rustc_infer/src/traits/util.rs7
-rw-r--r--compiler/rustc_interface/src/tests.rs3
-rw-r--r--compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp37
-rw-r--r--compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp24
-rw-r--r--compiler/rustc_metadata/src/locator.rs49
-rw-r--r--compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs1
-rw-r--r--compiler/rustc_metadata/src/rmeta/encoder.rs1
-rw-r--r--compiler/rustc_metadata/src/rmeta/mod.rs3
-rw-r--r--compiler/rustc_middle/src/mir/interpret/error.rs1
-rw-r--r--compiler/rustc_middle/src/mir/interpret/pointer.rs30
-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/ty/context.rs12
-rw-r--r--compiler/rustc_middle/src/ty/fast_reject.rs115
-rw-r--r--compiler/rustc_middle/src/ty/layout.rs286
-rw-r--r--compiler/rustc_mir_dataflow/src/impls/liveness.rs2
-rw-r--r--compiler/rustc_mir_dataflow/src/impls/mod.rs1
-rw-r--r--compiler/rustc_mir_transform/src/const_prop.rs215
-rw-r--r--compiler/rustc_mir_transform/src/dead_store_elimination.rs40
-rw-r--r--compiler/rustc_mir_transform/src/inline.rs15
-rw-r--r--compiler/rustc_query_system/src/query/job.rs3
-rw-r--r--compiler/rustc_session/messages.ftl2
-rw-r--r--compiler/rustc_session/src/config.rs117
-rw-r--r--compiler/rustc_session/src/errors.rs7
-rw-r--r--compiler/rustc_session/src/options.rs33
-rw-r--r--compiler/rustc_session/src/session.rs6
-rw-r--r--compiler/rustc_smir/src/rustc_internal/mod.rs56
-rw-r--r--compiler/rustc_smir/src/rustc_smir/mod.rs500
-rw-r--r--compiler/rustc_smir/src/stable_mir/ty.rs126
-rw-r--r--compiler/rustc_target/src/abi/mod.rs27
-rw-r--r--compiler/rustc_trait_selection/src/solve/assembly/mod.rs286
-rw-r--r--compiler/rustc_trait_selection/src/solve/project_goals.rs12
-rw-r--r--compiler/rustc_trait_selection/src/solve/search_graph/mod.rs25
-rw-r--r--compiler/rustc_trait_selection/src/solve/trait_goals.rs28
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs17
-rw-r--r--compiler/rustc_trait_selection/src/traits/vtable.rs155
-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_type_ir/src/sty.rs1
-rw-r--r--library/alloc/src/rc.rs8
-rw-r--r--library/alloc/src/sync.rs8
-rw-r--r--library/core/src/ptr/non_null.rs24
-rw-r--r--library/core/src/slice/iter.rs25
-rw-r--r--library/core/src/slice/iter/macros.rs146
-rw-r--r--library/core/src/sync/atomic.rs85
-rw-r--r--library/core/tests/iter/traits/iterator.rs31
-rw-r--r--library/core/tests/lib.rs2
-rw-r--r--library/core/tests/slice.rs28
-rw-r--r--library/std/src/path.rs24
-rw-r--r--library/std/src/path/tests.rs50
-rw-r--r--library/std/src/sync/mpmc/waker.rs46
-rw-r--r--library/std/src/sys/windows/thread_local_dtor.rs27
-rw-r--r--library/std/src/sys/windows/thread_local_key.rs46
-rw-r--r--library/std/src/sys/windows/thread_local_key/tests.rs4
-rw-r--r--src/bootstrap/test.rs3
-rw-r--r--src/doc/rustc/src/command-line-arguments.md4
-rw-r--r--src/doc/style-guide/src/README.md50
-rw-r--r--src/doc/style-guide/src/expressions.md267
-rw-r--r--src/doc/style-guide/src/items.md150
-rw-r--r--src/doc/style-guide/src/statements.md61
-rw-r--r--src/doc/style-guide/src/types.md16
-rw-r--r--src/librustdoc/clean/inline.rs4
-rw-r--r--src/librustdoc/clean/types.rs51
-rw-r--r--src/librustdoc/html/render/mod.rs8
-rw-r--r--src/librustdoc/html/static/css/rustdoc.css9
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs8
-rw-r--r--src/tools/clippy/clippy_utils/src/lib.rs47
-rw-r--r--src/tools/miri/rust-version2
-rw-r--r--src/tools/miri/src/intptrcast.rs2
-rw-r--r--src/tools/miri/src/shims/mod.rs4
-rw-r--r--src/tools/miri/src/shims/unix/fs.rs6
-rw-r--r--src/tools/miri/tests/fail/layout_cycle.rs2
-rw-r--r--src/tools/miri/tests/fail/layout_cycle.stderr7
-rw-r--r--tests/codegen/iter-repeat-n-trivial-drop.rs3
-rw-r--r--tests/codegen/llvm-ident.rs15
-rw-r--r--tests/codegen/move-operands.rs3
-rw-r--r--tests/codegen/slice-iter-len-eq-zero.rs4
-rw-r--r--tests/codegen/slice-iter-nonnull.rs39
-rw-r--r--tests/incremental/hashes/trait_defs.rs4
-rw-r--r--tests/mir-opt/const_prop/aggregate.main.ConstProp.panic-abort.diff3
-rw-r--r--tests/mir-opt/const_prop/aggregate.main.ConstProp.panic-unwind.diff3
-rw-r--r--tests/mir-opt/const_prop/aggregate.main.PreCodegen.after.panic-abort.mir2
-rw-r--r--tests/mir-opt/const_prop/aggregate.main.PreCodegen.after.panic-unwind.mir2
-rw-r--r--tests/mir-opt/const_prop/array_index.main.ConstProp.32bit.panic-abort.diff2
-rw-r--r--tests/mir-opt/const_prop/array_index.main.ConstProp.32bit.panic-unwind.diff2
-rw-r--r--tests/mir-opt/const_prop/array_index.main.ConstProp.64bit.panic-abort.diff2
-rw-r--r--tests/mir-opt/const_prop/array_index.main.ConstProp.64bit.panic-unwind.diff2
-rw-r--r--tests/mir-opt/const_prop/bad_op_div_by_zero.main.ConstProp.panic-abort.diff5
-rw-r--r--tests/mir-opt/const_prop/bad_op_div_by_zero.main.ConstProp.panic-unwind.diff5
-rw-r--r--tests/mir-opt/const_prop/bad_op_mod_by_zero.main.ConstProp.panic-abort.diff5
-rw-r--r--tests/mir-opt/const_prop/bad_op_mod_by_zero.main.ConstProp.panic-unwind.diff5
-rw-r--r--tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.32bit.panic-abort.diff2
-rw-r--r--tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.32bit.panic-unwind.diff2
-rw-r--r--tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.64bit.panic-abort.diff2
-rw-r--r--tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.64bit.panic-unwind.diff2
-rw-r--r--tests/mir-opt/const_prop/boxes.main.ConstProp.panic-abort.diff3
-rw-r--r--tests/mir-opt/const_prop/boxes.main.ConstProp.panic-unwind.diff3
-rw-r--r--tests/mir-opt/const_prop/control_flow_simplification.hello.ConstProp.panic-abort.diff3
-rw-r--r--tests/mir-opt/const_prop/control_flow_simplification.hello.ConstProp.panic-unwind.diff3
-rw-r--r--tests/mir-opt/const_prop/indirect.main.ConstProp.panic-abort.diff2
-rw-r--r--tests/mir-opt/const_prop/indirect.main.ConstProp.panic-unwind.diff2
-rw-r--r--tests/mir-opt/const_prop/inherit_overflow.main.ConstProp.panic-abort.diff2
-rw-r--r--tests/mir-opt/const_prop/inherit_overflow.main.ConstProp.panic-unwind.diff2
-rw-r--r--tests/mir-opt/const_prop/invalid_constant.main.ConstProp.diff3
-rw-r--r--tests/mir-opt/const_prop/issue_66971.main.ConstProp.panic-abort.diff6
-rw-r--r--tests/mir-opt/const_prop/issue_66971.main.ConstProp.panic-unwind.diff6
-rw-r--r--tests/mir-opt/const_prop/issue_67019.main.ConstProp.panic-abort.diff6
-rw-r--r--tests/mir-opt/const_prop/issue_67019.main.ConstProp.panic-unwind.diff6
-rw-r--r--tests/mir-opt/const_prop/large_array_index.main.ConstProp.32bit.panic-abort.diff2
-rw-r--r--tests/mir-opt/const_prop/large_array_index.main.ConstProp.32bit.panic-unwind.diff2
-rw-r--r--tests/mir-opt/const_prop/large_array_index.main.ConstProp.64bit.panic-abort.diff2
-rw-r--r--tests/mir-opt/const_prop/large_array_index.main.ConstProp.64bit.panic-unwind.diff2
-rw-r--r--tests/mir-opt/const_prop/offset_of.concrete.ConstProp.panic-abort.diff12
-rw-r--r--tests/mir-opt/const_prop/offset_of.concrete.ConstProp.panic-unwind.diff12
-rw-r--r--tests/mir-opt/const_prop/repeat.main.ConstProp.32bit.panic-abort.diff2
-rw-r--r--tests/mir-opt/const_prop/repeat.main.ConstProp.32bit.panic-unwind.diff2
-rw-r--r--tests/mir-opt/const_prop/repeat.main.ConstProp.64bit.panic-abort.diff2
-rw-r--r--tests/mir-opt/const_prop/repeat.main.ConstProp.64bit.panic-unwind.diff2
-rw-r--r--tests/mir-opt/const_prop/scalar_literal_propagation.main.ConstProp.panic-abort.diff3
-rw-r--r--tests/mir-opt/const_prop/scalar_literal_propagation.main.ConstProp.panic-unwind.diff3
-rw-r--r--tests/mir-opt/const_prop/slice_len.main.ConstProp.32bit.panic-abort.diff2
-rw-r--r--tests/mir-opt/const_prop/slice_len.main.ConstProp.32bit.panic-unwind.diff2
-rw-r--r--tests/mir-opt/const_prop/slice_len.main.ConstProp.64bit.panic-abort.diff2
-rw-r--r--tests/mir-opt/const_prop/slice_len.main.ConstProp.64bit.panic-unwind.diff2
-rw-r--r--tests/mir-opt/const_prop/tuple_literal_propagation.main.ConstProp.panic-abort.diff3
-rw-r--r--tests/mir-opt/const_prop/tuple_literal_propagation.main.ConstProp.panic-unwind.diff3
-rw-r--r--tests/mir-opt/dead-store-elimination/call_arg_copy.move_simple.DeadStoreElimination.panic-abort.diff23
-rw-r--r--tests/mir-opt/dead-store-elimination/call_arg_copy.move_simple.DeadStoreElimination.panic-unwind.diff23
-rw-r--r--tests/mir-opt/dead-store-elimination/call_arg_copy.rs15
-rw-r--r--tests/mir-opt/inline/dyn_trait.get_query.Inline.panic-abort.diff2
-rw-r--r--tests/mir-opt/inline/dyn_trait.get_query.Inline.panic-unwind.diff2
-rw-r--r--tests/mir-opt/inline/dyn_trait.try_execute_query.Inline.panic-abort.diff2
-rw-r--r--tests/mir-opt/inline/dyn_trait.try_execute_query.Inline.panic-unwind.diff2
-rw-r--r--tests/mir-opt/inline/inline_into_box_place.main.Inline.panic-abort.diff4
-rw-r--r--tests/mir-opt/inline/inline_into_box_place.main.Inline.panic-unwind.diff4
-rw-r--r--tests/mir-opt/inline/inline_trait_method_2.test2.Inline.after.panic-abort.mir2
-rw-r--r--tests/mir-opt/inline/inline_trait_method_2.test2.Inline.after.panic-unwind.mir2
-rw-r--r--tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-abort.diff6
-rw-r--r--tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-unwind.diff6
-rw-r--r--tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.panic-abort.mir16
-rw-r--r--tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.panic-unwind.mir16
-rw-r--r--tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.Inline.panic-abort.diff6
-rw-r--r--tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.Inline.panic-unwind.diff6
-rw-r--r--tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.PreCodegen.after.panic-abort.mir16
-rw-r--r--tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.PreCodegen.after.panic-unwind.mir16
-rw-r--r--tests/mir-opt/issue_101973.inner.ConstProp.panic-abort.diff2
-rw-r--r--tests/mir-opt/issue_101973.inner.ConstProp.panic-unwind.diff2
-rw-r--r--tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.panic-abort.mir2
-rw-r--r--tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.panic-unwind.mir2
-rw-r--r--tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.mir34
-rw-r--r--tests/mir-opt/pre-codegen/intrinsics.f_u64.PreCodegen.after.mir8
-rw-r--r--tests/mir-opt/pre-codegen/loops.int_range.PreCodegen.after.mir2
-rw-r--r--tests/mir-opt/pre-codegen/range_iter.inclusive_loop.PreCodegen.after.panic-abort.mir2
-rw-r--r--tests/mir-opt/pre-codegen/range_iter.inclusive_loop.PreCodegen.after.panic-unwind.mir2
-rw-r--r--tests/mir-opt/pre-codegen/range_iter.range_inclusive_iter_next.PreCodegen.after.panic-abort.mir2
-rw-r--r--tests/mir-opt/pre-codegen/range_iter.range_inclusive_iter_next.PreCodegen.after.panic-unwind.mir2
-rw-r--r--tests/mir-opt/pre-codegen/slice_index.slice_index_range.PreCodegen.after.panic-abort.mir2
-rw-r--r--tests/mir-opt/pre-codegen/slice_index.slice_index_range.PreCodegen.after.panic-unwind.mir2
-rw-r--r--tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-abort.mir4
-rw-r--r--tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-unwind.mir4
-rw-r--r--tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-abort.mir4
-rw-r--r--tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-unwind.mir4
-rw-r--r--tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir4
-rw-r--r--tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir4
-rw-r--r--tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-abort.mir2
-rw-r--r--tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-unwind.mir2
-rw-r--r--tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.PreCodegen.after.panic-abort.mir2
-rw-r--r--tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.PreCodegen.after.panic-unwind.mir2
-rw-r--r--tests/run-make/comment-section/Makefile15
-rw-r--r--tests/run-make/extern-flag-pathless/Makefile27
-rw-r--r--tests/run-make/extern-flag-pathless/bar-dynamic.rs3
-rw-r--r--tests/run-make/extern-flag-pathless/bar-static.rs3
-rw-r--r--tests/run-make/extern-flag-pathless/bar.rs1
-rw-r--r--tests/run-make/llvm-ident/Makefile19
-rw-r--r--tests/run-make/mixing-libs/Makefile8
-rw-r--r--tests/run-make/no-cdylib-as-rdylib/Makefile16
-rw-r--r--tests/run-make/no-cdylib-as-rdylib/bar.rs1
-rw-r--r--tests/run-make/no-cdylib-as-rdylib/foo.rs5
-rw-r--r--tests/run-make/print-cfg/Makefile19
-rw-r--r--tests/run-make/rmeta-preferred/Makefile16
-rw-r--r--tests/run-make/rmeta-preferred/lib.rs (renamed from tests/ui/rmeta/rmeta-rpass.rs)8
-rw-r--r--tests/run-make/rmeta-preferred/rmeta_aux.rs3
-rw-r--r--tests/rustdoc/default-trait-method.rs43
-rw-r--r--tests/ui/codegen/target-cpus.rs4
-rw-r--r--tests/ui/codegen/target-cpus.stdout4
-rw-r--r--tests/ui/consts/const-size_of-cycle.stderr3
-rw-r--r--tests/ui/consts/issue-44415.stderr3
-rw-r--r--tests/ui/dyn-star/param-env-region-infer.next.stderr2
-rw-r--r--tests/ui/generics/issue-32498.rs1
-rw-r--r--tests/ui/layout/valid_range_oob.stderr4
-rw-r--r--tests/ui/lint/invalid_value.stderr6
-rw-r--r--tests/ui/recursion/issue-26548-recursion-via-normalize.rs10
-rw-r--r--tests/ui/recursion/issue-26548-recursion-via-normalize.stderr10
-rw-r--r--tests/ui/recursion_limit/zero-overflow.rs2
-rw-r--r--tests/ui/recursion_limit/zero-overflow.stderr4
-rw-r--r--tests/ui/repr/repr-transparent.stderr8
-rw-r--r--tests/ui/rmeta/auxiliary/rmeta-rlib-rpass.rs8
-rw-r--r--tests/ui/rmeta/auxiliary/rmeta-rmeta.rs9
-rw-r--r--tests/ui/simd/shuffle.rs24
-rw-r--r--tests/ui/sized/recursive-type-2.rs2
-rw-r--r--tests/ui/sized/recursive-type-2.stderr12
-rw-r--r--tests/ui/traits/new-solver/assembly/assemble-normalizing-self-ty-impl-ambiguity.rs27
-rw-r--r--tests/ui/traits/new-solver/dont-normalize-proj-with-error.rs22
-rw-r--r--tests/ui/traits/new-solver/dont-normalize-proj-with-error.stderr9
-rw-r--r--tests/ui/traits/object/print_vtable_sizes.rs5
-rw-r--r--tests/ui/traits/object/print_vtable_sizes.stdout2
-rw-r--r--tests/ui/traits/vtable/multiple-markers.rs47
-rw-r--r--tests/ui/traits/vtable/multiple-markers.stderr52
-rw-r--r--tests/ui/transmute/transmute-fat-pointers.rs12
-rw-r--r--tests/ui/type-alias-impl-trait/issue-53092-2.stderr2
-rw-r--r--tests/ui/unpretty/avoid-crash.stderr2
253 files changed, 3881 insertions, 1906 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 01445e97ae7..8c2c3f5e628 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3367,7 +3367,6 @@ dependencies = [
  "rustc_type_ir",
  "serde_json",
  "smallvec",
- "snap",
  "tempfile",
  "thorin-dwp",
  "tracing",
diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs
index ef0c763ac20..d396f18d59c 100644
--- a/compiler/rustc_abi/src/lib.rs
+++ b/compiler/rustc_abi/src/lib.rs
@@ -49,6 +49,14 @@ 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 {
@@ -347,6 +355,33 @@ 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 {
@@ -474,6 +509,12 @@ 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
@@ -967,6 +1008,43 @@ 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 {
@@ -984,9 +1062,15 @@ 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();
-        debug_assert!(self.start <= max_value && self.end <= max_value);
-        self.start == (self.end.wrapping_add(1) & max_value)
+        self.start <= max_value && self.end <= max_value
     }
 }
 
@@ -1427,16 +1511,21 @@ 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 size = value.size(cx);
-        assert!(size.bits() <= 128);
-        let max_value = size.unsigned_int_max();
+        let max_value = value.size(cx).unsigned_int_max();
+        let distance_end_zero = max_value - v.end;
 
-        let niche = v.end.wrapping_add(1)..v.start;
-        let available = niche.end.wrapping_sub(niche.start) & max_value;
-        if count > available {
-            return None;
+        // 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 }));
         }
 
         // Extend the range of valid values being reserved by moving either `v.start` or `v.end` bound.
@@ -1459,7 +1548,6 @@ 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_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs
index ab68436c093..2f58f566c81 100644
--- a/compiler/rustc_ast_lowering/src/item.rs
+++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -551,17 +551,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 for &(ref use_tree, id) in trees {
                     let new_hir_id = self.local_def_id(id);
 
-                    let mut prefix = prefix.clone();
-
-                    // Give the segments new node-ids since they are being cloned.
-                    for seg in &mut prefix.segments {
-                        // Give the cloned segment the same resolution information
-                        // as the old one (this is needed for stability checking).
-                        let new_id = self.next_node_id();
-                        self.resolver.clone_res(seg.id, new_id);
-                        seg.id = new_id;
-                    }
-
                     // Each `use` import is an item and thus are owners of the
                     // names in the path. Up to this point the nested import is
                     // the current owner, since we want each desugared import to
@@ -570,6 +559,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     self.with_hir_id_owner(id, |this| {
                         let mut ident = *ident;
 
+                        // `prefix` is lowered multiple times, but in different HIR owners.
+                        // So each segment gets renewed `HirId` with the same
+                        // `ItemLocalId` and the new owner. (See `lower_node_id`)
                         let kind =
                             this.lower_use_tree(use_tree, &prefix, id, vis_span, &mut ident, attrs);
                         if let Some(attrs) = attrs {
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index 9e193402feb..ac750690046 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -148,10 +148,6 @@ trait ResolverAstLoweringExt {
     fn legacy_const_generic_args(&self, expr: &Expr) -> Option<Vec<usize>>;
     fn get_partial_res(&self, id: NodeId) -> Option<PartialRes>;
     fn get_import_res(&self, id: NodeId) -> PerNS<Option<Res<NodeId>>>;
-    // Clones the resolution (if any) on 'source' and applies it
-    // to 'target'. Used when desugaring a `UseTreeKind::Nested` to
-    // multiple `UseTreeKind::Simple`s
-    fn clone_res(&mut self, source: NodeId, target: NodeId);
     fn get_label_res(&self, id: NodeId) -> Option<NodeId>;
     fn get_lifetime_res(&self, id: NodeId) -> Option<LifetimeRes>;
     fn take_extra_lifetime_params(&mut self, id: NodeId) -> Vec<(Ident, NodeId, LifetimeRes)>;
@@ -184,12 +180,6 @@ impl ResolverAstLoweringExt for ResolverAstLowering {
         None
     }
 
-    fn clone_res(&mut self, source: NodeId, target: NodeId) {
-        if let Some(res) = self.partial_res_map.get(&source) {
-            self.partial_res_map.insert(target, *res);
-        }
-    }
-
     /// Obtains resolution for a `NodeId` with a single resolution.
     fn get_partial_res(&self, id: NodeId) -> Option<PartialRes> {
         self.partial_res_map.get(&id).copied()
diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs
index 2a6b642782d..04ac0254a81 100644
--- a/compiler/rustc_codegen_gcc/src/lib.rs
+++ b/compiler/rustc_codegen_gcc/src/lib.rs
@@ -239,6 +239,10 @@ impl WriteBackendMethods for GccCodegenBackend {
         unimplemented!();
     }
 
+    fn print_statistics(&self) {
+        unimplemented!()
+    }
+
     unsafe fn optimize(_cgcx: &CodegenContext<Self>, _diag_handler: &Handler, module: &ModuleCodegen<Self::Module>, config: &ModuleConfig) -> Result<(), FatalError> {
         module.module_llvm.context.set_optimization_level(to_gcc_opt_level(config.opt_level));
         Ok(())
diff --git a/compiler/rustc_codegen_gcc/src/type_of.rs b/compiler/rustc_codegen_gcc/src/type_of.rs
index 84d57838512..a30bce0a313 100644
--- a/compiler/rustc_codegen_gcc/src/type_of.rs
+++ b/compiler/rustc_codegen_gcc/src/type_of.rs
@@ -339,7 +339,8 @@ impl<'tcx> LayoutGccExt<'tcx> for TyAndLayout<'tcx> {
             return pointee;
         }
 
-        let result = Ty::ty_and_layout_pointee_info_at(*self, cx, offset);
+        let assume_valid_ptr = true;
+        let result = Ty::ty_and_layout_pointee_info_at(*self, cx, offset, assume_valid_ptr);
 
         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/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/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs
index 24ba28bbc82..5aab06febe4 100644
--- a/compiler/rustc_codegen_llvm/src/lib.rs
+++ b/compiler/rustc_codegen_llvm/src/lib.rs
@@ -40,12 +40,13 @@ use rustc_metadata::EncodedMetadata;
 use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
 use rustc_middle::query::Providers;
 use rustc_middle::ty::TyCtxt;
-use rustc_session::config::{OptLevel, OutputFilenames, PrintRequest};
+use rustc_session::config::{OptLevel, OutputFilenames, PrintKind, PrintRequest};
 use rustc_session::Session;
 use rustc_span::symbol::Symbol;
 
 use std::any::Any;
 use std::ffi::CStr;
+use std::io::Write;
 
 mod back {
     pub mod archive;
@@ -178,7 +179,28 @@ impl WriteBackendMethods for LlvmCodegenBackend {
     type ThinBuffer = back::lto::ThinBuffer;
     fn print_pass_timings(&self) {
         unsafe {
-            llvm::LLVMRustPrintPassTimings();
+            let mut size = 0;
+            let cstr = llvm::LLVMRustPrintPassTimings(&mut size as *mut usize);
+            if cstr.is_null() {
+                println!("failed to get pass timings");
+            } else {
+                let timings = std::slice::from_raw_parts(cstr as *const u8, size);
+                std::io::stdout().write_all(timings).unwrap();
+                libc::free(cstr as *mut _);
+            }
+        }
+    }
+    fn print_statistics(&self) {
+        unsafe {
+            let mut size = 0;
+            let cstr = llvm::LLVMRustPrintStatistics(&mut size as *mut usize);
+            if cstr.is_null() {
+                println!("failed to get pass stats");
+            } else {
+                let stats = std::slice::from_raw_parts(cstr as *const u8, size);
+                std::io::stdout().write_all(stats).unwrap();
+                libc::free(cstr as *mut _);
+            }
         }
     }
     fn run_link(
@@ -262,10 +284,10 @@ impl CodegenBackend for LlvmCodegenBackend {
             |tcx, ()| llvm_util::global_llvm_features(tcx.sess, true)
     }
 
-    fn print(&self, req: PrintRequest, sess: &Session) {
-        match req {
-            PrintRequest::RelocationModels => {
-                println!("Available relocation models:");
+    fn print(&self, req: &PrintRequest, out: &mut dyn PrintBackendInfo, sess: &Session) {
+        match req.kind {
+            PrintKind::RelocationModels => {
+                writeln!(out, "Available relocation models:");
                 for name in &[
                     "static",
                     "pic",
@@ -276,26 +298,27 @@ impl CodegenBackend for LlvmCodegenBackend {
                     "ropi-rwpi",
                     "default",
                 ] {
-                    println!("    {}", name);
+                    writeln!(out, "    {}", name);
                 }
-                println!();
+                writeln!(out);
             }
-            PrintRequest::CodeModels => {
-                println!("Available code models:");
+            PrintKind::CodeModels => {
+                writeln!(out, "Available code models:");
                 for name in &["tiny", "small", "kernel", "medium", "large"] {
-                    println!("    {}", name);
+                    writeln!(out, "    {}", name);
                 }
-                println!();
+                writeln!(out);
             }
-            PrintRequest::TlsModels => {
-                println!("Available TLS models:");
+            PrintKind::TlsModels => {
+                writeln!(out, "Available TLS models:");
                 for name in &["global-dynamic", "local-dynamic", "initial-exec", "local-exec"] {
-                    println!("    {}", name);
+                    writeln!(out, "    {}", name);
                 }
-                println!();
+                writeln!(out);
             }
-            PrintRequest::StackProtectorStrategies => {
-                println!(
+            PrintKind::StackProtectorStrategies => {
+                writeln!(
+                    out,
                     r#"Available stack protector strategies:
     all
         Generate stack canaries in all functions.
@@ -319,7 +342,7 @@ impl CodegenBackend for LlvmCodegenBackend {
 "#
                 );
             }
-            req => llvm_util::print(req, sess),
+            _other => llvm_util::print(req, out, sess),
         }
     }
 
diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
index 605f0154773..5fe6407ac4f 100644
--- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
@@ -1868,7 +1868,10 @@ extern "C" {
     pub fn LLVMRustGetLastError() -> *const c_char;
 
     /// Print the pass timings since static dtors aren't picking them up.
-    pub fn LLVMRustPrintPassTimings();
+    pub fn LLVMRustPrintPassTimings(size: *const size_t) -> *const c_char;
+
+    /// Print the statistics since static dtors aren't picking them up.
+    pub fn LLVMRustPrintStatistics(size: *const size_t) -> *const c_char;
 
     pub fn LLVMStructCreateNamed(C: &Context, Name: *const c_char) -> &Type;
 
@@ -2280,7 +2283,12 @@ extern "C" {
 
     pub fn LLVMRustHasFeature(T: &TargetMachine, s: *const c_char) -> bool;
 
-    pub fn LLVMRustPrintTargetCPUs(T: &TargetMachine, cpu: *const c_char);
+    pub fn LLVMRustPrintTargetCPUs(
+        T: &TargetMachine,
+        cpu: *const c_char,
+        print: unsafe extern "C" fn(out: *mut c_void, string: *const c_char, len: usize),
+        out: *mut c_void,
+    );
     pub fn LLVMRustGetTargetFeaturesCount(T: &TargetMachine) -> size_t;
     pub fn LLVMRustGetTargetFeature(
         T: &TargetMachine,
diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs
index 03be0654b50..4f20fbf2045 100644
--- a/compiler/rustc_codegen_llvm/src/llvm_util.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs
@@ -8,16 +8,17 @@ use libc::c_int;
 use rustc_codegen_ssa::target_features::{
     supported_target_features, tied_target_features, RUSTC_SPECIFIC_FEATURES,
 };
+use rustc_codegen_ssa::traits::PrintBackendInfo;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::small_c_str::SmallCStr;
 use rustc_fs_util::path_to_c_string;
 use rustc_middle::bug;
-use rustc_session::config::PrintRequest;
+use rustc_session::config::{PrintKind, PrintRequest};
 use rustc_session::Session;
 use rustc_span::symbol::Symbol;
 use rustc_target::spec::{MergeFunctions, PanicStrategy};
-use std::ffi::{CStr, CString};
 
+use std::ffi::{c_char, c_void, CStr, CString};
 use std::path::Path;
 use std::ptr;
 use std::slice;
@@ -110,6 +111,10 @@ unsafe fn configure_llvm(sess: &Session) {
         // Use non-zero `import-instr-limit` multiplier for cold callsites.
         add("-import-cold-multiplier=0.1", false);
 
+        if sess.print_llvm_stats() {
+            add("-stats", false);
+        }
+
         for arg in sess_args {
             add(&(*arg), true);
         }
@@ -350,7 +355,7 @@ fn llvm_target_features(tm: &llvm::TargetMachine) -> Vec<(&str, &str)> {
     ret
 }
 
-fn print_target_features(sess: &Session, tm: &llvm::TargetMachine) {
+fn print_target_features(out: &mut dyn PrintBackendInfo, sess: &Session, tm: &llvm::TargetMachine) {
     let mut llvm_target_features = llvm_target_features(tm);
     let mut known_llvm_target_features = FxHashSet::<&'static str>::default();
     let mut rustc_target_features = supported_target_features(sess)
@@ -383,36 +388,48 @@ fn print_target_features(sess: &Session, tm: &llvm::TargetMachine) {
         .max()
         .unwrap_or(0);
 
-    println!("Features supported by rustc for this target:");
+    writeln!(out, "Features supported by rustc for this target:");
     for (feature, desc) in &rustc_target_features {
-        println!("    {1:0$} - {2}.", max_feature_len, feature, desc);
+        writeln!(out, "    {1:0$} - {2}.", max_feature_len, feature, desc);
     }
-    println!("\nCode-generation features supported by LLVM for this target:");
+    writeln!(out, "\nCode-generation features supported by LLVM for this target:");
     for (feature, desc) in &llvm_target_features {
-        println!("    {1:0$} - {2}.", max_feature_len, feature, desc);
+        writeln!(out, "    {1:0$} - {2}.", max_feature_len, feature, desc);
     }
     if llvm_target_features.is_empty() {
-        println!("    Target features listing is not supported by this LLVM version.");
+        writeln!(out, "    Target features listing is not supported by this LLVM version.");
     }
-    println!("\nUse +feature to enable a feature, or -feature to disable it.");
-    println!("For example, rustc -C target-cpu=mycpu -C target-feature=+feature1,-feature2\n");
-    println!("Code-generation features cannot be used in cfg or #[target_feature],");
-    println!("and may be renamed or removed in a future version of LLVM or rustc.\n");
+    writeln!(out, "\nUse +feature to enable a feature, or -feature to disable it.");
+    writeln!(out, "For example, rustc -C target-cpu=mycpu -C target-feature=+feature1,-feature2\n");
+    writeln!(out, "Code-generation features cannot be used in cfg or #[target_feature],");
+    writeln!(out, "and may be renamed or removed in a future version of LLVM or rustc.\n");
 }
 
-pub(crate) fn print(req: PrintRequest, sess: &Session) {
+pub(crate) fn print(req: &PrintRequest, mut out: &mut dyn PrintBackendInfo, sess: &Session) {
     require_inited();
     let tm = create_informational_target_machine(sess);
-    match req {
-        PrintRequest::TargetCPUs => {
+    match req.kind {
+        PrintKind::TargetCPUs => {
             // SAFETY generate a C compatible string from a byte slice to pass
             // the target CPU name into LLVM, the lifetime of the reference is
             // at least as long as the C function
             let cpu_cstring = CString::new(handle_native(sess.target.cpu.as_ref()))
                 .unwrap_or_else(|e| bug!("failed to convert to cstring: {}", e));
-            unsafe { llvm::LLVMRustPrintTargetCPUs(tm, cpu_cstring.as_ptr()) };
+            unsafe extern "C" fn callback(out: *mut c_void, string: *const c_char, len: usize) {
+                let out = &mut *(out as *mut &mut dyn PrintBackendInfo);
+                let bytes = slice::from_raw_parts(string as *const u8, len);
+                write!(out, "{}", String::from_utf8_lossy(bytes));
+            }
+            unsafe {
+                llvm::LLVMRustPrintTargetCPUs(
+                    tm,
+                    cpu_cstring.as_ptr(),
+                    callback,
+                    &mut out as *mut &mut dyn PrintBackendInfo as *mut c_void,
+                );
+            }
         }
-        PrintRequest::TargetFeatures => print_target_features(sess, tm),
+        PrintKind::TargetFeatures => print_target_features(out, sess, tm),
         _ => bug!("rustc_codegen_llvm can't handle print request: {:?}", req),
     }
 }
diff --git a/compiler/rustc_codegen_llvm/src/type_of.rs b/compiler/rustc_codegen_llvm/src/type_of.rs
index 2dbd467cc84..29dd53ff763 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 result = Ty::ty_and_layout_pointee_info_at(*self, cx, offset);
+        let assume_valid_ptr = true;
+        let result = Ty::ty_and_layout_pointee_info_at(*self, cx, offset, assume_valid_ptr);
 
         cx.pointee_infos.borrow_mut().insert((self.ty, offset), result);
         result
diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml
index 984efa21044..6582fd62387 100644
--- a/compiler/rustc_codegen_ssa/Cargo.toml
+++ b/compiler/rustc_codegen_ssa/Cargo.toml
@@ -17,7 +17,6 @@ tempfile = "3.2"
 thorin-dwp = "0.6"
 pathdiff = "0.2.0"
 serde_json = "1.0.59"
-snap = "1"
 smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
 regex = "1.4"
 
diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl
index f73080182bf..b6c70c62249 100644
--- a/compiler/rustc_codegen_ssa/messages.ftl
+++ b/compiler/rustc_codegen_ssa/messages.ftl
@@ -197,6 +197,8 @@ codegen_ssa_specify_libraries_to_link = use the `-l` flag to specify native libr
 
 codegen_ssa_static_library_native_artifacts = Link against the following native artifacts when linking against this static library. The order and any duplication can be significant on some platforms.
 
+codegen_ssa_static_library_native_artifacts_to_file = Native artifacts to link against have been written to {$path}. The order and any duplication can be significant on some platforms.
+
 codegen_ssa_stripping_debug_info_failed = stripping debug info with `{$util}` failed: {$status}
     .note = {$output}
 
diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs
index 0dfb41f42f0..eefa4ac34dd 100644
--- a/compiler/rustc_codegen_ssa/src/back/link.rs
+++ b/compiler/rustc_codegen_ssa/src/back/link.rs
@@ -12,8 +12,8 @@ use rustc_metadata::fs::{copy_to_stdout, emit_wrapper_file, METADATA_FILENAME};
 use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile;
 use rustc_middle::middle::dependency_format::Linkage;
 use rustc_middle::middle::exported_symbols::SymbolExportKind;
-use rustc_session::config::{self, CFGuard, CrateType, DebugInfo, Strip};
-use rustc_session::config::{OutputFilenames, OutputType, PrintRequest, SplitDwarfKind};
+use rustc_session::config::{self, CFGuard, CrateType, DebugInfo, OutFileName, Strip};
+use rustc_session::config::{OutputFilenames, OutputType, PrintKind, SplitDwarfKind};
 use rustc_session::cstore::DllImport;
 use rustc_session::output::{check_file_is_writeable, invalid_output_for_target, out_filename};
 use rustc_session::search_paths::PathKind;
@@ -596,8 +596,10 @@ fn link_staticlib<'a>(
 
     all_native_libs.extend_from_slice(&codegen_results.crate_info.used_libraries);
 
-    if sess.opts.prints.contains(&PrintRequest::NativeStaticLibs) {
-        print_native_static_libs(sess, &all_native_libs, &all_rust_dylibs);
+    for print in &sess.opts.prints {
+        if print.kind == PrintKind::NativeStaticLibs {
+            print_native_static_libs(sess, &print.out, &all_native_libs, &all_rust_dylibs);
+        }
     }
 
     Ok(())
@@ -744,8 +746,11 @@ fn link_natively<'a>(
         cmd.env_remove(k.as_ref());
     }
 
-    if sess.opts.prints.contains(&PrintRequest::LinkArgs) {
-        println!("{:?}", &cmd);
+    for print in &sess.opts.prints {
+        if print.kind == PrintKind::LinkArgs {
+            let content = format!("{:?}", cmd);
+            print.out.overwrite(&content, sess);
+        }
     }
 
     // May have not found libraries in the right formats.
@@ -1386,6 +1391,7 @@ enum RlibFlavor {
 
 fn print_native_static_libs(
     sess: &Session,
+    out: &OutFileName,
     all_native_libs: &[NativeLib],
     all_rust_dylibs: &[&Path],
 ) {
@@ -1459,11 +1465,22 @@ fn print_native_static_libs(
             lib_args.push(format!("-l{}", lib));
         }
     }
-    if !lib_args.is_empty() {
-        sess.emit_note(errors::StaticLibraryNativeArtifacts);
-        // Prefix for greppability
-        // Note: This must not be translated as tools are allowed to depend on this exact string.
-        sess.note_without_error(format!("native-static-libs: {}", &lib_args.join(" ")));
+
+    match out {
+        OutFileName::Real(path) => {
+            out.overwrite(&lib_args.join(" "), sess);
+            if !lib_args.is_empty() {
+                sess.emit_note(errors::StaticLibraryNativeArtifactsToFile { path });
+            }
+        }
+        OutFileName::Stdout => {
+            if !lib_args.is_empty() {
+                sess.emit_note(errors::StaticLibraryNativeArtifacts);
+                // Prefix for greppability
+                // Note: This must not be translated as tools are allowed to depend on this exact string.
+                sess.note_without_error(format!("native-static-libs: {}", &lib_args.join(" ")));
+            }
+        }
     }
 }
 
diff --git a/compiler/rustc_codegen_ssa/src/back/metadata.rs b/compiler/rustc_codegen_ssa/src/back/metadata.rs
index e8b8665e39d..c4bb51edade 100644
--- a/compiler/rustc_codegen_ssa/src/back/metadata.rs
+++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs
@@ -10,8 +10,6 @@ use object::{
     ObjectSymbol, SectionFlags, SectionKind, SymbolFlags, SymbolKind, SymbolScope,
 };
 
-use snap::write::FrameEncoder;
-
 use rustc_data_structures::memmap::Mmap;
 use rustc_data_structures::owned_slice::{try_slice_owned, OwnedSlice};
 use rustc_metadata::fs::METADATA_FILENAME;
@@ -481,19 +479,15 @@ pub fn create_compressed_metadata_file(
     metadata: &EncodedMetadata,
     symbol_name: &str,
 ) -> Vec<u8> {
-    let mut compressed = rustc_metadata::METADATA_HEADER.to_vec();
-    // Our length will be backfilled once we're done writing
-    compressed.write_all(&[0; 4]).unwrap();
-    FrameEncoder::new(&mut compressed).write_all(metadata.raw_data()).unwrap();
-    let meta_len = rustc_metadata::METADATA_HEADER.len();
-    let data_len = (compressed.len() - meta_len - 4) as u32;
-    compressed[meta_len..meta_len + 4].copy_from_slice(&data_len.to_be_bytes());
+    let mut packed_metadata = rustc_metadata::METADATA_HEADER.to_vec();
+    packed_metadata.write_all(&(metadata.raw_data().len() as u32).to_be_bytes()).unwrap();
+    packed_metadata.extend(metadata.raw_data());
 
     let Some(mut file) = create_object_file(sess) else {
-        return compressed.to_vec();
+        return packed_metadata.to_vec();
     };
     if file.format() == BinaryFormat::Xcoff {
-        return create_compressed_metadata_file_for_xcoff(file, &compressed, symbol_name);
+        return create_compressed_metadata_file_for_xcoff(file, &packed_metadata, symbol_name);
     }
     let section = file.add_section(
         file.segment_name(StandardSegment::Data).to_vec(),
@@ -507,14 +501,14 @@ pub fn create_compressed_metadata_file(
         }
         _ => {}
     };
-    let offset = file.append_section_data(section, &compressed, 1);
+    let offset = file.append_section_data(section, &packed_metadata, 1);
 
     // For MachO and probably PE this is necessary to prevent the linker from throwing away the
     // .rustc section. For ELF this isn't necessary, but it also doesn't harm.
     file.add_symbol(Symbol {
         name: symbol_name.as_bytes().to_vec(),
         value: offset,
-        size: compressed.len() as u64,
+        size: packed_metadata.len() as u64,
         kind: SymbolKind::Data,
         scope: SymbolScope::Dynamic,
         weak: false,
diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs
index 1c5d7a7c68e..6c781c6f082 100644
--- a/compiler/rustc_codegen_ssa/src/back/write.rs
+++ b/compiler/rustc_codegen_ssa/src/back/write.rs
@@ -1945,6 +1945,10 @@ impl<B: ExtraBackendMethods> OngoingCodegen<B> {
             self.backend.print_pass_timings()
         }
 
+        if sess.print_llvm_stats() {
+            self.backend.print_statistics()
+        }
+
         (
             CodegenResults {
                 metadata: self.metadata,
diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs
index 056b4abd235..c72d37be748 100644
--- a/compiler/rustc_codegen_ssa/src/errors.rs
+++ b/compiler/rustc_codegen_ssa/src/errors.rs
@@ -456,6 +456,12 @@ pub struct LinkerFileStem;
 pub struct StaticLibraryNativeArtifacts;
 
 #[derive(Diagnostic)]
+#[diag(codegen_ssa_static_library_native_artifacts_to_file)]
+pub struct StaticLibraryNativeArtifactsToFile<'a> {
+    pub path: &'a Path,
+}
+
+#[derive(Diagnostic)]
 #[diag(codegen_ssa_link_script_unavailable)]
 pub struct LinkScriptUnavailable;
 
diff --git a/compiler/rustc_codegen_ssa/src/mir/constant.rs b/compiler/rustc_codegen_ssa/src/mir/constant.rs
index 1c5031dfc4b..babcf9bee24 100644
--- a/compiler/rustc_codegen_ssa/src/mir/constant.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/constant.rs
@@ -65,8 +65,22 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
         &self,
         constant: &mir::Constant<'tcx>,
     ) -> Result<Option<ty::ValTree<'tcx>>, ErrorHandled> {
-        let uv = match constant.literal {
+        let uv = match self.monomorphize(constant.literal) {
             mir::ConstantKind::Unevaluated(uv, _) => uv.shrink(),
+            mir::ConstantKind::Ty(c) => match c.kind() {
+                // A constant that came from a const generic but was then used as an argument to old-style
+                // simd_shuffle (passing as argument instead of as a generic param).
+                rustc_type_ir::ConstKind::Value(valtree) => return Ok(Some(valtree)),
+                other => span_bug!(constant.span, "{other:#?}"),
+            },
+            // We should never encounter `ConstantKind::Val` unless MIR opts (like const prop) evaluate
+            // a constant and write that value back into `Operand`s. This could happen, but is unlikely.
+            // Also: all users of `simd_shuffle` are on unstable and already need to take a lot of care
+            // around intrinsics. For an issue to happen here, it would require a macro expanding to a
+            // `simd_shuffle` call without wrapping the constant argument in a `const {}` block, but
+            // the user pass through arbitrary expressions.
+            // FIXME(oli-obk): replace the magic const generic argument of `simd_shuffle` with a real
+            // const generic.
             other => span_bug!(constant.span, "{other:#?}"),
         };
         let uv = self.monomorphize(uv);
diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs
index b3c9ecf8b93..1991b55f191 100644
--- a/compiler/rustc_codegen_ssa/src/traits/backend.rs
+++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs
@@ -23,6 +23,8 @@ use rustc_span::symbol::Symbol;
 use rustc_target::abi::call::FnAbi;
 use rustc_target::spec::Target;
 
+use std::fmt;
+
 pub trait BackendTypes {
     type Value: CodegenObject;
     type Function: CodegenObject;
@@ -61,7 +63,7 @@ pub trait CodegenBackend {
     fn locale_resource(&self) -> &'static str;
 
     fn init(&self, _sess: &Session) {}
-    fn print(&self, _req: PrintRequest, _sess: &Session) {}
+    fn print(&self, _req: &PrintRequest, _out: &mut dyn PrintBackendInfo, _sess: &Session) {}
     fn target_features(&self, _sess: &Session, _allow_unstable: bool) -> Vec<Symbol> {
         vec![]
     }
@@ -162,3 +164,19 @@ pub trait ExtraBackendMethods:
         std::thread::Builder::new().name(name).spawn(f)
     }
 }
+
+pub trait PrintBackendInfo {
+    fn infallible_write_fmt(&mut self, args: fmt::Arguments<'_>);
+}
+
+impl PrintBackendInfo for String {
+    fn infallible_write_fmt(&mut self, args: fmt::Arguments<'_>) {
+        fmt::Write::write_fmt(self, args).unwrap();
+    }
+}
+
+impl dyn PrintBackendInfo + '_ {
+    pub fn write_fmt(&mut self, args: fmt::Arguments<'_>) {
+        self.infallible_write_fmt(args);
+    }
+}
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_codegen_ssa/src/traits/mod.rs b/compiler/rustc_codegen_ssa/src/traits/mod.rs
index 8cb58bd4c70..728c2bc8c49 100644
--- a/compiler/rustc_codegen_ssa/src/traits/mod.rs
+++ b/compiler/rustc_codegen_ssa/src/traits/mod.rs
@@ -30,7 +30,9 @@ mod write;
 
 pub use self::abi::AbiBuilderMethods;
 pub use self::asm::{AsmBuilderMethods, AsmMethods, GlobalAsmOperandRef, InlineAsmOperandRef};
-pub use self::backend::{Backend, BackendTypes, CodegenBackend, ExtraBackendMethods};
+pub use self::backend::{
+    Backend, BackendTypes, CodegenBackend, ExtraBackendMethods, PrintBackendInfo,
+};
 pub use self::builder::{BuilderMethods, OverflowOp};
 pub use self::consts::ConstMethods;
 pub use self::coverageinfo::CoverageInfoBuilderMethods;
diff --git a/compiler/rustc_codegen_ssa/src/traits/write.rs b/compiler/rustc_codegen_ssa/src/traits/write.rs
index 9826256a4c5..a3b11ed4fe8 100644
--- a/compiler/rustc_codegen_ssa/src/traits/write.rs
+++ b/compiler/rustc_codegen_ssa/src/traits/write.rs
@@ -35,6 +35,7 @@ pub trait WriteBackendMethods: 'static + Sized + Clone {
         cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>,
     ) -> Result<(Vec<LtoModuleCodegen<Self>>, Vec<WorkProduct>), FatalError>;
     fn print_pass_timings(&self);
+    fn print_statistics(&self);
     unsafe fn optimize(
         cgcx: &CodegenContext<Self>,
         diag_handler: &Handler,
diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl
index d8eade5bd2a..8833f55831c 100644
--- a/compiler/rustc_const_eval/messages.ftl
+++ b/compiler/rustc_const_eval/messages.ftl
@@ -244,7 +244,6 @@ 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
 
diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs
index 267795a6cb4..0a9a47b2837 100644
--- a/compiler/rustc_const_eval/src/const_eval/machine.rs
+++ b/compiler/rustc_const_eval/src/const_eval/machine.rs
@@ -1,7 +1,6 @@
 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;
@@ -17,7 +16,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, Size};
+use rustc_target::abi::{Align, HasDataLayout as _, Size};
 use rustc_target::spec::abi::Abi as CallAbi;
 
 use crate::errors::{LongRunning, LongRunningWarn};
@@ -304,8 +303,8 @@ impl<'mir, 'tcx: 'mir> CompileTimeEvalContext<'mir, 'tcx> {
                     Ok(ControlFlow::Break(()))
                 } else {
                     // Not alignable in const, return `usize::MAX`.
-                    let usize_max = Scalar::from_target_usize(self.target_usize_max(), self);
-                    self.write_scalar(usize_max, dest)?;
+                    let usize_max = self.data_layout().target_usize_max();
+                    self.write_scalar(Scalar::from_target_usize(usize_max, self), dest)?;
                     self.return_to_block(ret)?;
                     Ok(ControlFlow::Break(()))
                 }
@@ -333,7 +332,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.scalar_may_be_null(ptr)? =>
+                if int.is_null() && !self.ptr_scalar_range(ptr)?.contains(&0) =>
             {
                 0
             }
diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs
index ca38cce710e..61ce695ccd2 100644
--- a/compiler/rustc_const_eval/src/errors.rs
+++ b/compiler/rustc_const_eval/src/errors.rs
@@ -617,7 +617,6 @@ 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,
@@ -732,9 +731,7 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> {
             | InvalidFnPtr { value } => {
                 err.set_arg("value", value);
             }
-            NullablePtrOutOfRange { range, max_value } | PtrOutOfRange { range, max_value } => {
-                add_range_arg(range, max_value, handler, err)
-            }
+            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);
diff --git a/compiler/rustc_const_eval/src/interpret/discriminant.rs b/compiler/rustc_const_eval/src/interpret/discriminant.rs
index f23a455c2ca..99ea0ab18bc 100644
--- a/compiler/rustc_const_eval/src/interpret/discriminant.rs
+++ b/compiler/rustc_const_eval/src/interpret/discriminant.rs
@@ -2,8 +2,7 @@
 
 use rustc_middle::ty::layout::{LayoutOf, PrimitiveExt};
 use rustc_middle::{mir, ty};
-use rustc_target::abi::{self, TagEncoding};
-use rustc_target::abi::{VariantIdx, Variants};
+use rustc_target::abi::{self, TagEncoding, VariantIdx, Variants, WrappingRange};
 
 use super::{ImmTy, InterpCx, InterpResult, Machine, OpTy, PlaceTy, Scalar};
 
@@ -180,19 +179,24 @@ 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 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 {
+                        // 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) {
                             throw_ub!(InvalidTag(dbg_val))
+                        } else {
+                            untagged_variant
                         }
-                        untagged_variant
                     }
                     Ok(tag_bits) => {
                         let tag_bits = tag_bits.assert_bits(tag_layout.size);
@@ -205,7 +209,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 <= u128::from(variants_end - variants_start) {
+                        if variant_index_relative <= variants_len {
                             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.
diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
index 04cae23f852..8ec9a71bf3a 100644
--- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs
+++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
@@ -5,9 +5,7 @@
 use rustc_hir::def_id::DefId;
 use rustc_middle::mir::{
     self,
-    interpret::{
-        Allocation, ConstAllocation, ConstValue, GlobalId, InterpResult, PointerArithmetic, Scalar,
-    },
+    interpret::{Allocation, ConstAllocation, ConstValue, GlobalId, InterpResult, Scalar},
     BinOp, NonDivergingIntrinsic,
 };
 use rustc_middle::ty;
@@ -15,7 +13,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, Primitive, Size};
+use rustc_target::abi::{Abi, Align, HasDataLayout as _, Primitive, Size};
 
 use super::{
     util::ensure_monomorphic_enough, CheckInAllocMsg, ImmTy, InterpCx, Machine, OpTy, PlaceTy,
@@ -361,11 +359,12 @@ 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 <= self.target_isize_max());
+                    assert!(0 <= dist && dist <= dl.target_isize_max());
                     usize_layout
                 } else {
-                    assert!(self.target_isize_min() <= dist && dist <= self.target_isize_max());
+                    assert!(dl.target_isize_min() <= dist && dist <= dl.target_isize_max());
                     isize_layout
                 };
                 let pointee_layout = self.layout_of(instance_args.type_at(0))?;
diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs
index 7b44a20ef03..29fc5ffcfe7 100644
--- a/compiler/rustc_const_eval/src/interpret/memory.rs
+++ b/compiler/rustc_const_eval/src/interpret/memory.rs
@@ -10,6 +10,7 @@ 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;
@@ -1222,24 +1223,34 @@ 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> {
-    /// Test if this value might be null.
+    /// Turn a pointer-sized scalar into a (non-empty) range of possible values.
     /// If the machine does not support ptr-to-int casts, this is conservative.
-    pub fn scalar_may_be_null(&self, scalar: Scalar<M::Provenance>) -> InterpResult<'tcx, bool> {
-        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"),
+    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)
                 }
             }
+            Err(_offset) => bug!("a non-int scalar is always a pointer"),
         })
     }
 
diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs
index da45fdca1a1..47383c0eabc 100644
--- a/compiler/rustc_const_eval/src/interpret/operand.rs
+++ b/compiler/rustc_const_eval/src/interpret/operand.rs
@@ -31,7 +31,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,
 }
 
diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs
index 21c655988a0..108394d224b 100644
--- a/compiler/rustc_const_eval/src/interpret/validity.rs
+++ b/compiler/rustc_const_eval/src/interpret/validity.rs
@@ -19,9 +19,7 @@ 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, WrappingRange,
-};
+use rustc_target::abi::{Abi, FieldIdx, Scalar as ScalarAbi, Size, VariantIdx, Variants};
 
 use std::hash::Hash;
 
@@ -554,7 +552,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.scalar_may_be_null(value)? {
+                    if self.ecx.ptr_scalar_range(value)?.contains(&0) {
                         throw_validation_failure!(self.path, NullFnPtr);
                     }
                 }
@@ -595,46 +593,36 @@ 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!(end <= max_value);
-        let bits = match scalar.try_to_int() {
-            Ok(int) => int.assert_bits(size),
+        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 }
+                    )
+                }
+            }
             Err(_) => {
                 // So this is a pointer then, and casting to an int failed.
                 // Can only happen during CTFE.
-                // 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(());
+                // 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(())
                 } else {
-                    // Conservatively, we reject, because the pointer *could* have a bad
-                    // value.
+                    // Reject conservatively, 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 }
-            )
         }
     }
 }
diff --git a/compiler/rustc_driver_impl/messages.ftl b/compiler/rustc_driver_impl/messages.ftl
index 9b2f2c33860..d3bd3244a52 100644
--- a/compiler/rustc_driver_impl/messages.ftl
+++ b/compiler/rustc_driver_impl/messages.ftl
@@ -19,5 +19,3 @@ driver_impl_rlink_rustc_version_mismatch = .rlink file was produced by rustc ver
 driver_impl_rlink_unable_to_read = failed to read rlink file: `{$err}`
 
 driver_impl_rlink_wrong_file_type = The input does not look like a .rlink file
-
-driver_impl_unpretty_dump_fail = pretty-print failed to write `{$path}` due to error `{$err}`
diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs
index 11303e7d09e..8722979cb5e 100644
--- a/compiler/rustc_driver_impl/src/lib.rs
+++ b/compiler/rustc_driver_impl/src/lib.rs
@@ -37,9 +37,7 @@ use rustc_interface::{interface, Queries};
 use rustc_lint::LintStore;
 use rustc_metadata::locator;
 use rustc_session::config::{nightly_options, CG_OPTIONS, Z_OPTIONS};
-use rustc_session::config::{
-    ErrorOutputType, Input, OutFileName, OutputType, PrintRequest, TrimmedDefPaths,
-};
+use rustc_session::config::{ErrorOutputType, Input, OutFileName, OutputType, TrimmedDefPaths};
 use rustc_session::cstore::MetadataLoader;
 use rustc_session::getopts::{self, Matches};
 use rustc_session::lint::{Lint, LintId};
@@ -53,6 +51,7 @@ use std::cmp::max;
 use std::collections::BTreeMap;
 use std::env;
 use std::ffi::OsString;
+use std::fmt::Write as _;
 use std::fs;
 use std::io::{self, IsTerminal, Read, Write};
 use std::panic::{self, catch_unwind};
@@ -72,6 +71,11 @@ macro do_not_use_print($($t:tt)*) {
     )
 }
 
+#[allow(unused_macros)]
+macro do_not_use_safe_print($($t:tt)*) {
+    std::compile_error!("Don't use `safe_print` or `safe_println` here, use `println_info` instead")
+}
+
 // This import blocks the use of panicking `print` and `println` in all the code
 // below. Please use `safe_print` and `safe_println` to avoid ICE when
 // encountering an I/O error during print.
@@ -720,10 +724,17 @@ fn print_crate_info(
     sess: &Session,
     parse_attrs: bool,
 ) -> Compilation {
-    use rustc_session::config::PrintRequest::*;
+    use rustc_session::config::PrintKind::*;
+
+    // This import prevents the following code from using the printing macros
+    // used by the rest of the module. Within this function, we only write to
+    // the output specified by `sess.io.output_file`.
+    #[allow(unused_imports)]
+    use {do_not_use_safe_print as safe_print, do_not_use_safe_print as safe_println};
+
     // NativeStaticLibs and LinkArgs are special - printed during linking
     // (empty iterator returns true)
-    if sess.opts.prints.iter().all(|&p| p == NativeStaticLibs || p == LinkArgs) {
+    if sess.opts.prints.iter().all(|p| p.kind == NativeStaticLibs || p.kind == LinkArgs) {
         return Compilation::Continue;
     }
 
@@ -739,17 +750,23 @@ fn print_crate_info(
     } else {
         None
     };
+
     for req in &sess.opts.prints {
-        match *req {
+        let mut crate_info = String::new();
+        macro println_info($($arg:tt)*) {
+            crate_info.write_fmt(format_args!("{}\n", format_args!($($arg)*))).unwrap()
+        }
+
+        match req.kind {
             TargetList => {
                 let mut targets = rustc_target::spec::TARGETS.to_vec();
                 targets.sort_unstable();
-                safe_println!("{}", targets.join("\n"));
+                println_info!("{}", targets.join("\n"));
             }
-            Sysroot => safe_println!("{}", sess.sysroot.display()),
-            TargetLibdir => safe_println!("{}", sess.target_tlib_path.dir.display()),
+            Sysroot => println_info!("{}", sess.sysroot.display()),
+            TargetLibdir => println_info!("{}", sess.target_tlib_path.dir.display()),
             TargetSpec => {
-                safe_println!("{}", serde_json::to_string_pretty(&sess.target.to_json()).unwrap());
+                println_info!("{}", serde_json::to_string_pretty(&sess.target.to_json()).unwrap());
             }
             AllTargetSpecs => {
                 let mut targets = BTreeMap::new();
@@ -758,26 +775,30 @@ fn print_crate_info(
                     let target = Target::expect_builtin(&triple);
                     targets.insert(name, target.to_json());
                 }
-                safe_println!("{}", serde_json::to_string_pretty(&targets).unwrap());
+                println_info!("{}", serde_json::to_string_pretty(&targets).unwrap());
             }
-            FileNames | CrateName => {
+            FileNames => {
                 let Some(attrs) = attrs.as_ref() else {
                     // no crate attributes, print out an error and exit
                     return Compilation::Continue;
                 };
                 let t_outputs = rustc_interface::util::build_output_filenames(attrs, sess);
                 let id = rustc_session::output::find_crate_name(sess, attrs);
-                if *req == PrintRequest::CrateName {
-                    safe_println!("{id}");
-                    continue;
-                }
                 let crate_types = collect_crate_types(sess, attrs);
                 for &style in &crate_types {
                     let fname =
                         rustc_session::output::filename_for_input(sess, style, id, &t_outputs);
-                    safe_println!("{}", fname.as_path().file_name().unwrap().to_string_lossy());
+                    println_info!("{}", fname.as_path().file_name().unwrap().to_string_lossy());
                 }
             }
+            CrateName => {
+                let Some(attrs) = attrs.as_ref() else {
+                    // no crate attributes, print out an error and exit
+                    return Compilation::Continue;
+                };
+                let id = rustc_session::output::find_crate_name(sess, attrs);
+                println_info!("{id}");
+            }
             Cfg => {
                 let mut cfgs = sess
                     .parse_sess
@@ -809,13 +830,13 @@ fn print_crate_info(
 
                 cfgs.sort();
                 for cfg in cfgs {
-                    safe_println!("{cfg}");
+                    println_info!("{cfg}");
                 }
             }
             CallingConventions => {
                 let mut calling_conventions = rustc_target::spec::abi::all_names();
                 calling_conventions.sort_unstable();
-                safe_println!("{}", calling_conventions.join("\n"));
+                println_info!("{}", calling_conventions.join("\n"));
             }
             RelocationModels
             | CodeModels
@@ -823,7 +844,7 @@ fn print_crate_info(
             | TargetCPUs
             | StackProtectorStrategies
             | TargetFeatures => {
-                codegen_backend.print(*req, sess);
+                codegen_backend.print(req, &mut crate_info, sess);
             }
             // Any output here interferes with Cargo's parsing of other printed output
             NativeStaticLibs => {}
@@ -833,7 +854,7 @@ fn print_crate_info(
 
                 for split in &[Off, Packed, Unpacked] {
                     if sess.target.options.supported_split_debuginfo.contains(split) {
-                        safe_println!("{split}");
+                        println_info!("{split}");
                     }
                 }
             }
@@ -841,7 +862,7 @@ fn print_crate_info(
                 use rustc_target::spec::current_apple_deployment_target;
 
                 if sess.target.is_like_osx {
-                    safe_println!(
+                    println_info!(
                         "deployment_target={}",
                         current_apple_deployment_target(&sess.target)
                             .expect("unknown Apple target OS")
@@ -852,6 +873,8 @@ fn print_crate_info(
                 }
             }
         }
+
+        req.out.overwrite(&crate_info, sess);
     }
     Compilation::Stop
 }
diff --git a/compiler/rustc_driver_impl/src/pretty.rs b/compiler/rustc_driver_impl/src/pretty.rs
index 24a5f4030b8..222c7b5d6a7 100644
--- a/compiler/rustc_driver_impl/src/pretty.rs
+++ b/compiler/rustc_driver_impl/src/pretty.rs
@@ -1,6 +1,5 @@
 //! The various pretty-printing routines.
 
-use crate::session_diagnostics::UnprettyDumpFail;
 use rustc_ast as ast;
 use rustc_ast_pretty::pprust;
 use rustc_errors::ErrorGuaranteed;
@@ -358,17 +357,7 @@ fn get_source(sess: &Session) -> (String, FileName) {
 }
 
 fn write_or_print(out: &str, sess: &Session) {
-    match &sess.io.output_file {
-        None | Some(OutFileName::Stdout) => print!("{out}"),
-        Some(OutFileName::Real(p)) => {
-            if let Err(e) = std::fs::write(p, out) {
-                sess.emit_fatal(UnprettyDumpFail {
-                    path: p.display().to_string(),
-                    err: e.to_string(),
-                });
-            }
-        }
-    }
+    sess.io.output_file.as_ref().unwrap_or(&OutFileName::Stdout).overwrite(out, sess);
 }
 
 pub fn print_after_parsing(sess: &Session, krate: &ast::Crate, ppm: PpMode) {
diff --git a/compiler/rustc_driver_impl/src/session_diagnostics.rs b/compiler/rustc_driver_impl/src/session_diagnostics.rs
index f7f06b7d0f2..5eb587c54d9 100644
--- a/compiler/rustc_driver_impl/src/session_diagnostics.rs
+++ b/compiler/rustc_driver_impl/src/session_diagnostics.rs
@@ -33,13 +33,6 @@ pub(crate) struct RLinkRustcVersionMismatch<'a> {
 pub(crate) struct RlinkNotAFile;
 
 #[derive(Diagnostic)]
-#[diag(driver_impl_unpretty_dump_fail)]
-pub(crate) struct UnprettyDumpFail {
-    pub path: String,
-    pub err: String,
-}
-
-#[derive(Diagnostic)]
 #[diag(driver_impl_ice)]
 pub(crate) struct Ice;
 
diff --git a/compiler/rustc_error_codes/src/error_codes/E0691.md b/compiler/rustc_error_codes/src/error_codes/E0691.md
index 60060cacbd6..483c74c0ff5 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0691.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0691.md
@@ -11,7 +11,8 @@ struct ForceAlign32;
 
 #[repr(transparent)]
 struct Wrapper(f32, ForceAlign32); // error: zero-sized field in transparent
-                                   //        struct has alignment larger than 1
+                                   //        struct has alignment of 32, which
+                                   //        is larger than 1
 ```
 
 A transparent struct, enum, or union is supposed to be represented exactly like
diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index d9e14096954..e0698a72335 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -1078,9 +1078,9 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
         // We are currently checking the type this field came from, so it must be local
         let span = tcx.hir().span_if_local(field.did).unwrap();
         let zst = layout.is_ok_and(|layout| layout.is_zst());
-        let align1 = layout.is_ok_and(|layout| layout.align.abi.bytes() == 1);
+        let align = layout.ok().map(|layout| layout.align.abi.bytes());
         if !zst {
-            return (span, zst, align1, None);
+            return (span, zst, align, None);
         }
 
         fn check_non_exhaustive<'tcx>(
@@ -1115,12 +1115,12 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
             }
         }
 
-        (span, zst, align1, check_non_exhaustive(tcx, ty).break_value())
+        (span, zst, align, check_non_exhaustive(tcx, ty).break_value())
     });
 
     let non_zst_fields = field_infos
         .clone()
-        .filter_map(|(span, zst, _align1, _non_exhaustive)| if !zst { Some(span) } else { None });
+        .filter_map(|(span, zst, _align, _non_exhaustive)| if !zst { Some(span) } else { None });
     let non_zst_count = non_zst_fields.clone().count();
     if non_zst_count >= 2 {
         bad_non_zero_sized_fields(tcx, adt, non_zst_count, non_zst_fields, tcx.def_span(adt.did()));
@@ -1128,17 +1128,26 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
     let incompatible_zst_fields =
         field_infos.clone().filter(|(_, _, _, opt)| opt.is_some()).count();
     let incompat = incompatible_zst_fields + non_zst_count >= 2 && non_zst_count < 2;
-    for (span, zst, align1, non_exhaustive) in field_infos {
-        if zst && !align1 {
-            struct_span_err!(
+    for (span, zst, align, non_exhaustive) in field_infos {
+        if zst && align != Some(1) {
+            let mut err = struct_span_err!(
                 tcx.sess,
                 span,
                 E0691,
                 "zero-sized field in transparent {} has alignment larger than 1",
                 adt.descr(),
-            )
-            .span_label(span, "has alignment larger than 1")
-            .emit();
+            );
+
+            if let Some(align_bytes) = align {
+                err.span_label(
+                    span,
+                    format!("has alignment of {align_bytes}, which is larger than 1"),
+                );
+            } else {
+                err.span_label(span, "may have alignment larger than 1");
+            }
+
+            err.emit();
         }
         if incompat && let Some((descr, def_id, args, non_exhaustive)) = non_exhaustive {
             tcx.struct_span_lint_hir(
diff --git a/compiler/rustc_hir_analysis/src/check_unused.rs b/compiler/rustc_hir_analysis/src/check_unused.rs
index 5318e637fc7..d10bc5b34ea 100644
--- a/compiler/rustc_hir_analysis/src/check_unused.rs
+++ b/compiler/rustc_hir_analysis/src/check_unused.rs
@@ -1,11 +1,16 @@
 use rustc_data_structures::unord::{ExtendUnord, UnordSet};
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::LocalDefId;
+use rustc_middle::query::Providers;
 use rustc_middle::ty::TyCtxt;
 use rustc_session::lint;
 
-pub fn check_crate(tcx: TyCtxt<'_>) {
-    let mut used_trait_imports: UnordSet<LocalDefId> = Default::default();
+pub fn provide(providers: &mut Providers) {
+    *providers = Providers { check_unused_traits, ..*providers };
+}
+
+fn check_unused_traits(tcx: TyCtxt<'_>, (): ()) {
+    let mut used_trait_imports = UnordSet::<LocalDefId>::default();
 
     // FIXME: Use `tcx.hir().par_body_owners()` when we implement creating `DefId`s
     // for anon constants during their parents' typeck.
diff --git a/compiler/rustc_hir_analysis/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs
index 05c78f57088..21ffbefcd08 100644
--- a/compiler/rustc_hir_analysis/src/coherence/orphan.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs
@@ -568,10 +568,10 @@ fn fast_reject_auto_impl<'tcx>(tcx: TyCtxt<'tcx>, trait_def_id: DefId, self_ty:
 
     impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for DisableAutoTraitVisitor<'tcx> {
         type BreakTy = ();
-        fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+        fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
             let tcx = self.tcx;
-            if t != self.self_ty_root {
-                for impl_def_id in tcx.non_blanket_impls_for_ty(self.trait_def_id, t) {
+            if ty != self.self_ty_root {
+                for impl_def_id in tcx.non_blanket_impls_for_ty(self.trait_def_id, ty) {
                     match tcx.impl_polarity(impl_def_id) {
                         ImplPolarity::Negative => return ControlFlow::Break(()),
                         ImplPolarity::Reservation => {}
@@ -584,7 +584,7 @@ fn fast_reject_auto_impl<'tcx>(tcx: TyCtxt<'tcx>, trait_def_id: DefId, self_ty:
                 }
             }
 
-            match t.kind() {
+            match ty.kind() {
                 ty::Adt(def, args) if def.is_phantom_data() => args.visit_with(self),
                 ty::Adt(def, args) => {
                     // @lcnr: This is the only place where cycles can happen. We avoid this
@@ -599,7 +599,7 @@ fn fast_reject_auto_impl<'tcx>(tcx: TyCtxt<'tcx>, trait_def_id: DefId, self_ty:
 
                     ControlFlow::Continue(())
                 }
-                _ => t.super_visit_with(self),
+                _ => ty.super_visit_with(self),
             }
         }
     }
diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs
index d7e62457f36..4f95174f869 100644
--- a/compiler/rustc_hir_analysis/src/lib.rs
+++ b/compiler/rustc_hir_analysis/src/lib.rs
@@ -177,6 +177,7 @@ pub fn provide(providers: &mut Providers) {
     collect::provide(providers);
     coherence::provide(providers);
     check::provide(providers);
+    check_unused::provide(providers);
     variance::provide(providers);
     outlives::provide(providers);
     impl_wf_check::provide(providers);
@@ -247,7 +248,7 @@ pub fn check_crate(tcx: TyCtxt<'_>) -> Result<(), ErrorGuaranteed> {
         }
     });
 
-    check_unused::check_crate(tcx);
+    tcx.ensure().check_unused_traits(());
 
     if let Some(reported) = tcx.sess.has_errors() { Err(reported) } else { Ok(()) }
 }
diff --git a/compiler/rustc_infer/src/traits/util.rs b/compiler/rustc_infer/src/traits/util.rs
index 074ff7ec97f..87ba6b3ec50 100644
--- a/compiler/rustc_infer/src/traits/util.rs
+++ b/compiler/rustc_infer/src/traits/util.rs
@@ -25,6 +25,13 @@ impl<'tcx> PredicateSet<'tcx> {
         Self { tcx, set: Default::default() }
     }
 
+    /// Adds a predicate to the set.
+    ///
+    /// Returns whether the predicate was newly inserted. That is:
+    /// - If the set did not previously contain this predicate, `true` is returned.
+    /// - If the set already contained this predicate, `false` is returned,
+    ///   and the set is not modified: original predicate is not replaced,
+    ///   and the predicate passed as argument is dropped.
     pub fn insert(&mut self, pred: ty::Predicate<'tcx>) -> bool {
         // We have to be careful here because we want
         //
diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs
index 5c6c3491b38..aedc662b067 100644
--- a/compiler/rustc_interface/src/tests.rs
+++ b/compiler/rustc_interface/src/tests.rs
@@ -28,6 +28,7 @@ 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};
 
@@ -715,6 +716,7 @@ fn test_unstable_options_tracking_hash() {
     untracked!(perf_stats, true);
     // `pre_link_arg` is omitted because it just forwards to `pre_link_args`.
     untracked!(pre_link_args, vec![String::from("abc"), String::from("def")]);
+    untracked!(print_codegen_stats, true);
     untracked!(print_llvm_passes, true);
     untracked!(print_mono_items, Some(String::from("abc")));
     untracked!(print_type_sizes, true);
@@ -819,6 +821,7 @@ 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_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
index eb3d67e720f..71df17c9ce7 100644
--- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
+++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
@@ -1,5 +1,6 @@
 #include <stdio.h>
 
+#include <iomanip>
 #include <vector>
 #include <set>
 
@@ -306,44 +307,55 @@ static size_t getLongestEntryLength(ArrayRef<KV> Table) {
   return MaxLen;
 }
 
-extern "C" void LLVMRustPrintTargetCPUs(LLVMTargetMachineRef TM, const char* TargetCPU) {
+using PrintBackendInfo = void(void*, const char* Data, size_t Len);
+
+extern "C" void LLVMRustPrintTargetCPUs(LLVMTargetMachineRef TM,
+                                        const char* TargetCPU,
+                                        PrintBackendInfo Print,
+                                        void* Out) {
   const TargetMachine *Target = unwrap(TM);
   const MCSubtargetInfo *MCInfo = Target->getMCSubtargetInfo();
   const Triple::ArchType HostArch = Triple(sys::getDefaultTargetTriple()).getArch();
   const Triple::ArchType TargetArch = Target->getTargetTriple().getArch();
 
+  std::ostringstream Buf;
+
 #if LLVM_VERSION_GE(17, 0)
   const ArrayRef<SubtargetSubTypeKV> CPUTable = MCInfo->getAllProcessorDescriptions();
 #elif defined(LLVM_RUSTLLVM)
   const ArrayRef<SubtargetSubTypeKV> CPUTable = MCInfo->getCPUTable();
 #else
-  printf("Full target CPU help is not supported by this LLVM version.\n\n");
+  Buf << "Full target CPU help is not supported by this LLVM version.\n\n";
   SubtargetSubTypeKV TargetCPUKV = { TargetCPU, {{}}, {{}} };
   const ArrayRef<SubtargetSubTypeKV> CPUTable = TargetCPUKV;
 #endif
   unsigned MaxCPULen = getLongestEntryLength(CPUTable);
 
-  printf("Available CPUs for this target:\n");
+  Buf << "Available CPUs for this target:\n";
   // Don't print the "native" entry when the user specifies --target with a
   // different arch since that could be wrong or misleading.
   if (HostArch == TargetArch) {
     MaxCPULen = std::max(MaxCPULen, (unsigned) std::strlen("native"));
     const StringRef HostCPU = sys::getHostCPUName();
-    printf("    %-*s - Select the CPU of the current host (currently %.*s).\n",
-      MaxCPULen, "native", (int)HostCPU.size(), HostCPU.data());
+    Buf << "    " << std::left << std::setw(MaxCPULen) << "native"
+        << " - Select the CPU of the current host "
+           "(currently " << HostCPU.str() << ").\n";
   }
   for (auto &CPU : CPUTable) {
     // Compare cpu against current target to label the default
     if (strcmp(CPU.Key, TargetCPU) == 0) {
-      printf("    %-*s - This is the default target CPU"
-      " for the current build target (currently %s).",
-        MaxCPULen, CPU.Key, Target->getTargetTriple().str().c_str());
+      Buf << "    " << std::left << std::setw(MaxCPULen) << CPU.Key
+          << " - This is the default target CPU for the current build target "
+             "(currently " << Target->getTargetTriple().str() << ").";
     }
     else {
-      printf("    %-*s", MaxCPULen, CPU.Key);
+      Buf << "    " << CPU.Key;
     }
-    printf("\n");
+    Buf << "\n";
   }
+
+  const auto &BufString = Buf.str();
+  Print(Out, BufString.data(), BufString.size());
 }
 
 extern "C" size_t LLVMRustGetTargetFeaturesCount(LLVMTargetMachineRef TM) {
@@ -1354,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_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
index ab5fa961b95..870a2e02cba 100644
--- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
+++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
@@ -1,4 +1,5 @@
 #include "LLVMWrapper.h"
+#include "llvm/ADT/Statistic.h"
 #include "llvm/IR/DebugInfoMetadata.h"
 #include "llvm/IR/DiagnosticHandler.h"
 #include "llvm/IR/DiagnosticInfo.h"
@@ -111,9 +112,26 @@ extern "C" void LLVMRustSetNormalizedTarget(LLVMModuleRef M,
   unwrap(M)->setTargetTriple(Triple::normalize(Triple));
 }
 
-extern "C" void LLVMRustPrintPassTimings() {
-  raw_fd_ostream OS(2, false); // stderr.
-  TimerGroup::printAll(OS);
+extern "C" const char *LLVMRustPrintPassTimings(size_t *Len) {
+  std::string buf;
+  raw_string_ostream SS(buf);
+  TimerGroup::printAll(SS);
+  SS.flush();
+  *Len = buf.length();
+  char *CStr = (char *)malloc(*Len);
+  memcpy(CStr, buf.c_str(), *Len);
+  return CStr;
+}
+
+extern "C" const char *LLVMRustPrintStatistics(size_t *Len) {
+  std::string buf;
+  raw_string_ostream SS(buf);
+  llvm::PrintStatistics(SS);
+  SS.flush();
+  *Len = buf.length();
+  char *CStr = (char *)malloc(*Len);
+  memcpy(CStr, buf.c_str(), *Len);
+  return CStr;
 }
 
 extern "C" LLVMValueRef LLVMRustGetNamedValue(LLVMModuleRef M, const char *Name,
diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs
index a1511c4b570..44195996762 100644
--- a/compiler/rustc_metadata/src/locator.rs
+++ b/compiler/rustc_metadata/src/locator.rs
@@ -511,7 +511,7 @@ impl<'a> CrateLocator<'a> {
             rlib: self.extract_one(rlibs, CrateFlavor::Rlib, &mut slot)?,
             dylib: self.extract_one(dylibs, CrateFlavor::Dylib, &mut slot)?,
         };
-        Ok(slot.map(|(svh, metadata)| (svh, Library { source, metadata })))
+        Ok(slot.map(|(svh, metadata, _)| (svh, Library { source, metadata })))
     }
 
     fn needs_crate_flavor(&self, flavor: CrateFlavor) -> bool {
@@ -535,11 +535,13 @@ impl<'a> CrateLocator<'a> {
     // read the metadata from it if `*slot` is `None`. If the metadata couldn't
     // be read, it is assumed that the file isn't a valid rust library (no
     // errors are emitted).
+    //
+    // The `PathBuf` in `slot` will only be used for diagnostic purposes.
     fn extract_one(
         &mut self,
         m: FxHashMap<PathBuf, PathKind>,
         flavor: CrateFlavor,
-        slot: &mut Option<(Svh, MetadataBlob)>,
+        slot: &mut Option<(Svh, MetadataBlob, PathBuf)>,
     ) -> Result<Option<(PathBuf, PathKind)>, CrateError> {
         // If we are producing an rlib, and we've already loaded metadata, then
         // we should not attempt to discover further crate sources (unless we're
@@ -550,16 +552,9 @@ impl<'a> CrateLocator<'a> {
         //
         // See also #68149 which provides more detail on why emitting the
         // dependency on the rlib is a bad thing.
-        //
-        // We currently do not verify that these other sources are even in sync,
-        // and this is arguably a bug (see #10786), but because reading metadata
-        // is quite slow (especially from dylibs) we currently do not read it
-        // from the other crate sources.
         if slot.is_some() {
             if m.is_empty() || !self.needs_crate_flavor(flavor) {
                 return Ok(None);
-            } else if m.len() == 1 {
-                return Ok(Some(m.into_iter().next().unwrap()));
             }
         }
 
@@ -610,8 +605,7 @@ impl<'a> CrateLocator<'a> {
                         candidates,
                     ));
                 }
-                err_data = Some(vec![ret.as_ref().unwrap().0.clone()]);
-                *slot = None;
+                err_data = Some(vec![slot.take().unwrap().2]);
             }
             if let Some(candidates) = &mut err_data {
                 candidates.push(lib);
@@ -644,7 +638,7 @@ impl<'a> CrateLocator<'a> {
                     continue;
                 }
             }
-            *slot = Some((hash, metadata));
+            *slot = Some((hash, metadata, lib.clone()));
             ret = Some((lib, kind));
         }
 
@@ -814,19 +808,26 @@ fn get_metadata_section<'p>(
             let compressed_len = u32::from_be_bytes(len_bytes) as usize;
 
             // Header is okay -> inflate the actual metadata
-            let compressed_bytes = &buf[data_start..(data_start + compressed_len)];
-            debug!("inflating {} bytes of compressed metadata", compressed_bytes.len());
-            // Assume the decompressed data will be at least the size of the compressed data, so we
-            // don't have to grow the buffer as much.
-            let mut inflated = Vec::with_capacity(compressed_bytes.len());
-            FrameDecoder::new(compressed_bytes).read_to_end(&mut inflated).map_err(|_| {
-                MetadataError::LoadFailure(format!(
-                    "failed to decompress metadata: {}",
-                    filename.display()
-                ))
-            })?;
+            let compressed_bytes = buf.slice(|buf| &buf[data_start..(data_start + compressed_len)]);
+            if &compressed_bytes[..cmp::min(METADATA_HEADER.len(), compressed_bytes.len())]
+                == METADATA_HEADER
+            {
+                // The metadata was not actually compressed.
+                compressed_bytes
+            } else {
+                debug!("inflating {} bytes of compressed metadata", compressed_bytes.len());
+                // Assume the decompressed data will be at least the size of the compressed data, so we
+                // don't have to grow the buffer as much.
+                let mut inflated = Vec::with_capacity(compressed_bytes.len());
+                FrameDecoder::new(&*compressed_bytes).read_to_end(&mut inflated).map_err(|_| {
+                    MetadataError::LoadFailure(format!(
+                        "failed to decompress metadata: {}",
+                        filename.display()
+                    ))
+                })?;
 
-            slice_owned(inflated, Deref::deref)
+                slice_owned(inflated, Deref::deref)
+            }
         }
         CrateFlavor::Rmeta => {
             // mmap the file, because only a small fraction of it is read.
diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
index a8815ee0908..77c33336dff 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
@@ -301,6 +301,7 @@ 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 ac86110f2bd..46571e7796d 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -673,6 +673,7 @@ 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),
diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs
index 0bc16fc64ff..8bc2e0aa5a9 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, VariantIdx};
+use rustc_target::abi::{FieldIdx, ReferenceNichePolicy, VariantIdx};
 use rustc_target::spec::{PanicStrategy, TargetTriple};
 
 use std::marker::PhantomData;
@@ -251,6 +251,7 @@ 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/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs
index 372452ea29a..1bcef17d73b 100644
--- a/compiler/rustc_middle/src/mir/interpret/error.rs
+++ b/compiler/rustc_middle/src/mir/interpret/error.rs
@@ -388,7 +388,6 @@ 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,
diff --git a/compiler/rustc_middle/src/mir/interpret/pointer.rs b/compiler/rustc_middle/src/mir/interpret/pointer.rs
index 65d04919357..c8133bcc387 100644
--- a/compiler/rustc_middle/src/mir/interpret/pointer.rs
+++ b/compiler/rustc_middle/src/mir/interpret/pointer.rs
@@ -19,33 +19,19 @@ pub trait PointerArithmetic: HasDataLayout {
 
     #[inline(always)]
     fn max_size_of_val(&self) -> Size {
-        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()
+        Size::from_bytes(self.data_layout().target_isize_max())
     }
 
     #[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 > self.target_isize_max() {
+        if val > dl.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!(self.pointer_size().bits() < 64);
-            let max_usize_plus_1 = 1u128 << self.pointer_size().bits();
+            debug_assert!(dl.pointer_size.bits() < 64);
+            let max_usize_plus_1 = 1u128 << dl.pointer_size.bits();
             val - i64::try_from(max_usize_plus_1).unwrap()
         } else {
             val
@@ -58,7 +44,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.pointer_size().bits();
+        let max_ptr_plus_1 = 1u128 << self.data_layout().pointer_size.bits();
         (u64::try_from(val % max_ptr_plus_1).unwrap(), over || val >= max_ptr_plus_1)
     }
 
@@ -76,11 +62,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.target_isize_max())
+            (val, over || i > self.data_layout().target_isize_max())
         } else {
             let res = val.overflowing_sub(n);
             let (val, over) = self.truncate_to_ptr(res);
-            (val, over || i < self.target_isize_min())
+            (val, over || i < self.data_layout().target_isize_min())
         }
     }
 
diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs
index 2c481745d98..9bf02267005 100644
--- a/compiler/rustc_middle/src/query/erase.rs
+++ b/compiler/rustc_middle/src/query/erase.rs
@@ -111,6 +111,11 @@ 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>>()];
 }
@@ -291,6 +296,7 @@ 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 062adc9ec54..b5b00b7b640 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -898,6 +898,10 @@ rustc_queries! {
         desc { |tcx| "linting {}", describe_as_module(key, tcx) }
     }
 
+    query check_unused_traits(_: ()) -> () {
+        desc { "checking unused trait imports in crate" }
+    }
+
     /// Checks the attributes in the module.
     query check_mod_attrs(key: LocalDefId) -> () {
         desc { |tcx| "checking attributes in {}", describe_as_module(key, tcx) }
@@ -1390,6 +1394,18 @@ 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`
@@ -1465,6 +1481,11 @@ 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]`" }
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index 1a23fa80210..aa1f8e48b1b 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -569,6 +569,7 @@ pub struct GlobalCtxt<'tcx> {
 
     /// Caches the results of goal evaluation in the new solver.
     pub new_solver_evaluation_cache: solve::EvaluationCache<'tcx>,
+    pub new_solver_coherence_evaluation_cache: solve::EvaluationCache<'tcx>,
 
     /// Data layout specification for the current target.
     pub data_layout: TargetDataLayout,
@@ -680,10 +681,12 @@ impl<'tcx> TyCtxt<'tcx> {
         value.lift_to_tcx(self)
     }
 
-    /// Creates a type context and call the closure with a `TyCtxt` reference
-    /// to the context. The closure enforces that the type context and any interned
-    /// value (types, args, etc.) can only be used while `ty::tls` has a valid
-    /// reference to the context, to allow formatting values that need it.
+    /// Creates a type context. To use the context call `fn enter` which
+    /// provides a `TyCtxt`.
+    ///
+    /// By only providing the `TyCtxt` inside of the closure we enforce that the type
+    /// context and any interned alue (types, args, etc.) can only be used while `ty::tls`
+    /// has a valid reference to the context, to allow formatting values that need it.
     pub fn create_global_ctxt(
         s: &'tcx Session,
         lint_store: Lrc<dyn Any + sync::DynSend + sync::DynSync>,
@@ -721,6 +724,7 @@ impl<'tcx> TyCtxt<'tcx> {
             selection_cache: Default::default(),
             evaluation_cache: Default::default(),
             new_solver_evaluation_cache: Default::default(),
+            new_solver_coherence_evaluation_cache: Default::default(),
             data_layout,
             alloc_map: Lock::new(interpret::AllocMap::new()),
         }
diff --git a/compiler/rustc_middle/src/ty/fast_reject.rs b/compiler/rustc_middle/src/ty/fast_reject.rs
index e86ff4d26aa..668aa4521c1 100644
--- a/compiler/rustc_middle/src/ty/fast_reject.rs
+++ b/compiler/rustc_middle/src/ty/fast_reject.rs
@@ -6,35 +6,33 @@ use std::fmt::Debug;
 use std::hash::Hash;
 use std::iter;
 
-use self::SimplifiedType::*;
-
 /// See `simplify_type`.
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)]
 pub enum SimplifiedType {
-    BoolSimplifiedType,
-    CharSimplifiedType,
-    IntSimplifiedType(ty::IntTy),
-    UintSimplifiedType(ty::UintTy),
-    FloatSimplifiedType(ty::FloatTy),
-    AdtSimplifiedType(DefId),
-    ForeignSimplifiedType(DefId),
-    StrSimplifiedType,
-    ArraySimplifiedType,
-    SliceSimplifiedType,
-    RefSimplifiedType(Mutability),
-    PtrSimplifiedType(Mutability),
-    NeverSimplifiedType,
-    TupleSimplifiedType(usize),
+    Bool,
+    Char,
+    Int(ty::IntTy),
+    Uint(ty::UintTy),
+    Float(ty::FloatTy),
+    Adt(DefId),
+    Foreign(DefId),
+    Str,
+    Array,
+    Slice,
+    Ref(Mutability),
+    Ptr(Mutability),
+    Never,
+    Tuple(usize),
     /// A trait object, all of whose components are markers
     /// (e.g., `dyn Send + Sync`).
-    MarkerTraitObjectSimplifiedType,
-    TraitSimplifiedType(DefId),
-    ClosureSimplifiedType(DefId),
-    GeneratorSimplifiedType(DefId),
-    GeneratorWitnessSimplifiedType(usize),
-    GeneratorWitnessMIRSimplifiedType(DefId),
-    FunctionSimplifiedType(usize),
-    PlaceholderSimplifiedType,
+    MarkerTraitObject,
+    Trait(DefId),
+    Closure(DefId),
+    Generator(DefId),
+    GeneratorWitness(usize),
+    GeneratorWitnessMIR(DefId),
+    Function(usize),
+    Placeholder,
 }
 
 /// Generic parameters are pretty much just bound variables, e.g.
@@ -64,6 +62,9 @@ pub enum TreatParams {
     /// correct mode for *lookup*, as during candidate selection.
     ///
     /// N.B. during deep rejection, this acts identically to `ForLookup`.
+    ///
+    /// FIXME(-Ztrait-solver=next): Remove this variant and cleanup
+    /// the code.
     NextSolverLookup,
 }
 
@@ -110,34 +111,36 @@ pub fn simplify_type<'tcx>(
     treat_params: TreatParams,
 ) -> Option<SimplifiedType> {
     match *ty.kind() {
-        ty::Bool => Some(BoolSimplifiedType),
-        ty::Char => Some(CharSimplifiedType),
-        ty::Int(int_type) => Some(IntSimplifiedType(int_type)),
-        ty::Uint(uint_type) => Some(UintSimplifiedType(uint_type)),
-        ty::Float(float_type) => Some(FloatSimplifiedType(float_type)),
-        ty::Adt(def, _) => Some(AdtSimplifiedType(def.did())),
-        ty::Str => Some(StrSimplifiedType),
-        ty::Array(..) => Some(ArraySimplifiedType),
-        ty::Slice(..) => Some(SliceSimplifiedType),
-        ty::RawPtr(ptr) => Some(PtrSimplifiedType(ptr.mutbl)),
+        ty::Bool => Some(SimplifiedType::Bool),
+        ty::Char => Some(SimplifiedType::Char),
+        ty::Int(int_type) => Some(SimplifiedType::Int(int_type)),
+        ty::Uint(uint_type) => Some(SimplifiedType::Uint(uint_type)),
+        ty::Float(float_type) => Some(SimplifiedType::Float(float_type)),
+        ty::Adt(def, _) => Some(SimplifiedType::Adt(def.did())),
+        ty::Str => Some(SimplifiedType::Str),
+        ty::Array(..) => Some(SimplifiedType::Array),
+        ty::Slice(..) => Some(SimplifiedType::Slice),
+        ty::RawPtr(ptr) => Some(SimplifiedType::Ptr(ptr.mutbl)),
         ty::Dynamic(trait_info, ..) => match trait_info.principal_def_id() {
             Some(principal_def_id) if !tcx.trait_is_auto(principal_def_id) => {
-                Some(TraitSimplifiedType(principal_def_id))
+                Some(SimplifiedType::Trait(principal_def_id))
             }
-            _ => Some(MarkerTraitObjectSimplifiedType),
+            _ => Some(SimplifiedType::MarkerTraitObject),
         },
-        ty::Ref(_, _, mutbl) => Some(RefSimplifiedType(mutbl)),
-        ty::FnDef(def_id, _) | ty::Closure(def_id, _) => Some(ClosureSimplifiedType(def_id)),
-        ty::Generator(def_id, _, _) => Some(GeneratorSimplifiedType(def_id)),
-        ty::GeneratorWitness(tys) => Some(GeneratorWitnessSimplifiedType(tys.skip_binder().len())),
-        ty::GeneratorWitnessMIR(def_id, _) => Some(GeneratorWitnessMIRSimplifiedType(def_id)),
-        ty::Never => Some(NeverSimplifiedType),
-        ty::Tuple(tys) => Some(TupleSimplifiedType(tys.len())),
-        ty::FnPtr(f) => Some(FunctionSimplifiedType(f.skip_binder().inputs().len())),
-        ty::Placeholder(..) => Some(PlaceholderSimplifiedType),
+        ty::Ref(_, _, mutbl) => Some(SimplifiedType::Ref(mutbl)),
+        ty::FnDef(def_id, _) | ty::Closure(def_id, _) => Some(SimplifiedType::Closure(def_id)),
+        ty::Generator(def_id, _, _) => Some(SimplifiedType::Generator(def_id)),
+        ty::GeneratorWitness(tys) => {
+            Some(SimplifiedType::GeneratorWitness(tys.skip_binder().len()))
+        }
+        ty::GeneratorWitnessMIR(def_id, _) => Some(SimplifiedType::GeneratorWitnessMIR(def_id)),
+        ty::Never => Some(SimplifiedType::Never),
+        ty::Tuple(tys) => Some(SimplifiedType::Tuple(tys.len())),
+        ty::FnPtr(f) => Some(SimplifiedType::Function(f.skip_binder().inputs().len())),
+        ty::Placeholder(..) => Some(SimplifiedType::Placeholder),
         ty::Param(_) => match treat_params {
             TreatParams::ForLookup | TreatParams::NextSolverLookup => {
-                Some(PlaceholderSimplifiedType)
+                Some(SimplifiedType::Placeholder)
             }
             TreatParams::AsCandidateKey => None,
         },
@@ -147,11 +150,13 @@ pub fn simplify_type<'tcx>(
             //
             // We will have to be careful with lazy normalization here.
             // FIXME(lazy_normalization): This is probably not right...
-            TreatParams::ForLookup if !ty.has_non_region_infer() => Some(PlaceholderSimplifiedType),
-            TreatParams::NextSolverLookup => Some(PlaceholderSimplifiedType),
+            TreatParams::ForLookup if !ty.has_non_region_infer() => {
+                Some(SimplifiedType::Placeholder)
+            }
+            TreatParams::NextSolverLookup => Some(SimplifiedType::Placeholder),
             TreatParams::ForLookup | TreatParams::AsCandidateKey => None,
         },
-        ty::Foreign(def_id) => Some(ForeignSimplifiedType(def_id)),
+        ty::Foreign(def_id) => Some(SimplifiedType::Foreign(def_id)),
         ty::Bound(..) | ty::Infer(_) | ty::Error(_) => None,
     }
 }
@@ -159,12 +164,12 @@ pub fn simplify_type<'tcx>(
 impl SimplifiedType {
     pub fn def(self) -> Option<DefId> {
         match self {
-            AdtSimplifiedType(d)
-            | ForeignSimplifiedType(d)
-            | TraitSimplifiedType(d)
-            | ClosureSimplifiedType(d)
-            | GeneratorSimplifiedType(d)
-            | GeneratorWitnessMIRSimplifiedType(d) => Some(d),
+            SimplifiedType::Adt(d)
+            | SimplifiedType::Foreign(d)
+            | SimplifiedType::Trait(d)
+            | SimplifiedType::Closure(d)
+            | SimplifiedType::Generator(d)
+            | SimplifiedType::GeneratorWitnessMIR(d) => Some(d),
             _ => None,
         }
     }
diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs
index 62805d1e8b5..26137e86fa0 100644
--- a/compiler/rustc_middle/src/ty/layout.rs
+++ b/compiler/rustc_middle/src/ty/layout.rs
@@ -313,7 +313,16 @@ impl<'tcx> SizeSkeleton<'tcx> {
     ) -> Result<SizeSkeleton<'tcx>, &'tcx LayoutError<'tcx>> {
         debug_assert!(!ty.has_non_region_infer());
 
-        // First try computing a static layout.
+        // 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).
         let err = match tcx.layout_of(param_env.and(ty)) {
             Ok(layout) => {
                 return Ok(SizeSkeleton::Known(layout.size));
@@ -327,6 +336,7 @@ 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();
@@ -621,6 +631,219 @@ 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> {
@@ -673,6 +896,19 @@ 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 {}
@@ -969,6 +1205,9 @@ 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();
@@ -991,19 +1230,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 kind = match mt {
-                    hir::Mutability::Not => PointerKind::SharedRef {
+                let safe = match (assume_valid_ptr, mt) {
+                    (true, hir::Mutability::Not) => Some(PointerKind::SharedRef {
                         frozen: optimize && ty.is_freeze(tcx, cx.param_env()),
-                    },
-                    hir::Mutability::Mut => PointerKind::MutableRef {
+                    }),
+                    (true, hir::Mutability::Mut) => Some(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: Some(kind),
+                    safe,
                 })
             }
 
@@ -1012,19 +1251,21 @@ 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, .. },
+                        tag_encoding:
+                            TagEncoding::Niche {
+                                untagged_variant,
+                                niche_variants: ref variants,
+                                niche_start,
+                            },
                         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),
@@ -1050,9 +1291,12 @@ where
                             result = field.to_result().ok().and_then(|field| {
                                 if ptr_end <= field_start + field.size {
                                     // We found the right field, look inside it.
-                                    let field_info =
-                                        field.pointee_info_at(cx, offset - field_start);
-                                    field_info
+                                    Self::ty_and_layout_pointee_info_at(
+                                        field,
+                                        cx,
+                                        offset - field_start,
+                                        assume_valid_ptr,
+                                    )
                                 } else {
                                     None
                                 }
@@ -1067,7 +1311,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 def.is_box() && offset.bytes() == 0 {
+                        if assume_valid_ptr && 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_mir_dataflow/src/impls/liveness.rs b/compiler/rustc_mir_dataflow/src/impls/liveness.rs
index 9662c19777f..34e0834a68b 100644
--- a/compiler/rustc_mir_dataflow/src/impls/liveness.rs
+++ b/compiler/rustc_mir_dataflow/src/impls/liveness.rs
@@ -88,7 +88,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeLiveLocals {
     }
 }
 
-struct TransferFunction<'a, T>(&'a mut T);
+pub struct TransferFunction<'a, T>(pub &'a mut T);
 
 impl<'tcx, T> Visitor<'tcx> for TransferFunction<'_, T>
 where
diff --git a/compiler/rustc_mir_dataflow/src/impls/mod.rs b/compiler/rustc_mir_dataflow/src/impls/mod.rs
index 633b99a332b..7ddd01e34aa 100644
--- a/compiler/rustc_mir_dataflow/src/impls/mod.rs
+++ b/compiler/rustc_mir_dataflow/src/impls/mod.rs
@@ -26,6 +26,7 @@ pub use self::borrowed_locals::borrowed_locals;
 pub use self::borrowed_locals::MaybeBorrowedLocals;
 pub use self::liveness::MaybeLiveLocals;
 pub use self::liveness::MaybeTransitiveLiveLocals;
+pub use self::liveness::TransferFunction as LivenessTransferFunction;
 pub use self::storage_liveness::{MaybeRequiresStorage, MaybeStorageDead, MaybeStorageLive};
 
 /// `MaybeInitializedPlaces` tracks all places that might be
diff --git a/compiler/rustc_mir_transform/src/const_prop.rs b/compiler/rustc_mir_transform/src/const_prop.rs
index 6437a227d47..7529ed8186b 100644
--- a/compiler/rustc_mir_transform/src/const_prop.rs
+++ b/compiler/rustc_mir_transform/src/const_prop.rs
@@ -14,8 +14,7 @@ use rustc_middle::mir::visit::{
 };
 use rustc_middle::mir::*;
 use rustc_middle::ty::layout::{LayoutError, LayoutOf, LayoutOfHelpers, TyAndLayout};
-use rustc_middle::ty::GenericArgs;
-use rustc_middle::ty::{self, ConstKind, Instance, ParamEnv, Ty, TyCtxt, TypeVisitableExt};
+use rustc_middle::ty::{self, GenericArgs, Instance, ParamEnv, Ty, TyCtxt, TypeVisitableExt};
 use rustc_span::{def_id::DefId, Span, DUMMY_SP};
 use rustc_target::abi::{self, Align, HasDataLayout, Size, TargetDataLayout};
 use rustc_target::spec::abi::Abi as CallAbi;
@@ -407,51 +406,9 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
         ecx.machine.written_only_inside_own_block_locals.remove(&local);
     }
 
-    /// Returns the value, if any, of evaluating `c`.
-    fn eval_constant(&mut self, c: &Constant<'tcx>) -> Option<OpTy<'tcx>> {
-        // FIXME we need to revisit this for #67176
-        if c.has_param() {
-            return None;
-        }
-
-        // No span, we don't want errors to be shown.
-        self.ecx.eval_mir_constant(&c.literal, None, None).ok()
-    }
-
-    /// Returns the value, if any, of evaluating `place`.
-    fn eval_place(&mut self, place: Place<'tcx>) -> Option<OpTy<'tcx>> {
-        trace!("eval_place(place={:?})", place);
-        self.ecx.eval_place_to_op(place, None).ok()
-    }
-
-    /// Returns the value, if any, of evaluating `op`. Calls upon `eval_constant`
-    /// or `eval_place`, depending on the variant of `Operand` used.
-    fn eval_operand(&mut self, op: &Operand<'tcx>) -> Option<OpTy<'tcx>> {
-        match *op {
-            Operand::Constant(ref c) => self.eval_constant(c),
-            Operand::Move(place) | Operand::Copy(place) => self.eval_place(place),
-        }
-    }
-
     fn propagate_operand(&mut self, operand: &mut Operand<'tcx>) {
-        match *operand {
-            Operand::Copy(l) | Operand::Move(l) => {
-                if let Some(value) = self.get_const(l) && self.should_const_prop(&value) {
-                    // FIXME(felix91gr): this code only handles `Scalar` cases.
-                    // For now, we're not handling `ScalarPair` cases because
-                    // doing so here would require a lot of code duplication.
-                    // We should hopefully generalize `Operand` handling into a fn,
-                    // and use it to do const-prop here and everywhere else
-                    // where it makes sense.
-                    if let interpret::Operand::Immediate(interpret::Immediate::Scalar(
-                        scalar,
-                    )) = *value
-                    {
-                        *operand = self.operand_from_scalar(scalar, value.layout.ty);
-                    }
-                }
-            }
-            Operand::Constant(_) => (),
+        if let Some(place) = operand.place() && let Some(op) = self.replace_with_const(place) {
+            *operand = op;
         }
     }
 
@@ -579,93 +536,45 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
         }))
     }
 
-    fn replace_with_const(&mut self, place: Place<'tcx>, rval: &mut Rvalue<'tcx>) {
+    fn replace_with_const(&mut self, place: Place<'tcx>) -> Option<Operand<'tcx>> {
         // This will return None if the above `const_prop` invocation only "wrote" a
         // type whose creation requires no write. E.g. a generator whose initial state
         // consists solely of uninitialized memory (so it doesn't capture any locals).
-        let Some(ref value) = self.get_const(place) else { return };
-        if !self.should_const_prop(value) {
-            return;
-        }
-        trace!("replacing {:?}={:?} with {:?}", place, rval, value);
-
-        if let Rvalue::Use(Operand::Constant(c)) = rval {
-            match c.literal {
-                ConstantKind::Ty(c) if matches!(c.kind(), ConstKind::Unevaluated(..)) => {}
-                _ => {
-                    trace!("skipping replace of Rvalue::Use({:?} because it is already a const", c);
-                    return;
-                }
-            }
+        let value = self.get_const(place)?;
+        if !self.tcx.consider_optimizing(|| format!("ConstantPropagation - {value:?}")) {
+            return None;
         }
+        trace!("replacing {:?} with {:?}", place, value);
 
-        trace!("attempting to replace {:?} with {:?}", rval, value);
         // FIXME> figure out what to do when read_immediate_raw fails
-        let imm = self.ecx.read_immediate_raw(value).ok();
+        let imm = self.ecx.read_immediate_raw(&value).ok()?;
 
-        if let Some(Right(imm)) = imm {
-            match *imm {
-                interpret::Immediate::Scalar(scalar) => {
-                    *rval = Rvalue::Use(self.operand_from_scalar(scalar, value.layout.ty));
-                }
-                Immediate::ScalarPair(..) => {
-                    // Found a value represented as a pair. For now only do const-prop if the type
-                    // of `rvalue` is also a tuple with two scalars.
-                    // FIXME: enable the general case stated above ^.
-                    let ty = value.layout.ty;
-                    // Only do it for tuples
-                    if let ty::Tuple(types) = ty.kind() {
-                        // Only do it if tuple is also a pair with two scalars
-                        if let [ty1, ty2] = types[..] {
-                            let ty_is_scalar = |ty| {
-                                self.ecx.layout_of(ty).ok().map(|layout| layout.abi.is_scalar())
-                                    == Some(true)
-                            };
-                            let alloc = if ty_is_scalar(ty1) && ty_is_scalar(ty2) {
-                                let alloc = self
-                                    .ecx
-                                    .intern_with_temp_alloc(value.layout, |ecx, dest| {
-                                        ecx.write_immediate(*imm, dest)
-                                    })
-                                    .unwrap();
-                                Some(alloc)
-                            } else {
-                                None
-                            };
-
-                            if let Some(alloc) = alloc {
-                                // Assign entire constant in a single statement.
-                                // We can't use aggregates, as we run after the aggregate-lowering `MirPhase`.
-                                let const_val = ConstValue::ByRef { alloc, offset: Size::ZERO };
-                                let literal = ConstantKind::Val(const_val, ty);
-                                *rval = Rvalue::Use(Operand::Constant(Box::new(Constant {
-                                    span: DUMMY_SP,
-                                    user_ty: None,
-                                    literal,
-                                })));
-                            }
-                        }
-                    }
-                }
-                // Scalars or scalar pairs that contain undef values are assumed to not have
-                // successfully evaluated and are thus not propagated.
-                _ => {}
+        let Right(imm) = imm else { return None };
+        match *imm {
+            Immediate::Scalar(scalar) if scalar.try_to_int().is_ok() => {
+                Some(self.operand_from_scalar(scalar, value.layout.ty))
             }
-        }
-    }
-
-    /// Returns `true` if and only if this `op` should be const-propagated into.
-    fn should_const_prop(&mut self, op: &OpTy<'tcx>) -> bool {
-        if !self.tcx.consider_optimizing(|| format!("ConstantPropagation - OpTy: {:?}", op)) {
-            return false;
-        }
-
-        match **op {
-            interpret::Operand::Immediate(Immediate::Scalar(s)) => s.try_to_int().is_ok(),
-            interpret::Operand::Immediate(Immediate::ScalarPair(l, r)) => {
-                l.try_to_int().is_ok() && r.try_to_int().is_ok()
+            Immediate::ScalarPair(l, r) if l.try_to_int().is_ok() && r.try_to_int().is_ok() => {
+                let alloc = self
+                    .ecx
+                    .intern_with_temp_alloc(value.layout, |ecx, dest| {
+                        ecx.write_immediate(*imm, dest)
+                    })
+                    .ok()?;
+
+                let literal = ConstantKind::Val(
+                    ConstValue::ByRef { alloc, offset: Size::ZERO },
+                    value.layout.ty,
+                );
+                Some(Operand::Constant(Box::new(Constant {
+                    span: DUMMY_SP,
+                    user_ty: None,
+                    literal,
+                })))
             }
-            _ => false,
+            // Scalars or scalar pairs that contain undef values are assumed to not have
+            // successfully evaluated and are thus not propagated.
+            _ => None,
         }
     }
 
@@ -810,12 +719,7 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> {
 
     fn visit_operand(&mut self, operand: &mut Operand<'tcx>, location: Location) {
         self.super_operand(operand, location);
-
-        // Only const prop copies and moves on `mir_opt_level=3` as doing so
-        // currently slightly increases compile time in some cases.
-        if self.tcx.sess.mir_opt_level() >= 3 {
-            self.propagate_operand(operand)
-        }
+        self.propagate_operand(operand)
     }
 
     fn process_projection_elem(
@@ -825,8 +729,7 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> {
     ) -> Option<PlaceElem<'tcx>> {
         if let PlaceElem::Index(local) = elem
             && let Some(value) = self.get_const(local.into())
-            && self.should_const_prop(&value)
-            && let interpret::Operand::Immediate(interpret::Immediate::Scalar(scalar)) = *value
+            && let interpret::Operand::Immediate(Immediate::Scalar(scalar)) = *value
             && let Ok(offset) = scalar.to_target_usize(&self.tcx)
             && let Some(min_length) = offset.checked_add(1)
         {
@@ -852,7 +755,14 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> {
             ConstPropMode::NoPropagation => self.ensure_not_propagated(place.local),
             ConstPropMode::OnlyInsideOwnBlock | ConstPropMode::FullConstProp => {
                 if let Some(()) = self.eval_rvalue_with_identities(rvalue, *place) {
-                    self.replace_with_const(*place, rvalue);
+                    // If this was already an evaluated constant, keep it.
+                    if let Rvalue::Use(Operand::Constant(c)) = rvalue
+                        && let ConstantKind::Val(..) = c.literal
+                    {
+                        trace!("skipping replace of Rvalue::Use({:?} because it is already a const", c);
+                    } else if let Some(operand) = self.replace_with_const(*place) {
+                        *rvalue = Rvalue::Use(operand);
+                    }
                 } else {
                     // Const prop failed, so erase the destination, ensuring that whatever happens
                     // from here on, does not know about the previous value.
@@ -919,45 +829,6 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> {
         }
     }
 
-    fn visit_terminator(&mut self, terminator: &mut Terminator<'tcx>, location: Location) {
-        self.super_terminator(terminator, location);
-
-        match &mut terminator.kind {
-            TerminatorKind::Assert { expected, ref mut cond, .. } => {
-                if let Some(ref value) = self.eval_operand(&cond)
-                    && let Ok(value_const) = self.ecx.read_scalar(&value)
-                    && self.should_const_prop(value)
-                {
-                    trace!("assertion on {:?} should be {:?}", value, expected);
-                    *cond = self.operand_from_scalar(value_const, self.tcx.types.bool);
-                }
-            }
-            TerminatorKind::SwitchInt { ref mut discr, .. } => {
-                // FIXME: This is currently redundant with `visit_operand`, but sadly
-                // always visiting operands currently causes a perf regression in LLVM codegen, so
-                // `visit_operand` currently only runs for propagates places for `mir_opt_level=4`.
-                self.propagate_operand(discr)
-            }
-            // None of these have Operands to const-propagate.
-            TerminatorKind::Goto { .. }
-            | TerminatorKind::Resume
-            | TerminatorKind::Terminate
-            | TerminatorKind::Return
-            | TerminatorKind::Unreachable
-            | TerminatorKind::Drop { .. }
-            | TerminatorKind::Yield { .. }
-            | TerminatorKind::GeneratorDrop
-            | TerminatorKind::FalseEdge { .. }
-            | TerminatorKind::FalseUnwind { .. }
-            | TerminatorKind::InlineAsm { .. } => {}
-            // Every argument in our function calls have already been propagated in `visit_operand`.
-            //
-            // NOTE: because LLVM codegen gives slight performance regressions with it, so this is
-            // gated on `mir_opt_level=3`.
-            TerminatorKind::Call { .. } => {}
-        }
-    }
-
     fn visit_basic_block_data(&mut self, block: BasicBlock, data: &mut BasicBlockData<'tcx>) {
         self.super_basic_block_data(block, data);
 
diff --git a/compiler/rustc_mir_transform/src/dead_store_elimination.rs b/compiler/rustc_mir_transform/src/dead_store_elimination.rs
index 7bc5183a00a..3f988930b5e 100644
--- a/compiler/rustc_mir_transform/src/dead_store_elimination.rs
+++ b/compiler/rustc_mir_transform/src/dead_store_elimination.rs
@@ -13,9 +13,12 @@
 //!
 
 use rustc_index::bit_set::BitSet;
+use rustc_middle::mir::visit::Visitor;
 use rustc_middle::mir::*;
 use rustc_middle::ty::TyCtxt;
-use rustc_mir_dataflow::impls::{borrowed_locals, MaybeTransitiveLiveLocals};
+use rustc_mir_dataflow::impls::{
+    borrowed_locals, LivenessTransferFunction, MaybeTransitiveLiveLocals,
+};
 use rustc_mir_dataflow::Analysis;
 
 /// Performs the optimization on the body
@@ -28,8 +31,33 @@ pub fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, borrowed: &BitS
         .iterate_to_fixpoint()
         .into_results_cursor(body);
 
+    // For blocks with a call terminator, if an argument copy can be turned into a move,
+    // record it as (block, argument index).
+    let mut call_operands_to_move = Vec::new();
     let mut patch = Vec::new();
+
     for (bb, bb_data) in traversal::preorder(body) {
+        if let TerminatorKind::Call { ref args, .. } = bb_data.terminator().kind {
+            let loc = Location { block: bb, statement_index: bb_data.statements.len() };
+
+            // Position ourselves between the evaluation of `args` and the write to `destination`.
+            live.seek_to_block_end(bb);
+            let mut state = live.get().clone();
+
+            for (index, arg) in args.iter().enumerate().rev() {
+                if let Operand::Copy(place) = *arg
+                    && !place.is_indirect()
+                    && !borrowed.contains(place.local)
+                    && !state.contains(place.local)
+                {
+                    call_operands_to_move.push((bb, index));
+                }
+
+                // Account that `arg` is read from, so we don't promote another argument to a move.
+                LivenessTransferFunction(&mut state).visit_operand(arg, loc);
+            }
+        }
+
         for (statement_index, statement) in bb_data.statements.iter().enumerate().rev() {
             let loc = Location { block: bb, statement_index };
             if let StatementKind::Assign(assign) = &statement.kind {
@@ -64,7 +92,7 @@ pub fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, borrowed: &BitS
         }
     }
 
-    if patch.is_empty() {
+    if patch.is_empty() && call_operands_to_move.is_empty() {
         return;
     }
 
@@ -72,6 +100,14 @@ pub fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, borrowed: &BitS
     for Location { block, statement_index } in patch {
         bbs[block].statements[statement_index].make_nop();
     }
+    for (block, argument_index) in call_operands_to_move {
+        let TerminatorKind::Call { ref mut args, .. } = bbs[block].terminator_mut().kind else {
+            bug!()
+        };
+        let arg = &mut args[argument_index];
+        let Operand::Copy(place) = *arg else { bug!() };
+        *arg = Operand::Move(place);
+    }
 
     crate::simplify::simplify_locals(body, tcx)
 }
diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs
index 7860cf76247..e08edfe143a 100644
--- a/compiler/rustc_mir_transform/src/inline.rs
+++ b/compiler/rustc_mir_transform/src/inline.rs
@@ -440,6 +440,10 @@ impl<'tcx> Inliner<'tcx> {
             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());
@@ -847,7 +851,16 @@ impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> {
         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| {
-                if !util::is_equal_up_to_subtyping(this.tcx, this.param_env, ty, 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;
diff --git a/compiler/rustc_query_system/src/query/job.rs b/compiler/rustc_query_system/src/query/job.rs
index d2140161f1d..a53d1fcc69e 100644
--- a/compiler/rustc_query_system/src/query/job.rs
+++ b/compiler/rustc_query_system/src/query/job.rs
@@ -176,7 +176,8 @@ impl QueryJobId {
         while let Some(id) = current_id {
             let info = query_map.get(&id).unwrap();
             // FIXME: This string comparison should probably not be done.
-            if format!("{:?}", info.query.dep_kind) == "layout_of" {
+            let query_name = format!("{:?}", info.query.dep_kind);
+            if query_name == "layout_of" || query_name == "naive_layout_of" {
                 depth += 1;
                 last_layout = Some((info.clone(), depth));
             }
diff --git a/compiler/rustc_session/messages.ftl b/compiler/rustc_session/messages.ftl
index 4897bd8d5da..ee24c6d902f 100644
--- a/compiler/rustc_session/messages.ftl
+++ b/compiler/rustc_session/messages.ftl
@@ -26,6 +26,8 @@ session_feature_gate_error = {$explain}
 
 session_file_is_not_writeable = output file {$file} is not writeable -- check its permissions
 
+session_file_write_fail = failed to write `{$path}` due to error `{$err}`
+
 session_hexadecimal_float_literal_not_supported = hexadecimal float literal is not supported
 
 session_incompatible_linker_flavor = linker flavor `{$flavor}` is incompatible with the current target
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index 593983f117d..1766e97b67d 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -3,6 +3,7 @@
 
 pub use crate::options::*;
 
+use crate::errors::FileWriteFail;
 use crate::search_paths::SearchPath;
 use crate::utils::{CanonicalizedPath, NativeLib, NativeLibKind};
 use crate::{lint, HashStableContext};
@@ -31,6 +32,7 @@ use std::collections::btree_map::{
 use std::collections::{BTreeMap, BTreeSet};
 use std::ffi::OsStr;
 use std::fmt;
+use std::fs;
 use std::hash::Hash;
 use std::iter;
 use std::path::{Path, PathBuf};
@@ -710,8 +712,14 @@ impl ExternEntry {
     }
 }
 
+#[derive(Clone, PartialEq, Debug)]
+pub struct PrintRequest {
+    pub kind: PrintKind,
+    pub out: OutFileName,
+}
+
 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
-pub enum PrintRequest {
+pub enum PrintKind {
     FileNames,
     Sysroot,
     TargetLibdir,
@@ -855,6 +863,17 @@ impl OutFileName {
             OutFileName::Stdout => outputs.temp_path(flavor, codegen_unit_name),
         }
     }
+
+    pub fn overwrite(&self, content: &str, sess: &Session) {
+        match self {
+            OutFileName::Stdout => print!("{content}"),
+            OutFileName::Real(path) => {
+                if let Err(e) = fs::write(path, content) {
+                    sess.emit_fatal(FileWriteFail { path, err: e.to_string() });
+                }
+            }
+        }
+    }
 }
 
 #[derive(Clone, Hash, Debug, HashStable_Generic)]
@@ -2005,13 +2024,7 @@ fn parse_output_types(
     if !unstable_opts.parse_only {
         for list in matches.opt_strs("emit") {
             for output_type in list.split(',') {
-                let (shorthand, path) = match output_type.split_once('=') {
-                    None => (output_type, None),
-                    Some((shorthand, "-")) => (shorthand, Some(OutFileName::Stdout)),
-                    Some((shorthand, path)) => {
-                        (shorthand, Some(OutFileName::Real(PathBuf::from(path))))
-                    }
-                };
+                let (shorthand, path) = split_out_file_name(output_type);
                 let output_type = OutputType::from_shorthand(shorthand).unwrap_or_else(|| {
                     handler.early_error(format!(
                         "unknown emission type: `{shorthand}` - expected one of: {display}",
@@ -2028,6 +2041,14 @@ fn parse_output_types(
     OutputTypes(output_types)
 }
 
+fn split_out_file_name(arg: &str) -> (&str, Option<OutFileName>) {
+    match arg.split_once('=') {
+        None => (arg, None),
+        Some((kind, "-")) => (kind, Some(OutFileName::Stdout)),
+        Some((kind, path)) => (kind, Some(OutFileName::Real(PathBuf::from(path)))),
+    }
+}
+
 fn should_override_cgus_and_disable_thinlto(
     handler: &EarlyErrorHandler,
     output_types: &OutputTypes,
@@ -2091,41 +2112,49 @@ fn collect_print_requests(
 ) -> Vec<PrintRequest> {
     let mut prints = Vec::<PrintRequest>::new();
     if cg.target_cpu.as_ref().is_some_and(|s| s == "help") {
-        prints.push(PrintRequest::TargetCPUs);
+        prints.push(PrintRequest { kind: PrintKind::TargetCPUs, out: OutFileName::Stdout });
         cg.target_cpu = None;
     };
     if cg.target_feature == "help" {
-        prints.push(PrintRequest::TargetFeatures);
+        prints.push(PrintRequest { kind: PrintKind::TargetFeatures, out: OutFileName::Stdout });
         cg.target_feature = String::new();
     }
 
-    const PRINT_REQUESTS: &[(&str, PrintRequest)] = &[
-        ("crate-name", PrintRequest::CrateName),
-        ("file-names", PrintRequest::FileNames),
-        ("sysroot", PrintRequest::Sysroot),
-        ("target-libdir", PrintRequest::TargetLibdir),
-        ("cfg", PrintRequest::Cfg),
-        ("calling-conventions", PrintRequest::CallingConventions),
-        ("target-list", PrintRequest::TargetList),
-        ("target-cpus", PrintRequest::TargetCPUs),
-        ("target-features", PrintRequest::TargetFeatures),
-        ("relocation-models", PrintRequest::RelocationModels),
-        ("code-models", PrintRequest::CodeModels),
-        ("tls-models", PrintRequest::TlsModels),
-        ("native-static-libs", PrintRequest::NativeStaticLibs),
-        ("stack-protector-strategies", PrintRequest::StackProtectorStrategies),
-        ("target-spec-json", PrintRequest::TargetSpec),
-        ("all-target-specs-json", PrintRequest::AllTargetSpecs),
-        ("link-args", PrintRequest::LinkArgs),
-        ("split-debuginfo", PrintRequest::SplitDebuginfo),
-        ("deployment-target", PrintRequest::DeploymentTarget),
+    const PRINT_KINDS: &[(&str, PrintKind)] = &[
+        ("crate-name", PrintKind::CrateName),
+        ("file-names", PrintKind::FileNames),
+        ("sysroot", PrintKind::Sysroot),
+        ("target-libdir", PrintKind::TargetLibdir),
+        ("cfg", PrintKind::Cfg),
+        ("calling-conventions", PrintKind::CallingConventions),
+        ("target-list", PrintKind::TargetList),
+        ("target-cpus", PrintKind::TargetCPUs),
+        ("target-features", PrintKind::TargetFeatures),
+        ("relocation-models", PrintKind::RelocationModels),
+        ("code-models", PrintKind::CodeModels),
+        ("tls-models", PrintKind::TlsModels),
+        ("native-static-libs", PrintKind::NativeStaticLibs),
+        ("stack-protector-strategies", PrintKind::StackProtectorStrategies),
+        ("target-spec-json", PrintKind::TargetSpec),
+        ("all-target-specs-json", PrintKind::AllTargetSpecs),
+        ("link-args", PrintKind::LinkArgs),
+        ("split-debuginfo", PrintKind::SplitDebuginfo),
+        ("deployment-target", PrintKind::DeploymentTarget),
     ];
 
+    // We disallow reusing the same path in multiple prints, such as `--print
+    // cfg=output.txt --print link-args=output.txt`, because outputs are printed
+    // by disparate pieces of the compiler, and keeping track of which files
+    // need to be overwritten vs appended to is annoying.
+    let mut printed_paths = FxHashSet::default();
+
     prints.extend(matches.opt_strs("print").into_iter().map(|req| {
-        match PRINT_REQUESTS.iter().find(|&&(name, _)| name == req) {
-            Some((_, PrintRequest::TargetSpec)) => {
+        let (req, out) = split_out_file_name(&req);
+
+        let kind = match PRINT_KINDS.iter().find(|&&(name, _)| name == req) {
+            Some((_, PrintKind::TargetSpec)) => {
                 if unstable_opts.unstable_options {
-                    PrintRequest::TargetSpec
+                    PrintKind::TargetSpec
                 } else {
                     handler.early_error(
                         "the `-Z unstable-options` flag must also be passed to \
@@ -2133,9 +2162,9 @@ fn collect_print_requests(
                     );
                 }
             }
-            Some((_, PrintRequest::AllTargetSpecs)) => {
+            Some((_, PrintKind::AllTargetSpecs)) => {
                 if unstable_opts.unstable_options {
-                    PrintRequest::AllTargetSpecs
+                    PrintKind::AllTargetSpecs
                 } else {
                     handler.early_error(
                         "the `-Z unstable-options` flag must also be passed to \
@@ -2143,16 +2172,28 @@ fn collect_print_requests(
                     );
                 }
             }
-            Some(&(_, print_request)) => print_request,
+            Some(&(_, print_kind)) => print_kind,
             None => {
                 let prints =
-                    PRINT_REQUESTS.iter().map(|(name, _)| format!("`{name}`")).collect::<Vec<_>>();
+                    PRINT_KINDS.iter().map(|(name, _)| format!("`{name}`")).collect::<Vec<_>>();
                 let prints = prints.join(", ");
                 handler.early_error(format!(
                     "unknown print request `{req}`. Valid print requests are: {prints}"
                 ));
             }
+        };
+
+        let out = out.unwrap_or(OutFileName::Stdout);
+        if let OutFileName::Real(path) = &out {
+            if !printed_paths.insert(path.clone()) {
+                handler.early_error(format!(
+                    "cannot print multiple outputs to the same path: {}",
+                    path.display(),
+                ));
+            }
         }
+
+        PrintRequest { kind, out }
     }));
 
     prints
@@ -3076,6 +3117,7 @@ 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,
@@ -3171,6 +3213,7 @@ 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 4a3e668da11..dd15ad45145 100644
--- a/compiler/rustc_session/src/errors.rs
+++ b/compiler/rustc_session/src/errors.rs
@@ -164,6 +164,13 @@ pub struct FileIsNotWriteable<'a> {
 }
 
 #[derive(Diagnostic)]
+#[diag(session_file_write_fail)]
+pub(crate) struct FileWriteFail<'a> {
+    pub path: &'a std::path::Path,
+    pub err: String,
+}
+
+#[derive(Diagnostic)]
 #[diag(session_crate_name_does_not_match)]
 pub struct CrateNameDoesNotMatch {
     #[primary_span]
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index 87d67c099ce..0c66121c72f 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -6,6 +6,7 @@ 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,
@@ -421,6 +422,8 @@ 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 {
@@ -1253,6 +1256,31 @@ 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! {
@@ -1668,6 +1696,9 @@ options! {
         "use a more precise version of drop elaboration for matches on enums (default: yes). \
         This results in better codegen, but has caused miscompilations on some tier 2 platforms. \
         See #77382 and #74551."),
+    #[rustc_lint_opt_deny_field_access("use `Session::print_codegen_stats` instead of this field")]
+    print_codegen_stats: bool = (false, parse_bool, [UNTRACKED],
+        "print codegen statistics (default: no)"),
     print_fuel: Option<String> = (None, parse_opt_string, [TRACKED],
         "make rustc print the total optimization fuel used by a crate"),
     print_llvm_passes: bool = (false, parse_bool, [UNTRACKED],
@@ -1698,6 +1729,8 @@ 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],
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index 146bb11bd3a..3c8be439111 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -1057,6 +1057,10 @@ impl Session {
         self.opts.unstable_opts.verbose
     }
 
+    pub fn print_llvm_stats(&self) -> bool {
+        self.opts.unstable_opts.print_codegen_stats
+    }
+
     pub fn verify_llvm_ir(&self) -> bool {
         self.opts.unstable_opts.verify_llvm_ir || option_env!("RUSTC_VERIFY_LLVM_IR").is_some()
     }
@@ -1421,7 +1425,7 @@ pub fn build_session(
     let loader = file_loader.unwrap_or_else(|| Box::new(RealFileLoader));
     let hash_kind = sopts.unstable_opts.src_hash_algorithm.unwrap_or_else(|| {
         if target_cfg.is_like_msvc {
-            SourceFileHashAlgorithm::Sha1
+            SourceFileHashAlgorithm::Sha256
         } else {
             SourceFileHashAlgorithm::Md5
         }
diff --git a/compiler/rustc_smir/src/rustc_internal/mod.rs b/compiler/rustc_smir/src/rustc_internal/mod.rs
index 527d5220564..e0cf698acd7 100644
--- a/compiler/rustc_smir/src/rustc_internal/mod.rs
+++ b/compiler/rustc_smir/src/rustc_internal/mod.rs
@@ -31,6 +31,34 @@ pub fn adt_def(did: DefId) -> stable_mir::ty::AdtDef {
     with_tables(|t| t.adt_def(did))
 }
 
+pub fn foreign_def(did: DefId) -> stable_mir::ty::ForeignDef {
+    with_tables(|t| t.foreign_def(did))
+}
+
+pub fn fn_def(did: DefId) -> stable_mir::ty::FnDef {
+    with_tables(|t| t.fn_def(did))
+}
+
+pub fn closure_def(did: DefId) -> stable_mir::ty::ClosureDef {
+    with_tables(|t| t.closure_def(did))
+}
+
+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))
+}
+
+pub fn br_named_def(did: DefId) -> stable_mir::ty::BrNamedDef {
+    with_tables(|t| t.br_named_def(did))
+}
+
 impl<'tcx> Tables<'tcx> {
     pub fn item_def_id(&self, item: &stable_mir::CrateItem) -> DefId {
         self.def_ids[item.0]
@@ -44,6 +72,34 @@ impl<'tcx> Tables<'tcx> {
         stable_mir::ty::AdtDef(self.create_def_id(did))
     }
 
+    pub fn foreign_def(&mut self, did: DefId) -> stable_mir::ty::ForeignDef {
+        stable_mir::ty::ForeignDef(self.create_def_id(did))
+    }
+
+    pub fn fn_def(&mut self, did: DefId) -> stable_mir::ty::FnDef {
+        stable_mir::ty::FnDef(self.create_def_id(did))
+    }
+
+    pub fn closure_def(&mut self, did: DefId) -> stable_mir::ty::ClosureDef {
+        stable_mir::ty::ClosureDef(self.create_def_id(did))
+    }
+
+    pub fn generator_def(&mut self, did: DefId) -> stable_mir::ty::GeneratorDef {
+        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))
+    }
+
+    pub fn br_named_def(&mut self, did: DefId) -> stable_mir::ty::BrNamedDef {
+        stable_mir::ty::BrNamedDef(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 0e5de1e74d3..97bde5c3c19 100644
--- a/compiler/rustc_smir/src/rustc_smir/mod.rs
+++ b/compiler/rustc_smir/src/rustc_smir/mod.rs
@@ -8,8 +8,9 @@
 //! For now, we are developing everything inside `rustc`, thus, we keep this module private.
 
 use crate::rustc_internal::{self, opaque};
-use crate::stable_mir::ty::{AdtSubsts, FloatTy, GenericArgKind, IntTy, RigidTy, TyKind, UintTy};
+use crate::stable_mir::ty::{FloatTy, IntTy, Movability, RigidTy, TyKind, UintTy};
 use crate::stable_mir::{self, Context};
+use rustc_hir as hir;
 use rustc_middle::mir;
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_span::def_id::{CrateNum, DefId, LOCAL_CRATE};
@@ -46,8 +47,12 @@ impl<'tcx> Context for Tables<'tcx> {
                 .basic_blocks
                 .iter()
                 .map(|block| stable_mir::mir::BasicBlock {
-                    terminator: block.terminator().stable(),
-                    statements: block.statements.iter().map(mir::Statement::stable).collect(),
+                    terminator: block.terminator().stable(self),
+                    statements: block
+                        .statements
+                        .iter()
+                        .map(|statement| statement.stable(self))
+                        .collect(),
                 })
                 .collect(),
             locals: mir.local_decls.iter().map(|decl| self.intern_ty(decl.ty)).collect(),
@@ -59,7 +64,8 @@ impl<'tcx> Context for Tables<'tcx> {
     }
 
     fn ty_kind(&mut self, ty: crate::stable_mir::ty::Ty) -> TyKind {
-        self.rustc_ty_to_ty(self.types[ty.0])
+        let ty = self.types[ty.0];
+        ty.stable(self)
     }
 }
 
@@ -70,83 +76,6 @@ pub struct Tables<'tcx> {
 }
 
 impl<'tcx> Tables<'tcx> {
-    fn rustc_ty_to_ty(&mut self, ty: Ty<'tcx>) -> TyKind {
-        match ty.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::Adt(adt_def, substs) => TyKind::RigidTy(RigidTy::Adt(
-                rustc_internal::adt_def(adt_def.did()),
-                AdtSubsts(
-                    substs
-                        .iter()
-                        .map(|arg| match arg.unpack() {
-                            ty::GenericArgKind::Lifetime(region) => {
-                                GenericArgKind::Lifetime(opaque(&region))
-                            }
-                            ty::GenericArgKind::Type(ty) => {
-                                GenericArgKind::Type(self.intern_ty(ty))
-                            }
-                            ty::GenericArgKind::Const(const_) => {
-                                GenericArgKind::Const(opaque(&const_))
-                            }
-                        })
-                        .collect(),
-                ),
-            )),
-            ty::Foreign(_) => todo!(),
-            ty::Str => TyKind::RigidTy(RigidTy::Str),
-            ty::Array(ty, constant) => {
-                TyKind::RigidTy(RigidTy::Array(self.intern_ty(*ty), opaque(constant)))
-            }
-            ty::Slice(ty) => TyKind::RigidTy(RigidTy::Slice(self.intern_ty(*ty))),
-            ty::RawPtr(ty::TypeAndMut { ty, mutbl }) => {
-                TyKind::RigidTy(RigidTy::RawPtr(self.intern_ty(*ty), mutbl.stable()))
-            }
-            ty::Ref(region, ty, mutbl) => {
-                TyKind::RigidTy(RigidTy::Ref(opaque(region), self.intern_ty(*ty), mutbl.stable()))
-            }
-            ty::FnDef(_, _) => todo!(),
-            ty::FnPtr(_) => todo!(),
-            ty::Dynamic(_, _, _) => todo!(),
-            ty::Closure(_, _) => todo!(),
-            ty::Generator(_, _, _) => todo!(),
-            ty::Never => todo!(),
-            ty::Tuple(fields) => TyKind::RigidTy(RigidTy::Tuple(
-                fields.iter().map(|ty| self.intern_ty(ty)).collect(),
-            )),
-            ty::Alias(_, _) => todo!(),
-            ty::Param(_) => todo!(),
-            ty::Bound(_, _) => todo!(),
-            ty::Placeholder(..)
-            | ty::GeneratorWitness(_)
-            | ty::GeneratorWitnessMIR(_, _)
-            | ty::Infer(_)
-            | ty::Error(_) => {
-                unreachable!();
-            }
-        }
-    }
-
     fn intern_ty(&mut self, ty: Ty<'tcx>) -> stable_mir::ty::Ty {
         if let Some(id) = self.types.iter().position(|&t| t == ty) {
             return stable_mir::ty::Ty(id);
@@ -166,20 +95,20 @@ fn smir_crate(tcx: TyCtxt<'_>, crate_num: CrateNum) -> stable_mir::Crate {
 }
 
 /// Trait used to convert between an internal MIR type to a Stable MIR type.
-pub(crate) trait Stable {
+pub(crate) trait Stable<'tcx> {
     /// The stable representation of the type implementing Stable.
     type T;
     /// Converts an object to the equivalent Stable MIR representation.
-    fn stable(&self) -> Self::T;
+    fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T;
 }
 
-impl<'tcx> Stable for mir::Statement<'tcx> {
+impl<'tcx> Stable<'tcx> for mir::Statement<'tcx> {
     type T = stable_mir::mir::Statement;
-    fn stable(&self) -> Self::T {
+    fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
         use rustc_middle::mir::StatementKind::*;
         match &self.kind {
             Assign(assign) => {
-                stable_mir::mir::Statement::Assign(assign.0.stable(), assign.1.stable())
+                stable_mir::mir::Statement::Assign(assign.0.stable(tables), assign.1.stable(tables))
             }
             FakeRead(_) => todo!(),
             SetDiscriminant { .. } => todo!(),
@@ -197,45 +126,51 @@ impl<'tcx> Stable for mir::Statement<'tcx> {
     }
 }
 
-impl<'tcx> Stable for mir::Rvalue<'tcx> {
+impl<'tcx> Stable<'tcx> for mir::Rvalue<'tcx> {
     type T = stable_mir::mir::Rvalue;
-    fn stable(&self) -> Self::T {
+    fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
         use mir::Rvalue::*;
         match self {
-            Use(op) => stable_mir::mir::Rvalue::Use(op.stable()),
+            Use(op) => stable_mir::mir::Rvalue::Use(op.stable(tables)),
             Repeat(_, _) => todo!(),
-            Ref(region, kind, place) => {
-                stable_mir::mir::Rvalue::Ref(opaque(region), kind.stable(), place.stable())
-            }
+            Ref(region, kind, place) => stable_mir::mir::Rvalue::Ref(
+                opaque(region),
+                kind.stable(tables),
+                place.stable(tables),
+            ),
             ThreadLocalRef(def_id) => {
                 stable_mir::mir::Rvalue::ThreadLocalRef(rustc_internal::crate_item(*def_id))
             }
             AddressOf(mutability, place) => {
-                stable_mir::mir::Rvalue::AddressOf(mutability.stable(), place.stable())
+                stable_mir::mir::Rvalue::AddressOf(mutability.stable(tables), place.stable(tables))
             }
-            Len(place) => stable_mir::mir::Rvalue::Len(place.stable()),
+            Len(place) => stable_mir::mir::Rvalue::Len(place.stable(tables)),
             Cast(_, _, _) => todo!(),
-            BinaryOp(bin_op, ops) => {
-                stable_mir::mir::Rvalue::BinaryOp(bin_op.stable(), ops.0.stable(), ops.1.stable())
-            }
+            BinaryOp(bin_op, ops) => stable_mir::mir::Rvalue::BinaryOp(
+                bin_op.stable(tables),
+                ops.0.stable(tables),
+                ops.1.stable(tables),
+            ),
             CheckedBinaryOp(bin_op, ops) => stable_mir::mir::Rvalue::CheckedBinaryOp(
-                bin_op.stable(),
-                ops.0.stable(),
-                ops.1.stable(),
+                bin_op.stable(tables),
+                ops.0.stable(tables),
+                ops.1.stable(tables),
             ),
             NullaryOp(_, _) => todo!(),
-            UnaryOp(un_op, op) => stable_mir::mir::Rvalue::UnaryOp(un_op.stable(), op.stable()),
-            Discriminant(place) => stable_mir::mir::Rvalue::Discriminant(place.stable()),
+            UnaryOp(un_op, op) => {
+                stable_mir::mir::Rvalue::UnaryOp(un_op.stable(tables), op.stable(tables))
+            }
+            Discriminant(place) => stable_mir::mir::Rvalue::Discriminant(place.stable(tables)),
             Aggregate(_, _) => todo!(),
             ShallowInitBox(_, _) => todo!(),
-            CopyForDeref(place) => stable_mir::mir::Rvalue::CopyForDeref(place.stable()),
+            CopyForDeref(place) => stable_mir::mir::Rvalue::CopyForDeref(place.stable(tables)),
         }
     }
 }
 
-impl Stable for mir::Mutability {
+impl<'tcx> Stable<'tcx> for mir::Mutability {
     type T = stable_mir::mir::Mutability;
-    fn stable(&self) -> Self::T {
+    fn stable(&self, _: &mut Tables<'tcx>) -> Self::T {
         use mir::Mutability::*;
         match *self {
             Not => stable_mir::mir::Mutability::Not,
@@ -244,21 +179,21 @@ impl Stable for mir::Mutability {
     }
 }
 
-impl Stable for mir::BorrowKind {
+impl<'tcx> Stable<'tcx> for mir::BorrowKind {
     type T = stable_mir::mir::BorrowKind;
-    fn stable(&self) -> Self::T {
+    fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
         use mir::BorrowKind::*;
         match *self {
             Shared => stable_mir::mir::BorrowKind::Shared,
             Shallow => stable_mir::mir::BorrowKind::Shallow,
-            Mut { kind } => stable_mir::mir::BorrowKind::Mut { kind: kind.stable() },
+            Mut { kind } => stable_mir::mir::BorrowKind::Mut { kind: kind.stable(tables) },
         }
     }
 }
 
-impl Stable for mir::MutBorrowKind {
+impl<'tcx> Stable<'tcx> for mir::MutBorrowKind {
     type T = stable_mir::mir::MutBorrowKind;
-    fn stable(&self) -> Self::T {
+    fn stable(&self, _: &mut Tables<'tcx>) -> Self::T {
         use mir::MutBorrowKind::*;
         match *self {
             Default => stable_mir::mir::MutBorrowKind::Default,
@@ -268,28 +203,28 @@ impl Stable for mir::MutBorrowKind {
     }
 }
 
-impl<'tcx> Stable for mir::NullOp<'tcx> {
+impl<'tcx> Stable<'tcx> for mir::NullOp<'tcx> {
     type T = stable_mir::mir::NullOp;
-    fn stable(&self) -> Self::T {
+    fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
         use mir::NullOp::*;
         match self {
             SizeOf => stable_mir::mir::NullOp::SizeOf,
             AlignOf => stable_mir::mir::NullOp::AlignOf,
-            OffsetOf(indices) => {
-                stable_mir::mir::NullOp::OffsetOf(indices.iter().map(|idx| idx.stable()).collect())
-            }
+            OffsetOf(indices) => stable_mir::mir::NullOp::OffsetOf(
+                indices.iter().map(|idx| idx.stable(tables)).collect(),
+            ),
         }
     }
 }
 
-impl Stable for mir::CastKind {
+impl<'tcx> Stable<'tcx> for mir::CastKind {
     type T = stable_mir::mir::CastKind;
-    fn stable(&self) -> Self::T {
+    fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
         use mir::CastKind::*;
         match self {
             PointerExposeAddress => stable_mir::mir::CastKind::PointerExposeAddress,
             PointerFromExposedAddress => stable_mir::mir::CastKind::PointerFromExposedAddress,
-            PointerCoercion(c) => stable_mir::mir::CastKind::PointerCoercion(c.stable()),
+            PointerCoercion(c) => stable_mir::mir::CastKind::PointerCoercion(c.stable(tables)),
             DynStar => stable_mir::mir::CastKind::DynStar,
             IntToInt => stable_mir::mir::CastKind::IntToInt,
             FloatToInt => stable_mir::mir::CastKind::FloatToInt,
@@ -302,15 +237,36 @@ impl Stable for mir::CastKind {
     }
 }
 
-impl Stable for ty::adjustment::PointerCoercion {
+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::adjustment::PointerCoercion {
     type T = stable_mir::mir::PointerCoercion;
-    fn stable(&self) -> Self::T {
+    fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
         use ty::adjustment::PointerCoercion;
         match self {
             PointerCoercion::ReifyFnPointer => stable_mir::mir::PointerCoercion::ReifyFnPointer,
             PointerCoercion::UnsafeFnPointer => stable_mir::mir::PointerCoercion::UnsafeFnPointer,
             PointerCoercion::ClosureFnPointer(unsafety) => {
-                stable_mir::mir::PointerCoercion::ClosureFnPointer(unsafety.stable())
+                stable_mir::mir::PointerCoercion::ClosureFnPointer(unsafety.stable(tables))
             }
             PointerCoercion::MutToConstPointer => {
                 stable_mir::mir::PointerCoercion::MutToConstPointer
@@ -321,9 +277,9 @@ impl Stable for ty::adjustment::PointerCoercion {
     }
 }
 
-impl Stable for rustc_hir::Unsafety {
+impl<'tcx> Stable<'tcx> for rustc_hir::Unsafety {
     type T = stable_mir::mir::Safety;
-    fn stable(&self) -> Self::T {
+    fn stable(&self, _: &mut Tables<'tcx>) -> Self::T {
         match self {
             rustc_hir::Unsafety::Unsafe => stable_mir::mir::Safety::Unsafe,
             rustc_hir::Unsafety::Normal => stable_mir::mir::Safety::Normal,
@@ -331,28 +287,28 @@ impl Stable for rustc_hir::Unsafety {
     }
 }
 
-impl Stable for FieldIdx {
+impl<'tcx> Stable<'tcx> for FieldIdx {
     type T = usize;
-    fn stable(&self) -> Self::T {
+    fn stable(&self, _: &mut Tables<'tcx>) -> Self::T {
         self.as_usize()
     }
 }
 
-impl<'tcx> Stable for mir::Operand<'tcx> {
+impl<'tcx> Stable<'tcx> for mir::Operand<'tcx> {
     type T = stable_mir::mir::Operand;
-    fn stable(&self) -> Self::T {
+    fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
         use mir::Operand::*;
         match self {
-            Copy(place) => stable_mir::mir::Operand::Copy(place.stable()),
-            Move(place) => stable_mir::mir::Operand::Move(place.stable()),
+            Copy(place) => stable_mir::mir::Operand::Copy(place.stable(tables)),
+            Move(place) => stable_mir::mir::Operand::Move(place.stable(tables)),
             Constant(c) => stable_mir::mir::Operand::Constant(c.to_string()),
         }
     }
 }
 
-impl<'tcx> Stable for mir::Place<'tcx> {
+impl<'tcx> Stable<'tcx> for mir::Place<'tcx> {
     type T = stable_mir::mir::Place;
-    fn stable(&self) -> Self::T {
+    fn stable(&self, _: &mut Tables<'tcx>) -> Self::T {
         stable_mir::mir::Place {
             local: self.local.as_usize(),
             projection: format!("{:?}", self.projection),
@@ -360,9 +316,9 @@ impl<'tcx> Stable for mir::Place<'tcx> {
     }
 }
 
-impl Stable for mir::UnwindAction {
+impl<'tcx> Stable<'tcx> for mir::UnwindAction {
     type T = stable_mir::mir::UnwindAction;
-    fn stable(&self) -> Self::T {
+    fn stable(&self, _: &mut Tables<'tcx>) -> Self::T {
         use rustc_middle::mir::UnwindAction;
         match self {
             UnwindAction::Continue => stable_mir::mir::UnwindAction::Continue,
@@ -373,46 +329,48 @@ impl Stable for mir::UnwindAction {
     }
 }
 
-impl<'tcx> Stable for mir::AssertMessage<'tcx> {
+impl<'tcx> Stable<'tcx> for mir::AssertMessage<'tcx> {
     type T = stable_mir::mir::AssertMessage;
-    fn stable(&self) -> Self::T {
+    fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
         use rustc_middle::mir::AssertKind;
         match self {
             AssertKind::BoundsCheck { len, index } => stable_mir::mir::AssertMessage::BoundsCheck {
-                len: len.stable(),
-                index: index.stable(),
+                len: len.stable(tables),
+                index: index.stable(tables),
             },
             AssertKind::Overflow(bin_op, op1, op2) => stable_mir::mir::AssertMessage::Overflow(
-                bin_op.stable(),
-                op1.stable(),
-                op2.stable(),
+                bin_op.stable(tables),
+                op1.stable(tables),
+                op2.stable(tables),
             ),
-            AssertKind::OverflowNeg(op) => stable_mir::mir::AssertMessage::OverflowNeg(op.stable()),
+            AssertKind::OverflowNeg(op) => {
+                stable_mir::mir::AssertMessage::OverflowNeg(op.stable(tables))
+            }
             AssertKind::DivisionByZero(op) => {
-                stable_mir::mir::AssertMessage::DivisionByZero(op.stable())
+                stable_mir::mir::AssertMessage::DivisionByZero(op.stable(tables))
             }
             AssertKind::RemainderByZero(op) => {
-                stable_mir::mir::AssertMessage::RemainderByZero(op.stable())
+                stable_mir::mir::AssertMessage::RemainderByZero(op.stable(tables))
             }
             AssertKind::ResumedAfterReturn(generator) => {
-                stable_mir::mir::AssertMessage::ResumedAfterReturn(generator.stable())
+                stable_mir::mir::AssertMessage::ResumedAfterReturn(generator.stable(tables))
             }
             AssertKind::ResumedAfterPanic(generator) => {
-                stable_mir::mir::AssertMessage::ResumedAfterPanic(generator.stable())
+                stable_mir::mir::AssertMessage::ResumedAfterPanic(generator.stable(tables))
             }
             AssertKind::MisalignedPointerDereference { required, found } => {
                 stable_mir::mir::AssertMessage::MisalignedPointerDereference {
-                    required: required.stable(),
-                    found: found.stable(),
+                    required: required.stable(tables),
+                    found: found.stable(tables),
                 }
             }
         }
     }
 }
 
-impl Stable for mir::BinOp {
+impl<'tcx> Stable<'tcx> for mir::BinOp {
     type T = stable_mir::mir::BinOp;
-    fn stable(&self) -> Self::T {
+    fn stable(&self, _: &mut Tables<'tcx>) -> Self::T {
         use mir::BinOp;
         match self {
             BinOp::Add => stable_mir::mir::BinOp::Add,
@@ -441,9 +399,9 @@ impl Stable for mir::BinOp {
     }
 }
 
-impl Stable for mir::UnOp {
+impl<'tcx> Stable<'tcx> for mir::UnOp {
     type T = stable_mir::mir::UnOp;
-    fn stable(&self) -> Self::T {
+    fn stable(&self, _: &mut Tables<'tcx>) -> Self::T {
         use mir::UnOp;
         match self {
             UnOp::Not => stable_mir::mir::UnOp::Not,
@@ -452,9 +410,9 @@ impl Stable for mir::UnOp {
     }
 }
 
-impl Stable for rustc_hir::GeneratorKind {
+impl<'tcx> Stable<'tcx> for rustc_hir::GeneratorKind {
     type T = stable_mir::mir::GeneratorKind;
-    fn stable(&self) -> Self::T {
+    fn stable(&self, _: &mut Tables<'tcx>) -> Self::T {
         use rustc_hir::{AsyncGeneratorKind, GeneratorKind};
         match self {
             GeneratorKind::Async(async_gen) => {
@@ -470,16 +428,16 @@ impl Stable for rustc_hir::GeneratorKind {
     }
 }
 
-impl<'tcx> Stable for mir::InlineAsmOperand<'tcx> {
+impl<'tcx> Stable<'tcx> for mir::InlineAsmOperand<'tcx> {
     type T = stable_mir::mir::InlineAsmOperand;
-    fn stable(&self) -> Self::T {
+    fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
         use rustc_middle::mir::InlineAsmOperand;
 
         let (in_value, out_place) = match self {
-            InlineAsmOperand::In { value, .. } => (Some(value.stable()), None),
-            InlineAsmOperand::Out { place, .. } => (None, place.map(|place| place.stable())),
+            InlineAsmOperand::In { value, .. } => (Some(value.stable(tables)), None),
+            InlineAsmOperand::Out { place, .. } => (None, place.map(|place| place.stable(tables))),
             InlineAsmOperand::InOut { in_value, out_place, .. } => {
-                (Some(in_value.stable()), out_place.map(|place| place.stable()))
+                (Some(in_value.stable(tables)), out_place.map(|place| place.stable(tables)))
             }
             InlineAsmOperand::Const { .. }
             | InlineAsmOperand::SymFn { .. }
@@ -490,15 +448,15 @@ impl<'tcx> Stable for mir::InlineAsmOperand<'tcx> {
     }
 }
 
-impl<'tcx> Stable for mir::Terminator<'tcx> {
+impl<'tcx> Stable<'tcx> for mir::Terminator<'tcx> {
     type T = stable_mir::mir::Terminator;
-    fn stable(&self) -> Self::T {
+    fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
         use rustc_middle::mir::TerminatorKind::*;
         use stable_mir::mir::Terminator;
         match &self.kind {
             Goto { target } => Terminator::Goto { target: target.as_usize() },
             SwitchInt { discr, targets } => Terminator::SwitchInt {
-                discr: discr.stable(),
+                discr: discr.stable(tables),
                 targets: targets
                     .iter()
                     .map(|(value, target)| stable_mir::mir::SwitchTarget {
@@ -513,37 +471,235 @@ impl<'tcx> Stable for mir::Terminator<'tcx> {
             Return => Terminator::Return,
             Unreachable => Terminator::Unreachable,
             Drop { place, target, unwind, replace: _ } => Terminator::Drop {
-                place: place.stable(),
+                place: place.stable(tables),
                 target: target.as_usize(),
-                unwind: unwind.stable(),
+                unwind: unwind.stable(tables),
             },
             Call { func, args, destination, target, unwind, call_source: _, fn_span: _ } => {
                 Terminator::Call {
-                    func: func.stable(),
-                    args: args.iter().map(|arg| arg.stable()).collect(),
-                    destination: destination.stable(),
+                    func: func.stable(tables),
+                    args: args.iter().map(|arg| arg.stable(tables)).collect(),
+                    destination: destination.stable(tables),
                     target: target.map(|t| t.as_usize()),
-                    unwind: unwind.stable(),
+                    unwind: unwind.stable(tables),
                 }
             }
             Assert { cond, expected, msg, target, unwind } => Terminator::Assert {
-                cond: cond.stable(),
+                cond: cond.stable(tables),
                 expected: *expected,
-                msg: msg.stable(),
+                msg: msg.stable(tables),
                 target: target.as_usize(),
-                unwind: unwind.stable(),
+                unwind: unwind.stable(tables),
             },
             InlineAsm { template, operands, options, line_spans, destination, unwind } => {
                 Terminator::InlineAsm {
                     template: format!("{:?}", template),
-                    operands: operands.iter().map(|operand| operand.stable()).collect(),
+                    operands: operands.iter().map(|operand| operand.stable(tables)).collect(),
                     options: format!("{:?}", options),
                     line_spans: format!("{:?}", line_spans),
                     destination: destination.map(|d| d.as_usize()),
-                    unwind: unwind.stable(),
+                    unwind: unwind.stable(tables),
                 }
             }
             Yield { .. } | GeneratorDrop | FalseEdge { .. } | FalseUnwind { .. } => unreachable!(),
         }
     }
 }
+
+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(),
+        )
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::PolyFnSig<'tcx> {
+    type T = stable_mir::ty::PolyFnSig;
+    fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
+        use stable_mir::ty::Binder;
+
+        Binder {
+            value: self.skip_binder().stable(tables),
+            bound_vars: self
+                .bound_vars()
+                .iter()
+                .map(|bound_var| bound_var.stable(tables))
+                .collect(),
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::FnSig<'tcx> {
+    type T = stable_mir::ty::FnSig;
+    fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
+        use rustc_target::spec::abi;
+        use stable_mir::ty::{Abi, FnSig, Unsafety};
+
+        FnSig {
+            inputs_and_output: self
+                .inputs_and_output
+                .iter()
+                .map(|ty| tables.intern_ty(ty))
+                .collect(),
+            c_variadic: self.c_variadic,
+            unsafety: match self.unsafety {
+                hir::Unsafety::Normal => Unsafety::Normal,
+                hir::Unsafety::Unsafe => Unsafety::Unsafe,
+            },
+            abi: match self.abi {
+                abi::Abi::Rust => Abi::Rust,
+                abi::Abi::C { unwind } => Abi::C { unwind },
+                abi::Abi::Cdecl { unwind } => Abi::Cdecl { unwind },
+                abi::Abi::Stdcall { unwind } => Abi::Stdcall { unwind },
+                abi::Abi::Fastcall { unwind } => Abi::Fastcall { unwind },
+                abi::Abi::Vectorcall { unwind } => Abi::Vectorcall { unwind },
+                abi::Abi::Thiscall { unwind } => Abi::Thiscall { unwind },
+                abi::Abi::Aapcs { unwind } => Abi::Aapcs { unwind },
+                abi::Abi::Win64 { unwind } => Abi::Win64 { unwind },
+                abi::Abi::SysV64 { unwind } => Abi::SysV64 { unwind },
+                abi::Abi::PtxKernel => Abi::PtxKernel,
+                abi::Abi::Msp430Interrupt => Abi::Msp430Interrupt,
+                abi::Abi::X86Interrupt => Abi::X86Interrupt,
+                abi::Abi::AmdGpuKernel => Abi::AmdGpuKernel,
+                abi::Abi::EfiApi => Abi::EfiApi,
+                abi::Abi::AvrInterrupt => Abi::AvrInterrupt,
+                abi::Abi::AvrNonBlockingInterrupt => Abi::AvrNonBlockingInterrupt,
+                abi::Abi::CCmseNonSecureCall => Abi::CCmseNonSecureCall,
+                abi::Abi::Wasm => Abi::Wasm,
+                abi::Abi::System { unwind } => Abi::System { unwind },
+                abi::Abi::RustIntrinsic => Abi::RustIntrinsic,
+                abi::Abi::RustCall => Abi::RustCall,
+                abi::Abi::PlatformIntrinsic => Abi::PlatformIntrinsic,
+                abi::Abi::Unadjusted => Abi::Unadjusted,
+                abi::Abi::RustCold => Abi::RustCold,
+            },
+        }
+    }
+}
+
+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};
+
+        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())
+                    }
+                })
+            }
+            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,
+                })
+            }
+            ty::BoundVariableKind::Const => BoundVariableKind::Const,
+        }
+    }
+}
+
+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::Adt(adt_def, generic_args) => TyKind::RigidTy(RigidTy::Adt(
+                rustc_internal::adt_def(adt_def.did()),
+                generic_args.stable(tables),
+            )),
+            ty::Foreign(def_id) => {
+                TyKind::RigidTy(RigidTy::Foreign(rustc_internal::foreign_def(*def_id)))
+            }
+            ty::Str => TyKind::RigidTy(RigidTy::Str),
+            ty::Array(ty, constant) => {
+                TyKind::RigidTy(RigidTy::Array(tables.intern_ty(*ty), opaque(constant)))
+            }
+            ty::Slice(ty) => TyKind::RigidTy(RigidTy::Slice(tables.intern_ty(*ty))),
+            ty::RawPtr(ty::TypeAndMut { ty, mutbl }) => {
+                TyKind::RigidTy(RigidTy::RawPtr(tables.intern_ty(*ty), mutbl.stable(tables)))
+            }
+            ty::Ref(region, ty, mutbl) => TyKind::RigidTy(RigidTy::Ref(
+                opaque(region),
+                tables.intern_ty(*ty),
+                mutbl.stable(tables),
+            )),
+            ty::FnDef(def_id, generic_args) => TyKind::RigidTy(RigidTy::FnDef(
+                rustc_internal::fn_def(*def_id),
+                generic_args.stable(tables),
+            )),
+            ty::FnPtr(poly_fn_sig) => TyKind::RigidTy(RigidTy::FnPtr(poly_fn_sig.stable(tables))),
+            ty::Dynamic(_, _, _) => todo!(),
+            ty::Closure(def_id, generic_args) => TyKind::RigidTy(RigidTy::Closure(
+                rustc_internal::closure_def(*def_id),
+                generic_args.stable(tables),
+            )),
+            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,
+                },
+            )),
+            ty::Never => TyKind::RigidTy(RigidTy::Never),
+            ty::Tuple(fields) => TyKind::RigidTy(RigidTy::Tuple(
+                fields.iter().map(|ty| tables.intern_ty(ty)).collect(),
+            )),
+            ty::Alias(alias_kind, alias_ty) => {
+                TyKind::Alias(alias_kind.stable(tables), alias_ty.stable(tables))
+            }
+            ty::Param(_) => todo!(),
+            ty::Bound(_, _) => todo!(),
+            ty::Placeholder(..)
+            | ty::GeneratorWitness(_)
+            | ty::GeneratorWitnessMIR(_, _)
+            | ty::Infer(_)
+            | ty::Error(_) => {
+                unreachable!();
+            }
+        }
+    }
+}
diff --git a/compiler/rustc_smir/src/stable_mir/ty.rs b/compiler/rustc_smir/src/stable_mir/ty.rs
index 7ae07efb729..885beeda78c 100644
--- a/compiler/rustc_smir/src/stable_mir/ty.rs
+++ b/compiler/rustc_smir/src/stable_mir/ty.rs
@@ -12,10 +12,12 @@ impl Ty {
 
 type Const = Opaque;
 pub(crate) type Region = Opaque;
+type Span = Opaque;
 
 #[derive(Clone, Debug)]
 pub enum TyKind {
     RigidTy(RigidTy),
+    Alias(AliasKind, AliasTy),
 }
 
 #[derive(Clone, Debug)]
@@ -25,12 +27,18 @@ pub enum RigidTy {
     Int(IntTy),
     Uint(UintTy),
     Float(FloatTy),
-    Adt(AdtDef, AdtSubsts),
+    Adt(AdtDef, GenericArgs),
+    Foreign(ForeignDef),
     Str,
     Array(Ty, Const),
     Slice(Ty),
     RawPtr(Ty, Mutability),
     Ref(Region, Ty, Mutability),
+    FnDef(FnDef, GenericArgs),
+    FnPtr(PolyFnSig),
+    Closure(ClosureDef, GenericArgs),
+    Generator(GeneratorDef, GenericArgs, Movability),
+    Never,
     Tuple(Vec<Ty>),
 }
 
@@ -60,17 +68,127 @@ pub enum FloatTy {
     F64,
 }
 
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum Movability {
+    Static,
+    Movable,
+}
+
+#[derive(Clone, PartialEq, Eq, Debug)]
+pub struct ForeignDef(pub(crate) DefId);
+
+#[derive(Clone, PartialEq, Eq, Debug)]
+pub struct FnDef(pub(crate) DefId);
+
+#[derive(Clone, PartialEq, Eq, Debug)]
+pub struct ClosureDef(pub(crate) DefId);
+
+#[derive(Clone, PartialEq, Eq, Debug)]
+pub struct GeneratorDef(pub(crate) DefId);
+
+#[derive(Clone, PartialEq, Eq, Debug)]
+pub struct ParamDef(pub(crate) DefId);
+
+#[derive(Clone, PartialEq, Eq, Debug)]
+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, Debug)]
-pub struct AdtSubsts(pub Vec<GenericArgKind>);
+pub struct GenericArgs(pub Vec<GenericArgKind>);
 
 #[derive(Clone, Debug)]
 pub enum GenericArgKind {
-    // FIXME add proper region
     Lifetime(Region),
     Type(Ty),
-    // FIXME add proper const
     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)]
+pub struct FnSig {
+    pub inputs_and_output: Vec<Ty>,
+    pub c_variadic: bool,
+    pub unsafety: Unsafety,
+    pub abi: Abi,
+}
+
+#[derive(Clone, PartialEq, Eq, Debug)]
+pub enum Unsafety {
+    Unsafe,
+    Normal,
+}
+
+#[derive(Clone, PartialEq, Eq, Debug)]
+pub enum Abi {
+    Rust,
+    C { unwind: bool },
+    Cdecl { unwind: bool },
+    Stdcall { unwind: bool },
+    Fastcall { unwind: bool },
+    Vectorcall { unwind: bool },
+    Thiscall { unwind: bool },
+    Aapcs { unwind: bool },
+    Win64 { unwind: bool },
+    SysV64 { unwind: bool },
+    PtxKernel,
+    Msp430Interrupt,
+    X86Interrupt,
+    AmdGpuKernel,
+    EfiApi,
+    AvrInterrupt,
+    AvrNonBlockingInterrupt,
+    CCmseNonSecureCall,
+    Wasm,
+    System { unwind: bool },
+    RustIntrinsic,
+    RustCall,
+    PlatformIntrinsic,
+    Unadjusted,
+    RustCold,
+}
+
+#[derive(Clone, Debug)]
+pub struct Binder<T> {
+    pub value: T,
+    pub bound_vars: Vec<BoundVariableKind>,
+}
+
+#[derive(Clone, Debug)]
+pub enum BoundVariableKind {
+    Ty(BoundTyKind),
+    Region(BoundRegionKind),
+    Const,
+}
+
+#[derive(Clone, PartialEq, Eq, Debug)]
+pub enum BoundTyKind {
+    Anon,
+    Param(ParamDef, String),
+}
+
+#[derive(Clone, Debug)]
+pub enum BoundRegionKind {
+    BrAnon(Option<Span>),
+    BrNamed(BrNamedDef, String),
+    BrEnv,
+}
diff --git a/compiler/rustc_target/src/abi/mod.rs b/compiler/rustc_target/src/abi/mod.rs
index 589cd3cf96b..11ba551dccc 100644
--- a/compiler/rustc_target/src/abi/mod.rs
+++ b/compiler/rustc_target/src/abi/mod.rs
@@ -50,6 +50,9 @@ 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;
@@ -76,7 +79,8 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
     where
         Ty: TyAbiInterface<'a, C>,
     {
-        Ty::ty_and_layout_pointee_info_at(self, cx, offset)
+        let assume_valid_ptr = true;
+        Ty::ty_and_layout_pointee_info_at(self, cx, offset, assume_valid_ptr)
     }
 
     pub fn is_single_fp_element<C>(self, cx: &C) -> bool
@@ -140,24 +144,3 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
         offset
     }
 }
-
-impl<'a, Ty> TyAndLayout<'a, Ty> {
-    /// Returns `true` if the layout corresponds to an unsized type.
-    pub fn is_unsized(&self) -> bool {
-        self.abi.is_unsized()
-    }
-
-    #[inline]
-    pub fn is_sized(&self) -> bool {
-        self.abi.is_sized()
-    }
-
-    /// Returns `true` if the type is a ZST and not unsized.
-    pub fn is_zst(&self) -> bool {
-        match self.abi {
-            Abi::Scalar(_) | Abi::ScalarPair(..) | Abi::Vector { .. } => false,
-            Abi::Uninhabited => self.size.bytes() == 0,
-            Abi::Aggregate { sized } => sized && self.size.bytes() == 0,
-        }
-    }
-}
diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
index 1e798998895..6920e790e71 100644
--- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
@@ -10,9 +10,11 @@ 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::ty::fast_reject::TreatProjections;
-use rustc_middle::ty::TypeFoldable;
+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_span::ErrorGuaranteed;
 use std::fmt::Debug;
 
 pub(super) mod structural_traits;
@@ -109,10 +111,10 @@ pub(super) trait GoalKind<'tcx>:
 
     fn trait_def_id(self, tcx: TyCtxt<'tcx>) -> DefId;
 
-    // Try equating an assumption predicate against a goal's predicate. If it
-    // holds, then execute the `then` callback, which should do any additional
-    // work, then produce a response (typically by executing
-    // [`EvalCtxt::evaluate_added_goals_and_make_canonical_response`]).
+    /// Try equating an assumption predicate against a goal's predicate. If it
+    /// holds, then execute the `then` callback, which should do any additional
+    /// work, then produce a response (typically by executing
+    /// [`EvalCtxt::evaluate_added_goals_and_make_canonical_response`]).
     fn probe_and_match_goal_against_assumption(
         ecx: &mut EvalCtxt<'_, 'tcx>,
         goal: Goal<'tcx, Self>,
@@ -120,9 +122,9 @@ pub(super) trait GoalKind<'tcx>:
         then: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> QueryResult<'tcx>,
     ) -> QueryResult<'tcx>;
 
-    // Consider a clause, which consists of a "assumption" and some "requirements",
-    // to satisfy a goal. If the requirements hold, then attempt to satisfy our
-    // goal by equating it with the assumption.
+    /// Consider a clause, which consists of a "assumption" and some "requirements",
+    /// to satisfy a goal. If the requirements hold, then attempt to satisfy our
+    /// goal by equating it with the assumption.
     fn consider_implied_clause(
         ecx: &mut EvalCtxt<'_, 'tcx>,
         goal: Goal<'tcx, Self>,
@@ -149,9 +151,9 @@ pub(super) trait GoalKind<'tcx>:
         })
     }
 
-    // Consider a clause specifically for a `dyn Trait` self type. This requires
-    // additionally checking all of the supertraits and object bounds to hold,
-    // since they're not implied by the well-formedness of the object type.
+    /// Consider a clause specifically for a `dyn Trait` self type. This requires
+    /// additionally checking all of the supertraits and object bounds to hold,
+    /// since they're not implied by the well-formedness of the object type.
     fn consider_object_bound_candidate(
         ecx: &mut EvalCtxt<'_, 'tcx>,
         goal: Goal<'tcx, Self>,
@@ -182,96 +184,113 @@ pub(super) trait GoalKind<'tcx>:
         impl_def_id: DefId,
     ) -> QueryResult<'tcx>;
 
-    // A type implements an `auto trait` if its components do as well. These components
-    // are given by built-in rules from [`instantiate_constituent_tys_for_auto_trait`].
+    /// If the predicate contained an error, we want to avoid emitting unnecessary trait
+    /// errors but still want to emit errors for other trait goals. We have some special
+    /// handling for this case.
+    ///
+    /// Trait goals always hold while projection goals never do. This is a bit arbitrary
+    /// but prevents incorrect normalization while hiding any trait errors.
+    fn consider_error_guaranteed_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        guar: ErrorGuaranteed,
+    ) -> QueryResult<'tcx>;
+
+    /// A type implements an `auto trait` if its components do as well.
+    ///
+    /// These components are given by built-in rules from
+    /// [`structural_traits::instantiate_constituent_tys_for_auto_trait`].
     fn consider_auto_trait_candidate(
         ecx: &mut EvalCtxt<'_, 'tcx>,
         goal: Goal<'tcx, Self>,
     ) -> QueryResult<'tcx>;
 
-    // A trait alias holds if the RHS traits and `where` clauses hold.
+    /// A trait alias holds if the RHS traits and `where` clauses hold.
     fn consider_trait_alias_candidate(
         ecx: &mut EvalCtxt<'_, 'tcx>,
         goal: Goal<'tcx, Self>,
     ) -> QueryResult<'tcx>;
 
-    // A type is `Copy` or `Clone` if its components are `Sized`. These components
-    // are given by built-in rules from [`instantiate_constituent_tys_for_sized_trait`].
+    /// A type is `Copy` or `Clone` if its components are `Sized`.
+    ///
+    /// These components are given by built-in rules from
+    /// [`structural_traits::instantiate_constituent_tys_for_sized_trait`].
     fn consider_builtin_sized_candidate(
         ecx: &mut EvalCtxt<'_, 'tcx>,
         goal: Goal<'tcx, Self>,
     ) -> QueryResult<'tcx>;
 
-    // A type is `Copy` or `Clone` if its components are `Copy` or `Clone`. These
-    // components are given by built-in rules from [`instantiate_constituent_tys_for_copy_clone_trait`].
+    /// A type is `Copy` or `Clone` if its components are `Copy` or `Clone`.
+    ///
+    /// These components are given by built-in rules from
+    /// [`structural_traits::instantiate_constituent_tys_for_copy_clone_trait`].
     fn consider_builtin_copy_clone_candidate(
         ecx: &mut EvalCtxt<'_, 'tcx>,
         goal: Goal<'tcx, Self>,
     ) -> QueryResult<'tcx>;
 
-    // A type is `PointerLike` if we can compute its layout, and that layout
-    // matches the layout of `usize`.
+    /// A type is `PointerLike` if we can compute its layout, and that layout
+    /// matches the layout of `usize`.
     fn consider_builtin_pointer_like_candidate(
         ecx: &mut EvalCtxt<'_, 'tcx>,
         goal: Goal<'tcx, Self>,
     ) -> QueryResult<'tcx>;
 
-    // A type is a `FnPtr` if it is of `FnPtr` type.
+    /// A type is a `FnPtr` if it is of `FnPtr` type.
     fn consider_builtin_fn_ptr_trait_candidate(
         ecx: &mut EvalCtxt<'_, 'tcx>,
         goal: Goal<'tcx, Self>,
     ) -> QueryResult<'tcx>;
 
-    // A callable type (a closure, fn def, or fn ptr) is known to implement the `Fn<A>`
-    // family of traits where `A` is given by the signature of the type.
+    /// A callable type (a closure, fn def, or fn ptr) is known to implement the `Fn<A>`
+    /// family of traits where `A` is given by the signature of the type.
     fn consider_builtin_fn_trait_candidates(
         ecx: &mut EvalCtxt<'_, 'tcx>,
         goal: Goal<'tcx, Self>,
         kind: ty::ClosureKind,
     ) -> QueryResult<'tcx>;
 
-    // `Tuple` is implemented if the `Self` type is a tuple.
+    /// `Tuple` is implemented if the `Self` type is a tuple.
     fn consider_builtin_tuple_candidate(
         ecx: &mut EvalCtxt<'_, 'tcx>,
         goal: Goal<'tcx, Self>,
     ) -> QueryResult<'tcx>;
 
-    // `Pointee` is always implemented.
-    //
-    // See the projection implementation for the `Metadata` types for all of
-    // the built-in types. For structs, the metadata type is given by the struct
-    // tail.
+    /// `Pointee` is always implemented.
+    ///
+    /// See the projection implementation for the `Metadata` types for all of
+    /// the built-in types. For structs, the metadata type is given by the struct
+    /// tail.
     fn consider_builtin_pointee_candidate(
         ecx: &mut EvalCtxt<'_, 'tcx>,
         goal: Goal<'tcx, Self>,
     ) -> QueryResult<'tcx>;
 
-    // A generator (that comes from an `async` desugaring) is known to implement
-    // `Future<Output = O>`, where `O` is given by the generator's return type
-    // that was computed during type-checking.
+    /// A generator (that comes from an `async` desugaring) is known to implement
+    /// `Future<Output = O>`, where `O` is given by the generator's return type
+    /// that was computed during type-checking.
     fn consider_builtin_future_candidate(
         ecx: &mut EvalCtxt<'_, 'tcx>,
         goal: Goal<'tcx, Self>,
     ) -> QueryResult<'tcx>;
 
-    // A generator (that doesn't come from an `async` desugaring) is known to
-    // implement `Generator<R, Yield = Y, Return = O>`, given the resume, yield,
-    // and return types of the generator computed during type-checking.
+    /// A generator (that doesn't come from an `async` desugaring) is known to
+    /// implement `Generator<R, Yield = Y, Return = O>`, given the resume, yield,
+    /// and return types of the generator computed during type-checking.
     fn consider_builtin_generator_candidate(
         ecx: &mut EvalCtxt<'_, '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.
+    /// 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`.
+    /// `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>,
@@ -299,35 +318,66 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         goal: Goal<'tcx, G>,
     ) -> Vec<Candidate<'tcx>> {
         debug_assert_eq!(goal, self.resolve_vars_if_possible(goal));
+        if let Some(ambig) = self.assemble_self_ty_infer_ambiguity_response(goal) {
+            return ambig;
+        }
+
+        let mut candidates = self.assemble_candidates_via_self_ty(goal);
+
+        self.assemble_blanket_impl_candidates(goal, &mut candidates);
 
-        // HACK: `_: Trait` is ambiguous, because it may be satisfied via a builtin rule,
-        // object bound, alias bound, etc. We are unable to determine this until we can at
-        // least structurally resolve the type one layer.
-        if goal.predicate.self_ty().is_ty_var() {
-            return vec![Candidate {
+        self.assemble_param_env_candidates(goal, &mut candidates);
+
+        candidates
+    }
+
+    /// `?0: Trait` is ambiguous, because it may be satisfied via a builtin rule,
+    /// object bound, alias bound, etc. We are unable to determine this until we can at
+    /// least structurally resolve the type one layer.
+    ///
+    /// It would also require us to consider all impls of the trait, which is both pretty
+    /// bad for perf and would also constrain the self type if there is just a single impl.
+    fn assemble_self_ty_infer_ambiguity_response<G: GoalKind<'tcx>>(
+        &mut self,
+        goal: Goal<'tcx, G>,
+    ) -> Option<Vec<Candidate<'tcx>>> {
+        goal.predicate.self_ty().is_ty_var().then(|| {
+            vec![Candidate {
                 source: CandidateSource::BuiltinImpl(BuiltinImplSource::Ambiguity),
                 result: self
                     .evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
                     .unwrap(),
-            }];
+            }]
+        })
+    }
+
+    /// Assemble candidates which apply to the self type. This only looks at candidate which
+    /// apply to the specific self type and ignores all others.
+    ///
+    /// Returns `None` if the self type is still ambiguous.
+    fn assemble_candidates_via_self_ty<G: GoalKind<'tcx>>(
+        &mut self,
+        goal: Goal<'tcx, G>,
+    ) -> Vec<Candidate<'tcx>> {
+        debug_assert_eq!(goal, self.resolve_vars_if_possible(goal));
+        if let Some(ambig) = self.assemble_self_ty_infer_ambiguity_response(goal) {
+            return ambig;
         }
 
         let mut candidates = Vec::new();
 
-        self.assemble_candidates_after_normalizing_self_ty(goal, &mut candidates);
-
-        self.assemble_impl_candidates(goal, &mut candidates);
+        self.assemble_non_blanket_impl_candidates(goal, &mut candidates);
 
         self.assemble_builtin_impl_candidates(goal, &mut candidates);
 
-        self.assemble_param_env_candidates(goal, &mut candidates);
-
         self.assemble_alias_bound_candidates(goal, &mut candidates);
 
         self.assemble_object_bound_candidates(goal, &mut candidates);
 
         self.assemble_coherence_unknowable_candidates(goal, &mut candidates);
 
+        self.assemble_candidates_after_normalizing_self_ty(goal, &mut candidates);
+
         candidates
     }
 
@@ -385,7 +435,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
                         // have a `Normalized` candidate. This doesn't work as long as we
                         // use `CandidateSource` in winnowing.
                         let goal = goal.with(tcx, goal.predicate.with_self_ty(tcx, normalized_ty));
-                        Ok(ecx.assemble_and_evaluate_candidates(goal))
+                        Ok(ecx.assemble_candidates_via_self_ty(goal))
                     },
                 )
             });
@@ -396,22 +446,125 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
     }
 
     #[instrument(level = "debug", skip_all)]
-    fn assemble_impl_candidates<G: GoalKind<'tcx>>(
+    fn assemble_non_blanket_impl_candidates<G: GoalKind<'tcx>>(
         &mut self,
         goal: Goal<'tcx, G>,
         candidates: &mut Vec<Candidate<'tcx>>,
     ) {
         let tcx = self.tcx();
-        tcx.for_each_relevant_impl_treating_projections(
-            goal.predicate.trait_def_id(tcx),
-            goal.predicate.self_ty(),
-            TreatProjections::NextSolverLookup,
-            |impl_def_id| match G::consider_impl_candidate(self, goal, impl_def_id) {
+        let self_ty = goal.predicate.self_ty();
+        let trait_impls = tcx.trait_impls_of(goal.predicate.trait_def_id(tcx));
+        let mut consider_impls_for_simplified_type = |simp| {
+            if let Some(impls_for_type) = trait_impls.non_blanket_impls().get(&simp) {
+                for &impl_def_id in impls_for_type {
+                    match G::consider_impl_candidate(self, goal, impl_def_id) {
+                        Ok(result) => candidates
+                            .push(Candidate { source: CandidateSource::Impl(impl_def_id), result }),
+                        Err(NoSolution) => (),
+                    }
+                }
+            }
+        };
+
+        match self_ty.kind() {
+            ty::Bool
+            | ty::Char
+            | ty::Int(_)
+            | ty::Uint(_)
+            | ty::Float(_)
+            | ty::Adt(_, _)
+            | ty::Foreign(_)
+            | ty::Str
+            | ty::Array(_, _)
+            | ty::Slice(_)
+            | ty::RawPtr(_)
+            | ty::Ref(_, _, _)
+            | ty::FnDef(_, _)
+            | ty::FnPtr(_)
+            | ty::Dynamic(_, _, _)
+            | ty::Closure(_, _)
+            | ty::Generator(_, _, _)
+            | ty::Never
+            | ty::Tuple(_) => {
+                let simp =
+                    fast_reject::simplify_type(tcx, self_ty, TreatParams::ForLookup).unwrap();
+                consider_impls_for_simplified_type(simp);
+            }
+
+            // HACK: For integer and float variables we have to manually look at all impls
+            // which have some integer or float as a self type.
+            ty::Infer(ty::IntVar(_)) => {
+                use ty::IntTy::*;
+                use ty::UintTy::*;
+                // This causes a compiler error if any new integer kinds are added.
+                let (I8 | I16 | I32 | I64 | I128 | Isize): ty::IntTy;
+                let (U8 | U16 | U32 | U64 | U128 | Usize): ty::UintTy;
+                let possible_integers = [
+                    // signed integers
+                    SimplifiedType::Int(I8),
+                    SimplifiedType::Int(I16),
+                    SimplifiedType::Int(I32),
+                    SimplifiedType::Int(I64),
+                    SimplifiedType::Int(I128),
+                    SimplifiedType::Int(Isize),
+                    // unsigned integers
+                    SimplifiedType::Uint(U8),
+                    SimplifiedType::Uint(U16),
+                    SimplifiedType::Uint(U32),
+                    SimplifiedType::Uint(U64),
+                    SimplifiedType::Uint(U128),
+                    SimplifiedType::Uint(Usize),
+                ];
+                for simp in possible_integers {
+                    consider_impls_for_simplified_type(simp);
+                }
+            }
+
+            ty::Infer(ty::FloatVar(_)) => {
+                // This causes a compiler error if any new float kinds are added.
+                let (ty::FloatTy::F32 | ty::FloatTy::F64);
+                let possible_floats = [
+                    SimplifiedType::Float(ty::FloatTy::F32),
+                    SimplifiedType::Float(ty::FloatTy::F64),
+                ];
+
+                for simp in possible_floats {
+                    consider_impls_for_simplified_type(simp);
+                }
+            }
+
+            // The only traits applying to aliases and placeholders are blanket impls.
+            //
+            // Impls which apply to an alias after normalization are handled by
+            // `assemble_candidates_after_normalizing_self_ty`.
+            ty::Alias(_, _) | ty::Placeholder(..) | ty::Error(_) => (),
+
+            // FIXME: These should ideally not exist as a self type. It would be nice for
+            // the builtin auto trait impls of generators should instead directly recurse
+            // into the witness.
+            ty::GeneratorWitness(_) | ty::GeneratorWitnessMIR(_, _) => (),
+
+            // These variants should not exist as a self type.
+            ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_))
+            | ty::Param(_)
+            | ty::Bound(_, _) => bug!("unexpected self type: {self_ty}"),
+        }
+    }
+
+    fn assemble_blanket_impl_candidates<G: GoalKind<'tcx>>(
+        &mut self,
+        goal: Goal<'tcx, G>,
+        candidates: &mut Vec<Candidate<'tcx>>,
+    ) {
+        let tcx = self.tcx();
+        let trait_impls = tcx.trait_impls_of(goal.predicate.trait_def_id(tcx));
+        for &impl_def_id in trait_impls.blanket_impls() {
+            match G::consider_impl_candidate(self, goal, impl_def_id) {
                 Ok(result) => candidates
                     .push(Candidate { source: CandidateSource::Impl(impl_def_id), result }),
                 Err(NoSolution) => (),
-            },
-        );
+            }
+        }
     }
 
     #[instrument(level = "debug", skip_all)]
@@ -420,8 +573,9 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         goal: Goal<'tcx, G>,
         candidates: &mut Vec<Candidate<'tcx>>,
     ) {
-        let lang_items = self.tcx().lang_items();
-        let trait_def_id = goal.predicate.trait_def_id(self.tcx());
+        let tcx = self.tcx();
+        let lang_items = tcx.lang_items();
+        let trait_def_id = goal.predicate.trait_def_id(tcx);
 
         // N.B. When assembling built-in candidates for lang items that are also
         // `auto` traits, then the auto trait candidate that is assembled in
@@ -430,9 +584,11 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         // Instead of adding the logic here, it's a better idea to add it in
         // `EvalCtxt::disqualify_auto_trait_candidate_due_to_possible_impl` in
         // `solve::trait_goals` instead.
-        let result = if self.tcx().trait_is_auto(trait_def_id) {
+        let result = if let Err(guar) = goal.predicate.error_reported() {
+            G::consider_error_guaranteed_candidate(self, guar)
+        } else if tcx.trait_is_auto(trait_def_id) {
             G::consider_auto_trait_candidate(self, goal)
-        } else if self.tcx().trait_is_alias(trait_def_id) {
+        } else if tcx.trait_is_alias(trait_def_id) {
             G::consider_trait_alias_candidate(self, goal)
         } else if lang_items.sized_trait() == Some(trait_def_id) {
             G::consider_builtin_sized_candidate(self, goal)
diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs
index d677fbdc7f4..222ed9939ba 100644
--- a/compiler/rustc_trait_selection/src/solve/project_goals.rs
+++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs
@@ -2,7 +2,6 @@ use crate::traits::specialization_graph;
 
 use super::assembly::{self, structural_traits};
 use super::EvalCtxt;
-use rustc_errors::ErrorGuaranteed;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::DefId;
 use rustc_hir::LangItem;
@@ -15,7 +14,7 @@ use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
 use rustc_middle::ty::ProjectionPredicate;
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_middle::ty::{ToPredicate, TypeVisitableExt};
-use rustc_span::{sym, DUMMY_SP};
+use rustc_span::{sym, ErrorGuaranteed, DUMMY_SP};
 
 impl<'tcx> EvalCtxt<'_, 'tcx> {
     #[instrument(level = "debug", skip(self), ret)]
@@ -246,6 +245,15 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
         })
     }
 
+    /// Fail to normalize if the predicate contains an error, alternatively, we could normalize to `ty::Error`
+    /// and succeed. Can experiment with this to figure out what results in better error messages.
+    fn consider_error_guaranteed_candidate(
+        _ecx: &mut EvalCtxt<'_, 'tcx>,
+        _guar: ErrorGuaranteed,
+    ) -> QueryResult<'tcx> {
+        Err(NoSolution)
+    }
+
     fn consider_auto_trait_candidate(
         ecx: &mut EvalCtxt<'_, 'tcx>,
         goal: Goal<'tcx, Self>,
diff --git a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs
index f00456e26df..98c8a74752c 100644
--- a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs
@@ -9,7 +9,9 @@ use cache::ProvisionalCache;
 use overflow::OverflowData;
 use rustc_index::IndexVec;
 use rustc_middle::dep_graph::DepKind;
-use rustc_middle::traits::solve::{CanonicalInput, Certainty, MaybeCause, QueryResult};
+use rustc_middle::traits::solve::{
+    CanonicalInput, Certainty, EvaluationCache, MaybeCause, QueryResult,
+};
 use rustc_middle::ty::TyCtxt;
 use std::{collections::hash_map::Entry, mem};
 
@@ -58,10 +60,10 @@ impl<'tcx> SearchGraph<'tcx> {
     ///
     /// We could add another global cache for coherence instead,
     /// but that's effort so let's only do it if necessary.
-    pub(super) fn should_use_global_cache(&self) -> bool {
+    pub(super) fn global_cache(&self, tcx: TyCtxt<'tcx>) -> &'tcx EvaluationCache<'tcx> {
         match self.mode {
-            SolverMode::Normal => true,
-            SolverMode::Coherence => false,
+            SolverMode::Normal => &tcx.new_solver_evaluation_cache,
+            SolverMode::Coherence => &tcx.new_solver_coherence_evaluation_cache,
         }
     }
 
@@ -213,8 +215,8 @@ impl<'tcx> SearchGraph<'tcx> {
         inspect: &mut ProofTreeBuilder<'tcx>,
         mut loop_body: impl FnMut(&mut Self, &mut ProofTreeBuilder<'tcx>) -> QueryResult<'tcx>,
     ) -> QueryResult<'tcx> {
-        if self.should_use_global_cache() && inspect.use_global_cache() {
-            if let Some(result) = tcx.new_solver_evaluation_cache.get(&canonical_input, tcx) {
+        if inspect.use_global_cache() {
+            if let Some(result) = self.global_cache(tcx).get(&canonical_input, tcx) {
                 debug!(?canonical_input, ?result, "cache hit");
                 inspect.cache_hit(CacheHit::Global);
                 return result;
@@ -278,13 +280,10 @@ impl<'tcx> SearchGraph<'tcx> {
             // dependencies, our non-root goal may no longer appear as child of the root goal.
             //
             // See https://github.com/rust-lang/rust/pull/108071 for some additional context.
-            let can_cache = !self.overflow_data.did_overflow() || self.stack.is_empty();
-            if self.should_use_global_cache() && can_cache {
-                tcx.new_solver_evaluation_cache.insert(
-                    current_goal.input,
-                    dep_node,
-                    current_goal.response,
-                );
+            let can_cache = inspect.use_global_cache()
+                && (!self.overflow_data.did_overflow() || self.stack.is_empty());
+            if can_cache {
+                self.global_cache(tcx).insert(current_goal.input, dep_node, current_goal.response)
             }
         }
 
diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs
index e7867eead15..761f5327f6d 100644
--- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs
+++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs
@@ -11,7 +11,7 @@ use rustc_middle::traits::Reveal;
 use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams, TreatProjections};
 use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt};
 use rustc_middle::ty::{TraitPredicate, TypeVisitableExt};
-use rustc_span::DUMMY_SP;
+use rustc_span::{ErrorGuaranteed, DUMMY_SP};
 
 impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     fn self_ty(self) -> Ty<'tcx> {
@@ -78,6 +78,13 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
         })
     }
 
+    fn consider_error_guaranteed_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        _guar: ErrorGuaranteed,
+    ) -> QueryResult<'tcx> {
+        ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+    }
+
     fn probe_and_match_goal_against_assumption(
         ecx: &mut EvalCtxt<'_, 'tcx>,
         goal: Goal<'tcx, Self>,
@@ -216,9 +223,20 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
             return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
         }
 
-        if let Ok(layout) = tcx.layout_of(key)
-            && layout.layout.is_pointer_like(&tcx.data_layout)
-        {
+        // 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 {
             // FIXME: We could make this faster by making a no-constraints response
             ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
         } else {
@@ -686,7 +704,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
             | ty::Tuple(_)
             | ty::Adt(_, _)
             // FIXME: Handling opaques here is kinda sus. Especially because we
-            // simplify them to PlaceholderSimplifiedType.
+            // simplify them to SimplifiedType::Placeholder.
             | ty::Alias(ty::Opaque, _) => {
                 let mut disqualifying_impl = None;
                 self.tcx().for_each_relevant_impl_treating_projections(
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 aa195d70a9f..f1d870269a6 100644
--- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
@@ -979,9 +979,20 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             return;
         }
 
-        if let Ok(layout) = tcx.layout_of(key)
-            && layout.layout.is_pointer_like(&tcx.data_layout)
-        {
+        // 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 {
             candidates.vec.push(BuiltinCandidate { has_nested: false });
         }
     }
diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs
index 78816fed017..b4f614e3e22 100644
--- a/compiler/rustc_trait_selection/src/traits/vtable.rs
+++ b/compiler/rustc_trait_selection/src/traits/vtable.rs
@@ -24,8 +24,18 @@ pub enum VtblSegment<'tcx> {
 pub fn prepare_vtable_segments<'tcx, T>(
     tcx: TyCtxt<'tcx>,
     trait_ref: ty::PolyTraitRef<'tcx>,
-    mut segment_visitor: impl FnMut(VtblSegment<'tcx>) -> ControlFlow<T>,
+    segment_visitor: impl FnMut(VtblSegment<'tcx>) -> ControlFlow<T>,
 ) -> Option<T> {
+    prepare_vtable_segments_inner(tcx, trait_ref, segment_visitor).break_value()
+}
+
+/// Helper for [`prepare_vtable_segments`] that returns `ControlFlow`,
+/// such that we can use `?` in the body.
+fn prepare_vtable_segments_inner<'tcx, T>(
+    tcx: TyCtxt<'tcx>,
+    trait_ref: ty::PolyTraitRef<'tcx>,
+    mut segment_visitor: impl FnMut(VtblSegment<'tcx>) -> ControlFlow<T>,
+) -> ControlFlow<T> {
     // The following constraints holds for the final arrangement.
     // 1. The whole virtual table of the first direct super trait is included as the
     //    the prefix. If this trait doesn't have any super traits, then this step
@@ -71,20 +81,18 @@ pub fn prepare_vtable_segments<'tcx, T>(
     //  N, N-vptr, O
 
     // emit dsa segment first.
-    if let ControlFlow::Break(v) = (segment_visitor)(VtblSegment::MetadataDSA) {
-        return Some(v);
-    }
+    segment_visitor(VtblSegment::MetadataDSA)?;
 
     let mut emit_vptr_on_new_entry = false;
     let mut visited = PredicateSet::new(tcx);
     let predicate = trait_ref.without_const().to_predicate(tcx);
     let mut stack: SmallVec<[(ty::PolyTraitRef<'tcx>, _, _); 5]> =
-        smallvec![(trait_ref, emit_vptr_on_new_entry, None)];
+        smallvec![(trait_ref, emit_vptr_on_new_entry, maybe_iter(None))];
     visited.insert(predicate);
 
     // the main traversal loop:
     // basically we want to cut the inheritance directed graph into a few non-overlapping slices of nodes
-    // that each node is emitted after all its descendents have been emitted.
+    // such that each node is emitted after all its descendants have been emitted.
     // so we convert the directed graph into a tree by skipping all previously visited nodes using a visited set.
     // this is done on the fly.
     // Each loop run emits a slice - it starts by find a "childless" unvisited node, backtracking upwards, and it
@@ -105,80 +113,81 @@ pub fn prepare_vtable_segments<'tcx, T>(
     // Loop run #1: Emitting the slice [D C] (in reverse order). No one has a next-sibling node.
     // Loop run #1: Stack after exiting out is []. Now the function exits.
 
-    loop {
+    'outer: loop {
         // dive deeper into the stack, recording the path
         'diving_in: loop {
-            if let Some((inner_most_trait_ref, _, _)) = stack.last() {
-                let inner_most_trait_ref = *inner_most_trait_ref;
-                let mut direct_super_traits_iter = tcx
-                    .super_predicates_of(inner_most_trait_ref.def_id())
-                    .predicates
-                    .into_iter()
-                    .filter_map(move |(pred, _)| {
-                        pred.subst_supertrait(tcx, &inner_most_trait_ref).as_trait_clause()
-                    });
+            let &(inner_most_trait_ref, _, _) = stack.last().unwrap();
+
+            let mut direct_super_traits_iter = tcx
+                .super_predicates_of(inner_most_trait_ref.def_id())
+                .predicates
+                .into_iter()
+                .filter_map(move |(pred, _)| {
+                    pred.subst_supertrait(tcx, &inner_most_trait_ref).as_trait_clause()
+                });
 
-                'diving_in_skip_visited_traits: loop {
-                    if let Some(next_super_trait) = direct_super_traits_iter.next() {
-                        if visited.insert(next_super_trait.to_predicate(tcx)) {
-                            // We're throwing away potential constness of super traits here.
-                            // FIXME: handle ~const super traits
-                            let next_super_trait = next_super_trait.map_bound(|t| t.trait_ref);
-                            stack.push((
-                                next_super_trait,
-                                emit_vptr_on_new_entry,
-                                Some(direct_super_traits_iter),
-                            ));
-                            break 'diving_in_skip_visited_traits;
-                        } else {
-                            continue 'diving_in_skip_visited_traits;
-                        }
-                    } else {
-                        break 'diving_in;
-                    }
+            // Find an unvisited supertrait
+            match direct_super_traits_iter
+                .find(|&super_trait| visited.insert(super_trait.to_predicate(tcx)))
+            {
+                // Push it to the stack for the next iteration of 'diving_in to pick up
+                Some(unvisited_super_trait) => {
+                    // We're throwing away potential constness of super traits here.
+                    // FIXME: handle ~const super traits
+                    let next_super_trait = unvisited_super_trait.map_bound(|t| t.trait_ref);
+                    stack.push((
+                        next_super_trait,
+                        emit_vptr_on_new_entry,
+                        maybe_iter(Some(direct_super_traits_iter)),
+                    ))
                 }
+
+                // There are no more unvisited direct super traits, dive-in finished
+                None => break 'diving_in,
             }
         }
 
-        // Other than the left-most path, vptr should be emitted for each trait.
-        emit_vptr_on_new_entry = true;
-
         // emit innermost item, move to next sibling and stop there if possible, otherwise jump to outer level.
-        'exiting_out: loop {
-            if let Some((inner_most_trait_ref, emit_vptr, siblings_opt)) = stack.last_mut() {
-                if let ControlFlow::Break(v) = (segment_visitor)(VtblSegment::TraitOwnEntries {
-                    trait_ref: *inner_most_trait_ref,
-                    emit_vptr: *emit_vptr,
-                }) {
-                    return Some(v);
-                }
+        while let Some((inner_most_trait_ref, emit_vptr, mut siblings)) = stack.pop() {
+            segment_visitor(VtblSegment::TraitOwnEntries {
+                trait_ref: inner_most_trait_ref,
+                emit_vptr,
+            })?;
+
+            // If we've emitted (fed to `segment_visitor`) a trait that has methods present in the vtable,
+            // we'll need to emit vptrs from now on.
+            if !emit_vptr_on_new_entry
+                && has_own_existential_vtable_entries(tcx, inner_most_trait_ref.def_id())
+            {
+                emit_vptr_on_new_entry = true;
+            }
 
-                'exiting_out_skip_visited_traits: loop {
-                    if let Some(siblings) = siblings_opt {
-                        if let Some(next_inner_most_trait_ref) = siblings.next() {
-                            if visited.insert(next_inner_most_trait_ref.to_predicate(tcx)) {
-                                // We're throwing away potential constness of super traits here.
-                                // FIXME: handle ~const super traits
-                                let next_inner_most_trait_ref =
-                                    next_inner_most_trait_ref.map_bound(|t| t.trait_ref);
-                                *inner_most_trait_ref = next_inner_most_trait_ref;
-                                *emit_vptr = emit_vptr_on_new_entry;
-                                break 'exiting_out;
-                            } else {
-                                continue 'exiting_out_skip_visited_traits;
-                            }
-                        }
-                    }
-                    stack.pop();
-                    continue 'exiting_out;
-                }
+            if let Some(next_inner_most_trait_ref) =
+                siblings.find(|&sibling| visited.insert(sibling.to_predicate(tcx)))
+            {
+                // We're throwing away potential constness of super traits here.
+                // FIXME: handle ~const super traits
+                let next_inner_most_trait_ref =
+                    next_inner_most_trait_ref.map_bound(|t| t.trait_ref);
+
+                stack.push((next_inner_most_trait_ref, emit_vptr_on_new_entry, siblings));
+
+                // just pushed a new trait onto the stack, so we need to go through its super traits
+                continue 'outer;
             }
-            // all done
-            return None;
         }
+
+        // the stack is empty, all done
+        return ControlFlow::Continue(());
     }
 }
 
+/// Turns option of iterator into an iterator (this is just flatten)
+fn maybe_iter<I: Iterator>(i: Option<I>) -> impl Iterator<Item = I::Item> {
+    // Flatten is bad perf-vise, we could probably implement a special case here that is better
+    i.into_iter().flatten()
+}
+
 fn dump_vtable_entries<'tcx>(
     tcx: TyCtxt<'tcx>,
     sp: Span,
@@ -192,11 +201,23 @@ fn dump_vtable_entries<'tcx>(
     });
 }
 
+fn has_own_existential_vtable_entries(tcx: TyCtxt<'_>, trait_def_id: DefId) -> bool {
+    own_existential_vtable_entries_iter(tcx, trait_def_id).next().is_some()
+}
+
 fn own_existential_vtable_entries(tcx: TyCtxt<'_>, trait_def_id: DefId) -> &[DefId] {
+    tcx.arena.alloc_from_iter(own_existential_vtable_entries_iter(tcx, trait_def_id))
+}
+
+fn own_existential_vtable_entries_iter(
+    tcx: TyCtxt<'_>,
+    trait_def_id: DefId,
+) -> impl Iterator<Item = DefId> + '_ {
     let trait_methods = tcx
         .associated_items(trait_def_id)
         .in_definition_order()
         .filter(|item| item.kind == ty::AssocKind::Fn);
+
     // Now list each method's DefId (for within its trait).
     let own_entries = trait_methods.filter_map(move |&trait_method| {
         debug!("own_existential_vtable_entry: trait_method={:?}", trait_method);
@@ -211,7 +232,7 @@ fn own_existential_vtable_entries(tcx: TyCtxt<'_>, trait_def_id: DefId) -> &[Def
         Some(def_id)
     });
 
-    tcx.arena.alloc_from_iter(own_entries.into_iter())
+    own_entries
 }
 
 /// Given a trait `trait_ref`, iterates the vtable entries
diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs
index b840ff184e0..da1eba68d53 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::Providers;
+use rustc_middle::query::{LocalCrate, Providers};
 use rustc_middle::ty::layout::{
     IntegerExt, LayoutCx, LayoutError, LayoutOf, TyAndLayout, MAX_SIMD_LANES,
 };
@@ -24,32 +24,28 @@ use crate::errors::{
 use crate::layout_sanity_check::sanity_check_layout;
 
 pub fn provide(providers: &mut Providers) {
-    *providers = Providers { layout_of, ..*providers };
+    *providers = Providers { layout_of, reference_niches_policy, ..*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, ty) = query.into_parts();
-    debug!(?ty);
-
+    let (param_env, unnormalized_ty) = query.into_parts();
     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)));
-        }
-    };
+    // `naive_layout_of` takes care of normalizing the type.
+    let naive = tcx.naive_layout_of(query)?;
+    let ty = naive.ty;
 
     if ty != unnormalized_ty {
         // Ensure this layout is also cached for the normalized type.
@@ -57,13 +53,11 @@ fn layout_of<'tcx>(
     }
 
     let cx = LayoutCx { tcx, param_env };
-
     let layout = layout_of_uncached(&cx, ty)?;
-    let layout = TyAndLayout { ty, layout };
 
+    let layout = TyAndLayout { ty, layout };
     record_layout_for_printing(&cx, layout);
-
-    sanity_check_layout(&cx, &layout);
+    sanity_check_layout(&cx, &layout, &naive);
 
     Ok(layout)
 }
@@ -83,12 +77,10 @@ fn univariant_uninterned<'tcx>(
     kind: StructKind,
 ) -> Result<LayoutS, &'tcx LayoutError<'tcx>> {
     let dl = cx.data_layout();
-    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)));
-    }
-
+    assert!(
+        !(repr.pack.is_some() && repr.align.is_some()),
+        "already rejected by `naive_layout_of`"
+    );
     cx.univariant(dl, fields, repr, kind).ok_or_else(|| error(cx, LayoutError::SizeOverflow(ty)))
 }
 
@@ -146,75 +138,35 @@ 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() {
-                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)));
-                    },
+                // 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,
                 };
 
-                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)));
-                }
+                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 Abi::Scalar(metadata) = metadata_layout.abi else {
-                    return Err(error(cx, LayoutError::Unknown(pointee)));
-                };
+                *data_ptr.valid_range_mut() =
+                    WrappingRange { start: min_addr.into(), end: max_addr.into() };
+            }
 
-                metadata
+            if let Some(metadata) = ptr_metadata_scalar(cx, pointee)? {
+                // Effectively a (ptr, meta) tuple.
+                tcx.mk_layout(cx.scalar_pair(data_ptr, metadata))
             } else {
-                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))
+                // No metadata, this is a thin pointer.
+                tcx.mk_layout(LayoutS::scalar(cx, data_ptr))
+            }
         }
 
         ty::Dynamic(_, _, ty::DynStar) => {
@@ -226,16 +178,8 @@ fn layout_of_uncached<'tcx>(
         }
 
         // Arrays and slices.
-        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)
+        ty::Array(element, count) => {
+            let count = compute_array_count(cx, count)
                 .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?;
             let element = cx.layout_of(element)?;
             let size = element
@@ -558,20 +502,104 @@ fn layout_of_uncached<'tcx>(
         }
 
         // 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::Alias(..)
+        | ty::Bound(..)
+        | ty::GeneratorWitness(..)
+        | ty::GeneratorWitnessMIR(..)
+        | ty::Infer(_)
+        | ty::Placeholder(..)
+        | ty::Param(_)
+        | ty::Error(_) => {
+            unreachable!("already rejected by `naive_layout_of`");
         }
+    })
+}
 
-        ty::Bound(..) | ty::GeneratorWitness(..) | ty::GeneratorWitnessMIR(..) | ty::Infer(_) => {
-            bug!("Layout::compute: unexpected type `{}`", 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;
         }
+    }
+
+    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) }
+    };
 
-        ty::Placeholder(..) | ty::Param(_) | ty::Error(_) => {
-            return Err(error(cx, LayoutError::Unknown(ty)));
+    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))),
+        }
+    }
 }
 
 /// 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
new file mode 100644
index 00000000000..3070ab59d53
--- /dev/null
+++ b/compiler/rustc_ty_utils/src/layout_naive.rs
@@ -0,0 +1,322 @@
+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 8633334381a..2e3fe4e7fb8 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, TyAndLayout},
+    layout::{LayoutCx, NaiveLayout, TyAndLayout},
     TyCtxt,
 };
 use rustc_target::abi::*;
@@ -10,6 +10,7 @@ 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) {
@@ -20,6 +21,10 @@ 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 55b8857ed39..e2db6a6993f 100644
--- a/compiler/rustc_ty_utils/src/lib.rs
+++ b/compiler/rustc_ty_utils/src/lib.rs
@@ -31,6 +31,7 @@ mod errors;
 mod implied_bounds;
 pub mod instance;
 mod layout;
+mod layout_naive;
 mod layout_sanity_check;
 mod needs_drop;
 mod opaque_types;
@@ -47,6 +48,7 @@ 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_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
diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs
index b3ec830a7d7..bf01b2082ed 100644
--- a/library/alloc/src/rc.rs
+++ b/library/alloc/src/rc.rs
@@ -661,10 +661,14 @@ impl<T> Rc<T> {
 
 impl<T, A: Allocator> Rc<T, A> {
     /// Returns a reference to the underlying allocator.
+    ///
+    /// Note: this is an associated function, which means that you have
+    /// to call it as `Rc::allocator(&r)` instead of `r.allocator()`. This
+    /// is so that there is no conflict with a method on the inner type.
     #[inline]
     #[unstable(feature = "allocator_api", issue = "32838")]
-    pub fn allocator(&self) -> &A {
-        &self.alloc
+    pub fn allocator(this: &Self) -> &A {
+        &this.alloc
     }
     /// Constructs a new `Rc` in the provided allocator.
     ///
diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs
index e00850eb5d8..c2202f2fce5 100644
--- a/library/alloc/src/sync.rs
+++ b/library/alloc/src/sync.rs
@@ -678,10 +678,14 @@ impl<T> Arc<T> {
 
 impl<T, A: Allocator> Arc<T, A> {
     /// Returns a reference to the underlying allocator.
+    ///
+    /// Note: this is an associated function, which means that you have
+    /// to call it as `Arc::allocator(&a)` instead of `a.allocator()`. This
+    /// is so that there is no conflict with a method on the inner type.
     #[inline]
     #[unstable(feature = "allocator_api", issue = "32838")]
-    pub fn allocator(&self) -> &A {
-        &self.alloc
+    pub fn allocator(this: &Self) -> &A {
+        &this.alloc
     }
     /// Constructs a new `Arc<T>` in the provided allocator.
     ///
diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs
index b492d2f07bc..a8074c8659b 100644
--- a/library/core/src/ptr/non_null.rs
+++ b/library/core/src/ptr/non_null.rs
@@ -462,6 +462,30 @@ impl<T: ?Sized> NonNull<T> {
         // And the caller promised the `delta` is sound to add.
         unsafe { NonNull { pointer: self.pointer.add(delta) } }
     }
+
+    /// See [`pointer::sub`] for semantics and safety requirements.
+    #[inline]
+    pub(crate) const unsafe fn sub(self, delta: usize) -> Self
+    where
+        T: Sized,
+    {
+        // SAFETY: We require that the delta stays in-bounds of the object, and
+        // thus it cannot become null, as no legal objects can be allocated
+        // in such as way that the null address is part of them.
+        // And the caller promised the `delta` is sound to subtract.
+        unsafe { NonNull { pointer: self.pointer.sub(delta) } }
+    }
+
+    /// See [`pointer::sub_ptr`] for semantics and safety requirements.
+    #[inline]
+    pub(crate) const unsafe fn sub_ptr(self, subtrahend: Self) -> usize
+    where
+        T: Sized,
+    {
+        // SAFETY: The caller promised that this is safe to do, and
+        // the non-nullness is irrelevant to the operation.
+        unsafe { self.pointer.sub_ptr(subtrahend.pointer) }
+    }
 }
 
 impl<T> NonNull<[T]> {
diff --git a/library/core/src/slice/iter.rs b/library/core/src/slice/iter.rs
index 5369fe0a9a9..cc931355318 100644
--- a/library/core/src/slice/iter.rs
+++ b/library/core/src/slice/iter.rs
@@ -13,7 +13,7 @@ use crate::iter::{
 use crate::marker::{PhantomData, Send, Sized, Sync};
 use crate::mem::{self, SizedTypeProperties};
 use crate::num::NonZeroUsize;
-use crate::ptr::{invalid, invalid_mut, NonNull};
+use crate::ptr::{self, invalid, invalid_mut, NonNull};
 
 use super::{from_raw_parts, from_raw_parts_mut};
 
@@ -68,7 +68,7 @@ pub struct Iter<'a, T: 'a> {
     /// For non-ZSTs, the non-null pointer to the past-the-end element.
     ///
     /// For ZSTs, this is `ptr::invalid(len)`.
-    end: *const T,
+    end_or_len: *const T,
     _marker: PhantomData<&'a T>,
 }
 
@@ -90,9 +90,9 @@ impl<'a, T> Iter<'a, T> {
         let ptr = slice.as_ptr();
         // SAFETY: Similar to `IterMut::new`.
         unsafe {
-            let end = if T::IS_ZST { invalid(slice.len()) } else { ptr.add(slice.len()) };
+            let end_or_len = if T::IS_ZST { invalid(slice.len()) } else { ptr.add(slice.len()) };
 
-            Self { ptr: NonNull::new_unchecked(ptr as *mut T), end, _marker: PhantomData }
+            Self { ptr: NonNull::new_unchecked(ptr as *mut T), end_or_len, _marker: PhantomData }
         }
     }
 
@@ -128,7 +128,7 @@ impl<'a, T> Iter<'a, T> {
     }
 }
 
-iterator! {struct Iter -> *const T, &'a T, const, {/* no mut */}, {
+iterator! {struct Iter -> *const T, &'a T, const, {/* no mut */}, as_ref, {
     fn is_sorted_by<F>(self, mut compare: F) -> bool
     where
         Self: Sized,
@@ -142,7 +142,7 @@ iterator! {struct Iter -> *const T, &'a T, const, {/* no mut */}, {
 impl<T> Clone for Iter<'_, T> {
     #[inline]
     fn clone(&self) -> Self {
-        Iter { ptr: self.ptr, end: self.end, _marker: self._marker }
+        Iter { ptr: self.ptr, end_or_len: self.end_or_len, _marker: self._marker }
     }
 }
 
@@ -189,7 +189,7 @@ pub struct IterMut<'a, T: 'a> {
     /// For non-ZSTs, the non-null pointer to the past-the-end element.
     ///
     /// For ZSTs, this is `ptr::invalid_mut(len)`.
-    end: *mut T,
+    end_or_len: *mut T,
     _marker: PhantomData<&'a mut T>,
 }
 
@@ -220,15 +220,16 @@ impl<'a, T> IterMut<'a, T> {
         // for direct pointer equality with `ptr` to check if the iterator is
         // done.
         //
-        // In the case of a ZST, the end pointer is just the start pointer plus
-        // the length, to also allows for the fast `ptr == end` check.
+        // In the case of a ZST, the end pointer is just the length.  It's never
+        // used as a pointer at all, and thus it's fine to have no provenance.
         //
         // See the `next_unchecked!` and `is_empty!` macros as well as the
         // `post_inc_start` method for more information.
         unsafe {
-            let end = if T::IS_ZST { invalid_mut(slice.len()) } else { ptr.add(slice.len()) };
+            let end_or_len =
+                if T::IS_ZST { invalid_mut(slice.len()) } else { ptr.add(slice.len()) };
 
-            Self { ptr: NonNull::new_unchecked(ptr), end, _marker: PhantomData }
+            Self { ptr: NonNull::new_unchecked(ptr), end_or_len, _marker: PhantomData }
         }
     }
 
@@ -360,7 +361,7 @@ impl<T> AsRef<[T]> for IterMut<'_, T> {
 //     }
 // }
 
-iterator! {struct IterMut -> *mut T, &'a mut T, mut, {mut}, {}}
+iterator! {struct IterMut -> *mut T, &'a mut T, mut, {mut}, as_mut, {}}
 
 /// An internal abstraction over the splitting iterators, so that
 /// splitn, splitn_mut etc can be implemented once.
diff --git a/library/core/src/slice/iter/macros.rs b/library/core/src/slice/iter/macros.rs
index 96a145e22ed..95bcd123b82 100644
--- a/library/core/src/slice/iter/macros.rs
+++ b/library/core/src/slice/iter/macros.rs
@@ -1,45 +1,62 @@
 //! Macros used by iterators of slice.
 
-// Shrinks the iterator when T is a ZST, setting the length to `new_len`.
-// `new_len` must not exceed `self.len()`.
-macro_rules! zst_set_len {
-    ($self: ident, $new_len: expr) => {{
+/// Convenience & performance macro for consuming the `end_or_len` field, by
+/// giving a `(&mut) usize` or `(&mut) NonNull<T>` depending whether `T` is
+/// or is not a ZST respectively.
+///
+/// Internally, this reads the `end` through a pointer-to-`NonNull` so that
+/// it'll get the appropriate non-null metadata in the backend without needing
+/// to call `assume` manually.
+macro_rules! if_zst {
+    (mut $this:ident, $len:ident => $zst_body:expr, $end:ident => $other_body:expr,) => {{
         #![allow(unused_unsafe)] // we're sometimes used within an unsafe block
 
-        // SAFETY: same as `invalid(_mut)`, but the macro doesn't know
-        // which versions of that function to call, so open-code it.
-        $self.end = unsafe { mem::transmute::<usize, _>($new_len) };
+        if T::IS_ZST {
+            // SAFETY: for ZSTs, the pointer is storing a provenance-free length,
+            // so consuming and updating it as a `usize` is fine.
+            let $len = unsafe { &mut *ptr::addr_of_mut!($this.end_or_len).cast::<usize>() };
+            $zst_body
+        } else {
+            // SAFETY: for non-ZSTs, the type invariant ensures it cannot be null
+            let $end = unsafe { &mut *ptr::addr_of_mut!($this.end_or_len).cast::<NonNull<T>>() };
+            $other_body
+        }
     }};
-}
+    ($this:ident, $len:ident => $zst_body:expr, $end:ident => $other_body:expr,) => {{
+        #![allow(unused_unsafe)] // we're sometimes used within an unsafe block
 
-// Shrinks the iterator when T is a ZST, reducing the length by `n`.
-// `n` must not exceed `self.len()`.
-macro_rules! zst_shrink {
-    ($self: ident, $n: ident) => {
-        let new_len = $self.end.addr() - $n;
-        zst_set_len!($self, new_len);
-    };
+        if T::IS_ZST {
+            let $len = $this.end_or_len.addr();
+            $zst_body
+        } else {
+            // SAFETY: for non-ZSTs, the type invariant ensures it cannot be null
+            let $end = unsafe { *ptr::addr_of!($this.end_or_len).cast::<NonNull<T>>() };
+            $other_body
+        }
+    }};
 }
 
 // Inlining is_empty and len makes a huge performance difference
 macro_rules! is_empty {
     ($self: ident) => {
-        if T::IS_ZST { $self.end.addr() == 0 } else { $self.ptr.as_ptr() as *const _ == $self.end }
+        if_zst!($self,
+            len => len == 0,
+            end => $self.ptr == end,
+        )
     };
 }
 
 macro_rules! len {
     ($self: ident) => {{
-        #![allow(unused_unsafe)] // we're sometimes used within an unsafe block
-
-        if T::IS_ZST {
-            $self.end.addr()
-        } else {
-            // To get rid of some bounds checks (see `position`), we use ptr_sub instead of
-            // offset_from (Tested by `codegen/slice-position-bounds-check`.)
-            // SAFETY: by the type invariant pointers are aligned and `start <= end`
-            unsafe { $self.end.sub_ptr($self.ptr.as_ptr()) }
-        }
+        if_zst!($self,
+            len => len,
+            end => {
+                // To get rid of some bounds checks (see `position`), we use ptr_sub instead of
+                // offset_from (Tested by `codegen/slice-position-bounds-check`.)
+                // SAFETY: by the type invariant pointers are aligned and `start <= end`
+                unsafe { end.sub_ptr($self.ptr) }
+            },
+        )
     }};
 }
 
@@ -50,20 +67,21 @@ macro_rules! iterator {
         $elem:ty,
         $raw_mut:tt,
         {$( $mut_:tt )?},
+        $into_ref:ident,
         {$($extra:tt)*}
     ) => {
         // Returns the first element and moves the start of the iterator forwards by 1.
         // Greatly improves performance compared to an inlined function. The iterator
         // must not be empty.
         macro_rules! next_unchecked {
-            ($self: ident) => {& $( $mut_ )? *$self.post_inc_start(1)}
+            ($self: ident) => { $self.post_inc_start(1).$into_ref() }
         }
 
         // Returns the last element and moves the end of the iterator backwards by 1.
         // Greatly improves performance compared to an inlined function. The iterator
         // must not be empty.
         macro_rules! next_back_unchecked {
-            ($self: ident) => {& $( $mut_ )? *$self.pre_dec_end(1)}
+            ($self: ident) => { $self.pre_dec_end(1).$into_ref() }
         }
 
         impl<'a, T> $name<'a, T> {
@@ -80,33 +98,40 @@ macro_rules! iterator {
             // returning the old start.
             // Unsafe because the offset must not exceed `self.len()`.
             #[inline(always)]
-            unsafe fn post_inc_start(&mut self, offset: usize) -> * $raw_mut T {
+            unsafe fn post_inc_start(&mut self, offset: usize) -> NonNull<T> {
                 let old = self.ptr;
-                if T::IS_ZST {
-                    zst_shrink!(self, offset);
-                } else {
-                    // SAFETY: the caller guarantees that `offset` doesn't exceed `self.len()`,
-                    // so this new pointer is inside `self` and thus guaranteed to be non-null.
-                    self.ptr = unsafe { self.ptr.add(offset) };
+
+                // SAFETY: the caller guarantees that `offset` doesn't exceed `self.len()`,
+                // so this new pointer is inside `self` and thus guaranteed to be non-null.
+                unsafe {
+                    if_zst!(mut self,
+                        len => *len = len.unchecked_sub(offset),
+                        _end => self.ptr = self.ptr.add(offset),
+                    );
                 }
-                old.as_ptr()
+                old
             }
 
             // Helper function for moving the end of the iterator backwards by `offset` elements,
             // returning the new end.
             // Unsafe because the offset must not exceed `self.len()`.
             #[inline(always)]
-            unsafe fn pre_dec_end(&mut self, offset: usize) -> * $raw_mut T {
-                if T::IS_ZST {
-                    zst_shrink!(self, offset);
-                    self.ptr.as_ptr()
-                } else {
+            unsafe fn pre_dec_end(&mut self, offset: usize) -> NonNull<T> {
+                if_zst!(mut self,
+                    // SAFETY: By our precondition, `offset` can be at most the
+                    // current length, so the subtraction can never overflow.
+                    len => unsafe {
+                        *len = len.unchecked_sub(offset);
+                        self.ptr
+                    },
                     // SAFETY: the caller guarantees that `offset` doesn't exceed `self.len()`,
                     // which is guaranteed to not overflow an `isize`. Also, the resulting pointer
                     // is in bounds of `slice`, which fulfills the other requirements for `offset`.
-                    self.end = unsafe { self.end.sub(offset) };
-                    self.end
-                }
+                    end => unsafe {
+                        *end = end.sub(offset);
+                        *end
+                    },
+                )
             }
         }
 
@@ -131,13 +156,9 @@ macro_rules! iterator {
             fn next(&mut self) -> Option<$elem> {
                 // could be implemented with slices, but this avoids bounds checks
 
-                // SAFETY: `assume` call is safe because slices over non-ZSTs must
-                // have a non-null end pointer. The call to `next_unchecked!` is
+                // SAFETY: The call to `next_unchecked!` is
                 // safe since we check if the iterator is empty first.
                 unsafe {
-                    if !<T>::IS_ZST {
-                        assume(!self.end.is_null());
-                    }
                     if is_empty!(self) {
                         None
                     } else {
@@ -161,14 +182,10 @@ macro_rules! iterator {
             fn nth(&mut self, n: usize) -> Option<$elem> {
                 if n >= len!(self) {
                     // This iterator is now empty.
-                    if T::IS_ZST {
-                        zst_set_len!(self, 0);
-                    } else {
-                        // SAFETY: end can't be 0 if T isn't ZST because ptr isn't 0 and end >= ptr
-                        unsafe {
-                            self.ptr = NonNull::new_unchecked(self.end as *mut T);
-                        }
-                    }
+                    if_zst!(mut self,
+                        len => *len = 0,
+                        end => self.ptr = *end,
+                    );
                     return None;
                 }
                 // SAFETY: We are in bounds. `post_inc_start` does the right thing even for ZSTs.
@@ -375,13 +392,9 @@ macro_rules! iterator {
             fn next_back(&mut self) -> Option<$elem> {
                 // could be implemented with slices, but this avoids bounds checks
 
-                // SAFETY: `assume` call is safe because slices over non-ZSTs must
-                // have a non-null end pointer. The call to `next_back_unchecked!`
+                // SAFETY: The call to `next_back_unchecked!`
                 // is safe since we check if the iterator is empty first.
                 unsafe {
-                    if !<T>::IS_ZST {
-                        assume(!self.end.is_null());
-                    }
                     if is_empty!(self) {
                         None
                     } else {
@@ -394,11 +407,10 @@ macro_rules! iterator {
             fn nth_back(&mut self, n: usize) -> Option<$elem> {
                 if n >= len!(self) {
                     // This iterator is now empty.
-                    if T::IS_ZST {
-                        zst_set_len!(self, 0);
-                    } else {
-                        self.end = self.ptr.as_ptr();
-                    }
+                    if_zst!(mut self,
+                        len => *len = 0,
+                        end => *end = self.ptr,
+                    );
                     return None;
                 }
                 // SAFETY: We are in bounds. `pre_dec_end` does the right thing even for ZSTs.
diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs
index 236b7f423d6..092b097c396 100644
--- a/library/core/src/sync/atomic.rs
+++ b/library/core/src/sync/atomic.rs
@@ -1958,14 +1958,12 @@ macro_rules! atomic_int {
      $stable_from:meta,
      $stable_nand:meta,
      $const_stable:meta,
-     $stable_init_const:meta,
      $diagnostic_item:meta,
      $s_int_type:literal,
      $extra_feature:expr,
      $min_fn:ident, $max_fn:ident,
      $align:expr,
-     $atomic_new:expr,
-     $int_type:ident $atomic_type:ident $atomic_init:ident) => {
+     $int_type:ident $atomic_type:ident) => {
         /// An integer type which can be safely shared between threads.
         ///
         /// This type has the same in-memory representation as the underlying
@@ -1988,15 +1986,6 @@ macro_rules! atomic_int {
             v: UnsafeCell<$int_type>,
         }
 
-        /// An atomic integer initialized to `0`.
-        #[$stable_init_const]
-        #[deprecated(
-            since = "1.34.0",
-            note = "the `new` function is now preferred",
-            suggestion = $atomic_new,
-        )]
-        pub const $atomic_init: $atomic_type = $atomic_type::new(0);
-
         #[$stable]
         impl Default for $atomic_type {
             #[inline]
@@ -2874,14 +2863,12 @@ atomic_int! {
     stable(feature = "integer_atomics_stable", since = "1.34.0"),
     stable(feature = "integer_atomics_stable", since = "1.34.0"),
     rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"),
-    unstable(feature = "integer_atomics", issue = "99069"),
     cfg_attr(not(test), rustc_diagnostic_item = "AtomicI8"),
     "i8",
     "",
     atomic_min, atomic_max,
     1,
-    "AtomicI8::new(0)",
-    i8 AtomicI8 ATOMIC_I8_INIT
+    i8 AtomicI8
 }
 #[cfg(target_has_atomic_load_store = "8")]
 atomic_int! {
@@ -2894,14 +2881,12 @@ atomic_int! {
     stable(feature = "integer_atomics_stable", since = "1.34.0"),
     stable(feature = "integer_atomics_stable", since = "1.34.0"),
     rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"),
-    unstable(feature = "integer_atomics", issue = "99069"),
     cfg_attr(not(test), rustc_diagnostic_item = "AtomicU8"),
     "u8",
     "",
     atomic_umin, atomic_umax,
     1,
-    "AtomicU8::new(0)",
-    u8 AtomicU8 ATOMIC_U8_INIT
+    u8 AtomicU8
 }
 #[cfg(target_has_atomic_load_store = "16")]
 atomic_int! {
@@ -2914,14 +2899,12 @@ atomic_int! {
     stable(feature = "integer_atomics_stable", since = "1.34.0"),
     stable(feature = "integer_atomics_stable", since = "1.34.0"),
     rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"),
-    unstable(feature = "integer_atomics", issue = "99069"),
     cfg_attr(not(test), rustc_diagnostic_item = "AtomicI16"),
     "i16",
     "",
     atomic_min, atomic_max,
     2,
-    "AtomicI16::new(0)",
-    i16 AtomicI16 ATOMIC_I16_INIT
+    i16 AtomicI16
 }
 #[cfg(target_has_atomic_load_store = "16")]
 atomic_int! {
@@ -2934,14 +2917,12 @@ atomic_int! {
     stable(feature = "integer_atomics_stable", since = "1.34.0"),
     stable(feature = "integer_atomics_stable", since = "1.34.0"),
     rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"),
-    unstable(feature = "integer_atomics", issue = "99069"),
     cfg_attr(not(test), rustc_diagnostic_item = "AtomicU16"),
     "u16",
     "",
     atomic_umin, atomic_umax,
     2,
-    "AtomicU16::new(0)",
-    u16 AtomicU16 ATOMIC_U16_INIT
+    u16 AtomicU16
 }
 #[cfg(target_has_atomic_load_store = "32")]
 atomic_int! {
@@ -2954,14 +2935,12 @@ atomic_int! {
     stable(feature = "integer_atomics_stable", since = "1.34.0"),
     stable(feature = "integer_atomics_stable", since = "1.34.0"),
     rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"),
-    unstable(feature = "integer_atomics", issue = "99069"),
     cfg_attr(not(test), rustc_diagnostic_item = "AtomicI32"),
     "i32",
     "",
     atomic_min, atomic_max,
     4,
-    "AtomicI32::new(0)",
-    i32 AtomicI32 ATOMIC_I32_INIT
+    i32 AtomicI32
 }
 #[cfg(target_has_atomic_load_store = "32")]
 atomic_int! {
@@ -2974,14 +2953,12 @@ atomic_int! {
     stable(feature = "integer_atomics_stable", since = "1.34.0"),
     stable(feature = "integer_atomics_stable", since = "1.34.0"),
     rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"),
-    unstable(feature = "integer_atomics", issue = "99069"),
     cfg_attr(not(test), rustc_diagnostic_item = "AtomicU32"),
     "u32",
     "",
     atomic_umin, atomic_umax,
     4,
-    "AtomicU32::new(0)",
-    u32 AtomicU32 ATOMIC_U32_INIT
+    u32 AtomicU32
 }
 #[cfg(target_has_atomic_load_store = "64")]
 atomic_int! {
@@ -2994,14 +2971,12 @@ atomic_int! {
     stable(feature = "integer_atomics_stable", since = "1.34.0"),
     stable(feature = "integer_atomics_stable", since = "1.34.0"),
     rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"),
-    unstable(feature = "integer_atomics", issue = "99069"),
     cfg_attr(not(test), rustc_diagnostic_item = "AtomicI64"),
     "i64",
     "",
     atomic_min, atomic_max,
     8,
-    "AtomicI64::new(0)",
-    i64 AtomicI64 ATOMIC_I64_INIT
+    i64 AtomicI64
 }
 #[cfg(target_has_atomic_load_store = "64")]
 atomic_int! {
@@ -3014,14 +2989,12 @@ atomic_int! {
     stable(feature = "integer_atomics_stable", since = "1.34.0"),
     stable(feature = "integer_atomics_stable", since = "1.34.0"),
     rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"),
-    unstable(feature = "integer_atomics", issue = "99069"),
     cfg_attr(not(test), rustc_diagnostic_item = "AtomicU64"),
     "u64",
     "",
     atomic_umin, atomic_umax,
     8,
-    "AtomicU64::new(0)",
-    u64 AtomicU64 ATOMIC_U64_INIT
+    u64 AtomicU64
 }
 #[cfg(target_has_atomic_load_store = "128")]
 atomic_int! {
@@ -3034,14 +3007,12 @@ atomic_int! {
     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! {
@@ -3054,19 +3025,17 @@ atomic_int! {
     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)* ) => { $(
-        #[cfg(target_has_atomic_load_store = "ptr")]
         #[cfg(target_pointer_width = $target_pointer_width)]
         atomic_int! {
             cfg(target_has_atomic = "ptr"),
@@ -3078,16 +3047,13 @@ macro_rules! atomic_int_ptr_sized {
             stable(feature = "atomic_from", since = "1.23.0"),
             stable(feature = "atomic_nand", since = "1.27.0"),
             rustc_const_stable(feature = "const_ptr_sized_atomics", since = "1.24.0"),
-            stable(feature = "rust1", since = "1.0.0"),
             cfg_attr(not(test), rustc_diagnostic_item = "AtomicIsize"),
             "isize",
             "",
             atomic_min, atomic_max,
             $align,
-            "AtomicIsize::new(0)",
-            isize AtomicIsize ATOMIC_ISIZE_INIT
+            isize AtomicIsize
         }
-        #[cfg(target_has_atomic_load_store = "ptr")]
         #[cfg(target_pointer_width = $target_pointer_width)]
         atomic_int! {
             cfg(target_has_atomic = "ptr"),
@@ -3099,18 +3065,37 @@ macro_rules! atomic_int_ptr_sized {
             stable(feature = "atomic_from", since = "1.23.0"),
             stable(feature = "atomic_nand", since = "1.27.0"),
             rustc_const_stable(feature = "const_ptr_sized_atomics", since = "1.24.0"),
-            stable(feature = "rust1", since = "1.0.0"),
             cfg_attr(not(test), rustc_diagnostic_item = "AtomicUsize"),
             "usize",
             "",
             atomic_umin, atomic_umax,
             $align,
-            "AtomicUsize::new(0)",
-            usize AtomicUsize ATOMIC_USIZE_INIT
+            usize AtomicUsize
         }
+
+        /// An [`AtomicIsize`] initialized to `0`.
+        #[cfg(target_pointer_width = $target_pointer_width)]
+        #[stable(feature = "rust1", since = "1.0.0")]
+        #[deprecated(
+            since = "1.34.0",
+            note = "the `new` function is now preferred",
+            suggestion = "AtomicIsize::new(0)",
+        )]
+        pub const ATOMIC_ISIZE_INIT: AtomicIsize = AtomicIsize::new(0);
+
+        /// An [`AtomicUsize`] initialized to `0`.
+        #[cfg(target_pointer_width = $target_pointer_width)]
+        #[stable(feature = "rust1", since = "1.0.0")]
+        #[deprecated(
+            since = "1.34.0",
+            note = "the `new` function is now preferred",
+            suggestion = "AtomicUsize::new(0)",
+        )]
+        pub const ATOMIC_USIZE_INIT: AtomicUsize = AtomicUsize::new(0);
     )* };
 }
 
+#[cfg(target_has_atomic_load_store = "ptr")]
 atomic_int_ptr_sized! {
     "16" 2
     "32" 4
diff --git a/library/core/tests/iter/traits/iterator.rs b/library/core/tests/iter/traits/iterator.rs
index 9eebfb1f1f3..995bbf0e261 100644
--- a/library/core/tests/iter/traits/iterator.rs
+++ b/library/core/tests/iter/traits/iterator.rs
@@ -1,3 +1,4 @@
+use core::cmp::Ordering;
 use core::num::NonZeroUsize;
 
 /// A wrapper struct that implements `Eq` and `Ord` based on the wrapped
@@ -371,11 +372,39 @@ fn test_by_ref() {
 
 #[test]
 fn test_is_sorted() {
+    // Tests on integers
     assert!([1, 2, 2, 9].iter().is_sorted());
     assert!(![1, 3, 2].iter().is_sorted());
     assert!([0].iter().is_sorted());
-    assert!(std::iter::empty::<i32>().is_sorted());
+    assert!([0, 0].iter().is_sorted());
+    assert!(core::iter::empty::<i32>().is_sorted());
+
+    // Tests on floats
+    assert!([1.0f32, 2.0, 2.0, 9.0].iter().is_sorted());
+    assert!(![1.0f32, 3.0f32, 2.0f32].iter().is_sorted());
+    assert!([0.0f32].iter().is_sorted());
+    assert!([0.0f32, 0.0f32].iter().is_sorted());
+    // Test cases with NaNs
+    assert!([f32::NAN].iter().is_sorted());
+    assert!(![f32::NAN, f32::NAN].iter().is_sorted());
     assert!(![0.0, 1.0, f32::NAN].iter().is_sorted());
+    // Tests from <https://github.com/rust-lang/rust/pull/55045#discussion_r229689884>
+    assert!(![f32::NAN, f32::NAN, f32::NAN].iter().is_sorted());
+    assert!(![1.0, f32::NAN, 2.0].iter().is_sorted());
+    assert!(![2.0, f32::NAN, 1.0].iter().is_sorted());
+    assert!(![2.0, f32::NAN, 1.0, 7.0].iter().is_sorted());
+    assert!(![2.0, f32::NAN, 1.0, 0.0].iter().is_sorted());
+    assert!(![-f32::NAN, -1.0, 0.0, 1.0, f32::NAN].iter().is_sorted());
+    assert!(![f32::NAN, -f32::NAN, -1.0, 0.0, 1.0].iter().is_sorted());
+    assert!(![1.0, f32::NAN, -f32::NAN, -1.0, 0.0].iter().is_sorted());
+    assert!(![0.0, 1.0, f32::NAN, -f32::NAN, -1.0].iter().is_sorted());
+    assert!(![-1.0, 0.0, 1.0, f32::NAN, -f32::NAN].iter().is_sorted());
+
+    // Tests for is_sorted_by
+    assert!(![6, 2, 8, 5, 1, -60, 1337].iter().is_sorted());
+    assert!([6, 2, 8, 5, 1, -60, 1337].iter().is_sorted_by(|_, _| Some(Ordering::Less)));
+
+    // Tests for is_sorted_by_key
     assert!([-2, -1, 0, 3].iter().is_sorted());
     assert!(![-2i32, -1, 0, 3].iter().is_sorted_by_key(|n| n.abs()));
     assert!(!["c", "bb", "aaa"].iter().is_sorted());
diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs
index 3e6d31fcd2f..897a5e9b870 100644
--- a/library/core/tests/lib.rs
+++ b/library/core/tests/lib.rs
@@ -93,7 +93,7 @@
 #![feature(const_option)]
 #![feature(const_option_ext)]
 #![feature(const_result)]
-#![feature(integer_atomics)]
+#![cfg_attr(target_has_atomic = "128", feature(integer_atomics))]
 #![feature(int_roundings)]
 #![feature(slice_group_by)]
 #![feature(split_array)]
diff --git a/library/core/tests/slice.rs b/library/core/tests/slice.rs
index 88f54591bb4..865e702b5c2 100644
--- a/library/core/tests/slice.rs
+++ b/library/core/tests/slice.rs
@@ -2278,11 +2278,39 @@ fn test_copy_within_panics_src_out_of_bounds() {
 fn test_is_sorted() {
     let empty: [i32; 0] = [];
 
+    // Tests on integers
     assert!([1, 2, 2, 9].is_sorted());
     assert!(![1, 3, 2].is_sorted());
     assert!([0].is_sorted());
+    assert!([0, 0].is_sorted());
     assert!(empty.is_sorted());
+
+    // Tests on floats
+    assert!([1.0f32, 2.0, 2.0, 9.0].is_sorted());
+    assert!(![1.0f32, 3.0f32, 2.0f32].is_sorted());
+    assert!([0.0f32].is_sorted());
+    assert!([0.0f32, 0.0f32].is_sorted());
+    // Test cases with NaNs
+    assert!([f32::NAN].is_sorted());
+    assert!(![f32::NAN, f32::NAN].is_sorted());
     assert!(![0.0, 1.0, f32::NAN].is_sorted());
+    // Tests from <https://github.com/rust-lang/rust/pull/55045#discussion_r229689884>
+    assert!(![f32::NAN, f32::NAN, f32::NAN].is_sorted());
+    assert!(![1.0, f32::NAN, 2.0].is_sorted());
+    assert!(![2.0, f32::NAN, 1.0].is_sorted());
+    assert!(![2.0, f32::NAN, 1.0, 7.0].is_sorted());
+    assert!(![2.0, f32::NAN, 1.0, 0.0].is_sorted());
+    assert!(![-f32::NAN, -1.0, 0.0, 1.0, f32::NAN].is_sorted());
+    assert!(![f32::NAN, -f32::NAN, -1.0, 0.0, 1.0].is_sorted());
+    assert!(![1.0, f32::NAN, -f32::NAN, -1.0, 0.0].is_sorted());
+    assert!(![0.0, 1.0, f32::NAN, -f32::NAN, -1.0].is_sorted());
+    assert!(![-1.0, 0.0, 1.0, f32::NAN, -f32::NAN].is_sorted());
+
+    // Tests for is_sorted_by
+    assert!(![6, 2, 8, 5, 1, -60, 1337].is_sorted());
+    assert!([6, 2, 8, 5, 1, -60, 1337].is_sorted_by(|_, _| Some(Ordering::Less)));
+
+    // Tests for is_sorted_by_key
     assert!([-2, -1, 0, 3].is_sorted());
     assert!(![-2i32, -1, 0, 3].is_sorted_by_key(|n| n.abs()));
     assert!(!["c", "bb", "aaa"].is_sorted());
diff --git a/library/std/src/path.rs b/library/std/src/path.rs
index 28cd3c4e4db..99f7a60f8ab 100644
--- a/library/std/src/path.rs
+++ b/library/std/src/path.rs
@@ -2608,9 +2608,27 @@ impl Path {
     }
 
     fn _with_extension(&self, extension: &OsStr) -> PathBuf {
-        let mut buf = self.to_path_buf();
-        buf.set_extension(extension);
-        buf
+        let self_len = self.as_os_str().len();
+        let self_bytes = self.as_os_str().as_os_str_bytes();
+
+        let (new_capacity, slice_to_copy) = match self.extension() {
+            None => {
+                // Enough capacity for the extension and the dot
+                let capacity = self_len + extension.len() + 1;
+                let whole_path = self_bytes.iter();
+                (capacity, whole_path)
+            }
+            Some(previous_extension) => {
+                let capacity = self_len + extension.len() - previous_extension.len();
+                let path_till_dot = self_bytes[..self_len - previous_extension.len()].iter();
+                (capacity, path_till_dot)
+            }
+        };
+
+        let mut new_path = PathBuf::with_capacity(new_capacity);
+        new_path.as_mut_vec().extend(slice_to_copy);
+        new_path.set_extension(extension);
+        new_path
     }
 
     /// Produces an iterator over the [`Component`]s of the path.
diff --git a/library/std/src/path/tests.rs b/library/std/src/path/tests.rs
index dd307022c6d..f12ffbf2e01 100644
--- a/library/std/src/path/tests.rs
+++ b/library/std/src/path/tests.rs
@@ -1183,7 +1183,7 @@ pub fn test_prefix_ext() {
 #[test]
 pub fn test_push() {
     macro_rules! tp (
-        ($path:expr, $push:expr, $expected:expr) => ( {
+        ($path:expr, $push:expr, $expected:expr) => ({
             let mut actual = PathBuf::from($path);
             actual.push($push);
             assert!(actual.to_str() == Some($expected),
@@ -1281,7 +1281,7 @@ pub fn test_push() {
 #[test]
 pub fn test_pop() {
     macro_rules! tp (
-        ($path:expr, $expected:expr, $output:expr) => ( {
+        ($path:expr, $expected:expr, $output:expr) => ({
             let mut actual = PathBuf::from($path);
             let output = actual.pop();
             assert!(actual.to_str() == Some($expected) && output == $output,
@@ -1335,7 +1335,7 @@ pub fn test_pop() {
 #[test]
 pub fn test_set_file_name() {
     macro_rules! tfn (
-            ($path:expr, $file:expr, $expected:expr) => ( {
+        ($path:expr, $file:expr, $expected:expr) => ({
             let mut p = PathBuf::from($path);
             p.set_file_name($file);
             assert!(p.to_str() == Some($expected),
@@ -1369,7 +1369,7 @@ pub fn test_set_file_name() {
 #[test]
 pub fn test_set_extension() {
     macro_rules! tfe (
-            ($path:expr, $ext:expr, $expected:expr, $output:expr) => ( {
+        ($path:expr, $ext:expr, $expected:expr, $output:expr) => ({
             let mut p = PathBuf::from($path);
             let output = p.set_extension($ext);
             assert!(p.to_str() == Some($expected) && output == $output,
@@ -1395,6 +1395,46 @@ pub fn test_set_extension() {
 }
 
 #[test]
+pub fn test_with_extension() {
+    macro_rules! twe (
+        ($input:expr, $extension:expr, $expected:expr) => ({
+            let input = Path::new($input);
+            let output = input.with_extension($extension);
+
+            assert!(
+                output.to_str() == Some($expected),
+                "calling Path::new({:?}).with_extension({:?}): Expected {:?}, got {:?}",
+                $input, $extension, $expected, output,
+            );
+        });
+    );
+
+    twe!("foo", "txt", "foo.txt");
+    twe!("foo.bar", "txt", "foo.txt");
+    twe!("foo.bar.baz", "txt", "foo.bar.txt");
+    twe!(".test", "txt", ".test.txt");
+    twe!("foo.txt", "", "foo");
+    twe!("foo", "", "foo");
+    twe!("", "foo", "");
+    twe!(".", "foo", ".");
+    twe!("foo/", "bar", "foo.bar");
+    twe!("foo/.", "bar", "foo.bar");
+    twe!("..", "foo", "..");
+    twe!("foo/..", "bar", "foo/..");
+    twe!("/", "foo", "/");
+
+    // New extension is smaller than file name
+    twe!("aaa_aaa_aaa", "bbb_bbb", "aaa_aaa_aaa.bbb_bbb");
+    // New extension is greater than file name
+    twe!("bbb_bbb", "aaa_aaa_aaa", "bbb_bbb.aaa_aaa_aaa");
+
+    // New extension is smaller than previous extension
+    twe!("ccc.aaa_aaa_aaa", "bbb_bbb", "ccc.bbb_bbb");
+    // New extension is greater than previous extension
+    twe!("ccc.bbb_bbb", "aaa_aaa_aaa", "ccc.aaa_aaa_aaa");
+}
+
+#[test]
 fn test_eq_receivers() {
     use crate::borrow::Cow;
 
@@ -1669,7 +1709,7 @@ fn into_rc() {
 #[test]
 fn test_ord() {
     macro_rules! ord(
-        ($ord:ident, $left:expr, $right:expr) => ( {
+        ($ord:ident, $left:expr, $right:expr) => ({
             use core::cmp::Ordering;
 
             let left = Path::new($left);
diff --git a/library/std/src/sync/mpmc/waker.rs b/library/std/src/sync/mpmc/waker.rs
index 4912ca4f815..9aab1b9417e 100644
--- a/library/std/src/sync/mpmc/waker.rs
+++ b/library/std/src/sync/mpmc/waker.rs
@@ -66,26 +66,32 @@ impl Waker {
     /// Attempts to find another thread's entry, select the operation, and wake it up.
     #[inline]
     pub(crate) fn try_select(&mut self) -> Option<Entry> {
-        self.selectors
-            .iter()
-            .position(|selector| {
-                // Does the entry belong to a different thread?
-                selector.cx.thread_id() != current_thread_id()
-                    && selector // Try selecting this operation.
-                        .cx
-                        .try_select(Selected::Operation(selector.oper))
-                        .is_ok()
-                    && {
-                        // Provide the packet.
-                        selector.cx.store_packet(selector.packet);
-                        // Wake the thread up.
-                        selector.cx.unpark();
-                        true
-                    }
-            })
-            // Remove the entry from the queue to keep it clean and improve
-            // performance.
-            .map(|pos| self.selectors.remove(pos))
+        if self.selectors.is_empty() {
+            None
+        } else {
+            let thread_id = current_thread_id();
+
+            self.selectors
+                .iter()
+                .position(|selector| {
+                    // Does the entry belong to a different thread?
+                    selector.cx.thread_id() != thread_id
+                        && selector // Try selecting this operation.
+                            .cx
+                            .try_select(Selected::Operation(selector.oper))
+                            .is_ok()
+                        && {
+                            // Provide the packet.
+                            selector.cx.store_packet(selector.packet);
+                            // Wake the thread up.
+                            selector.cx.unpark();
+                            true
+                        }
+                })
+                // Remove the entry from the queue to keep it clean and improve
+                // performance.
+                .map(|pos| self.selectors.remove(pos))
+        }
     }
 
     /// Notifies all operations waiting to be ready.
diff --git a/library/std/src/sys/windows/thread_local_dtor.rs b/library/std/src/sys/windows/thread_local_dtor.rs
index 9707a95dff2..cf542d2bfb8 100644
--- a/library/std/src/sys/windows/thread_local_dtor.rs
+++ b/library/std/src/sys/windows/thread_local_dtor.rs
@@ -4,29 +4,4 @@
 #![unstable(feature = "thread_local_internals", issue = "none")]
 #![cfg(target_thread_local)]
 
-// Using a per-thread list avoids the problems in synchronizing global state.
-#[thread_local]
-static mut DESTRUCTORS: Vec<(*mut u8, unsafe extern "C" fn(*mut u8))> = Vec::new();
-
-// Ensure this can never be inlined because otherwise this may break in dylibs.
-// See #44391.
-#[inline(never)]
-pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
-    DESTRUCTORS.push((t, dtor));
-}
-
-#[inline(never)] // See comment above
-/// Runs destructors. This should not be called until thread exit.
-pub unsafe fn run_keyless_dtors() {
-    // Drop all the destructors.
-    //
-    // Note: While this is potentially an infinite loop, it *should* be
-    // the case that this loop always terminates because we provide the
-    // guarantee that a TLS key cannot be set after it is flagged for
-    // destruction.
-    while let Some((ptr, dtor)) = DESTRUCTORS.pop() {
-        (dtor)(ptr);
-    }
-    // We're done so free the memory.
-    DESTRUCTORS = Vec::new();
-}
+pub use super::thread_local_key::register_keyless_dtor as register_dtor;
diff --git a/library/std/src/sys/windows/thread_local_key.rs b/library/std/src/sys/windows/thread_local_key.rs
index 17628b7579b..036d96596e9 100644
--- a/library/std/src/sys/windows/thread_local_key.rs
+++ b/library/std/src/sys/windows/thread_local_key.rs
@@ -1,7 +1,7 @@
 use crate::cell::UnsafeCell;
 use crate::ptr;
 use crate::sync::atomic::{
-    AtomicPtr, AtomicU32,
+    AtomicBool, AtomicPtr, AtomicU32,
     Ordering::{AcqRel, Acquire, Relaxed, Release},
 };
 use crate::sys::c;
@@ -9,6 +9,41 @@ use crate::sys::c;
 #[cfg(test)]
 mod tests;
 
+/// An optimization hint. The compiler is often smart enough to know if an atomic
+/// is never set and can remove dead code based on that fact.
+static HAS_DTORS: AtomicBool = AtomicBool::new(false);
+
+// Using a per-thread list avoids the problems in synchronizing global state.
+#[thread_local]
+#[cfg(target_thread_local)]
+static mut DESTRUCTORS: Vec<(*mut u8, unsafe extern "C" fn(*mut u8))> = Vec::new();
+
+// Ensure this can never be inlined because otherwise this may break in dylibs.
+// See #44391.
+#[inline(never)]
+#[cfg(target_thread_local)]
+pub unsafe fn register_keyless_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
+    DESTRUCTORS.push((t, dtor));
+    HAS_DTORS.store(true, Relaxed);
+}
+
+#[inline(never)] // See comment above
+#[cfg(target_thread_local)]
+/// Runs destructors. This should not be called until thread exit.
+unsafe fn run_keyless_dtors() {
+    // Drop all the destructors.
+    //
+    // Note: While this is potentially an infinite loop, it *should* be
+    // the case that this loop always terminates because we provide the
+    // guarantee that a TLS key cannot be set after it is flagged for
+    // destruction.
+    while let Some((ptr, dtor)) = DESTRUCTORS.pop() {
+        (dtor)(ptr);
+    }
+    // We're done so free the memory.
+    DESTRUCTORS = Vec::new();
+}
+
 type Key = c::DWORD;
 type Dtor = unsafe extern "C" fn(*mut u8);
 
@@ -156,6 +191,8 @@ static DTORS: AtomicPtr<StaticKey> = AtomicPtr::new(ptr::null_mut());
 /// Should only be called once per key, otherwise loops or breaks may occur in
 /// the linked list.
 unsafe fn register_dtor(key: &'static StaticKey) {
+    // Ensure this is never run when native thread locals are available.
+    assert_eq!(false, cfg!(target_thread_local));
     let this = <*const StaticKey>::cast_mut(key);
     // Use acquire ordering to pass along the changes done by the previously
     // registered keys when we store the new head with release ordering.
@@ -167,6 +204,7 @@ unsafe fn register_dtor(key: &'static StaticKey) {
             Err(new) => head = new,
         }
     }
+    HAS_DTORS.store(true, Release);
 }
 
 // -------------------------------------------------------------------------
@@ -240,10 +278,14 @@ pub static p_thread_callback: unsafe extern "system" fn(c::LPVOID, c::DWORD, c::
 
 #[allow(dead_code, unused_variables)]
 unsafe extern "system" fn on_tls_callback(h: c::LPVOID, dwReason: c::DWORD, pv: c::LPVOID) {
+    if !HAS_DTORS.load(Acquire) {
+        return;
+    }
     if dwReason == c::DLL_THREAD_DETACH || dwReason == c::DLL_PROCESS_DETACH {
+        #[cfg(not(target_thread_local))]
         run_dtors();
         #[cfg(target_thread_local)]
-        super::thread_local_dtor::run_keyless_dtors();
+        run_keyless_dtors();
     }
 
     // See comments above for what this is doing. Note that we don't need this
diff --git a/library/std/src/sys/windows/thread_local_key/tests.rs b/library/std/src/sys/windows/thread_local_key/tests.rs
index c95f383fb90..c739f0caf3e 100644
--- a/library/std/src/sys/windows/thread_local_key/tests.rs
+++ b/library/std/src/sys/windows/thread_local_key/tests.rs
@@ -1,3 +1,7 @@
+// This file only tests the thread local key fallback.
+// Windows targets with native thread local support do not use this.
+#![cfg(not(target_thread_local))]
+
 use super::StaticKey;
 use crate::ptr;
 
diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs
index 44b8c2d8c01..c69b21488d2 100644
--- a/src/bootstrap/test.rs
+++ b/src/bootstrap/test.rs
@@ -2087,10 +2087,11 @@ impl Step for ErrorIndex {
         let mut tool = tool::ErrorIndex::command(builder);
         tool.arg("markdown").arg(&output);
 
-        let _guard =
+        let guard =
             builder.msg(Kind::Test, compiler.stage, "error-index", compiler.host, compiler.host);
         let _time = util::timeit(&builder);
         builder.run_quiet(&mut tool);
+        drop(guard);
         // The tests themselves need to link to std, so make sure it is
         // available.
         builder.ensure(compile::Std::new(compiler, compiler.host));
diff --git a/src/doc/rustc/src/command-line-arguments.md b/src/doc/rustc/src/command-line-arguments.md
index 2c7c05c0c4b..4d32897cc14 100644
--- a/src/doc/rustc/src/command-line-arguments.md
+++ b/src/doc/rustc/src/command-line-arguments.md
@@ -260,6 +260,10 @@ The valid types of print values are:
   This returns rustc's minimum supported deployment target if no `*_DEPLOYMENT_TARGET` variable
   is present in the environment, or otherwise returns the variable's parsed value.
 
+A filepath may optionally be specified for each requested information kind, in
+the format `--print KIND=PATH`, just like for `--emit`. When a path is
+specified, information will be written there instead of to stdout.
+
 [conditional compilation]: ../reference/conditional-compilation.html
 [deployment target]: https://developer.apple.com/library/archive/documentation/DeveloperTools/Conceptual/cross_development/Configuring/configuring.html
 
diff --git a/src/doc/style-guide/src/README.md b/src/doc/style-guide/src/README.md
index b8aa64ba14f..6bdb3ae26ac 100644
--- a/src/doc/style-guide/src/README.md
+++ b/src/doc/style-guide/src/README.md
@@ -37,10 +37,9 @@ options.
 ### Indentation and line width
 
 * Use spaces, not tabs.
-* Each level of indentation must be four spaces (that is, all indentation
-  outside of string literals and comments must be a multiple of four).
+* Each level of indentation must be 4 spaces (that is, all indentation
+  outside of string literals and comments must be a multiple of 4).
 * The maximum width for a line is 100 characters.
-* A tool may choose to make some of these configurable.
 
 #### Block indent
 
@@ -63,7 +62,8 @@ example) and less rightward drift.
 
 ### Trailing commas
 
-Lists should have a trailing comma when followed by a newline:
+In comma-separated lists of any kind, use a trailing comma when followed by a
+newline:
 
 ```rust
 function_call(
@@ -99,8 +99,6 @@ fn bar() {}
 fn baz() {}
 ```
 
-Formatting tools may wish to make the bounds on blank lines configurable.
-
 ### [Module-level items](items.md)
 ### [Statements](statements.md)
 ### [Expressions](expressions.md)
@@ -114,17 +112,17 @@ formatter might skip formatting of comments.
 
 Prefer line comments (`//`) to block comments (`/* ... */`).
 
-When using line comments there should be a single space after the opening sigil.
+When using line comments, put a single space after the opening sigil.
 
-When using single-line block comments there should be a single space after the
-opening sigil and before the closing sigil. Multi-line block comments should
-have a newline after the opening sigil and before the closing sigil.
+When using single-line block comments, put a single space after the opening
+sigil and before the closing sigil. For multi-line block comments, put a
+newline after the opening sigil, and a newline before the closing sigil.
 
-Prefer to put a comment on its own line. Where a comment follows code, there
-should be a single space before it. Where a block comment is inline, there
-should be surrounding whitespace as if it were an identifier or keyword. There
-should be no trailing whitespace after a comment or at the end of any line in a
-multi-line comment. Examples:
+Prefer to put a comment on its own line. Where a comment follows code, put a
+single space before it. Where a block comment appears inline, use surrounding
+whitespace as if it were an identifier or keyword. Do not include trailing
+whitespace after a comment or at the end of any line in a multi-line comment.
+Examples:
 
 ```rust
 // A comment on an item.
@@ -173,7 +171,7 @@ Prefer line comments (`///`) to block comments (`/** ... */`).
 Prefer outer doc comments (`///` or `/** ... */`), only use inner doc comments
 (`//!` and `/*! ... */`) to write module-level or crate-level documentation.
 
-Doc comments should come before attributes.
+Put doc comments before attributes.
 
 ### Attributes
 
@@ -198,18 +196,20 @@ struct CRepr {
 }
 ```
 
-For attributes with an equal sign, there should be a single space before and
-after the `=`, e.g., `#[foo = 42]`.
+For attributes with an equal sign, put a single space before and after the `=`,
+e.g., `#[foo = 42]`.
 
 There must only be a single `derive` attribute. Note for tool authors: if
 combining multiple `derive` attributes into a single attribute, the ordering of
-the derived names should be preserved. E.g., `#[derive(bar)] #[derive(foo)]
-struct Baz;` should be formatted to `#[derive(bar, foo)] struct Baz;`.
+the derived names must generally be preserved for correctness:
+`#[derive(Foo)] #[derive(Bar)] struct Baz;` must be formatted to
+`#[derive(Foo, Bar)] struct Baz;`.
 
 ### *small* items
 
-In many places in this guide we specify that a formatter may format an item
-differently if it is *small*, for example struct literals:
+In many places in this guide we specify formatting that depends on a code
+construct being *small*. For example, single-line vs multi-line struct
+literals:
 
 ```rust
 // Normal formatting
@@ -218,7 +218,7 @@ Foo {
     f2: another_expression(),
 }
 
-// *small* formatting
+// "small" formatting
 Foo { f1, f2 }
 ```
 
@@ -231,10 +231,6 @@ complexity of an item (for example, that all components must be simple names,
 not more complex sub-expressions). For more discussion on suitable heuristics,
 see [this issue](https://github.com/rust-lang-nursery/fmt-rfcs/issues/47).
 
-Tools should give the user an option to ignore such heuristics and always use
-the normal formatting.
-
-
 ## [Non-formatting conventions](advice.md)
 
 ## [Cargo.toml conventions](cargo.md)
diff --git a/src/doc/style-guide/src/expressions.md b/src/doc/style-guide/src/expressions.md
index 41a9eb82cb5..bf3fe87a0a4 100644
--- a/src/doc/style-guide/src/expressions.md
+++ b/src/doc/style-guide/src/expressions.md
@@ -2,10 +2,13 @@
 
 ### Blocks
 
-A block expression should have a newline after the initial `{` and before the
-terminal `}`. Any qualifier before the block (e.g., `unsafe`) should always be
-on the same line as the opening brace, and separated with a single space. The
-contents of the block should be block indented:
+A block expression must have a newline after the initial `{` and before the
+terminal `}`, unless it qualifies to be written as a single line based on
+another style rule.
+
+A keyword before the block (such as `unsafe` or `async`) must be on the same
+line as the opening brace, with a single space between the keyword and the
+opening brace. Indent the contents of the block.
 
 ```rust
 fn block_as_stmt() {
@@ -40,7 +43,7 @@ fn unsafe_block_as_stmt() {
 }
 ```
 
-If a block has an attribute, it should be on its own line:
+If a block has an attribute, put it on its own line before the block:
 
 ```rust
 fn block_as_stmt() {
@@ -54,18 +57,18 @@ fn block_as_stmt() {
 }
 ```
 
-Avoid writing comments on the same line as the braces.
+Avoid writing comments on the same lines as either of the braces.
 
-An empty block should be written as `{}`.
+Write an empty block as `{}`.
 
-A block may be written on a single line if:
+Write a block on a single line if:
 
 * it is either used in expression position (not statement position) or is an
-  unsafe block in statement position
-* contains a single-line expression and no statements
-* contains no comments
+  unsafe block in statement position,
+* it contains a single-line expression and no statements, and
+* it contains no comments
 
-A single line block should have spaces after the opening brace and before the
+For a single-line block, put spaces after the opening brace and before the
 closing brace.
 
 Examples:
@@ -117,14 +120,14 @@ fn main() {
 ### Closures
 
 Don't put any extra spaces before the first `|` (unless the closure is prefixed
-by `move`); put a space between the second `|` and the expression of the
-closure. Between the `|`s, you should use function definition syntax, however,
-elide types where possible.
+by a keyword such as `move`); put a space between the second `|` and the
+expression of the closure. Between the `|`s, use function definition syntax,
+but elide types where possible.
 
 Use closures without the enclosing `{}`, if possible. Add the `{}` when you have
-a return type, when there are statements, there are comments in the body, or the
-body expression spans multiple lines and is a control-flow expression. If using
-braces, follow the rules above for blocks. Examples:
+a return type, when there are statements, when there are comments inside the
+closure, or when the body expression is a control-flow expression that spans
+multiple lines. If using braces, follow the rules above for blocks. Examples:
 
 ```rust
 |arg1, arg2| expr
@@ -155,13 +158,14 @@ move |arg1: i32, arg2: i32| -> i32 {
 
 ### Struct literals
 
-If a struct literal is *small* it may be formatted on a single line. If not,
-each field should be on it's own, block-indented line. There should be a
-trailing comma in the multi-line form only. There should be a space after the
-colon only.
+If a struct literal is *small*, format it on a single line, and do not use a
+trailing comma. If not, split it across multiple lines, with each field on its
+own block-indented line, and use a trailing comma.
+
+For each `field: value` entry, put a space after the colon only.
 
-There should be a space before the opening brace. In the single-line form there
-should be spaces after the opening brace and before the closing brace.
+Put a space before the opening brace. In the single-line form, put spaces after
+the opening brace and before the closing brace.
 
 ```rust
 Foo { field1, field2: 0 }
@@ -172,19 +176,25 @@ let f = Foo {
 ```
 
 Functional record update syntax is treated like a field, but it must never have
-a trailing comma. There should be no space after `..`.
+a trailing comma. Do not put a space after `..`.
 
+```rust
 let f = Foo {
     field1,
     ..an_expr
 };
+```
 
 
 ### Tuple literals
 
-Use a single-line form where possible. There should not be spaces around the
-parentheses. Where a single-line form is not possible, each element of the tuple
-should be on its own block-indented line and there should be a trailing comma.
+Use a single-line form where possible. Do not put spaces between the opening
+parenthesis and the first element, or between the last element and the closing
+parenthesis. Separate elements with a comma followed by a space.
+
+Where a single-line form is not possible, write the tuple across
+multiple lines, with each element of the tuple on its own block-indented line,
+and use a trailing comma.
 
 ```rust
 (a, b, c)
@@ -198,14 +208,23 @@ let x = (
 
 ### Tuple struct literals
 
-There should be no space between the identifier and the opening parenthesis.
-Otherwise, follow the rules for tuple literals, e.g., `Foo(a, b)`.
+Do not put space between the identifier and the opening parenthesis. Otherwise,
+follow the rules for tuple literals:
+
+```rust
+Foo(a, b, c)
+
+let x = Foo(
+    a_long_expr,
+    another_very_long_expr,
+);
+```
 
 
 ### Enum literals
 
 Follow the formatting rules for the various struct literals. Prefer using the
-name of the enum as a qualifying name, unless the enum is in the prelude. E.g.,
+name of the enum as a qualifying name, unless the enum is in the prelude:
 
 ```rust
 Foo::Bar(a, b)
@@ -219,24 +238,29 @@ Ok(an_expr)
 
 ### Array literals
 
-For simple array literals, avoid line breaking, no spaces around square
-brackets, contents of the array should be separated by commas and spaces. If
-using the repeating initialiser, there should be a space after the semicolon
-only. Apply the same rules if using the `vec!` or similar macros (always use
-square brackets here). Examples:
+Write small array literals on a single line. Do not put spaces between the opening
+square bracket and the first element, or between the last element and the closing
+square bracket. Separate elements with a comma followed by a space.
+
+If using the repeating initializer, put a space after the semicolon
+only.
+
+Apply the same rules if using `vec!` or similar array-like macros; always use
+square brackets with such macros. Examples:
 
 ```rust
 fn main() {
-    [1, 2, 3];
-    vec![a, b, c, d];
+    let x = [1, 2, 3];
+    let y = vec![a, b, c, d];
     let a = [42; 10];
 }
 ```
 
-If a line must be broken, prefer breaking only after the `;`, if possible.
-Otherwise, follow the rules below for function calls. In any case, the contents
-of the initialiser should be block indented and there should be line breaks
-after the opening bracket and before the closing bracket:
+For arrays that have to be broken across lines, if using the repeating
+initializer, break after the `;`, not before. Otherwise, follow the rules below
+for function calls. In any case, block-indent the contents of the initializer,
+and put line breaks after the opening square bracket and before the closing
+square bracket:
 
 ```rust
 fn main() {
@@ -255,11 +279,12 @@ fn main() {
 
 ### Array accesses, indexing, and slicing.
 
-No spaces around the square brackets, avoid breaking lines if possible, never
-break a line between the target expression and the opening bracket. If the
-indexing expression covers multiple lines, then it should be block indented and
-there should be newlines after the opening brackets and before the closing
-bracket. However, this should be avoided where possible.
+Don't put spaces around the square brackets. Avoid breaking lines if possible.
+Never break a line between the target expression and the opening square
+bracket. If the indexing expression must be broken onto a subsequent line, or
+spans multiple lines itself, then block-indent the indexing expression, and put
+newlines after the opening square bracket and before the closing square
+bracket:
 
 Examples:
 
@@ -291,7 +316,7 @@ if you have `t: &T`, and `u: U`, prefer `*t op u` to `t op &u`. In general,
 within expressions, prefer dereferencing to taking references, unless necessary
 (e.g. to avoid an unnecessarily expensive operation).
 
-Use parentheses liberally, do not necessarily elide them due to precedence.
+Use parentheses liberally; do not necessarily elide them due to precedence.
 Tools should not automatically insert or remove parentheses. Do not use spaces
 to indicate precedence.
 
@@ -353,10 +378,10 @@ foo(x, y, z)
 #### Multi-line calls
 
 If the function call is not *small*, it would otherwise over-run the max width,
-or any argument or the callee is multi-line, then the call should be formatted
-across multiple lines. In this case, each argument should be on it's own block-
-indented line, there should be a newline after the opening parenthesis and
-before the closing parenthesis, and there should be a trailing comma. E.g.,
+or any argument or the callee is multi-line, then format the call across
+multiple lines. In this case, put each argument on its own block-indented line,
+break after the opening parenthesis and before the closing parenthesis,
+and use a trailing comma:
 
 ```rust
 a_function_call(
@@ -379,17 +404,18 @@ x.foo().bar().baz(x, y, z);
 
 ### Macro uses
 
-Macros which can be parsed like other constructs should be formatted like those
+If a macro can be parsed like other constructs, format it like those
 constructs. For example, a macro use `foo!(a, b, c)` can be parsed like a
-function call (ignoring the `!`), therefore it should be formatted following the
-rules for function calls.
+function call (ignoring the `!`), so format it using the rules for function
+calls.
 
 #### Special case macros
 
-Macros which take a format string and where all other arguments are *small* may
-be formatted with arguments before and after the format string on a single line
-and the format string on its own line, rather than putting each argument on its
-own line. For example,
+For macros which take a format string, if all other arguments are *small*,
+format the arguments before the format string on a single line if they fit, and
+format the arguments after the format string on a single line if they fit, with
+the format string on its own line. If the arguments are not small or do not
+fit, put each on its own line as with a function. For example:
 
 ```rust
 println!(
@@ -416,13 +442,13 @@ let cstr = "Hi\0" as *const str as *const [u8] as *const std::os::raw::c_char;
 
 ### Chains of fields and method calls
 
-A chain is a sequence of field accesses and/or method calls. A chain may also
-include the try operator ('?'). E.g., `a.b.c().d` or `foo?.bar().baz?`.
+A chain is a sequence of field accesses, method calls, and/or uses of the try
+operator `?`. E.g., `a.b.c().d` or `foo?.bar().baz?`.
 
-Prefer formatting on one line if possible, and the chain is *small*. If
-formatting on multiple lines, each field access or method call in the chain
-should be on its own line with the line-break before the `.` and after any `?`.
-Each line should be block-indented. E.g.,
+Format the chain on one line if it is "small" and otherwise possible to do so.
+If formatting on multiple lines, put each field access or method call in the
+chain on its own line, with the line-break before the `.` and after any `?`.
+Block-indent each subsequent line:
 
 ```rust
 let foo = bar
@@ -431,13 +457,16 @@ let foo = bar
 ```
 
 If the length of the last line of the first element plus its indentation is
-less than or equal to the indentation of the second line (and there is space),
-then combine the first and second lines, e.g.,
+less than or equal to the indentation of the second line, then combine the
+first and second lines if they fit. Apply this rule recursively.
 
 ```rust
 x.baz?
     .qux()
 
+x.y.z
+    .qux()
+
 let foo = x
     .baz?
     .qux();
@@ -489,13 +518,13 @@ self.pre_comment.as_ref().map_or(
 This section covers `if`, `if let`, `loop`, `while`, `while let`, and `for`
 expressions.
 
-The keyword, any initial clauses, and the opening brace of the block should be
-on a single line. The usual rules for [block formatting](#blocks) should be
-applied to the block.
+Put the keyword, any initial clauses, and the opening brace of the block all on
+a single line, if they fit. Apply the usual rules for [block
+formatting](#blocks) to the block.
 
-If there is an `else` component, then the closing brace, `else`, any following
-clause, and the opening brace should all be on the same line. There should be a
-single space before and after the `else` keyword. For example:
+If there is an `else` component, then put the closing brace, `else`, any
+following clause, and the opening brace all on the same line, with a single
+space before and after the `else` keyword:
 
 ```rust
 if ... {
@@ -513,10 +542,10 @@ if let ... {
 }
 ```
 
-If the control line needs to be broken, then prefer to break before the `=` in
-`* let` expressions and before `in` in a `for` expression; the following line
-should be block indented. If the control line is broken for any reason, then the
-opening brace should be on its own line and not indented. Examples:
+If the control line needs to be broken, prefer to break before the `=` in `*
+let` expressions and before `in` in a `for` expression; block-indent the
+following line. If the control line is broken for any reason, put the opening
+brace on its own line, not indented. Examples:
 
 ```rust
 while let Some(foo)
@@ -539,10 +568,10 @@ if a_long_expression
 }
 ```
 
-Where the initial clause is multi-lined and ends with one or more closing
-parentheses, square brackets, or braces, and there is nothing else on that line,
-and that line is not indented beyond the indent on the first line of the control
-flow expression, then the opening brace of the block should be put on the same
+Where the initial clause spans multiple lines and ends with one or more closing
+parentheses, square brackets, or braces, and there is nothing else on that
+line, and that line is not indented beyond the indent on the first line of the
+control flow expression, then put the opening brace of the block on the same
 line with a preceding space. For example:
 
 ```rust
@@ -558,9 +587,9 @@ if !self.config.file_lines().intersects(
 
 #### Single line `if else`
 
-Formatters may place an `if else` or `if let else` on a single line if it occurs
-in expression context (i.e., is not a standalone statement), it contains a
-single `else` clause, and is *small*. For example:
+Put an `if else` or `if let else` on a single line if it occurs in expression
+context (i.e., is not a standalone statement), it contains a single `else`
+clause, and is *small*:
 
 ```rust
 let y = if x { 0 } else { 1 };
@@ -582,9 +611,9 @@ if x {
 
 ### Match
 
-Prefer not to line-break inside the discriminant expression. There must always
-be a line break after the opening brace and before the closing brace. The match
-arms must be block indented once:
+Prefer not to line-break inside the discriminant expression. Always break after
+the opening brace and before the closing brace. Block-indent the match arms
+once:
 
 ```rust
 match foo {
@@ -598,7 +627,7 @@ let x = match foo.bar.baz() {
 
 Use a trailing comma for a match arm if and only if not using a block.
 
-Never start a match arm pattern with `|`, e.g.,
+Never start a match arm pattern with `|`:
 
 ```rust
 match foo {
@@ -608,14 +637,13 @@ match foo {
     | a_very_long_pattern
     | another_pattern
     | yet_another_pattern
-    | a_forth_pattern => {
+    | a_fourth_pattern => {
         ...
     }
 }
 ```
 
-Prefer
-
+Prefer:
 
 ```rust
 match foo {
@@ -623,7 +651,7 @@ match foo {
     a_very_long_pattern
     | another_pattern
     | yet_another_pattern
-    | a_forth_pattern => {
+    | a_fourth_pattern => {
         ...
     }
 }
@@ -633,11 +661,11 @@ Avoid splitting the left-hand side (before the `=>`) of a match arm where
 possible. If the right-hand side of the match arm is kept on the same line,
 never use a block (unless the block is empty).
 
-If the right-hand side consists of multiple statements or has line comments or
-the start of the line cannot be fit on the same line as the left-hand side, use
-a block.
+If the right-hand side consists of multiple statements, or has line comments,
+or the start of the line does not fit on the same line as the left-hand side,
+use a block.
 
-The body of a block arm should be block indented once.
+Block-indent the body of a block arm.
 
 Examples:
 
@@ -662,8 +690,8 @@ match foo {
 ```
 
 If the body is a single expression with no line comments and not a control flow
-expression, then it may be started on the same line as the right-hand side. If
-not, then it must be in a block. Example,
+expression, start it on the same line as the left-hand side. If not, then it
+must be in a block. Example:
 
 ```rust
 match foo {
@@ -687,8 +715,8 @@ match foo {
 
 #### Line-breaking
 
-Where it is possible to use a block form on the right-hand side and avoid
-breaking the left-hand side, do that. E.g.
+If using a block form on the right-hand side of a match arm makes it possible
+to avoid breaking on the left-hand side, do that:
 
 ```rust
     // Assuming the following line does not fit in the max width
@@ -720,7 +748,7 @@ body on a new line:
 
 If required to break the pattern, put each clause of the pattern on its own
 line with no additional indent, breaking before the `|`. If there is an `if`
-clause, then you must use the above form:
+clause, use the above form:
 
 ```rust
     a_very_long_pattern
@@ -740,7 +768,7 @@ clause, then you must use the above form:
 ```
 
 If the pattern is multi-line, and the last line is less wide than the indent, do
-not put the `if` clause on a newline. E.g.,
+not put the `if` clause on a new line. E.g.,
 
 ```rust
     Token::Dimension {
@@ -753,8 +781,8 @@ not put the `if` clause on a newline. E.g.,
 ```
 
 If every clause in a pattern is *small*, but the whole pattern does not fit on
-one line, then the pattern may be formatted across multiple lines with as many
-clauses per line as possible. Again break before a `|`:
+one line, then format the pattern across multiple lines with as many clauses
+per line as possible. Again, break before a `|`:
 
 ```rust
     foo | bar | baz
@@ -783,8 +811,8 @@ E.g., `&&Some(foo)` matches, `Foo(4, Bar)` does not.
 ### Combinable expressions
 
 Where a function call has a single argument, and that argument is formatted
-across multiple-lines, the outer call may be formatted as if it were a single-
-line call. The same combining behaviour may be applied to any similar
+across multiple-lines, format the outer call as if it were a single-line call,
+if the result fits. Apply the same combining behaviour to any similar
 expressions which have multi-line, block-indented lists of sub-expressions
 delimited by parentheses (e.g., macros or tuple struct literals). E.g.,
 
@@ -814,13 +842,12 @@ let arr = [combinable(
 )];
 ```
 
-Such behaviour should extend recursively, however, tools may choose to limit the
-depth of nesting.
+Apply this behavior recursively.
 
-Only where the multi-line sub-expression is a closure with an explicit block,
-this combining behaviour may be used where there are other arguments, as long as
-all the arguments and the first line of the closure fit on the first line, the
-closure is the last argument, and there is only one closure argument:
+For a function with multiple arguments, if the last argument is a multi-line
+closure with an explicit block, there are no other closure arguments, and all
+the arguments and the first line of the closure fit on the first line, use the
+same combining behavior:
 
 ```rust
 foo(first_arg, x, |param| {
@@ -835,16 +862,17 @@ foo(first_arg, x, |param| {
 Do not put spaces in ranges, e.g., `0..10`, `x..=y`, `..x.len()`, `foo..`.
 
 When writing a range with both upper and lower bounds, if the line must be
-broken, break before the range operator and block indent the second line:
+broken within the range, break before the range operator and block indent the
+second line:
 
 ```rust
 a_long_expression
     ..another_long_expression
 ```
 
-For the sake of indicating precedence, we recommend that if either bound is a
-compound expression, then use parentheses around it, e.g., `..(x + 1)`,
-`(x.f)..(x.f.len())`, or `0..(x - 10)`.
+For the sake of indicating precedence, if either bound is a compound
+expression, use parentheses around it, e.g., `..(x + 1)`, `(x.f)..(x.f.len())`,
+or `0..(x - 10)`.
 
 
 ### Hexadecimal literals
@@ -852,11 +880,8 @@ compound expression, then use parentheses around it, e.g., `..(x + 1)`,
 Hexadecimal literals may use upper- or lower-case letters, but they must not be
 mixed within the same literal. Projects should use the same case for all
 literals, but we do not make a recommendation for either lower- or upper-case.
-Tools should have an option to convert mixed case literals to upper-case, and
-may have an option to convert all literals to either lower- or upper-case.
-
 
 ## Patterns
 
-Patterns should be formatted like their corresponding expressions. See the
-section on `match` for additional formatting for patterns in match arms.
+Format patterns like their corresponding expressions. See the section on
+`match` for additional formatting for patterns in match arms.
diff --git a/src/doc/style-guide/src/items.md b/src/doc/style-guide/src/items.md
index de31e8a8799..6e5ea335e6a 100644
--- a/src/doc/style-guide/src/items.md
+++ b/src/doc/style-guide/src/items.md
@@ -9,19 +9,17 @@ an item appears at module level or within another item.
 alphabetically.
 
 `use` statements, and module *declarations* (`mod foo;`, not `mod { ... }`)
-must come before other items. We recommend that imports come before module
-declarations; if imports and modules are separated, then they should be ordered
-alphabetically. When sorting, `self` and `super` must come before any other
-names. Module declarations should not be moved if they are annotated with
-`#[macro_use]`, since that may be semantics changing.
-
-Tools should make the above ordering optional.
+must come before other items. Put imports before module declarations. Sort each
+alphabetically, except that `self` and `super` must come before any other
+names.
 
+Don't automatically move module declarations annotated with `#[macro_use]`,
+since that might change semantics.
 
 ### Function definitions
 
 In Rust, people often find functions by searching for `fn [function-name]`, so
-the formatting of function definitions shold enable this.
+the formatting of function definitions must enable this.
 
 The proper ordering and spacing is:
 
@@ -83,9 +81,9 @@ enum FooBar {
 }
 ```
 
-If a struct variant is [*small*](index.html#small-items), it may be formatted on
-one line. In this case, do not use a trailing comma for the field list, but do
-put spaces around each brace:
+If a struct variant is [*small*](index.html#small-items), format it on one
+line. In this case, do not use a trailing comma for the field list, but do put
+spaces around each brace:
 
 ```rust
 enum FooBar {
@@ -94,9 +92,9 @@ enum FooBar {
 ```
 
 In an enum with multiple struct variants, if any struct variant is written on
-multiple lines, then the multi-line formatting should be used for all struct
-variants. However, such a situation might be an indication that you should
-factor out the fields of the variant into their own struct.
+multiple lines, use the multi-line formatting for all struct variants. However,
+such a situation might be an indication that you should factor out the fields
+of the variant into their own struct.
 
 
 ### Structs and Unions
@@ -143,9 +141,9 @@ union Foo {
 
 ### Tuple structs
 
-Put the whole struct on one line if possible. Types in the parentheses should be
-separated by a comma and space with no trailing comma. No spaces around the
-parentheses or semicolon:
+Put the whole struct on one line if possible. Separate types within the
+parentheses using a comma and space. Don't use a trailing comma for a
+single-line tuple struct. Don't put spaces around the parentheses or semicolon:
 
 ```rust
 pub struct Foo(String, u8);
@@ -154,9 +152,11 @@ pub struct Foo(String, u8);
 Prefer unit structs to empty tuple structs (these only exist to simplify code
 generation), e.g., `struct Foo;` rather than `struct Foo();`.
 
-For more than a few fields, prefer a proper struct with named fields. Given
-this, a tuple struct should always fit on one line. If it does not, block format
-the fields with a field on each line and a trailing comma:
+For more than a few fields (in particular if the tuple struct does not fit on
+one line), prefer a proper struct with named fields.
+
+For a multi-line tuple struct, block-format the fields with a field on each
+line and a trailing comma:
 
 ```rust
 pub struct Foo(
@@ -168,9 +168,9 @@ pub struct Foo(
 
 ### Traits
 
-Trait items should be block-indented. If there are no items, the trait may be
-formatted on a single line. Otherwise there should be line-breaks after the
-opening brace and before the closing brace:
+Use block-indent for trait items. If there are no items, format the trait (including its `{}`)
+on a single line. Otherwise, break after the opening brace and before
+the closing brace:
 
 ```rust
 trait Foo {}
@@ -180,8 +180,8 @@ pub trait Bar {
 }
 ```
 
-If the trait has bounds, there should be a space after the colon but not before
-and before and after each `+`, e.g.,
+If the trait has bounds, put a space after the colon but not before,
+and put spaces around each `+`, e.g.,
 
 ```rust
 trait Foo: Debug + Bar {}
@@ -207,9 +207,9 @@ pub trait IndexRanges:
 
 ### Impls
 
-Impl items should be block indented. If there are no items, the impl may be
-formatted on a single line. Otherwise there should be line-breaks after the
-opening brace and before the closing brace:
+Use block-indent for impl items. If there are no items, format the impl
+(including its `{}`) on a single line. Otherwise, break after the opening brace
+and before the closing brace:
 
 ```rust
 impl Foo {}
@@ -267,12 +267,12 @@ macro_rules! foo {
 
 Prefer to put a generics clause on one line. Break other parts of an item
 declaration rather than line-breaking a generics clause. If a generics clause is
-large enough to require line-breaking, you should prefer to use a `where` clause
-instead.
+large enough to require line-breaking, prefer a `where` clause instead.
 
-Do not put spaces before or after `<` nor before `>`. Only put a space after `>`
-if it is followed by a word or opening brace, not an opening parenthesis. There
-should be a space after each comma and no trailing comma.
+Do not put spaces before or after `<` nor before `>`. Only put a space after
+`>` if it is followed by a word or opening brace, not an opening parenthesis.
+Put a space after each comma. Do not use a trailing comma for a single-line
+generics clause.
 
 ```rust
 fn foo<T: Display, U: Debug>(x: Vec<T>, y: Vec<U>) ...
@@ -280,10 +280,9 @@ fn foo<T: Display, U: Debug>(x: Vec<T>, y: Vec<U>) ...
 impl<T: Display, U: Debug> SomeType<T, U> { ...
 ```
 
-If the generics clause must be formatted across multiple lines, each parameter
-should have its own block-indented line, there should be newlines after the
-opening bracket and before the closing bracket, and there should be a trailing
-comma.
+If the generics clause must be formatted across multiple lines, put each
+parameter on its own block-indented line, break after the opening `<` and
+before the closing `>`, and use a trailing comma.
 
 ```rust
 fn foo<
@@ -292,8 +291,7 @@ fn foo<
 >(x: Vec<T>, y: Vec<U>) ...
 ```
 
-If an associated type is bound in a generic type, then there should be spaces on
-either side of the `=`:
+If an associated type is bound in a generic type, put spaces around the `=`:
 
 ```rust
 <T: Example<Item = u32>>
@@ -306,12 +304,14 @@ Prefer to use single-letter names for generic parameters.
 
 These rules apply for `where` clauses on any item.
 
-A `where` clause may immediately follow a closing bracket of any kind.
-Otherwise, it must start a new line, with no indent. Each component of a `where`
-clause must be on its own line and be block indented. There should be a trailing
+If immediately following a closing bracket of any kind, write the keyword
+`where` on the same line, with a space before it.
+
+Otherwise, put `where` on a new line at the same indentation level. Put each
+component of a `where` clause on its own line, block-indented. Use a trailing
 comma, unless the clause is terminated with a semicolon. If the `where` clause
-is followed by a block (or assignment), the block should be started on a new
-line. Examples:
+is followed by a block (or assignment), start that block on a new line.
+Examples:
 
 ```rust
 fn function<T, U>(args)
@@ -355,12 +355,12 @@ where
 = Bar<T>;
 ```
 
-If a `where` clause is very short, we recommend using an inline bound on the
-type parameter.
+If a `where` clause is very short, prefer using an inline bound on the type
+parameter.
 
-
-If a component of a `where` clause is long, it may be broken before `+` and
-further block indented. Each bound should go on its own line. E.g.,
+If a component of a `where` clause does not fit and contains `+`, break it
+before each `+` and block-indent the continuation lines. Put each bound on its
+own line. E.g.,
 
 ```rust
 impl<T: ?Sized, Idx> IndexRanges<Idx> for T
@@ -369,40 +369,14 @@ where
         + Index<RangeTo<Idx>, Output = Self::Output>
         + Index<RangeFrom<Idx>, Output = Self::Output>
         + Index<RangeInclusive<Idx>, Output = Self::Output>
-        + Index<RangeToInclusive<Idx>, Output = Self::Output> + Index<RangeFull>
-```
-
-#### Option - `where_single_line`
-
-`where_single_line` is `false` by default. If `true`, then a where clause with
-exactly one component may be formatted on a single line if the rest of the
-item's signature is also kept on one line. In this case, there is no need for a
-trailing comma and if followed by a block, no need for a newline before the
-block. E.g.,
-
-```rust
-// May be single-lined.
-fn foo<T>(args) -> ReturnType
-where T: Bound {
-    body
-}
-
-// Must be multi-lined.
-fn foo<T>(
-    args
-) -> ReturnType
-where
-    T: Bound,
-{
-    body
-}
+        + Index<RangeToInclusive<Idx>, Output = Self::Output>
+        + Index<RangeFull>,
 ```
 
-
 ### Type aliases
 
-Type aliases should generally be kept on one line. If necessary to break the
-line, do so after the `=`; the right-hand-side should be block indented:
+Keep type aliases on one line when they fit. If necessary to break the line, do
+so after the `=`, and block-indent the right-hand side:
 
 ```rust
 pub type Foo = Bar<T>;
@@ -427,9 +401,8 @@ where
 
 ### Associated types
 
-Associated types should follow the guidelines above for type aliases. Where an
-associated type has a bound, there should be a space after the colon but not
-before:
+Format associated types like type aliases. Where an associated type has a
+bound, put a space after the colon but not before:
 
 ```rust
 pub type Foo: Bar;
@@ -438,15 +411,14 @@ pub type Foo: Bar;
 
 ### extern items
 
-When writing extern items (such as `extern "C" fn`), always be explicit about
-the ABI. For example, write `extern "C" fn foo ...`, not `extern fn foo ...`, or
+When writing extern items (such as `extern "C" fn`), always specify the ABI.
+For example, write `extern "C" fn foo ...`, not `extern fn foo ...`, or
 `extern "C" { ... }`.
 
 
 ### Imports (`use` statements)
 
-If an import can be formatted on one line, do so. There should be no spaces
-around braces.
+Format imports on one line where possible. Don't put spaces around braces.
 
 ```rust
 use a::b::c;
@@ -458,7 +430,7 @@ use a::b::{foo, bar, baz};
 #### Large list imports
 
 Prefer to use multiple imports rather than a multi-line import. However, tools
-should not split imports by default (they may offer this as an option).
+should not split imports by default.
 
 If an import does require multiple lines (either because a list of single names
 does not fit within the max width, or because of the rules for nested imports
@@ -521,14 +493,12 @@ example, `a::*` comes before `b::a` but `a::b` comes before `a::*`. E.g.,
 
 #### Normalisation
 
-Tools must make the following normalisations:
+Tools must make the following normalisations, recursively:
 
 * `use a::self;` -> `use a;`
 * `use a::{};` -> (nothing)
 * `use a::{b};` -> `use a::b;`
 
-And must apply these recursively.
-
 Tools must not otherwise merge or un-merge import lists or adjust glob imports
 (without an explicit option).
 
diff --git a/src/doc/style-guide/src/statements.md b/src/doc/style-guide/src/statements.md
index a5cd6da1061..8c8f893fbe9 100644
--- a/src/doc/style-guide/src/statements.md
+++ b/src/doc/style-guide/src/statements.md
@@ -2,8 +2,8 @@
 
 ### Let statements
 
-There should be spaces after the `:` and on both sides of the `=` (if they are
-present). No space before the semicolon.
+Put a space after the `:` and on both sides of the `=` (if they are present).
+Don't put a space before the semicolon.
 
 ```rust
 // A comment.
@@ -14,19 +14,19 @@ let pattern: Type;
 let pattern = expr;
 ```
 
-If possible the declaration should be formatted on a single line. If this is not
-possible, then try splitting after the `=`, if the declaration can fit on two
-lines. The expression should be block indented.
+If possible, format the declaration on a single line. If not possible, then try
+splitting after the `=`, if the declaration fits on two lines. Block-indent the
+expression.
 
 ```rust
 let pattern: Type =
     expr;
 ```
 
-If the first line does not fit on a single line, then split after the colon,
-using block indentation. If the type covers multiple lines, even after line-
-breaking after the `:`, then the first line may be placed on the same line as
-the `:`, subject to the [combining rules](https://github.com/rust-lang-nursery/fmt-rfcs/issues/61) (WIP).
+If the first line still does not fit on a single line, split after the `:`, and
+use block indentation. If the type requires multiple lines, even after
+line-breaking after the `:`, then place the first line on the same line as the
+`:`, subject to the [combining rules](expressions.html#combinable-expressions).
 
 
 ```rust
@@ -51,12 +51,12 @@ let (abcd,
 ```
 
 If the expression covers multiple lines, if the first line of the expression
-fits in the remaining space, it stays on the same line as the `=`, the rest of the
-expression is not indented. If the first line does not fit, then it should start
-on the next lines, and should be block indented. If the expression is a block
-and the type or pattern cover multiple lines, then the opening brace should be
-on a new line and not indented (this provides separation for the interior of the
-block from the type), otherwise the opening brace follows the `=`.
+fits in the remaining space, it stays on the same line as the `=`, and the rest
+of the expression is not further indented. If the first line does not fit, then
+put the expression on subsequent lines, block indented. If the expression is a
+block and the type or pattern cover multiple lines, put the opening brace on a
+new line and not indented (this provides separation for the interior of the
+block from the type); otherwise, the opening brace follows the `=`.
 
 Examples:
 
@@ -108,8 +108,8 @@ In this case, always apply the same formatting rules to the components preceding
 the `else` block (i.e. the `let pattern: Type = initializer_expr` portion)
 as described [for other let statements](#let-statements).
 
-The entire let-else statement may be formatted on a single line if all the
-following are true:
+Format the entire let-else statement on a single line if all the following are
+true:
 
 * the entire statement is *short*
 * the `else` block contains only a single-line expression and no statements
@@ -120,9 +120,6 @@ following are true:
 let Some(1) = opt else { return };
 ```
 
-Formatters may allow users to configure the value of the threshold
-used to determine whether a let-else statement is *short*.
-
 Otherwise, the let-else statement requires some line breaks.
 
 If breaking a let-else statement across multiple lines, never break between the
@@ -157,9 +154,9 @@ before the `else`.
     };
 ```
 
-If the initializer expression is multi-line, the `else` keyword and opening
-brace of the block (i.e. `else {`) should be put on the same line as the end of
-the initializer expression, with a space between them, if and only if all the
+If the initializer expression is multi-line, put the `else` keyword and opening
+brace of the block (i.e. `else {`) on the same line as the end of the
+initializer expression, with a space between them, if and only if all the
 following are true:
 
 * The initializer expression ends with one or more closing
@@ -182,9 +179,9 @@ let Some(x) = y.foo(
 }
 ```
 
-Otherwise, the `else` keyword and opening brace should be placed on the next
-line after the end of the initializer expression, and the `else` keyword should
-have the same indentation level as the `let` keyword.
+Otherwise, put the `else` keyword and opening brace on the next line after the
+end of the initializer expression, with the `else` keyword at the same
+indentation level as the `let` keyword.
 
 For example:
 
@@ -236,9 +233,9 @@ fn main() {
 
 ### Macros in statement position
 
-A macro use in statement position should use parentheses or square brackets as
-delimiters and should be terminated with a semicolon. There should be no spaces
-between the name, `!`, the delimiters, or the `;`.
+For a macro use in statement position, use parentheses or square brackets as
+delimiters, and terminate it with a semicolon. Do not put spaces around the
+name, `!`, the delimiters, or the `;`.
 
 ```rust
 // A comment.
@@ -248,14 +245,14 @@ a_macro!(...);
 
 ### Expressions in statement position
 
-There should be no space between the expression and the semicolon.
+Do not put space between the expression and the semicolon.
 
 ```
 <expr>;
 ```
 
-All expressions in statement position should be terminated with a semicolon,
-unless they end with a block or are used as the value for a block.
+Terminate all expressions in statement position with a semicolon, unless they
+end with a block or are used as the value for a block.
 
 E.g.,
 
diff --git a/src/doc/style-guide/src/types.md b/src/doc/style-guide/src/types.md
index ae456ef21c8..15b001d4f2e 100644
--- a/src/doc/style-guide/src/types.md
+++ b/src/doc/style-guide/src/types.md
@@ -7,14 +7,14 @@
 * `*const T`, `*mut T` (no space after `*`, space before type)
 * `&'a T`, `&T`, `&'a mut T`, `&mut T` (no space after `&`, single spaces separating other words)
 * `unsafe extern "C" fn<'a, 'b, 'c>(T, U, V) -> W` or `fn()` (single spaces around keywords and sigils, and after commas, no trailing commas, no spaces around brackets)
-* `!` should be treated like any other type name, `Name`
+* `!` gets treated like any other type name, `Name`
 * `(A, B, C, D)` (spaces after commas, no spaces around parens, no trailing comma unless it is a one-tuple)
 * `<Baz<T> as SomeTrait>::Foo::Bar` or `Foo::Bar` or `::Foo::Bar` (no spaces around `::` or angle brackets, single spaces around `as`)
 * `Foo::Bar<T, U, V>` (spaces after commas, no trailing comma, no spaces around angle brackets)
 * `T + T + T` (single spaces between types, and `+`).
 * `impl T + T + T` (single spaces between keyword, types, and `+`).
 
-Parentheses used in types should not be surrounded by whitespace, e.g., `(Foo)`
+Do not put space around parentheses used in types, e.g., `(Foo)`
 
 
 ### Line breaks
@@ -37,13 +37,17 @@ Foo<Bar, Baz<
 >>
 ```
 
-`[T; expr]` may be broken after the `;` if necessary.
+If a type requires line-breaks in order to fit, this section outlines where to
+break such types if necessary.
 
-Function types may be broken following the rules for function declarations.
+Break `[T; expr]` after the `;` if necessary.
 
-Generic types may be broken following the rules for generics.
+Break function types following the rules for function declarations.
 
-Types with `+` may be broken after any `+` using block indent and breaking before the `+`. When breaking such a type, all `+`s should be line broken, e.g.,
+Break generic types following the rules for generics.
+
+Break types with `+` by breaking before the `+` and block-indenting the
+subsequent lines. When breaking such a type, break before *every* `+`:
 
 ```rust
 impl Clone
diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs
index 5e2e2d24950..cfd9875e1c8 100644
--- a/src/librustdoc/clean/inline.rs
+++ b/src/librustdoc/clean/inline.rs
@@ -12,6 +12,7 @@ use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{DefId, DefIdSet, LocalDefId};
 use rustc_hir::Mutability;
 use rustc_metadata::creader::{CStore, LoadedMacro};
+use rustc_middle::ty::fast_reject::SimplifiedType;
 use rustc_middle::ty::{self, TyCtxt};
 use rustc_span::hygiene::MacroKind;
 use rustc_span::symbol::{kw, sym, Symbol};
@@ -314,9 +315,8 @@ pub(crate) fn build_impls(
     // * https://github.com/rust-lang/rust/pull/99917 — where the feature got used
     // * https://github.com/rust-lang/rust/issues/53487 — overall tracking issue for Error
     if tcx.has_attr(did, sym::rustc_has_incoherent_inherent_impls) {
-        use rustc_middle::ty::fast_reject::SimplifiedType::*;
         let type_ =
-            if tcx.is_trait(did) { TraitSimplifiedType(did) } else { AdtSimplifiedType(did) };
+            if tcx.is_trait(did) { SimplifiedType::Trait(did) } else { SimplifiedType::Adt(did) };
         for &did in tcx.incoherent_impls(type_) {
             build_impl(cx, did, attrs, ret);
         }
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index cfe62407fd3..ddef165a054 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -1776,7 +1776,6 @@ impl PrimitiveType {
     }
 
     pub(crate) fn simplified_types() -> &'static SimplifiedTypes {
-        use ty::fast_reject::SimplifiedType::*;
         use ty::{FloatTy, IntTy, UintTy};
         use PrimitiveType::*;
         static CELL: OnceCell<SimplifiedTypes> = OnceCell::new();
@@ -1784,38 +1783,38 @@ impl PrimitiveType {
         let single = |x| iter::once(x).collect();
         CELL.get_or_init(move || {
             map! {
-                Isize => single(IntSimplifiedType(IntTy::Isize)),
-                I8 => single(IntSimplifiedType(IntTy::I8)),
-                I16 => single(IntSimplifiedType(IntTy::I16)),
-                I32 => single(IntSimplifiedType(IntTy::I32)),
-                I64 => single(IntSimplifiedType(IntTy::I64)),
-                I128 => single(IntSimplifiedType(IntTy::I128)),
-                Usize => single(UintSimplifiedType(UintTy::Usize)),
-                U8 => single(UintSimplifiedType(UintTy::U8)),
-                U16 => single(UintSimplifiedType(UintTy::U16)),
-                U32 => single(UintSimplifiedType(UintTy::U32)),
-                U64 => single(UintSimplifiedType(UintTy::U64)),
-                U128 => single(UintSimplifiedType(UintTy::U128)),
-                F32 => single(FloatSimplifiedType(FloatTy::F32)),
-                F64 => single(FloatSimplifiedType(FloatTy::F64)),
-                Str => single(StrSimplifiedType),
-                Bool => single(BoolSimplifiedType),
-                Char => single(CharSimplifiedType),
-                Array => single(ArraySimplifiedType),
-                Slice => single(SliceSimplifiedType),
+                Isize => single(SimplifiedType::Int(IntTy::Isize)),
+                I8 => single(SimplifiedType::Int(IntTy::I8)),
+                I16 => single(SimplifiedType::Int(IntTy::I16)),
+                I32 => single(SimplifiedType::Int(IntTy::I32)),
+                I64 => single(SimplifiedType::Int(IntTy::I64)),
+                I128 => single(SimplifiedType::Int(IntTy::I128)),
+                Usize => single(SimplifiedType::Uint(UintTy::Usize)),
+                U8 => single(SimplifiedType::Uint(UintTy::U8)),
+                U16 => single(SimplifiedType::Uint(UintTy::U16)),
+                U32 => single(SimplifiedType::Uint(UintTy::U32)),
+                U64 => single(SimplifiedType::Uint(UintTy::U64)),
+                U128 => single(SimplifiedType::Uint(UintTy::U128)),
+                F32 => single(SimplifiedType::Float(FloatTy::F32)),
+                F64 => single(SimplifiedType::Float(FloatTy::F64)),
+                Str => single(SimplifiedType::Str),
+                Bool => single(SimplifiedType::Bool),
+                Char => single(SimplifiedType::Char),
+                Array => single(SimplifiedType::Array),
+                Slice => single(SimplifiedType::Slice),
                 // FIXME: If we ever add an inherent impl for tuples
                 // with different lengths, they won't show in rustdoc.
                 //
                 // Either manually update this arrayvec at this point
                 // or start with a more complex refactoring.
-                Tuple => [TupleSimplifiedType(1), TupleSimplifiedType(2), TupleSimplifiedType(3)].into(),
-                Unit => single(TupleSimplifiedType(0)),
-                RawPointer => [PtrSimplifiedType(Mutability::Not), PtrSimplifiedType(Mutability::Mut)].into_iter().collect(),
-                Reference => [RefSimplifiedType(Mutability::Not), RefSimplifiedType(Mutability::Mut)].into_iter().collect(),
+                Tuple => [SimplifiedType::Tuple(1), SimplifiedType::Tuple(2), SimplifiedType::Tuple(3)].into(),
+                Unit => single(SimplifiedType::Tuple(0)),
+                RawPointer => [SimplifiedType::Ptr(Mutability::Not), SimplifiedType::Ptr(Mutability::Mut)].into_iter().collect(),
+                Reference => [SimplifiedType::Ref(Mutability::Not), SimplifiedType::Ref(Mutability::Mut)].into_iter().collect(),
                 // FIXME: This will be wrong if we ever add inherent impls
                 // for function pointers.
-                Fn => single(FunctionSimplifiedType(1)),
-                Never => single(NeverSimplifiedType),
+                Fn => single(SimplifiedType::Function(1)),
+                Never => single(SimplifiedType::Never),
             }
         })
     }
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 575ce6aa99a..4d029407afa 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -820,6 +820,7 @@ fn assoc_method(
     let header = meth.fn_header(tcx).expect("Trying to get header from a non-function item");
     let name = meth.name.as_ref().unwrap();
     let vis = visibility_print_with_space(meth.visibility(tcx), meth.item_id, cx).to_string();
+    let defaultness = print_default_space(meth.is_default());
     // FIXME: Once https://github.com/rust-lang/rust/issues/67792 is implemented, we can remove
     // this condition.
     let constness = match render_mode {
@@ -830,7 +831,6 @@ fn assoc_method(
     };
     let asyncness = header.asyncness.print_with_space();
     let unsafety = header.unsafety.print_with_space();
-    let defaultness = print_default_space(meth.is_default());
     let abi = print_abi_with_space(header.abi).to_string();
     let href = assoc_href_attr(meth, link, cx);
 
@@ -838,10 +838,10 @@ fn assoc_method(
     let generics_len = format!("{:#}", g.print(cx)).len();
     let mut header_len = "fn ".len()
         + vis.len()
+        + defaultness.len()
         + constness.len()
         + asyncness.len()
         + unsafety.len()
-        + defaultness.len()
         + abi.len()
         + name.as_str().len()
         + generics_len;
@@ -860,14 +860,14 @@ fn assoc_method(
     w.reserve(header_len + "<a href=\"\" class=\"fn\">{".len() + "</a>".len());
     write!(
         w,
-        "{indent}{vis}{constness}{asyncness}{unsafety}{defaultness}{abi}fn \
+        "{indent}{vis}{defaultness}{constness}{asyncness}{unsafety}{abi}fn \
          <a{href} class=\"fn\">{name}</a>{generics}{decl}{notable_traits}{where_clause}",
         indent = indent_str,
         vis = vis,
+        defaultness = defaultness,
         constness = constness,
         asyncness = asyncness,
         unsafety = unsafety,
-        defaultness = defaultness,
         abi = abi,
         href = href,
         name = name,
diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css
index 94e778406f8..b1de8c1529e 100644
--- a/src/librustdoc/html/static/css/rustdoc.css
+++ b/src/librustdoc/html/static/css/rustdoc.css
@@ -776,7 +776,6 @@ table,
 }
 #crate-search {
 	min-width: 115px;
-	/* keep these two in sync with "@-moz-document url-prefix()" below */
 	padding: 0 23px 0 4px;
 	/* prevents the <select> from overflowing the containing div in case it's shrunk */
 	max-width: 100%;
@@ -798,14 +797,6 @@ table,
 #crate-search:hover, #crate-search:focus {
 	border-color: var(--crate-search-hover-border);
 }
-/* cancel stylistic differences in padding in firefox
-for "appearance: none"-style (or equivalent) <select>s */
-@-moz-document url-prefix() {
-	#crate-search {
-		padding-left: 0px; /* == 4px - 4px */
-		padding-right: 19px; /* == 23px - 4px */
-	}
-}
 /* pseudo-element for holding the dropdown-arrow image; needs to be a separate thing
 so that we can apply CSS-filters to change the arrow color in themes */
 #crate-search-div::after {
diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs
index 94b56304bca..e4906944c8d 100644
--- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs
@@ -74,10 +74,10 @@ pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool {
     let lang_items = cx.tcx.lang_items();
     // This list isn't complete, but good enough for our current list of paths.
     let incoherent_impls = [
-        SimplifiedType::FloatSimplifiedType(FloatTy::F32),
-        SimplifiedType::FloatSimplifiedType(FloatTy::F64),
-        SimplifiedType::SliceSimplifiedType,
-        SimplifiedType::StrSimplifiedType,
+        SimplifiedType::Float(FloatTy::F32),
+        SimplifiedType::Float(FloatTy::F64),
+        SimplifiedType::Slice,
+        SimplifiedType::Str,
     ]
     .iter()
     .flat_map(|&ty| cx.tcx.incoherent_impls(ty).iter().copied());
diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs
index 00e893fbdda..035511e8912 100644
--- a/src/tools/clippy/clippy_utils/src/lib.rs
+++ b/src/tools/clippy/clippy_utils/src/lib.rs
@@ -100,10 +100,7 @@ use rustc_middle::mir::ConstantKind;
 use rustc_middle::ty as rustc_ty;
 use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
 use rustc_middle::ty::binding::BindingMode;
-use rustc_middle::ty::fast_reject::SimplifiedType::{
-    ArraySimplifiedType, BoolSimplifiedType, CharSimplifiedType, FloatSimplifiedType, IntSimplifiedType,
-    PtrSimplifiedType, SliceSimplifiedType, StrSimplifiedType, UintSimplifiedType,
-};
+use rustc_middle::ty::fast_reject::SimplifiedType;
 use rustc_middle::ty::layout::IntegerExt;
 use rustc_middle::ty::{
     BorrowKind, ClosureKind, FloatTy, IntTy, Ty, TyCtxt, TypeAndMut, TypeVisitableExt, UintTy, UpvarCapture,
@@ -512,30 +509,30 @@ pub fn path_def_id<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>
 
 fn find_primitive_impls<'tcx>(tcx: TyCtxt<'tcx>, name: &str) -> impl Iterator<Item = DefId> + 'tcx {
     let ty = match name {
-        "bool" => BoolSimplifiedType,
-        "char" => CharSimplifiedType,
-        "str" => StrSimplifiedType,
-        "array" => ArraySimplifiedType,
-        "slice" => SliceSimplifiedType,
+        "bool" => SimplifiedType::Bool,
+        "char" => SimplifiedType::Char,
+        "str" => SimplifiedType::Str,
+        "array" => SimplifiedType::Array,
+        "slice" => SimplifiedType::Slice,
         // FIXME: rustdoc documents these two using just `pointer`.
         //
         // Maybe this is something we should do here too.
-        "const_ptr" => PtrSimplifiedType(Mutability::Not),
-        "mut_ptr" => PtrSimplifiedType(Mutability::Mut),
-        "isize" => IntSimplifiedType(IntTy::Isize),
-        "i8" => IntSimplifiedType(IntTy::I8),
-        "i16" => IntSimplifiedType(IntTy::I16),
-        "i32" => IntSimplifiedType(IntTy::I32),
-        "i64" => IntSimplifiedType(IntTy::I64),
-        "i128" => IntSimplifiedType(IntTy::I128),
-        "usize" => UintSimplifiedType(UintTy::Usize),
-        "u8" => UintSimplifiedType(UintTy::U8),
-        "u16" => UintSimplifiedType(UintTy::U16),
-        "u32" => UintSimplifiedType(UintTy::U32),
-        "u64" => UintSimplifiedType(UintTy::U64),
-        "u128" => UintSimplifiedType(UintTy::U128),
-        "f32" => FloatSimplifiedType(FloatTy::F32),
-        "f64" => FloatSimplifiedType(FloatTy::F64),
+        "const_ptr" => SimplifiedType::Ptr(Mutability::Not),
+        "mut_ptr" => SimplifiedType::Ptr(Mutability::Mut),
+        "isize" => SimplifiedType::Int(IntTy::Isize),
+        "i8" => SimplifiedType::Int(IntTy::I8),
+        "i16" => SimplifiedType::Int(IntTy::I16),
+        "i32" => SimplifiedType::Int(IntTy::I32),
+        "i64" => SimplifiedType::Int(IntTy::I64),
+        "i128" => SimplifiedType::Int(IntTy::I128),
+        "usize" => SimplifiedType::Uint(UintTy::Usize),
+        "u8" => SimplifiedType::Uint(UintTy::U8),
+        "u16" => SimplifiedType::Uint(UintTy::U16),
+        "u32" => SimplifiedType::Uint(UintTy::U32),
+        "u64" => SimplifiedType::Uint(UintTy::U64),
+        "u128" => SimplifiedType::Uint(UintTy::U128),
+        "f32" => SimplifiedType::Float(FloatTy::F32),
+        "f64" => SimplifiedType::Float(FloatTy::F64),
         _ => return [].iter().copied(),
     };
 
diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version
index da89b71ccd5..6f8ad244fa2 100644
--- a/src/tools/miri/rust-version
+++ b/src/tools/miri/rust-version
@@ -1 +1 @@
-0646a5d1aa3745cb448db247f6fa432890a1812b
+a5e2eca40ec17f17b6641bcc7c069380ac395acf
diff --git a/src/tools/miri/src/intptrcast.rs b/src/tools/miri/src/intptrcast.rs
index 4fd0af35304..a43ac61da74 100644
--- a/src/tools/miri/src/intptrcast.rs
+++ b/src/tools/miri/src/intptrcast.rs
@@ -207,7 +207,7 @@ impl<'mir, 'tcx> GlobalStateInner {
                     .checked_add(max(size.bytes(), 1))
                     .ok_or_else(|| err_exhaust!(AddressSpaceFull))?;
                 // Even if `Size` didn't overflow, we might still have filled up the address space.
-                if global_state.next_base_addr > ecx.target_usize_max() {
+                if global_state.next_base_addr > ecx.data_layout().target_usize_max() {
                     throw_exhaust!(AddressSpaceFull);
                 }
                 // Given that `next_base_addr` increases in each allocation, pushing the
diff --git a/src/tools/miri/src/shims/mod.rs b/src/tools/miri/src/shims/mod.rs
index 1027b24e301..63e55e7369a 100644
--- a/src/tools/miri/src/shims/mod.rs
+++ b/src/tools/miri/src/shims/mod.rs
@@ -20,6 +20,7 @@ pub mod tls;
 use log::trace;
 
 use rustc_middle::{mir, ty};
+use rustc_target::abi::HasDataLayout as _;
 use rustc_target::spec::abi::Abi;
 
 use crate::*;
@@ -108,7 +109,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
         }
 
         // Return error result (usize::MAX), and jump to caller.
-        this.write_scalar(Scalar::from_target_usize(this.target_usize_max(), this), dest)?;
+        let usize_max = this.data_layout().target_usize_max();
+        this.write_scalar(Scalar::from_target_usize(usize_max, this), dest)?;
         this.go_to_block(ret);
         Ok(true)
     }
diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs
index 0fdd55b407c..b973a9e4766 100644
--- a/src/tools/miri/src/shims/unix/fs.rs
+++ b/src/tools/miri/src/shims/unix/fs.rs
@@ -12,7 +12,7 @@ use log::trace;
 
 use rustc_data_structures::fx::FxHashMap;
 use rustc_middle::ty::TyCtxt;
-use rustc_target::abi::{Align, Size};
+use rustc_target::abi::{Align, HasDataLayout as _, Size};
 
 use crate::shims::os_str::bytes_to_os_str;
 use crate::*;
@@ -753,7 +753,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
         // We cap the number of read bytes to the largest value that we are able to fit in both the
         // host's and target's `isize`. This saves us from having to handle overflows later.
         let count = count
-            .min(u64::try_from(this.target_isize_max()).unwrap())
+            .min(u64::try_from(this.data_layout().target_isize_max()).unwrap())
             .min(u64::try_from(isize::MAX).unwrap());
         let communicate = this.machine.communicate();
 
@@ -807,7 +807,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
         // We cap the number of written bytes to the largest value that we are able to fit in both the
         // host's and target's `isize`. This saves us from having to handle overflows later.
         let count = count
-            .min(u64::try_from(this.target_isize_max()).unwrap())
+            .min(u64::try_from(this.data_layout().target_isize_max()).unwrap())
             .min(u64::try_from(isize::MAX).unwrap());
         let communicate = this.machine.communicate();
 
diff --git a/src/tools/miri/tests/fail/layout_cycle.rs b/src/tools/miri/tests/fail/layout_cycle.rs
index 3e0dd881db8..d6a937de15c 100644
--- a/src/tools/miri/tests/fail/layout_cycle.rs
+++ b/src/tools/miri/tests/fail/layout_cycle.rs
@@ -1,5 +1,5 @@
 //@error-in-other-file: a cycle occurred during layout computation
-//~^ ERROR: cycle detected when computing layout of
+//~^ ERROR: cycle detected when computing layout (naive) of
 
 use std::mem;
 
diff --git a/src/tools/miri/tests/fail/layout_cycle.stderr b/src/tools/miri/tests/fail/layout_cycle.stderr
index 38907a1c50c..ccf93a9def4 100644
--- a/src/tools/miri/tests/fail/layout_cycle.stderr
+++ b/src/tools/miri/tests/fail/layout_cycle.stderr
@@ -1,7 +1,8 @@
-error[E0391]: cycle detected when computing layout of `S<S<()>>`
+error[E0391]: cycle detected when computing layout (naive) of `S<S<()>>`
    |
-   = note: ...which requires computing layout of `<S<()> as Tr>::I`...
-   = note: ...which again requires computing layout of `S<S<()>>`, completing the cycle
+   = note: ...which requires computing layout (naive) of `<S<()> as Tr>::I`...
+   = note: ...which again requires computing layout (naive) of `S<S<()>>`, completing the cycle
+   = note: cycle used when computing layout of `S<S<()>>`
    = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
 
 error: post-monomorphization error: a cycle occurred during layout computation
diff --git a/tests/codegen/iter-repeat-n-trivial-drop.rs b/tests/codegen/iter-repeat-n-trivial-drop.rs
index 65a0f7e7ffb..0b08e578151 100644
--- a/tests/codegen/iter-repeat-n-trivial-drop.rs
+++ b/tests/codegen/iter-repeat-n-trivial-drop.rs
@@ -33,7 +33,8 @@ pub fn iter_repeat_n_next(it: &mut std::iter::RepeatN<NotCopy>) -> Option<NotCop
 
     // CHECK: [[EMPTY]]:
     // CHECK-NOT: br
-    // CHECK: phi i16 [ %[[VAL]], %[[NOT_EMPTY]] ], [ undef, %start ]
+    // CHECK: phi i16
+    // CHECK-SAME: [ %[[VAL]], %[[NOT_EMPTY]] ]
     // CHECK-NOT: br
     // CHECK: ret
 
diff --git a/tests/codegen/llvm-ident.rs b/tests/codegen/llvm-ident.rs
new file mode 100644
index 00000000000..927f0d602ad
--- /dev/null
+++ b/tests/codegen/llvm-ident.rs
@@ -0,0 +1,15 @@
+// Verifies that the `!llvm.ident` named metadata is emitted.
+//
+// revisions: NONE OPT DEBUG
+//
+// [OPT] compile-flags: -Copt-level=2
+// [DEBUG] compile-flags: -Cdebuginfo=2
+
+// The named metadata should contain a single metadata node (see
+// `LLVMRustPrepareThinLTOImport` for details).
+// CHECK: !llvm.ident = !{![[ID:[0-9]+]]}
+
+// In addition, check that the metadata node has the expected content.
+// CHECK: ![[ID]] = !{!"rustc version 1.{{.*}}"}
+
+fn main() {}
diff --git a/tests/codegen/move-operands.rs b/tests/codegen/move-operands.rs
index 1d8209e8ea5..df4fbe29ffd 100644
--- a/tests/codegen/move-operands.rs
+++ b/tests/codegen/move-operands.rs
@@ -1,4 +1,5 @@
-// compile-flags: -C no-prepopulate-passes -Zmir-enable-passes=+DestinationPropagation,-CopyProp
+// Verify that optimized MIR only copies `a` once.
+// compile-flags: -O -C no-prepopulate-passes
 
 #![crate_type = "lib"]
 
diff --git a/tests/codegen/slice-iter-len-eq-zero.rs b/tests/codegen/slice-iter-len-eq-zero.rs
index 69f78cea564..efa7b6a9680 100644
--- a/tests/codegen/slice-iter-len-eq-zero.rs
+++ b/tests/codegen/slice-iter-len-eq-zero.rs
@@ -9,8 +9,8 @@ type Demo = [u8; 3];
 #[no_mangle]
 pub fn slice_iter_len_eq_zero(y: std::slice::Iter<'_, Demo>) -> bool {
     // CHECK-NOT: sub
-    // CHECK: %_0 = icmp eq {{i8\*|ptr}} {{%1|%0}}, {{%1|%0}}
-    // CHECK: ret i1 %_0
+    // CHECK: %[[RET:.+]] = icmp eq {{i8\*|ptr}} {{%1|%0}}, {{%1|%0}}
+    // CHECK: ret i1 %[[RET]]
     y.len() == 0
 }
 
diff --git a/tests/codegen/slice-iter-nonnull.rs b/tests/codegen/slice-iter-nonnull.rs
index 997bdaf5636..f7d164bc856 100644
--- a/tests/codegen/slice-iter-nonnull.rs
+++ b/tests/codegen/slice-iter-nonnull.rs
@@ -2,11 +2,16 @@
 // compile-flags: -O
 // ignore-debug (these add extra checks that make it hard to verify)
 #![crate_type = "lib"]
+#![feature(exact_size_is_empty)]
 
 // The slice iterator used to `assume` that the `start` pointer was non-null.
 // That ought to be unneeded, though, since the type is `NonNull`, so this test
 // confirms that the appropriate metadata is included to denote that.
 
+// It also used to `assume` the `end` pointer was non-null, but that's no longer
+// needed as the code changed to read it as a `NonNull`, and thus gets the
+// appropriate `!nonnull` annotations naturally.
+
 // CHECK-LABEL: @slice_iter_next(
 #[no_mangle]
 pub fn slice_iter_next<'a>(it: &mut std::slice::Iter<'a, u32>) -> Option<&'a u32> {
@@ -75,3 +80,37 @@ pub fn slice_iter_mut_new(slice: &mut [u32]) -> std::slice::IterMut<'_, u32> {
     // CHECK: }
     slice.iter_mut()
 }
+
+// CHECK-LABEL: @slice_iter_is_empty
+#[no_mangle]
+pub fn slice_iter_is_empty(it: &std::slice::Iter<'_, u32>) -> bool {
+    // CHECK: %[[ENDP:.+]] = getelementptr{{.+}}ptr %it,{{.+}} 1
+    // CHECK: %[[END:.+]] = load ptr, ptr %[[ENDP]]
+    // CHECK-SAME: !nonnull
+    // CHECK-SAME: !noundef
+    // CHECK: %[[START:.+]] = load ptr, ptr %it,
+    // CHECK-SAME: !nonnull
+    // CHECK-SAME: !noundef
+
+    // CHECK: %[[RET:.+]] = icmp eq ptr %[[START]], %[[END]]
+    // CHECK: ret i1 %[[RET]]
+    it.is_empty()
+}
+
+// CHECK-LABEL: @slice_iter_len
+#[no_mangle]
+pub fn slice_iter_len(it: &std::slice::Iter<'_, u32>) -> usize {
+    // CHECK: %[[START:.+]] = load ptr, ptr %it,
+    // CHECK-SAME: !nonnull
+    // CHECK-SAME: !noundef
+    // CHECK: %[[ENDP:.+]] = getelementptr{{.+}}ptr %it,{{.+}} 1
+    // CHECK: %[[END:.+]] = load ptr, ptr %[[ENDP]]
+    // CHECK-SAME: !nonnull
+    // CHECK-SAME: !noundef
+
+    // CHECK: ptrtoint
+    // CHECK: ptrtoint
+    // CHECK: sub nuw
+    // CHECK: lshr exact
+    it.len()
+}
diff --git a/tests/incremental/hashes/trait_defs.rs b/tests/incremental/hashes/trait_defs.rs
index b583bee2f24..7b8c6245d2d 100644
--- a/tests/incremental/hashes/trait_defs.rs
+++ b/tests/incremental/hashes/trait_defs.rs
@@ -420,13 +420,13 @@ trait TraitAddExternModifier {
     // -------------------------
     // --------------------------------------------------------------------
     // -------------------------
-    fn method()           ;
+    fn            method();
 }
 
 #[cfg(not(any(cfail1,cfail4)))]
 #[rustc_clean(cfg="cfail2")]
 #[rustc_clean(cfg="cfail3")]
-#[rustc_clean(except="hir_owner", cfg="cfail5")]
+#[rustc_clean(cfg="cfail5")]
 #[rustc_clean(cfg="cfail6")]
 trait TraitAddExternModifier {
     #[rustc_clean(except="hir_owner,hir_owner_nodes,fn_sig", cfg="cfail2")]
diff --git a/tests/mir-opt/const_prop/aggregate.main.ConstProp.panic-abort.diff b/tests/mir-opt/const_prop/aggregate.main.ConstProp.panic-abort.diff
index 0fad2310442..a4911a6d48a 100644
--- a/tests/mir-opt/const_prop/aggregate.main.ConstProp.panic-abort.diff
+++ b/tests/mir-opt/const_prop/aggregate.main.ConstProp.panic-abort.diff
@@ -26,8 +26,9 @@
           StorageLive(_4);
           StorageLive(_5);
 -         _5 = _1;
+-         _4 = foo(move _5) -> [return: bb1, unwind unreachable];
 +         _5 = const 1_u8;
-          _4 = foo(move _5) -> [return: bb1, unwind unreachable];
++         _4 = foo(const 1_u8) -> [return: bb1, unwind unreachable];
       }
   
       bb1: {
diff --git a/tests/mir-opt/const_prop/aggregate.main.ConstProp.panic-unwind.diff b/tests/mir-opt/const_prop/aggregate.main.ConstProp.panic-unwind.diff
index e4650046bdf..b8b9fa5cc1c 100644
--- a/tests/mir-opt/const_prop/aggregate.main.ConstProp.panic-unwind.diff
+++ b/tests/mir-opt/const_prop/aggregate.main.ConstProp.panic-unwind.diff
@@ -26,8 +26,9 @@
           StorageLive(_4);
           StorageLive(_5);
 -         _5 = _1;
+-         _4 = foo(move _5) -> [return: bb1, unwind continue];
 +         _5 = const 1_u8;
-          _4 = foo(move _5) -> [return: bb1, unwind continue];
++         _4 = foo(const 1_u8) -> [return: bb1, unwind continue];
       }
   
       bb1: {
diff --git a/tests/mir-opt/const_prop/aggregate.main.PreCodegen.after.panic-abort.mir b/tests/mir-opt/const_prop/aggregate.main.PreCodegen.after.panic-abort.mir
index 2ab6c1bf34d..44a85a5636b 100644
--- a/tests/mir-opt/const_prop/aggregate.main.PreCodegen.after.panic-abort.mir
+++ b/tests/mir-opt/const_prop/aggregate.main.PreCodegen.after.panic-abort.mir
@@ -23,7 +23,7 @@ fn main() -> () {
         StorageLive(_4);
         StorageLive(_5);
         _5 = const 1_u8;
-        _4 = foo(move _5) -> [return: bb1, unwind unreachable];
+        _4 = foo(const 1_u8) -> [return: bb1, unwind unreachable];
     }
 
     bb1: {
diff --git a/tests/mir-opt/const_prop/aggregate.main.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/const_prop/aggregate.main.PreCodegen.after.panic-unwind.mir
index 9590c7f90aa..2c7bdbb5055 100644
--- a/tests/mir-opt/const_prop/aggregate.main.PreCodegen.after.panic-unwind.mir
+++ b/tests/mir-opt/const_prop/aggregate.main.PreCodegen.after.panic-unwind.mir
@@ -23,7 +23,7 @@ fn main() -> () {
         StorageLive(_4);
         StorageLive(_5);
         _5 = const 1_u8;
-        _4 = foo(move _5) -> [return: bb1, unwind continue];
+        _4 = foo(const 1_u8) -> [return: bb1, unwind continue];
     }
 
     bb1: {
diff --git a/tests/mir-opt/const_prop/array_index.main.ConstProp.32bit.panic-abort.diff b/tests/mir-opt/const_prop/array_index.main.ConstProp.32bit.panic-abort.diff
index 012b11e0e38..b2f58f8f771 100644
--- a/tests/mir-opt/const_prop/array_index.main.ConstProp.32bit.panic-abort.diff
+++ b/tests/mir-opt/const_prop/array_index.main.ConstProp.32bit.panic-abort.diff
@@ -23,7 +23,7 @@
 -         assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, _3) -> [success: bb1, unwind unreachable];
 +         _4 = const 4_usize;
 +         _5 = const true;
-+         assert(const true, "index out of bounds: the length is {} but the index is {}", move _4, _3) -> [success: bb1, unwind unreachable];
++         assert(const true, "index out of bounds: the length is {} but the index is {}", const 4_usize, const 2_usize) -> [success: bb1, unwind unreachable];
       }
   
       bb1: {
diff --git a/tests/mir-opt/const_prop/array_index.main.ConstProp.32bit.panic-unwind.diff b/tests/mir-opt/const_prop/array_index.main.ConstProp.32bit.panic-unwind.diff
index ec11395c376..f9e3f8f171a 100644
--- a/tests/mir-opt/const_prop/array_index.main.ConstProp.32bit.panic-unwind.diff
+++ b/tests/mir-opt/const_prop/array_index.main.ConstProp.32bit.panic-unwind.diff
@@ -23,7 +23,7 @@
 -         assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, _3) -> [success: bb1, unwind continue];
 +         _4 = const 4_usize;
 +         _5 = const true;
-+         assert(const true, "index out of bounds: the length is {} but the index is {}", move _4, _3) -> [success: bb1, unwind continue];
++         assert(const true, "index out of bounds: the length is {} but the index is {}", const 4_usize, const 2_usize) -> [success: bb1, unwind continue];
       }
   
       bb1: {
diff --git a/tests/mir-opt/const_prop/array_index.main.ConstProp.64bit.panic-abort.diff b/tests/mir-opt/const_prop/array_index.main.ConstProp.64bit.panic-abort.diff
index 012b11e0e38..b2f58f8f771 100644
--- a/tests/mir-opt/const_prop/array_index.main.ConstProp.64bit.panic-abort.diff
+++ b/tests/mir-opt/const_prop/array_index.main.ConstProp.64bit.panic-abort.diff
@@ -23,7 +23,7 @@
 -         assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, _3) -> [success: bb1, unwind unreachable];
 +         _4 = const 4_usize;
 +         _5 = const true;
-+         assert(const true, "index out of bounds: the length is {} but the index is {}", move _4, _3) -> [success: bb1, unwind unreachable];
++         assert(const true, "index out of bounds: the length is {} but the index is {}", const 4_usize, const 2_usize) -> [success: bb1, unwind unreachable];
       }
   
       bb1: {
diff --git a/tests/mir-opt/const_prop/array_index.main.ConstProp.64bit.panic-unwind.diff b/tests/mir-opt/const_prop/array_index.main.ConstProp.64bit.panic-unwind.diff
index ec11395c376..f9e3f8f171a 100644
--- a/tests/mir-opt/const_prop/array_index.main.ConstProp.64bit.panic-unwind.diff
+++ b/tests/mir-opt/const_prop/array_index.main.ConstProp.64bit.panic-unwind.diff
@@ -23,7 +23,7 @@
 -         assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, _3) -> [success: bb1, unwind continue];
 +         _4 = const 4_usize;
 +         _5 = const true;
-+         assert(const true, "index out of bounds: the length is {} but the index is {}", move _4, _3) -> [success: bb1, unwind continue];
++         assert(const true, "index out of bounds: the length is {} but the index is {}", const 4_usize, const 2_usize) -> [success: bb1, unwind continue];
       }
   
       bb1: {
diff --git a/tests/mir-opt/const_prop/bad_op_div_by_zero.main.ConstProp.panic-abort.diff b/tests/mir-opt/const_prop/bad_op_div_by_zero.main.ConstProp.panic-abort.diff
index 34163d4d84a..cead70110dc 100644
--- a/tests/mir-opt/const_prop/bad_op_div_by_zero.main.ConstProp.panic-abort.diff
+++ b/tests/mir-opt/const_prop/bad_op_div_by_zero.main.ConstProp.panic-abort.diff
@@ -38,11 +38,12 @@
 +         _5 = const false;
 +         _6 = const false;
 +         _7 = const false;
-+         assert(!const false, "attempt to compute `{} / {}`, which would overflow", const 1_i32, _3) -> [success: bb2, unwind unreachable];
++         assert(!const false, "attempt to compute `{} / {}`, which would overflow", const 1_i32, const 0_i32) -> [success: bb2, unwind unreachable];
       }
   
       bb2: {
-          _2 = Div(const 1_i32, move _3);
+-         _2 = Div(const 1_i32, move _3);
++         _2 = Div(const 1_i32, const 0_i32);
           StorageDead(_3);
           _0 = const ();
           StorageDead(_2);
diff --git a/tests/mir-opt/const_prop/bad_op_div_by_zero.main.ConstProp.panic-unwind.diff b/tests/mir-opt/const_prop/bad_op_div_by_zero.main.ConstProp.panic-unwind.diff
index a5b51681ec9..c9c4ba8548c 100644
--- a/tests/mir-opt/const_prop/bad_op_div_by_zero.main.ConstProp.panic-unwind.diff
+++ b/tests/mir-opt/const_prop/bad_op_div_by_zero.main.ConstProp.panic-unwind.diff
@@ -38,11 +38,12 @@
 +         _5 = const false;
 +         _6 = const false;
 +         _7 = const false;
-+         assert(!const false, "attempt to compute `{} / {}`, which would overflow", const 1_i32, _3) -> [success: bb2, unwind continue];
++         assert(!const false, "attempt to compute `{} / {}`, which would overflow", const 1_i32, const 0_i32) -> [success: bb2, unwind continue];
       }
   
       bb2: {
-          _2 = Div(const 1_i32, move _3);
+-         _2 = Div(const 1_i32, move _3);
++         _2 = Div(const 1_i32, const 0_i32);
           StorageDead(_3);
           _0 = const ();
           StorageDead(_2);
diff --git a/tests/mir-opt/const_prop/bad_op_mod_by_zero.main.ConstProp.panic-abort.diff b/tests/mir-opt/const_prop/bad_op_mod_by_zero.main.ConstProp.panic-abort.diff
index eb1c7d34f0f..2666fd9eb91 100644
--- a/tests/mir-opt/const_prop/bad_op_mod_by_zero.main.ConstProp.panic-abort.diff
+++ b/tests/mir-opt/const_prop/bad_op_mod_by_zero.main.ConstProp.panic-abort.diff
@@ -38,11 +38,12 @@
 +         _5 = const false;
 +         _6 = const false;
 +         _7 = const false;
-+         assert(!const false, "attempt to compute the remainder of `{} % {}`, which would overflow", const 1_i32, _3) -> [success: bb2, unwind unreachable];
++         assert(!const false, "attempt to compute the remainder of `{} % {}`, which would overflow", const 1_i32, const 0_i32) -> [success: bb2, unwind unreachable];
       }
   
       bb2: {
-          _2 = Rem(const 1_i32, move _3);
+-         _2 = Rem(const 1_i32, move _3);
++         _2 = Rem(const 1_i32, const 0_i32);
           StorageDead(_3);
           _0 = const ();
           StorageDead(_2);
diff --git a/tests/mir-opt/const_prop/bad_op_mod_by_zero.main.ConstProp.panic-unwind.diff b/tests/mir-opt/const_prop/bad_op_mod_by_zero.main.ConstProp.panic-unwind.diff
index 4afddf3c92d..679df90f16f 100644
--- a/tests/mir-opt/const_prop/bad_op_mod_by_zero.main.ConstProp.panic-unwind.diff
+++ b/tests/mir-opt/const_prop/bad_op_mod_by_zero.main.ConstProp.panic-unwind.diff
@@ -38,11 +38,12 @@
 +         _5 = const false;
 +         _6 = const false;
 +         _7 = const false;
-+         assert(!const false, "attempt to compute the remainder of `{} % {}`, which would overflow", const 1_i32, _3) -> [success: bb2, unwind continue];
++         assert(!const false, "attempt to compute the remainder of `{} % {}`, which would overflow", const 1_i32, const 0_i32) -> [success: bb2, unwind continue];
       }
   
       bb2: {
-          _2 = Rem(const 1_i32, move _3);
+-         _2 = Rem(const 1_i32, move _3);
++         _2 = Rem(const 1_i32, const 0_i32);
           StorageDead(_3);
           _0 = const ();
           StorageDead(_2);
diff --git a/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.32bit.panic-abort.diff b/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.32bit.panic-abort.diff
index 30402df47c2..55c774d555d 100644
--- a/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.32bit.panic-abort.diff
+++ b/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.32bit.panic-abort.diff
@@ -38,7 +38,7 @@
 -         _8 = Lt(_6, _7);
 -         assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> [success: bb1, unwind unreachable];
 +         _8 = const false;
-+         assert(const false, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> [success: bb1, unwind unreachable];
++         assert(const false, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 3_usize) -> [success: bb1, unwind unreachable];
       }
   
       bb1: {
diff --git a/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.32bit.panic-unwind.diff b/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.32bit.panic-unwind.diff
index 16d62daed29..dcab570ea77 100644
--- a/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.32bit.panic-unwind.diff
+++ b/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.32bit.panic-unwind.diff
@@ -38,7 +38,7 @@
 -         _8 = Lt(_6, _7);
 -         assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> [success: bb1, unwind continue];
 +         _8 = const false;
-+         assert(const false, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> [success: bb1, unwind continue];
++         assert(const false, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 3_usize) -> [success: bb1, unwind continue];
       }
   
       bb1: {
diff --git a/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.64bit.panic-abort.diff b/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.64bit.panic-abort.diff
index 30402df47c2..55c774d555d 100644
--- a/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.64bit.panic-abort.diff
+++ b/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.64bit.panic-abort.diff
@@ -38,7 +38,7 @@
 -         _8 = Lt(_6, _7);
 -         assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> [success: bb1, unwind unreachable];
 +         _8 = const false;
-+         assert(const false, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> [success: bb1, unwind unreachable];
++         assert(const false, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 3_usize) -> [success: bb1, unwind unreachable];
       }
   
       bb1: {
diff --git a/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.64bit.panic-unwind.diff b/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.64bit.panic-unwind.diff
index 16d62daed29..dcab570ea77 100644
--- a/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.64bit.panic-unwind.diff
+++ b/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.64bit.panic-unwind.diff
@@ -38,7 +38,7 @@
 -         _8 = Lt(_6, _7);
 -         assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> [success: bb1, unwind continue];
 +         _8 = const false;
-+         assert(const false, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> [success: bb1, unwind continue];
++         assert(const false, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 3_usize) -> [success: bb1, unwind continue];
       }
   
       bb1: {
diff --git a/tests/mir-opt/const_prop/boxes.main.ConstProp.panic-abort.diff b/tests/mir-opt/const_prop/boxes.main.ConstProp.panic-abort.diff
index 24b4796949a..c9670a5ee43 100644
--- a/tests/mir-opt/const_prop/boxes.main.ConstProp.panic-abort.diff
+++ b/tests/mir-opt/const_prop/boxes.main.ConstProp.panic-abort.diff
@@ -24,9 +24,10 @@
           StorageLive(_3);
 -         _4 = SizeOf(i32);
 -         _5 = AlignOf(i32);
+-         _6 = alloc::alloc::exchange_malloc(move _4, move _5) -> [return: bb1, unwind unreachable];
 +         _4 = const 4_usize;
 +         _5 = const 4_usize;
-          _6 = alloc::alloc::exchange_malloc(move _4, move _5) -> [return: bb1, unwind unreachable];
++         _6 = alloc::alloc::exchange_malloc(const 4_usize, const 4_usize) -> [return: bb1, unwind unreachable];
       }
   
       bb1: {
diff --git a/tests/mir-opt/const_prop/boxes.main.ConstProp.panic-unwind.diff b/tests/mir-opt/const_prop/boxes.main.ConstProp.panic-unwind.diff
index 6214766c7ee..64fe72be5c8 100644
--- a/tests/mir-opt/const_prop/boxes.main.ConstProp.panic-unwind.diff
+++ b/tests/mir-opt/const_prop/boxes.main.ConstProp.panic-unwind.diff
@@ -24,9 +24,10 @@
           StorageLive(_3);
 -         _4 = SizeOf(i32);
 -         _5 = AlignOf(i32);
+-         _6 = alloc::alloc::exchange_malloc(move _4, move _5) -> [return: bb1, unwind continue];
 +         _4 = const 4_usize;
 +         _5 = const 4_usize;
-          _6 = alloc::alloc::exchange_malloc(move _4, move _5) -> [return: bb1, unwind continue];
++         _6 = alloc::alloc::exchange_malloc(const 4_usize, const 4_usize) -> [return: bb1, unwind continue];
       }
   
       bb1: {
diff --git a/tests/mir-opt/const_prop/control_flow_simplification.hello.ConstProp.panic-abort.diff b/tests/mir-opt/const_prop/control_flow_simplification.hello.ConstProp.panic-abort.diff
index e77c09848b7..ba2e89f0a74 100644
--- a/tests/mir-opt/const_prop/control_flow_simplification.hello.ConstProp.panic-abort.diff
+++ b/tests/mir-opt/const_prop/control_flow_simplification.hello.ConstProp.panic-abort.diff
@@ -8,8 +8,9 @@
   
       bb0: {
           StorageLive(_1);
-          _1 = const _;
+-         _1 = const _;
 -         switchInt(move _1) -> [0: bb2, otherwise: bb1];
++         _1 = const false;
 +         switchInt(const false) -> [0: bb2, otherwise: bb1];
       }
   
diff --git a/tests/mir-opt/const_prop/control_flow_simplification.hello.ConstProp.panic-unwind.diff b/tests/mir-opt/const_prop/control_flow_simplification.hello.ConstProp.panic-unwind.diff
index 7496d254309..e0a610f60a7 100644
--- a/tests/mir-opt/const_prop/control_flow_simplification.hello.ConstProp.panic-unwind.diff
+++ b/tests/mir-opt/const_prop/control_flow_simplification.hello.ConstProp.panic-unwind.diff
@@ -8,8 +8,9 @@
   
       bb0: {
           StorageLive(_1);
-          _1 = const _;
+-         _1 = const _;
 -         switchInt(move _1) -> [0: bb2, otherwise: bb1];
++         _1 = const false;
 +         switchInt(const false) -> [0: bb2, otherwise: bb1];
       }
   
diff --git a/tests/mir-opt/const_prop/indirect.main.ConstProp.panic-abort.diff b/tests/mir-opt/const_prop/indirect.main.ConstProp.panic-abort.diff
index eef9282c2cf..ca0ce2888cd 100644
--- a/tests/mir-opt/const_prop/indirect.main.ConstProp.panic-abort.diff
+++ b/tests/mir-opt/const_prop/indirect.main.ConstProp.panic-abort.diff
@@ -18,7 +18,7 @@
 -         assert(!move (_3.1: bool), "attempt to compute `{} + {}`, which would overflow", move _2, const 1_u8) -> [success: bb1, unwind unreachable];
 +         _2 = const 2_u8;
 +         _3 = const (3_u8, false);
-+         assert(!const false, "attempt to compute `{} + {}`, which would overflow", move _2, const 1_u8) -> [success: bb1, unwind unreachable];
++         assert(!const false, "attempt to compute `{} + {}`, which would overflow", const 2_u8, const 1_u8) -> [success: bb1, unwind unreachable];
       }
   
       bb1: {
diff --git a/tests/mir-opt/const_prop/indirect.main.ConstProp.panic-unwind.diff b/tests/mir-opt/const_prop/indirect.main.ConstProp.panic-unwind.diff
index ccfa35f001b..d63fb9255a3 100644
--- a/tests/mir-opt/const_prop/indirect.main.ConstProp.panic-unwind.diff
+++ b/tests/mir-opt/const_prop/indirect.main.ConstProp.panic-unwind.diff
@@ -18,7 +18,7 @@
 -         assert(!move (_3.1: bool), "attempt to compute `{} + {}`, which would overflow", move _2, const 1_u8) -> [success: bb1, unwind continue];
 +         _2 = const 2_u8;
 +         _3 = const (3_u8, false);
-+         assert(!const false, "attempt to compute `{} + {}`, which would overflow", move _2, const 1_u8) -> [success: bb1, unwind continue];
++         assert(!const false, "attempt to compute `{} + {}`, which would overflow", const 2_u8, const 1_u8) -> [success: bb1, unwind continue];
       }
   
       bb1: {
diff --git a/tests/mir-opt/const_prop/inherit_overflow.main.ConstProp.panic-abort.diff b/tests/mir-opt/const_prop/inherit_overflow.main.ConstProp.panic-abort.diff
index e0467e3fcff..51e17cf690a 100644
--- a/tests/mir-opt/const_prop/inherit_overflow.main.ConstProp.panic-abort.diff
+++ b/tests/mir-opt/const_prop/inherit_overflow.main.ConstProp.panic-abort.diff
@@ -23,7 +23,7 @@
 -         _4 = CheckedAdd(_2, _3);
 -         assert(!move (_4.1: bool), "attempt to compute `{} + {}`, which would overflow", _2, _3) -> [success: bb1, unwind unreachable];
 +         _4 = const (0_u8, true);
-+         assert(!const true, "attempt to compute `{} + {}`, which would overflow", _2, _3) -> [success: bb1, unwind unreachable];
++         assert(!const true, "attempt to compute `{} + {}`, which would overflow", const u8::MAX, const 1_u8) -> [success: bb1, unwind unreachable];
       }
   
       bb1: {
diff --git a/tests/mir-opt/const_prop/inherit_overflow.main.ConstProp.panic-unwind.diff b/tests/mir-opt/const_prop/inherit_overflow.main.ConstProp.panic-unwind.diff
index 4f8e0f0f599..5ef201497fb 100644
--- a/tests/mir-opt/const_prop/inherit_overflow.main.ConstProp.panic-unwind.diff
+++ b/tests/mir-opt/const_prop/inherit_overflow.main.ConstProp.panic-unwind.diff
@@ -23,7 +23,7 @@
 -         _4 = CheckedAdd(_2, _3);
 -         assert(!move (_4.1: bool), "attempt to compute `{} + {}`, which would overflow", _2, _3) -> [success: bb1, unwind continue];
 +         _4 = const (0_u8, true);
-+         assert(!const true, "attempt to compute `{} + {}`, which would overflow", _2, _3) -> [success: bb1, unwind continue];
++         assert(!const true, "attempt to compute `{} + {}`, which would overflow", const u8::MAX, const 1_u8) -> [success: bb1, unwind continue];
       }
   
       bb1: {
diff --git a/tests/mir-opt/const_prop/invalid_constant.main.ConstProp.diff b/tests/mir-opt/const_prop/invalid_constant.main.ConstProp.diff
index 0c9d1f4a38a..10e978a683a 100644
--- a/tests/mir-opt/const_prop/invalid_constant.main.ConstProp.diff
+++ b/tests/mir-opt/const_prop/invalid_constant.main.ConstProp.diff
@@ -43,8 +43,9 @@
           StorageLive(_5);
           _5 = InvalidTag { int: const 4_u32 };
 -         _4 = (_5.1: E);
+-         _3 = [move _4];
 +         _4 = const Scalar(0x00000004): E;
-          _3 = [move _4];
++         _3 = [const Scalar(0x00000004): E];
           StorageDead(_4);
           StorageDead(_5);
           nop;
diff --git a/tests/mir-opt/const_prop/issue_66971.main.ConstProp.panic-abort.diff b/tests/mir-opt/const_prop/issue_66971.main.ConstProp.panic-abort.diff
index 516f13586d3..170c019782d 100644
--- a/tests/mir-opt/const_prop/issue_66971.main.ConstProp.panic-abort.diff
+++ b/tests/mir-opt/const_prop/issue_66971.main.ConstProp.panic-abort.diff
@@ -8,8 +8,10 @@
   
       bb0: {
           StorageLive(_2);
-          _2 = (const (), const 0_u8, const 0_u8);
-          _1 = encode(move _2) -> [return: bb1, unwind unreachable];
+-         _2 = (const (), const 0_u8, const 0_u8);
+-         _1 = encode(move _2) -> [return: bb1, unwind unreachable];
++         _2 = const ((), 0_u8, 0_u8);
++         _1 = encode(const ((), 0_u8, 0_u8)) -> [return: bb1, unwind unreachable];
       }
   
       bb1: {
diff --git a/tests/mir-opt/const_prop/issue_66971.main.ConstProp.panic-unwind.diff b/tests/mir-opt/const_prop/issue_66971.main.ConstProp.panic-unwind.diff
index 5e3443228cd..64227dfd78c 100644
--- a/tests/mir-opt/const_prop/issue_66971.main.ConstProp.panic-unwind.diff
+++ b/tests/mir-opt/const_prop/issue_66971.main.ConstProp.panic-unwind.diff
@@ -8,8 +8,10 @@
   
       bb0: {
           StorageLive(_2);
-          _2 = (const (), const 0_u8, const 0_u8);
-          _1 = encode(move _2) -> [return: bb1, unwind continue];
+-         _2 = (const (), const 0_u8, const 0_u8);
+-         _1 = encode(move _2) -> [return: bb1, unwind continue];
++         _2 = const ((), 0_u8, 0_u8);
++         _1 = encode(const ((), 0_u8, 0_u8)) -> [return: bb1, unwind continue];
       }
   
       bb1: {
diff --git a/tests/mir-opt/const_prop/issue_67019.main.ConstProp.panic-abort.diff b/tests/mir-opt/const_prop/issue_67019.main.ConstProp.panic-abort.diff
index 96b4093726c..e1f3f37b370 100644
--- a/tests/mir-opt/const_prop/issue_67019.main.ConstProp.panic-abort.diff
+++ b/tests/mir-opt/const_prop/issue_67019.main.ConstProp.panic-abort.diff
@@ -11,10 +11,12 @@
           StorageLive(_2);
           StorageLive(_3);
 -         _3 = (const 1_u8, const 2_u8);
+-         _2 = (move _3,);
 +         _3 = const (1_u8, 2_u8);
-          _2 = (move _3,);
++         _2 = const ((1_u8, 2_u8),);
           StorageDead(_3);
-          _1 = test(move _2) -> [return: bb1, unwind unreachable];
+-         _1 = test(move _2) -> [return: bb1, unwind unreachable];
++         _1 = test(const ((1_u8, 2_u8),)) -> [return: bb1, unwind unreachable];
       }
   
       bb1: {
diff --git a/tests/mir-opt/const_prop/issue_67019.main.ConstProp.panic-unwind.diff b/tests/mir-opt/const_prop/issue_67019.main.ConstProp.panic-unwind.diff
index 95776030162..aaa376a95cf 100644
--- a/tests/mir-opt/const_prop/issue_67019.main.ConstProp.panic-unwind.diff
+++ b/tests/mir-opt/const_prop/issue_67019.main.ConstProp.panic-unwind.diff
@@ -11,10 +11,12 @@
           StorageLive(_2);
           StorageLive(_3);
 -         _3 = (const 1_u8, const 2_u8);
+-         _2 = (move _3,);
 +         _3 = const (1_u8, 2_u8);
-          _2 = (move _3,);
++         _2 = const ((1_u8, 2_u8),);
           StorageDead(_3);
-          _1 = test(move _2) -> [return: bb1, unwind continue];
+-         _1 = test(move _2) -> [return: bb1, unwind continue];
++         _1 = test(const ((1_u8, 2_u8),)) -> [return: bb1, unwind continue];
       }
   
       bb1: {
diff --git a/tests/mir-opt/const_prop/large_array_index.main.ConstProp.32bit.panic-abort.diff b/tests/mir-opt/const_prop/large_array_index.main.ConstProp.32bit.panic-abort.diff
index 61ba52fb0d6..20e2ee32698 100644
--- a/tests/mir-opt/const_prop/large_array_index.main.ConstProp.32bit.panic-abort.diff
+++ b/tests/mir-opt/const_prop/large_array_index.main.ConstProp.32bit.panic-abort.diff
@@ -23,7 +23,7 @@
 -         assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, _3) -> [success: bb1, unwind unreachable];
 +         _4 = const 5000_usize;
 +         _5 = const true;
-+         assert(const true, "index out of bounds: the length is {} but the index is {}", move _4, _3) -> [success: bb1, unwind unreachable];
++         assert(const true, "index out of bounds: the length is {} but the index is {}", const 5000_usize, const 2_usize) -> [success: bb1, unwind unreachable];
       }
   
       bb1: {
diff --git a/tests/mir-opt/const_prop/large_array_index.main.ConstProp.32bit.panic-unwind.diff b/tests/mir-opt/const_prop/large_array_index.main.ConstProp.32bit.panic-unwind.diff
index 658607116c8..1bdbbbf7863 100644
--- a/tests/mir-opt/const_prop/large_array_index.main.ConstProp.32bit.panic-unwind.diff
+++ b/tests/mir-opt/const_prop/large_array_index.main.ConstProp.32bit.panic-unwind.diff
@@ -23,7 +23,7 @@
 -         assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, _3) -> [success: bb1, unwind continue];
 +         _4 = const 5000_usize;
 +         _5 = const true;
-+         assert(const true, "index out of bounds: the length is {} but the index is {}", move _4, _3) -> [success: bb1, unwind continue];
++         assert(const true, "index out of bounds: the length is {} but the index is {}", const 5000_usize, const 2_usize) -> [success: bb1, unwind continue];
       }
   
       bb1: {
diff --git a/tests/mir-opt/const_prop/large_array_index.main.ConstProp.64bit.panic-abort.diff b/tests/mir-opt/const_prop/large_array_index.main.ConstProp.64bit.panic-abort.diff
index 61ba52fb0d6..20e2ee32698 100644
--- a/tests/mir-opt/const_prop/large_array_index.main.ConstProp.64bit.panic-abort.diff
+++ b/tests/mir-opt/const_prop/large_array_index.main.ConstProp.64bit.panic-abort.diff
@@ -23,7 +23,7 @@
 -         assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, _3) -> [success: bb1, unwind unreachable];
 +         _4 = const 5000_usize;
 +         _5 = const true;
-+         assert(const true, "index out of bounds: the length is {} but the index is {}", move _4, _3) -> [success: bb1, unwind unreachable];
++         assert(const true, "index out of bounds: the length is {} but the index is {}", const 5000_usize, const 2_usize) -> [success: bb1, unwind unreachable];
       }
   
       bb1: {
diff --git a/tests/mir-opt/const_prop/large_array_index.main.ConstProp.64bit.panic-unwind.diff b/tests/mir-opt/const_prop/large_array_index.main.ConstProp.64bit.panic-unwind.diff
index 658607116c8..1bdbbbf7863 100644
--- a/tests/mir-opt/const_prop/large_array_index.main.ConstProp.64bit.panic-unwind.diff
+++ b/tests/mir-opt/const_prop/large_array_index.main.ConstProp.64bit.panic-unwind.diff
@@ -23,7 +23,7 @@
 -         assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, _3) -> [success: bb1, unwind continue];
 +         _4 = const 5000_usize;
 +         _5 = const true;
-+         assert(const true, "index out of bounds: the length is {} but the index is {}", move _4, _3) -> [success: bb1, unwind continue];
++         assert(const true, "index out of bounds: the length is {} but the index is {}", const 5000_usize, const 2_usize) -> [success: bb1, unwind continue];
       }
   
       bb1: {
diff --git a/tests/mir-opt/const_prop/offset_of.concrete.ConstProp.panic-abort.diff b/tests/mir-opt/const_prop/offset_of.concrete.ConstProp.panic-abort.diff
index 046a79b4bfb..c73d217aeec 100644
--- a/tests/mir-opt/const_prop/offset_of.concrete.ConstProp.panic-abort.diff
+++ b/tests/mir-opt/const_prop/offset_of.concrete.ConstProp.panic-abort.diff
@@ -28,8 +28,9 @@
           StorageLive(_1);
           StorageLive(_2);
 -         _2 = OffsetOf(Alpha, [0]);
+-         _1 = must_use::<usize>(move _2) -> [return: bb1, unwind unreachable];
 +         _2 = const 4_usize;
-          _1 = must_use::<usize>(move _2) -> [return: bb1, unwind unreachable];
++         _1 = must_use::<usize>(const 4_usize) -> [return: bb1, unwind unreachable];
       }
   
       bb1: {
@@ -37,8 +38,9 @@
           StorageLive(_3);
           StorageLive(_4);
 -         _4 = OffsetOf(Alpha, [1]);
+-         _3 = must_use::<usize>(move _4) -> [return: bb2, unwind unreachable];
 +         _4 = const 0_usize;
-          _3 = must_use::<usize>(move _4) -> [return: bb2, unwind unreachable];
++         _3 = must_use::<usize>(const 0_usize) -> [return: bb2, unwind unreachable];
       }
   
       bb2: {
@@ -46,8 +48,9 @@
           StorageLive(_5);
           StorageLive(_6);
 -         _6 = OffsetOf(Alpha, [2, 0]);
+-         _5 = must_use::<usize>(move _6) -> [return: bb3, unwind unreachable];
 +         _6 = const 2_usize;
-          _5 = must_use::<usize>(move _6) -> [return: bb3, unwind unreachable];
++         _5 = must_use::<usize>(const 2_usize) -> [return: bb3, unwind unreachable];
       }
   
       bb3: {
@@ -55,8 +58,9 @@
           StorageLive(_7);
           StorageLive(_8);
 -         _8 = OffsetOf(Alpha, [2, 1]);
+-         _7 = must_use::<usize>(move _8) -> [return: bb4, unwind unreachable];
 +         _8 = const 3_usize;
-          _7 = must_use::<usize>(move _8) -> [return: bb4, unwind unreachable];
++         _7 = must_use::<usize>(const 3_usize) -> [return: bb4, unwind unreachable];
       }
   
       bb4: {
diff --git a/tests/mir-opt/const_prop/offset_of.concrete.ConstProp.panic-unwind.diff b/tests/mir-opt/const_prop/offset_of.concrete.ConstProp.panic-unwind.diff
index bbb807d8fcd..913ffca4ae9 100644
--- a/tests/mir-opt/const_prop/offset_of.concrete.ConstProp.panic-unwind.diff
+++ b/tests/mir-opt/const_prop/offset_of.concrete.ConstProp.panic-unwind.diff
@@ -28,8 +28,9 @@
           StorageLive(_1);
           StorageLive(_2);
 -         _2 = OffsetOf(Alpha, [0]);
+-         _1 = must_use::<usize>(move _2) -> [return: bb1, unwind continue];
 +         _2 = const 4_usize;
-          _1 = must_use::<usize>(move _2) -> [return: bb1, unwind continue];
++         _1 = must_use::<usize>(const 4_usize) -> [return: bb1, unwind continue];
       }
   
       bb1: {
@@ -37,8 +38,9 @@
           StorageLive(_3);
           StorageLive(_4);
 -         _4 = OffsetOf(Alpha, [1]);
+-         _3 = must_use::<usize>(move _4) -> [return: bb2, unwind continue];
 +         _4 = const 0_usize;
-          _3 = must_use::<usize>(move _4) -> [return: bb2, unwind continue];
++         _3 = must_use::<usize>(const 0_usize) -> [return: bb2, unwind continue];
       }
   
       bb2: {
@@ -46,8 +48,9 @@
           StorageLive(_5);
           StorageLive(_6);
 -         _6 = OffsetOf(Alpha, [2, 0]);
+-         _5 = must_use::<usize>(move _6) -> [return: bb3, unwind continue];
 +         _6 = const 2_usize;
-          _5 = must_use::<usize>(move _6) -> [return: bb3, unwind continue];
++         _5 = must_use::<usize>(const 2_usize) -> [return: bb3, unwind continue];
       }
   
       bb3: {
@@ -55,8 +58,9 @@
           StorageLive(_7);
           StorageLive(_8);
 -         _8 = OffsetOf(Alpha, [2, 1]);
+-         _7 = must_use::<usize>(move _8) -> [return: bb4, unwind continue];
 +         _8 = const 3_usize;
-          _7 = must_use::<usize>(move _8) -> [return: bb4, unwind continue];
++         _7 = must_use::<usize>(const 3_usize) -> [return: bb4, unwind continue];
       }
   
       bb4: {
diff --git a/tests/mir-opt/const_prop/repeat.main.ConstProp.32bit.panic-abort.diff b/tests/mir-opt/const_prop/repeat.main.ConstProp.32bit.panic-abort.diff
index e095dd01da6..a55bd029e99 100644
--- a/tests/mir-opt/const_prop/repeat.main.ConstProp.32bit.panic-abort.diff
+++ b/tests/mir-opt/const_prop/repeat.main.ConstProp.32bit.panic-abort.diff
@@ -25,7 +25,7 @@
 -         assert(move _6, "index out of bounds: the length is {} but the index is {}", move _5, _4) -> [success: bb1, unwind unreachable];
 +         _5 = const 8_usize;
 +         _6 = const true;
-+         assert(const true, "index out of bounds: the length is {} but the index is {}", move _5, _4) -> [success: bb1, unwind unreachable];
++         assert(const true, "index out of bounds: the length is {} but the index is {}", const 8_usize, const 2_usize) -> [success: bb1, unwind unreachable];
       }
   
       bb1: {
diff --git a/tests/mir-opt/const_prop/repeat.main.ConstProp.32bit.panic-unwind.diff b/tests/mir-opt/const_prop/repeat.main.ConstProp.32bit.panic-unwind.diff
index 571f279a8c1..d49ef2e0179 100644
--- a/tests/mir-opt/const_prop/repeat.main.ConstProp.32bit.panic-unwind.diff
+++ b/tests/mir-opt/const_prop/repeat.main.ConstProp.32bit.panic-unwind.diff
@@ -25,7 +25,7 @@
 -         assert(move _6, "index out of bounds: the length is {} but the index is {}", move _5, _4) -> [success: bb1, unwind continue];
 +         _5 = const 8_usize;
 +         _6 = const true;
-+         assert(const true, "index out of bounds: the length is {} but the index is {}", move _5, _4) -> [success: bb1, unwind continue];
++         assert(const true, "index out of bounds: the length is {} but the index is {}", const 8_usize, const 2_usize) -> [success: bb1, unwind continue];
       }
   
       bb1: {
diff --git a/tests/mir-opt/const_prop/repeat.main.ConstProp.64bit.panic-abort.diff b/tests/mir-opt/const_prop/repeat.main.ConstProp.64bit.panic-abort.diff
index e095dd01da6..a55bd029e99 100644
--- a/tests/mir-opt/const_prop/repeat.main.ConstProp.64bit.panic-abort.diff
+++ b/tests/mir-opt/const_prop/repeat.main.ConstProp.64bit.panic-abort.diff
@@ -25,7 +25,7 @@
 -         assert(move _6, "index out of bounds: the length is {} but the index is {}", move _5, _4) -> [success: bb1, unwind unreachable];
 +         _5 = const 8_usize;
 +         _6 = const true;
-+         assert(const true, "index out of bounds: the length is {} but the index is {}", move _5, _4) -> [success: bb1, unwind unreachable];
++         assert(const true, "index out of bounds: the length is {} but the index is {}", const 8_usize, const 2_usize) -> [success: bb1, unwind unreachable];
       }
   
       bb1: {
diff --git a/tests/mir-opt/const_prop/repeat.main.ConstProp.64bit.panic-unwind.diff b/tests/mir-opt/const_prop/repeat.main.ConstProp.64bit.panic-unwind.diff
index 571f279a8c1..d49ef2e0179 100644
--- a/tests/mir-opt/const_prop/repeat.main.ConstProp.64bit.panic-unwind.diff
+++ b/tests/mir-opt/const_prop/repeat.main.ConstProp.64bit.panic-unwind.diff
@@ -25,7 +25,7 @@
 -         assert(move _6, "index out of bounds: the length is {} but the index is {}", move _5, _4) -> [success: bb1, unwind continue];
 +         _5 = const 8_usize;
 +         _6 = const true;
-+         assert(const true, "index out of bounds: the length is {} but the index is {}", move _5, _4) -> [success: bb1, unwind continue];
++         assert(const true, "index out of bounds: the length is {} but the index is {}", const 8_usize, const 2_usize) -> [success: bb1, unwind continue];
       }
   
       bb1: {
diff --git a/tests/mir-opt/const_prop/scalar_literal_propagation.main.ConstProp.panic-abort.diff b/tests/mir-opt/const_prop/scalar_literal_propagation.main.ConstProp.panic-abort.diff
index 0677295d078..c5c09c8edd7 100644
--- a/tests/mir-opt/const_prop/scalar_literal_propagation.main.ConstProp.panic-abort.diff
+++ b/tests/mir-opt/const_prop/scalar_literal_propagation.main.ConstProp.panic-abort.diff
@@ -16,8 +16,9 @@
           StorageLive(_2);
           StorageLive(_3);
 -         _3 = _1;
+-         _2 = consume(move _3) -> [return: bb1, unwind unreachable];
 +         _3 = const 1_u32;
-          _2 = consume(move _3) -> [return: bb1, unwind unreachable];
++         _2 = consume(const 1_u32) -> [return: bb1, unwind unreachable];
       }
   
       bb1: {
diff --git a/tests/mir-opt/const_prop/scalar_literal_propagation.main.ConstProp.panic-unwind.diff b/tests/mir-opt/const_prop/scalar_literal_propagation.main.ConstProp.panic-unwind.diff
index a7d7a7224ce..b256c56765e 100644
--- a/tests/mir-opt/const_prop/scalar_literal_propagation.main.ConstProp.panic-unwind.diff
+++ b/tests/mir-opt/const_prop/scalar_literal_propagation.main.ConstProp.panic-unwind.diff
@@ -16,8 +16,9 @@
           StorageLive(_2);
           StorageLive(_3);
 -         _3 = _1;
+-         _2 = consume(move _3) -> [return: bb1, unwind continue];
 +         _3 = const 1_u32;
-          _2 = consume(move _3) -> [return: bb1, unwind continue];
++         _2 = consume(const 1_u32) -> [return: bb1, unwind continue];
       }
   
       bb1: {
diff --git a/tests/mir-opt/const_prop/slice_len.main.ConstProp.32bit.panic-abort.diff b/tests/mir-opt/const_prop/slice_len.main.ConstProp.32bit.panic-abort.diff
index 3c2b8e111cb..c2e1288b41e 100644
--- a/tests/mir-opt/const_prop/slice_len.main.ConstProp.32bit.panic-abort.diff
+++ b/tests/mir-opt/const_prop/slice_len.main.ConstProp.32bit.panic-abort.diff
@@ -30,7 +30,7 @@
 -         assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> [success: bb1, unwind unreachable];
 +         _7 = const 3_usize;
 +         _8 = const true;
-+         assert(const true, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> [success: bb1, unwind unreachable];
++         assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb1, unwind unreachable];
       }
   
       bb1: {
diff --git a/tests/mir-opt/const_prop/slice_len.main.ConstProp.32bit.panic-unwind.diff b/tests/mir-opt/const_prop/slice_len.main.ConstProp.32bit.panic-unwind.diff
index 303096030b4..23646c3c976 100644
--- a/tests/mir-opt/const_prop/slice_len.main.ConstProp.32bit.panic-unwind.diff
+++ b/tests/mir-opt/const_prop/slice_len.main.ConstProp.32bit.panic-unwind.diff
@@ -30,7 +30,7 @@
 -         assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> [success: bb1, unwind continue];
 +         _7 = const 3_usize;
 +         _8 = const true;
-+         assert(const true, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> [success: bb1, unwind continue];
++         assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb1, unwind continue];
       }
   
       bb1: {
diff --git a/tests/mir-opt/const_prop/slice_len.main.ConstProp.64bit.panic-abort.diff b/tests/mir-opt/const_prop/slice_len.main.ConstProp.64bit.panic-abort.diff
index 3c2b8e111cb..c2e1288b41e 100644
--- a/tests/mir-opt/const_prop/slice_len.main.ConstProp.64bit.panic-abort.diff
+++ b/tests/mir-opt/const_prop/slice_len.main.ConstProp.64bit.panic-abort.diff
@@ -30,7 +30,7 @@
 -         assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> [success: bb1, unwind unreachable];
 +         _7 = const 3_usize;
 +         _8 = const true;
-+         assert(const true, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> [success: bb1, unwind unreachable];
++         assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb1, unwind unreachable];
       }
   
       bb1: {
diff --git a/tests/mir-opt/const_prop/slice_len.main.ConstProp.64bit.panic-unwind.diff b/tests/mir-opt/const_prop/slice_len.main.ConstProp.64bit.panic-unwind.diff
index 303096030b4..23646c3c976 100644
--- a/tests/mir-opt/const_prop/slice_len.main.ConstProp.64bit.panic-unwind.diff
+++ b/tests/mir-opt/const_prop/slice_len.main.ConstProp.64bit.panic-unwind.diff
@@ -30,7 +30,7 @@
 -         assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> [success: bb1, unwind continue];
 +         _7 = const 3_usize;
 +         _8 = const true;
-+         assert(const true, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> [success: bb1, unwind continue];
++         assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb1, unwind continue];
       }
   
       bb1: {
diff --git a/tests/mir-opt/const_prop/tuple_literal_propagation.main.ConstProp.panic-abort.diff b/tests/mir-opt/const_prop/tuple_literal_propagation.main.ConstProp.panic-abort.diff
index a72f24152fb..9e705695ac0 100644
--- a/tests/mir-opt/const_prop/tuple_literal_propagation.main.ConstProp.panic-abort.diff
+++ b/tests/mir-opt/const_prop/tuple_literal_propagation.main.ConstProp.panic-abort.diff
@@ -17,8 +17,9 @@
           StorageLive(_2);
           StorageLive(_3);
 -         _3 = _1;
+-         _2 = consume(move _3) -> [return: bb1, unwind unreachable];
 +         _3 = const (1_u32, 2_u32);
-          _2 = consume(move _3) -> [return: bb1, unwind unreachable];
++         _2 = consume(const (1_u32, 2_u32)) -> [return: bb1, unwind unreachable];
       }
   
       bb1: {
diff --git a/tests/mir-opt/const_prop/tuple_literal_propagation.main.ConstProp.panic-unwind.diff b/tests/mir-opt/const_prop/tuple_literal_propagation.main.ConstProp.panic-unwind.diff
index 6255f9ec596..882dd97cc16 100644
--- a/tests/mir-opt/const_prop/tuple_literal_propagation.main.ConstProp.panic-unwind.diff
+++ b/tests/mir-opt/const_prop/tuple_literal_propagation.main.ConstProp.panic-unwind.diff
@@ -17,8 +17,9 @@
           StorageLive(_2);
           StorageLive(_3);
 -         _3 = _1;
+-         _2 = consume(move _3) -> [return: bb1, unwind continue];
 +         _3 = const (1_u32, 2_u32);
-          _2 = consume(move _3) -> [return: bb1, unwind continue];
++         _2 = consume(const (1_u32, 2_u32)) -> [return: bb1, unwind continue];
       }
   
       bb1: {
diff --git a/tests/mir-opt/dead-store-elimination/call_arg_copy.move_simple.DeadStoreElimination.panic-abort.diff b/tests/mir-opt/dead-store-elimination/call_arg_copy.move_simple.DeadStoreElimination.panic-abort.diff
new file mode 100644
index 00000000000..8f799b33f45
--- /dev/null
+++ b/tests/mir-opt/dead-store-elimination/call_arg_copy.move_simple.DeadStoreElimination.panic-abort.diff
@@ -0,0 +1,23 @@
+- // MIR for `move_simple` before DeadStoreElimination
++ // MIR for `move_simple` after DeadStoreElimination
+  
+  fn move_simple(_1: i32) -> () {
+      debug x => _1;
+      let mut _0: ();
+      let _2: ();
+-     let mut _3: i32;
+-     let mut _4: i32;
+  
+      bb0: {
+          StorageLive(_2);
+-         _2 = use_both(_1, _1) -> [return: bb1, unwind unreachable];
++         _2 = use_both(_1, move _1) -> [return: bb1, unwind unreachable];
+      }
+  
+      bb1: {
+          StorageDead(_2);
+          _0 = const ();
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dead-store-elimination/call_arg_copy.move_simple.DeadStoreElimination.panic-unwind.diff b/tests/mir-opt/dead-store-elimination/call_arg_copy.move_simple.DeadStoreElimination.panic-unwind.diff
new file mode 100644
index 00000000000..0551d663b50
--- /dev/null
+++ b/tests/mir-opt/dead-store-elimination/call_arg_copy.move_simple.DeadStoreElimination.panic-unwind.diff
@@ -0,0 +1,23 @@
+- // MIR for `move_simple` before DeadStoreElimination
++ // MIR for `move_simple` after DeadStoreElimination
+  
+  fn move_simple(_1: i32) -> () {
+      debug x => _1;
+      let mut _0: ();
+      let _2: ();
+-     let mut _3: i32;
+-     let mut _4: i32;
+  
+      bb0: {
+          StorageLive(_2);
+-         _2 = use_both(_1, _1) -> [return: bb1, unwind continue];
++         _2 = use_both(_1, move _1) -> [return: bb1, unwind continue];
+      }
+  
+      bb1: {
+          StorageDead(_2);
+          _0 = const ();
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dead-store-elimination/call_arg_copy.rs b/tests/mir-opt/dead-store-elimination/call_arg_copy.rs
new file mode 100644
index 00000000000..41f91fc1306
--- /dev/null
+++ b/tests/mir-opt/dead-store-elimination/call_arg_copy.rs
@@ -0,0 +1,15 @@
+// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
+// unit-test: DeadStoreElimination
+// compile-flags: -Zmir-enable-passes=+CopyProp
+
+#[inline(never)]
+fn use_both(_: i32, _: i32) {}
+
+// EMIT_MIR call_arg_copy.move_simple.DeadStoreElimination.diff
+fn move_simple(x: i32) {
+    use_both(x, x);
+}
+
+fn main() {
+    move_simple(1);
+}
diff --git a/tests/mir-opt/inline/dyn_trait.get_query.Inline.panic-abort.diff b/tests/mir-opt/inline/dyn_trait.get_query.Inline.panic-abort.diff
index 57b0849e111..9d5042caae2 100644
--- a/tests/mir-opt/inline/dyn_trait.get_query.Inline.panic-abort.diff
+++ b/tests/mir-opt/inline/dyn_trait.get_query.Inline.panic-abort.diff
@@ -32,7 +32,7 @@
 -         _0 = try_execute_query::<<Q as Query>::C>(move _4) -> [return: bb2, unwind unreachable];
 +         StorageLive(_5);
 +         _5 = _4 as &dyn Cache<V = <Q as Query>::V> (PointerCoercion(Unsize));
-+         _0 = <dyn Cache<V = <Q as Query>::V> as Cache>::store_nocache(_5) -> [return: bb2, unwind unreachable];
++         _0 = <dyn Cache<V = <Q as Query>::V> as Cache>::store_nocache(move _5) -> [return: bb2, unwind unreachable];
       }
   
       bb2: {
diff --git a/tests/mir-opt/inline/dyn_trait.get_query.Inline.panic-unwind.diff b/tests/mir-opt/inline/dyn_trait.get_query.Inline.panic-unwind.diff
index 706c1d48195..9bd3855c58f 100644
--- a/tests/mir-opt/inline/dyn_trait.get_query.Inline.panic-unwind.diff
+++ b/tests/mir-opt/inline/dyn_trait.get_query.Inline.panic-unwind.diff
@@ -32,7 +32,7 @@
 -         _0 = try_execute_query::<<Q as Query>::C>(move _4) -> [return: bb2, unwind continue];
 +         StorageLive(_5);
 +         _5 = _4 as &dyn Cache<V = <Q as Query>::V> (PointerCoercion(Unsize));
-+         _0 = <dyn Cache<V = <Q as Query>::V> as Cache>::store_nocache(_5) -> [return: bb2, unwind continue];
++         _0 = <dyn Cache<V = <Q as Query>::V> as Cache>::store_nocache(move _5) -> [return: bb2, unwind continue];
       }
   
       bb2: {
diff --git a/tests/mir-opt/inline/dyn_trait.try_execute_query.Inline.panic-abort.diff b/tests/mir-opt/inline/dyn_trait.try_execute_query.Inline.panic-abort.diff
index 9a6d3596fb9..96e16d023ba 100644
--- a/tests/mir-opt/inline/dyn_trait.try_execute_query.Inline.panic-abort.diff
+++ b/tests/mir-opt/inline/dyn_trait.try_execute_query.Inline.panic-abort.diff
@@ -17,7 +17,7 @@
           _2 = move _3 as &dyn Cache<V = <C as Cache>::V> (PointerCoercion(Unsize));
           StorageDead(_3);
 -         _0 = mk_cycle::<<C as Cache>::V>(move _2) -> [return: bb1, unwind unreachable];
-+         _0 = <dyn Cache<V = <C as Cache>::V> as Cache>::store_nocache(_2) -> [return: bb1, unwind unreachable];
++         _0 = <dyn Cache<V = <C as Cache>::V> as Cache>::store_nocache(move _2) -> [return: bb1, unwind unreachable];
       }
   
       bb1: {
diff --git a/tests/mir-opt/inline/dyn_trait.try_execute_query.Inline.panic-unwind.diff b/tests/mir-opt/inline/dyn_trait.try_execute_query.Inline.panic-unwind.diff
index 1a08df2b09b..06d65abcbc1 100644
--- a/tests/mir-opt/inline/dyn_trait.try_execute_query.Inline.panic-unwind.diff
+++ b/tests/mir-opt/inline/dyn_trait.try_execute_query.Inline.panic-unwind.diff
@@ -17,7 +17,7 @@
           _2 = move _3 as &dyn Cache<V = <C as Cache>::V> (PointerCoercion(Unsize));
           StorageDead(_3);
 -         _0 = mk_cycle::<<C as Cache>::V>(move _2) -> [return: bb1, unwind continue];
-+         _0 = <dyn Cache<V = <C as Cache>::V> as Cache>::store_nocache(_2) -> [return: bb1, unwind continue];
++         _0 = <dyn Cache<V = <C as Cache>::V> as Cache>::store_nocache(move _2) -> [return: bb1, unwind continue];
       }
   
       bb1: {
diff --git a/tests/mir-opt/inline/inline_into_box_place.main.Inline.panic-abort.diff b/tests/mir-opt/inline/inline_into_box_place.main.Inline.panic-abort.diff
index dfc00026ad0..dc0ab255afd 100644
--- a/tests/mir-opt/inline/inline_into_box_place.main.Inline.panic-abort.diff
+++ b/tests/mir-opt/inline/inline_into_box_place.main.Inline.panic-abort.diff
@@ -134,7 +134,7 @@
 +         StorageDead(_14);
 +         StorageLive(_9);
 +         _13 = const _;
-+         _9 = std::alloc::Global::alloc_impl(_13, _8, const false) -> [return: bb5, unwind unreachable];
++         _9 = std::alloc::Global::alloc_impl(move _13, _8, const false) -> [return: bb5, unwind unreachable];
       }
   
       bb1: {
@@ -144,7 +144,7 @@
       }
   
       bb2: {
-+         _12 = handle_alloc_error(_8) -> unwind unreachable;
++         _12 = handle_alloc_error(move _8) -> unwind unreachable;
 +     }
 + 
 +     bb3: {
diff --git a/tests/mir-opt/inline/inline_into_box_place.main.Inline.panic-unwind.diff b/tests/mir-opt/inline/inline_into_box_place.main.Inline.panic-unwind.diff
index f582adda88f..54c33aac9e8 100644
--- a/tests/mir-opt/inline/inline_into_box_place.main.Inline.panic-unwind.diff
+++ b/tests/mir-opt/inline/inline_into_box_place.main.Inline.panic-unwind.diff
@@ -134,7 +134,7 @@
 +         StorageDead(_14);
 +         StorageLive(_9);
 +         _13 = const _;
-+         _9 = std::alloc::Global::alloc_impl(_13, _8, const false) -> [return: bb7, unwind: bb3];
++         _9 = std::alloc::Global::alloc_impl(move _13, _8, const false) -> [return: bb7, unwind: bb3];
       }
   
       bb1: {
@@ -161,7 +161,7 @@
 -     bb4 (cleanup): {
 -         resume;
 +     bb4: {
-+         _12 = handle_alloc_error(_8) -> bb3;
++         _12 = handle_alloc_error(move _8) -> bb3;
 +     }
 + 
 +     bb5: {
diff --git a/tests/mir-opt/inline/inline_trait_method_2.test2.Inline.after.panic-abort.mir b/tests/mir-opt/inline/inline_trait_method_2.test2.Inline.after.panic-abort.mir
index 503f153089c..f0d1cfe0359 100644
--- a/tests/mir-opt/inline/inline_trait_method_2.test2.Inline.after.panic-abort.mir
+++ b/tests/mir-opt/inline/inline_trait_method_2.test2.Inline.after.panic-abort.mir
@@ -15,7 +15,7 @@ fn test2(_1: &dyn X) -> bool {
         _3 = &(*_1);
         _2 = move _3 as &dyn X (PointerCoercion(Unsize));
         StorageDead(_3);
-        _0 = <dyn X as X>::y(_2) -> [return: bb1, unwind unreachable];
+        _0 = <dyn X as X>::y(move _2) -> [return: bb1, unwind unreachable];
     }
 
     bb1: {
diff --git a/tests/mir-opt/inline/inline_trait_method_2.test2.Inline.after.panic-unwind.mir b/tests/mir-opt/inline/inline_trait_method_2.test2.Inline.after.panic-unwind.mir
index 37bb53e79c6..f37b0814301 100644
--- a/tests/mir-opt/inline/inline_trait_method_2.test2.Inline.after.panic-unwind.mir
+++ b/tests/mir-opt/inline/inline_trait_method_2.test2.Inline.after.panic-unwind.mir
@@ -15,7 +15,7 @@ fn test2(_1: &dyn X) -> bool {
         _3 = &(*_1);
         _2 = move _3 as &dyn X (PointerCoercion(Unsize));
         StorageDead(_3);
-        _0 = <dyn X as X>::y(_2) -> [return: bb1, unwind continue];
+        _0 = <dyn X as X>::y(move _2) -> [return: bb1, unwind continue];
     }
 
     bb1: {
diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-abort.diff b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-abort.diff
index 90b32247c95..d052219661b 100644
--- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-abort.diff
+++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-abort.diff
@@ -12,7 +12,6 @@
 +         debug rhs => _4;
 +         let mut _5: u16;
 +         let mut _6: bool;
-+         let mut _7: u32;
 +         scope 2 {
 +         }
 +     }
@@ -28,10 +27,7 @@
 -     bb1: {
 +         StorageLive(_5);
 +         StorageLive(_6);
-+         StorageLive(_7);
-+         _7 = const 65535_u32;
-+         _6 = Le(_4, move _7);
-+         StorageDead(_7);
++         _6 = Le(_4, const 65535_u32);
 +         assume(move _6);
 +         StorageDead(_6);
 +         _5 = _4 as u16 (IntToInt);
diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-unwind.diff b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-unwind.diff
index cae25759cd8..67a5ac2483b 100644
--- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-unwind.diff
+++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-unwind.diff
@@ -12,7 +12,6 @@
 +         debug rhs => _4;
 +         let mut _5: u16;
 +         let mut _6: bool;
-+         let mut _7: u32;
 +         scope 2 {
 +         }
 +     }
@@ -28,10 +27,7 @@
 -     bb1: {
 +         StorageLive(_5);
 +         StorageLive(_6);
-+         StorageLive(_7);
-+         _7 = const 65535_u32;
-+         _6 = Le(_4, move _7);
-+         StorageDead(_7);
++         _6 = Le(_4, const 65535_u32);
 +         assume(move _6);
 +         StorageDead(_6);
 +         _5 = _4 as u16 (IntToInt);
diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.panic-abort.mir b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.panic-abort.mir
index 3f388a69d7e..f9dff62e0c8 100644
--- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.panic-abort.mir
+++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.panic-abort.mir
@@ -7,25 +7,21 @@ fn unchecked_shl_unsigned_smaller(_1: u16, _2: u32) -> u16 {
     scope 1 (inlined core::num::<impl u16>::unchecked_shl) {
         debug self => _1;
         debug rhs => _2;
-        let mut _3: u32;
-        let mut _4: bool;
-        let mut _5: u16;
+        let mut _3: bool;
+        let mut _4: u16;
         scope 2 {
         }
     }
 
     bb0: {
-        StorageLive(_5);
         StorageLive(_4);
         StorageLive(_3);
-        _3 = const 65535_u32;
-        _4 = Le(_2, move _3);
+        _3 = Le(_2, const 65535_u32);
+        assume(move _3);
         StorageDead(_3);
-        assume(move _4);
+        _4 = _2 as u16 (IntToInt);
+        _0 = ShlUnchecked(_1, move _4);
         StorageDead(_4);
-        _5 = _2 as u16 (IntToInt);
-        _0 = ShlUnchecked(_1, move _5);
-        StorageDead(_5);
         return;
     }
 }
diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.panic-unwind.mir
index 3f388a69d7e..f9dff62e0c8 100644
--- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.panic-unwind.mir
+++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.panic-unwind.mir
@@ -7,25 +7,21 @@ fn unchecked_shl_unsigned_smaller(_1: u16, _2: u32) -> u16 {
     scope 1 (inlined core::num::<impl u16>::unchecked_shl) {
         debug self => _1;
         debug rhs => _2;
-        let mut _3: u32;
-        let mut _4: bool;
-        let mut _5: u16;
+        let mut _3: bool;
+        let mut _4: u16;
         scope 2 {
         }
     }
 
     bb0: {
-        StorageLive(_5);
         StorageLive(_4);
         StorageLive(_3);
-        _3 = const 65535_u32;
-        _4 = Le(_2, move _3);
+        _3 = Le(_2, const 65535_u32);
+        assume(move _3);
         StorageDead(_3);
-        assume(move _4);
+        _4 = _2 as u16 (IntToInt);
+        _0 = ShlUnchecked(_1, move _4);
         StorageDead(_4);
-        _5 = _2 as u16 (IntToInt);
-        _0 = ShlUnchecked(_1, move _5);
-        StorageDead(_5);
         return;
     }
 }
diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.Inline.panic-abort.diff b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.Inline.panic-abort.diff
index fa7e5d16e39..15b36b284de 100644
--- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.Inline.panic-abort.diff
+++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.Inline.panic-abort.diff
@@ -12,7 +12,6 @@
 +         debug rhs => _4;
 +         let mut _5: i16;
 +         let mut _6: bool;
-+         let mut _7: u32;
 +         scope 2 {
 +         }
 +     }
@@ -28,10 +27,7 @@
 -     bb1: {
 +         StorageLive(_5);
 +         StorageLive(_6);
-+         StorageLive(_7);
-+         _7 = const 32767_u32;
-+         _6 = Le(_4, move _7);
-+         StorageDead(_7);
++         _6 = Le(_4, const 32767_u32);
 +         assume(move _6);
 +         StorageDead(_6);
 +         _5 = _4 as i16 (IntToInt);
diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.Inline.panic-unwind.diff b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.Inline.panic-unwind.diff
index fe533121486..8629f92dbad 100644
--- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.Inline.panic-unwind.diff
+++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.Inline.panic-unwind.diff
@@ -12,7 +12,6 @@
 +         debug rhs => _4;
 +         let mut _5: i16;
 +         let mut _6: bool;
-+         let mut _7: u32;
 +         scope 2 {
 +         }
 +     }
@@ -28,10 +27,7 @@
 -     bb1: {
 +         StorageLive(_5);
 +         StorageLive(_6);
-+         StorageLive(_7);
-+         _7 = const 32767_u32;
-+         _6 = Le(_4, move _7);
-+         StorageDead(_7);
++         _6 = Le(_4, const 32767_u32);
 +         assume(move _6);
 +         StorageDead(_6);
 +         _5 = _4 as i16 (IntToInt);
diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.PreCodegen.after.panic-abort.mir b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.PreCodegen.after.panic-abort.mir
index 64ea25349ac..65fa0d956c0 100644
--- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.PreCodegen.after.panic-abort.mir
+++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.PreCodegen.after.panic-abort.mir
@@ -7,25 +7,21 @@ fn unchecked_shr_signed_smaller(_1: i16, _2: u32) -> i16 {
     scope 1 (inlined core::num::<impl i16>::unchecked_shr) {
         debug self => _1;
         debug rhs => _2;
-        let mut _3: u32;
-        let mut _4: bool;
-        let mut _5: i16;
+        let mut _3: bool;
+        let mut _4: i16;
         scope 2 {
         }
     }
 
     bb0: {
-        StorageLive(_5);
         StorageLive(_4);
         StorageLive(_3);
-        _3 = const 32767_u32;
-        _4 = Le(_2, move _3);
+        _3 = Le(_2, const 32767_u32);
+        assume(move _3);
         StorageDead(_3);
-        assume(move _4);
+        _4 = _2 as i16 (IntToInt);
+        _0 = ShrUnchecked(_1, move _4);
         StorageDead(_4);
-        _5 = _2 as i16 (IntToInt);
-        _0 = ShrUnchecked(_1, move _5);
-        StorageDead(_5);
         return;
     }
 }
diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.PreCodegen.after.panic-unwind.mir
index 64ea25349ac..65fa0d956c0 100644
--- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.PreCodegen.after.panic-unwind.mir
+++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.PreCodegen.after.panic-unwind.mir
@@ -7,25 +7,21 @@ fn unchecked_shr_signed_smaller(_1: i16, _2: u32) -> i16 {
     scope 1 (inlined core::num::<impl i16>::unchecked_shr) {
         debug self => _1;
         debug rhs => _2;
-        let mut _3: u32;
-        let mut _4: bool;
-        let mut _5: i16;
+        let mut _3: bool;
+        let mut _4: i16;
         scope 2 {
         }
     }
 
     bb0: {
-        StorageLive(_5);
         StorageLive(_4);
         StorageLive(_3);
-        _3 = const 32767_u32;
-        _4 = Le(_2, move _3);
+        _3 = Le(_2, const 32767_u32);
+        assume(move _3);
         StorageDead(_3);
-        assume(move _4);
+        _4 = _2 as i16 (IntToInt);
+        _0 = ShrUnchecked(_1, move _4);
         StorageDead(_4);
-        _5 = _2 as i16 (IntToInt);
-        _0 = ShrUnchecked(_1, move _5);
-        StorageDead(_5);
         return;
     }
 }
diff --git a/tests/mir-opt/issue_101973.inner.ConstProp.panic-abort.diff b/tests/mir-opt/issue_101973.inner.ConstProp.panic-abort.diff
index e018563dbfe..ce490e894f0 100644
--- a/tests/mir-opt/issue_101973.inner.ConstProp.panic-abort.diff
+++ b/tests/mir-opt/issue_101973.inner.ConstProp.panic-abort.diff
@@ -66,7 +66,7 @@
       bb2: {
           _6 = Shl(move _7, const 1_i32);
           StorageDead(_7);
-          _3 = rotate_right::<u32>(_4, _6) -> [return: bb3, unwind unreachable];
+          _3 = rotate_right::<u32>(move _4, move _6) -> [return: bb3, unwind unreachable];
       }
   
       bb3: {
diff --git a/tests/mir-opt/issue_101973.inner.ConstProp.panic-unwind.diff b/tests/mir-opt/issue_101973.inner.ConstProp.panic-unwind.diff
index a6bd29e1c9d..254557b9947 100644
--- a/tests/mir-opt/issue_101973.inner.ConstProp.panic-unwind.diff
+++ b/tests/mir-opt/issue_101973.inner.ConstProp.panic-unwind.diff
@@ -66,7 +66,7 @@
       bb2: {
           _6 = Shl(move _7, const 1_i32);
           StorageDead(_7);
-          _3 = rotate_right::<u32>(_4, _6) -> [return: bb3, unwind unreachable];
+          _3 = rotate_right::<u32>(move _4, move _6) -> [return: bb3, unwind unreachable];
       }
   
       bb3: {
diff --git a/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.panic-abort.mir b/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.panic-abort.mir
index 9743e192462..a7a14ea645b 100644
--- a/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.panic-abort.mir
+++ b/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.panic-abort.mir
@@ -35,7 +35,7 @@ fn num_to_digit(_1: char) -> u32 {
 
     bb2: {
         StorageLive(_4);
-        _4 = char::methods::<impl char>::to_digit(_1, const 8_u32) -> [return: bb3, unwind unreachable];
+        _4 = char::methods::<impl char>::to_digit(move _1, const 8_u32) -> [return: bb3, unwind unreachable];
     }
 
     bb3: {
diff --git a/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.panic-unwind.mir
index e89d6eb4d50..5f8c6f7283c 100644
--- a/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.panic-unwind.mir
+++ b/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.panic-unwind.mir
@@ -35,7 +35,7 @@ fn num_to_digit(_1: char) -> u32 {
 
     bb2: {
         StorageLive(_4);
-        _4 = char::methods::<impl char>::to_digit(_1, const 8_u32) -> [return: bb3, unwind continue];
+        _4 = char::methods::<impl char>::to_digit(move _1, const 8_u32) -> [return: bb3, unwind continue];
     }
 
     bb3: {
diff --git a/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.mir
index 800308c2e0b..8304cb45b35 100644
--- a/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.mir
+++ b/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.mir
@@ -7,25 +7,24 @@ fn checked_shl(_1: u32, _2: u32) -> Option<u32> {
     scope 1 (inlined core::num::<impl u32>::checked_shl) {
         debug self => _1;
         debug rhs => _2;
-        let mut _7: bool;
+        let mut _6: bool;
         scope 2 {
-            debug a => _5;
-            debug b => _6;
+            debug a => _4;
+            debug b => _5;
         }
         scope 3 (inlined core::num::<impl u32>::overflowing_shl) {
             debug self => _1;
             debug rhs => _2;
-            let mut _5: u32;
-            let mut _6: bool;
+            let mut _4: u32;
+            let mut _5: bool;
             scope 4 (inlined core::num::<impl u32>::wrapping_shl) {
                 debug self => _1;
                 debug rhs => _2;
                 let mut _3: u32;
-                let mut _4: u32;
                 scope 5 {
                     scope 6 (inlined core::num::<impl u32>::unchecked_shl) {
                         debug self => _1;
-                        debug rhs => _4;
+                        debug rhs => _3;
                         scope 7 {
                         }
                     }
@@ -35,26 +34,23 @@ fn checked_shl(_1: u32, _2: u32) -> Option<u32> {
     }
 
     bb0: {
-        StorageLive(_5);
-        StorageLive(_6);
         StorageLive(_4);
+        StorageLive(_5);
         StorageLive(_3);
-        _3 = const 31_u32;
-        _4 = BitAnd(_2, move _3);
+        _3 = BitAnd(_2, const 31_u32);
+        _4 = ShlUnchecked(_1, _3);
         StorageDead(_3);
-        _5 = ShlUnchecked(_1, _4);
-        StorageDead(_4);
-        _6 = Ge(_2, const _);
-        StorageLive(_7);
-        _7 = unlikely(_6) -> [return: bb1, unwind unreachable];
+        _5 = Ge(_2, const _);
+        StorageLive(_6);
+        _6 = unlikely(move _5) -> [return: bb1, unwind unreachable];
     }
 
     bb1: {
-        switchInt(move _7) -> [0: bb2, otherwise: bb3];
+        switchInt(move _6) -> [0: bb2, otherwise: bb3];
     }
 
     bb2: {
-        _0 = Option::<u32>::Some(_5);
+        _0 = Option::<u32>::Some(_4);
         goto -> bb4;
     }
 
@@ -64,9 +60,9 @@ fn checked_shl(_1: u32, _2: u32) -> Option<u32> {
     }
 
     bb4: {
-        StorageDead(_7);
         StorageDead(_6);
         StorageDead(_5);
+        StorageDead(_4);
         return;
     }
 }
diff --git a/tests/mir-opt/pre-codegen/intrinsics.f_u64.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/intrinsics.f_u64.PreCodegen.after.mir
index 66acbbbb21e..174fb2c0c3c 100644
--- a/tests/mir-opt/pre-codegen/intrinsics.f_u64.PreCodegen.after.mir
+++ b/tests/mir-opt/pre-codegen/intrinsics.f_u64.PreCodegen.after.mir
@@ -2,22 +2,18 @@
 
 fn f_u64() -> () {
     let mut _0: ();
-    let mut _1: u64;
     scope 1 (inlined f_dispatch::<u64>) {
         debug t => const 0_u64;
-        let _2: ();
+        let _1: ();
         scope 2 (inlined std::mem::size_of::<u64>) {
         }
     }
 
     bb0: {
-        StorageLive(_1);
-        _1 = const 0_u64;
-        _2 = f_non_zst::<u64>(move _1) -> [return: bb1, unwind unreachable];
+        _1 = f_non_zst::<u64>(const 0_u64) -> [return: bb1, unwind unreachable];
     }
 
     bb1: {
-        StorageDead(_1);
         return;
     }
 }
diff --git a/tests/mir-opt/pre-codegen/loops.int_range.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/loops.int_range.PreCodegen.after.mir
index 99dc9600e41..4c6bcd1bdbd 100644
--- a/tests/mir-opt/pre-codegen/loops.int_range.PreCodegen.after.mir
+++ b/tests/mir-opt/pre-codegen/loops.int_range.PreCodegen.after.mir
@@ -88,7 +88,7 @@ fn int_range(_1: usize, _2: usize) -> () {
 
     bb7: {
         _10 = ((_6 as Some).0: usize);
-        _11 = opaque::<usize>(_10) -> [return: bb8, unwind continue];
+        _11 = opaque::<usize>(move _10) -> [return: bb8, unwind continue];
     }
 
     bb8: {
diff --git a/tests/mir-opt/pre-codegen/range_iter.inclusive_loop.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/range_iter.inclusive_loop.PreCodegen.after.panic-abort.mir
index 8d306858b43..1b23e421368 100644
--- a/tests/mir-opt/pre-codegen/range_iter.inclusive_loop.PreCodegen.after.panic-abort.mir
+++ b/tests/mir-opt/pre-codegen/range_iter.inclusive_loop.PreCodegen.after.panic-abort.mir
@@ -42,7 +42,7 @@ fn inclusive_loop(_1: u32, _2: u32, _3: impl Fn(u32)) -> () {
         StorageLive(_7);
         StorageLive(_6);
         _6 = &mut _5;
-        _7 = <RangeInclusive<u32> as iter::range::RangeInclusiveIteratorImpl>::spec_next(_6) -> [return: bb2, unwind unreachable];
+        _7 = <RangeInclusive<u32> as iter::range::RangeInclusiveIteratorImpl>::spec_next(move _6) -> [return: bb2, unwind unreachable];
     }
 
     bb2: {
diff --git a/tests/mir-opt/pre-codegen/range_iter.inclusive_loop.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/range_iter.inclusive_loop.PreCodegen.after.panic-unwind.mir
index e92c054c838..bbab4e47a3a 100644
--- a/tests/mir-opt/pre-codegen/range_iter.inclusive_loop.PreCodegen.after.panic-unwind.mir
+++ b/tests/mir-opt/pre-codegen/range_iter.inclusive_loop.PreCodegen.after.panic-unwind.mir
@@ -42,7 +42,7 @@ fn inclusive_loop(_1: u32, _2: u32, _3: impl Fn(u32)) -> () {
         StorageLive(_7);
         StorageLive(_6);
         _6 = &mut _5;
-        _7 = <RangeInclusive<u32> as iter::range::RangeInclusiveIteratorImpl>::spec_next(_6) -> [return: bb2, unwind: bb8];
+        _7 = <RangeInclusive<u32> as iter::range::RangeInclusiveIteratorImpl>::spec_next(move _6) -> [return: bb2, unwind: bb8];
     }
 
     bb2: {
diff --git a/tests/mir-opt/pre-codegen/range_iter.range_inclusive_iter_next.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/range_iter.range_inclusive_iter_next.PreCodegen.after.panic-abort.mir
index 0a71b6b2cf4..b0f475b4db7 100644
--- a/tests/mir-opt/pre-codegen/range_iter.range_inclusive_iter_next.PreCodegen.after.panic-abort.mir
+++ b/tests/mir-opt/pre-codegen/range_iter.range_inclusive_iter_next.PreCodegen.after.panic-abort.mir
@@ -8,7 +8,7 @@ fn range_inclusive_iter_next(_1: &mut RangeInclusive<u32>) -> Option<u32> {
     }
 
     bb0: {
-        _0 = <RangeInclusive<u32> as iter::range::RangeInclusiveIteratorImpl>::spec_next(_1) -> [return: bb1, unwind unreachable];
+        _0 = <RangeInclusive<u32> as iter::range::RangeInclusiveIteratorImpl>::spec_next(move _1) -> [return: bb1, unwind unreachable];
     }
 
     bb1: {
diff --git a/tests/mir-opt/pre-codegen/range_iter.range_inclusive_iter_next.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/range_iter.range_inclusive_iter_next.PreCodegen.after.panic-unwind.mir
index fd565fe75ec..663ec229f72 100644
--- a/tests/mir-opt/pre-codegen/range_iter.range_inclusive_iter_next.PreCodegen.after.panic-unwind.mir
+++ b/tests/mir-opt/pre-codegen/range_iter.range_inclusive_iter_next.PreCodegen.after.panic-unwind.mir
@@ -8,7 +8,7 @@ fn range_inclusive_iter_next(_1: &mut RangeInclusive<u32>) -> Option<u32> {
     }
 
     bb0: {
-        _0 = <RangeInclusive<u32> as iter::range::RangeInclusiveIteratorImpl>::spec_next(_1) -> [return: bb1, unwind continue];
+        _0 = <RangeInclusive<u32> as iter::range::RangeInclusiveIteratorImpl>::spec_next(move _1) -> [return: bb1, unwind continue];
     }
 
     bb1: {
diff --git a/tests/mir-opt/pre-codegen/slice_index.slice_index_range.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_index.slice_index_range.PreCodegen.after.panic-abort.mir
index aff718566ae..df6d2263dc3 100644
--- a/tests/mir-opt/pre-codegen/slice_index.slice_index_range.PreCodegen.after.panic-abort.mir
+++ b/tests/mir-opt/pre-codegen/slice_index.slice_index_range.PreCodegen.after.panic-abort.mir
@@ -12,7 +12,7 @@ fn slice_index_range(_1: &[u32], _2: std::ops::Range<usize>) -> &[u32] {
 
     bb0: {
         StorageLive(_3);
-        _3 = <std::ops::Range<usize> as SliceIndex<[u32]>>::index(move _2, _1) -> [return: bb1, unwind unreachable];
+        _3 = <std::ops::Range<usize> as SliceIndex<[u32]>>::index(move _2, move _1) -> [return: bb1, unwind unreachable];
     }
 
     bb1: {
diff --git a/tests/mir-opt/pre-codegen/slice_index.slice_index_range.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_index.slice_index_range.PreCodegen.after.panic-unwind.mir
index a6b931d2c24..cc1795c3f97 100644
--- a/tests/mir-opt/pre-codegen/slice_index.slice_index_range.PreCodegen.after.panic-unwind.mir
+++ b/tests/mir-opt/pre-codegen/slice_index.slice_index_range.PreCodegen.after.panic-unwind.mir
@@ -12,7 +12,7 @@ fn slice_index_range(_1: &[u32], _2: std::ops::Range<usize>) -> &[u32] {
 
     bb0: {
         StorageLive(_3);
-        _3 = <std::ops::Range<usize> as SliceIndex<[u32]>>::index(move _2, _1) -> [return: bb1, unwind continue];
+        _3 = <std::ops::Range<usize> as SliceIndex<[u32]>>::index(move _2, move _1) -> [return: bb1, unwind continue];
     }
 
     bb1: {
diff --git a/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-abort.mir
index 2cf81d86267..89009864c32 100644
--- a/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-abort.mir
+++ b/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-abort.mir
@@ -38,7 +38,7 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () {
                 scope 6 {
                     let _7: *const T;
                     scope 7 {
-                        debug end => _7;
+                        debug end_or_len => _7;
                         scope 13 (inlined NonNull::<T>::new_unchecked) {
                             debug ptr => _9;
                             let mut _10: *const T;
@@ -138,7 +138,7 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () {
         StorageDead(_9);
         StorageLive(_12);
         _12 = _7;
-        _13 = std::slice::Iter::<'_, T> { ptr: move _11, end: move _12, _marker: const ZeroSized: PhantomData<&T> };
+        _13 = std::slice::Iter::<'_, T> { ptr: move _11, end_or_len: move _12, _marker: const ZeroSized: PhantomData<&T> };
         StorageDead(_12);
         StorageDead(_11);
         StorageDead(_7);
diff --git a/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-unwind.mir
index 6985806ec93..836fa2677b1 100644
--- a/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-unwind.mir
+++ b/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-unwind.mir
@@ -38,7 +38,7 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () {
                 scope 6 {
                     let _7: *const T;
                     scope 7 {
-                        debug end => _7;
+                        debug end_or_len => _7;
                         scope 13 (inlined NonNull::<T>::new_unchecked) {
                             debug ptr => _9;
                             let mut _10: *const T;
@@ -138,7 +138,7 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () {
         StorageDead(_9);
         StorageLive(_12);
         _12 = _7;
-        _13 = std::slice::Iter::<'_, T> { ptr: move _11, end: move _12, _marker: const ZeroSized: PhantomData<&T> };
+        _13 = std::slice::Iter::<'_, T> { ptr: move _11, end_or_len: move _12, _marker: const ZeroSized: PhantomData<&T> };
         StorageDead(_12);
         StorageDead(_11);
         StorageDead(_7);
diff --git a/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-abort.mir
index a4b8460e98e..146fa57a0b1 100644
--- a/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-abort.mir
+++ b/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-abort.mir
@@ -35,7 +35,7 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () {
                 scope 6 {
                     let _7: *const T;
                     scope 7 {
-                        debug end => _7;
+                        debug end_or_len => _7;
                         scope 13 (inlined NonNull::<T>::new_unchecked) {
                             debug ptr => _9;
                             let mut _10: *const T;
@@ -128,7 +128,7 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () {
         StorageDead(_9);
         StorageLive(_12);
         _12 = _7;
-        _13 = std::slice::Iter::<'_, T> { ptr: move _11, end: move _12, _marker: const ZeroSized: PhantomData<&T> };
+        _13 = std::slice::Iter::<'_, T> { ptr: move _11, end_or_len: move _12, _marker: const ZeroSized: PhantomData<&T> };
         StorageDead(_12);
         StorageDead(_11);
         StorageDead(_7);
diff --git a/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-unwind.mir
index 58f312b1aac..65baaf64a9e 100644
--- a/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-unwind.mir
+++ b/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-unwind.mir
@@ -35,7 +35,7 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () {
                 scope 6 {
                     let _7: *const T;
                     scope 7 {
-                        debug end => _7;
+                        debug end_or_len => _7;
                         scope 13 (inlined NonNull::<T>::new_unchecked) {
                             debug ptr => _9;
                             let mut _10: *const T;
@@ -128,7 +128,7 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () {
         StorageDead(_9);
         StorageLive(_12);
         _12 = _7;
-        _13 = std::slice::Iter::<'_, T> { ptr: move _11, end: move _12, _marker: const ZeroSized: PhantomData<&T> };
+        _13 = std::slice::Iter::<'_, T> { ptr: move _11, end_or_len: move _12, _marker: const ZeroSized: PhantomData<&T> };
         StorageDead(_12);
         StorageDead(_11);
         StorageDead(_7);
diff --git a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir
index b550711aa41..a5df36ca388 100644
--- a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir
+++ b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir
@@ -39,7 +39,7 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () {
                 scope 6 {
                     let _7: *const T;
                     scope 7 {
-                        debug end => _7;
+                        debug end_or_len => _7;
                         scope 13 (inlined NonNull::<T>::new_unchecked) {
                             debug ptr => _9;
                             let mut _10: *const T;
@@ -139,7 +139,7 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () {
         StorageDead(_9);
         StorageLive(_12);
         _12 = _7;
-        _13 = std::slice::Iter::<'_, T> { ptr: move _11, end: move _12, _marker: const ZeroSized: PhantomData<&T> };
+        _13 = std::slice::Iter::<'_, T> { ptr: move _11, end_or_len: move _12, _marker: const ZeroSized: PhantomData<&T> };
         StorageDead(_12);
         StorageDead(_11);
         StorageDead(_7);
diff --git a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir
index 23444241cd2..f681da4d275 100644
--- a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir
+++ b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir
@@ -39,7 +39,7 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () {
                 scope 6 {
                     let _7: *const T;
                     scope 7 {
-                        debug end => _7;
+                        debug end_or_len => _7;
                         scope 13 (inlined NonNull::<T>::new_unchecked) {
                             debug ptr => _9;
                             let mut _10: *const T;
@@ -139,7 +139,7 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () {
         StorageDead(_9);
         StorageLive(_12);
         _12 = _7;
-        _13 = std::slice::Iter::<'_, T> { ptr: move _11, end: move _12, _marker: const ZeroSized: PhantomData<&T> };
+        _13 = std::slice::Iter::<'_, T> { ptr: move _11, end_or_len: move _12, _marker: const ZeroSized: PhantomData<&T> };
         StorageDead(_12);
         StorageDead(_11);
         StorageDead(_7);
diff --git a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-abort.mir
index 0471d0757c7..78f96bf4195 100644
--- a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-abort.mir
+++ b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-abort.mir
@@ -5,7 +5,7 @@ fn slice_iter_mut_next_back(_1: &mut std::slice::IterMut<'_, T>) -> Option<&mut
     let mut _0: std::option::Option<&mut T>;
 
     bb0: {
-        _0 = <std::slice::IterMut<'_, T> as DoubleEndedIterator>::next_back(_1) -> [return: bb1, unwind unreachable];
+        _0 = <std::slice::IterMut<'_, T> as DoubleEndedIterator>::next_back(move _1) -> [return: bb1, unwind unreachable];
     }
 
     bb1: {
diff --git a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-unwind.mir
index 386f3a9edcd..dfe5e206fad 100644
--- a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-unwind.mir
+++ b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-unwind.mir
@@ -5,7 +5,7 @@ fn slice_iter_mut_next_back(_1: &mut std::slice::IterMut<'_, T>) -> Option<&mut
     let mut _0: std::option::Option<&mut T>;
 
     bb0: {
-        _0 = <std::slice::IterMut<'_, T> as DoubleEndedIterator>::next_back(_1) -> [return: bb1, unwind continue];
+        _0 = <std::slice::IterMut<'_, T> as DoubleEndedIterator>::next_back(move _1) -> [return: bb1, unwind continue];
     }
 
     bb1: {
diff --git a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.PreCodegen.after.panic-abort.mir
index 8c0209ae19b..8edac638ccd 100644
--- a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.PreCodegen.after.panic-abort.mir
+++ b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.PreCodegen.after.panic-abort.mir
@@ -5,7 +5,7 @@ fn slice_iter_next(_1: &mut std::slice::Iter<'_, T>) -> Option<&T> {
     let mut _0: std::option::Option<&T>;
 
     bb0: {
-        _0 = <std::slice::Iter<'_, T> as Iterator>::next(_1) -> [return: bb1, unwind unreachable];
+        _0 = <std::slice::Iter<'_, T> as Iterator>::next(move _1) -> [return: bb1, unwind unreachable];
     }
 
     bb1: {
diff --git a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.PreCodegen.after.panic-unwind.mir
index e76ec00391c..fdde0717343 100644
--- a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.PreCodegen.after.panic-unwind.mir
+++ b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.PreCodegen.after.panic-unwind.mir
@@ -5,7 +5,7 @@ fn slice_iter_next(_1: &mut std::slice::Iter<'_, T>) -> Option<&T> {
     let mut _0: std::option::Option<&T>;
 
     bb0: {
-        _0 = <std::slice::Iter<'_, T> as Iterator>::next(_1) -> [return: bb1, unwind continue];
+        _0 = <std::slice::Iter<'_, T> as Iterator>::next(move _1) -> [return: bb1, unwind continue];
     }
 
     bb1: {
diff --git a/tests/run-make/comment-section/Makefile b/tests/run-make/comment-section/Makefile
new file mode 100644
index 00000000000..9f810063cc8
--- /dev/null
+++ b/tests/run-make/comment-section/Makefile
@@ -0,0 +1,15 @@
+include ../tools.mk
+
+# only-linux
+
+all:
+	echo 'fn main(){}' | $(RUSTC) - --emit=link,obj -Csave-temps --target=$(TARGET)
+
+	# Check linked output has a `.comment` section with the expected content.
+	readelf -p '.comment' $(TMPDIR)/rust_out | $(CGREP) -F 'rustc version 1.'
+
+	# Check all object files (including temporary outputs) have a `.comment`
+	# section with the expected content.
+	set -e; for f in $(TMPDIR)/*.o; do \
+		readelf -p '.comment' $$f | $(CGREP) -F 'rustc version 1.'; \
+	done
diff --git a/tests/run-make/extern-flag-pathless/Makefile b/tests/run-make/extern-flag-pathless/Makefile
index 701bfcd28c8..36b374e0d2e 100644
--- a/tests/run-make/extern-flag-pathless/Makefile
+++ b/tests/run-make/extern-flag-pathless/Makefile
@@ -3,17 +3,32 @@ include ../tools.mk
 
 # Test mixing pathless --extern with paths.
 
+# Test for static linking by checking that the binary runs if the dylib
+# is removed and test for dynamic linking by checking that the binary
+# fails to run if the dylib is removed.
+
 all:
-	$(RUSTC) bar-static.rs --crate-name=bar --crate-type=rlib
-	$(RUSTC) bar-dynamic.rs --crate-name=bar --crate-type=dylib -C prefer-dynamic
+	$(RUSTC) bar.rs --crate-type=rlib --crate-type=dylib -Cprefer-dynamic
+
 	# rlib preferred over dylib
 	$(RUSTC) foo.rs --extern bar
-	$(call RUN,foo) | $(CGREP) 'static'
+	mv $(call DYLIB,bar) $(TMPDIR)/bar.tmp
+	$(call RUN,foo)
+	mv $(TMPDIR)/bar.tmp $(call DYLIB,bar)
+
 	$(RUSTC) foo.rs --extern bar=$(TMPDIR)/libbar.rlib --extern bar
-	$(call RUN,foo) | $(CGREP) 'static'
+	mv $(call DYLIB,bar) $(TMPDIR)/bar.tmp
+	$(call RUN,foo)
+	mv $(TMPDIR)/bar.tmp $(call DYLIB,bar)
+
 	# explicit --extern overrides pathless
 	$(RUSTC) foo.rs --extern bar=$(call DYLIB,bar) --extern bar
-	$(call RUN,foo) | $(CGREP) 'dynamic'
+	mv $(call DYLIB,bar) $(TMPDIR)/bar.tmp
+	$(call FAIL,foo)
+	mv $(TMPDIR)/bar.tmp $(call DYLIB,bar)
+
 	# prefer-dynamic does what it says
 	$(RUSTC) foo.rs --extern bar -C prefer-dynamic
-	$(call RUN,foo) | $(CGREP) 'dynamic'
+	mv $(call DYLIB,bar) $(TMPDIR)/bar.tmp
+	$(call FAIL,foo)
+	mv $(TMPDIR)/bar.tmp $(call DYLIB,bar)
diff --git a/tests/run-make/extern-flag-pathless/bar-dynamic.rs b/tests/run-make/extern-flag-pathless/bar-dynamic.rs
deleted file mode 100644
index e2d68d517ff..00000000000
--- a/tests/run-make/extern-flag-pathless/bar-dynamic.rs
+++ /dev/null
@@ -1,3 +0,0 @@
-pub fn f() {
-    println!("dynamic");
-}
diff --git a/tests/run-make/extern-flag-pathless/bar-static.rs b/tests/run-make/extern-flag-pathless/bar-static.rs
deleted file mode 100644
index 240d8bde4d1..00000000000
--- a/tests/run-make/extern-flag-pathless/bar-static.rs
+++ /dev/null
@@ -1,3 +0,0 @@
-pub fn f() {
-    println!("static");
-}
diff --git a/tests/run-make/extern-flag-pathless/bar.rs b/tests/run-make/extern-flag-pathless/bar.rs
new file mode 100644
index 00000000000..cdc6c27d800
--- /dev/null
+++ b/tests/run-make/extern-flag-pathless/bar.rs
@@ -0,0 +1 @@
+pub fn f() {}
diff --git a/tests/run-make/llvm-ident/Makefile b/tests/run-make/llvm-ident/Makefile
new file mode 100644
index 00000000000..e583e6018e0
--- /dev/null
+++ b/tests/run-make/llvm-ident/Makefile
@@ -0,0 +1,19 @@
+include ../tools.mk
+
+# only-linux
+
+all:
+	# `-Ccodegen-units=16 -Copt-level=2` is used here to trigger thin LTO
+	# across codegen units to test deduplication of the named metadata
+	# (see `LLVMRustPrepareThinLTOImport` for details).
+	echo 'fn main(){}' | $(RUSTC) - --emit=link,obj -Csave-temps -Ccodegen-units=16 -Copt-level=2 --target=$(TARGET)
+
+	# `llvm-dis` is used here since `--emit=llvm-ir` does not emit LLVM IR
+	# for temporary outputs.
+	"$(LLVM_BIN_DIR)"/llvm-dis $(TMPDIR)/*.bc
+
+	# Check LLVM IR files (including temporary outputs) have `!llvm.ident`
+	# named metadata, reusing the related codegen test.
+	set -e; for f in $(TMPDIR)/*.ll; do \
+		$(LLVM_FILECHECK) --input-file $$f ../../codegen/llvm-ident.rs; \
+	done
diff --git a/tests/run-make/mixing-libs/Makefile b/tests/run-make/mixing-libs/Makefile
index e8262b28401..459db0dfdb2 100644
--- a/tests/run-make/mixing-libs/Makefile
+++ b/tests/run-make/mixing-libs/Makefile
@@ -2,9 +2,7 @@
 include ../tools.mk
 
 all:
-	$(RUSTC) rlib.rs
-	$(RUSTC) dylib.rs
-	$(RUSTC) rlib.rs --crate-type=dylib
-	$(RUSTC) dylib.rs
-	$(call REMOVE_DYLIBS,rlib)
+	$(RUSTC) rlib.rs --crate-type=rlib --crate-type=dylib
+	$(RUSTC) dylib.rs # no -Cprefer-dynamic so statically linking librlib.rlib
+	$(call REMOVE_DYLIBS,rlib) # remove librlib.so to test that prog.rs doesn't get confused about the removed dylib version of librlib
 	$(RUSTC) prog.rs && exit 1 || exit 0
diff --git a/tests/run-make/no-cdylib-as-rdylib/Makefile b/tests/run-make/no-cdylib-as-rdylib/Makefile
new file mode 100644
index 00000000000..4d2be0aea91
--- /dev/null
+++ b/tests/run-make/no-cdylib-as-rdylib/Makefile
@@ -0,0 +1,16 @@
+# ignore-cross-compile
+include ../tools.mk
+
+# Test that rustc will not attempt to link against a cdylib as if
+# it is a rust dylib when an rlib for the same crate is available.
+# Previously rustc didn't actually check if any further formats of
+# a crate which has been loaded are of the same version and if
+# they are actually valid. This caused a cdylib to be interpreted
+# as rust dylib as soon as the corresponding rlib was loaded. As
+# cdylibs don't export any rust symbols, linking would fail if
+# rustc decides to link against the cdylib rather than the rlib.
+
+all:
+	$(RUSTC) bar.rs --crate-type=rlib --crate-type=cdylib
+	$(RUSTC) foo.rs -C prefer-dynamic
+	$(call RUN,foo)
diff --git a/tests/run-make/no-cdylib-as-rdylib/bar.rs b/tests/run-make/no-cdylib-as-rdylib/bar.rs
new file mode 100644
index 00000000000..c5c0bc606cd
--- /dev/null
+++ b/tests/run-make/no-cdylib-as-rdylib/bar.rs
@@ -0,0 +1 @@
+pub fn bar() {}
diff --git a/tests/run-make/no-cdylib-as-rdylib/foo.rs b/tests/run-make/no-cdylib-as-rdylib/foo.rs
new file mode 100644
index 00000000000..8d68535e3b6
--- /dev/null
+++ b/tests/run-make/no-cdylib-as-rdylib/foo.rs
@@ -0,0 +1,5 @@
+extern crate bar;
+
+fn main() {
+    bar::bar();
+}
diff --git a/tests/run-make/print-cfg/Makefile b/tests/run-make/print-cfg/Makefile
index 126f5768c90..6b153e5b54e 100644
--- a/tests/run-make/print-cfg/Makefile
+++ b/tests/run-make/print-cfg/Makefile
@@ -2,7 +2,7 @@
 
 include ../tools.mk
 
-all: default
+all: default output_to_file
 	$(RUSTC) --target x86_64-pc-windows-gnu --print cfg | $(CGREP) windows
 	$(RUSTC) --target x86_64-pc-windows-gnu --print cfg | $(CGREP) x86_64
 	$(RUSTC) --target i686-pc-windows-msvc --print cfg | $(CGREP) msvc
@@ -11,6 +11,23 @@ all: default
 	$(RUSTC) --target arm-unknown-linux-gnueabihf --print cfg | $(CGREP) target_abi=
 	$(RUSTC) --target arm-unknown-linux-gnueabihf --print cfg | $(CGREP) eabihf
 
+output_to_file:
+	# Backend-independent, printed by rustc_driver_impl/src/lib.rs
+	$(RUSTC) --target x86_64-pc-windows-gnu --print cfg=$(TMPDIR)/cfg.txt
+	$(CGREP) windows < $(TMPDIR)/cfg.txt
+
+	# Printed from CodegenBackend trait impl in rustc_codegen_llvm/src/lib.rs
+	$(RUSTC) --print relocation-models=$(TMPDIR)/relocation-models.txt
+	$(CGREP) dynamic-no-pic < $(TMPDIR)/relocation-models.txt
+
+	# Printed by compiler/rustc_codegen_llvm/src/llvm_util.rs
+	$(RUSTC) --target wasm32-unknown-unknown --print target-features=$(TMPDIR)/target-features.txt
+	$(CGREP) reference-types < $(TMPDIR)/target-features.txt
+
+	# Printed by C++ code in rustc_llvm/llvm-wrapper/PassWrapper.cpp
+	$(RUSTC) --target wasm32-unknown-unknown --print target-cpus=$(TMPDIR)/target-cpus.txt
+	$(CGREP) generic < $(TMPDIR)/target-cpus.txt
+
 ifdef IS_WINDOWS
 default:
 	$(RUSTC) --print cfg | $(CGREP) windows
diff --git a/tests/run-make/rmeta-preferred/Makefile b/tests/run-make/rmeta-preferred/Makefile
new file mode 100644
index 00000000000..3bf12cced29
--- /dev/null
+++ b/tests/run-make/rmeta-preferred/Makefile
@@ -0,0 +1,16 @@
+# ignore-cross-compile
+include ../tools.mk
+
+# Test that using rlibs and rmeta dep crates work together. Specifically, that
+# there can be both an rmeta and an rlib file and rustc will prefer the rmeta
+# file.
+#
+# This behavior is simply making sure this doesn't accidentally change; in this
+# case we want to make sure that the rlib isn't being used as that would cause
+# bugs in -Zbinary-dep-depinfo (see #68298).
+
+all:
+	$(RUSTC) rmeta_aux.rs --crate-type=rlib --emit link,metadata
+	$(RUSTC) lib.rs --crate-type=rlib --emit dep-info -Zbinary-dep-depinfo
+	$(CGREP) "librmeta_aux.rmeta" < $(TMPDIR)/lib.d
+	$(CGREP) -v "librmeta_aux.rlib" < $(TMPDIR)/lib.d
diff --git a/tests/ui/rmeta/rmeta-rpass.rs b/tests/run-make/rmeta-preferred/lib.rs
index 173a6a394eb..d0b81a0628a 100644
--- a/tests/ui/rmeta/rmeta-rpass.rs
+++ b/tests/run-make/rmeta-preferred/lib.rs
@@ -1,4 +1,3 @@
-// run-pass
 // Test that using rlibs and rmeta dep crates work together. Specifically, that
 // there can be both an rmeta and an rlib file and rustc will prefer the rmeta
 // file.
@@ -7,12 +6,9 @@
 // case we want to make sure that the rlib isn't being used as that would cause
 // bugs in -Zbinary-dep-depinfo (see #68298).
 
-// aux-build:rmeta-rmeta.rs
-// aux-build:rmeta-rlib-rpass.rs
-
 extern crate rmeta_aux;
 use rmeta_aux::Foo;
 
-pub fn main() {
-    let _ = Foo { field2: 42 };
+pub fn foo() {
+    let _ = Foo { field: 42 };
 }
diff --git a/tests/run-make/rmeta-preferred/rmeta_aux.rs b/tests/run-make/rmeta-preferred/rmeta_aux.rs
new file mode 100644
index 00000000000..3f7a12b5054
--- /dev/null
+++ b/tests/run-make/rmeta-preferred/rmeta_aux.rs
@@ -0,0 +1,3 @@
+pub struct Foo {
+    pub field: i32,
+}
diff --git a/tests/rustdoc/default-trait-method.rs b/tests/rustdoc/default-trait-method.rs
index 6d0e339c48d..c8950678164 100644
--- a/tests/rustdoc/default-trait-method.rs
+++ b/tests/rustdoc/default-trait-method.rs
@@ -1,26 +1,45 @@
 #![feature(min_specialization)]
 
 // @has default_trait_method/trait.Item.html
-// @has - '//*[@id="tymethod.foo"]' 'fn foo()'
-// @!has - '//*[@id="tymethod.foo"]' 'default fn foo()'
-// @has - '//*[@id="tymethod.bar"]' 'fn bar()'
-// @!has - '//*[@id="tymethod.bar"]' 'default fn bar()'
-// @has - '//*[@id="method.baz"]' 'fn baz()'
-// @!has - '//*[@id="method.baz"]' 'default fn baz()'
 pub trait Item {
+    // @has - '//*[@id="tymethod.foo"]' 'fn foo()'
+    // @!has - '//*[@id="tymethod.foo"]' 'default fn foo()'
     fn foo();
+
+    // @has - '//*[@id="tymethod.bar"]' 'fn bar()'
+    // @!has - '//*[@id="tymethod.bar"]' 'default fn bar()'
     fn bar();
-    fn baz() {}
+
+    // @has - '//*[@id="tymethod.baz"]' 'unsafe fn baz()'
+    // @!has - '//*[@id="tymethod.baz"]' 'default unsafe fn baz()'
+    unsafe fn baz();
+
+    // @has - '//*[@id="tymethod.quux"]' 'unsafe fn quux()'
+    // @!has - '//*[@id="tymethod.quux"]' 'default unsafe fn quux()'
+    unsafe fn quux();
+
+    // @has - '//*[@id="method.xyzzy"]' 'fn xyzzy()'
+    // @!has - '//*[@id="method.xyzzy"]' 'default fn xyzzy()'
+    fn xyzzy() {}
 }
 
 // @has default_trait_method/struct.Foo.html
-// @has - '//*[@id="method.foo"]' 'default fn foo()'
-// @has - '//*[@id="method.bar"]' 'fn bar()'
-// @!has - '//*[@id="method.bar"]' 'default fn bar()'
-// @has - '//*[@id="method.baz"]' 'fn baz()'
-// @!has - '//*[@id="method.baz"]' 'default fn baz()'
 pub struct Foo;
 impl Item for Foo {
+    // @has - '//*[@id="method.foo"]' 'default fn foo()'
     default fn foo() {}
+
+    // @has - '//*[@id="method.bar"]' 'fn bar()'
+    // @!has - '//*[@id="method.bar"]' 'default fn bar()'
     fn bar() {}
+
+    // @has - '//*[@id="method.baz"]' 'default unsafe fn baz()'
+    default unsafe fn baz() {}
+
+    // @has - '//*[@id="method.quux"]' 'unsafe fn quux()'
+    // @!has - '//*[@id="method.quux"]' 'default unsafe fn quux()'
+    unsafe fn quux() {}
+
+    // @has - '//*[@id="method.xyzzy"]' 'fn xyzzy()'
+    // @!has - '//*[@id="method.xyzzy"]' 'default fn xyzzy()'
 }
diff --git a/tests/ui/codegen/target-cpus.rs b/tests/ui/codegen/target-cpus.rs
new file mode 100644
index 00000000000..1dff3ee6011
--- /dev/null
+++ b/tests/ui/codegen/target-cpus.rs
@@ -0,0 +1,4 @@
+// needs-llvm-components: webassembly
+// min-llvm-version: 17
+// compile-flags: --print=target-cpus --target=wasm32-unknown-unknown
+// check-pass
diff --git a/tests/ui/codegen/target-cpus.stdout b/tests/ui/codegen/target-cpus.stdout
new file mode 100644
index 00000000000..f60ba0f5034
--- /dev/null
+++ b/tests/ui/codegen/target-cpus.stdout
@@ -0,0 +1,4 @@
+Available CPUs for this target:
+    bleeding-edge
+    generic       - This is the default target CPU for the current build target (currently wasm32-unknown-unknown).
+    mvp
diff --git a/tests/ui/consts/const-size_of-cycle.stderr b/tests/ui/consts/const-size_of-cycle.stderr
index 46b432357aa..08f0c1563cc 100644
--- a/tests/ui/consts/const-size_of-cycle.stderr
+++ b/tests/ui/consts/const-size_of-cycle.stderr
@@ -15,7 +15,8 @@ note: ...which requires const-evaluating + checking `Foo::bytes::{constant#0}`..
 LL |     bytes: [u8; std::mem::size_of::<Foo>()]
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^
    = note: ...which requires computing layout of `Foo`...
-   = note: ...which requires computing layout of `[u8; std::mem::size_of::<Foo>()]`...
+   = note: ...which requires computing layout (naive) of `Foo`...
+   = note: ...which requires computing layout (naive) of `[u8; std::mem::size_of::<Foo>()]`...
    = note: ...which requires normalizing `[u8; std::mem::size_of::<Foo>()]`...
    = note: ...which again requires evaluating type-level constant, completing the cycle
 note: cycle used when checking that `Foo` is well-formed
diff --git a/tests/ui/consts/issue-44415.stderr b/tests/ui/consts/issue-44415.stderr
index 01d24a62081..7ff413def86 100644
--- a/tests/ui/consts/issue-44415.stderr
+++ b/tests/ui/consts/issue-44415.stderr
@@ -15,7 +15,8 @@ note: ...which requires const-evaluating + checking `Foo::bytes::{constant#0}`..
 LL |     bytes: [u8; unsafe { intrinsics::size_of::<Foo>() }],
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = note: ...which requires computing layout of `Foo`...
-   = note: ...which requires computing layout of `[u8; unsafe { intrinsics::size_of::<Foo>() }]`...
+   = note: ...which requires computing layout (naive) of `Foo`...
+   = note: ...which requires computing layout (naive) of `[u8; unsafe { intrinsics::size_of::<Foo>() }]`...
    = note: ...which requires normalizing `[u8; unsafe { intrinsics::size_of::<Foo>() }]`...
    = note: ...which again requires evaluating type-level constant, completing the cycle
 note: cycle used when checking that `Foo` is well-formed
diff --git a/tests/ui/dyn-star/param-env-region-infer.next.stderr b/tests/ui/dyn-star/param-env-region-infer.next.stderr
index 28aec533a00..51df71a373e 100644
--- a/tests/ui/dyn-star/param-env-region-infer.next.stderr
+++ b/tests/ui/dyn-star/param-env-region-infer.next.stderr
@@ -9,7 +9,7 @@ note: ...which requires type-checking `make_dyn_star`...
    |
 LL | fn make_dyn_star<'a, T: PointerLike + Debug + 'a>(t: T) -> impl PointerLike + Debug + 'a {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   = note: ...which requires computing layout of `make_dyn_star::{opaque#0}`...
+   = note: ...which requires computing layout (naive) of `make_dyn_star::{opaque#0}`...
    = note: ...which requires normalizing `make_dyn_star::{opaque#0}`...
    = note: ...which again requires computing type of `make_dyn_star::{opaque#0}`, completing the cycle
 note: cycle used when checking item types in top-level module
diff --git a/tests/ui/generics/issue-32498.rs b/tests/ui/generics/issue-32498.rs
index 1b54401097e..0abd5b1a9b1 100644
--- a/tests/ui/generics/issue-32498.rs
+++ b/tests/ui/generics/issue-32498.rs
@@ -1,5 +1,6 @@
 // run-pass
 #![allow(dead_code)]
+#![recursion_limit = "129"]
 
 // Making sure that no overflow occurs.
 
diff --git a/tests/ui/layout/valid_range_oob.stderr b/tests/ui/layout/valid_range_oob.stderr
index a3a514fb830..772113fa5fb 100644
--- a/tests/ui/layout/valid_range_oob.stderr
+++ b/tests/ui/layout/valid_range_oob.stderr
@@ -1,6 +1,6 @@
 error: the compiler unexpectedly panicked. this is a bug.
 
 query stack during panic:
-#0 [layout_of] computing layout of `Foo`
-#1 [eval_to_allocation_raw] const-evaluating + checking `FOO`
+#0 [naive_layout_of] computing layout (naive) of `Foo`
+#1 [layout_of] computing layout of `Foo`
 end of query stack
diff --git a/tests/ui/lint/invalid_value.stderr b/tests/ui/lint/invalid_value.stderr
index 57531b0968f..066fdccbaad 100644
--- a/tests/ui/lint/invalid_value.stderr
+++ b/tests/ui/lint/invalid_value.stderr
@@ -34,8 +34,7 @@ LL |         let _val: Wrap<&'static T> = mem::zeroed();
    |                                      this code causes undefined behavior when executed
    |                                      help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
    |
-   = note: `Wrap<&T>` must be non-null
-note: because references must be non-null (in this struct field)
+note: references must be non-null (in this struct field)
   --> $DIR/invalid_value.rs:17:18
    |
 LL | struct Wrap<T> { wrapped: T }
@@ -50,8 +49,7 @@ LL |         let _val: Wrap<&'static T> = mem::uninitialized();
    |                                      this code causes undefined behavior when executed
    |                                      help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
    |
-   = note: `Wrap<&T>` must be non-null
-note: because references must be non-null (in this struct field)
+note: references must be non-null (in this struct field)
   --> $DIR/invalid_value.rs:17:18
    |
 LL | struct Wrap<T> { wrapped: T }
diff --git a/tests/ui/recursion/issue-26548-recursion-via-normalize.rs b/tests/ui/recursion/issue-26548-recursion-via-normalize.rs
index 6c7fc4beb54..14bc74f57f6 100644
--- a/tests/ui/recursion/issue-26548-recursion-via-normalize.rs
+++ b/tests/ui/recursion/issue-26548-recursion-via-normalize.rs
@@ -1,9 +1,9 @@
-//~ ERROR cycle detected when computing layout of `core::option::Option<S>`
+//~ ERROR cycle detected when computing layout (naive) of `core::option::Option<S>`
 //~| NOTE see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
-//~| NOTE ...which requires computing layout of `S`...
-//~| NOTE ...which requires computing layout of `core::option::Option<<S as Mirror>::It>`...
-//~| NOTE ...which again requires computing layout of `core::option::Option<S>`, completing the cycle
-//~| NOTE cycle used when computing layout of `core::option::Option<<S as Mirror>::It>`
+//~| NOTE ...which requires computing layout (naive) of `S`...
+//~| NOTE ...which requires computing layout (naive) of `core::option::Option<<S as Mirror>::It>`...
+//~| NOTE ...which again requires computing layout (naive) of `core::option::Option<S>`, completing the cycle
+//~| NOTE cycle used when computing layout (naive) of `core::option::Option<<S as Mirror>::It>`
 
 trait Mirror {
     type It: ?Sized;
diff --git a/tests/ui/recursion/issue-26548-recursion-via-normalize.stderr b/tests/ui/recursion/issue-26548-recursion-via-normalize.stderr
index 514bed60700..109ba278232 100644
--- a/tests/ui/recursion/issue-26548-recursion-via-normalize.stderr
+++ b/tests/ui/recursion/issue-26548-recursion-via-normalize.stderr
@@ -1,9 +1,9 @@
-error[E0391]: cycle detected when computing layout of `core::option::Option<S>`
+error[E0391]: cycle detected when computing layout (naive) of `core::option::Option<S>`
    |
-   = note: ...which requires computing layout of `S`...
-   = note: ...which requires computing layout of `core::option::Option<<S as Mirror>::It>`...
-   = note: ...which again requires computing layout of `core::option::Option<S>`, completing the cycle
-   = note: cycle used when computing layout of `core::option::Option<<S as Mirror>::It>`
+   = note: ...which requires computing layout (naive) of `S`...
+   = note: ...which requires computing layout (naive) of `core::option::Option<<S as Mirror>::It>`...
+   = note: ...which again requires computing layout (naive) of `core::option::Option<S>`, completing the cycle
+   = note: cycle used when computing layout (naive) of `core::option::Option<<S as Mirror>::It>`
    = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
 
 error: aborting due to previous error
diff --git a/tests/ui/recursion_limit/zero-overflow.rs b/tests/ui/recursion_limit/zero-overflow.rs
index 77bd8185676..98b3da65135 100644
--- a/tests/ui/recursion_limit/zero-overflow.rs
+++ b/tests/ui/recursion_limit/zero-overflow.rs
@@ -1,4 +1,4 @@
-//~ ERROR overflow evaluating the requirement `&mut Self: DispatchFromDyn<&mut RustaceansAreAwesome>
+//~ ERROR queries overflow the depth limit!
 //~| HELP consider increasing the recursion limit
 // build-fail
 
diff --git a/tests/ui/recursion_limit/zero-overflow.stderr b/tests/ui/recursion_limit/zero-overflow.stderr
index 9007ec0d784..172c767d9f0 100644
--- a/tests/ui/recursion_limit/zero-overflow.stderr
+++ b/tests/ui/recursion_limit/zero-overflow.stderr
@@ -1,7 +1,7 @@
-error[E0275]: overflow evaluating the requirement `&mut Self: DispatchFromDyn<&mut RustaceansAreAwesome>`
+error: queries overflow the depth limit!
    |
    = help: consider increasing the recursion limit by adding a `#![recursion_limit = "2"]` attribute to your crate (`zero_overflow`)
+   = note: query depth increased by 2 when computing layout of `()`
 
 error: aborting due to previous error
 
-For more information about this error, try `rustc --explain E0275`.
diff --git a/tests/ui/repr/repr-transparent.stderr b/tests/ui/repr/repr-transparent.stderr
index cb1e2337776..028fc25db46 100644
--- a/tests/ui/repr/repr-transparent.stderr
+++ b/tests/ui/repr/repr-transparent.stderr
@@ -20,13 +20,13 @@ error[E0691]: zero-sized field in transparent struct has alignment larger than 1
   --> $DIR/repr-transparent.rs:36:32
    |
 LL | struct NontrivialAlignZst(u32, [u16; 0]);
-   |                                ^^^^^^^^ has alignment larger than 1
+   |                                ^^^^^^^^ has alignment of 2, which is larger than 1
 
 error[E0691]: zero-sized field in transparent struct has alignment larger than 1
   --> $DIR/repr-transparent.rs:42:24
    |
 LL | struct GenericAlign<T>(ZstAlign32<T>, u32);
-   |                        ^^^^^^^^^^^^^ has alignment larger than 1
+   |                        ^^^^^^^^^^^^^ has alignment of 32, which is larger than 1
 
 error[E0084]: unsupported representation for zero-variant enum
   --> $DIR/repr-transparent.rs:44:1
@@ -66,13 +66,13 @@ error[E0691]: zero-sized field in transparent enum has alignment larger than 1
   --> $DIR/repr-transparent.rs:71:14
    |
 LL |     Foo(u32, [u16; 0]),
-   |              ^^^^^^^^ has alignment larger than 1
+   |              ^^^^^^^^ has alignment of 2, which is larger than 1
 
 error[E0691]: zero-sized field in transparent enum has alignment larger than 1
   --> $DIR/repr-transparent.rs:76:11
    |
 LL |     Foo { bar: ZstAlign32<T>, baz: u32 }
-   |           ^^^^^^^^^^^^^^^^^^ has alignment larger than 1
+   |           ^^^^^^^^^^^^^^^^^^ has alignment of 32, which is larger than 1
 
 error[E0690]: transparent union needs at most one non-zero-sized field, but has 2
   --> $DIR/repr-transparent.rs:85:1
diff --git a/tests/ui/rmeta/auxiliary/rmeta-rlib-rpass.rs b/tests/ui/rmeta/auxiliary/rmeta-rlib-rpass.rs
deleted file mode 100644
index f5e8c3d2a5c..00000000000
--- a/tests/ui/rmeta/auxiliary/rmeta-rlib-rpass.rs
+++ /dev/null
@@ -1,8 +0,0 @@
-// no-prefer-dynamic
-
-#![crate_type="rlib"]
-#![crate_name="rmeta_aux"]
-
-pub struct Foo {
-    pub field: i32,
-}
diff --git a/tests/ui/rmeta/auxiliary/rmeta-rmeta.rs b/tests/ui/rmeta/auxiliary/rmeta-rmeta.rs
deleted file mode 100644
index 4a6d055a81f..00000000000
--- a/tests/ui/rmeta/auxiliary/rmeta-rmeta.rs
+++ /dev/null
@@ -1,9 +0,0 @@
-// no-prefer-dynamic
-// compile-flags: --emit=metadata
-
-#![crate_type="rlib"]
-#![crate_name="rmeta_aux"]
-
-pub struct Foo {
-    pub field2: i32,
-}
diff --git a/tests/ui/simd/shuffle.rs b/tests/ui/simd/shuffle.rs
index 3592adfdc6a..461243d4892 100644
--- a/tests/ui/simd/shuffle.rs
+++ b/tests/ui/simd/shuffle.rs
@@ -1,14 +1,24 @@
-//run-pass
+// run-pass
+// revisions: opt noopt
+//[noopt] compile-flags: -Copt-level=0
+//[opt] compile-flags: -O
 #![feature(repr_simd, platform_intrinsics)]
+#![allow(incomplete_features)]
+#![feature(adt_const_params)]
 
 extern "platform-intrinsic" {
     fn simd_shuffle<T, I, U>(a: T, b: T, i: I) -> U;
+    fn simd_shuffle16<T, U>(x: T, y: T, idx: [u32; 16]) -> U;
 }
 
 #[derive(Copy, Clone)]
 #[repr(simd)]
 struct Simd<T, const N: usize>([T; N]);
 
+pub unsafe fn __shuffle_vector16<const IDX: [u32; 16], T, U>(x: T, y: T) -> U {
+    simd_shuffle16(x, y, IDX)
+}
+
 fn main() {
     const I1: [u32; 4] = [0, 2, 4, 6];
     const I2: [u32; 2] = [1, 5];
@@ -21,4 +31,16 @@ fn main() {
         let y: Simd<u8, 2> = simd_shuffle(a, b, I2);
         assert_eq!(y.0, [1, 5]);
     }
+    // Test that an indirection (via an unnamed constant)
+    // through a const generic parameter also works.
+    // See https://github.com/rust-lang/rust/issues/113500 for details.
+    let a = Simd::<u8, 16>([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]);
+    let b = Simd::<u8, 16>([16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]);
+    unsafe {
+        __shuffle_vector16::<
+            { [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] },
+            Simd<u8, 16>,
+            Simd<u8, 16>,
+        >(a, b);
+    }
 }
diff --git a/tests/ui/sized/recursive-type-2.rs b/tests/ui/sized/recursive-type-2.rs
index 7d95417a6ff..7ee5ee854d4 100644
--- a/tests/ui/sized/recursive-type-2.rs
+++ b/tests/ui/sized/recursive-type-2.rs
@@ -1,5 +1,5 @@
 // build-fail
-//~^ ERROR cycle detected when computing layout of `Foo<()>`
+//~^ ERROR cycle detected when computing layout (naive) of `Foo<()>`
 
 trait A { type Assoc: ?Sized; }
 
diff --git a/tests/ui/sized/recursive-type-2.stderr b/tests/ui/sized/recursive-type-2.stderr
index 0f72f74145e..502b0a4352c 100644
--- a/tests/ui/sized/recursive-type-2.stderr
+++ b/tests/ui/sized/recursive-type-2.stderr
@@ -1,12 +1,8 @@
-error[E0391]: cycle detected when computing layout of `Foo<()>`
+error[E0391]: cycle detected when computing layout (naive) of `Foo<()>`
    |
-   = note: ...which requires computing layout of `<() as A>::Assoc`...
-   = note: ...which again requires computing layout of `Foo<()>`, completing the cycle
-note: cycle used when elaborating drops for `main`
-  --> $DIR/recursive-type-2.rs:11:1
-   |
-LL | fn main() {
-   | ^^^^^^^^^
+   = note: ...which requires computing layout (naive) of `<() as A>::Assoc`...
+   = note: ...which again requires computing layout (naive) of `Foo<()>`, completing the cycle
+   = note: cycle used when computing layout of `Foo<()>`
    = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
 
 error: aborting due to previous error
diff --git a/tests/ui/traits/new-solver/assembly/assemble-normalizing-self-ty-impl-ambiguity.rs b/tests/ui/traits/new-solver/assembly/assemble-normalizing-self-ty-impl-ambiguity.rs
new file mode 100644
index 00000000000..826e8c1e0b1
--- /dev/null
+++ b/tests/ui/traits/new-solver/assembly/assemble-normalizing-self-ty-impl-ambiguity.rs
@@ -0,0 +1,27 @@
+// compile-flags: -Ztrait-solver=next
+// check-pass
+
+// Checks that we do not get ambiguity by considering an impl
+// multiple times if we're able to normalize the self type.
+
+trait Trait<'a> {}
+
+impl<'a, T: 'a> Trait<'a> for T {}
+
+fn impls_trait<'a, T: Trait<'a>>() {}
+
+trait Id {
+    type Assoc;
+}
+impl<T> Id for T {
+    type Assoc = T;
+}
+
+fn call<T>() {
+    impls_trait::<<T as Id>::Assoc>();
+}
+
+fn main() {
+    call::<()>();
+    impls_trait::<<<() as Id>::Assoc as Id>::Assoc>();
+}
diff --git a/tests/ui/traits/new-solver/dont-normalize-proj-with-error.rs b/tests/ui/traits/new-solver/dont-normalize-proj-with-error.rs
new file mode 100644
index 00000000000..19a6fa990ff
--- /dev/null
+++ b/tests/ui/traits/new-solver/dont-normalize-proj-with-error.rs
@@ -0,0 +1,22 @@
+// compile-flags: -Ztrait-solver=next
+
+// Test that we don't incorrectly leak unconstrained inference variables
+// if the projection contained an error. This caused an ICE in writeback.
+
+trait Mirror {
+    type Assoc: ?Sized;
+}
+
+struct Wrapper<T: ?Sized>(T);
+impl<T: ?Sized> Mirror for Wrapper<T> {
+    type Assoc = T;
+}
+
+fn mirror<W: Mirror>(_: W) -> Box<W::Assoc> { todo!() }
+
+fn type_error() -> TypeError { todo!() }
+//~^ ERROR cannot find type `TypeError` in this scope
+
+fn main() {
+    let x = mirror(type_error());
+}
diff --git a/tests/ui/traits/new-solver/dont-normalize-proj-with-error.stderr b/tests/ui/traits/new-solver/dont-normalize-proj-with-error.stderr
new file mode 100644
index 00000000000..5a7459ec1fd
--- /dev/null
+++ b/tests/ui/traits/new-solver/dont-normalize-proj-with-error.stderr
@@ -0,0 +1,9 @@
+error[E0412]: cannot find type `TypeError` in this scope
+  --> $DIR/dont-normalize-proj-with-error.rs:17:20
+   |
+LL | fn type_error() -> TypeError { todo!() }
+   |                    ^^^^^^^^^ not found in this scope
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0412`.
diff --git a/tests/ui/traits/object/print_vtable_sizes.rs b/tests/ui/traits/object/print_vtable_sizes.rs
index 5656094990b..f510608537a 100644
--- a/tests/ui/traits/object/print_vtable_sizes.rs
+++ b/tests/ui/traits/object/print_vtable_sizes.rs
@@ -10,8 +10,9 @@ trait C {
     fn x() {} // not object safe, shouldn't be reported
 }
 
-// This ideally should not have any upcasting cost,
-// but currently does due to a bug
+// This does not have any upcasting cost,
+// because both `Send` and `Sync` are traits
+// with no methods
 trait D: Send + Sync + help::MarkerWithSuper {}
 
 // This can't have no cost without reordering,
diff --git a/tests/ui/traits/object/print_vtable_sizes.stdout b/tests/ui/traits/object/print_vtable_sizes.stdout
index 3ba650bc360..ce90c76217b 100644
--- a/tests/ui/traits/object/print_vtable_sizes.stdout
+++ b/tests/ui/traits/object/print_vtable_sizes.stdout
@@ -1,8 +1,8 @@
-print-vtable-sizes { "crate_name": "<UNKNOWN_CRATE>", "trait_name": "D", "entries": "7", "entries_ignoring_upcasting": "4", "entries_for_upcasting": "3", "upcasting_cost_percent": "75" }
 print-vtable-sizes { "crate_name": "<UNKNOWN_CRATE>", "trait_name": "E", "entries": "6", "entries_ignoring_upcasting": "4", "entries_for_upcasting": "2", "upcasting_cost_percent": "50" }
 print-vtable-sizes { "crate_name": "<UNKNOWN_CRATE>", "trait_name": "G", "entries": "14", "entries_ignoring_upcasting": "11", "entries_for_upcasting": "3", "upcasting_cost_percent": "27.27272727272727" }
 print-vtable-sizes { "crate_name": "<UNKNOWN_CRATE>", "trait_name": "A", "entries": "6", "entries_ignoring_upcasting": "5", "entries_for_upcasting": "1", "upcasting_cost_percent": "20" }
 print-vtable-sizes { "crate_name": "<UNKNOWN_CRATE>", "trait_name": "B", "entries": "4", "entries_ignoring_upcasting": "4", "entries_for_upcasting": "0", "upcasting_cost_percent": "0" }
+print-vtable-sizes { "crate_name": "<UNKNOWN_CRATE>", "trait_name": "D", "entries": "4", "entries_ignoring_upcasting": "4", "entries_for_upcasting": "0", "upcasting_cost_percent": "0" }
 print-vtable-sizes { "crate_name": "<UNKNOWN_CRATE>", "trait_name": "F", "entries": "6", "entries_ignoring_upcasting": "6", "entries_for_upcasting": "0", "upcasting_cost_percent": "0" }
 print-vtable-sizes { "crate_name": "<UNKNOWN_CRATE>", "trait_name": "_::S", "entries": "3", "entries_ignoring_upcasting": "3", "entries_for_upcasting": "0", "upcasting_cost_percent": "0" }
 print-vtable-sizes { "crate_name": "<UNKNOWN_CRATE>", "trait_name": "_::S", "entries": "3", "entries_ignoring_upcasting": "3", "entries_for_upcasting": "0", "upcasting_cost_percent": "0" }
diff --git a/tests/ui/traits/vtable/multiple-markers.rs b/tests/ui/traits/vtable/multiple-markers.rs
new file mode 100644
index 00000000000..1e6e3087027
--- /dev/null
+++ b/tests/ui/traits/vtable/multiple-markers.rs
@@ -0,0 +1,47 @@
+// Regression test for <https://github.com/rust-lang/rust/issues/113840>
+//
+// This test makes sure that multiple marker (method-less) traits can reuse the
+// same pointer for upcasting.
+//
+// build-fail
+#![crate_type = "lib"]
+#![feature(rustc_attrs)]
+
+// Markers
+trait M0 {}
+trait M1 {}
+trait M2 {}
+
+// Just a trait with a method
+trait T {
+    fn method(&self) {}
+}
+
+#[rustc_dump_vtable]
+trait A: M0 + M1 + M2 + T {} //~ error: vtable entries for `<S as A>`:
+
+#[rustc_dump_vtable]
+trait B: M0 + M1 + T + M2 {} //~ error: vtable entries for `<S as B>`:
+
+#[rustc_dump_vtable]
+trait C: M0 + T + M1 + M2 {} //~ error: vtable entries for `<S as C>`:
+
+#[rustc_dump_vtable]
+trait D: T + M0 + M1 + M2 {} //~ error: vtable entries for `<S as D>`:
+
+struct S;
+
+impl M0 for S {}
+impl M1 for S {}
+impl M2 for S {}
+impl T for S {}
+impl A for S {}
+impl B for S {}
+impl C for S {}
+impl D for S {}
+
+pub fn require_vtables() {
+    fn require_vtables(_: &dyn A, _: &dyn B, _: &dyn C, _: &dyn D) {}
+
+    require_vtables(&S, &S, &S, &S)
+}
diff --git a/tests/ui/traits/vtable/multiple-markers.stderr b/tests/ui/traits/vtable/multiple-markers.stderr
new file mode 100644
index 00000000000..4497c703ae8
--- /dev/null
+++ b/tests/ui/traits/vtable/multiple-markers.stderr
@@ -0,0 +1,52 @@
+error: vtable entries for `<S as A>`: [
+           MetadataDropInPlace,
+           MetadataSize,
+           MetadataAlign,
+           Method(<S as T>::method),
+       ]
+  --> $DIR/multiple-markers.rs:21:1
+   |
+LL | trait A: M0 + M1 + M2 + T {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: vtable entries for `<S as B>`: [
+           MetadataDropInPlace,
+           MetadataSize,
+           MetadataAlign,
+           Method(<S as T>::method),
+           TraitVPtr(<S as M2>),
+       ]
+  --> $DIR/multiple-markers.rs:24:1
+   |
+LL | trait B: M0 + M1 + T + M2 {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: vtable entries for `<S as C>`: [
+           MetadataDropInPlace,
+           MetadataSize,
+           MetadataAlign,
+           Method(<S as T>::method),
+           TraitVPtr(<S as M1>),
+           TraitVPtr(<S as M2>),
+       ]
+  --> $DIR/multiple-markers.rs:27:1
+   |
+LL | trait C: M0 + T + M1 + M2 {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: vtable entries for `<S as D>`: [
+           MetadataDropInPlace,
+           MetadataSize,
+           MetadataAlign,
+           Method(<S as T>::method),
+           TraitVPtr(<S as M0>),
+           TraitVPtr(<S as M1>),
+           TraitVPtr(<S as M2>),
+       ]
+  --> $DIR/multiple-markers.rs:30:1
+   |
+LL | trait D: T + M0 + M1 + M2 {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 4 previous errors
+
diff --git a/tests/ui/transmute/transmute-fat-pointers.rs b/tests/ui/transmute/transmute-fat-pointers.rs
index 7c1beffd14e..d373ff5f24a 100644
--- a/tests/ui/transmute/transmute-fat-pointers.rs
+++ b/tests/ui/transmute/transmute-fat-pointers.rs
@@ -30,4 +30,16 @@ fn f<T, U: ?Sized>(x: &T) -> &U {
     unsafe { transmute(x) } //~ ERROR cannot transmute between types of different sizes
 }
 
+fn g<T, U>(x: &T) -> Option<&U> {
+    unsafe { transmute(x) }
+}
+
+fn h<T>(x: &[T]) -> Option<&dyn Send> {
+    unsafe { transmute(x) }
+}
+
+fn i<T>(x: [usize; 1]) -> Option<&'static T> {
+    unsafe { transmute(x) }
+}
+
 fn main() { }
diff --git a/tests/ui/type-alias-impl-trait/issue-53092-2.stderr b/tests/ui/type-alias-impl-trait/issue-53092-2.stderr
index 6148131b491..9d90c6fbc58 100644
--- a/tests/ui/type-alias-impl-trait/issue-53092-2.stderr
+++ b/tests/ui/type-alias-impl-trait/issue-53092-2.stderr
@@ -9,7 +9,7 @@ note: ...which requires type-checking `CONST_BUG`...
    |
 LL | const CONST_BUG: Bug<u8, ()> = unsafe { std::mem::transmute(|_: u8| ()) };
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   = note: ...which requires computing layout of `Bug<u8, ()>`...
+   = note: ...which requires computing layout (naive) of `Bug<u8, ()>`...
    = note: ...which requires normalizing `Bug<u8, ()>`...
    = note: ...which again requires computing type of `Bug::{opaque#0}`, completing the cycle
 note: cycle used when checking item types in top-level module
diff --git a/tests/ui/unpretty/avoid-crash.stderr b/tests/ui/unpretty/avoid-crash.stderr
index 11cd3866fa8..15bcc277e64 100644
--- a/tests/ui/unpretty/avoid-crash.stderr
+++ b/tests/ui/unpretty/avoid-crash.stderr
@@ -1,4 +1,4 @@
-error: pretty-print failed to write `/tmp/` due to $ERROR_MESSAGE
+error: failed to write `/tmp/` due to $ERROR_MESSAGE
 
 error: aborting due to previous error