about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_ast/src/ast.rs27
-rw-r--r--compiler/rustc_ast/src/lib.rs2
-rw-r--r--compiler/rustc_ast/src/mut_visit.rs18
-rw-r--r--compiler/rustc_ast/src/visit.rs25
-rw-r--r--compiler/rustc_ast_lowering/src/asm.rs7
-rw-r--r--compiler/rustc_ast_lowering/src/block.rs95
-rw-r--r--compiler/rustc_ast_lowering/src/expr.rs71
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs41
-rw-r--r--compiler/rustc_ast_passes/src/ast_validation.rs46
-rw-r--r--compiler/rustc_ast_passes/src/feature_gate.rs24
-rw-r--r--compiler/rustc_ast_passes/src/lib.rs2
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state/expr.rs11
-rw-r--r--compiler/rustc_attr/src/builtin.rs115
-rw-r--r--compiler/rustc_attr/src/lib.rs2
-rw-r--r--compiler/rustc_borrowck/src/consumers.rs4
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs95
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs4
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/mod.rs6
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/region_errors.rs25
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/region_name.rs32
-rw-r--r--compiler/rustc_borrowck/src/facts.rs2
-rw-r--r--compiler/rustc_borrowck/src/lib.rs19
-rw-r--r--compiler/rustc_borrowck/src/member_constraints.rs3
-rw-r--r--compiler/rustc_borrowck/src/nll.rs10
-rw-r--r--compiler/rustc_borrowck/src/region_infer/mod.rs3
-rw-r--r--compiler/rustc_borrowck/src/region_infer/opaque_types.rs125
-rw-r--r--compiler/rustc_borrowck/src/type_check/canonical.rs2
-rw-r--r--compiler/rustc_borrowck/src/type_check/constraint_conversion.rs10
-rw-r--r--compiler/rustc_borrowck/src/type_check/free_region_relations.rs23
-rw-r--r--compiler/rustc_borrowck/src/type_check/input_output.rs2
-rw-r--r--compiler/rustc_borrowck/src/type_check/liveness/trace.rs2
-rw-r--r--compiler/rustc_builtin_macros/src/asm.rs6
-rw-r--r--compiler/rustc_builtin_macros/src/assert/context.rs2
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/clone.rs8
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs15
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs20
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs16
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/debug.rs13
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/encodable.rs2
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/generic/mod.rs498
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/generic/ty.rs5
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/hash.rs38
-rw-r--r--compiler/rustc_builtin_macros/src/format.rs256
-rw-r--r--compiler/rustc_builtin_macros/src/lib.rs2
-rw-r--r--compiler/rustc_builtin_macros/src/test_harness.rs2
-rw-r--r--compiler/rustc_codegen_cranelift/.vscode/settings.json7
-rw-r--r--compiler/rustc_codegen_cranelift/Cargo.lock143
-rw-r--r--compiler/rustc_codegen_cranelift/Cargo.toml16
-rw-r--r--compiler/rustc_codegen_cranelift/build_sysroot/Cargo.lock17
-rw-r--r--compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs2
-rw-r--r--compiler/rustc_codegen_cranelift/example/mini_core.rs6
-rw-r--r--compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs42
-rw-r--r--compiler/rustc_codegen_cranelift/example/std_example.rs19
-rw-r--r--compiler/rustc_codegen_cranelift/rust-toolchain2
-rw-r--r--compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh10
-rwxr-xr-xcompiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh12
-rw-r--r--compiler/rustc_codegen_cranelift/src/abi/mod.rs34
-rw-r--r--compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs28
-rw-r--r--compiler/rustc_codegen_cranelift/src/allocator.rs2
-rw-r--r--compiler/rustc_codegen_cranelift/src/archive.rs33
-rw-r--r--compiler/rustc_codegen_cranelift/src/base.rs39
-rw-r--r--compiler/rustc_codegen_cranelift/src/cast.rs15
-rw-r--r--compiler/rustc_codegen_cranelift/src/constant.rs60
-rw-r--r--compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs2
-rw-r--r--compiler/rustc_codegen_cranelift/src/driver/aot.rs2
-rw-r--r--compiler/rustc_codegen_cranelift/src/inline_asm.rs170
-rw-r--r--compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs145
-rw-r--r--compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs645
-rw-r--r--compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs399
-rw-r--r--compiler/rustc_codegen_cranelift/src/lib.rs9
-rw-r--r--compiler/rustc_codegen_cranelift/src/main_shim.rs3
-rw-r--r--compiler/rustc_codegen_cranelift/src/pretty_clif.rs3
-rw-r--r--compiler/rustc_codegen_cranelift/src/unsize.rs6
-rw-r--r--compiler/rustc_codegen_cranelift/src/value_and_place.rs57
-rw-r--r--compiler/rustc_codegen_gcc/src/abi.rs2
-rw-r--r--compiler/rustc_codegen_gcc/src/allocator.rs2
-rw-r--r--compiler/rustc_codegen_gcc/src/archive.rs12
-rw-r--r--compiler/rustc_codegen_gcc/src/base.rs2
-rw-r--r--compiler/rustc_codegen_gcc/src/builder.rs12
-rw-r--r--compiler/rustc_codegen_gcc/src/common.rs5
-rw-r--r--compiler/rustc_codegen_gcc/src/lib.rs8
-rw-r--r--compiler/rustc_codegen_gcc/src/type_.rs13
-rw-r--r--compiler/rustc_codegen_llvm/Cargo.toml2
-rw-r--r--compiler/rustc_codegen_llvm/src/abi.rs18
-rw-r--r--compiler/rustc_codegen_llvm/src/allocator.rs2
-rw-r--r--compiler/rustc_codegen_llvm/src/attributes.rs68
-rw-r--r--compiler/rustc_codegen_llvm/src/back/archive.rs46
-rw-r--r--compiler/rustc_codegen_llvm/src/back/lto.rs24
-rw-r--r--compiler/rustc_codegen_llvm/src/back/write.rs16
-rw-r--r--compiler/rustc_codegen_llvm/src/builder.rs26
-rw-r--r--compiler/rustc_codegen_llvm/src/common.rs9
-rw-r--r--compiler/rustc_codegen_llvm/src/consts.rs20
-rw-r--r--compiler/rustc_codegen_llvm/src/context.rs8
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs2
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs12
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs6
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/mod.rs37
-rw-r--r--compiler/rustc_codegen_llvm/src/declare.rs8
-rw-r--r--compiler/rustc_codegen_llvm/src/lib.rs8
-rw-r--r--compiler/rustc_codegen_llvm/src/llvm/diagnostic.rs13
-rw-r--r--compiler/rustc_codegen_llvm/src/llvm/ffi.rs63
-rw-r--r--compiler/rustc_codegen_llvm/src/llvm/mod.rs14
-rw-r--r--compiler/rustc_codegen_llvm/src/llvm_util.rs51
-rw-r--r--compiler/rustc_codegen_llvm/src/type_.rs30
-rw-r--r--compiler/rustc_codegen_ssa/Cargo.toml3
-rw-r--r--compiler/rustc_codegen_ssa/src/back/archive.rs29
-rw-r--r--compiler/rustc_codegen_ssa/src/back/link.rs80
-rw-r--r--compiler/rustc_codegen_ssa/src/back/linker.rs25
-rw-r--r--compiler/rustc_codegen_ssa/src/back/metadata.rs3
-rw-r--r--compiler/rustc_codegen_ssa/src/back/symbol_export.rs6
-rw-r--r--compiler/rustc_codegen_ssa/src/back/write.rs116
-rw-r--r--compiler/rustc_codegen_ssa/src/base.rs62
-rw-r--r--compiler/rustc_codegen_ssa/src/lib.rs3
-rw-r--r--compiler/rustc_codegen_ssa/src/meth.rs6
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/block.rs13
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs7
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/debuginfo.rs2
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/intrinsic.rs20
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/mod.rs8
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/place.rs12
-rw-r--r--compiler/rustc_codegen_ssa/src/target_features.rs1
-rw-r--r--compiler/rustc_codegen_ssa/src/traits/backend.rs2
-rw-r--r--compiler/rustc_codegen_ssa/src/traits/builder.rs4
-rw-r--r--compiler/rustc_codegen_ssa/src/traits/mod.rs3
-rw-r--r--compiler/rustc_codegen_ssa/src/traits/statics.rs4
-rw-r--r--compiler/rustc_codegen_ssa/src/traits/type_.rs17
-rw-r--r--compiler/rustc_const_eval/src/const_eval/eval_queries.rs24
-rw-r--r--compiler/rustc_const_eval/src/const_eval/machine.rs10
-rw-r--r--compiler/rustc_const_eval/src/const_eval/mod.rs3
-rw-r--r--compiler/rustc_const_eval/src/const_eval/valtrees.rs2
-rw-r--r--compiler/rustc_const_eval/src/interpret/cast.rs62
-rw-r--r--compiler/rustc_const_eval/src/interpret/eval_context.rs102
-rw-r--r--compiler/rustc_const_eval/src/interpret/intern.rs12
-rw-r--r--compiler/rustc_const_eval/src/interpret/intrinsics.rs189
-rw-r--r--compiler/rustc_const_eval/src/interpret/intrinsics/caller_location.rs6
-rw-r--r--compiler/rustc_const_eval/src/interpret/machine.rs128
-rw-r--r--compiler/rustc_const_eval/src/interpret/memory.rs238
-rw-r--r--compiler/rustc_const_eval/src/interpret/mod.rs1
-rw-r--r--compiler/rustc_const_eval/src/interpret/operand.rs398
-rw-r--r--compiler/rustc_const_eval/src/interpret/operator.rs44
-rw-r--r--compiler/rustc_const_eval/src/interpret/place.rs567
-rw-r--r--compiler/rustc_const_eval/src/interpret/projection.rs391
-rw-r--r--compiler/rustc_const_eval/src/interpret/terminator.rs96
-rw-r--r--compiler/rustc_const_eval/src/interpret/traits.rs146
-rw-r--r--compiler/rustc_const_eval/src/interpret/validity.rs163
-rw-r--r--compiler/rustc_const_eval/src/interpret/visitor.rs297
-rw-r--r--compiler/rustc_const_eval/src/lib.rs8
-rw-r--r--compiler/rustc_const_eval/src/might_permit_raw_init.rs40
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/check.rs2
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/ops.rs8
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs6
-rw-r--r--compiler/rustc_const_eval/src/transform/validate.rs2
-rw-r--r--compiler/rustc_data_structures/Cargo.toml2
-rw-r--r--compiler/rustc_data_structures/src/lib.rs2
-rw-r--r--compiler/rustc_data_structures/src/memmap.rs63
-rw-r--r--compiler/rustc_data_structures/src/stable_map.rs100
-rw-r--r--compiler/rustc_data_structures/src/stable_set.rs77
-rw-r--r--compiler/rustc_data_structures/src/sync.rs2
-rw-r--r--compiler/rustc_driver/src/lib.rs31
-rw-r--r--compiler/rustc_error_codes/src/error_codes.rs4
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0118.md10
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0283.md45
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0790.md47
-rw-r--r--compiler/rustc_error_messages/locales/en-US/lint.ftl5
-rw-r--r--compiler/rustc_error_messages/locales/en-US/passes.ftl264
-rw-r--r--compiler/rustc_error_messages/locales/en-US/privacy.ftl9
-rw-r--r--compiler/rustc_error_messages/src/lib.rs5
-rw-r--r--compiler/rustc_errors/src/diagnostic.rs130
-rw-r--r--compiler/rustc_errors/src/diagnostic_builder.rs1
-rw-r--r--compiler/rustc_errors/src/emitter.rs24
-rw-r--r--compiler/rustc_errors/src/lib.rs1
-rw-r--r--compiler/rustc_expand/src/base.rs11
-rw-r--r--compiler/rustc_expand/src/build.rs1
-rw-r--r--compiler/rustc_expand/src/config.rs4
-rw-r--r--compiler/rustc_expand/src/expand.rs2
-rw-r--r--compiler/rustc_expand/src/lib.rs3
-rw-r--r--compiler/rustc_expand/src/mbe/macro_rules.rs4
-rw-r--r--compiler/rustc_expand/src/mbe/quoted.rs2
-rw-r--r--compiler/rustc_expand/src/mbe/transcribe.rs13
-rw-r--r--compiler/rustc_expand/src/module.rs5
-rw-r--r--compiler/rustc_expand/src/proc_macro_server.rs402
-rw-r--r--compiler/rustc_feature/src/accepted.rs2
-rw-r--r--compiler/rustc_feature/src/active.rs16
-rw-r--r--compiler/rustc_feature/src/builtin_attrs.rs12
-rw-r--r--compiler/rustc_feature/src/removed.rs3
-rw-r--r--compiler/rustc_hir/src/arena.rs1
-rw-r--r--compiler/rustc_hir/src/def.rs11
-rw-r--r--compiler/rustc_hir/src/hir.rs74
-rw-r--r--compiler/rustc_hir/src/intravisit.rs15
-rw-r--r--compiler/rustc_hir/src/lang_items.rs6
-rw-r--r--compiler/rustc_hir/src/pat_util.rs2
-rw-r--r--compiler/rustc_hir/src/weak_lang_items.rs6
-rw-r--r--compiler/rustc_hir_pretty/src/lib.rs66
-rw-r--r--compiler/rustc_incremental/src/assert_dep_graph.rs6
-rw-r--r--compiler/rustc_incremental/src/assert_module_sources.rs4
-rw-r--r--compiler/rustc_incremental/src/persist/dirty_clean.rs2
-rw-r--r--compiler/rustc_incremental/src/persist/fs.rs2
-rw-r--r--compiler/rustc_incremental/src/persist/load.rs6
-rw-r--r--compiler/rustc_incremental/src/persist/save.rs6
-rw-r--r--compiler/rustc_incremental/src/persist/work_product.rs2
-rw-r--r--compiler/rustc_index/src/vec.rs8
-rw-r--r--compiler/rustc_infer/src/infer/at.rs1
-rw-r--r--compiler/rustc_infer/src/infer/canonical/query_response.rs7
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/mod.rs697
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs49
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs2
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs31
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs2
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs2
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs2
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs69
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/note.rs20
-rw-r--r--compiler/rustc_infer/src/infer/higher_ranked/mod.rs2
-rw-r--r--compiler/rustc_infer/src/infer/mod.rs168
-rw-r--r--compiler/rustc_infer/src/infer/opaque_types.rs121
-rw-r--r--compiler/rustc_infer/src/infer/outlives/components.rs6
-rw-r--r--compiler/rustc_infer/src/infer/outlives/env.rs10
-rw-r--r--compiler/rustc_infer/src/infer/outlives/obligations.rs4
-rw-r--r--compiler/rustc_infer/src/infer/outlives/verify.rs116
-rw-r--r--compiler/rustc_infer/src/infer/region_constraints/mod.rs5
-rw-r--r--compiler/rustc_infer/src/lib.rs2
-rw-r--r--compiler/rustc_infer/src/traits/error_reporting/mod.rs20
-rw-r--r--compiler/rustc_infer/src/traits/util.rs7
-rw-r--r--compiler/rustc_interface/Cargo.toml1
-rw-r--r--compiler/rustc_interface/src/interface.rs6
-rw-r--r--compiler/rustc_interface/src/passes.rs109
-rw-r--r--compiler/rustc_interface/src/queries.rs2
-rw-r--r--compiler/rustc_interface/src/tests.rs19
-rw-r--r--compiler/rustc_interface/src/util.rs33
-rw-r--r--compiler/rustc_lint/src/builtin.rs7
-rw-r--r--compiler/rustc_lint/src/context.rs16
-rw-r--r--compiler/rustc_lint/src/early.rs5
-rw-r--r--compiler/rustc_lint/src/internal.rs49
-rw-r--r--compiler/rustc_lint/src/late.rs4
-rw-r--r--compiler/rustc_lint/src/lib.rs9
-rw-r--r--compiler/rustc_lint/src/types.rs46
-rw-r--r--compiler/rustc_lint_defs/src/builtin.rs86
-rw-r--r--compiler/rustc_lint_defs/src/lib.rs1
-rw-r--r--compiler/rustc_llvm/build.rs62
-rw-r--r--compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h5
-rw-r--r--compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp58
-rw-r--r--compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp110
-rw-r--r--compiler/rustc_macros/src/diagnostics/diagnostic.rs2
-rw-r--r--compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs192
-rw-r--r--compiler/rustc_macros/src/diagnostics/fluent.rs12
-rw-r--r--compiler/rustc_macros/src/diagnostics/subdiagnostic.rs4
-rw-r--r--compiler/rustc_macros/src/diagnostics/utils.rs8
-rw-r--r--compiler/rustc_macros/src/lib.rs7
-rw-r--r--compiler/rustc_metadata/Cargo.toml1
-rw-r--r--compiler/rustc_metadata/src/creader.rs10
-rw-r--r--compiler/rustc_metadata/src/dependency_format.rs4
-rw-r--r--compiler/rustc_metadata/src/fs.rs137
-rw-r--r--compiler/rustc_metadata/src/lib.rs4
-rw-r--r--compiler/rustc_metadata/src/locator.rs4
-rw-r--r--compiler/rustc_metadata/src/rmeta/decoder.rs52
-rw-r--r--compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs23
-rw-r--r--compiler/rustc_metadata/src/rmeta/encoder.rs130
-rw-r--r--compiler/rustc_metadata/src/rmeta/mod.rs8
-rw-r--r--compiler/rustc_metadata/src/rmeta/table.rs6
-rw-r--r--compiler/rustc_middle/src/arena.rs1
-rw-r--r--compiler/rustc_middle/src/hir/map/mod.rs231
-rw-r--r--compiler/rustc_middle/src/hir/mod.rs1
-rw-r--r--compiler/rustc_middle/src/infer/mod.rs8
-rw-r--r--compiler/rustc_middle/src/lib.rs2
-rw-r--r--compiler/rustc_middle/src/lint.rs2
-rw-r--r--compiler/rustc_middle/src/middle/codegen_fn_attrs.rs8
-rw-r--r--compiler/rustc_middle/src/middle/limits.rs2
-rw-r--r--compiler/rustc_middle/src/middle/mod.rs14
-rw-r--r--compiler/rustc_middle/src/middle/resolve_lifetime.rs1
-rw-r--r--compiler/rustc_middle/src/middle/stability.rs53
-rw-r--r--compiler/rustc_middle/src/mir/generic_graph.rs2
-rw-r--r--compiler/rustc_middle/src/mir/generic_graphviz.rs4
-rw-r--r--compiler/rustc_middle/src/mir/graphviz.rs4
-rw-r--r--compiler/rustc_middle/src/mir/interpret/allocation.rs83
-rw-r--r--compiler/rustc_middle/src/mir/interpret/error.rs30
-rw-r--r--compiler/rustc_middle/src/mir/interpret/mod.rs52
-rw-r--r--compiler/rustc_middle/src/mir/interpret/pointer.rs52
-rw-r--r--compiler/rustc_middle/src/mir/interpret/value.rs77
-rw-r--r--compiler/rustc_middle/src/mir/mod.rs34
-rw-r--r--compiler/rustc_middle/src/mir/mono.rs6
-rw-r--r--compiler/rustc_middle/src/mir/pretty.rs58
-rw-r--r--compiler/rustc_middle/src/mir/query.rs10
-rw-r--r--compiler/rustc_middle/src/mir/switch_sources.rs2
-rw-r--r--compiler/rustc_middle/src/mir/visit.rs10
-rw-r--r--compiler/rustc_middle/src/query/mod.rs20
-rw-r--r--compiler/rustc_middle/src/thir.rs4
-rw-r--r--compiler/rustc_middle/src/thir/abstract_const.rs61
-rw-r--r--compiler/rustc_middle/src/thir/visit.rs4
-rw-r--r--compiler/rustc_middle/src/traits/mod.rs57
-rw-r--r--compiler/rustc_middle/src/traits/structural_impls.rs1
-rw-r--r--compiler/rustc_middle/src/traits/util.rs2
-rw-r--r--compiler/rustc_middle/src/ty/abstract_const.rs194
-rw-r--r--compiler/rustc_middle/src/ty/adt.rs11
-rw-r--r--compiler/rustc_middle/src/ty/assoc.rs10
-rw-r--r--compiler/rustc_middle/src/ty/closure.rs14
-rw-r--r--compiler/rustc_middle/src/ty/codec.rs6
-rw-r--r--compiler/rustc_middle/src/ty/consts.rs2
-rw-r--r--compiler/rustc_middle/src/ty/consts/valtree.rs36
-rw-r--r--compiler/rustc_middle/src/ty/context.rs2
-rw-r--r--compiler/rustc_middle/src/ty/diagnostics.rs18
-rw-r--r--compiler/rustc_middle/src/ty/error.rs8
-rw-r--r--compiler/rustc_middle/src/ty/fold.rs7
-rw-r--r--compiler/rustc_middle/src/ty/generics.rs4
-rw-r--r--compiler/rustc_middle/src/ty/impls_ty.rs6
-rw-r--r--compiler/rustc_middle/src/ty/instance.rs16
-rw-r--r--compiler/rustc_middle/src/ty/layout.rs81
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs72
-rw-r--r--compiler/rustc_middle/src/ty/parameterized.rs2
-rw-r--r--compiler/rustc_middle/src/ty/print/pretty.rs42
-rw-r--r--compiler/rustc_middle/src/ty/query.rs1
-rw-r--r--compiler/rustc_middle/src/ty/structural_impls.rs8
-rw-r--r--compiler/rustc_middle/src/ty/sty.rs11
-rw-r--r--compiler/rustc_middle/src/ty/subst.rs126
-rw-r--r--compiler/rustc_middle/src/ty/trait_def.rs4
-rw-r--r--compiler/rustc_middle/src/ty/util.rs4
-rw-r--r--compiler/rustc_mir_build/src/build/block.rs33
-rw-r--r--compiler/rustc_mir_build/src/build/matches/mod.rs77
-rw-r--r--compiler/rustc_mir_build/src/build/mod.rs7
-rw-r--r--compiler/rustc_mir_build/src/check_unsafety.rs22
-rw-r--r--compiler/rustc_mir_build/src/lib.rs2
-rw-r--r--compiler/rustc_mir_build/src/thir/cx/block.rs3
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/check_match.rs23
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs60
-rw-r--r--compiler/rustc_mir_dataflow/src/framework/engine.rs6
-rw-r--r--compiler/rustc_mir_dataflow/src/impls/mod.rs8
-rw-r--r--compiler/rustc_mir_dataflow/src/storage.rs3
-rw-r--r--compiler/rustc_mir_dataflow/src/un_derefer.rs2
-rw-r--r--compiler/rustc_mir_transform/src/abort_unwinding_calls.rs2
-rw-r--r--compiler/rustc_mir_transform/src/add_retag.rs28
-rw-r--r--compiler/rustc_mir_transform/src/check_packed_ref.rs13
-rw-r--r--compiler/rustc_mir_transform/src/check_unsafety.rs20
-rw-r--r--compiler/rustc_mir_transform/src/const_debuginfo.rs2
-rw-r--r--compiler/rustc_mir_transform/src/const_prop.rs30
-rw-r--r--compiler/rustc_mir_transform/src/const_prop_lint.rs357
-rw-r--r--compiler/rustc_mir_transform/src/coverage/mod.rs4
-rw-r--r--compiler/rustc_mir_transform/src/dest_prop.rs2
-rw-r--r--compiler/rustc_mir_transform/src/early_otherwise_branch.rs2
-rw-r--r--compiler/rustc_mir_transform/src/generator.rs4
-rw-r--r--compiler/rustc_mir_transform/src/inline.rs8
-rw-r--r--compiler/rustc_mir_transform/src/inline/cycle.rs2
-rw-r--r--compiler/rustc_mir_transform/src/lib.rs4
-rw-r--r--compiler/rustc_mir_transform/src/lower_slice_len.rs2
-rw-r--r--compiler/rustc_mir_transform/src/pass_manager.rs4
-rw-r--r--compiler/rustc_mir_transform/src/remove_uninit_drops.rs2
-rw-r--r--compiler/rustc_mir_transform/src/reveal_all.rs2
-rw-r--r--compiler/rustc_mir_transform/src/shim.rs17
-rw-r--r--compiler/rustc_mir_transform/src/simplify.rs86
-rw-r--r--compiler/rustc_mir_transform/src/simplify_try.rs4
-rw-r--r--compiler/rustc_mir_transform/src/uninhabited_enum_branching.rs2
-rw-r--r--compiler/rustc_monomorphize/src/collector.rs15
-rw-r--r--compiler/rustc_monomorphize/src/partitioning/default.rs6
-rw-r--r--compiler/rustc_monomorphize/src/partitioning/merging.rs2
-rw-r--r--compiler/rustc_monomorphize/src/partitioning/mod.rs18
-rw-r--r--compiler/rustc_monomorphize/src/polymorphize.rs2
-rw-r--r--compiler/rustc_parse/src/lib.rs6
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs121
-rw-r--r--compiler/rustc_parse/src/parser/item.rs1
-rw-r--r--compiler/rustc_parse_format/src/lib.rs23
-rw-r--r--compiler/rustc_parse_format/src/tests.rs43
-rw-r--r--compiler/rustc_passes/Cargo.toml1
-rw-r--r--compiler/rustc_passes/src/check_attr.rs1155
-rw-r--r--compiler/rustc_passes/src/check_const.rs2
-rw-r--r--compiler/rustc_passes/src/entry.rs2
-rw-r--r--compiler/rustc_passes/src/errors.rs645
-rw-r--r--compiler/rustc_passes/src/hir_id_validator.rs2
-rw-r--r--compiler/rustc_passes/src/lib.rs3
-rw-r--r--compiler/rustc_passes/src/lib_features.rs18
-rw-r--r--compiler/rustc_passes/src/liveness.rs45
-rw-r--r--compiler/rustc_passes/src/loops.rs8
-rw-r--r--compiler/rustc_passes/src/reachable.rs5
-rw-r--r--compiler/rustc_passes/src/stability.rs221
-rw-r--r--compiler/rustc_passes/src/weak_lang_items.rs2
-rw-r--r--compiler/rustc_privacy/src/errors.rs18
-rw-r--r--compiler/rustc_privacy/src/lib.rs38
-rw-r--r--compiler/rustc_query_impl/src/keys.rs12
-rw-r--r--compiler/rustc_query_impl/src/lib.rs2
-rw-r--r--compiler/rustc_query_impl/src/on_disk_cache.rs3
-rw-r--r--compiler/rustc_query_impl/src/plumbing.rs4
-rw-r--r--compiler/rustc_query_system/src/dep_graph/dep_node.rs4
-rw-r--r--compiler/rustc_query_system/src/dep_graph/graph.rs2
-rw-r--r--compiler/rustc_query_system/src/ich/hcx.rs8
-rw-r--r--compiler/rustc_query_system/src/query/config.rs6
-rw-r--r--compiler/rustc_query_system/src/query/mod.rs2
-rw-r--r--compiler/rustc_query_system/src/query/plumbing.rs16
-rw-r--r--compiler/rustc_resolve/src/build_reduced_graph.rs4
-rw-r--r--compiler/rustc_resolve/src/def_collector.rs2
-rw-r--r--compiler/rustc_resolve/src/diagnostics.rs48
-rw-r--r--compiler/rustc_resolve/src/ident.rs4
-rw-r--r--compiler/rustc_resolve/src/imports.rs16
-rw-r--r--compiler/rustc_resolve/src/late.rs732
-rw-r--r--compiler/rustc_resolve/src/late/diagnostics.rs844
-rw-r--r--compiler/rustc_resolve/src/late/lifetimes.rs774
-rw-r--r--compiler/rustc_resolve/src/lib.rs9
-rw-r--r--compiler/rustc_resolve/src/macros.rs17
-rw-r--r--compiler/rustc_save_analysis/src/dump_visitor.rs14
-rw-r--r--compiler/rustc_serialize/Cargo.toml2
-rw-r--r--compiler/rustc_serialize/src/opaque.rs4
-rw-r--r--compiler/rustc_session/src/code_stats.rs7
-rw-r--r--compiler/rustc_session/src/config.rs104
-rw-r--r--compiler/rustc_session/src/lib.rs2
-rw-r--r--compiler/rustc_session/src/options.rs95
-rw-r--r--compiler/rustc_session/src/session.rs647
-rw-r--r--compiler/rustc_span/src/hygiene.rs10
-rw-r--r--compiler/rustc_span/src/lib.rs5
-rw-r--r--compiler/rustc_span/src/source_map.rs43
-rw-r--r--compiler/rustc_span/src/symbol.rs32
-rw-r--r--compiler/rustc_symbol_mangling/Cargo.toml1
-rw-r--r--compiler/rustc_symbol_mangling/src/legacy.rs4
-rw-r--r--compiler/rustc_symbol_mangling/src/lib.rs9
-rw-r--r--compiler/rustc_symbol_mangling/src/typeid.rs18
-rw-r--r--compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs929
-rw-r--r--compiler/rustc_symbol_mangling/src/v0.rs38
-rw-r--r--compiler/rustc_target/src/abi/mod.rs50
-rw-r--r--compiler/rustc_target/src/asm/aarch64.rs2
-rw-r--r--compiler/rustc_target/src/asm/arm.rs2
-rw-r--r--compiler/rustc_target/src/asm/riscv.rs2
-rw-r--r--compiler/rustc_target/src/asm/x86.rs2
-rw-r--r--compiler/rustc_target/src/spec/aarch64_linux_android.rs1
-rw-r--r--compiler/rustc_target/src/spec/aarch64_nintendo_switch_freestanding.rs26
-rw-r--r--compiler/rustc_target/src/spec/aarch64_nintendo_switch_freestanding_linker_script.ld78
-rw-r--r--compiler/rustc_target/src/spec/aarch64_unknown_none.rs1
-rw-r--r--compiler/rustc_target/src/spec/aarch64_unknown_none_softfloat.rs1
-rw-r--r--compiler/rustc_target/src/spec/apple_base.rs1
-rw-r--r--compiler/rustc_target/src/spec/apple_sdk_base.rs1
-rw-r--r--compiler/rustc_target/src/spec/armebv7r_none_eabi.rs1
-rw-r--r--compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs1
-rw-r--r--compiler/rustc_target/src/spec/armv6k_nintendo_3ds.rs1
-rw-r--r--compiler/rustc_target/src/spec/armv7a_none_eabi.rs1
-rw-r--r--compiler/rustc_target/src/spec/armv7a_none_eabihf.rs1
-rw-r--r--compiler/rustc_target/src/spec/armv7r_none_eabi.rs1
-rw-r--r--compiler/rustc_target/src/spec/armv7r_none_eabihf.rs1
-rw-r--r--compiler/rustc_target/src/spec/avr_gnu_base.rs1
-rw-r--r--compiler/rustc_target/src/spec/bpf_base.rs1
-rw-r--r--compiler/rustc_target/src/spec/dragonfly_base.rs1
-rw-r--r--compiler/rustc_target/src/spec/freebsd_base.rs1
-rw-r--r--compiler/rustc_target/src/spec/fuchsia_base.rs1
-rw-r--r--compiler/rustc_target/src/spec/haiku_base.rs1
-rw-r--r--compiler/rustc_target/src/spec/hermit_base.rs1
-rw-r--r--compiler/rustc_target/src/spec/hexagon_unknown_linux_musl.rs1
-rw-r--r--compiler/rustc_target/src/spec/illumos_base.rs1
-rw-r--r--compiler/rustc_target/src/spec/l4re_base.rs1
-rw-r--r--compiler/rustc_target/src/spec/linux_base.rs1
-rw-r--r--compiler/rustc_target/src/spec/mipsel_sony_psp.rs1
-rw-r--r--compiler/rustc_target/src/spec/mipsel_unknown_none.rs1
-rw-r--r--compiler/rustc_target/src/spec/mod.rs11
-rw-r--r--compiler/rustc_target/src/spec/msp430_none_elf.rs1
-rw-r--r--compiler/rustc_target/src/spec/msvc_base.rs1
-rw-r--r--compiler/rustc_target/src/spec/netbsd_base.rs1
-rw-r--r--compiler/rustc_target/src/spec/nvptx64_nvidia_cuda.rs1
-rw-r--r--compiler/rustc_target/src/spec/openbsd_base.rs1
-rw-r--r--compiler/rustc_target/src/spec/redox_base.rs1
-rw-r--r--compiler/rustc_target/src/spec/riscv32i_unknown_none_elf.rs1
-rw-r--r--compiler/rustc_target/src/spec/riscv32im_unknown_none_elf.rs1
-rw-r--r--compiler/rustc_target/src/spec/riscv32imac_unknown_none_elf.rs1
-rw-r--r--compiler/rustc_target/src/spec/riscv32imac_unknown_xous_elf.rs1
-rw-r--r--compiler/rustc_target/src/spec/riscv32imc_esp_espidf.rs1
-rw-r--r--compiler/rustc_target/src/spec/riscv32imc_unknown_none_elf.rs1
-rw-r--r--compiler/rustc_target/src/spec/riscv64gc_unknown_none_elf.rs1
-rw-r--r--compiler/rustc_target/src/spec/riscv64imac_unknown_none_elf.rs1
-rw-r--r--compiler/rustc_target/src/spec/solaris_base.rs1
-rw-r--r--compiler/rustc_target/src/spec/solid_base.rs1
-rw-r--r--compiler/rustc_target/src/spec/thumb_base.rs1
-rw-r--r--compiler/rustc_target/src/spec/vxworks_base.rs1
-rw-r--r--compiler/rustc_target/src/spec/wasm_base.rs3
-rw-r--r--compiler/rustc_target/src/spec/windows_gnu_base.rs1
-rw-r--r--compiler/rustc_target/src/spec/windows_gnullvm_base.rs1
-rw-r--r--compiler/rustc_target/src/spec/windows_uwp_gnu_base.rs1
-rw-r--r--compiler/rustc_target/src/spec/x86_64_fortanix_unknown_sgx.rs1
-rw-r--r--compiler/rustc_target/src/spec/x86_64_unknown_none.rs1
-rw-r--r--compiler/rustc_trait_selection/src/lib.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/auto_trait.rs4
-rw-r--r--compiler/rustc_trait_selection/src/traits/codegen.rs11
-rw-r--r--compiler/rustc_trait_selection/src/traits/coherence.rs348
-rw-r--r--compiler/rustc_trait_selection/src/traits/const_evaluatable.rs802
-rw-r--r--compiler/rustc_trait_selection/src/traits/engine.rs98
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs235
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs89
-rw-r--r--compiler/rustc_trait_selection/src/traits/fulfill.rs55
-rw-r--r--compiler/rustc_trait_selection/src/traits/mod.rs91
-rw-r--r--compiler/rustc_trait_selection/src/traits/object_safety.rs16
-rw-r--r--compiler/rustc_trait_selection/src/traits/on_unimplemented.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/project.rs7
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/normalize.rs4
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs3
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs18
-rw-r--r--compiler/rustc_trait_selection/src/traits/relationships.rs4
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs43
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/confirmation.rs79
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs10
-rw-r--r--compiler/rustc_trait_selection/src/traits/specialize/mod.rs13
-rw-r--r--compiler/rustc_trait_selection/src/traits/structural_match.rs131
-rw-r--r--compiler/rustc_trait_selection/src/traits/wf.rs161
-rw-r--r--compiler/rustc_traits/src/evaluate_obligation.rs6
-rw-r--r--compiler/rustc_traits/src/implied_outlives_bounds.rs2
-rw-r--r--compiler/rustc_traits/src/type_op.rs15
-rw-r--r--compiler/rustc_ty_utils/Cargo.toml1
-rw-r--r--compiler/rustc_ty_utils/src/consts.rs396
-rw-r--r--compiler/rustc_ty_utils/src/instance.rs5
-rw-r--r--compiler/rustc_ty_utils/src/lib.rs2
-rw-r--r--compiler/rustc_ty_utils/src/representability.rs2
-rw-r--r--compiler/rustc_ty_utils/src/ty.rs2
-rw-r--r--compiler/rustc_type_ir/src/codec.rs2
-rw-r--r--compiler/rustc_type_ir/src/lib.rs5
-rw-r--r--compiler/rustc_typeck/src/astconv/errors.rs4
-rw-r--r--compiler/rustc_typeck/src/astconv/generics.rs2
-rw-r--r--compiler/rustc_typeck/src/astconv/mod.rs36
-rw-r--r--compiler/rustc_typeck/src/check/_match.rs181
-rw-r--r--compiler/rustc_typeck/src/check/callee.rs47
-rw-r--r--compiler/rustc_typeck/src/check/cast.rs6
-rw-r--r--compiler/rustc_typeck/src/check/check.rs232
-rw-r--r--compiler/rustc_typeck/src/check/closure.rs16
-rw-r--r--compiler/rustc_typeck/src/check/coercion.rs12
-rw-r--r--compiler/rustc_typeck/src/check/compare_method.rs219
-rw-r--r--compiler/rustc_typeck/src/check/demand.rs100
-rw-r--r--compiler/rustc_typeck/src/check/expr.rs94
-rw-r--r--compiler/rustc_typeck/src/check/fallback.rs19
-rw-r--r--compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs134
-rw-r--r--compiler/rustc_typeck/src/check/fn_ctxt/checks.rs396
-rw-r--r--compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs151
-rw-r--r--compiler/rustc_typeck/src/check/gather_locals.rs9
-rw-r--r--compiler/rustc_typeck/src/check/generator_interior.rs30
-rw-r--r--compiler/rustc_typeck/src/check/generator_interior/drop_ranges.rs5
-rw-r--r--compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs4
-rw-r--r--compiler/rustc_typeck/src/check/generator_interior/drop_ranges/record_consumed_borrow.rs7
-rw-r--r--compiler/rustc_typeck/src/check/inherited.rs16
-rw-r--r--compiler/rustc_typeck/src/check/intrinsic.rs4
-rw-r--r--compiler/rustc_typeck/src/check/intrinsicck.rs32
-rw-r--r--compiler/rustc_typeck/src/check/method/mod.rs30
-rw-r--r--compiler/rustc_typeck/src/check/method/probe.rs23
-rw-r--r--compiler/rustc_typeck/src/check/method/suggest.rs54
-rw-r--r--compiler/rustc_typeck/src/check/mod.rs53
-rw-r--r--compiler/rustc_typeck/src/check/op.rs109
-rw-r--r--compiler/rustc_typeck/src/check/pat.rs7
-rw-r--r--compiler/rustc_typeck/src/check/region.rs14
-rw-r--r--compiler/rustc_typeck/src/check/regionck.rs2
-rw-r--r--compiler/rustc_typeck/src/check/upvar.rs11
-rw-r--r--compiler/rustc_typeck/src/check/wfcheck.rs661
-rw-r--r--compiler/rustc_typeck/src/check/writeback.rs13
-rw-r--r--compiler/rustc_typeck/src/coherence/builtin.rs8
-rw-r--r--compiler/rustc_typeck/src/coherence/inherent_impls.rs3
-rw-r--r--compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs6
-rw-r--r--compiler/rustc_typeck/src/collect.rs82
-rw-r--r--compiler/rustc_typeck/src/collect/type_of.rs174
-rw-r--r--compiler/rustc_typeck/src/errors.rs12
-rw-r--r--compiler/rustc_typeck/src/expr_use_visitor.rs206
-rw-r--r--compiler/rustc_typeck/src/impl_wf_check.rs15
-rw-r--r--compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs15
-rw-r--r--compiler/rustc_typeck/src/lib.rs3
-rw-r--r--compiler/rustc_typeck/src/outlives/explicit.rs8
-rw-r--r--compiler/rustc_typeck/src/outlives/implicit_infer.rs40
-rw-r--r--compiler/rustc_typeck/src/outlives/mod.rs6
-rw-r--r--compiler/rustc_typeck/src/outlives/utils.rs4
-rw-r--r--compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs6
555 files changed, 15494 insertions, 11686 deletions
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index f705d004422..ac2328a5824 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -1390,7 +1390,7 @@ pub enum ExprKind {
     /// A closure (e.g., `move |a, b, c| a + b + c`).
     ///
     /// The final span is the span of the argument block `|...|`.
-    Closure(CaptureBy, Async, Movability, P<FnDecl>, P<Expr>, Span),
+    Closure(ClosureBinder, CaptureBy, Async, Movability, P<FnDecl>, P<Expr>, Span),
     /// A block (`'label: { ... }`).
     Block(P<Block>, Option<Label>),
     /// An async block (`async move { ... }`).
@@ -1518,6 +1518,31 @@ pub enum Movability {
     Movable,
 }
 
+/// Closure lifetime binder, `for<'a, 'b>` in `for<'a, 'b> |_: &'a (), _: &'b ()|`.
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub enum ClosureBinder {
+    /// The binder is not present, all closure lifetimes are inferred.
+    NotPresent,
+    /// The binder is present.
+    For {
+        /// Span of the whole `for<>` clause
+        ///
+        /// ```text
+        /// for<'a, 'b> |_: &'a (), _: &'b ()| { ... }
+        /// ^^^^^^^^^^^ -- this
+        /// ```
+        span: Span,
+
+        /// Lifetimes in the `for<>` closure
+        ///
+        /// ```text
+        /// for<'a, 'b> |_: &'a (), _: &'b ()| { ... }
+        ///     ^^^^^^ -- this
+        /// ```
+        generic_params: P<[GenericParam]>,
+    },
+}
+
 /// Represents a macro invocation. The `path` indicates which macro
 /// is being invoked, and the `args` are arguments passed to it.
 #[derive(Clone, Encodable, Decodable, Debug)]
diff --git a/compiler/rustc_ast/src/lib.rs b/compiler/rustc_ast/src/lib.rs
index 4b94ec0d6d8..838f77512c5 100644
--- a/compiler/rustc_ast/src/lib.rs
+++ b/compiler/rustc_ast/src/lib.rs
@@ -14,7 +14,7 @@
 #![feature(const_trait_impl)]
 #![feature(if_let_guard)]
 #![feature(label_break_value)]
-#![feature(let_chains)]
+#![cfg_attr(bootstrap, feature(let_chains))]
 #![feature(min_specialization)]
 #![feature(negative_impls)]
 #![feature(slice_internals)]
diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs
index 85bb5296486..d933ea2da9e 100644
--- a/compiler/rustc_ast/src/mut_visit.rs
+++ b/compiler/rustc_ast/src/mut_visit.rs
@@ -125,6 +125,10 @@ pub trait MutVisitor: Sized {
         noop_visit_asyncness(a, self);
     }
 
+    fn visit_closure_binder(&mut self, b: &mut ClosureBinder) {
+        noop_visit_closure_binder(b, self);
+    }
+
     fn visit_block(&mut self, b: &mut P<Block>) {
         noop_visit_block(b, self);
     }
@@ -825,6 +829,17 @@ pub fn visit_constness<T: MutVisitor>(constness: &mut Const, vis: &mut T) {
     }
 }
 
+pub fn noop_visit_closure_binder<T: MutVisitor>(binder: &mut ClosureBinder, vis: &mut T) {
+    match binder {
+        ClosureBinder::NotPresent => {}
+        ClosureBinder::For { span: _, generic_params } => {
+            let mut vec = std::mem::take(generic_params).into_vec();
+            vec.flat_map_in_place(|param| vis.flat_map_generic_param(param));
+            *generic_params = P::from_vec(vec);
+        }
+    }
+}
+
 pub fn noop_visit_asyncness<T: MutVisitor>(asyncness: &mut Async, vis: &mut T) {
     match asyncness {
         Async::Yes { span: _, closure_id, return_impl_trait_id } => {
@@ -1336,7 +1351,8 @@ pub fn noop_visit_expr<T: MutVisitor>(
             vis.visit_expr(expr);
             arms.flat_map_in_place(|arm| vis.flat_map_arm(arm));
         }
-        ExprKind::Closure(_capture_by, asyncness, _movability, decl, body, span) => {
+        ExprKind::Closure(binder, _capture_by, asyncness, _movability, decl, body, span) => {
+            vis.visit_closure_binder(binder);
             vis.visit_asyncness(asyncness);
             vis.visit_fn_decl(decl);
             vis.visit_expr(body);
diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs
index 2ce8590d771..3f830acbf27 100644
--- a/compiler/rustc_ast/src/visit.rs
+++ b/compiler/rustc_ast/src/visit.rs
@@ -56,14 +56,14 @@ pub enum FnKind<'a> {
     Fn(FnCtxt, Ident, &'a FnSig, &'a Visibility, &'a Generics, Option<&'a Block>),
 
     /// E.g., `|x, y| body`.
-    Closure(&'a FnDecl, &'a Expr),
+    Closure(&'a ClosureBinder, &'a FnDecl, &'a Expr),
 }
 
 impl<'a> FnKind<'a> {
     pub fn header(&self) -> Option<&'a FnHeader> {
         match *self {
             FnKind::Fn(_, _, sig, _, _, _) => Some(&sig.header),
-            FnKind::Closure(_, _) => None,
+            FnKind::Closure(_, _, _) => None,
         }
     }
 
@@ -77,7 +77,7 @@ impl<'a> FnKind<'a> {
     pub fn decl(&self) -> &'a FnDecl {
         match self {
             FnKind::Fn(_, _, sig, _, _, _) => &sig.decl,
-            FnKind::Closure(decl, _) => decl,
+            FnKind::Closure(_, decl, _) => decl,
         }
     }
 
@@ -155,6 +155,9 @@ pub trait Visitor<'ast>: Sized {
     fn visit_generics(&mut self, g: &'ast Generics) {
         walk_generics(self, g)
     }
+    fn visit_closure_binder(&mut self, b: &'ast ClosureBinder) {
+        walk_closure_binder(self, b)
+    }
     fn visit_where_predicate(&mut self, p: &'ast WherePredicate) {
         walk_where_predicate(self, p)
     }
@@ -636,6 +639,15 @@ pub fn walk_generics<'a, V: Visitor<'a>>(visitor: &mut V, generics: &'a Generics
     walk_list!(visitor, visit_where_predicate, &generics.where_clause.predicates);
 }
 
+pub fn walk_closure_binder<'a, V: Visitor<'a>>(visitor: &mut V, binder: &'a ClosureBinder) {
+    match binder {
+        ClosureBinder::NotPresent => {}
+        ClosureBinder::For { generic_params, span: _ } => {
+            walk_list!(visitor, visit_generic_param, generic_params)
+        }
+    }
+}
+
 pub fn walk_where_predicate<'a, V: Visitor<'a>>(visitor: &mut V, predicate: &'a WherePredicate) {
     match *predicate {
         WherePredicate::BoundPredicate(WhereBoundPredicate {
@@ -682,7 +694,8 @@ pub fn walk_fn<'a, V: Visitor<'a>>(visitor: &mut V, kind: FnKind<'a>, _span: Spa
             walk_fn_decl(visitor, &sig.decl);
             walk_list!(visitor, visit_block, body);
         }
-        FnKind::Closure(decl, body) => {
+        FnKind::Closure(binder, decl, body) => {
+            visitor.visit_closure_binder(binder);
             walk_fn_decl(visitor, decl);
             visitor.visit_expr(body);
         }
@@ -856,8 +869,8 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
             visitor.visit_expr(subexpression);
             walk_list!(visitor, visit_arm, arms);
         }
-        ExprKind::Closure(_, _, _, ref decl, ref body, _decl_span) => {
-            visitor.visit_fn(FnKind::Closure(decl, body), expression.span, expression.id)
+        ExprKind::Closure(ref binder, _, _, _, ref decl, ref body, _decl_span) => {
+            visitor.visit_fn(FnKind::Closure(binder, decl, body), expression.span, expression.id)
         }
         ExprKind::Block(ref block, ref opt_label) => {
             walk_list!(visitor, visit_label, opt_label);
diff --git a/compiler/rustc_ast_lowering/src/asm.rs b/compiler/rustc_ast_lowering/src/asm.rs
index 0e395d70335..4166b4fc2e5 100644
--- a/compiler/rustc_ast_lowering/src/asm.rs
+++ b/compiler/rustc_ast_lowering/src/asm.rs
@@ -4,8 +4,7 @@ use super::LoweringContext;
 
 use rustc_ast::ptr::P;
 use rustc_ast::*;
-use rustc_data_structures::fx::FxHashMap;
-use rustc_data_structures::stable_set::FxHashSet;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_errors::struct_span_err;
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
@@ -145,7 +144,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                     InlineAsmRegOrRegClass::Reg(s) => {
                         asm::InlineAsmRegOrRegClass::Reg(if let Some(asm_arch) = asm_arch {
                             asm::InlineAsmReg::parse(asm_arch, s).unwrap_or_else(|e| {
-                                let msg = format!("invalid register `{}`: {}", s.as_str(), e);
+                                let msg = format!("invalid register `{}`: {}", s, e);
                                 sess.struct_span_err(*op_sp, &msg).emit();
                                 asm::InlineAsmReg::Err
                             })
@@ -156,7 +155,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                     InlineAsmRegOrRegClass::RegClass(s) => {
                         asm::InlineAsmRegOrRegClass::RegClass(if let Some(asm_arch) = asm_arch {
                             asm::InlineAsmRegClass::parse(asm_arch, s).unwrap_or_else(|e| {
-                                let msg = format!("invalid register class `{}`: {}", s.as_str(), e);
+                                let msg = format!("invalid register class `{}`: {}", s, e);
                                 sess.struct_span_err(*op_sp, &msg).emit();
                                 asm::InlineAsmRegClass::Err
                             })
diff --git a/compiler/rustc_ast_lowering/src/block.rs b/compiler/rustc_ast_lowering/src/block.rs
index 9444fffc331..7cbfe143b4d 100644
--- a/compiler/rustc_ast_lowering/src/block.rs
+++ b/compiler/rustc_ast_lowering/src/block.rs
@@ -1,8 +1,8 @@
 use crate::{ImplTraitContext, ImplTraitPosition, LoweringContext};
-use rustc_ast::{AttrVec, Block, BlockCheckMode, Expr, Local, LocalKind, Stmt, StmtKind};
+use rustc_ast::{Block, BlockCheckMode, Local, LocalKind, Stmt, StmtKind};
 use rustc_hir as hir;
 use rustc_session::parse::feature_err;
-use rustc_span::{sym, DesugaringKind};
+use rustc_span::sym;
 
 use smallvec::SmallVec;
 
@@ -36,21 +36,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             match s.kind {
                 StmtKind::Local(ref local) => {
                     let hir_id = self.lower_node_id(s.id);
-                    match &local.kind {
-                        LocalKind::InitElse(init, els) => {
-                            let e = self.lower_let_else(hir_id, local, init, els, tail);
-                            expr = Some(e);
-                            // remaining statements are in let-else expression
-                            break;
-                        }
-                        _ => {
-                            let local = self.lower_local(local);
-                            self.alias_attrs(hir_id, local.hir_id);
-                            let kind = hir::StmtKind::Local(local);
-                            let span = self.lower_span(s.span);
-                            stmts.push(hir::Stmt { hir_id, kind, span });
-                        }
-                    }
+                    let local = self.lower_local(local);
+                    self.alias_attrs(hir_id, local.hir_id);
+                    let kind = hir::StmtKind::Local(local);
+                    let span = self.lower_span(s.span);
+                    stmts.push(hir::Stmt { hir_id, kind, span });
                 }
                 StmtKind::Item(ref it) => {
                     stmts.extend(self.lower_item_ref(it).into_iter().enumerate().map(
@@ -101,10 +91,24 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         let init = l.kind.init().map(|init| self.lower_expr(init));
         let hir_id = self.lower_node_id(l.id);
         let pat = self.lower_pat(&l.pat);
+        let els = if let LocalKind::InitElse(_, els) = &l.kind {
+            if !self.tcx.features().let_else {
+                feature_err(
+                    &self.tcx.sess.parse_sess,
+                    sym::let_else,
+                    l.span,
+                    "`let...else` statements are unstable",
+                )
+                .emit();
+            }
+            Some(self.lower_block(els, false))
+        } else {
+            None
+        };
         let span = self.lower_span(l.span);
         let source = hir::LocalSource::Normal;
         self.lower_attrs(hir_id, &l.attrs);
-        self.arena.alloc(hir::Local { hir_id, ty, pat, init, span, source })
+        self.arena.alloc(hir::Local { hir_id, ty, pat, init, els, span, source })
     }
 
     fn lower_block_check_mode(&mut self, b: &BlockCheckMode) -> hir::BlockCheckMode {
@@ -115,59 +119,4 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             }
         }
     }
-
-    fn lower_let_else(
-        &mut self,
-        stmt_hir_id: hir::HirId,
-        local: &Local,
-        init: &Expr,
-        els: &Block,
-        tail: &[Stmt],
-    ) -> &'hir hir::Expr<'hir> {
-        let ty = local
-            .ty
-            .as_ref()
-            .map(|t| self.lower_ty(t, ImplTraitContext::Disallowed(ImplTraitPosition::Variable)));
-        let span = self.lower_span(local.span);
-        let span = self.mark_span_with_reason(DesugaringKind::LetElse, span, None);
-        let init = self.lower_expr(init);
-        let local_hir_id = self.lower_node_id(local.id);
-        self.lower_attrs(local_hir_id, &local.attrs);
-        let let_expr = {
-            let lex = self.arena.alloc(hir::Let {
-                hir_id: local_hir_id,
-                pat: self.lower_pat(&local.pat),
-                ty,
-                init,
-                span,
-            });
-            self.arena.alloc(self.expr(span, hir::ExprKind::Let(lex), AttrVec::new()))
-        };
-        let then_expr = {
-            let (stmts, expr) = self.lower_stmts(tail);
-            let block = self.block_all(span, stmts, expr);
-            self.arena.alloc(self.expr_block(block, AttrVec::new()))
-        };
-        let else_expr = {
-            let block = self.lower_block(els, false);
-            self.arena.alloc(self.expr_block(block, AttrVec::new()))
-        };
-        self.alias_attrs(let_expr.hir_id, local_hir_id);
-        self.alias_attrs(else_expr.hir_id, local_hir_id);
-        let if_expr = self.arena.alloc(hir::Expr {
-            hir_id: stmt_hir_id,
-            span,
-            kind: hir::ExprKind::If(let_expr, then_expr, Some(else_expr)),
-        });
-        if !self.tcx.features().let_else {
-            feature_err(
-                &self.tcx.sess.parse_sess,
-                sym::let_else,
-                local.span,
-                "`let...else` statements are unstable",
-            )
-            .emit();
-        }
-        if_expr
-    }
 }
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index 9e02e7ed3b9..983efa48a45 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -155,6 +155,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     self.lower_expr_await(span, expr)
                 }
                 ExprKind::Closure(
+                    ref binder,
                     capture_clause,
                     asyncness,
                     movability,
@@ -164,6 +165,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 ) => {
                     if let Async::Yes { closure_id, .. } = asyncness {
                         self.lower_expr_async_closure(
+                            binder,
                             capture_clause,
                             e.id,
                             closure_id,
@@ -173,6 +175,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                         )
                     } else {
                         self.lower_expr_closure(
+                            binder,
                             capture_clause,
                             e.id,
                             movability,
@@ -605,13 +608,18 @@ impl<'hir> LoweringContext<'_, 'hir> {
         });
 
         // `static |_task_context| -> <ret_ty> { body }`:
-        let generator_kind = hir::ExprKind::Closure {
-            capture_clause,
-            bound_generic_params: &[],
-            fn_decl,
-            body,
-            fn_decl_span: self.lower_span(span),
-            movability: Some(hir::Movability::Static),
+        let generator_kind = {
+            let c = self.arena.alloc(hir::Closure {
+                binder: hir::ClosureBinder::Default,
+                capture_clause,
+                bound_generic_params: &[],
+                fn_decl,
+                body,
+                fn_decl_span: self.lower_span(span),
+                movability: Some(hir::Movability::Static),
+            });
+
+            hir::ExprKind::Closure(c)
         };
         let generator = hir::Expr {
             hir_id: self.lower_node_id(closure_node_id),
@@ -831,6 +839,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
 
     fn lower_expr_closure(
         &mut self,
+        binder: &ClosureBinder,
         capture_clause: CaptureBy,
         closure_id: NodeId,
         movability: Movability,
@@ -838,7 +847,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
         body: &Expr,
         fn_decl_span: Span,
     ) -> hir::ExprKind<'hir> {
-        let (body, generator_option) = self.with_new_scopes(move |this| {
+        let (binder_clause, generic_params) = self.lower_closure_binder(binder);
+
+        let (body_id, generator_option) = self.with_new_scopes(move |this| {
             let prev = this.current_item;
             this.current_item = Some(fn_decl_span);
             let mut generator_kind = None;
@@ -853,18 +864,21 @@ impl<'hir> LoweringContext<'_, 'hir> {
             (body_id, generator_option)
         });
 
-        self.with_lifetime_binder(closure_id, &[], |this, bound_generic_params| {
+        self.with_lifetime_binder(closure_id, generic_params, |this, bound_generic_params| {
             // Lower outside new scope to preserve `is_in_loop_condition`.
             let fn_decl = this.lower_fn_decl(decl, None, FnDeclKind::Closure, None);
 
-            hir::ExprKind::Closure {
+            let c = self.arena.alloc(hir::Closure {
+                binder: binder_clause,
                 capture_clause,
                 bound_generic_params,
                 fn_decl,
-                body,
+                body: body_id,
                 fn_decl_span: this.lower_span(fn_decl_span),
                 movability: generator_option,
-            }
+            });
+
+            hir::ExprKind::Closure(c)
         })
     }
 
@@ -906,8 +920,24 @@ impl<'hir> LoweringContext<'_, 'hir> {
         }
     }
 
+    fn lower_closure_binder<'c>(
+        &mut self,
+        binder: &'c ClosureBinder,
+    ) -> (hir::ClosureBinder, &'c [GenericParam]) {
+        let (binder, params) = match binder {
+            ClosureBinder::NotPresent => (hir::ClosureBinder::Default, &[][..]),
+            &ClosureBinder::For { span, ref generic_params } => {
+                let span = self.lower_span(span);
+                (hir::ClosureBinder::For { span }, &**generic_params)
+            }
+        };
+
+        (binder, params)
+    }
+
     fn lower_expr_async_closure(
         &mut self,
+        binder: &ClosureBinder,
         capture_clause: CaptureBy,
         closure_id: NodeId,
         inner_closure_id: NodeId,
@@ -915,6 +945,15 @@ impl<'hir> LoweringContext<'_, 'hir> {
         body: &Expr,
         fn_decl_span: Span,
     ) -> hir::ExprKind<'hir> {
+        if let &ClosureBinder::For { span, .. } = binder {
+            self.tcx.sess.span_err(
+                span,
+                "`for<...>` binders on `async` closures are not currently supported",
+            );
+        }
+
+        let (binder_clause, generic_params) = self.lower_closure_binder(binder);
+
         let outer_decl =
             FnDecl { inputs: decl.inputs.clone(), output: FnRetTy::Default(fn_decl_span) };
 
@@ -952,20 +991,22 @@ impl<'hir> LoweringContext<'_, 'hir> {
             body_id
         });
 
-        self.with_lifetime_binder(closure_id, &[], |this, bound_generic_params| {
+        self.with_lifetime_binder(closure_id, generic_params, |this, bound_generic_params| {
             // We need to lower the declaration outside the new scope, because we
             // have to conserve the state of being inside a loop condition for the
             // closure argument types.
             let fn_decl = this.lower_fn_decl(&outer_decl, None, FnDeclKind::Closure, None);
 
-            hir::ExprKind::Closure {
+            let c = self.arena.alloc(hir::Closure {
+                binder: binder_clause,
                 capture_clause,
                 bound_generic_params,
                 fn_decl,
                 body,
                 fn_decl_span: this.lower_span(fn_decl_span),
                 movability: None,
-            }
+            });
+            hir::ExprKind::Closure(c)
         })
     }
 
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index fdf60e60914..a1bf0f94964 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -31,7 +31,7 @@
 //! in the HIR, especially for multiple identifiers.
 
 #![feature(box_patterns)]
-#![feature(let_chains)]
+#![cfg_attr(bootstrap, feature(let_chains))]
 #![feature(let_else)]
 #![feature(never_type)]
 #![recursion_limit = "256"]
@@ -427,7 +427,7 @@ pub fn lower_to_hir<'hir>(tcx: TyCtxt<'hir>, (): ()) -> hir::Crate<'hir> {
     sess.time("drop_ast", || std::mem::drop(krate));
 
     // Discard hygiene data, which isn't required after lowering to HIR.
-    if !sess.opts.debugging_opts.keep_hygiene_data {
+    if !sess.opts.unstable_opts.keep_hygiene_data {
         rustc_span::hygiene::clear_syntax_context_map();
     }
 
@@ -699,7 +699,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
     /// Intercept all spans entering HIR.
     /// Mark a span as relative to the current owning item.
     fn lower_span(&self, span: Span) -> Span {
-        if self.tcx.sess.opts.debugging_opts.incremental_relative_spans {
+        if self.tcx.sess.opts.unstable_opts.incremental_relative_spans {
             span.with_parent(Some(self.current_hir_id_owner))
         } else {
             // Do not make spans relative when not using incremental compilation.
@@ -1835,7 +1835,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         debug!(?self.captured_lifetimes);
         let name = match res {
             LifetimeRes::Param { mut param, binder } => {
-                debug_assert_ne!(ident.name, kw::UnderscoreLifetime);
                 let p_name = ParamName::Plain(ident);
                 if let Some(mut captured_lifetimes) = self.captured_lifetimes.take() {
                     if !captured_lifetimes.binders_to_ignore.contains(&binder) {
@@ -1884,29 +1883,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                 }
                 hir::LifetimeName::Param(param, ParamName::Fresh)
             }
-            LifetimeRes::Anonymous { binder, elided } => {
-                let mut l_name = None;
-                if let Some(mut captured_lifetimes) = self.captured_lifetimes.take() {
-                    if !captured_lifetimes.binders_to_ignore.contains(&binder) {
-                        let p_id = self.next_node_id();
-                        let p_def_id = self.create_def(
-                            captured_lifetimes.parent_def_id,
-                            p_id,
-                            DefPathData::LifetimeNs(kw::UnderscoreLifetime),
-                        );
-                        captured_lifetimes
-                            .captures
-                            .insert(p_def_id, (span, p_id, ParamName::Fresh, res));
-                        l_name = Some(hir::LifetimeName::Param(p_def_id, ParamName::Fresh));
-                    }
-                    self.captured_lifetimes = Some(captured_lifetimes);
-                };
-                l_name.unwrap_or(if elided {
-                    hir::LifetimeName::Implicit
-                } else {
-                    hir::LifetimeName::Underscore
-                })
-            }
+            LifetimeRes::Infer => hir::LifetimeName::Infer,
             LifetimeRes::Static => hir::LifetimeName::Static,
             LifetimeRes::Error => hir::LifetimeName::Error,
             res => panic!("Unexpected lifetime resolution {:?} for {:?} at {:?}", res, ident, span),
@@ -2146,7 +2123,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             debug_assert!(!a.is_empty());
             self.attrs.insert(hir_id.local_id, a);
         }
-        let local = hir::Local { hir_id, init, pat, source, span: self.lower_span(span), ty: None };
+        let local = hir::Local {
+            hir_id,
+            init,
+            pat,
+            els: None,
+            source,
+            span: self.lower_span(span),
+            ty: None,
+        };
         self.stmt(span, hir::StmtKind::Local(self.arena.alloc(local)))
     }
 
diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs
index 3942062656f..60560b1c00e 100644
--- a/compiler/rustc_ast_passes/src/ast_validation.rs
+++ b/compiler/rustc_ast_passes/src/ast_validation.rs
@@ -119,33 +119,23 @@ impl<'a> AstValidator<'a> {
 
     /// Emits an error banning the `let` expression provided in the given location.
     fn ban_let_expr(&self, expr: &'a Expr, forbidden_let_reason: ForbiddenLetReason) {
-        let sess = &self.session;
-        if sess.opts.unstable_features.is_nightly_build() {
-            let err = "`let` expressions are not supported here";
-            let mut diag = sess.struct_span_err(expr.span, err);
-            diag.note("only supported directly in conditions of `if` and `while` expressions");
-            match forbidden_let_reason {
-                ForbiddenLetReason::GenericForbidden => {}
-                ForbiddenLetReason::NotSupportedOr(span) => {
-                    diag.span_note(
-                        span,
-                        "`||` operators are not supported in let chain expressions",
-                    );
-                }
-                ForbiddenLetReason::NotSupportedParentheses(span) => {
-                    diag.span_note(
-                        span,
-                        "`let`s wrapped in parentheses are not supported in a context with let \
-                        chains",
-                    );
-                }
+        let err = "`let` expressions are not supported here";
+        let mut diag = self.session.struct_span_err(expr.span, err);
+        diag.note("only supported directly in conditions of `if` and `while` expressions");
+        match forbidden_let_reason {
+            ForbiddenLetReason::GenericForbidden => {}
+            ForbiddenLetReason::NotSupportedOr(span) => {
+                diag.span_note(span, "`||` operators are not supported in let chain expressions");
+            }
+            ForbiddenLetReason::NotSupportedParentheses(span) => {
+                diag.span_note(
+                    span,
+                    "`let`s wrapped in parentheses are not supported in a context with let \
+                    chains",
+                );
             }
-            diag.emit();
-        } else {
-            sess.struct_span_err(expr.span, "expected expression, found statement (`let`)")
-                .note("variable declaration using `let` is a statement")
-                .emit();
         }
+        diag.emit();
     }
 
     fn check_gat_where(
@@ -1282,7 +1272,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                 self.visit_vis(&item.vis);
                 self.visit_ident(item.ident);
                 self.visit_generics(generics);
-                self.with_banned_tilde_const(|this| {
+                self.with_tilde_const_allowed(|this| {
                     walk_list!(this, visit_param_bound, bounds, BoundKind::SuperTraits)
                 });
                 walk_list!(self, visit_assoc_item, items, AssocCtxt::Trait);
@@ -1597,6 +1587,10 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                 .emit();
         }
 
+        if let FnKind::Closure(ClosureBinder::For { generic_params, .. }, ..) = fk {
+            self.check_late_bound_lifetime_defs(generic_params);
+        }
+
         if let FnKind::Fn(
             _,
             _,
diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
index fd2dd6cf6c7..ad9ed798e55 100644
--- a/compiler/rustc_ast_passes/src/feature_gate.rs
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -62,9 +62,9 @@ impl<'a> PostExpansionVisitor<'a> {
         let ast::StrLit { symbol_unescaped, span, .. } = abi;
 
         if let ast::Const::Yes(_) = constness {
-            match symbol_unescaped.as_str() {
+            match symbol_unescaped {
                 // Stable
-                "Rust" | "C" => {}
+                sym::Rust | sym::C => {}
                 abi => gate_feature_post!(
                     &self,
                     const_extern_fn,
@@ -274,10 +274,12 @@ impl<'a> PostExpansionVisitor<'a> {
                 );
             }
             abi => {
-                self.sess.parse_sess.span_diagnostic.delay_span_bug(
-                    span,
-                    &format!("unrecognized ABI not caught in lowering: {}", abi),
-                );
+                if self.sess.opts.pretty.map_or(true, |ppm| ppm.needs_hir()) {
+                    self.sess.parse_sess.span_diagnostic.delay_span_bug(
+                        span,
+                        &format!("unrecognized ABI not caught in lowering: {}", abi),
+                    );
+                }
             }
         }
     }
@@ -402,8 +404,8 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
                     gate_feature_post!(self, rustdoc_internals, attr.span, msg);
                 }
 
-                if nested_meta.has_name(sym::tuple_variadic) {
-                    let msg = "`#[doc(tuple_variadic)]` is meant for internal use only";
+                if nested_meta.has_name(sym::fake_variadic) {
+                    let msg = "`#[doc(fake_variadic)]` is meant for internal use only";
                     gate_feature_post!(self, rustdoc_internals, attr.span, msg);
                 }
             }
@@ -738,12 +740,16 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) {
         "`if let` guards are experimental",
         "you can write `if matches!(<expr>, <pattern>)` instead of `if let <pattern> = <expr>`"
     );
-    gate_all!(let_chains, "`let` expressions in this position are unstable");
     gate_all!(
         async_closure,
         "async closures are unstable",
         "to use an async block, remove the `||`: `async {`"
     );
+    gate_all!(
+        closure_lifetime_binder,
+        "`for<...>` binders for closures are experimental",
+        "consider removing `for<...>`"
+    );
     gate_all!(more_qualified_paths, "usage of qualified paths in this context is experimental");
     gate_all!(generators, "yield syntax is experimental");
     gate_all!(raw_ref_op, "raw address of syntax is experimental");
diff --git a/compiler/rustc_ast_passes/src/lib.rs b/compiler/rustc_ast_passes/src/lib.rs
index 9d52c32885d..2e3ac0c6018 100644
--- a/compiler/rustc_ast_passes/src/lib.rs
+++ b/compiler/rustc_ast_passes/src/lib.rs
@@ -8,7 +8,7 @@
 #![feature(box_patterns)]
 #![feature(if_let_guard)]
 #![feature(iter_is_partitioned)]
-#![feature(let_chains)]
+#![cfg_attr(bootstrap, feature(let_chains))]
 #![feature(let_else)]
 #![recursion_limit = "256"]
 
diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
index 9f44f1b6cc2..ead38caee28 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
@@ -389,6 +389,7 @@ impl<'a> State<'a> {
                 self.bclose(expr.span, empty);
             }
             ast::ExprKind::Closure(
+                ref binder,
                 capture_clause,
                 asyncness,
                 movability,
@@ -396,6 +397,7 @@ impl<'a> State<'a> {
                 ref body,
                 _,
             ) => {
+                self.print_closure_binder(binder);
                 self.print_movability(movability);
                 self.print_asyncness(asyncness);
                 self.print_capture_clause(capture_clause);
@@ -594,6 +596,15 @@ impl<'a> State<'a> {
         self.end(); // Close enclosing cbox.
     }
 
+    fn print_closure_binder(&mut self, binder: &ast::ClosureBinder) {
+        match binder {
+            ast::ClosureBinder::NotPresent => {}
+            ast::ClosureBinder::For { generic_params, .. } => {
+                self.print_formal_generic_params(&generic_params)
+            }
+        }
+    }
+
     fn print_movability(&mut self, movability: ast::Movability) {
         match movability {
             ast::Movability::Static => self.word_space("static"),
diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs
index ce7c0eb72cd..10a9cfb626e 100644
--- a/compiler/rustc_attr/src/builtin.rs
+++ b/compiler/rustc_attr/src/builtin.rs
@@ -135,9 +135,42 @@ impl ConstStability {
 #[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)]
 #[derive(HashStable_Generic)]
 pub enum StabilityLevel {
-    // Reason for the current stability level and the relevant rust-lang issue
-    Unstable { reason: Option<Symbol>, issue: Option<NonZeroU32>, is_soft: bool },
-    Stable { since: Symbol },
+    /// `#[unstable]`
+    Unstable {
+        /// Reason for the current stability level.
+        reason: UnstableReason,
+        /// Relevant `rust-lang/rust` issue.
+        issue: Option<NonZeroU32>,
+        is_soft: bool,
+        /// If part of a feature is stabilized and a new feature is added for the remaining parts,
+        /// then the `implied_by` attribute is used to indicate which now-stable feature previously
+        /// contained a item.
+        ///
+        /// ```pseudo-Rust
+        /// #[unstable(feature = "foo", issue = "...")]
+        /// fn foo() {}
+        /// #[unstable(feature = "foo", issue = "...")]
+        /// fn foobar() {}
+        /// ```
+        ///
+        /// ...becomes...
+        ///
+        /// ```pseudo-Rust
+        /// #[stable(feature = "foo", since = "1.XX.X")]
+        /// fn foo() {}
+        /// #[unstable(feature = "foobar", issue = "...", implied_by = "foo")]
+        /// fn foobar() {}
+        /// ```
+        implied_by: Option<Symbol>,
+    },
+    /// `#[stable]`
+    Stable {
+        /// Rust release which stabilized this feature.
+        since: Symbol,
+        /// Is this item allowed to be referred to on stable, despite being contained in unstable
+        /// modules?
+        allowed_through_unstable_modules: bool,
+    },
 }
 
 impl StabilityLevel {
@@ -149,6 +182,32 @@ impl StabilityLevel {
     }
 }
 
+#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)]
+#[derive(HashStable_Generic)]
+pub enum UnstableReason {
+    None,
+    Default,
+    Some(Symbol),
+}
+
+impl UnstableReason {
+    fn from_opt_reason(reason: Option<Symbol>) -> Self {
+        // UnstableReason::Default constructed manually
+        match reason {
+            Some(r) => Self::Some(r),
+            None => Self::None,
+        }
+    }
+
+    pub fn to_opt_reason(&self) -> Option<Symbol> {
+        match self {
+            Self::None => None,
+            Self::Default => Some(sym::unstable_location_reason_default),
+            Self::Some(r) => Some(*r),
+        }
+    }
+}
+
 /// Collects stability info from all stability attributes in `attrs`.
 /// Returns `None` if no stability attributes are found.
 pub fn find_stability(
@@ -172,6 +231,7 @@ where
     let mut stab: Option<(Stability, Span)> = None;
     let mut const_stab: Option<(ConstStability, Span)> = None;
     let mut promotable = false;
+    let mut allowed_through_unstable_modules = false;
 
     let diagnostic = &sess.parse_sess.span_diagnostic;
 
@@ -182,6 +242,7 @@ where
             sym::unstable,
             sym::stable,
             sym::rustc_promotable,
+            sym::rustc_allowed_through_unstable_modules,
         ]
         .iter()
         .any(|&s| attr.has_name(s))
@@ -193,6 +254,8 @@ where
 
         if attr.has_name(sym::rustc_promotable) {
             promotable = true;
+        } else if attr.has_name(sym::rustc_allowed_through_unstable_modules) {
+            allowed_through_unstable_modules = true;
         }
         // attributes with data
         else if let Some(MetaItem { kind: MetaItemKind::List(ref metas), .. }) = meta {
@@ -239,6 +302,7 @@ where
                     let mut issue = None;
                     let mut issue_num = None;
                     let mut is_soft = false;
+                    let mut implied_by = None;
                     for meta in metas {
                         let Some(mi) = meta.meta_item() else {
                             handle_errors(
@@ -304,6 +368,11 @@ where
                                 }
                                 is_soft = true;
                             }
+                            sym::implied_by => {
+                                if !get(mi, &mut implied_by) {
+                                    continue 'outer;
+                                }
+                            }
                             _ => {
                                 handle_errors(
                                     &sess.parse_sess,
@@ -328,7 +397,12 @@ where
                                 );
                                 continue;
                             }
-                            let level = Unstable { reason, issue: issue_num, is_soft };
+                            let level = Unstable {
+                                reason: UnstableReason::from_opt_reason(reason),
+                                issue: issue_num,
+                                is_soft,
+                                implied_by,
+                            };
                             if sym::unstable == meta_name {
                                 stab = Some((Stability { level, feature }, attr.span));
                             } else {
@@ -387,7 +461,7 @@ where
                                         meta.span(),
                                         AttrError::UnknownMetaItem(
                                             pprust::path_to_string(&mi.path),
-                                            &["since", "note"],
+                                            &["feature", "since"],
                                         ),
                                     );
                                     continue 'outer;
@@ -406,7 +480,7 @@ where
 
                     match (feature, since) {
                         (Some(feature), Some(since)) => {
-                            let level = Stable { since };
+                            let level = Stable { since, allowed_through_unstable_modules: false };
                             if sym::stable == meta_name {
                                 stab = Some((Stability { level, feature }, attr.span));
                             } else {
@@ -447,6 +521,27 @@ where
         }
     }
 
+    if allowed_through_unstable_modules {
+        if let Some((
+            Stability {
+                level: StabilityLevel::Stable { ref mut allowed_through_unstable_modules, .. },
+                ..
+            },
+            _,
+        )) = stab
+        {
+            *allowed_through_unstable_modules = true;
+        } else {
+            struct_span_err!(
+                diagnostic,
+                item_sp,
+                E0789,
+                "`rustc_allowed_through_unstable_modules` attribute must be paired with a `stable` attribute"
+            )
+            .emit();
+        }
+    }
+
     (stab, const_stab)
 }
 
@@ -856,7 +951,6 @@ pub enum ReprAttr {
     ReprSimd,
     ReprTransparent,
     ReprAlign(u32),
-    ReprNoNiche,
 }
 
 #[derive(Eq, PartialEq, Debug, Copy, Clone)]
@@ -904,7 +998,6 @@ pub fn parse_repr_attr(sess: &Session, attr: &Attribute) -> Vec<ReprAttr> {
                     sym::packed => Some(ReprPacked(1)),
                     sym::simd => Some(ReprSimd),
                     sym::transparent => Some(ReprTransparent),
-                    sym::no_niche => Some(ReprNoNiche),
                     sym::align => {
                         let mut err = struct_span_err!(
                             diagnostic,
@@ -943,7 +1036,7 @@ pub fn parse_repr_attr(sess: &Session, attr: &Attribute) -> Vec<ReprAttr> {
                         Ok(literal) => acc.push(ReprPacked(literal)),
                         Err(message) => literal_error = Some(message),
                     };
-                } else if matches!(name, sym::C | sym::simd | sym::transparent | sym::no_niche)
+                } else if matches!(name, sym::C | sym::simd | sym::transparent)
                     || int_type_of_word(name).is_some()
                 {
                     recognised = true;
@@ -1001,7 +1094,7 @@ pub fn parse_repr_attr(sess: &Session, attr: &Attribute) -> Vec<ReprAttr> {
                     } else {
                         if matches!(
                             meta_item.name_or_empty(),
-                            sym::C | sym::simd | sym::transparent | sym::no_niche
+                            sym::C | sym::simd | sym::transparent
                         ) || int_type_of_word(meta_item.name_or_empty()).is_some()
                         {
                             recognised = true;
@@ -1039,7 +1132,7 @@ pub fn parse_repr_attr(sess: &Session, attr: &Attribute) -> Vec<ReprAttr> {
                         .emit();
                     } else if matches!(
                         meta_item.name_or_empty(),
-                        sym::C | sym::simd | sym::transparent | sym::no_niche
+                        sym::C | sym::simd | sym::transparent
                     ) || int_type_of_word(meta_item.name_or_empty()).is_some()
                     {
                         recognised = true;
diff --git a/compiler/rustc_attr/src/lib.rs b/compiler/rustc_attr/src/lib.rs
index c3f9f0cf362..afe1d191d10 100644
--- a/compiler/rustc_attr/src/lib.rs
+++ b/compiler/rustc_attr/src/lib.rs
@@ -4,7 +4,7 @@
 //! The goal is to move the definition of `MetaItem` and things that don't need to be in `syntax`
 //! to this crate.
 
-#![feature(let_chains)]
+#![cfg_attr(bootstrap, feature(let_chains))]
 #![feature(let_else)]
 
 #[macro_use]
diff --git a/compiler/rustc_borrowck/src/consumers.rs b/compiler/rustc_borrowck/src/consumers.rs
index 97daad201d9..efc17a173f4 100644
--- a/compiler/rustc_borrowck/src/consumers.rs
+++ b/compiler/rustc_borrowck/src/consumers.rs
@@ -2,7 +2,7 @@
 
 use rustc_hir::def_id::LocalDefId;
 use rustc_index::vec::IndexVec;
-use rustc_infer::infer::TyCtxtInferExt;
+use rustc_infer::infer::{DefiningAnchor, TyCtxtInferExt};
 use rustc_middle::mir::Body;
 use rustc_middle::ty::{self, TyCtxt};
 
@@ -31,7 +31,7 @@ pub fn get_body_with_borrowck_facts<'tcx>(
     def: ty::WithOptConstParam<LocalDefId>,
 ) -> BodyWithBorrowckFacts<'tcx> {
     let (input_body, promoted) = tcx.mir_promoted(def);
-    tcx.infer_ctxt().with_opaque_type_inference(def.did).enter(|infcx| {
+    tcx.infer_ctxt().with_opaque_type_inference(DefiningAnchor::Bind(def.did)).enter(|infcx| {
         let input_body: &Body<'_> = &input_body.borrow();
         let promoted: &IndexVec<_, _> = &promoted.borrow();
         *super::do_mir_borrowck(&infcx, input_body, promoted, true).1.unwrap()
diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
index 687ff0fb505..1b3c6cac9c4 100644
--- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
@@ -7,7 +7,7 @@ use rustc_errors::{
 };
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
-use rustc_hir::intravisit::{walk_expr, Visitor};
+use rustc_hir::intravisit::{walk_block, walk_expr, Visitor};
 use rustc_hir::{AsyncGeneratorKind, GeneratorKind};
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_infer::traits::ObligationCause;
@@ -23,7 +23,7 @@ use rustc_middle::ty::{
 use rustc_mir_dataflow::move_paths::{InitKind, MoveOutIndex, MovePathIndex};
 use rustc_span::hygiene::DesugaringKind;
 use rustc_span::symbol::sym;
-use rustc_span::{BytePos, Span};
+use rustc_span::{BytePos, Span, Symbol};
 use rustc_trait_selection::infer::InferCtxtExt;
 use rustc_trait_selection::traits::TraitEngineExt as _;
 
@@ -1227,8 +1227,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                     from_closure: false,
                     region_name:
                         RegionName {
-                            source:
-                                RegionNameSource::AnonRegionFromUpvar(upvar_span, ref upvar_name),
+                            source: RegionNameSource::AnonRegionFromUpvar(upvar_span, upvar_name),
                             ..
                         },
                     span,
@@ -1500,7 +1499,70 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             | BorrowExplanation::UsedLaterInLoop(..)
             | BorrowExplanation::UsedLaterWhenDropped { .. } => {
                 // Only give this note and suggestion if it could be relevant.
-                err.note("consider using a `let` binding to create a longer lived value");
+                let sm = self.infcx.tcx.sess.source_map();
+                let mut suggested = false;
+                let msg = "consider using a `let` binding to create a longer lived value";
+
+                /// We check that there's a single level of block nesting to ensure always correct
+                /// suggestions. If we don't, then we only provide a free-form message to avoid
+                /// misleading users in cases like `src/test/ui/nll/borrowed-temporary-error.rs`.
+                /// We could expand the analysis to suggest hoising all of the relevant parts of
+                /// the users' code to make the code compile, but that could be too much.
+                struct NestedStatementVisitor {
+                    span: Span,
+                    current: usize,
+                    found: usize,
+                }
+
+                impl<'tcx> Visitor<'tcx> for NestedStatementVisitor {
+                    fn visit_block(&mut self, block: &hir::Block<'tcx>) {
+                        self.current += 1;
+                        walk_block(self, block);
+                        self.current -= 1;
+                    }
+                    fn visit_expr(&mut self, expr: &hir::Expr<'tcx>) {
+                        if self.span == expr.span {
+                            self.found = self.current;
+                        }
+                        walk_expr(self, expr);
+                    }
+                }
+                let source_info = self.body.source_info(location);
+                if let Some(scope) = self.body.source_scopes.get(source_info.scope)
+                    && let ClearCrossCrate::Set(scope_data) = &scope.local_data
+                    && let Some(node) = self.infcx.tcx.hir().find(scope_data.lint_root)
+                    && let Some(id) = node.body_id()
+                    && let hir::ExprKind::Block(block, _) = self.infcx.tcx.hir().body(id).value.kind
+                {
+                    for stmt in block.stmts {
+                        let mut visitor = NestedStatementVisitor {
+                            span: proper_span,
+                            current: 0,
+                            found: 0,
+                        };
+                        visitor.visit_stmt(stmt);
+                        if visitor.found == 0
+                            && stmt.span.contains(proper_span)
+                            && let Some(p) = sm.span_to_margin(stmt.span)
+                            && let Ok(s) = sm.span_to_snippet(proper_span)
+                        {
+                            let addition = format!("let binding = {};\n{}", s, " ".repeat(p));
+                            err.multipart_suggestion_verbose(
+                                msg,
+                                vec![
+                                    (stmt.span.shrink_to_lo(), addition),
+                                    (proper_span, "binding".to_string()),
+                                ],
+                                Applicability::MaybeIncorrect,
+                            );
+                            suggested = true;
+                            break;
+                        }
+                    }
+                }
+                if !suggested {
+                    err.note(msg);
+                }
             }
             _ => {}
         }
@@ -1598,21 +1660,18 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             let return_ty = tcx.erase_regions(return_ty);
 
             // to avoid panics
-            if let Some(iter_trait) = tcx.get_diagnostic_item(sym::Iterator) {
-                if self
+            if let Some(iter_trait) = tcx.get_diagnostic_item(sym::Iterator)
+                && self
                     .infcx
                     .type_implements_trait(iter_trait, return_ty, ty_params, self.param_env)
                     .must_apply_modulo_regions()
-                {
-                    if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(return_span) {
-                        err.span_suggestion_hidden(
-                            return_span,
-                            "use `.collect()` to allocate the iterator",
-                            format!("{snippet}.collect::<Vec<_>>()"),
-                            Applicability::MaybeIncorrect,
-                        );
-                    }
-                }
+            {
+                err.span_suggestion_hidden(
+                    return_span.shrink_to_hi(),
+                    "use `.collect()` to allocate the iterator",
+                    ".collect::<Vec<_>>()",
+                    Applicability::MaybeIncorrect,
+                );
             }
         }
 
@@ -1702,7 +1761,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         borrow_span: Span,
         name: &Option<String>,
         upvar_span: Span,
-        upvar_name: &str,
+        upvar_name: Symbol,
         escape_span: Span,
     ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
         let tcx = self.infcx.tcx;
diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
index 230ccf51990..72aee0267ac 100644
--- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
@@ -12,7 +12,7 @@ use rustc_middle::mir::{
 };
 use rustc_middle::ty::adjustment::PointerCast;
 use rustc_middle::ty::{self, RegionVid, TyCtxt};
-use rustc_span::symbol::Symbol;
+use rustc_span::symbol::{kw, Symbol};
 use rustc_span::{sym, DesugaringKind, Span};
 
 use crate::region_infer::BlameConstraint;
@@ -282,7 +282,7 @@ impl<'tcx> BorrowExplanation<'tcx> {
     ) {
         if let ConstraintCategory::OpaqueType = category {
             let suggestable_name =
-                if region_name.was_named() { region_name.to_string() } else { "'_".to_string() };
+                if region_name.was_named() { region_name.name } else { kw::UnderscoreLifetime };
 
             let msg = format!(
                 "you can add a bound to the {}to make it last less than `'static` and match `{}`",
diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs
index 9c7671eee38..53c07a3d481 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mod.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs
@@ -891,7 +891,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(local_did);
         let expr = &self.infcx.tcx.hir().expect_expr(hir_id).kind;
         debug!("closure_span: hir_id={:?} expr={:?}", hir_id, expr);
-        if let hir::ExprKind::Closure { body, fn_decl_span, .. } = expr {
+        if let hir::ExprKind::Closure(&hir::Closure { body, fn_decl_span, .. }) = expr {
             for (captured_place, place) in self
                 .infcx
                 .tcx
@@ -904,11 +904,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                         if target_place == place.as_ref() =>
                     {
                         debug!("closure_span: found captured local {:?}", place);
-                        let body = self.infcx.tcx.hir().body(*body);
+                        let body = self.infcx.tcx.hir().body(body);
                         let generator_kind = body.generator_kind();
 
                         return Some((
-                            *fn_decl_span,
+                            fn_decl_span,
                             generator_kind,
                             captured_place.get_capture_kind_span(self.infcx.tcx),
                             captured_place.get_path_span(self.infcx.tcx),
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
index 5d3997289bb..176090c3b7a 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
@@ -1,6 +1,6 @@
 //! Error reporting machinery for lifetime errors.
 
-use rustc_data_structures::stable_set::FxHashSet;
+use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan};
 use rustc_hir::def_id::DefId;
 use rustc_hir::intravisit::Visitor;
@@ -19,8 +19,7 @@ use rustc_middle::ty::subst::InternalSubsts;
 use rustc_middle::ty::Region;
 use rustc_middle::ty::TypeVisitor;
 use rustc_middle::ty::{self, RegionVid, Ty};
-use rustc_span::symbol::sym;
-use rustc_span::symbol::Ident;
+use rustc_span::symbol::{kw, sym, Ident};
 use rustc_span::Span;
 
 use crate::borrowck_errors;
@@ -79,6 +78,8 @@ pub(crate) enum RegionErrorKind<'tcx> {
         span: Span,
         /// The hidden type.
         hidden_ty: Ty<'tcx>,
+        /// The opaque type.
+        key: ty::OpaqueTypeKey<'tcx>,
         /// The unexpected region.
         member_region: ty::Region<'tcx>,
     },
@@ -181,11 +182,11 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                         let generic_ty = type_test.generic_kind.to_ty(self.infcx.tcx);
                         let origin = RelateParamBound(type_test_span, generic_ty, None);
                         self.buffer_error(self.infcx.construct_generic_bound_failure(
+                            self.body.source.def_id().expect_local(),
                             type_test_span,
                             Some(origin),
                             type_test.generic_kind,
                             lower_bound_region,
-                            self.body.source.def_id().as_local(),
                         ));
                     } else {
                         // FIXME. We should handle this case better. It
@@ -206,14 +207,16 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                     }
                 }
 
-                RegionErrorKind::UnexpectedHiddenRegion { span, hidden_ty, member_region } => {
+                RegionErrorKind::UnexpectedHiddenRegion { span, hidden_ty, key, member_region } => {
                     let named_ty = self.regioncx.name_regions(self.infcx.tcx, hidden_ty);
+                    let named_key = self.regioncx.name_regions(self.infcx.tcx, key);
                     let named_region = self.regioncx.name_regions(self.infcx.tcx, member_region);
                     self.buffer_error(unexpected_hidden_region_diagnostic(
                         self.infcx.tcx,
                         span,
                         named_ty,
                         named_region,
+                        named_key,
                     ));
                 }
 
@@ -758,7 +761,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                 return;
             };
 
-            let lifetime = if f.has_name() { fr_name.to_string() } else { "'_".to_string() };
+            let lifetime = if f.has_name() { fr_name.name } else { kw::UnderscoreLifetime };
 
             let arg = match param.param.pat.simple_ident() {
                 Some(simple_ident) => format!("argument `{}`", simple_ident),
@@ -770,7 +773,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                 self.infcx.tcx,
                 diag,
                 fn_returns,
-                lifetime,
+                lifetime.to_string(),
                 Some(arg),
                 captures,
                 Some((param.param_ty_span, param.param_ty.to_string())),
@@ -851,13 +854,11 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
             debug!("trait spans found: {:?}", traits);
             for span in &traits {
                 let mut multi_span: MultiSpan = vec![*span].into();
-                multi_span.push_span_label(
-                    *span,
-                    "this has an implicit `'static` lifetime requirement".to_string(),
-                );
+                multi_span
+                    .push_span_label(*span, "this has an implicit `'static` lifetime requirement");
                 multi_span.push_span_label(
                     ident.span,
-                    "calling this method introduces the `impl`'s 'static` requirement".to_string(),
+                    "calling this method introduces the `impl`'s 'static` requirement",
                 );
                 err.span_note(multi_span, "the used `impl` has a `'static` requirement");
                 err.span_suggestion_verbose(
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs
index e60e11f11df..0662d4d882f 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs
@@ -34,13 +34,13 @@ pub(crate) enum RegionNameSource {
     /// The `'static` region.
     Static,
     /// The free region corresponding to the environment of a closure.
-    SynthesizedFreeEnvRegion(Span, String),
+    SynthesizedFreeEnvRegion(Span, &'static str),
     /// The region corresponding to an argument.
     AnonRegionFromArgument(RegionNameHighlight),
     /// The region corresponding to a closure upvar.
-    AnonRegionFromUpvar(Span, String),
+    AnonRegionFromUpvar(Span, Symbol),
     /// The region corresponding to the return type of a closure.
-    AnonRegionFromOutput(RegionNameHighlight, String),
+    AnonRegionFromOutput(RegionNameHighlight, &'static str),
     /// The region from a type yielded by a generator.
     AnonRegionFromYieldTy(Span, String),
     /// An anonymous region from an async fn.
@@ -110,7 +110,7 @@ impl RegionName {
             }
             RegionNameSource::SynthesizedFreeEnvRegion(span, note) => {
                 diag.span_label(*span, format!("lifetime `{self}` represents this closure's body"));
-                diag.note(note);
+                diag.note(*note);
             }
             RegionNameSource::AnonRegionFromArgument(RegionNameHighlight::CannotMatchHirTy(
                 span,
@@ -325,7 +325,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
                         // Can't have BrEnv in functions, constants or generators.
                         bug!("BrEnv outside of closure.");
                     };
-                    let hir::ExprKind::Closure { fn_decl_span, .. }
+                    let hir::ExprKind::Closure(&hir::Closure { fn_decl_span, .. })
                         = tcx.hir().expect_expr(self.mir_hir_id()).kind
                     else {
                         bug!("Closure is not defined by a closure expr");
@@ -350,10 +350,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
 
                     Some(RegionName {
                         name: region_name,
-                        source: RegionNameSource::SynthesizedFreeEnvRegion(
-                            fn_decl_span,
-                            note.to_string(),
-                        ),
+                        source: RegionNameSource::SynthesizedFreeEnvRegion(fn_decl_span, note),
                     })
                 }
 
@@ -592,8 +589,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
 
             hir::LifetimeName::Param(_, hir::ParamName::Fresh)
             | hir::LifetimeName::ImplicitObjectLifetimeDefault
-            | hir::LifetimeName::Implicit
-            | hir::LifetimeName::Underscore => {
+            | hir::LifetimeName::Infer => {
                 // In this case, the user left off the lifetime; so
                 // they wrote something like:
                 //
@@ -678,7 +674,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
 
         Some(RegionName {
             name: region_name,
-            source: RegionNameSource::AnonRegionFromUpvar(upvar_span, upvar_name.to_string()),
+            source: RegionNameSource::AnonRegionFromUpvar(upvar_span, upvar_name),
         })
     }
 
@@ -701,16 +697,16 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
 
         let (return_span, mir_description, hir_ty) = match hir.get(mir_hir_id) {
             hir::Node::Expr(hir::Expr {
-                kind: hir::ExprKind::Closure { fn_decl, body, fn_decl_span, .. },
+                kind: hir::ExprKind::Closure(&hir::Closure { fn_decl, body, fn_decl_span, .. }),
                 ..
             }) => {
                 let (mut span, mut hir_ty) = match fn_decl.output {
                     hir::FnRetTy::DefaultReturn(_) => {
-                        (tcx.sess.source_map().end_point(*fn_decl_span), None)
+                        (tcx.sess.source_map().end_point(fn_decl_span), None)
                     }
                     hir::FnRetTy::Return(hir_ty) => (fn_decl.output.span(), Some(hir_ty)),
                 };
-                let mir_description = match hir.body(*body).generator_kind {
+                let mir_description = match hir.body(body).generator_kind {
                     Some(hir::GeneratorKind::Async(gen)) => match gen {
                         hir::AsyncGeneratorKind::Block => " of async block",
                         hir::AsyncGeneratorKind::Closure => " of async closure",
@@ -756,7 +752,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
 
         Some(RegionName {
             name: self.synthesize_region_name(),
-            source: RegionNameSource::AnonRegionFromOutput(highlight, mir_description.to_string()),
+            source: RegionNameSource::AnonRegionFromOutput(highlight, mir_description),
         })
     }
 
@@ -841,9 +837,9 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
 
         let yield_span = match tcx.hir().get(self.mir_hir_id()) {
             hir::Node::Expr(hir::Expr {
-                kind: hir::ExprKind::Closure { fn_decl_span, .. },
+                kind: hir::ExprKind::Closure(&hir::Closure { fn_decl_span, .. }),
                 ..
-            }) => (tcx.sess.source_map().end_point(*fn_decl_span)),
+            }) => (tcx.sess.source_map().end_point(fn_decl_span)),
             _ => self.body.span,
         };
 
diff --git a/compiler/rustc_borrowck/src/facts.rs b/compiler/rustc_borrowck/src/facts.rs
index 7f0a637c9d3..22134d5a71c 100644
--- a/compiler/rustc_borrowck/src/facts.rs
+++ b/compiler/rustc_borrowck/src/facts.rs
@@ -40,7 +40,7 @@ pub(crate) trait AllFactsExt {
 impl AllFactsExt for AllFacts {
     /// Return
     fn enabled(tcx: TyCtxt<'_>) -> bool {
-        tcx.sess.opts.debugging_opts.nll_facts || tcx.sess.opts.debugging_opts.polonius
+        tcx.sess.opts.unstable_opts.nll_facts || tcx.sess.opts.unstable_opts.polonius
     }
 
     fn write_to_dir(
diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs
index 5bda6a41ced..3e2d57ae00c 100644
--- a/compiler/rustc_borrowck/src/lib.rs
+++ b/compiler/rustc_borrowck/src/lib.rs
@@ -2,7 +2,7 @@
 
 #![allow(rustc::potential_query_instability)]
 #![feature(box_patterns)]
-#![feature(let_chains)]
+#![cfg_attr(bootstrap, feature(let_chains))]
 #![feature(let_else)]
 #![feature(min_specialization)]
 #![feature(never_type)]
@@ -24,7 +24,7 @@ use rustc_hir as hir;
 use rustc_hir::def_id::LocalDefId;
 use rustc_index::bit_set::ChunkedBitSet;
 use rustc_index::vec::IndexVec;
-use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
+use rustc_infer::infer::{DefiningAnchor, InferCtxt, TyCtxtInferExt};
 use rustc_middle::mir::{
     traversal, Body, ClearCrossCrate, Local, Location, Mutability, Operand, Place, PlaceElem,
     PlaceRef, VarDebugInfoContents,
@@ -130,11 +130,14 @@ fn mir_borrowck<'tcx>(
     debug!("run query mir_borrowck: {}", tcx.def_path_str(def.did.to_def_id()));
     let hir_owner = tcx.hir().local_def_id_to_hir_id(def.did).owner;
 
-    let opt_closure_req = tcx.infer_ctxt().with_opaque_type_inference(hir_owner).enter(|infcx| {
-        let input_body: &Body<'_> = &input_body.borrow();
-        let promoted: &IndexVec<_, _> = &promoted.borrow();
-        do_mir_borrowck(&infcx, input_body, promoted, false).0
-    });
+    let opt_closure_req = tcx
+        .infer_ctxt()
+        .with_opaque_type_inference(DefiningAnchor::Bind(hir_owner))
+        .enter(|infcx| {
+            let input_body: &Body<'_> = &input_body.borrow();
+            let promoted: &IndexVec<_, _> = &promoted.borrow();
+            do_mir_borrowck(&infcx, input_body, promoted, false).0
+        });
     debug!("mir_borrowck done");
 
     tcx.arena.alloc(opt_closure_req)
@@ -231,7 +234,7 @@ fn do_mir_borrowck<'a, 'tcx>(
     let borrow_set =
         Rc::new(BorrowSet::build(tcx, body, locals_are_invalidated_at_exit, &mdpe.move_data));
 
-    let use_polonius = return_body_with_facts || infcx.tcx.sess.opts.debugging_opts.polonius;
+    let use_polonius = return_body_with_facts || infcx.tcx.sess.opts.unstable_opts.polonius;
 
     // Compute non-lexical lifetimes.
     let nll::NllOutput {
diff --git a/compiler/rustc_borrowck/src/member_constraints.rs b/compiler/rustc_borrowck/src/member_constraints.rs
index e91fcf1472d..43253a2aab0 100644
--- a/compiler/rustc_borrowck/src/member_constraints.rs
+++ b/compiler/rustc_borrowck/src/member_constraints.rs
@@ -38,6 +38,8 @@ pub(crate) struct NllMemberConstraint<'tcx> {
     /// The hidden type in which `R0` appears. (Used in error reporting.)
     pub(crate) hidden_ty: Ty<'tcx>,
 
+    pub(crate) key: ty::OpaqueTypeKey<'tcx>,
+
     /// The region `R0`.
     pub(crate) member_region_vid: ty::RegionVid,
 
@@ -90,6 +92,7 @@ impl<'tcx> MemberConstraintSet<'tcx, ty::RegionVid> {
             member_region_vid,
             definition_span: m_c.definition_span,
             hidden_ty: m_c.hidden_ty,
+            key: m_c.key,
             start_index,
             end_index,
         });
diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs
index 3a919e954a4..0961203d76d 100644
--- a/compiler/rustc_borrowck/src/nll.rs
+++ b/compiler/rustc_borrowck/src/nll.rs
@@ -1,7 +1,7 @@
 //! The entry point of the NLL borrow checker.
 
 use rustc_data_structures::vec_map::VecMap;
-use rustc_hir::def_id::DefId;
+use rustc_hir::def_id::LocalDefId;
 use rustc_index::vec::IndexVec;
 use rustc_infer::infer::InferCtxt;
 use rustc_middle::mir::{create_dump_file, dump_enabled, dump_mir, PassWhere};
@@ -44,7 +44,7 @@ pub type PoloniusOutput = Output<RustcFacts>;
 /// closure requirements to propagate, and any generated errors.
 pub(crate) struct NllOutput<'tcx> {
     pub regioncx: RegionInferenceContext<'tcx>,
-    pub opaque_type_values: VecMap<DefId, OpaqueHiddenType<'tcx>>,
+    pub opaque_type_values: VecMap<LocalDefId, OpaqueHiddenType<'tcx>>,
     pub polonius_input: Option<Box<AllFacts>>,
     pub polonius_output: Option<Rc<PoloniusOutput>>,
     pub opt_closure_req: Option<ClosureRegionRequirements<'tcx>>,
@@ -278,9 +278,9 @@ pub(crate) fn compute_regions<'cx, 'tcx>(
 
     // Dump facts if requested.
     let polonius_output = all_facts.as_ref().and_then(|all_facts| {
-        if infcx.tcx.sess.opts.debugging_opts.nll_facts {
+        if infcx.tcx.sess.opts.unstable_opts.nll_facts {
             let def_path = infcx.tcx.def_path(def_id);
-            let dir_path = PathBuf::from(&infcx.tcx.sess.opts.debugging_opts.nll_facts_dir)
+            let dir_path = PathBuf::from(&infcx.tcx.sess.opts.unstable_opts.nll_facts_dir)
                 .join(def_path.to_filename_friendly_no_crate());
             all_facts.write_to_dir(dir_path, location_table).unwrap();
         }
@@ -373,7 +373,7 @@ pub(super) fn dump_annotation<'a, 'tcx>(
     body: &Body<'tcx>,
     regioncx: &RegionInferenceContext<'tcx>,
     closure_region_requirements: &Option<ClosureRegionRequirements<'_>>,
-    opaque_type_values: &VecMap<DefId, OpaqueHiddenType<'tcx>>,
+    opaque_type_values: &VecMap<LocalDefId, OpaqueHiddenType<'tcx>>,
     errors: &mut crate::error::BorrowckErrors<'tcx>,
 ) {
     let tcx = infcx.tcx;
diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs
index 0cf04b369de..2894c6d29ec 100644
--- a/compiler/rustc_borrowck/src/region_infer/mod.rs
+++ b/compiler/rustc_borrowck/src/region_infer/mod.rs
@@ -589,7 +589,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         // In Polonius mode, the errors about missing universal region relations are in the output
         // and need to be emitted or propagated. Otherwise, we need to check whether the
         // constraints were too strong, and if so, emit or propagate those errors.
-        if infcx.tcx.sess.opts.debugging_opts.polonius {
+        if infcx.tcx.sess.opts.unstable_opts.polonius {
             self.check_polonius_subset_errors(
                 body,
                 outlives_requirements.as_mut(),
@@ -1763,6 +1763,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
             errors_buffer.push(RegionErrorKind::UnexpectedHiddenRegion {
                 span: m_c.definition_span,
                 hidden_ty: m_c.hidden_ty,
+                key: m_c.key,
                 member_region,
             });
         }
diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
index 7c1fa28b8df..d6712b6a479 100644
--- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
+++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
@@ -1,10 +1,10 @@
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::vec_map::VecMap;
-use rustc_hir::def_id::DefId;
+use rustc_hir::def_id::LocalDefId;
 use rustc_hir::OpaqueTyOrigin;
 use rustc_infer::infer::error_reporting::unexpected_hidden_region_diagnostic;
-use rustc_infer::infer::InferCtxt;
 use rustc_infer::infer::TyCtxtInferExt as _;
+use rustc_infer::infer::{DefiningAnchor, InferCtxt};
 use rustc_infer::traits::{Obligation, ObligationCause, TraitEngine};
 use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable};
 use rustc_middle::ty::subst::{GenericArg, GenericArgKind, InternalSubsts};
@@ -63,8 +63,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         &self,
         infcx: &InferCtxt<'_, 'tcx>,
         opaque_ty_decls: VecMap<OpaqueTypeKey<'tcx>, (OpaqueHiddenType<'tcx>, OpaqueTyOrigin)>,
-    ) -> VecMap<DefId, OpaqueHiddenType<'tcx>> {
-        let mut result: VecMap<DefId, OpaqueHiddenType<'tcx>> = VecMap::new();
+    ) -> VecMap<LocalDefId, OpaqueHiddenType<'tcx>> {
+        let mut result: VecMap<LocalDefId, OpaqueHiddenType<'tcx>> = VecMap::new();
         for (opaque_type_key, (concrete_type, origin)) in opaque_ty_decls {
             let substs = opaque_type_key.substs;
             debug!(?concrete_type, ?substs);
@@ -235,7 +235,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
         // lifetimes with 'static and remapping only those used in the
         // `impl Trait` return type, resulting in the parameters
         // shifting.
-        let id_substs = InternalSubsts::identity_for_item(self.tcx, def_id);
+        let id_substs = InternalSubsts::identity_for_item(self.tcx, def_id.to_def_id());
         debug!(?id_substs);
         let map: FxHashMap<GenericArg<'tcx>, GenericArg<'tcx>> =
             substs.iter().enumerate().map(|(index, subst)| (subst, id_substs[index])).collect();
@@ -246,7 +246,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
         // after producing an error for each of them.
         let definition_ty = instantiated_ty.ty.fold_with(&mut ReverseMapper::new(
             self.tcx,
-            def_id,
+            opaque_type_key,
             map,
             instantiated_ty.ty,
             instantiated_ty.span,
@@ -268,60 +268,66 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
             // This logic duplicates most of `check_opaque_meets_bounds`.
             // FIXME(oli-obk): Also do region checks here and then consider removing `check_opaque_meets_bounds` entirely.
             let param_env = self.tcx.param_env(def_id);
-            let body_id = self.tcx.local_def_id_to_hir_id(def_id.as_local().unwrap());
-            self.tcx.infer_ctxt().enter(move |infcx| {
-                // Require the hidden type to be well-formed with only the generics of the opaque type.
-                // Defining use functions may have more bounds than the opaque type, which is ok, as long as the
-                // hidden type is well formed even without those bounds.
-                let predicate =
-                    ty::Binder::dummy(ty::PredicateKind::WellFormed(definition_ty.into()))
-                        .to_predicate(infcx.tcx);
-                let mut fulfillment_cx = <dyn TraitEngine<'tcx>>::new(infcx.tcx);
-
-                // Require that the hidden type actually fulfills all the bounds of the opaque type, even without
-                // the bounds that the function supplies.
-                match infcx.register_hidden_type(
-                    OpaqueTypeKey { def_id, substs: id_substs },
-                    ObligationCause::misc(instantiated_ty.span, body_id),
-                    param_env,
-                    definition_ty,
-                    origin,
-                ) {
-                    Ok(infer_ok) => {
-                        for obligation in infer_ok.obligations {
-                            fulfillment_cx.register_predicate_obligation(&infcx, obligation);
+            let body_id = self.tcx.local_def_id_to_hir_id(def_id);
+            // HACK This bubble is required for this tests to pass:
+            // type-alias-impl-trait/issue-67844-nested-opaque.rs
+            self.tcx.infer_ctxt().with_opaque_type_inference(DefiningAnchor::Bubble).enter(
+                move |infcx| {
+                    // Require the hidden type to be well-formed with only the generics of the opaque type.
+                    // Defining use functions may have more bounds than the opaque type, which is ok, as long as the
+                    // hidden type is well formed even without those bounds.
+                    let predicate =
+                        ty::Binder::dummy(ty::PredicateKind::WellFormed(definition_ty.into()))
+                            .to_predicate(infcx.tcx);
+                    let mut fulfillment_cx = <dyn TraitEngine<'tcx>>::new(infcx.tcx);
+
+                    // Require that the hidden type actually fulfills all the bounds of the opaque type, even without
+                    // the bounds that the function supplies.
+                    match infcx.register_hidden_type(
+                        OpaqueTypeKey { def_id, substs: id_substs },
+                        ObligationCause::misc(instantiated_ty.span, body_id),
+                        param_env,
+                        definition_ty,
+                        origin,
+                    ) {
+                        Ok(infer_ok) => {
+                            for obligation in infer_ok.obligations {
+                                fulfillment_cx.register_predicate_obligation(&infcx, obligation);
+                            }
+                        }
+                        Err(err) => {
+                            infcx
+                                .report_mismatched_types(
+                                    &ObligationCause::misc(instantiated_ty.span, body_id),
+                                    self.tcx.mk_opaque(def_id.to_def_id(), id_substs),
+                                    definition_ty,
+                                    err,
+                                )
+                                .emit();
                         }
                     }
-                    Err(err) => {
-                        infcx
-                            .report_mismatched_types(
-                                &ObligationCause::misc(instantiated_ty.span, body_id),
-                                self.tcx.mk_opaque(def_id, id_substs),
-                                definition_ty,
-                                err,
-                            )
-                            .emit();
-                    }
-                }
 
-                fulfillment_cx.register_predicate_obligation(
-                    &infcx,
-                    Obligation::misc(instantiated_ty.span, body_id, param_env, predicate),
-                );
+                    fulfillment_cx.register_predicate_obligation(
+                        &infcx,
+                        Obligation::misc(instantiated_ty.span, body_id, param_env, predicate),
+                    );
 
-                // Check that all obligations are satisfied by the implementation's
-                // version.
-                let errors = fulfillment_cx.select_all_or_error(&infcx);
+                    // Check that all obligations are satisfied by the implementation's
+                    // version.
+                    let errors = fulfillment_cx.select_all_or_error(&infcx);
 
-                let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types();
+                    // This is still required for many(half of the tests in ui/type-alias-impl-trait)
+                    // tests to pass
+                    let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types();
 
-                if errors.is_empty() {
-                    definition_ty
-                } else {
-                    infcx.report_fulfillment_errors(&errors, None, false);
-                    self.tcx.ty_error()
-                }
-            })
+                    if errors.is_empty() {
+                        definition_ty
+                    } else {
+                        infcx.report_fulfillment_errors(&errors, None, false);
+                        self.tcx.ty_error()
+                    }
+                },
+            )
         } else {
             definition_ty
         }
@@ -423,7 +429,7 @@ fn check_opaque_type_parameter_valid(
 struct ReverseMapper<'tcx> {
     tcx: TyCtxt<'tcx>,
 
-    opaque_type_def_id: DefId,
+    key: ty::OpaqueTypeKey<'tcx>,
     map: FxHashMap<GenericArg<'tcx>, GenericArg<'tcx>>,
     map_missing_regions_to_empty: bool,
 
@@ -437,14 +443,14 @@ struct ReverseMapper<'tcx> {
 impl<'tcx> ReverseMapper<'tcx> {
     fn new(
         tcx: TyCtxt<'tcx>,
-        opaque_type_def_id: DefId,
+        key: ty::OpaqueTypeKey<'tcx>,
         map: FxHashMap<GenericArg<'tcx>, GenericArg<'tcx>>,
         hidden_ty: Ty<'tcx>,
         span: Span,
     ) -> Self {
         Self {
             tcx,
-            opaque_type_def_id,
+            key,
             map,
             map_missing_regions_to_empty: false,
             hidden_ty: Some(hidden_ty),
@@ -498,7 +504,7 @@ impl<'tcx> TypeFolder<'tcx> for ReverseMapper<'tcx> {
             }
         }
 
-        let generics = self.tcx().generics_of(self.opaque_type_def_id);
+        let generics = self.tcx().generics_of(self.key.def_id);
         match self.map.get(&r.into()).map(|k| k.unpack()) {
             Some(GenericArgKind::Lifetime(r1)) => r1,
             Some(u) => panic!("region mapped to unexpected kind: {:?}", u),
@@ -507,9 +513,10 @@ impl<'tcx> TypeFolder<'tcx> for ReverseMapper<'tcx> {
                 if let Some(hidden_ty) = self.hidden_ty.take() {
                     unexpected_hidden_region_diagnostic(
                         self.tcx,
-                        self.tcx.def_span(self.opaque_type_def_id),
+                        self.tcx.def_span(self.key.def_id),
                         hidden_ty,
                         r,
+                        self.key,
                     )
                     .emit();
                 }
diff --git a/compiler/rustc_borrowck/src/type_check/canonical.rs b/compiler/rustc_borrowck/src/type_check/canonical.rs
index 55c0bf05b48..6cfe5efb688 100644
--- a/compiler/rustc_borrowck/src/type_check/canonical.rs
+++ b/compiler/rustc_borrowck/src/type_check/canonical.rs
@@ -39,7 +39,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
 
         let TypeOpOutput { output, constraints, error_info } = op.fully_perform(self.infcx)?;
 
-        if let Some(data) = &constraints {
+        if let Some(data) = constraints {
             self.push_region_constraints(locations, category, data);
         }
 
diff --git a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs
index 3f9c0cecccc..16796091830 100644
--- a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs
+++ b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs
@@ -22,6 +22,16 @@ pub(crate) struct ConstraintConversion<'a, 'tcx> {
     infcx: &'a InferCtxt<'a, 'tcx>,
     tcx: TyCtxt<'tcx>,
     universal_regions: &'a UniversalRegions<'tcx>,
+    /// Each RBP `GK: 'a` is assumed to be true. These encode
+    /// relationships like `T: 'a` that are added via implicit bounds
+    /// or the `param_env`.
+    ///
+    /// Each region here is guaranteed to be a key in the `indices`
+    /// map. We use the "original" regions (i.e., the keys from the
+    /// map, and not the values) because the code in
+    /// `process_registered_region_obligations` has some special-cased
+    /// logic expecting to see (e.g.) `ReStatic`, and if we supplied
+    /// our special inference variable there, we would mess that up.
     region_bound_pairs: &'a RegionBoundPairs<'tcx>,
     implicit_region_bound: ty::Region<'tcx>,
     param_env: ty::ParamEnv<'tcx>,
diff --git a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs
index 421ef5be812..cc0318ede54 100644
--- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs
+++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs
@@ -2,6 +2,7 @@ use rustc_data_structures::frozen::Frozen;
 use rustc_data_structures::transitive_relation::TransitiveRelation;
 use rustc_infer::infer::canonical::QueryRegionConstraints;
 use rustc_infer::infer::outlives;
+use rustc_infer::infer::outlives::env::RegionBoundPairs;
 use rustc_infer::infer::region_constraints::GenericKind;
 use rustc_infer::infer::InferCtxt;
 use rustc_middle::mir::ConstraintCategory;
@@ -34,18 +35,6 @@ pub(crate) struct UniversalRegionRelations<'tcx> {
     inverse_outlives: TransitiveRelation<RegionVid>,
 }
 
-/// Each RBP `('a, GK)` indicates that `GK: 'a` can be assumed to
-/// be true. These encode relationships like `T: 'a` that are
-/// added via implicit bounds.
-///
-/// Each region here is guaranteed to be a key in the `indices`
-/// map. We use the "original" regions (i.e., the keys from the
-/// map, and not the values) because the code in
-/// `process_registered_region_obligations` has some special-cased
-/// logic expecting to see (e.g.) `ReStatic`, and if we supplied
-/// our special inference variable there, we would mess that up.
-type RegionBoundPairs<'tcx> = Vec<(ty::Region<'tcx>, GenericKind<'tcx>)>;
-
 /// As part of computing the free region relations, we also have to
 /// normalize the input-output types, which we then need later. So we
 /// return those. This vector consists of first the input types and
@@ -71,7 +60,7 @@ pub(crate) fn create<'tcx>(
         implicit_region_bound,
         constraints,
         universal_regions: universal_regions.clone(),
-        region_bound_pairs: Vec::new(),
+        region_bound_pairs: Default::default(),
         relations: UniversalRegionRelations {
             universal_regions: universal_regions.clone(),
             outlives: Default::default(),
@@ -335,7 +324,7 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
     /// the same time, compute and add any implied bounds that come
     /// from this local.
     #[instrument(level = "debug", skip(self))]
-    fn add_implied_bounds(&mut self, ty: Ty<'tcx>) -> Option<Rc<QueryRegionConstraints<'tcx>>> {
+    fn add_implied_bounds(&mut self, ty: Ty<'tcx>) -> Option<&'tcx QueryRegionConstraints<'tcx>> {
         let TypeOpOutput { output: bounds, constraints, .. } = self
             .param_env
             .and(type_op::implied_outlives_bounds::ImpliedOutlivesBounds { ty })
@@ -371,11 +360,13 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
                 }
 
                 OutlivesBound::RegionSubParam(r_a, param_b) => {
-                    self.region_bound_pairs.push((r_a, GenericKind::Param(param_b)));
+                    self.region_bound_pairs
+                        .insert(ty::OutlivesPredicate(GenericKind::Param(param_b), r_a));
                 }
 
                 OutlivesBound::RegionSubProjection(r_a, projection_b) => {
-                    self.region_bound_pairs.push((r_a, GenericKind::Projection(projection_b)));
+                    self.region_bound_pairs
+                        .insert(ty::OutlivesPredicate(GenericKind::Projection(projection_b), r_a));
                 }
             }
         }
diff --git a/compiler/rustc_borrowck/src/type_check/input_output.rs b/compiler/rustc_borrowck/src/type_check/input_output.rs
index 2a6ca5246da..4431a2e8ec6 100644
--- a/compiler/rustc_borrowck/src/type_check/input_output.rs
+++ b/compiler/rustc_borrowck/src/type_check/input_output.rs
@@ -225,7 +225,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
 
         debug!("{:?} normalized to {:?}", t, norm_ty);
 
-        for data in constraints.into_iter().collect::<Vec<_>>() {
+        for data in constraints {
             ConstraintConversion::new(
                 self.infcx,
                 &self.borrowck_context.universal_regions,
diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs
index 3795378b568..42b577175e4 100644
--- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs
+++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs
@@ -98,7 +98,7 @@ struct LivenessContext<'me, 'typeck, 'flow, 'tcx> {
 
 struct DropData<'tcx> {
     dropck_result: DropckOutlivesResult<'tcx>,
-    region_constraint_data: Option<Rc<QueryRegionConstraints<'tcx>>>,
+    region_constraint_data: Option<&'tcx QueryRegionConstraints<'tcx>>,
 }
 
 struct LivenessResults<'me, 'typeck, 'flow, 'tcx> {
diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs
index 42bddd1b6ed..a2205c3613d 100644
--- a/compiler/rustc_builtin_macros/src/asm.rs
+++ b/compiler/rustc_builtin_macros/src/asm.rs
@@ -534,8 +534,8 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option<ast::Inl
 
     let mut template_strs = Vec::with_capacity(args.templates.len());
 
-    for template_expr in args.templates.into_iter() {
-        if !template.is_empty() {
+    for (i, template_expr) in args.templates.into_iter().enumerate() {
+        if i != 0 {
             template.push(ast::InlineAsmTemplatePiece::String("\n".to_string()));
         }
 
@@ -656,7 +656,7 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option<ast::Inl
                     let span = arg_spans.next().unwrap_or(template_sp);
 
                     let operand_idx = match arg.position {
-                        parse::ArgumentIs(idx) | parse::ArgumentImplicitlyIs(idx) => {
+                        parse::ArgumentIs(idx, _) | parse::ArgumentImplicitlyIs(idx) => {
                             if idx >= args.operands.len()
                                 || named_pos.contains_key(&idx)
                                 || args.reg_args.contains(&idx)
diff --git a/compiler/rustc_builtin_macros/src/assert/context.rs b/compiler/rustc_builtin_macros/src/assert/context.rs
index 9e50d33486c..01152ff7df5 100644
--- a/compiler/rustc_builtin_macros/src/assert/context.rs
+++ b/compiler/rustc_builtin_macros/src/assert/context.rs
@@ -294,7 +294,7 @@ impl<'cx, 'a> Context<'cx, 'a> {
             | ExprKind::Block(_, _)
             | ExprKind::Box(_)
             | ExprKind::Break(_, _)
-            | ExprKind::Closure(_, _, _, _, _, _)
+            | ExprKind::Closure(_, _, _, _, _, _, _)
             | ExprKind::ConstBlock(_)
             | ExprKind::Continue(_)
             | ExprKind::Err
diff --git a/compiler/rustc_builtin_macros/src/deriving/clone.rs b/compiler/rustc_builtin_macros/src/deriving/clone.rs
index 9cd72ed0c67..7755ff779c4 100644
--- a/compiler/rustc_builtin_macros/src/deriving/clone.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/clone.rs
@@ -148,7 +148,7 @@ fn cs_clone_simple(
             ),
         }
     }
-    BlockOrExpr::new_mixed(stmts, cx.expr_deref(trait_span, cx.expr_self(trait_span)))
+    BlockOrExpr::new_mixed(stmts, Some(cx.expr_deref(trait_span, cx.expr_self(trait_span))))
 }
 
 fn cs_clone(
@@ -161,7 +161,7 @@ fn cs_clone(
     let all_fields;
     let fn_path = cx.std_path(&[sym::clone, sym::Clone, sym::clone]);
     let subcall = |cx: &mut ExtCtxt<'_>, field: &FieldInfo| {
-        let args = vec![cx.expr_addr_of(field.span, field.self_expr.clone())];
+        let args = vec![field.self_expr.clone()];
         cx.expr_call_global(field.span, fn_path.clone(), args)
     };
 
@@ -177,9 +177,7 @@ fn cs_clone(
             all_fields = af;
             vdata = &variant.data;
         }
-        EnumNonMatchingCollapsed(..) => {
-            cx.span_bug(trait_span, &format!("non-matching enum variants in `derive({})`", name,))
-        }
+        EnumTag(..) => cx.span_bug(trait_span, &format!("enum tags in `derive({})`", name,)),
         StaticEnum(..) | StaticStruct(..) => {
             cx.span_bug(trait_span, &format!("associated function in `derive({})`", name))
         }
diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs
index 859e995356e..1612be86237 100644
--- a/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs
@@ -63,10 +63,7 @@ pub fn cs_cmp(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> Bl
                 let [other_expr] = &field.other_selflike_exprs[..] else {
                         cx.span_bug(field.span, "not exactly 2 arguments in `derive(Ord)`");
                     };
-                let args = vec![
-                    cx.expr_addr_of(field.span, field.self_expr.clone()),
-                    cx.expr_addr_of(field.span, other_expr.clone()),
-                ];
+                let args = vec![field.self_expr.clone(), other_expr.clone()];
                 cx.expr_call_global(field.span, cmp_path.clone(), args)
             }
             CsFold::Combine(span, expr1, expr2) => {
@@ -76,16 +73,6 @@ pub fn cs_cmp(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> Bl
                 cx.expr_match(span, expr2, vec![eq_arm, neq_arm])
             }
             CsFold::Fieldless => cx.expr_path(equal_path.clone()),
-            CsFold::EnumNonMatching(span, tag_tuple) => {
-                if tag_tuple.len() != 2 {
-                    cx.span_bug(span, "not exactly 2 arguments in `derive(Ord)`")
-                } else {
-                    let lft = cx.expr_addr_of(span, cx.expr_ident(span, tag_tuple[0]));
-                    let rgt = cx.expr_addr_of(span, cx.expr_ident(span, tag_tuple[1]));
-                    let fn_cmp_path = cx.std_path(&[sym::cmp, sym::Ord, sym::cmp]);
-                    cx.expr_call_global(span, fn_cmp_path, vec![lft, rgt])
-                }
-            }
         },
     );
     BlockOrExpr::new_expr(expr)
diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs
index 724c639984c..0141b337726 100644
--- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs
@@ -2,7 +2,8 @@ use crate::deriving::generic::ty::*;
 use crate::deriving::generic::*;
 use crate::deriving::{path_local, path_std};
 
-use rustc_ast::{BinOpKind, MetaItem};
+use rustc_ast::ptr::P;
+use rustc_ast::{BinOpKind, BorrowKind, Expr, ExprKind, MetaItem, Mutability};
 use rustc_expand::base::{Annotatable, ExtCtxt};
 use rustc_span::symbol::sym;
 use rustc_span::Span;
@@ -32,11 +33,24 @@ pub fn expand_deriving_partial_eq(
                     let [other_expr] = &field.other_selflike_exprs[..] else {
                         cx.span_bug(field.span, "not exactly 2 arguments in `derive(PartialEq)`");
                     };
-                    cx.expr_binary(field.span, op, field.self_expr.clone(), other_expr.clone())
+
+                    // We received `&T` arguments. Convert them to `T` by
+                    // stripping `&` or adding `*`. This isn't necessary for
+                    // type checking, but it results in much better error
+                    // messages if something goes wrong.
+                    let convert = |expr: &P<Expr>| {
+                        if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) =
+                            &expr.kind
+                        {
+                            inner.clone()
+                        } else {
+                            cx.expr_deref(field.span, expr.clone())
+                        }
+                    };
+                    cx.expr_binary(field.span, op, convert(&field.self_expr), convert(other_expr))
                 }
                 CsFold::Combine(span, expr1, expr2) => cx.expr_binary(span, combiner, expr1, expr2),
                 CsFold::Fieldless => cx.expr_bool(span, base),
-                CsFold::EnumNonMatching(span, _tag_tuple) => cx.expr_bool(span, !base),
             },
         );
         BlockOrExpr::new_expr(expr)
diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs
index 3f9843922da..2ebb01cc8a0 100644
--- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs
@@ -71,10 +71,7 @@ pub fn cs_partial_cmp(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_
                 let [other_expr] = &field.other_selflike_exprs[..] else {
                         cx.span_bug(field.span, "not exactly 2 arguments in `derive(Ord)`");
                     };
-                let args = vec![
-                    cx.expr_addr_of(field.span, field.self_expr.clone()),
-                    cx.expr_addr_of(field.span, other_expr.clone()),
-                ];
+                let args = vec![field.self_expr.clone(), other_expr.clone()];
                 cx.expr_call_global(field.span, partial_cmp_path.clone(), args)
             }
             CsFold::Combine(span, expr1, expr2) => {
@@ -85,17 +82,6 @@ pub fn cs_partial_cmp(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_
                 cx.expr_match(span, expr2, vec![eq_arm, neq_arm])
             }
             CsFold::Fieldless => cx.expr_some(span, cx.expr_path(equal_path.clone())),
-            CsFold::EnumNonMatching(span, tag_tuple) => {
-                if tag_tuple.len() != 2 {
-                    cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`")
-                } else {
-                    let lft = cx.expr_addr_of(span, cx.expr_ident(span, tag_tuple[0]));
-                    let rgt = cx.expr_addr_of(span, cx.expr_ident(span, tag_tuple[1]));
-                    let fn_partial_cmp_path =
-                        cx.std_path(&[sym::cmp, sym::PartialOrd, sym::partial_cmp]);
-                    cx.expr_call_global(span, fn_partial_cmp_path, vec![lft, rgt])
-                }
-            }
         },
     );
     BlockOrExpr::new_expr(expr)
diff --git a/compiler/rustc_builtin_macros/src/deriving/debug.rs b/compiler/rustc_builtin_macros/src/deriving/debug.rs
index b99198054de..ceef893e862 100644
--- a/compiler/rustc_builtin_macros/src/deriving/debug.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/debug.rs
@@ -45,7 +45,7 @@ fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>
     let (ident, vdata, fields) = match substr.fields {
         Struct(vdata, fields) => (substr.type_ident, *vdata, fields),
         EnumMatching(_, _, v, fields) => (v.ident, &v.data, fields),
-        EnumNonMatchingCollapsed(..) | StaticStruct(..) | StaticEnum(..) => {
+        EnumTag(..) | StaticStruct(..) | StaticEnum(..) => {
             cx.span_bug(span, "nonsensical .fields in `#[derive(Debug)]`")
         }
     };
@@ -95,9 +95,8 @@ fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>
                 );
                 args.push(name);
             }
-            // Use double indirection to make sure this works for unsized types
+            // Use an extra indirection to make sure this works for unsized types.
             let field = cx.expr_addr_of(field.span, field.self_expr.clone());
-            let field = cx.expr_addr_of(field.span, field);
             args.push(field);
         }
         let expr = cx.expr_call_global(span, fn_path_debug, args);
@@ -115,9 +114,9 @@ fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>
                 ));
             }
 
-            // Use double indirection to make sure this works for unsized types
-            let value_ref = cx.expr_addr_of(field.span, field.self_expr.clone());
-            value_exprs.push(cx.expr_addr_of(field.span, value_ref));
+            // Use an extra indirection to make sure this works for unsized types.
+            let field = cx.expr_addr_of(field.span, field.self_expr.clone());
+            value_exprs.push(field);
         }
 
         // `let names: &'static _ = &["field1", "field2"];`
@@ -177,6 +176,6 @@ fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>
             stmts.push(names_let.unwrap());
         }
         stmts.push(values_let);
-        BlockOrExpr::new_mixed(stmts, expr)
+        BlockOrExpr::new_mixed(stmts, Some(expr))
     }
 }
diff --git a/compiler/rustc_builtin_macros/src/deriving/encodable.rs b/compiler/rustc_builtin_macros/src/deriving/encodable.rs
index 49dbe51f762..70167cac68a 100644
--- a/compiler/rustc_builtin_macros/src/deriving/encodable.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/encodable.rs
@@ -287,7 +287,7 @@ fn encodable_substructure(
                 fn_emit_enum_path,
                 vec![encoder, cx.expr_str(trait_span, substr.type_ident.name), blk],
             );
-            BlockOrExpr::new_mixed(vec![me], expr)
+            BlockOrExpr::new_mixed(vec![me], Some(expr))
         }
 
         _ => cx.bug("expected Struct or EnumMatching in derive(Encodable)"),
diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
index 74e18bffc2e..735017aa5a8 100644
--- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
@@ -21,21 +21,14 @@
 //!   `struct T(i32, char)`).
 //! - `EnumMatching`, when `Self` is an enum and all the arguments are the
 //!   same variant of the enum (e.g., `Some(1)`, `Some(3)` and `Some(4)`)
-//! - `EnumNonMatchingCollapsed` when `Self` is an enum and the arguments
-//!   are not the same variant (e.g., `None`, `Some(1)` and `None`).
+//! - `EnumTag` when `Self` is an enum, for comparing the enum tags.
 //! - `StaticEnum` and `StaticStruct` for static methods, where the type
 //!   being derived upon is either an enum or struct respectively. (Any
 //!   argument with type Self is just grouped among the non-self
 //!   arguments.)
 //!
 //! In the first two cases, the values from the corresponding fields in
-//! all the arguments are grouped together. For `EnumNonMatchingCollapsed`
-//! this isn't possible (different variants have different fields), so the
-//! fields are inaccessible. (Previous versions of the deriving infrastructure
-//! had a way to expand into code that could access them, at the cost of
-//! generating exponential amounts of code; see issue #15375). There are no
-//! fields with values in the static cases, so these are treated entirely
-//! differently.
+//! all the arguments are grouped together.
 //!
 //! The non-static cases have `Option<ident>` in several places associated
 //! with field `expr`s. This represents the name of the field it is
@@ -142,21 +135,15 @@
 //!                }])
 //! ```
 //!
-//! For `C0(a)` and `C1 {x}` ,
+//! For the tags,
 //!
 //! ```{.text}
-//! EnumNonMatchingCollapsed(
-//!     &[<ident for self index value>, <ident of __arg_1 index value>])
+//! EnumTag(
+//!     &[<ident of self tag>, <ident of other tag>], <expr to combine with>)
 //! ```
-//!
-//! It is the same for when the arguments are flipped to `C1 {x}` and
-//! `C0(a)`; the only difference is what the values of the identifiers
-//! <ident for self index value> and <ident of __arg_1 index value> will
-//! be in the generated code.
-//!
-//! `EnumNonMatchingCollapsed` deliberately provides far less information
-//! than is generally available for a given pair of variants; see #15375
-//! for discussion.
+//! Note that this setup doesn't allow for the brute-force "match every variant
+//! against every other variant" approach, which is bad because it produces a
+//! quadratic amount of code (see #15375).
 //!
 //! ## Static
 //!
@@ -180,10 +167,9 @@ use std::iter;
 use std::vec;
 
 use rustc_ast::ptr::P;
-use rustc_ast::{self as ast, BinOpKind, EnumDef, Expr, Generics, PatKind};
+use rustc_ast::{self as ast, EnumDef, Expr, Generics, PatKind};
 use rustc_ast::{GenericArg, GenericParamKind, VariantData};
 use rustc_attr as attr;
-use rustc_data_structures::map_in_place::MapInPlace;
 use rustc_expand::base::{Annotatable, ExtCtxt};
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use rustc_span::Span;
@@ -236,6 +222,8 @@ pub struct MethodDef<'a> {
     pub attributes: Vec<ast::Attribute>,
 
     /// Can we combine fieldless variants for enums into a single match arm?
+    /// If true, indicates that the trait operation uses the enum tag in some
+    /// way.
     pub unify_fieldless_variants: bool,
 
     pub combine_substructure: RefCell<CombineSubstructureFunc<'a>>,
@@ -275,19 +263,22 @@ pub enum StaticFields {
 
 /// A summary of the possible sets of fields.
 pub enum SubstructureFields<'a> {
+    /// A non-static method with `Self` is a struct.
     Struct(&'a ast::VariantData, Vec<FieldInfo>),
+
     /// Matching variants of the enum: variant index, variant count, ast::Variant,
     /// fields: the field name is only non-`None` in the case of a struct
     /// variant.
     EnumMatching(usize, usize, &'a ast::Variant, Vec<FieldInfo>),
 
-    /// Non-matching variants of the enum, but with all state hidden from the
-    /// consequent code. The field is a list of `Ident`s bound to the variant
-    /// index values for each of the actual input `Self` arguments.
-    EnumNonMatchingCollapsed(&'a [Ident]),
+    /// The tag of an enum. The first field is a `FieldInfo` for the tags, as
+    /// if they were fields. The second field is the expression to combine the
+    /// tag expression with; it will be `None` if no match is necessary.
+    EnumTag(FieldInfo, Option<P<Expr>>),
 
     /// A static method where `Self` is a struct.
     StaticStruct(&'a ast::VariantData, StaticFields),
+
     /// A static method where `Self` is an enum.
     StaticEnum(&'a ast::EnumDef, Vec<(Ident, Span, StaticFields)>),
 }
@@ -325,8 +316,8 @@ impl BlockOrExpr {
         BlockOrExpr(vec![], Some(expr))
     }
 
-    pub fn new_mixed(stmts: Vec<ast::Stmt>, expr: P<Expr>) -> BlockOrExpr {
-        BlockOrExpr(stmts, Some(expr))
+    pub fn new_mixed(stmts: Vec<ast::Stmt>, expr: Option<P<Expr>>) -> BlockOrExpr {
+        BlockOrExpr(stmts, expr)
     }
 
     // Converts it into a block.
@@ -344,7 +335,14 @@ impl BlockOrExpr {
                 None => cx.expr_block(cx.block(span, vec![])),
                 Some(expr) => expr,
             }
+        } else if self.0.len() == 1
+            && let ast::StmtKind::Expr(expr) = &self.0[0].kind
+            && self.1.is_none()
+        {
+            // There's only a single statement expression. Pull it out.
+            expr.clone()
         } else {
+            // Multiple statements and/or expressions.
             cx.expr_block(self.into_block(cx, span))
         }
     }
@@ -455,7 +453,6 @@ impl<'a> TraitDef<'a> {
                 };
                 let container_id = cx.current_expansion.id.expn_data().parent.expect_local();
                 let always_copy = has_no_type_params && cx.resolver.has_derive_copy(container_id);
-                let use_temporaries = is_packed && always_copy;
 
                 let newitem = match item.kind {
                     ast::ItemKind::Struct(ref struct_def, ref generics) => self.expand_struct_def(
@@ -464,11 +461,11 @@ impl<'a> TraitDef<'a> {
                         item.ident,
                         generics,
                         from_scratch,
-                        use_temporaries,
                         is_packed,
+                        always_copy,
                     ),
                     ast::ItemKind::Enum(ref enum_def, ref generics) => {
-                        // We ignore `use_temporaries` here, because
+                        // We ignore `is_packed`/`always_copy` here, because
                         // `repr(packed)` enums cause an error later on.
                         //
                         // This can only cause further compilation errors
@@ -484,8 +481,8 @@ impl<'a> TraitDef<'a> {
                                 item.ident,
                                 generics,
                                 from_scratch,
-                                use_temporaries,
                                 is_packed,
+                                always_copy,
                             )
                         } else {
                             cx.span_err(mitem.span, "this trait cannot be derived for unions");
@@ -730,16 +727,8 @@ impl<'a> TraitDef<'a> {
 
         let attr = cx.attribute(cx.meta_word(self.span, sym::automatically_derived));
         let opt_trait_ref = Some(trait_ref);
-        let unused_qual = {
-            let word = rustc_ast::attr::mk_nested_word_item(Ident::new(
-                sym::unused_qualifications,
-                self.span,
-            ));
-            let list = rustc_ast::attr::mk_list_item(Ident::new(sym::allow, self.span), vec![word]);
-            cx.attribute(list)
-        };
 
-        let mut a = vec![attr, unused_qual];
+        let mut a = vec![attr];
         a.extend(self.attributes.iter().cloned());
 
         cx.item(
@@ -766,8 +755,8 @@ impl<'a> TraitDef<'a> {
         type_ident: Ident,
         generics: &Generics,
         from_scratch: bool,
-        use_temporaries: bool,
         is_packed: bool,
+        always_copy: bool,
     ) -> P<ast::Item> {
         let field_tys: Vec<P<ast::Ty>> =
             struct_def.fields().iter().map(|field| field.ty.clone()).collect();
@@ -795,8 +784,8 @@ impl<'a> TraitDef<'a> {
                         type_ident,
                         &selflike_args,
                         &nonselflike_args,
-                        use_temporaries,
                         is_packed,
+                        always_copy,
                     )
                 };
 
@@ -937,9 +926,7 @@ impl<'a> MethodDef<'a> {
 
             match ty {
                 // Selflike (`&Self`) arguments only occur in non-static methods.
-                Ref(box Self_, _) if !self.is_static() => {
-                    selflike_args.push(cx.expr_deref(span, arg_expr))
-                }
+                Ref(box Self_, _) if !self.is_static() => selflike_args.push(arg_expr),
                 Self_ => cx.span_bug(span, "`Self` in non-return position"),
                 _ => nonselflike_args.push(arg_expr),
             }
@@ -1008,7 +995,7 @@ impl<'a> MethodDef<'a> {
     /// ```
     /// #[derive(PartialEq)]
     /// # struct Dummy;
-    /// struct A { x: i32, y: i32 }
+    /// struct A { x: u8, y: u8 }
     ///
     /// // equivalent to:
     /// impl PartialEq for A {
@@ -1018,11 +1005,27 @@ impl<'a> MethodDef<'a> {
     /// }
     /// ```
     /// But if the struct is `repr(packed)`, we can't use something like
-    /// `&self.x` on a packed type (as required for e.g. `Debug` and `Hash`)
-    /// because that might cause an unaligned ref. So we use let-destructuring
-    /// instead.
+    /// `&self.x` because that might cause an unaligned ref. So for any trait
+    /// method that takes a reference, if the struct impls `Copy` then we use a
+    /// local block to force a copy:
+    /// ```
+    /// # struct A { x: u8, y: u8 }
+    /// impl PartialEq for A {
+    ///     fn eq(&self, other: &A) -> bool {
+    ///         // Desugars to `{ self.x }.eq(&{ other.y }) && ...`
+    ///         { self.x } == { other.y } && { self.y } == { other.y }
+    ///     }
+    /// }
+    /// impl Hash for A {
+    ///     fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () {
+    ///         ::core::hash::Hash::hash(&{ self.x }, state);
+    ///         ::core::hash::Hash::hash(&{ self.y }, state)
+    ///     }
+    /// }
+    /// ```
+    /// If the struct doesn't impl `Copy`, we use let-destructuring with `ref`:
     /// ```
-    /// # struct A { x: i32, y: i32 }
+    /// # struct A { x: u8, y: u8 }
     /// impl PartialEq for A {
     ///     fn eq(&self, other: &A) -> bool {
     ///         let Self { x: ref __self_0_0, y: ref __self_0_1 } = *self;
@@ -1031,6 +1034,8 @@ impl<'a> MethodDef<'a> {
     ///     }
     /// }
     /// ```
+    /// This latter case only works if the fields match the alignment required
+    /// by the `packed(N)` attribute. (We'll get errors later on if not.)
     fn expand_struct_method_body<'b>(
         &self,
         cx: &mut ExtCtxt<'_>,
@@ -1039,8 +1044,8 @@ impl<'a> MethodDef<'a> {
         type_ident: Ident,
         selflike_args: &[P<Expr>],
         nonselflike_args: &[P<Expr>],
-        use_temporaries: bool,
         is_packed: bool,
+        always_copy: bool,
     ) -> BlockOrExpr {
         let span = trait_.span;
         assert!(selflike_args.len() == 1 || selflike_args.len() == 2);
@@ -1057,29 +1062,31 @@ impl<'a> MethodDef<'a> {
 
         if !is_packed {
             let selflike_fields =
-                trait_.create_struct_field_access_fields(cx, selflike_args, struct_def);
+                trait_.create_struct_field_access_fields(cx, selflike_args, struct_def, false);
+            mk_body(cx, selflike_fields)
+        } else if always_copy {
+            let selflike_fields =
+                trait_.create_struct_field_access_fields(cx, selflike_args, struct_def, true);
             mk_body(cx, selflike_fields)
         } else {
+            // Neither packed nor copy. Need to use ref patterns.
             let prefixes: Vec<_> =
                 (0..selflike_args.len()).map(|i| format!("__self_{}", i)).collect();
+            let addr_of = always_copy;
             let selflike_fields =
-                trait_.create_struct_pattern_fields(cx, struct_def, &prefixes, use_temporaries);
+                trait_.create_struct_pattern_fields(cx, struct_def, &prefixes, addr_of);
             let mut body = mk_body(cx, selflike_fields);
 
             let struct_path = cx.path(span, vec![Ident::new(kw::SelfUpper, type_ident.span)]);
-            let patterns = trait_.create_struct_patterns(
-                cx,
-                struct_path,
-                struct_def,
-                &prefixes,
-                ast::Mutability::Not,
-                use_temporaries,
-            );
+            let use_ref_pat = is_packed && !always_copy;
+            let patterns =
+                trait_.create_struct_patterns(cx, struct_path, struct_def, &prefixes, use_ref_pat);
 
             // Do the let-destructuring.
             let mut stmts: Vec<_> = iter::zip(selflike_args, patterns)
                 .map(|(selflike_arg_expr, pat)| {
-                    cx.stmt_let_pat(span, pat, selflike_arg_expr.clone())
+                    let selflike_arg_expr = cx.expr_deref(span, selflike_arg_expr.clone());
+                    cx.stmt_let_pat(span, pat, selflike_arg_expr)
                 })
                 .collect();
             stmts.extend(std::mem::take(&mut body.0));
@@ -1119,82 +1126,128 @@ impl<'a> MethodDef<'a> {
     /// impl ::core::cmp::PartialEq for A {
     ///     #[inline]
     ///     fn eq(&self, other: &A) -> bool {
-    ///         {
-    ///             let __self_vi = ::core::intrinsics::discriminant_value(&*self);
-    ///             let __arg_1_vi = ::core::intrinsics::discriminant_value(&*other);
-    ///             if true && __self_vi == __arg_1_vi {
-    ///                 match (&*self, &*other) {
-    ///                     (&A::A2(ref __self_0), &A::A2(ref __arg_1_0)) =>
-    ///                         (*__self_0) == (*__arg_1_0),
-    ///                     _ => true,
-    ///                 }
-    ///             } else {
-    ///                 false // catch-all handler
+    ///         let __self_tag = ::core::intrinsics::discriminant_value(self);
+    ///         let __arg1_tag = ::core::intrinsics::discriminant_value(other);
+    ///         __self_tag == __arg1_tag &&
+    ///             match (self, other) {
+    ///                 (A::A2(__self_0), A::A2(__arg1_0)) =>
+    ///                     *__self_0 == *__arg1_0,
+    ///                 _ => true,
     ///             }
-    ///         }
     ///     }
     /// }
     /// ```
-    /// Creates a match for a tuple of all `selflike_args`, where either all
-    /// variants match, or it falls into a catch-all for when one variant
-    /// does not match.
-    ///
-    /// There are N + 1 cases because is a case for each of the N
-    /// variants where all of the variants match, and one catch-all for
-    /// when one does not match.
-    ///
-    /// As an optimization we generate code which checks whether all variants
-    /// match first which makes llvm see that C-like enums can be compiled into
-    /// a simple equality check (for PartialEq).
-    ///
-    /// The catch-all handler is provided access the variant index values
-    /// for each of the selflike_args, carried in precomputed variables.
+    /// Creates a tag check combined with a match for a tuple of all
+    /// `selflike_args`, with an arm for each variant with fields, possibly an
+    /// arm for each fieldless variant (if `!unify_fieldless_variants` is not
+    /// true), and possibly a default arm.
     fn expand_enum_method_body<'b>(
         &self,
         cx: &mut ExtCtxt<'_>,
         trait_: &TraitDef<'b>,
         enum_def: &'b EnumDef,
         type_ident: Ident,
-        mut selflike_args: Vec<P<Expr>>,
+        selflike_args: Vec<P<Expr>>,
         nonselflike_args: &[P<Expr>],
     ) -> BlockOrExpr {
         let span = trait_.span;
         let variants = &enum_def.variants;
 
+        // Traits that unify fieldless variants always use the tag(s).
+        let uses_tags = self.unify_fieldless_variants;
+
+        // There is no sensible code to be generated for *any* deriving on a
+        // zero-variant enum. So we just generate a failing expression.
+        if variants.is_empty() {
+            return BlockOrExpr(vec![], Some(deriving::call_unreachable(cx, span)));
+        }
+
         let prefixes = iter::once("__self".to_string())
             .chain(
                 selflike_args
                     .iter()
                     .enumerate()
                     .skip(1)
-                    .map(|(arg_count, _selflike_arg)| format!("__arg_{}", arg_count)),
+                    .map(|(arg_count, _selflike_arg)| format!("__arg{}", arg_count)),
             )
             .collect::<Vec<String>>();
 
-        // The `vi_idents` will be bound, solely in the catch-all, to
-        // a series of let statements mapping each selflike_arg to an int
-        // value corresponding to its discriminant.
-        let vi_idents = prefixes
-            .iter()
-            .map(|name| {
-                let vi_suffix = format!("{}_vi", name);
-                Ident::from_str_and_span(&vi_suffix, span)
-            })
-            .collect::<Vec<Ident>>();
+        // Build a series of let statements mapping each selflike_arg
+        // to its discriminant value.
+        //
+        // e.g. for `PartialEq::eq` builds two statements:
+        // ```
+        // let __self_tag = ::core::intrinsics::discriminant_value(self);
+        // let __arg1_tag = ::core::intrinsics::discriminant_value(other);
+        // ```
+        let get_tag_pieces = |cx: &ExtCtxt<'_>| {
+            let tag_idents: Vec<_> = prefixes
+                .iter()
+                .map(|name| Ident::from_str_and_span(&format!("{}_tag", name), span))
+                .collect();
 
-        // Builds, via callback to call_substructure_method, the
-        // delegated expression that handles the catch-all case,
-        // using `__variants_tuple` to drive logic if necessary.
-        let catch_all_substructure = EnumNonMatchingCollapsed(&vi_idents);
+            let mut tag_exprs: Vec<_> = tag_idents
+                .iter()
+                .map(|&ident| cx.expr_addr_of(span, cx.expr_ident(span, ident)))
+                .collect();
 
-        let first_fieldless = variants.iter().find(|v| v.data.fields().is_empty());
+            let self_expr = tag_exprs.remove(0);
+            let other_selflike_exprs = tag_exprs;
+            let tag_field = FieldInfo { span, name: None, self_expr, other_selflike_exprs };
+
+            let tag_let_stmts: Vec<_> = iter::zip(&tag_idents, &selflike_args)
+                .map(|(&ident, selflike_arg)| {
+                    let variant_value = deriving::call_intrinsic(
+                        cx,
+                        span,
+                        sym::discriminant_value,
+                        vec![selflike_arg.clone()],
+                    );
+                    cx.stmt_let(span, false, ident, variant_value)
+                })
+                .collect();
+
+            (tag_field, tag_let_stmts)
+        };
+
+        // There are some special cases involving fieldless enums where no
+        // match is necessary.
+        let all_fieldless = variants.iter().all(|v| v.data.fields().is_empty());
+        if all_fieldless {
+            if uses_tags && variants.len() > 1 {
+                // If the type is fieldless and the trait uses the tag and
+                // there are multiple variants, we need just an operation on
+                // the tag(s).
+                let (tag_field, mut tag_let_stmts) = get_tag_pieces(cx);
+                let mut tag_check = self.call_substructure_method(
+                    cx,
+                    trait_,
+                    type_ident,
+                    nonselflike_args,
+                    &EnumTag(tag_field, None),
+                );
+                tag_let_stmts.append(&mut tag_check.0);
+                return BlockOrExpr(tag_let_stmts, tag_check.1);
+            }
+
+            if variants.len() == 1 {
+                // If there is a single variant, we don't need an operation on
+                // the tag(s). Just use the most degenerate result.
+                return self.call_substructure_method(
+                    cx,
+                    trait_,
+                    type_ident,
+                    nonselflike_args,
+                    &EnumMatching(0, 1, &variants[0], Vec::new()),
+                );
+            };
+        }
 
         // These arms are of the form:
         // (Variant1, Variant1, ...) => Body1
         // (Variant2, Variant2, ...) => Body2
         // ...
         // where each tuple has length = selflike_args.len()
-
         let mut match_arms: Vec<ast::Arm> = variants
             .iter()
             .enumerate()
@@ -1203,30 +1256,22 @@ impl<'a> MethodDef<'a> {
                 // A single arm has form (&VariantK, &VariantK, ...) => BodyK
                 // (see "Final wrinkle" note below for why.)
 
-                let use_temporaries = false; // enums can't be repr(packed)
-                let fields = trait_.create_struct_pattern_fields(
+                let addr_of = false; // because enums can't be repr(packed)
+                let fields =
+                    trait_.create_struct_pattern_fields(cx, &variant.data, &prefixes, addr_of);
+
+                let sp = variant.span.with_ctxt(trait_.span.ctxt());
+                let variant_path = cx.path(sp, vec![type_ident, variant.ident]);
+                let use_ref_pat = false; // because enums can't be repr(packed)
+                let mut subpats: Vec<_> = trait_.create_struct_patterns(
                     cx,
+                    variant_path,
                     &variant.data,
                     &prefixes,
-                    use_temporaries,
+                    use_ref_pat,
                 );
 
-                let sp = variant.span.with_ctxt(trait_.span.ctxt());
-                let variant_path = cx.path(sp, vec![type_ident, variant.ident]);
-                let mut subpats: Vec<_> = trait_
-                    .create_struct_patterns(
-                        cx,
-                        variant_path,
-                        &variant.data,
-                        &prefixes,
-                        ast::Mutability::Not,
-                        use_temporaries,
-                    )
-                    .into_iter()
-                    .map(|p| cx.pat(span, PatKind::Ref(p, ast::Mutability::Not)))
-                    .collect();
-
-                // Here is the pat = `(&VariantK, &VariantK, ...)`
+                // `(VariantK, VariantK, ...)` or just `VariantK`.
                 let single_pat = if subpats.len() == 1 {
                     subpats.pop().unwrap()
                 } else {
@@ -1256,27 +1301,28 @@ impl<'a> MethodDef<'a> {
             })
             .collect();
 
+        // Add a default arm to the match, if necessary.
+        let first_fieldless = variants.iter().find(|v| v.data.fields().is_empty());
         let default = match first_fieldless {
             Some(v) if self.unify_fieldless_variants => {
-                // We need a default case that handles the fieldless variants.
-                // The index and actual variant aren't meaningful in this case,
-                // so just use whatever
-                let substructure = EnumMatching(0, variants.len(), v, Vec::new());
+                // We need a default case that handles all the fieldless
+                // variants. The index and actual variant aren't meaningful in
+                // this case, so just use dummy values.
                 Some(
                     self.call_substructure_method(
                         cx,
                         trait_,
                         type_ident,
                         nonselflike_args,
-                        &substructure,
+                        &EnumMatching(0, variants.len(), v, Vec::new()),
                     )
                     .into_expr(cx, span),
                 )
             }
             _ if variants.len() > 1 && selflike_args.len() > 1 => {
-                // Since we know that all the arguments will match if we reach
+                // Because we know that all the arguments will match if we reach
                 // the match expression we add the unreachable intrinsics as the
-                // result of the catch all which should help llvm in optimizing it
+                // result of the default which should help llvm in optimizing it.
                 Some(deriving::call_unreachable(cx, span))
             }
             _ => None,
@@ -1285,111 +1331,41 @@ impl<'a> MethodDef<'a> {
             match_arms.push(cx.arm(span, cx.pat_wild(span), arm));
         }
 
-        // We will usually need the catch-all after matching the
-        // tuples `(VariantK, VariantK, ...)` for each VariantK of the
-        // enum.  But:
-        //
-        // * when there is only one Self arg, the arms above suffice
-        // (and the deriving we call back into may not be prepared to
-        // handle EnumNonMatchCollapsed), and,
-        //
-        // * when the enum has only one variant, the single arm that
-        // is already present always suffices.
-        //
-        // * In either of the two cases above, if we *did* add a
-        //   catch-all `_` match, it would trigger the
-        //   unreachable-pattern error.
-        //
-        if variants.len() > 1 && selflike_args.len() > 1 {
-            // Build a series of let statements mapping each selflike_arg
-            // to its discriminant value.
-            //
-            // i.e., for `enum E<T> { A, B(1), C(T, T) }`, and a deriving
-            // with three Self args, builds three statements:
-            // ```
-            // let __self_vi = std::intrinsics::discriminant_value(&self);
-            // let __arg_1_vi = std::intrinsics::discriminant_value(&arg1);
-            // let __arg_2_vi = std::intrinsics::discriminant_value(&arg2);
-            // ```
-            let mut index_let_stmts: Vec<ast::Stmt> = Vec::with_capacity(vi_idents.len() + 1);
-
-            // We also build an expression which checks whether all discriminants are equal:
-            // `__self_vi == __arg_1_vi && __self_vi == __arg_2_vi && ...`
-            let mut discriminant_test = cx.expr_bool(span, true);
-            for (i, (&ident, selflike_arg)) in iter::zip(&vi_idents, &selflike_args).enumerate() {
-                let selflike_addr = cx.expr_addr_of(span, selflike_arg.clone());
-                let variant_value = deriving::call_intrinsic(
-                    cx,
-                    span,
-                    sym::discriminant_value,
-                    vec![selflike_addr],
-                );
-                let let_stmt = cx.stmt_let(span, false, ident, variant_value);
-                index_let_stmts.push(let_stmt);
-
-                if i > 0 {
-                    let id0 = cx.expr_ident(span, vi_idents[0]);
-                    let id = cx.expr_ident(span, ident);
-                    let test = cx.expr_binary(span, BinOpKind::Eq, id0, id);
-                    discriminant_test = if i == 1 {
-                        test
-                    } else {
-                        cx.expr_binary(span, BinOpKind::And, discriminant_test, test)
-                    };
-                }
-            }
-
-            let arm_expr = self
-                .call_substructure_method(
-                    cx,
-                    trait_,
-                    type_ident,
-                    nonselflike_args,
-                    &catch_all_substructure,
-                )
-                .into_expr(cx, span);
-
-            // Final wrinkle: the selflike_args are expressions that deref
-            // down to desired places, but we cannot actually deref
-            // them when they are fed as r-values into a tuple
-            // expression; here add a layer of borrowing, turning
-            // `(*self, *__arg_0, ...)` into `(&*self, &*__arg_0, ...)`.
-            selflike_args.map_in_place(|selflike_arg| cx.expr_addr_of(span, selflike_arg));
-            let match_arg = cx.expr(span, ast::ExprKind::Tup(selflike_args));
-
-            // Lastly we create an expression which branches on all discriminants being equal
-            //  if discriminant_test {
-            //      match (...) {
-            //          (Variant1, Variant1, ...) => Body1
-            //          (Variant2, Variant2, ...) => Body2,
-            //          ...
-            //          _ => ::core::intrinsics::unreachable()
-            //      }
-            //  }
-            //  else {
-            //      <delegated expression referring to __self_vi, et al.>
-            //  }
-            let all_match = cx.expr_match(span, match_arg, match_arms);
-            let arm_expr = cx.expr_if(span, discriminant_test, all_match, Some(arm_expr));
-            BlockOrExpr(index_let_stmts, Some(arm_expr))
-        } else if variants.is_empty() {
-            // There is no sensible code to be generated for *any* deriving on
-            // a zero-variant enum. So we just generate a failing expression
-            // for the zero variant case.
-            BlockOrExpr(vec![], Some(deriving::call_unreachable(cx, span)))
-        } else {
-            // Final wrinkle: the selflike_args are expressions that deref
-            // down to desired places, but we cannot actually deref
-            // them when they are fed as r-values into a tuple
-            // expression; here add a layer of borrowing, turning
-            // `(*self, *__arg_0, ...)` into `(&*self, &*__arg_0, ...)`.
-            selflike_args.map_in_place(|selflike_arg| cx.expr_addr_of(span, selflike_arg));
+        // Create a match expression with one arm per discriminant plus
+        // possibly a default arm, e.g.:
+        //      match (self, other) {
+        //          (Variant1, Variant1, ...) => Body1
+        //          (Variant2, Variant2, ...) => Body2,
+        //          ...
+        //          _ => ::core::intrinsics::unreachable()
+        //      }
+        let get_match_expr = |mut selflike_args: Vec<P<Expr>>| {
             let match_arg = if selflike_args.len() == 1 {
                 selflike_args.pop().unwrap()
             } else {
                 cx.expr(span, ast::ExprKind::Tup(selflike_args))
             };
-            BlockOrExpr(vec![], Some(cx.expr_match(span, match_arg, match_arms)))
+            cx.expr_match(span, match_arg, match_arms)
+        };
+
+        // If the trait uses the tag and there are multiple variants, we need
+        // to add a tag check operation before the match. Otherwise, the match
+        // is enough.
+        if uses_tags && variants.len() > 1 {
+            let (tag_field, mut tag_let_stmts) = get_tag_pieces(cx);
+
+            // Combine a tag check with the match.
+            let mut tag_check_plus_match = self.call_substructure_method(
+                cx,
+                trait_,
+                type_ident,
+                nonselflike_args,
+                &EnumTag(tag_field, Some(get_match_expr(selflike_args))),
+            );
+            tag_let_stmts.append(&mut tag_check_plus_match.0);
+            BlockOrExpr(tag_let_stmts, tag_check_plus_match.1)
+        } else {
+            BlockOrExpr(vec![], Some(get_match_expr(selflike_args)))
         }
     }
 
@@ -1453,8 +1429,7 @@ impl<'a> TraitDef<'a> {
         struct_path: ast::Path,
         struct_def: &'a VariantData,
         prefixes: &[String],
-        mutbl: ast::Mutability,
-        use_temporaries: bool,
+        use_ref_pat: bool,
     ) -> Vec<P<ast::Pat>> {
         prefixes
             .iter()
@@ -1462,10 +1437,10 @@ impl<'a> TraitDef<'a> {
                 let pieces_iter =
                     struct_def.fields().iter().enumerate().map(|(i, struct_field)| {
                         let sp = struct_field.span.with_ctxt(self.span.ctxt());
-                        let binding_mode = if use_temporaries {
-                            ast::BindingMode::ByValue(ast::Mutability::Not)
+                        let binding_mode = if use_ref_pat {
+                            ast::BindingMode::ByRef(ast::Mutability::Not)
                         } else {
-                            ast::BindingMode::ByRef(mutbl)
+                            ast::BindingMode::ByValue(ast::Mutability::Not)
                         };
                         let ident = self.mk_pattern_ident(prefix, i);
                         let path = ident.with_span_pos(sp);
@@ -1544,7 +1519,7 @@ impl<'a> TraitDef<'a> {
         cx: &mut ExtCtxt<'_>,
         struct_def: &'a VariantData,
         prefixes: &[String],
-        use_temporaries: bool,
+        addr_of: bool,
     ) -> Vec<FieldInfo> {
         self.create_fields(struct_def, |i, _struct_field, sp| {
             prefixes
@@ -1552,7 +1527,7 @@ impl<'a> TraitDef<'a> {
                 .map(|prefix| {
                     let ident = self.mk_pattern_ident(prefix, i);
                     let expr = cx.expr_path(cx.path_ident(sp, ident));
-                    if use_temporaries { expr } else { cx.expr_deref(sp, expr) }
+                    if addr_of { cx.expr_addr_of(sp, expr) } else { expr }
                 })
                 .collect()
         })
@@ -1563,20 +1538,17 @@ impl<'a> TraitDef<'a> {
         cx: &mut ExtCtxt<'_>,
         selflike_args: &[P<Expr>],
         struct_def: &'a VariantData,
+        copy: bool,
     ) -> Vec<FieldInfo> {
         self.create_fields(struct_def, |i, struct_field, sp| {
             selflike_args
                 .iter()
-                .map(|mut selflike_arg| {
-                    // We don't the need the deref, if there is one.
-                    if let ast::ExprKind::Unary(ast::UnOp::Deref, inner) = &selflike_arg.kind {
-                        selflike_arg = inner;
-                    }
-                    // Note: we must use `struct_field.span` rather than `span` in the
+                .map(|selflike_arg| {
+                    // Note: we must use `struct_field.span` rather than `sp` in the
                     // `unwrap_or_else` case otherwise the hygiene is wrong and we get
                     // "field `0` of struct `Point` is private" errors on tuple
                     // structs.
-                    cx.expr(
+                    let mut field_expr = cx.expr(
                         sp,
                         ast::ExprKind::Field(
                             selflike_arg.clone(),
@@ -1584,7 +1556,13 @@ impl<'a> TraitDef<'a> {
                                 Ident::from_str_and_span(&i.to_string(), struct_field.span)
                             }),
                         ),
-                    )
+                    );
+                    if copy {
+                        field_expr = cx.expr_block(
+                            cx.block(struct_field.span, vec![cx.stmt_expr(field_expr)]),
+                        );
+                    }
+                    cx.expr_addr_of(sp, field_expr)
                 })
                 .collect()
         })
@@ -1605,11 +1583,6 @@ pub enum CsFold<'a> {
 
     // The fallback case for a struct or enum variant with no fields.
     Fieldless,
-
-    /// The fallback case for non-matching enum variants. The slice is the
-    /// identifiers holding the variant index value for each of the `Self`
-    /// arguments.
-    EnumNonMatching(Span, &'a [Ident]),
 }
 
 /// Folds over fields, combining the expressions for each field in a sequence.
@@ -1624,8 +1597,8 @@ pub fn cs_fold<F>(
 where
     F: FnMut(&mut ExtCtxt<'_>, CsFold<'_>) -> P<Expr>,
 {
-    match *substructure.fields {
-        EnumMatching(.., ref all_fields) | Struct(_, ref all_fields) => {
+    match substructure.fields {
+        EnumMatching(.., all_fields) | Struct(_, all_fields) => {
             if all_fields.is_empty() {
                 return f(cx, CsFold::Fieldless);
             }
@@ -1649,7 +1622,18 @@ where
                 rest.iter().rfold(base_expr, op)
             }
         }
-        EnumNonMatchingCollapsed(tuple) => f(cx, CsFold::EnumNonMatching(trait_span, tuple)),
+        EnumTag(tag_field, match_expr) => {
+            let tag_check_expr = f(cx, CsFold::Single(tag_field));
+            if let Some(match_expr) = match_expr {
+                if use_foldl {
+                    f(cx, CsFold::Combine(trait_span, tag_check_expr, match_expr.clone()))
+                } else {
+                    f(cx, CsFold::Combine(trait_span, match_expr.clone(), tag_check_expr))
+                }
+            } else {
+                tag_check_expr
+            }
+        }
         StaticEnum(..) | StaticStruct(..) => cx.span_bug(trait_span, "static function in `derive`"),
     }
 }
diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs b/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs
index 4b20d87629d..4d46f7cd48a 100644
--- a/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs
@@ -196,9 +196,8 @@ impl Bounds {
 }
 
 pub fn get_explicit_self(cx: &ExtCtxt<'_>, span: Span) -> (P<Expr>, ast::ExplicitSelf) {
-    // this constructs a fresh `self` path
+    // This constructs a fresh `self` path.
     let self_path = cx.expr_self(span);
     let self_ty = respan(span, SelfKind::Region(None, ast::Mutability::Not));
-    let self_expr = cx.expr_deref(span, self_path);
-    (self_expr, self_ty)
+    (self_path, self_ty)
 }
diff --git a/compiler/rustc_builtin_macros/src/deriving/hash.rs b/compiler/rustc_builtin_macros/src/deriving/hash.rs
index 2213cd6d37d..32ae3d34478 100644
--- a/compiler/rustc_builtin_macros/src/deriving/hash.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/hash.rs
@@ -1,6 +1,6 @@
 use crate::deriving::generic::ty::*;
 use crate::deriving::generic::*;
-use crate::deriving::{self, path_std, pathvec_std};
+use crate::deriving::{path_std, pathvec_std};
 
 use rustc_ast::{MetaItem, Mutability};
 use rustc_expand::base::{Annotatable, ExtCtxt};
@@ -52,39 +52,29 @@ fn hash_substructure(
     let [state_expr] = substr.nonselflike_args else {
         cx.span_bug(trait_span, "incorrect number of arguments in `derive(Hash)`");
     };
-    let call_hash = |span, thing_expr| {
+    let call_hash = |span, expr| {
         let hash_path = {
             let strs = cx.std_path(&[sym::hash, sym::Hash, sym::hash]);
 
             cx.expr_path(cx.path_global(span, strs))
         };
-        let ref_thing = cx.expr_addr_of(span, thing_expr);
-        let expr = cx.expr_call(span, hash_path, vec![ref_thing, state_expr.clone()]);
+        let expr = cx.expr_call(span, hash_path, vec![expr, state_expr.clone()]);
         cx.stmt_expr(expr)
     };
-    let mut stmts = Vec::new();
 
-    let fields = match substr.fields {
-        Struct(_, fs) | EnumMatching(_, 1, .., fs) => fs,
-        EnumMatching(.., fs) => {
-            let variant_value = deriving::call_intrinsic(
-                cx,
-                trait_span,
-                sym::discriminant_value,
-                vec![cx.expr_self(trait_span)],
-            );
-
-            stmts.push(call_hash(trait_span, variant_value));
-
-            fs
+    let (stmts, match_expr) = match substr.fields {
+        Struct(_, fields) | EnumMatching(.., fields) => {
+            let stmts =
+                fields.iter().map(|field| call_hash(field.span, field.self_expr.clone())).collect();
+            (stmts, None)
+        }
+        EnumTag(tag_field, match_expr) => {
+            assert!(tag_field.other_selflike_exprs.is_empty());
+            let stmts = vec![call_hash(tag_field.span, tag_field.self_expr.clone())];
+            (stmts, match_expr.clone())
         }
         _ => cx.span_bug(trait_span, "impossible substructure in `derive(Hash)`"),
     };
 
-    stmts.extend(
-        fields
-            .iter()
-            .map(|FieldInfo { ref self_expr, span, .. }| call_hash(*span, self_expr.clone())),
-    );
-    BlockOrExpr::new_stmts(stmts)
+    BlockOrExpr::new_mixed(stmts, match_expr)
 }
diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs
index 6c2ac343544..082c7893426 100644
--- a/compiler/rustc_builtin_macros/src/format.rs
+++ b/compiler/rustc_builtin_macros/src/format.rs
@@ -11,9 +11,11 @@ use rustc_errors::{pluralize, Applicability, MultiSpan, PResult};
 use rustc_expand::base::{self, *};
 use rustc_parse_format as parse;
 use rustc_span::symbol::{sym, Ident, Symbol};
-use rustc_span::{InnerSpan, Span};
+use rustc_span::{BytePos, InnerSpan, Span};
 use smallvec::SmallVec;
 
+use rustc_lint_defs::builtin::NAMED_ARGUMENTS_USED_POSITIONALLY;
+use rustc_lint_defs::{BufferedEarlyLint, BuiltinLintDiagnostics, LintId};
 use std::borrow::Cow;
 use std::collections::hash_map::Entry;
 
@@ -29,6 +31,136 @@ enum Position {
     Named(Symbol, InnerSpan),
 }
 
+/// Indicates how positional named argument (i.e. an named argument which is used by position
+/// instead of by name) is used in format string
+/// * `Arg` is the actual argument to print
+/// * `Width` is width format argument
+/// * `Precision` is precion format argument
+/// Example: `{Arg:Width$.Precision$}
+#[derive(Debug, Eq, PartialEq)]
+enum PositionalNamedArgType {
+    Arg,
+    Width,
+    Precision,
+}
+
+/// Contains information necessary to create a lint for a positional named argument
+#[derive(Debug)]
+struct PositionalNamedArg {
+    ty: PositionalNamedArgType,
+    /// The piece of the using this argument (multiple pieces can use the same argument)
+    cur_piece: usize,
+    /// The InnerSpan for in the string to be replaced with the named argument
+    /// This will be None when the position is implicit
+    inner_span_to_replace: Option<rustc_parse_format::InnerSpan>,
+    /// The name to use instead of the position
+    replacement: Symbol,
+    /// The span for the positional named argument (so the lint can point a message to it)
+    positional_named_arg_span: Span,
+}
+
+impl PositionalNamedArg {
+    /// Determines what span to replace with the name of the named argument
+    fn get_span_to_replace(&self, cx: &Context<'_, '_>) -> Option<Span> {
+        if let Some(inner_span) = &self.inner_span_to_replace {
+            return Some(
+                cx.fmtsp.from_inner(InnerSpan { start: inner_span.start, end: inner_span.end }),
+            );
+        } else if self.ty == PositionalNamedArgType::Arg {
+            // In the case of a named argument whose position is implicit, there will not be a span
+            // to replace. Instead, we insert the name after the `{`, which is the first character
+            // of arg_span.
+            return cx
+                .arg_spans
+                .get(self.cur_piece)
+                .map(|arg_span| arg_span.with_lo(arg_span.lo() + BytePos(1)).shrink_to_lo());
+        }
+
+        None
+    }
+}
+
+/// Encapsulates all the named arguments that have been used positionally
+#[derive(Debug)]
+struct PositionalNamedArgsLint {
+    positional_named_args: Vec<PositionalNamedArg>,
+}
+
+impl PositionalNamedArgsLint {
+    /// For a given positional argument, check if the index is for a named argument.
+    ///
+    /// Since positional arguments are required to come before named arguments, if the positional
+    /// index is greater than or equal to the start of named arguments, we know it's a named
+    /// argument used positionally.
+    ///
+    /// Example:
+    /// println!("{} {} {2}", 0, a=1, b=2);
+    ///
+    /// In this case, the first piece (`{}`) would be ArgumentImplicitlyIs with an index of 0. The
+    /// total number of arguments is 3 and the number of named arguments is 2, so the start of named
+    /// arguments is index 1. Therefore, the index of 0 is okay.
+    ///
+    /// The second piece (`{}`) would be ArgumentImplicitlyIs with an index of 1, which is the start
+    /// of named arguments, and so we should add a lint to use the named argument `a`.
+    ///
+    /// The third piece (`{2}`) would be ArgumentIs with an index of 2, which is greater than the
+    /// start of named arguments, and so we should add a lint to use the named argument `b`.
+    ///
+    /// This same check also works for width and precision formatting when either or both are
+    /// CountIsParam, which contains an index into the arguments.
+    fn maybe_add_positional_named_arg(
+        &mut self,
+        current_positional_arg: usize,
+        total_args_length: usize,
+        format_argument_index: usize,
+        ty: PositionalNamedArgType,
+        cur_piece: usize,
+        inner_span_to_replace: Option<rustc_parse_format::InnerSpan>,
+        names: &FxHashMap<Symbol, (usize, Span)>,
+    ) {
+        let start_of_named_args = total_args_length - names.len();
+        if current_positional_arg >= start_of_named_args {
+            self.maybe_push(format_argument_index, ty, cur_piece, inner_span_to_replace, names)
+        }
+    }
+
+    /// Try constructing a PositionalNamedArg struct and pushing it into the vec of positional
+    /// named arguments. If a named arg associated with `format_argument_index` cannot be found,
+    /// a new item will not be added as the lint cannot be emitted in this case.
+    fn maybe_push(
+        &mut self,
+        format_argument_index: usize,
+        ty: PositionalNamedArgType,
+        cur_piece: usize,
+        inner_span_to_replace: Option<rustc_parse_format::InnerSpan>,
+        names: &FxHashMap<Symbol, (usize, Span)>,
+    ) {
+        let named_arg = names
+            .iter()
+            .find(|&(_, &(index, _))| index == format_argument_index)
+            .map(|found| found.clone());
+
+        if let Some((&replacement, &(_, positional_named_arg_span))) = named_arg {
+            // In FormatSpec, `precision_span` starts at the leading `.`, which we want to keep in
+            // the lint suggestion, so increment `start` by 1 when `PositionalArgumentType` is
+            // `Precision`.
+            let inner_span_to_replace = if ty == PositionalNamedArgType::Precision {
+                inner_span_to_replace
+                    .map(|is| rustc_parse_format::InnerSpan { start: is.start + 1, end: is.end })
+            } else {
+                inner_span_to_replace
+            };
+            self.positional_named_args.push(PositionalNamedArg {
+                ty,
+                cur_piece,
+                inner_span_to_replace,
+                replacement,
+                positional_named_arg_span,
+            });
+        }
+    }
+}
+
 struct Context<'a, 'b> {
     ecx: &'a mut ExtCtxt<'b>,
     /// The macro's call site. References to unstable formatting internals must
@@ -57,7 +189,7 @@ struct Context<'a, 'b> {
     /// Unique format specs seen for each argument.
     arg_unique_types: Vec<Vec<ArgumentType>>,
     /// Map from named arguments to their resolved indices.
-    names: FxHashMap<Symbol, usize>,
+    names: FxHashMap<Symbol, (usize, Span)>,
 
     /// The latest consecutive literal strings, or empty if there weren't any.
     literal: String,
@@ -115,6 +247,7 @@ struct Context<'a, 'b> {
 
     /// Whether this format string came from a string literal, as opposed to a macro.
     is_literal: bool,
+    unused_names_lint: PositionalNamedArgsLint,
 }
 
 /// Parses the arguments from the given list of tokens, returning the diagnostic
@@ -130,9 +263,9 @@ fn parse_args<'a>(
     ecx: &mut ExtCtxt<'a>,
     sp: Span,
     tts: TokenStream,
-) -> PResult<'a, (P<ast::Expr>, Vec<P<ast::Expr>>, FxHashMap<Symbol, usize>)> {
+) -> PResult<'a, (P<ast::Expr>, Vec<P<ast::Expr>>, FxHashMap<Symbol, (usize, Span)>)> {
     let mut args = Vec::<P<ast::Expr>>::new();
-    let mut names = FxHashMap::<Symbol, usize>::default();
+    let mut names = FxHashMap::<Symbol, (usize, Span)>::default();
 
     let mut p = ecx.new_parser_from_tts(tts);
 
@@ -197,7 +330,7 @@ fn parse_args<'a>(
                 p.bump();
                 p.expect(&token::Eq)?;
                 let e = p.parse_expr()?;
-                if let Some(prev) = names.get(&ident.name) {
+                if let Some((prev, _)) = names.get(&ident.name) {
                     ecx.struct_span_err(e.span, &format!("duplicate argument named `{}`", ident))
                         .span_label(args[*prev].span, "previously here")
                         .span_label(e.span, "duplicate argument")
@@ -210,7 +343,7 @@ fn parse_args<'a>(
                 // if the input is valid, we can simply append to the positional
                 // args. And remember the names.
                 let slot = args.len();
-                names.insert(ident.name, slot);
+                names.insert(ident.name, (slot, ident.span));
                 args.push(e);
             }
             _ => {
@@ -222,7 +355,7 @@ fn parse_args<'a>(
                     );
                     err.span_label(e.span, "positional arguments must be before named arguments");
                     for pos in names.values() {
-                        err.span_label(args[*pos].span, "named argument");
+                        err.span_label(args[pos.0].span, "named argument");
                     }
                     err.emit();
                 }
@@ -239,16 +372,17 @@ impl<'a, 'b> Context<'a, 'b> {
         self.args.len() - self.num_captured_args
     }
 
-    fn resolve_name_inplace(&self, p: &mut parse::Piece<'_>) {
+    fn resolve_name_inplace(&mut self, p: &mut parse::Piece<'_>) {
         // NOTE: the `unwrap_or` branch is needed in case of invalid format
         // arguments, e.g., `format_args!("{foo}")`.
-        let lookup = |s: &str| *self.names.get(&Symbol::intern(s)).unwrap_or(&0);
+        let lookup =
+            |s: &str| self.names.get(&Symbol::intern(s)).unwrap_or(&(0, Span::default())).0;
 
         match *p {
             parse::String(_) => {}
             parse::NextArgument(ref mut arg) => {
                 if let parse::ArgumentNamed(s, _) = arg.position {
-                    arg.position = parse::ArgumentIs(lookup(s));
+                    arg.position = parse::ArgumentIs(lookup(s), None);
                 }
                 if let parse::CountIsName(s, _) = arg.format.width {
                     arg.format.width = parse::CountIsParam(lookup(s));
@@ -269,15 +403,48 @@ impl<'a, 'b> Context<'a, 'b> {
             parse::NextArgument(ref arg) => {
                 // width/precision first, if they have implicit positional
                 // parameters it makes more sense to consume them first.
-                self.verify_count(arg.format.width);
-                self.verify_count(arg.format.precision);
+                self.verify_count(
+                    arg.format.width,
+                    &arg.format.width_span,
+                    PositionalNamedArgType::Width,
+                );
+                self.verify_count(
+                    arg.format.precision,
+                    &arg.format.precision_span,
+                    PositionalNamedArgType::Precision,
+                );
 
                 // argument second, if it's an implicit positional parameter
                 // it's written second, so it should come after width/precision.
                 let pos = match arg.position {
-                    parse::ArgumentIs(i) | parse::ArgumentImplicitlyIs(i) => Exact(i),
+                    parse::ArgumentIs(i, arg_end) => {
+                        self.unused_names_lint.maybe_add_positional_named_arg(
+                            i,
+                            self.args.len(),
+                            i,
+                            PositionalNamedArgType::Arg,
+                            self.curpiece,
+                            arg_end,
+                            &self.names,
+                        );
+
+                        Exact(i)
+                    }
+                    parse::ArgumentImplicitlyIs(i) => {
+                        self.unused_names_lint.maybe_add_positional_named_arg(
+                            i,
+                            self.args.len(),
+                            i,
+                            PositionalNamedArgType::Arg,
+                            self.curpiece,
+                            None,
+                            &self.names,
+                        );
+                        Exact(i)
+                    }
                     parse::ArgumentNamed(s, span) => {
-                        Named(Symbol::intern(s), InnerSpan::new(span.start, span.end))
+                        let symbol = Symbol::intern(s);
+                        Named(symbol, InnerSpan::new(span.start, span.end))
                     }
                 };
 
@@ -345,10 +512,24 @@ impl<'a, 'b> Context<'a, 'b> {
         }
     }
 
-    fn verify_count(&mut self, c: parse::Count<'_>) {
+    fn verify_count(
+        &mut self,
+        c: parse::Count<'_>,
+        inner_span: &Option<rustc_parse_format::InnerSpan>,
+        named_arg_type: PositionalNamedArgType,
+    ) {
         match c {
             parse::CountImplied | parse::CountIs(..) => {}
             parse::CountIsParam(i) => {
+                self.unused_names_lint.maybe_add_positional_named_arg(
+                    i,
+                    self.args.len(),
+                    i,
+                    named_arg_type,
+                    self.curpiece,
+                    *inner_span,
+                    &self.names,
+                );
                 self.verify_arg_type(Exact(i), Count);
             }
             parse::CountIsName(s, span) => {
@@ -481,7 +662,7 @@ impl<'a, 'b> Context<'a, 'b> {
             if let Some(span) = fmt.width_span {
                 let span = self.fmtsp.from_inner(InnerSpan::new(span.start, span.end));
                 match fmt.width {
-                    parse::CountIsParam(pos) if pos > self.num_args() => {
+                    parse::CountIsParam(pos) if pos >= self.num_args() => {
                         e.span_label(
                             span,
                             &format!(
@@ -548,7 +729,7 @@ impl<'a, 'b> Context<'a, 'b> {
                 match self.names.get(&name) {
                     Some(&idx) => {
                         // Treat as positional arg.
-                        self.verify_arg_type(Capture(idx), ty)
+                        self.verify_arg_type(Capture(idx.0), ty)
                     }
                     None => {
                         // For the moment capturing variables from format strings expanded from macros is
@@ -565,7 +746,7 @@ impl<'a, 'b> Context<'a, 'b> {
                             };
                             self.num_captured_args += 1;
                             self.args.push(self.ecx.expr_ident(span, Ident::new(name, span)));
-                            self.names.insert(name, idx);
+                            self.names.insert(name, (idx, span));
                             self.verify_arg_type(Capture(idx), ty)
                         } else {
                             let msg = format!("there is no argument named `{}`", name);
@@ -669,7 +850,7 @@ impl<'a, 'b> Context<'a, 'b> {
                 // Build the position
                 let pos = {
                     match arg.position {
-                        parse::ArgumentIs(i) | parse::ArgumentImplicitlyIs(i) => {
+                        parse::ArgumentIs(i, ..) | parse::ArgumentImplicitlyIs(i) => {
                             // Map to index in final generated argument array
                             // in case of multiple types specified
                             let arg_idx = match arg_index_consumed.get_mut(i) {
@@ -697,7 +878,7 @@ impl<'a, 'b> Context<'a, 'b> {
                         // track the current argument ourselves.
                         let i = self.curarg;
                         self.curarg += 1;
-                        parse::ArgumentIs(i)
+                        parse::ArgumentIs(i, None)
                     },
                     format: parse::FormatSpec {
                         fill: arg.format.fill,
@@ -967,6 +1148,30 @@ pub fn expand_format_args_nl<'cx>(
     expand_format_args_impl(ecx, sp, tts, true)
 }
 
+fn create_lints_for_named_arguments_used_positionally(cx: &mut Context<'_, '_>) {
+    for named_arg in &cx.unused_names_lint.positional_named_args {
+        let arg_span = named_arg.get_span_to_replace(cx);
+
+        let msg = format!("named argument `{}` is not used by name", named_arg.replacement);
+        let replacement = match named_arg.ty {
+            PositionalNamedArgType::Arg => named_arg.replacement.to_string(),
+            _ => named_arg.replacement.to_string() + "$",
+        };
+
+        cx.ecx.buffered_early_lint.push(BufferedEarlyLint {
+            span: MultiSpan::from_span(named_arg.positional_named_arg_span),
+            msg: msg.clone(),
+            node_id: ast::CRATE_NODE_ID,
+            lint_id: LintId::of(&NAMED_ARGUMENTS_USED_POSITIONALLY),
+            diagnostic: BuiltinLintDiagnostics::NamedArgumentUsedPositionally(
+                arg_span,
+                named_arg.positional_named_arg_span,
+                replacement,
+            ),
+        });
+    }
+}
+
 /// Take the various parts of `format_args!(efmt, args..., name=names...)`
 /// and construct the appropriate formatting expression.
 pub fn expand_preparsed_format_args(
@@ -974,7 +1179,7 @@ pub fn expand_preparsed_format_args(
     sp: Span,
     efmt: P<ast::Expr>,
     args: Vec<P<ast::Expr>>,
-    names: FxHashMap<Symbol, usize>,
+    names: FxHashMap<Symbol, (usize, Span)>,
     append_newline: bool,
 ) -> P<ast::Expr> {
     // NOTE: this verbose way of initializing `Vec<Vec<ArgumentType>>` is because
@@ -1073,7 +1278,7 @@ pub fn expand_preparsed_format_args(
         .map(|span| fmt_span.from_inner(InnerSpan::new(span.start, span.end)))
         .collect();
 
-    let named_pos: FxHashSet<usize> = names.values().cloned().collect();
+    let named_pos: FxHashSet<usize> = names.values().cloned().map(|(i, _)| i).collect();
 
     let mut cx = Context {
         ecx,
@@ -1099,6 +1304,7 @@ pub fn expand_preparsed_format_args(
         arg_spans,
         arg_with_formatting: Vec::new(),
         is_literal: parser.is_literal,
+        unused_names_lint: PositionalNamedArgsLint { positional_named_args: vec![] },
     };
 
     // This needs to happen *after* the Parser has consumed all pieces to create all the spans
@@ -1113,7 +1319,7 @@ pub fn expand_preparsed_format_args(
 
     let numbered_position_args = pieces.iter().any(|arg: &parse::Piece<'_>| match *arg {
         parse::String(_) => false,
-        parse::NextArgument(arg) => matches!(arg.position, parse::Position::ArgumentIs(_)),
+        parse::NextArgument(arg) => matches!(arg.position, parse::Position::ArgumentIs(..)),
     });
 
     cx.build_index_map();
@@ -1265,6 +1471,10 @@ pub fn expand_preparsed_format_args(
         }
 
         diag.emit();
+    } else if cx.invalid_refs.is_empty() && cx.ecx.sess.err_count() == 0 {
+        // Only check for unused named argument names if there are no other errors to avoid causing
+        // too much noise in output errors, such as when a named argument is entirely unused.
+        create_lints_for_named_arguments_used_positionally(&mut cx);
     }
 
     cx.into_expr()
diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs
index 11565ba72d7..0192f3c8ca4 100644
--- a/compiler/rustc_builtin_macros/src/lib.rs
+++ b/compiler/rustc_builtin_macros/src/lib.rs
@@ -8,7 +8,7 @@
 #![feature(decl_macro)]
 #![feature(if_let_guard)]
 #![feature(is_sorted)]
-#![feature(let_chains)]
+#![cfg_attr(bootstrap, feature(let_chains))]
 #![feature(let_else)]
 #![feature(proc_macro_internals)]
 #![feature(proc_macro_quote)]
diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs
index e2a6e77ab44..0ebe29df95f 100644
--- a/compiler/rustc_builtin_macros/src/test_harness.rs
+++ b/compiler/rustc_builtin_macros/src/test_harness.rs
@@ -51,7 +51,7 @@ pub fn inject(sess: &Session, resolver: &mut dyn ResolverExpand, krate: &mut ast
     let test_runner = get_test_runner(sess, span_diagnostic, &krate);
 
     if sess.opts.test {
-        let panic_strategy = match (panic_strategy, sess.opts.debugging_opts.panic_abort_tests) {
+        let panic_strategy = match (panic_strategy, sess.opts.unstable_opts.panic_abort_tests) {
             (PanicStrategy::Abort, true) => PanicStrategy::Abort,
             (PanicStrategy::Abort, false) => {
                 if panic_strategy == platform_panic_strategy {
diff --git a/compiler/rustc_codegen_cranelift/.vscode/settings.json b/compiler/rustc_codegen_cranelift/.vscode/settings.json
index ecb20f22d8c..d88309e412e 100644
--- a/compiler/rustc_codegen_cranelift/.vscode/settings.json
+++ b/compiler/rustc_codegen_cranelift/.vscode/settings.json
@@ -1,10 +1,9 @@
 {
     // source for rustc_* is not included in the rust-src component; disable the errors about this
     "rust-analyzer.diagnostics.disabled": ["unresolved-extern-crate", "unresolved-macro-call"],
-    "rust-analyzer.assist.importGranularity": "module",
-    "rust-analyzer.assist.importEnforceGranularity": true,
-    "rust-analyzer.assist.importPrefix": "crate",
-    "rust-analyzer.cargo.runBuildScripts": true,
+    "rust-analyzer.imports.granularity.enforce": true,
+    "rust-analyzer.imports.granularity.group": "module",
+    "rust-analyzer.imports.prefix": "crate",
     "rust-analyzer.cargo.features": ["unstable-features"],
     "rust-analyzer.linkedProjects": [
         "./Cargo.toml",
diff --git a/compiler/rustc_codegen_cranelift/Cargo.lock b/compiler/rustc_codegen_cranelift/Cargo.lock
index da18ac7eacb..532049c858d 100644
--- a/compiler/rustc_codegen_cranelift/Cargo.lock
+++ b/compiler/rustc_codegen_cranelift/Cargo.lock
@@ -3,6 +3,17 @@
 version = 3
 
 [[package]]
+name = "ahash"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
+dependencies = [
+ "getrandom",
+ "once_cell",
+ "version_check",
+]
+
+[[package]]
 name = "anyhow"
 version = "1.0.56"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -26,6 +37,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
 
 [[package]]
+name = "byteorder"
+version = "1.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
+
+[[package]]
 name = "cfg-if"
 version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -33,56 +50,57 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
 [[package]]
 name = "cranelift-bforest"
-version = "0.83.0"
+version = "0.85.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed44413e7e2fe3260d0ed73e6956ab188b69c10ee92b892e401e0f4f6808c68b"
+checksum = "749d0d6022c9038dccf480bdde2a38d435937335bf2bb0f14e815d94517cdce8"
 dependencies = [
  "cranelift-entity",
 ]
 
 [[package]]
 name = "cranelift-codegen"
-version = "0.83.0"
+version = "0.85.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b5d83f0f26bf213f971f45589d17e5b65e4861f9ed22392b0cbb6eaa5bd329c"
+checksum = "e94370cc7b37bf652ccd8bb8f09bd900997f7ccf97520edfc75554bb5c4abbea"
 dependencies = [
  "cranelift-bforest",
  "cranelift-codegen-meta",
  "cranelift-codegen-shared",
  "cranelift-entity",
+ "cranelift-isle",
  "gimli",
  "log",
- "regalloc",
+ "regalloc2",
  "smallvec",
  "target-lexicon",
 ]
 
 [[package]]
 name = "cranelift-codegen-meta"
-version = "0.83.0"
+version = "0.85.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6800dc386177df6ecc5a32680607ed8ba1fa0d31a2a59c8c61fbf44826b8191d"
+checksum = "e0a3cea8fdab90e44018c5b9a1dfd460d8ee265ac354337150222a354628bdb6"
 dependencies = [
  "cranelift-codegen-shared",
 ]
 
 [[package]]
 name = "cranelift-codegen-shared"
-version = "0.83.0"
+version = "0.85.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c961f85070985ebc8fcdb81b838a5cf842294d1e6ed4852446161c7e246fd455"
+checksum = "5ac72f76f2698598951ab26d8c96eaa854810e693e7dd52523958b5909fde6b2"
 
 [[package]]
 name = "cranelift-entity"
-version = "0.83.0"
+version = "0.85.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2347b2b8d1d5429213668f2a8e36c85ee3c73984a2f6a79007e365d3e575e7ed"
+checksum = "09eaeacfcd2356fe0e66b295e8f9d59fdd1ac3ace53ba50de14d628ec902f72d"
 
 [[package]]
 name = "cranelift-frontend"
-version = "0.83.0"
+version = "0.85.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4cbcdbf7bed29e363568b778649b69dabc3d727256d5d25236096ef693757654"
+checksum = "dba69c9980d5ffd62c18a2bde927855fcd7c8dc92f29feaf8636052662cbd99c"
 dependencies = [
  "cranelift-codegen",
  "log",
@@ -91,10 +109,16 @@ dependencies = [
 ]
 
 [[package]]
+name = "cranelift-isle"
+version = "0.85.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2920dc1e05cac40304456ed3301fde2c09bd6a9b0210bcfa2f101398d628d5b"
+
+[[package]]
 name = "cranelift-jit"
-version = "0.83.0"
+version = "0.85.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7c769d4e0d76f59c8b2a3bf0477d89ee149bb0731b53fbb245ee081d49063095"
+checksum = "1c3c5ed067f2c81577e431f3039148a9c187b33cc79e0d1731fede27d801ec56"
 dependencies = [
  "anyhow",
  "cranelift-codegen",
@@ -110,9 +134,9 @@ dependencies = [
 
 [[package]]
 name = "cranelift-module"
-version = "0.83.0"
+version = "0.85.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0ab57d399a2401074bb0cc40b3031e420f3d66d46ec0cf21eeae53ac04bd73e2"
+checksum = "eee6784303bf9af235237a4885f7417e09a35df896d38ea969a0081064b3ede4"
 dependencies = [
  "anyhow",
  "cranelift-codegen",
@@ -120,9 +144,9 @@ dependencies = [
 
 [[package]]
 name = "cranelift-native"
-version = "0.83.0"
+version = "0.85.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f4cdf93552e5ceb2e3c042829ebb4de4378492705f769eadc6a7c6c5251624c"
+checksum = "f04dfa45f9b2a6f587c564d6b63388e00cd6589d2df6ea2758cf79e1a13285e6"
 dependencies = [
  "cranelift-codegen",
  "libc",
@@ -131,9 +155,9 @@ dependencies = [
 
 [[package]]
 name = "cranelift-object"
-version = "0.83.0"
+version = "0.85.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf8e65f4839c26e6237fc0744911d79b0a2ac5e76b4e4eebd14db2b8d849fd31"
+checksum = "0bf38b2c505db749276793116c0cb30bd096206c7810e471677a453134881881"
 dependencies = [
  "anyhow",
  "cranelift-codegen",
@@ -153,6 +177,26 @@ dependencies = [
 ]
 
 [[package]]
+name = "fxhash"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
+dependencies = [
+ "byteorder",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi",
+]
+
+[[package]]
 name = "gimli"
 version = "0.26.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -166,22 +210,31 @@ name = "hashbrown"
 version = "0.11.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
+dependencies = [
+ "ahash",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
 
 [[package]]
 name = "indexmap"
-version = "1.8.0"
+version = "1.9.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223"
+checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
 dependencies = [
  "autocfg",
- "hashbrown",
+ "hashbrown 0.12.3",
 ]
 
 [[package]]
 name = "libc"
-version = "0.2.119"
+version = "0.2.126"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4"
+checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
 
 [[package]]
 name = "libloading"
@@ -219,11 +272,12 @@ checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
 
 [[package]]
 name = "object"
-version = "0.27.1"
+version = "0.28.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "67ac1d3f9a1d3616fd9a60c8d74296f22406a238b6a72f5cc1e6f314df4ffbf9"
+checksum = "e42c982f2d955fac81dd7e1d0e1426a7d702acd9c98d19ab01083a6a0328c424"
 dependencies = [
  "crc32fast",
+ "hashbrown 0.11.2",
  "indexmap",
  "memchr",
 ]
@@ -235,13 +289,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9"
 
 [[package]]
-name = "regalloc"
-version = "0.0.34"
+name = "regalloc2"
+version = "0.2.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62446b1d3ebf980bdc68837700af1d77b37bc430e524bf95319c6eada2a4cc02"
+checksum = "4a8d23b35d7177df3b9d31ed8a9ab4bf625c668be77a319d4f5efd4a5257701c"
 dependencies = [
+ "fxhash",
  "log",
- "rustc-hash",
+ "slice-group-by",
  "smallvec",
 ]
 
@@ -258,12 +313,6 @@ dependencies = [
 ]
 
 [[package]]
-name = "rustc-hash"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
-
-[[package]]
 name = "rustc_codegen_cranelift"
 version = "0.1.0"
 dependencies = [
@@ -284,6 +333,12 @@ dependencies = [
 ]
 
 [[package]]
+name = "slice-group-by"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03b634d87b960ab1a38c4fe143b508576f075e7c978bfad18217645ebfdfa2ec"
+
+[[package]]
 name = "smallvec"
 version = "1.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -296,6 +351,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d7fa7e55043acb85fca6b3c01485a2eeb6b69c5d21002e273c79e465f43b7ac1"
 
 [[package]]
+name = "version_check"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+
+[[package]]
+name = "wasi"
+version = "0.10.2+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
+
+[[package]]
 name = "winapi"
 version = "0.3.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/compiler/rustc_codegen_cranelift/Cargo.toml b/compiler/rustc_codegen_cranelift/Cargo.toml
index ec2c1f2ca71..61e977e3e69 100644
--- a/compiler/rustc_codegen_cranelift/Cargo.toml
+++ b/compiler/rustc_codegen_cranelift/Cargo.toml
@@ -8,18 +8,18 @@ crate-type = ["dylib"]
 
 [dependencies]
 # These have to be in sync with each other
-cranelift-codegen = { version = "0.83.0", features = ["unwind", "all-arch"] }
-cranelift-frontend = "0.83.0"
-cranelift-module = "0.83.0"
-cranelift-native = "0.83.0"
-cranelift-jit = { version = "0.83.0", optional = true }
-cranelift-object = "0.83.0"
+cranelift-codegen = { version = "0.85.3", features = ["unwind", "all-arch"] }
+cranelift-frontend = "0.85.3"
+cranelift-module = "0.85.3"
+cranelift-native = "0.85.3"
+cranelift-jit = { version = "0.85.3", optional = true }
+cranelift-object = "0.85.3"
 target-lexicon = "0.12.0"
 gimli = { version = "0.26.0", default-features = false, features = ["write"]}
-object = { version = "0.27.0", default-features = false, features = ["std", "read_core", "write", "archive", "coff", "elf", "macho", "pe"] }
+object = { version = "0.28.0", default-features = false, features = ["std", "read_core", "write", "archive", "coff", "elf", "macho", "pe"] }
 
 ar = { git = "https://github.com/bjorn3/rust-ar.git", branch = "do_not_remove_cg_clif_ranlib" }
-indexmap = "1.8.0"
+indexmap = "1.9.1"
 libloading = { version = "0.6.0", optional = true }
 once_cell = "1.10.0"
 smallvec = "1.8.1"
diff --git a/compiler/rustc_codegen_cranelift/build_sysroot/Cargo.lock b/compiler/rustc_codegen_cranelift/build_sysroot/Cargo.lock
index efee6ef3f37..7b2cdd27336 100644
--- a/compiler/rustc_codegen_cranelift/build_sysroot/Cargo.lock
+++ b/compiler/rustc_codegen_cranelift/build_sysroot/Cargo.lock
@@ -56,9 +56,9 @@ dependencies = [
 
 [[package]]
 name = "compiler_builtins"
-version = "0.1.72"
+version = "0.1.75"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "afdbb35d279238cf77f0c9e8d90ad50d6c7bff476ab342baafa29440f0f10bff"
+checksum = "c6e3183e88f659a862835db8f4b67dbeed3d93e44dd4927eef78edb1c149d784"
 dependencies = [
  "rustc-std-workspace-core",
 ]
@@ -112,9 +112,9 @@ dependencies = [
 
 [[package]]
 name = "hashbrown"
-version = "0.12.1"
+version = "0.12.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3"
+checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
 dependencies = [
  "compiler_builtins",
  "rustc-std-workspace-alloc",
@@ -123,20 +123,21 @@ dependencies = [
 
 [[package]]
 name = "hermit-abi"
-version = "0.2.0"
+version = "0.2.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ab7905ea95c6d9af62940f9d7dd9596d54c334ae2c15300c482051292d5637f"
+checksum = "7668753748e445859e4e373c3d41117235d9feed578392f5a3a73efdc751ca4a"
 dependencies = [
  "compiler_builtins",
  "libc",
+ "rustc-std-workspace-alloc",
  "rustc-std-workspace-core",
 ]
 
 [[package]]
 name = "libc"
-version = "0.2.125"
+version = "0.2.126"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5916d2ae698f6de9bfb891ad7a8d65c09d232dc58cc4ac433c7da3b2fd84bc2b"
+checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
 dependencies = [
  "rustc-std-workspace-core",
 ]
diff --git a/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs b/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs
index 8682204f4fd..16cce83dd9c 100644
--- a/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs
+++ b/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs
@@ -205,7 +205,7 @@ fn build_clif_sysroot_for_triple(
     {
         let entry = entry.unwrap();
         if let Some(ext) = entry.path().extension() {
-            if ext == "rmeta" || ext == "d" || ext == "dSYM" {
+            if ext == "rmeta" || ext == "d" || ext == "dSYM" || ext == "clif" {
                 continue;
             }
         } else {
diff --git a/compiler/rustc_codegen_cranelift/example/mini_core.rs b/compiler/rustc_codegen_cranelift/example/mini_core.rs
index 489259d1a6b..8b6042a3d66 100644
--- a/compiler/rustc_codegen_cranelift/example/mini_core.rs
+++ b/compiler/rustc_codegen_cranelift/example/mini_core.rs
@@ -458,7 +458,7 @@ pub trait FnMut<Args>: FnOnce<Args> {
 
 #[lang = "panic"]
 #[track_caller]
-pub fn panic(_msg: &str) -> ! {
+pub fn panic(_msg: &'static str) -> ! {
     unsafe {
         libc::puts("Panicking\n\0" as *const str as *const i8);
         intrinsics::abort();
@@ -497,7 +497,7 @@ pub trait Deref {
 #[repr(transparent)]
 #[rustc_layout_scalar_valid_range_start(1)]
 #[rustc_nonnull_optimization_guaranteed]
-pub struct NonNull<T: ?Sized>(pub *mut T);
+pub struct NonNull<T: ?Sized>(pub *const T);
 
 impl<T: ?Sized, U: ?Sized> CoerceUnsized<NonNull<U>> for NonNull<T> where T: Unsize<U> {}
 impl<T: ?Sized, U: ?Sized> DispatchFromDyn<NonNull<U>> for NonNull<T> where T: Unsize<U> {}
@@ -521,7 +521,7 @@ impl<T: ?Sized> Drop for Box<T> {
     }
 }
 
-impl<T> Deref for Box<T> {
+impl<T: ?Sized> Deref for Box<T> {
     type Target = T;
 
     fn deref(&self) -> &Self::Target {
diff --git a/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs b/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs
index 0f1245c2758..aa1f239bae2 100644
--- a/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs
+++ b/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs
@@ -124,6 +124,23 @@ fn call_return_u128_pair() {
     return_u128_pair();
 }
 
+#[repr(C)]
+pub struct bool_11 {
+    field0: bool,
+    field1: bool,
+    field2: bool,
+    field3: bool,
+    field4: bool,
+    field5: bool,
+    field6: bool,
+    field7: bool,
+    field8: bool,
+    field9: bool,
+    field10: bool,
+}
+
+extern "C" fn bool_struct_in_11(arg0: bool_11) {}
+
 #[allow(unreachable_code)] // FIXME false positive
 fn main() {
     take_unique(Unique {
@@ -134,6 +151,20 @@ fn main() {
 
     call_return_u128_pair();
 
+    bool_struct_in_11(bool_11 {
+        field0: true,
+        field1: true,
+        field2: true,
+        field3: true,
+        field4: true,
+        field5: true,
+        field6: true,
+        field7: true,
+        field8: true,
+        field9: true,
+        field10: true,
+    });
+
     let slice = &[0, 1] as &[i32];
     let slice_ptr = slice as *const [i32] as *const i32;
 
@@ -299,6 +330,17 @@ fn main() {
     static REF1: &u8 = &42;
     static REF2: &u8 = REF1;
     assert_eq!(*REF1, *REF2);
+
+    extern "C" {
+        type A;
+    }
+
+    fn main() {
+        let x: &A = unsafe { &*(1usize as *const A) };
+
+        assert_eq!(unsafe { intrinsics::size_of_val(x) }, 0);
+        assert_eq!(unsafe { intrinsics::min_align_of_val(x) }, 1);
+}
 }
 
 #[cfg(all(not(jit), target_arch = "x86_64", target_os = "linux"))]
diff --git a/compiler/rustc_codegen_cranelift/example/std_example.rs b/compiler/rustc_codegen_cranelift/example/std_example.rs
index 0a2bce2621d..0b5b6cd55d7 100644
--- a/compiler/rustc_codegen_cranelift/example/std_example.rs
+++ b/compiler/rustc_codegen_cranelift/example/std_example.rs
@@ -128,6 +128,25 @@ fn main() {
         0 => loop {},
         v => panic(v),
     };
+
+    if black_box(false) {
+        // Based on https://github.com/rust-lang/rust/blob/2f320a224e827b400be25966755a621779f797cc/src/test/ui/debuginfo/debuginfo_with_uninhabitable_field_and_unsized.rs
+        let _ = Foo::<dyn Send>::new();
+
+        #[allow(dead_code)]
+        struct Foo<T: ?Sized> {
+            base: Never,
+            value: T,
+        }
+
+        impl<T: ?Sized> Foo<T> {
+            pub fn new() -> Box<Foo<T>> {
+                todo!()
+            }
+        }
+
+        enum Never {}
+    }
 }
 
 fn panic(_: u128) {
diff --git a/compiler/rustc_codegen_cranelift/rust-toolchain b/compiler/rustc_codegen_cranelift/rust-toolchain
index e98e92e468e..3ab395d89d5 100644
--- a/compiler/rustc_codegen_cranelift/rust-toolchain
+++ b/compiler/rustc_codegen_cranelift/rust-toolchain
@@ -1,3 +1,3 @@
 [toolchain]
-channel = "nightly-2022-05-15"
+channel = "nightly-2022-07-25"
 components = ["rust-src", "rustc-dev", "llvm-tools-preview"]
diff --git a/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh b/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh
index 4d0dfa16c5e..091bfa1e992 100644
--- a/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh
+++ b/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh
@@ -29,14 +29,15 @@ diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/ru
 index 8431aa7b818..a3ff7e68ce5 100644
 --- a/src/tools/compiletest/src/runtest.rs
 +++ b/src/tools/compiletest/src/runtest.rs
-@@ -3489,11 +3489,7 @@ fn normalize_output(&self, output: &str, custom_rules: &[(String, String)]) -> S
-             .join("library");
-         normalize_path(&src_dir, "$(echo '$SRC_DIR')");
+@@ -3489,12 +3489,7 @@ fn normalize_output(&self, output: &str, custom_rules: &[(String, String)]) -> S
+         let compiler_src_dir = base_dir.join("compiler");
+         normalize_path(&compiler_src_dir, "$(echo '$COMPILER_DIR')");
 
 -        if let Some(virtual_rust_source_base_dir) =
 -            option_env!("CFG_VIRTUAL_RUST_SOURCE_BASE_DIR").map(PathBuf::from)
 -        {
 -            normalize_path(&virtual_rust_source_base_dir.join("library"), "$(echo '$SRC_DIR')");
+-            normalize_path(&virtual_rust_source_base_dir.join("compiler"), "$(echo '$COMPILER_DIR')");
 -        }
 +        normalize_path(&Path::new("$(cd ../build_sysroot/sysroot_src/library; pwd)"), "$(echo '$SRC_DIR')");
 
@@ -62,3 +63,6 @@ deny-warnings = false
 verbose-tests = false
 EOF
 popd
+
+# FIXME remove once inline asm is fully supported
+export RUSTFLAGS="$RUSTFLAGS --cfg=rustix_use_libc"
diff --git a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh
index 9bdb9f22c54..944787612d8 100755
--- a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh
+++ b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh
@@ -33,6 +33,7 @@ rm src/test/ui/test-attrs/test-fn-signature-verification-for-explicit-return-typ
 rm src/test/ui/async-await/async-fn-size-moved-locals.rs # -Cpanic=abort shrinks some generator by one byte
 rm src/test/ui/async-await/async-fn-size-uninit-locals.rs # same
 rm src/test/ui/generator/size-moved-locals.rs # same
+rm -r src/test/ui/macros/rfc-2011-nicer-assert-messages/
 
 # vendor intrinsics
 rm src/test/ui/sse2.rs # cpuid not supported, so sse2 not detected
@@ -65,11 +66,13 @@ rm src/test/ui/intrinsics/intrinsic-nearby.rs # unimplemented nearbyintf32 and n
 rm src/test/ui/target-feature/missing-plusminus.rs # error not implemented
 rm src/test/ui/fn/dyn-fn-alignment.rs # wants a 256 byte alignment
 rm -r src/test/run-make/emit-named-files # requires full --emit support
+rm src/test/ui/abi/stack-probes.rs # stack probes not yet implemented
 
 # optimization tests
 # ==================
-rm src/test/ui/issues/issue-28950.rs # depends on stack size optimizations
+rm src/test/ui/codegen/issue-28950.rs # depends on stack size optimizations
 rm src/test/ui/codegen/init-large-type.rs # same
+rm src/test/ui/issues/issue-40883.rs # same
 rm -r src/test/run-make/fmt-write-bloat/ # tests an optimization
 
 # backend specific tests
@@ -89,14 +92,13 @@ rm src/test/ui/consts/issue-33537.rs # same
 rm -r src/test/run-make/emit-shared-files # requires the rustdoc executable in build/bin/
 rm -r src/test/run-make/unstable-flag-required # same
 rm -r src/test/run-make/rustdoc-* # same
+rm -r src/test/run-make/issue-88756-default-output # same
+rm -r src/test/run-make/remap-path-prefix-dwarf # requires llvm-dwarfdump
 
 # genuine bugs
 # ============
 rm src/test/ui/allocator/no_std-alloc-error-handler-default.rs # missing rust_oom definition
 
-rm -r src/test/ui/polymorphization/ # polymorphization not yet supported
-rm src/test/codegen-units/polymorphization/unused_type_parameters.rs # same
-
 rm src/test/incremental/spike-neg1.rs # errors out for some reason
 rm src/test/incremental/spike-neg2.rs # same
 rm src/test/ui/issues/issue-74564-if-expr-stack-overflow.rs # gives a stackoverflow before the backend runs
@@ -111,6 +113,8 @@ rm src/test/ui/backtrace.rs # TODO warning
 rm src/test/ui/empty_global_asm.rs # TODO add needs-asm-support
 rm src/test/ui/simple_global_asm.rs # TODO add needs-asm-support
 rm src/test/ui/test-attrs/test-type.rs # TODO panic message on stderr. correct stdout
+# not sure if this is actually a bug in the test suite, but the symbol list shows the function without leading _ for some reason
+rm -r src/test/run-make/native-link-modifier-bundle
 
 echo "[TEST] rustc test suite"
 RUST_TEST_NOCAPTURE=1 COMPILETEST_FORCE_STAGE0=1 ./x.py test --stage 0 src/test/{codegen-units,run-make,run-pass-valgrind,ui,incremental}
diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs
index ffa5d747b11..815450f689e 100644
--- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs
+++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs
@@ -4,6 +4,7 @@ mod comments;
 mod pass_mode;
 mod returning;
 
+use cranelift_module::ModuleError;
 use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
 use rustc_middle::ty::layout::FnAbiOf;
 use rustc_target::abi::call::{Conv, FnAbi};
@@ -69,7 +70,17 @@ pub(crate) fn import_function<'tcx>(
 ) -> FuncId {
     let name = tcx.symbol_name(inst).name;
     let sig = get_function_sig(tcx, module.isa().triple(), inst);
-    module.declare_function(name, Linkage::Import, &sig).unwrap()
+    match module.declare_function(name, Linkage::Import, &sig) {
+        Ok(func_id) => func_id,
+        Err(ModuleError::IncompatibleDeclaration(_)) => tcx.sess.fatal(&format!(
+            "attempt to declare `{name}` as function, but it was already declared as static"
+        )),
+        Err(ModuleError::IncompatibleSignature(_, prev_sig, new_sig)) => tcx.sess.fatal(&format!(
+            "attempt to declare `{name}` with signature {new_sig:?}, \
+             but it was already declared with signature {prev_sig:?}"
+        )),
+        Err(err) => Err::<_, _>(err).unwrap(),
+    }
 }
 
 impl<'tcx> FunctionCx<'_, '_, 'tcx> {
@@ -182,6 +193,15 @@ pub(crate) fn codegen_fn_prelude<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, start_
     }
 
     let fn_abi = fx.fn_abi.take().unwrap();
+
+    // FIXME implement variadics in cranelift
+    if fn_abi.c_variadic {
+        fx.tcx.sess.span_fatal(
+            fx.mir.span,
+            "Defining variadic functions is not yet supported by Cranelift",
+        );
+    }
+
     let mut arg_abis_iter = fn_abi.args.iter();
 
     let func_params = fx
@@ -376,9 +396,15 @@ pub(crate) fn codegen_terminator_call<'tcx>(
         RevealAllLayoutCx(fx.tcx).fn_abi_of_fn_ptr(fn_ty.fn_sig(fx.tcx), extra_args)
     };
 
-    let is_cold = instance
-        .map(|inst| fx.tcx.codegen_fn_attrs(inst.def_id()).flags.contains(CodegenFnAttrFlags::COLD))
-        .unwrap_or(false);
+    let is_cold = if fn_sig.abi == Abi::RustCold {
+        true
+    } else {
+        instance
+            .map(|inst| {
+                fx.tcx.codegen_fn_attrs(inst.def_id()).flags.contains(CodegenFnAttrFlags::COLD)
+            })
+            .unwrap_or(false)
+    };
     if is_cold {
         fx.bcx.set_cold_block(fx.bcx.current_block().unwrap());
         if let Some(destination_block) = target {
diff --git a/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs b/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs
index 9f0bd31e95f..6c10baa53d4 100644
--- a/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs
+++ b/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs
@@ -18,9 +18,9 @@ fn reg_to_abi_param(reg: Reg) -> AbiParam {
     let clif_ty = match (reg.kind, reg.size.bytes()) {
         (RegKind::Integer, 1) => types::I8,
         (RegKind::Integer, 2) => types::I16,
-        (RegKind::Integer, 4) => types::I32,
-        (RegKind::Integer, 8) => types::I64,
-        (RegKind::Integer, 16) => types::I128,
+        (RegKind::Integer, 3..=4) => types::I32,
+        (RegKind::Integer, 5..=8) => types::I64,
+        (RegKind::Integer, 9..=16) => types::I128,
         (RegKind::Float, 4) => types::F32,
         (RegKind::Float, 8) => types::F64,
         (RegKind::Vector, size) => types::I8.by(u16::try_from(size).unwrap()).unwrap(),
@@ -48,23 +48,9 @@ fn cast_target_to_abi_params(cast: CastTarget) -> SmallVec<[AbiParam; 2]> {
         )
     };
 
-    if cast.prefix.iter().all(|x| x.is_none()) {
-        // Simplify to a single unit when there is no prefix and size <= unit size
-        if cast.rest.total <= cast.rest.unit.size {
-            let clif_ty = match (cast.rest.unit.kind, cast.rest.unit.size.bytes()) {
-                (RegKind::Integer, 1) => types::I8,
-                (RegKind::Integer, 2) => types::I16,
-                (RegKind::Integer, 3..=4) => types::I32,
-                (RegKind::Integer, 5..=8) => types::I64,
-                (RegKind::Integer, 9..=16) => types::I128,
-                (RegKind::Float, 4) => types::F32,
-                (RegKind::Float, 8) => types::F64,
-                (RegKind::Vector, size) => types::I8.by(u16::try_from(size).unwrap()).unwrap(),
-                _ => unreachable!("{:?}", cast.rest.unit),
-            };
-            return smallvec![AbiParam::new(clif_ty)];
-        }
-    }
+    // Note: Unlike the LLVM equivalent of this code we don't have separate branches for when there
+    // is no prefix as a single unit, an array and a heterogeneous struct are not represented using
+    // different types in Cranelift IR. Instead a single array of primitive types is used.
 
     // Create list of fields in the main structure
     let mut args = cast
@@ -230,7 +216,7 @@ pub(super) fn adjust_arg_for_abi<'tcx>(
     arg_abi: &ArgAbi<'tcx, Ty<'tcx>>,
     is_owned: bool,
 ) -> SmallVec<[Value; 2]> {
-    assert_assignable(fx, arg.layout().ty, arg_abi.layout.ty);
+    assert_assignable(fx, arg.layout().ty, arg_abi.layout.ty, 16);
     match arg_abi.mode {
         PassMode::Ignore => smallvec![],
         PassMode::Direct(_) => smallvec![arg.load_scalar(fx)],
diff --git a/compiler/rustc_codegen_cranelift/src/allocator.rs b/compiler/rustc_codegen_cranelift/src/allocator.rs
index c3b99b64263..6d321c7b298 100644
--- a/compiler/rustc_codegen_cranelift/src/allocator.rs
+++ b/compiler/rustc_codegen_cranelift/src/allocator.rs
@@ -24,7 +24,7 @@ pub(crate) fn codegen(
             unwind_context,
             kind,
             tcx.lang_items().oom().is_some(),
-            tcx.sess.opts.debugging_opts.oom,
+            tcx.sess.opts.unstable_opts.oom,
         );
         true
     } else {
diff --git a/compiler/rustc_codegen_cranelift/src/archive.rs b/compiler/rustc_codegen_cranelift/src/archive.rs
index 0812f930b5d..c92c1051139 100644
--- a/compiler/rustc_codegen_cranelift/src/archive.rs
+++ b/compiler/rustc_codegen_cranelift/src/archive.rs
@@ -86,7 +86,7 @@ impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> {
 
         let mut entries = Vec::new();
 
-        for (entry_name, entry) in self.entries {
+        for (mut entry_name, entry) in self.entries {
             // FIXME only read the symbol table of the object files to avoid having to keep all
             // object files in memory at once, or read them twice.
             let data = match entry {
@@ -109,6 +109,23 @@ impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> {
             };
 
             if !self.no_builtin_ranlib {
+                if symbol_table.contains_key(&entry_name) {
+                    // The ar crate can't handle creating a symbol table in case of multiple archive
+                    // members with the same name. Work around this by prepending a number until we
+                    // get a unique name.
+                    for i in 1.. {
+                        let new_name = format!("{}_", i)
+                            .into_bytes()
+                            .into_iter()
+                            .chain(entry_name.iter().copied())
+                            .collect::<Vec<_>>();
+                        if !symbol_table.contains_key(&new_name) {
+                            entry_name = new_name;
+                            break;
+                        }
+                    }
+                }
+
                 match object::File::parse(&*data) {
                     Ok(object) => {
                         symbol_table.insert(
@@ -204,12 +221,16 @@ impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> {
         any_members
     }
 
-    fn inject_dll_import_lib(
-        &mut self,
+    fn sess(&self) -> &Session {
+        self.sess
+    }
+
+    fn create_dll_import_lib(
+        _sess: &Session,
         _lib_name: &str,
         _dll_imports: &[rustc_session::cstore::DllImport],
-        _tmpdir: &rustc_data_structures::temp_dir::MaybeTempDir,
-    ) {
-        bug!("injecting dll imports is not supported");
+        _tmpdir: &Path,
+    ) -> PathBuf {
+        bug!("creating dll imports is not supported");
     }
 }
diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs
index 63cd4d6de4c..122e103ff62 100644
--- a/compiler/rustc_codegen_cranelift/src/base.rs
+++ b/compiler/rustc_codegen_cranelift/src/base.rs
@@ -175,10 +175,37 @@ fn compile_fn<'tcx>(
         );
     });
 
+    #[cfg(any())] // This is never true
+    let _clif_guard = {
+        use std::fmt::Write;
+
+        let func_clone = context.func.clone();
+        let clif_comments_clone = clif_comments.clone();
+        let mut clif = String::new();
+        for flag in module.isa().flags().iter() {
+            writeln!(clif, "set {}", flag).unwrap();
+        }
+        write!(clif, "target {}", module.isa().triple().architecture.to_string()).unwrap();
+        for isa_flag in module.isa().isa_flags().iter() {
+            write!(clif, " {}", isa_flag).unwrap();
+        }
+        writeln!(clif, "\n").unwrap();
+        crate::PrintOnPanic(move || {
+            let mut clif = clif.clone();
+            ::cranelift_codegen::write::decorate_function(
+                &mut &clif_comments_clone,
+                &mut clif,
+                &func_clone,
+            )
+            .unwrap();
+            clif
+        })
+    };
+
     // Define function
     tcx.sess.time("define function", || {
         context.want_disasm = crate::pretty_clif::should_write_ir(tcx);
-        module.define_function(func_id, context).unwrap()
+        module.define_function(func_id, context).unwrap();
     });
 
     // Write optimized function to file for debugging
@@ -815,15 +842,7 @@ pub(crate) fn codegen_place<'tcx>(
     for elem in place.projection {
         match elem {
             PlaceElem::Deref => {
-                if cplace.layout().ty.is_box() {
-                    cplace = cplace
-                        .place_field(fx, Field::new(0)) // Box<T> -> Unique<T>
-                        .place_field(fx, Field::new(0)) // Unique<T> -> NonNull<T>
-                        .place_field(fx, Field::new(0)) // NonNull<T> -> *mut T
-                        .place_deref(fx);
-                } else {
-                    cplace = cplace.place_deref(fx);
-                }
+                cplace = cplace.place_deref(fx);
             }
             PlaceElem::Field(field, _ty) => {
                 cplace = cplace.place_field(fx, field);
diff --git a/compiler/rustc_codegen_cranelift/src/cast.rs b/compiler/rustc_codegen_cranelift/src/cast.rs
index e19070774c6..bad5d1f08a9 100644
--- a/compiler/rustc_codegen_cranelift/src/cast.rs
+++ b/compiler/rustc_codegen_cranelift/src/cast.rs
@@ -144,22 +144,13 @@ pub(crate) fn clif_int_or_float_cast(
             fx.bcx.ins().fcvt_to_uint_sat(to_ty, from)
         };
 
-        if let Some(false) = fx.tcx.sess.opts.debugging_opts.saturating_float_casts {
+        if let Some(false) = fx.tcx.sess.opts.unstable_opts.saturating_float_casts {
             return val;
         }
 
         let is_not_nan = fx.bcx.ins().fcmp(FloatCC::Equal, from, from);
-        if to_ty == types::I128 {
-            // FIXME(bytecodealliance/wasmtime#3963): select.i128 on fcmp eq miscompiles
-            let (lsb, msb) = fx.bcx.ins().isplit(val);
-            let zero = fx.bcx.ins().iconst(types::I64, 0);
-            let lsb = fx.bcx.ins().select(is_not_nan, lsb, zero);
-            let msb = fx.bcx.ins().select(is_not_nan, msb, zero);
-            fx.bcx.ins().iconcat(lsb, msb)
-        } else {
-            let zero = fx.bcx.ins().iconst(to_ty, 0);
-            fx.bcx.ins().select(is_not_nan, val, zero)
-        }
+        let zero = fx.bcx.ins().iconst(to_ty, 0);
+        fx.bcx.ins().select(is_not_nan, val, zero)
     } else if from_ty.is_float() && to_ty.is_float() {
         // float -> float
         match (from_ty, to_ty) {
diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs
index 48972321a9f..7f7fd0e9c57 100644
--- a/compiler/rustc_codegen_cranelift/src/constant.rs
+++ b/compiler/rustc_codegen_cranelift/src/constant.rs
@@ -195,9 +195,8 @@ pub(crate) fn codegen_const_value<'tcx>(
             }
             Scalar::Ptr(ptr, _size) => {
                 let (alloc_id, offset) = ptr.into_parts(); // we know the `offset` is relative
-                let alloc_kind = fx.tcx.get_global_alloc(alloc_id);
-                let base_addr = match alloc_kind {
-                    Some(GlobalAlloc::Memory(alloc)) => {
+                let base_addr = match fx.tcx.global_alloc(alloc_id) {
+                    GlobalAlloc::Memory(alloc) => {
                         let data_id = data_id_for_alloc_id(
                             &mut fx.constants_cx,
                             fx.module,
@@ -211,13 +210,27 @@ pub(crate) fn codegen_const_value<'tcx>(
                         }
                         fx.bcx.ins().global_value(fx.pointer_type, local_data_id)
                     }
-                    Some(GlobalAlloc::Function(instance)) => {
+                    GlobalAlloc::Function(instance) => {
                         let func_id = crate::abi::import_function(fx.tcx, fx.module, instance);
                         let local_func_id =
                             fx.module.declare_func_in_func(func_id, &mut fx.bcx.func);
                         fx.bcx.ins().func_addr(fx.pointer_type, local_func_id)
                     }
-                    Some(GlobalAlloc::Static(def_id)) => {
+                    GlobalAlloc::VTable(ty, trait_ref) => {
+                        let alloc_id = fx.tcx.vtable_allocation((ty, trait_ref));
+                        let alloc = fx.tcx.global_alloc(alloc_id).unwrap_memory();
+                        // FIXME: factor this common code with the `Memory` arm into a function?
+                        let data_id = data_id_for_alloc_id(
+                            &mut fx.constants_cx,
+                            fx.module,
+                            alloc_id,
+                            alloc.inner().mutability,
+                        );
+                        let local_data_id =
+                            fx.module.declare_data_in_func(data_id, &mut fx.bcx.func);
+                        fx.bcx.ins().global_value(fx.pointer_type, local_data_id)
+                    }
+                    GlobalAlloc::Static(def_id) => {
                         assert!(fx.tcx.is_static(def_id));
                         let data_id = data_id_for_static(fx.tcx, fx.module, def_id, false);
                         let local_data_id =
@@ -227,7 +240,6 @@ pub(crate) fn codegen_const_value<'tcx>(
                         }
                         fx.bcx.ins().global_value(fx.pointer_type, local_data_id)
                     }
-                    None => bug!("missing allocation {:?}", alloc_id),
                 };
                 let val = if offset.bytes() != 0 {
                     fx.bcx.ins().iadd_imm(base_addr, i64::try_from(offset.bytes()).unwrap())
@@ -316,14 +328,18 @@ fn data_id_for_static(
 
     let attrs = tcx.codegen_fn_attrs(def_id);
 
-    let data_id = module
-        .declare_data(
-            &*symbol_name,
-            linkage,
-            is_mutable,
-            attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL),
-        )
-        .unwrap();
+    let data_id = match module.declare_data(
+        &*symbol_name,
+        linkage,
+        is_mutable,
+        attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL),
+    ) {
+        Ok(data_id) => data_id,
+        Err(ModuleError::IncompatibleDeclaration(_)) => tcx.sess.fatal(&format!(
+            "attempt to declare `{symbol_name}` as static, but it was already declared as function"
+        )),
+        Err(err) => Err::<_, _>(err).unwrap(),
+    };
 
     if rlinkage.is_some() {
         // Comment copied from https://github.com/rust-lang/rust/blob/45060c2a66dfd667f88bd8b94261b28a58d85bd5/src/librustc_codegen_llvm/consts.rs#L141
@@ -357,10 +373,11 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant
     while let Some(todo_item) = cx.todo.pop() {
         let (data_id, alloc, section_name) = match todo_item {
             TodoItem::Alloc(alloc_id) => {
-                //println!("alloc_id {}", alloc_id);
-                let alloc = match tcx.get_global_alloc(alloc_id).unwrap() {
+                let alloc = match tcx.global_alloc(alloc_id) {
                     GlobalAlloc::Memory(alloc) => alloc,
-                    GlobalAlloc::Function(_) | GlobalAlloc::Static(_) => unreachable!(),
+                    GlobalAlloc::Function(_) | GlobalAlloc::Static(_) | GlobalAlloc::VTable(..) => {
+                        unreachable!()
+                    }
                 };
                 let data_id = *cx.anon_allocs.entry(alloc_id).or_insert_with(|| {
                     module
@@ -424,11 +441,12 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant
                 read_target_uint(endianness, bytes).unwrap()
             };
 
-            let reloc_target_alloc = tcx.get_global_alloc(alloc_id).unwrap();
+            let reloc_target_alloc = tcx.global_alloc(alloc_id);
             let data_id = match reloc_target_alloc {
                 GlobalAlloc::Function(instance) => {
                     assert_eq!(addend, 0);
-                    let func_id = crate::abi::import_function(tcx, module, instance);
+                    let func_id =
+                        crate::abi::import_function(tcx, module, instance.polymorphize(tcx));
                     let local_func_id = module.declare_func_in_data(func_id, &mut data_ctx);
                     data_ctx.write_function_addr(offset.bytes() as u32, local_func_id);
                     continue;
@@ -436,6 +454,10 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant
                 GlobalAlloc::Memory(target_alloc) => {
                     data_id_for_alloc_id(cx, module, alloc_id, target_alloc.inner().mutability)
                 }
+                GlobalAlloc::VTable(ty, trait_ref) => {
+                    let alloc_id = tcx.vtable_allocation((ty, trait_ref));
+                    data_id_for_alloc_id(cx, module, alloc_id, Mutability::Not)
+                }
                 GlobalAlloc::Static(def_id) => {
                     if tcx.codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::THREAD_LOCAL)
                     {
diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs
index 476d6a54e12..bbcb9591373 100644
--- a/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs
+++ b/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs
@@ -140,7 +140,7 @@ impl<'tcx> DebugContext<'tcx> {
             // In order to have a good line stepping behavior in debugger, we overwrite debug
             // locations of macro expansions with that of the outermost expansion site
             // (unless the crate is being compiled with `-Z debug-macros`).
-            let span = if !span.from_expansion() || tcx.sess.opts.debugging_opts.debug_macros {
+            let span = if !span.from_expansion() || tcx.sess.opts.unstable_opts.debug_macros {
                 span
             } else {
                 // Walk up the macro expansion chain until we reach a non-expanded span.
diff --git a/compiler/rustc_codegen_cranelift/src/driver/aot.rs b/compiler/rustc_codegen_cranelift/src/driver/aot.rs
index 50d8fc30d7d..3cd1ef5639e 100644
--- a/compiler/rustc_codegen_cranelift/src/driver/aot.rs
+++ b/compiler/rustc_codegen_cranelift/src/driver/aot.rs
@@ -33,7 +33,7 @@ fn make_module(sess: &Session, isa: Box<dyn TargetIsa>, name: String) -> ObjectM
     // Unlike cg_llvm, cg_clif defaults to disabling -Zfunction-sections. For cg_llvm binary size
     // is important, while cg_clif cares more about compilation times. Enabling -Zfunction-sections
     // can easily double the amount of time necessary to perform linking.
-    builder.per_function_section(sess.opts.debugging_opts.function_sections.unwrap_or(false));
+    builder.per_function_section(sess.opts.unstable_opts.function_sections.unwrap_or(false));
     ObjectModule::new(builder)
 }
 
diff --git a/compiler/rustc_codegen_cranelift/src/inline_asm.rs b/compiler/rustc_codegen_cranelift/src/inline_asm.rs
index deac5dfd3ec..241de5e3653 100644
--- a/compiler/rustc_codegen_cranelift/src/inline_asm.rs
+++ b/compiler/rustc_codegen_cranelift/src/inline_asm.rs
@@ -18,86 +18,96 @@ pub(crate) fn codegen_inline_asm<'tcx>(
 ) {
     // FIXME add .eh_frame unwind info directives
 
-    if template[0] == InlineAsmTemplatePiece::String("int $$0x29".to_string()) {
-        let true_ = fx.bcx.ins().iconst(types::I32, 1);
-        fx.bcx.ins().trapnz(true_, TrapCode::User(1));
-        return;
-    } else if template[0] == InlineAsmTemplatePiece::String("movq %rbx, ".to_string())
-        && matches!(
-            template[1],
-            InlineAsmTemplatePiece::Placeholder { operand_idx: 0, modifier: Some('r'), span: _ }
-        )
-        && template[2] == InlineAsmTemplatePiece::String("\n".to_string())
-        && template[3] == InlineAsmTemplatePiece::String("cpuid".to_string())
-        && template[4] == InlineAsmTemplatePiece::String("\n".to_string())
-        && template[5] == InlineAsmTemplatePiece::String("xchgq %rbx, ".to_string())
-        && matches!(
-            template[6],
-            InlineAsmTemplatePiece::Placeholder { operand_idx: 0, modifier: Some('r'), span: _ }
-        )
-    {
-        assert_eq!(operands.len(), 4);
-        let (leaf, eax_place) = match operands[1] {
-            InlineAsmOperand::InOut { reg, late: true, ref in_value, out_place } => {
-                assert_eq!(
-                    reg,
-                    InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::ax))
-                );
-                (
-                    crate::base::codegen_operand(fx, in_value).load_scalar(fx),
-                    crate::base::codegen_place(fx, out_place.unwrap()),
-                )
-            }
-            _ => unreachable!(),
-        };
-        let ebx_place = match operands[0] {
-            InlineAsmOperand::Out { reg, late: true, place } => {
-                assert_eq!(
-                    reg,
-                    InlineAsmRegOrRegClass::RegClass(InlineAsmRegClass::X86(
-                        X86InlineAsmRegClass::reg
-                    ))
-                );
-                crate::base::codegen_place(fx, place.unwrap())
-            }
-            _ => unreachable!(),
-        };
-        let (sub_leaf, ecx_place) = match operands[2] {
-            InlineAsmOperand::InOut { reg, late: true, ref in_value, out_place } => {
-                assert_eq!(
-                    reg,
-                    InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::cx))
-                );
-                (
-                    crate::base::codegen_operand(fx, in_value).load_scalar(fx),
-                    crate::base::codegen_place(fx, out_place.unwrap()),
-                )
-            }
-            _ => unreachable!(),
-        };
-        let edx_place = match operands[3] {
-            InlineAsmOperand::Out { reg, late: true, place } => {
-                assert_eq!(
-                    reg,
-                    InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::dx))
-                );
-                crate::base::codegen_place(fx, place.unwrap())
-            }
-            _ => unreachable!(),
-        };
-
-        let (eax, ebx, ecx, edx) = crate::intrinsics::codegen_cpuid_call(fx, leaf, sub_leaf);
-
-        eax_place.write_cvalue(fx, CValue::by_val(eax, fx.layout_of(fx.tcx.types.u32)));
-        ebx_place.write_cvalue(fx, CValue::by_val(ebx, fx.layout_of(fx.tcx.types.u32)));
-        ecx_place.write_cvalue(fx, CValue::by_val(ecx, fx.layout_of(fx.tcx.types.u32)));
-        edx_place.write_cvalue(fx, CValue::by_val(edx, fx.layout_of(fx.tcx.types.u32)));
-        return;
-    } else if fx.tcx.symbol_name(fx.instance).name.starts_with("___chkstk") {
-        // ___chkstk, ___chkstk_ms and __alloca are only used on Windows
-        crate::trap::trap_unimplemented(fx, "Stack probes are not supported");
-    } else if fx.tcx.symbol_name(fx.instance).name == "__alloca" {
-        crate::trap::trap_unimplemented(fx, "Alloca is not supported");
+    if !template.is_empty() {
+        if template[0] == InlineAsmTemplatePiece::String("int $$0x29".to_string()) {
+            let true_ = fx.bcx.ins().iconst(types::I32, 1);
+            fx.bcx.ins().trapnz(true_, TrapCode::User(1));
+            return;
+        } else if template[0] == InlineAsmTemplatePiece::String("movq %rbx, ".to_string())
+            && matches!(
+                template[1],
+                InlineAsmTemplatePiece::Placeholder {
+                    operand_idx: 0,
+                    modifier: Some('r'),
+                    span: _
+                }
+            )
+            && template[2] == InlineAsmTemplatePiece::String("\n".to_string())
+            && template[3] == InlineAsmTemplatePiece::String("cpuid".to_string())
+            && template[4] == InlineAsmTemplatePiece::String("\n".to_string())
+            && template[5] == InlineAsmTemplatePiece::String("xchgq %rbx, ".to_string())
+            && matches!(
+                template[6],
+                InlineAsmTemplatePiece::Placeholder {
+                    operand_idx: 0,
+                    modifier: Some('r'),
+                    span: _
+                }
+            )
+        {
+            assert_eq!(operands.len(), 4);
+            let (leaf, eax_place) = match operands[1] {
+                InlineAsmOperand::InOut { reg, late: true, ref in_value, out_place } => {
+                    assert_eq!(
+                        reg,
+                        InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::ax))
+                    );
+                    (
+                        crate::base::codegen_operand(fx, in_value).load_scalar(fx),
+                        crate::base::codegen_place(fx, out_place.unwrap()),
+                    )
+                }
+                _ => unreachable!(),
+            };
+            let ebx_place = match operands[0] {
+                InlineAsmOperand::Out { reg, late: true, place } => {
+                    assert_eq!(
+                        reg,
+                        InlineAsmRegOrRegClass::RegClass(InlineAsmRegClass::X86(
+                            X86InlineAsmRegClass::reg
+                        ))
+                    );
+                    crate::base::codegen_place(fx, place.unwrap())
+                }
+                _ => unreachable!(),
+            };
+            let (sub_leaf, ecx_place) = match operands[2] {
+                InlineAsmOperand::InOut { reg, late: true, ref in_value, out_place } => {
+                    assert_eq!(
+                        reg,
+                        InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::cx))
+                    );
+                    (
+                        crate::base::codegen_operand(fx, in_value).load_scalar(fx),
+                        crate::base::codegen_place(fx, out_place.unwrap()),
+                    )
+                }
+                _ => unreachable!(),
+            };
+            let edx_place = match operands[3] {
+                InlineAsmOperand::Out { reg, late: true, place } => {
+                    assert_eq!(
+                        reg,
+                        InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::dx))
+                    );
+                    crate::base::codegen_place(fx, place.unwrap())
+                }
+                _ => unreachable!(),
+            };
+
+            let (eax, ebx, ecx, edx) = crate::intrinsics::codegen_cpuid_call(fx, leaf, sub_leaf);
+
+            eax_place.write_cvalue(fx, CValue::by_val(eax, fx.layout_of(fx.tcx.types.u32)));
+            ebx_place.write_cvalue(fx, CValue::by_val(ebx, fx.layout_of(fx.tcx.types.u32)));
+            ecx_place.write_cvalue(fx, CValue::by_val(ecx, fx.layout_of(fx.tcx.types.u32)));
+            edx_place.write_cvalue(fx, CValue::by_val(edx, fx.layout_of(fx.tcx.types.u32)));
+            return;
+        } else if fx.tcx.symbol_name(fx.instance).name.starts_with("___chkstk") {
+            // ___chkstk, ___chkstk_ms and __alloca are only used on Windows
+            crate::trap::trap_unimplemented(fx, "Stack probes are not supported");
+        } else if fx.tcx.symbol_name(fx.instance).name == "__alloca" {
+            crate::trap::trap_unimplemented(fx, "Alloca is not supported");
+        }
     }
 
     let mut inputs = Vec::new();
diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs
index 77ac46540a9..869670c8cfa 100644
--- a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs
+++ b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs
@@ -13,15 +13,11 @@ pub(crate) fn codegen_llvm_intrinsic_call<'tcx>(
     ret: CPlace<'tcx>,
     target: Option<BasicBlock>,
 ) {
-    intrinsic_match! {
-        fx, intrinsic, args,
-        _ => {
-            fx.tcx.sess.warn(&format!("unsupported llvm intrinsic {}; replacing with trap", intrinsic));
-            crate::trap::trap_unimplemented(fx, intrinsic);
-        };
-
+    match intrinsic {
         // Used by `_mm_movemask_epi8` and `_mm256_movemask_epi8`
-        "llvm.x86.sse2.pmovmskb.128" | "llvm.x86.avx2.pmovmskb" | "llvm.x86.sse2.movmsk.pd", (c a) {
+        "llvm.x86.sse2.pmovmskb.128" | "llvm.x86.avx2.pmovmskb" | "llvm.x86.sse2.movmsk.pd" => {
+            intrinsic_args!(fx, args => (a); intrinsic);
+
             let (lane_count, lane_ty) = a.layout().ty.simd_size_and_type(fx.tcx);
             let lane_ty = fx.clif_type(lane_ty).unwrap();
             assert!(lane_count <= 32);
@@ -29,7 +25,8 @@ pub(crate) fn codegen_llvm_intrinsic_call<'tcx>(
             let mut res = fx.bcx.ins().iconst(types::I32, 0);
 
             for lane in (0..lane_count).rev() {
-                let a_lane = a.value_field(fx, mir::Field::new(lane.try_into().unwrap())).load_scalar(fx);
+                let a_lane =
+                    a.value_field(fx, mir::Field::new(lane.try_into().unwrap())).load_scalar(fx);
 
                 // cast float to int
                 let a_lane = match lane_ty {
@@ -49,26 +46,29 @@ pub(crate) fn codegen_llvm_intrinsic_call<'tcx>(
 
             let res = CValue::by_val(res, fx.layout_of(fx.tcx.types.i32));
             ret.write_cvalue(fx, res);
-        };
-        "llvm.x86.sse2.cmp.ps" | "llvm.x86.sse2.cmp.pd", (c x, c y, o kind) {
-            let kind = crate::constant::mir_operand_get_const_val(fx, kind).expect("llvm.x86.sse2.cmp.* kind not const");
-            let flt_cc = match kind.try_to_bits(Size::from_bytes(1)).unwrap_or_else(|| panic!("kind not scalar: {:?}", kind)) {
+        }
+        "llvm.x86.sse2.cmp.ps" | "llvm.x86.sse2.cmp.pd" => {
+            let (x, y, kind) = match args {
+                [x, y, kind] => (x, y, kind),
+                _ => bug!("wrong number of args for intrinsic {intrinsic}"),
+            };
+            let x = codegen_operand(fx, x);
+            let y = codegen_operand(fx, y);
+            let kind = crate::constant::mir_operand_get_const_val(fx, kind)
+                .expect("llvm.x86.sse2.cmp.* kind not const");
+
+            let flt_cc = match kind
+                .try_to_bits(Size::from_bytes(1))
+                .unwrap_or_else(|| panic!("kind not scalar: {:?}", kind))
+            {
                 0 => FloatCC::Equal,
                 1 => FloatCC::LessThan,
                 2 => FloatCC::LessThanOrEqual,
-                7 => {
-                    unimplemented!("Compares corresponding elements in `a` and `b` to see if neither is `NaN`.");
-                }
-                3 => {
-                    unimplemented!("Compares corresponding elements in `a` and `b` to see if either is `NaN`.");
-                }
+                7 => FloatCC::Ordered,
+                3 => FloatCC::Unordered,
                 4 => FloatCC::NotEqual,
-                5 => {
-                    unimplemented!("not less than");
-                }
-                6 => {
-                    unimplemented!("not less than or equal");
-                }
+                5 => FloatCC::UnorderedOrGreaterThanOrEqual,
+                6 => FloatCC::UnorderedOrGreaterThan,
                 kind => unreachable!("kind {:?}", kind),
             };
 
@@ -79,50 +79,67 @@ pub(crate) fn codegen_llvm_intrinsic_call<'tcx>(
                 };
                 bool_to_zero_or_max_uint(fx, res_lane_ty, res_lane)
             });
-        };
-        "llvm.x86.sse2.psrli.d", (c a, o imm8) {
-            let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8).expect("llvm.x86.sse2.psrli.d imm8 not const");
-            simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| {
-                match imm8.try_to_bits(Size::from_bytes(4)).unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8)) {
-                    imm8 if imm8 < 32 => fx.bcx.ins().ushr_imm(lane, i64::from(imm8 as u8)),
-                    _ => fx.bcx.ins().iconst(types::I32, 0),
-                }
+        }
+        "llvm.x86.sse2.psrli.d" => {
+            let (a, imm8) = match args {
+                [a, imm8] => (a, imm8),
+                _ => bug!("wrong number of args for intrinsic {intrinsic}"),
+            };
+            let a = codegen_operand(fx, a);
+            let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8)
+                .expect("llvm.x86.sse2.psrli.d imm8 not const");
+
+            simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| match imm8
+                .try_to_bits(Size::from_bytes(4))
+                .unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8))
+            {
+                imm8 if imm8 < 32 => fx.bcx.ins().ushr_imm(lane, i64::from(imm8 as u8)),
+                _ => fx.bcx.ins().iconst(types::I32, 0),
             });
-        };
-        "llvm.x86.sse2.pslli.d", (c a, o imm8) {
-            let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8).expect("llvm.x86.sse2.psrli.d imm8 not const");
-            simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| {
-                match imm8.try_to_bits(Size::from_bytes(4)).unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8)) {
-                    imm8 if imm8 < 32 => fx.bcx.ins().ishl_imm(lane, i64::from(imm8 as u8)),
-                    _ => fx.bcx.ins().iconst(types::I32, 0),
-                }
+        }
+        "llvm.x86.sse2.pslli.d" => {
+            let (a, imm8) = match args {
+                [a, imm8] => (a, imm8),
+                _ => bug!("wrong number of args for intrinsic {intrinsic}"),
+            };
+            let a = codegen_operand(fx, a);
+            let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8)
+                .expect("llvm.x86.sse2.psrli.d imm8 not const");
+
+            simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| match imm8
+                .try_to_bits(Size::from_bytes(4))
+                .unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8))
+            {
+                imm8 if imm8 < 32 => fx.bcx.ins().ishl_imm(lane, i64::from(imm8 as u8)),
+                _ => fx.bcx.ins().iconst(types::I32, 0),
             });
-        };
-        "llvm.x86.sse2.storeu.dq", (v mem_addr, c a) {
+        }
+        "llvm.x86.sse2.storeu.dq" => {
+            intrinsic_args!(fx, args => (mem_addr, a); intrinsic);
+            let mem_addr = mem_addr.load_scalar(fx);
+
             // FIXME correctly handle the unalignment
             let dest = CPlace::for_ptr(Pointer::new(mem_addr), a.layout());
             dest.write_cvalue(fx, a);
-        };
-        "llvm.x86.addcarry.64", (v c_in, c a, c b) {
-            llvm_add_sub(
-                fx,
-                BinOp::Add,
-                ret,
-                c_in,
-                a,
-                b
-            );
-        };
-        "llvm.x86.subborrow.64", (v b_in, c a, c b) {
-            llvm_add_sub(
-                fx,
-                BinOp::Sub,
-                ret,
-                b_in,
-                a,
-                b
-            );
-        };
+        }
+        "llvm.x86.addcarry.64" => {
+            intrinsic_args!(fx, args => (c_in, a, b); intrinsic);
+            let c_in = c_in.load_scalar(fx);
+
+            llvm_add_sub(fx, BinOp::Add, ret, c_in, a, b);
+        }
+        "llvm.x86.subborrow.64" => {
+            intrinsic_args!(fx, args => (b_in, a, b); intrinsic);
+            let b_in = b_in.load_scalar(fx);
+
+            llvm_add_sub(fx, BinOp::Sub, ret, b_in, a, b);
+        }
+        _ => {
+            fx.tcx
+                .sess
+                .warn(&format!("unsupported llvm intrinsic {}; replacing with trap", intrinsic));
+            crate::trap::trap_unimplemented(fx, intrinsic);
+        }
     }
 
     let dest = target.expect("all llvm intrinsics used by stdlib should return");
diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs
index 6937e658ed5..b2a83e1d4eb 100644
--- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs
+++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs
@@ -1,50 +1,14 @@
 //! Codegen of intrinsics. This includes `extern "rust-intrinsic"`, `extern "platform-intrinsic"`
 //! and LLVM intrinsics that have symbol names starting with `llvm.`.
 
-macro_rules! intrinsic_pat {
-    (_) => {
-        _
-    };
-    ($name:ident) => {
-        sym::$name
-    };
-    (kw.$name:ident) => {
-        kw::$name
-    };
-    ($name:literal) => {
-        $name
-    };
-}
-
-macro_rules! intrinsic_arg {
-    (o $fx:expr, $arg:ident) => {};
-    (c $fx:expr, $arg:ident) => {
-        let $arg = codegen_operand($fx, $arg);
-    };
-    (v $fx:expr, $arg:ident) => {
-        let $arg = codegen_operand($fx, $arg).load_scalar($fx);
-    };
-}
-
-macro_rules! intrinsic_match {
-    ($fx:expr, $intrinsic:expr, $args:expr,
-    _ => $unknown:block;
-    $(
-        $($($name:tt).*)|+ $(if $cond:expr)?, ($($a:ident $arg:ident),*) $content:block;
-    )*) => {
-        match $intrinsic {
-            $(
-                $(intrinsic_pat!($($name).*))|* $(if $cond)? => {
-                    if let [$($arg),*] = $args {
-                        $(intrinsic_arg!($a $fx, $arg);)*
-                        $content
-                    } else {
-                        bug!("wrong number of args for intrinsic {:?}", $intrinsic);
-                    }
-                }
-            )*
-            _ => $unknown,
-        }
+macro_rules! intrinsic_args {
+    ($fx:expr, $args:expr => ($($arg:tt),*); $intrinsic:expr) => {
+        #[allow(unused_parens)]
+        let ($($arg),*) = if let [$($arg),*] = $args {
+            ($(codegen_operand($fx, $arg)),*)
+        } else {
+            $crate::intrinsics::bug_on_incorrect_arg_count($intrinsic);
+        };
     }
 }
 
@@ -58,11 +22,14 @@ pub(crate) use llvm::codegen_llvm_intrinsic_call;
 use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_middle::ty::subst::SubstsRef;
 use rustc_span::symbol::{kw, sym, Symbol};
-use rustc_target::abi::InitKind;
 
 use crate::prelude::*;
 use cranelift_codegen::ir::AtomicRmwOp;
 
+fn bug_on_incorrect_arg_count(intrinsic: impl std::fmt::Display) -> ! {
+    bug!("wrong number of args for intrinsic {}", intrinsic);
+}
+
 fn report_atomic_type_validation_error<'tcx>(
     fx: &mut FunctionCx<'_, '_, 'tcx>,
     intrinsic: Symbol,
@@ -352,28 +319,31 @@ fn codegen_regular_intrinsic_call<'tcx>(
 ) {
     let usize_layout = fx.layout_of(fx.tcx.types.usize);
 
-    intrinsic_match! {
-        fx, intrinsic, args,
-        _ => {
-            fx.tcx.sess.span_fatal(source_info.span, &format!("unsupported intrinsic {}", intrinsic));
-        };
+    match intrinsic {
+        sym::assume => {
+            intrinsic_args!(fx, args => (_a); intrinsic);
+        }
+        sym::likely | sym::unlikely => {
+            intrinsic_args!(fx, args => (a); intrinsic);
 
-        assume, (c _a) {};
-        likely | unlikely, (c a) {
             ret.write_cvalue(fx, a);
-        };
-        breakpoint, () {
+        }
+        sym::breakpoint => {
+            intrinsic_args!(fx, args => (); intrinsic);
+
             fx.bcx.ins().debugtrap();
-        };
-        copy | copy_nonoverlapping, (v src, v dst, v count) {
+        }
+        sym::copy | sym::copy_nonoverlapping => {
+            intrinsic_args!(fx, args => (src, dst, count); intrinsic);
+            let src = src.load_scalar(fx);
+            let dst = dst.load_scalar(fx);
+            let count = count.load_scalar(fx);
+
             let elem_ty = substs.type_at(0);
             let elem_size: u64 = fx.layout_of(elem_ty).size.bytes();
             assert_eq!(args.len(), 3);
-            let byte_amount = if elem_size != 1 {
-                fx.bcx.ins().imul_imm(count, elem_size as i64)
-            } else {
-                count
-            };
+            let byte_amount =
+                if elem_size != 1 { fx.bcx.ins().imul_imm(count, elem_size as i64) } else { count };
 
             if intrinsic == sym::copy_nonoverlapping {
                 // FIXME emit_small_memcpy
@@ -382,17 +352,19 @@ fn codegen_regular_intrinsic_call<'tcx>(
                 // FIXME emit_small_memmove
                 fx.bcx.call_memmove(fx.target_config, dst, src, byte_amount);
             }
-        };
-        // NOTE: the volatile variants have src and dst swapped
-        volatile_copy_memory | volatile_copy_nonoverlapping_memory, (v dst, v src, v count) {
+        }
+        sym::volatile_copy_memory | sym::volatile_copy_nonoverlapping_memory => {
+            // NOTE: the volatile variants have src and dst swapped
+            intrinsic_args!(fx, args => (dst, src, count); intrinsic);
+            let dst = dst.load_scalar(fx);
+            let src = src.load_scalar(fx);
+            let count = count.load_scalar(fx);
+
             let elem_ty = substs.type_at(0);
             let elem_size: u64 = fx.layout_of(elem_ty).size.bytes();
             assert_eq!(args.len(), 3);
-            let byte_amount = if elem_size != 1 {
-                fx.bcx.ins().imul_imm(count, elem_size as i64)
-            } else {
-                count
-            };
+            let byte_amount =
+                if elem_size != 1 { fx.bcx.ins().imul_imm(count, elem_size as i64) } else { count };
 
             // FIXME make the copy actually volatile when using emit_small_mem{cpy,move}
             if intrinsic == sym::volatile_copy_nonoverlapping_memory {
@@ -402,38 +374,64 @@ fn codegen_regular_intrinsic_call<'tcx>(
                 // FIXME emit_small_memmove
                 fx.bcx.call_memmove(fx.target_config, dst, src, byte_amount);
             }
-        };
-        size_of_val, (c ptr) {
+        }
+        sym::size_of_val => {
+            intrinsic_args!(fx, args => (ptr); intrinsic);
+
             let layout = fx.layout_of(substs.type_at(0));
-            let size = if layout.is_unsized() {
+            // Note: Can't use is_unsized here as truly unsized types need to take the fixed size
+            // branch
+            let size = if let Abi::ScalarPair(_, _) = ptr.layout().abi {
                 let (_ptr, info) = ptr.load_scalar_pair(fx);
                 let (size, _align) = crate::unsize::size_and_align_of_dst(fx, layout, info);
                 size
             } else {
-                fx
-                    .bcx
-                    .ins()
-                    .iconst(fx.pointer_type, layout.size.bytes() as i64)
+                fx.bcx.ins().iconst(fx.pointer_type, layout.size.bytes() as i64)
             };
             ret.write_cvalue(fx, CValue::by_val(size, usize_layout));
-        };
-        min_align_of_val, (c ptr) {
+        }
+        sym::min_align_of_val => {
+            intrinsic_args!(fx, args => (ptr); intrinsic);
+
             let layout = fx.layout_of(substs.type_at(0));
-            let align = if layout.is_unsized() {
+            // Note: Can't use is_unsized here as truly unsized types need to take the fixed size
+            // branch
+            let align = if let Abi::ScalarPair(_, _) = ptr.layout().abi {
                 let (_ptr, info) = ptr.load_scalar_pair(fx);
                 let (_size, align) = crate::unsize::size_and_align_of_dst(fx, layout, info);
                 align
             } else {
-                fx
-                    .bcx
-                    .ins()
-                    .iconst(fx.pointer_type, layout.align.abi.bytes() as i64)
+                fx.bcx.ins().iconst(fx.pointer_type, layout.align.abi.bytes() as i64)
             };
             ret.write_cvalue(fx, CValue::by_val(align, usize_layout));
-        };
+        }
+
+        sym::vtable_size => {
+            intrinsic_args!(fx, args => (vtable); intrinsic);
+            let vtable = vtable.load_scalar(fx);
+
+            let size = crate::vtable::size_of_obj(fx, vtable);
+            ret.write_cvalue(fx, CValue::by_val(size, usize_layout));
+        }
+
+        sym::vtable_align => {
+            intrinsic_args!(fx, args => (vtable); intrinsic);
+            let vtable = vtable.load_scalar(fx);
+
+            let align = crate::vtable::min_align_of_obj(fx, vtable);
+            ret.write_cvalue(fx, CValue::by_val(align, usize_layout));
+        }
+
+        sym::unchecked_add
+        | sym::unchecked_sub
+        | sym::unchecked_mul
+        | sym::unchecked_div
+        | sym::exact_div
+        | sym::unchecked_rem
+        | sym::unchecked_shl
+        | sym::unchecked_shr => {
+            intrinsic_args!(fx, args => (x, y); intrinsic);
 
-        unchecked_add | unchecked_sub | unchecked_mul | unchecked_div | exact_div | unchecked_rem
-        | unchecked_shl | unchecked_shr, (c x, c y) {
             // FIXME trap on overflow
             let bin_op = match intrinsic {
                 sym::unchecked_add => BinOp::Add,
@@ -447,8 +445,10 @@ fn codegen_regular_intrinsic_call<'tcx>(
             };
             let res = crate::num::codegen_int_binop(fx, bin_op, x, y);
             ret.write_cvalue(fx, res);
-        };
-        add_with_overflow | sub_with_overflow | mul_with_overflow, (c x, c y) {
+        }
+        sym::add_with_overflow | sym::sub_with_overflow | sym::mul_with_overflow => {
+            intrinsic_args!(fx, args => (x, y); intrinsic);
+
             assert_eq!(x.layout().ty, y.layout().ty);
             let bin_op = match intrinsic {
                 sym::add_with_overflow => BinOp::Add,
@@ -457,15 +457,12 @@ fn codegen_regular_intrinsic_call<'tcx>(
                 _ => unreachable!(),
             };
 
-            let res = crate::num::codegen_checked_int_binop(
-                fx,
-                bin_op,
-                x,
-                y,
-            );
+            let res = crate::num::codegen_checked_int_binop(fx, bin_op, x, y);
             ret.write_cvalue(fx, res);
-        };
-        saturating_add | saturating_sub, (c lhs, c rhs) {
+        }
+        sym::saturating_add | sym::saturating_sub => {
+            intrinsic_args!(fx, args => (lhs, rhs); intrinsic);
+
             assert_eq!(lhs.layout().ty, rhs.layout().ty);
             let bin_op = match intrinsic {
                 sym::saturating_add => BinOp::Add,
@@ -475,12 +472,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
 
             let signed = type_sign(lhs.layout().ty);
 
-            let checked_res = crate::num::codegen_checked_int_binop(
-                fx,
-                bin_op,
-                lhs,
-                rhs,
-            );
+            let checked_res = crate::num::codegen_checked_int_binop(fx, bin_op, lhs, rhs);
 
             let (val, has_overflow) = checked_res.load_scalar_pair(fx);
             let clif_ty = fx.clif_type(lhs.layout().ty).unwrap();
@@ -492,13 +484,15 @@ fn codegen_regular_intrinsic_call<'tcx>(
                 (sym::saturating_sub, false) => fx.bcx.ins().select(has_overflow, min, val),
                 (sym::saturating_add, true) => {
                     let rhs = rhs.load_scalar(fx);
-                    let rhs_ge_zero = fx.bcx.ins().icmp_imm(IntCC::SignedGreaterThanOrEqual, rhs, 0);
+                    let rhs_ge_zero =
+                        fx.bcx.ins().icmp_imm(IntCC::SignedGreaterThanOrEqual, rhs, 0);
                     let sat_val = fx.bcx.ins().select(rhs_ge_zero, max, min);
                     fx.bcx.ins().select(has_overflow, sat_val, val)
                 }
                 (sym::saturating_sub, true) => {
                     let rhs = rhs.load_scalar(fx);
-                    let rhs_ge_zero = fx.bcx.ins().icmp_imm(IntCC::SignedGreaterThanOrEqual, rhs, 0);
+                    let rhs_ge_zero =
+                        fx.bcx.ins().icmp_imm(IntCC::SignedGreaterThanOrEqual, rhs, 0);
                     let sat_val = fx.bcx.ins().select(rhs_ge_zero, min, max);
                     fx.bcx.ins().select(has_overflow, sat_val, val)
                 }
@@ -508,23 +502,32 @@ fn codegen_regular_intrinsic_call<'tcx>(
             let res = CValue::by_val(val, lhs.layout());
 
             ret.write_cvalue(fx, res);
-        };
-        rotate_left, (c x, v y) {
+        }
+        sym::rotate_left => {
+            intrinsic_args!(fx, args => (x, y); intrinsic);
+            let y = y.load_scalar(fx);
+
             let layout = x.layout();
             let x = x.load_scalar(fx);
             let res = fx.bcx.ins().rotl(x, y);
             ret.write_cvalue(fx, CValue::by_val(res, layout));
-        };
-        rotate_right, (c x, v y) {
+        }
+        sym::rotate_right => {
+            intrinsic_args!(fx, args => (x, y); intrinsic);
+            let y = y.load_scalar(fx);
+
             let layout = x.layout();
             let x = x.load_scalar(fx);
             let res = fx.bcx.ins().rotr(x, y);
             ret.write_cvalue(fx, CValue::by_val(res, layout));
-        };
+        }
 
         // The only difference between offset and arith_offset is regarding UB. Because Cranelift
         // doesn't have UB both are codegen'ed the same way
-        offset | arith_offset, (c base, v offset) {
+        sym::offset | sym::arith_offset => {
+            intrinsic_args!(fx, args => (base, offset); intrinsic);
+            let offset = offset.load_scalar(fx);
+
             let pointee_ty = base.layout().ty.builtin_deref(true).unwrap().ty;
             let pointee_size = fx.layout_of(pointee_ty).size.bytes();
             let ptr_diff = if pointee_size != 1 {
@@ -535,12 +538,18 @@ fn codegen_regular_intrinsic_call<'tcx>(
             let base_val = base.load_scalar(fx);
             let res = fx.bcx.ins().iadd(base_val, ptr_diff);
             ret.write_cvalue(fx, CValue::by_val(res, base.layout()));
-        };
+        }
+
+        sym::transmute => {
+            intrinsic_args!(fx, args => (from); intrinsic);
 
-        transmute, (c from) {
             ret.write_cvalue_transmute(fx, from);
-        };
-        write_bytes | volatile_set_memory, (c dst, v val, v count) {
+        }
+        sym::write_bytes | sym::volatile_set_memory => {
+            intrinsic_args!(fx, args => (dst, val, count); intrinsic);
+            let val = val.load_scalar(fx);
+            let count = count.load_scalar(fx);
+
             let pointee_ty = dst.layout().ty.builtin_deref(true).unwrap().ty;
             let pointee_size = fx.layout_of(pointee_ty).size.bytes();
             let count = if pointee_size != 1 {
@@ -552,34 +561,42 @@ fn codegen_regular_intrinsic_call<'tcx>(
             // FIXME make the memset actually volatile when switching to emit_small_memset
             // FIXME use emit_small_memset
             fx.bcx.call_memset(fx.target_config, dst_ptr, val, count);
-        };
-        ctlz | ctlz_nonzero, (c arg) {
+        }
+        sym::ctlz | sym::ctlz_nonzero => {
+            intrinsic_args!(fx, args => (arg); intrinsic);
             let val = arg.load_scalar(fx);
+
             // FIXME trap on `ctlz_nonzero` with zero arg.
             let res = fx.bcx.ins().clz(val);
             let res = CValue::by_val(res, arg.layout());
             ret.write_cvalue(fx, res);
-        };
-        cttz | cttz_nonzero, (c arg) {
+        }
+        sym::cttz | sym::cttz_nonzero => {
+            intrinsic_args!(fx, args => (arg); intrinsic);
             let val = arg.load_scalar(fx);
+
             // FIXME trap on `cttz_nonzero` with zero arg.
             let res = fx.bcx.ins().ctz(val);
             let res = CValue::by_val(res, arg.layout());
             ret.write_cvalue(fx, res);
-        };
-        ctpop, (c arg) {
+        }
+        sym::ctpop => {
+            intrinsic_args!(fx, args => (arg); intrinsic);
             let val = arg.load_scalar(fx);
+
             let res = fx.bcx.ins().popcnt(val);
             let res = CValue::by_val(res, arg.layout());
             ret.write_cvalue(fx, res);
-        };
-        bitreverse, (c arg) {
+        }
+        sym::bitreverse => {
+            intrinsic_args!(fx, args => (arg); intrinsic);
             let val = arg.load_scalar(fx);
+
             let res = fx.bcx.ins().bitrev(val);
             let res = CValue::by_val(res, arg.layout());
             ret.write_cvalue(fx, res);
-        };
-        bswap, (c arg) {
+        }
+        sym::bswap => {
             // FIXME(CraneStation/cranelift#794) add bswap instruction to cranelift
             fn swap(bcx: &mut FunctionBuilder<'_>, v: Value) -> Value {
                 match bcx.func.dfg.value_type(v) {
@@ -655,11 +672,15 @@ fn codegen_regular_intrinsic_call<'tcx>(
                     ty => unreachable!("bswap {}", ty),
                 }
             }
+            intrinsic_args!(fx, args => (arg); intrinsic);
             let val = arg.load_scalar(fx);
+
             let res = CValue::by_val(swap(&mut fx.bcx, val), arg.layout());
             ret.write_cvalue(fx, res);
-        };
-        assert_inhabited | assert_zero_valid | assert_uninit_valid, () {
+        }
+        sym::assert_inhabited | sym::assert_zero_valid | sym::assert_uninit_valid => {
+            intrinsic_args!(fx, args => (); intrinsic);
+
             let layout = fx.layout_of(substs.type_at(0));
             if layout.abi.is_uninhabited() {
                 with_no_trimmed_paths!({
@@ -672,66 +693,71 @@ fn codegen_regular_intrinsic_call<'tcx>(
                 return;
             }
 
-            if intrinsic == sym::assert_zero_valid
-                && !layout.might_permit_raw_init(
-                    fx,
-                    InitKind::Zero,
-                    fx.tcx.sess.opts.debugging_opts.strict_init_checks) {
-
+            if intrinsic == sym::assert_zero_valid && !fx.tcx.permits_zero_init(layout) {
                 with_no_trimmed_paths!({
                     crate::base::codegen_panic(
                         fx,
-                        &format!("attempted to zero-initialize type `{}`, which is invalid", layout.ty),
+                        &format!(
+                            "attempted to zero-initialize type `{}`, which is invalid",
+                            layout.ty
+                        ),
                         source_info,
                     );
                 });
                 return;
             }
 
-            if intrinsic == sym::assert_uninit_valid
-                && !layout.might_permit_raw_init(
-                    fx,
-                    InitKind::Uninit,
-                    fx.tcx.sess.opts.debugging_opts.strict_init_checks) {
-
+            if intrinsic == sym::assert_uninit_valid && !fx.tcx.permits_uninit_init(layout) {
                 with_no_trimmed_paths!({
                     crate::base::codegen_panic(
                         fx,
-                        &format!("attempted to leave type `{}` uninitialized, which is invalid", layout.ty),
+                        &format!(
+                            "attempted to leave type `{}` uninitialized, which is invalid",
+                            layout.ty
+                        ),
                         source_info,
                     )
                 });
                 return;
             }
-        };
+        }
+
+        sym::volatile_load | sym::unaligned_volatile_load => {
+            intrinsic_args!(fx, args => (ptr); intrinsic);
 
-        volatile_load | unaligned_volatile_load, (c ptr) {
             // Cranelift treats loads as volatile by default
             // FIXME correctly handle unaligned_volatile_load
-            let inner_layout =
-                fx.layout_of(ptr.layout().ty.builtin_deref(true).unwrap().ty);
+            let inner_layout = fx.layout_of(ptr.layout().ty.builtin_deref(true).unwrap().ty);
             let val = CValue::by_ref(Pointer::new(ptr.load_scalar(fx)), inner_layout);
             ret.write_cvalue(fx, val);
-        };
-        volatile_store | unaligned_volatile_store, (v ptr, c val) {
+        }
+        sym::volatile_store | sym::unaligned_volatile_store => {
+            intrinsic_args!(fx, args => (ptr, val); intrinsic);
+            let ptr = ptr.load_scalar(fx);
+
             // Cranelift treats stores as volatile by default
             // FIXME correctly handle unaligned_volatile_store
             let dest = CPlace::for_ptr(Pointer::new(ptr), val.layout());
             dest.write_cvalue(fx, val);
-        };
+        }
+
+        sym::pref_align_of
+        | sym::needs_drop
+        | sym::type_id
+        | sym::type_name
+        | sym::variant_count => {
+            intrinsic_args!(fx, args => (); intrinsic);
 
-        pref_align_of | needs_drop | type_id | type_name | variant_count, () {
             let const_val =
                 fx.tcx.const_eval_instance(ParamEnv::reveal_all(), instance, None).unwrap();
-            let val = crate::constant::codegen_const_value(
-                fx,
-                const_val,
-                ret.layout().ty,
-            );
+            let val = crate::constant::codegen_const_value(fx, const_val, ret.layout().ty);
             ret.write_cvalue(fx, val);
-        };
+        }
 
-        ptr_offset_from | ptr_offset_from_unsigned, (v ptr, v base) {
+        sym::ptr_offset_from | sym::ptr_offset_from_unsigned => {
+            intrinsic_args!(fx, args => (ptr, base); intrinsic);
+            let ptr = ptr.load_scalar(fx);
+            let base = base.load_scalar(fx);
             let ty = substs.type_at(0);
 
             let pointee_size: u64 = fx.layout_of(ty).size.bytes();
@@ -747,31 +773,44 @@ fn codegen_regular_intrinsic_call<'tcx>(
                 CValue::by_val(fx.bcx.ins().sdiv_imm(diff_bytes, pointee_size as i64), isize_layout)
             };
             ret.write_cvalue(fx, val);
-        };
+        }
+
+        sym::ptr_guaranteed_eq => {
+            intrinsic_args!(fx, args => (a, b); intrinsic);
 
-        ptr_guaranteed_eq, (c a, c b) {
             let val = crate::num::codegen_ptr_binop(fx, BinOp::Eq, a, b);
             ret.write_cvalue(fx, val);
-        };
+        }
+
+        sym::ptr_guaranteed_ne => {
+            intrinsic_args!(fx, args => (a, b); intrinsic);
 
-        ptr_guaranteed_ne, (c a, c b) {
             let val = crate::num::codegen_ptr_binop(fx, BinOp::Ne, a, b);
             ret.write_cvalue(fx, val);
-        };
+        }
+
+        sym::caller_location => {
+            intrinsic_args!(fx, args => (); intrinsic);
 
-        caller_location, () {
             let caller_location = fx.get_caller_location(source_info);
             ret.write_cvalue(fx, caller_location);
-        };
+        }
+
+        _ if intrinsic.as_str().starts_with("atomic_fence") => {
+            intrinsic_args!(fx, args => (); intrinsic);
 
-        _ if intrinsic.as_str().starts_with("atomic_fence"), () {
             fx.bcx.ins().fence();
-        };
-        _ if intrinsic.as_str().starts_with("atomic_singlethreadfence"), () {
+        }
+        _ if intrinsic.as_str().starts_with("atomic_singlethreadfence") => {
+            intrinsic_args!(fx, args => (); intrinsic);
+
             // FIXME use a compiler fence once Cranelift supports it
             fx.bcx.ins().fence();
-        };
-        _ if intrinsic.as_str().starts_with("atomic_load"), (v ptr) {
+        }
+        _ if intrinsic.as_str().starts_with("atomic_load") => {
+            intrinsic_args!(fx, args => (ptr); intrinsic);
+            let ptr = ptr.load_scalar(fx);
+
             let ty = substs.type_at(0);
             match ty.kind() {
                 ty::Uint(UintTy::U128) | ty::Int(IntTy::I128) => {
@@ -783,7 +822,9 @@ fn codegen_regular_intrinsic_call<'tcx>(
                         fx.bcx.ins().jump(ret_block, &[]);
                         return;
                     } else {
-                        fx.tcx.sess.span_fatal(source_info.span, "128bit atomics not yet supported");
+                        fx.tcx
+                            .sess
+                            .span_fatal(source_info.span, "128bit atomics not yet supported");
                     }
                 }
                 ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {}
@@ -798,8 +839,11 @@ fn codegen_regular_intrinsic_call<'tcx>(
 
             let val = CValue::by_val(val, fx.layout_of(ty));
             ret.write_cvalue(fx, val);
-        };
-        _ if intrinsic.as_str().starts_with("atomic_store"), (v ptr, c val) {
+        }
+        _ if intrinsic.as_str().starts_with("atomic_store") => {
+            intrinsic_args!(fx, args => (ptr, val); intrinsic);
+            let ptr = ptr.load_scalar(fx);
+
             let ty = substs.type_at(0);
             match ty.kind() {
                 ty::Uint(UintTy::U128) | ty::Int(IntTy::I128) => {
@@ -811,7 +855,9 @@ fn codegen_regular_intrinsic_call<'tcx>(
                         fx.bcx.ins().jump(ret_block, &[]);
                         return;
                     } else {
-                        fx.tcx.sess.span_fatal(source_info.span, "128bit atomics not yet supported");
+                        fx.tcx
+                            .sess
+                            .span_fatal(source_info.span, "128bit atomics not yet supported");
                     }
                 }
                 ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {}
@@ -824,8 +870,11 @@ fn codegen_regular_intrinsic_call<'tcx>(
             let val = val.load_scalar(fx);
 
             fx.bcx.ins().atomic_store(MemFlags::trusted(), val, ptr);
-        };
-        _ if intrinsic.as_str().starts_with("atomic_xchg"), (v ptr, c new) {
+        }
+        _ if intrinsic.as_str().starts_with("atomic_xchg") => {
+            intrinsic_args!(fx, args => (ptr, new); intrinsic);
+            let ptr = ptr.load_scalar(fx);
+
             let layout = new.layout();
             match layout.ty.kind() {
                 ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {}
@@ -842,8 +891,12 @@ fn codegen_regular_intrinsic_call<'tcx>(
 
             let old = CValue::by_val(old, layout);
             ret.write_cvalue(fx, old);
-        };
-        _ if intrinsic.as_str().starts_with("atomic_cxchg"), (v ptr, c test_old, c new) { // both atomic_cxchg_* and atomic_cxchgweak_*
+        }
+        _ if intrinsic.as_str().starts_with("atomic_cxchg") => {
+            // both atomic_cxchg_* and atomic_cxchgweak_*
+            intrinsic_args!(fx, args => (ptr, test_old, new); intrinsic);
+            let ptr = ptr.load_scalar(fx);
+
             let layout = new.layout();
             match layout.ty.kind() {
                 ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {}
@@ -859,11 +912,15 @@ fn codegen_regular_intrinsic_call<'tcx>(
             let old = fx.bcx.ins().atomic_cas(MemFlags::trusted(), ptr, test_old, new);
             let is_eq = fx.bcx.ins().icmp(IntCC::Equal, old, test_old);
 
-            let ret_val = CValue::by_val_pair(old, fx.bcx.ins().bint(types::I8, is_eq), ret.layout());
+            let ret_val =
+                CValue::by_val_pair(old, fx.bcx.ins().bint(types::I8, is_eq), ret.layout());
             ret.write_cvalue(fx, ret_val)
-        };
+        }
+
+        _ if intrinsic.as_str().starts_with("atomic_xadd") => {
+            intrinsic_args!(fx, args => (ptr, amount); intrinsic);
+            let ptr = ptr.load_scalar(fx);
 
-        _ if intrinsic.as_str().starts_with("atomic_xadd"), (v ptr, c amount) {
             let layout = amount.layout();
             match layout.ty.kind() {
                 ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {}
@@ -876,12 +933,16 @@ fn codegen_regular_intrinsic_call<'tcx>(
 
             let amount = amount.load_scalar(fx);
 
-            let old = fx.bcx.ins().atomic_rmw(ty, MemFlags::trusted(), AtomicRmwOp::Add, ptr, amount);
+            let old =
+                fx.bcx.ins().atomic_rmw(ty, MemFlags::trusted(), AtomicRmwOp::Add, ptr, amount);
 
             let old = CValue::by_val(old, layout);
             ret.write_cvalue(fx, old);
-        };
-        _ if intrinsic.as_str().starts_with("atomic_xsub"), (v ptr, c amount) {
+        }
+        _ if intrinsic.as_str().starts_with("atomic_xsub") => {
+            intrinsic_args!(fx, args => (ptr, amount); intrinsic);
+            let ptr = ptr.load_scalar(fx);
+
             let layout = amount.layout();
             match layout.ty.kind() {
                 ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {}
@@ -894,12 +955,16 @@ fn codegen_regular_intrinsic_call<'tcx>(
 
             let amount = amount.load_scalar(fx);
 
-            let old = fx.bcx.ins().atomic_rmw(ty, MemFlags::trusted(), AtomicRmwOp::Sub, ptr, amount);
+            let old =
+                fx.bcx.ins().atomic_rmw(ty, MemFlags::trusted(), AtomicRmwOp::Sub, ptr, amount);
 
             let old = CValue::by_val(old, layout);
             ret.write_cvalue(fx, old);
-        };
-        _ if intrinsic.as_str().starts_with("atomic_and"), (v ptr, c src) {
+        }
+        _ if intrinsic.as_str().starts_with("atomic_and") => {
+            intrinsic_args!(fx, args => (ptr, src); intrinsic);
+            let ptr = ptr.load_scalar(fx);
+
             let layout = src.layout();
             match layout.ty.kind() {
                 ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {}
@@ -916,8 +981,11 @@ fn codegen_regular_intrinsic_call<'tcx>(
 
             let old = CValue::by_val(old, layout);
             ret.write_cvalue(fx, old);
-        };
-        _ if intrinsic.as_str().starts_with("atomic_or"), (v ptr, c src) {
+        }
+        _ if intrinsic.as_str().starts_with("atomic_or") => {
+            intrinsic_args!(fx, args => (ptr, src); intrinsic);
+            let ptr = ptr.load_scalar(fx);
+
             let layout = src.layout();
             match layout.ty.kind() {
                 ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {}
@@ -934,8 +1002,11 @@ fn codegen_regular_intrinsic_call<'tcx>(
 
             let old = CValue::by_val(old, layout);
             ret.write_cvalue(fx, old);
-        };
-        _ if intrinsic.as_str().starts_with("atomic_xor"), (v ptr, c src) {
+        }
+        _ if intrinsic.as_str().starts_with("atomic_xor") => {
+            intrinsic_args!(fx, args => (ptr, src); intrinsic);
+            let ptr = ptr.load_scalar(fx);
+
             let layout = src.layout();
             match layout.ty.kind() {
                 ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {}
@@ -952,8 +1023,11 @@ fn codegen_regular_intrinsic_call<'tcx>(
 
             let old = CValue::by_val(old, layout);
             ret.write_cvalue(fx, old);
-        };
-        _ if intrinsic.as_str().starts_with("atomic_nand"), (v ptr, c src) {
+        }
+        _ if intrinsic.as_str().starts_with("atomic_nand") => {
+            intrinsic_args!(fx, args => (ptr, src); intrinsic);
+            let ptr = ptr.load_scalar(fx);
+
             let layout = src.layout();
             match layout.ty.kind() {
                 ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {}
@@ -970,8 +1044,11 @@ fn codegen_regular_intrinsic_call<'tcx>(
 
             let old = CValue::by_val(old, layout);
             ret.write_cvalue(fx, old);
-        };
-        _ if intrinsic.as_str().starts_with("atomic_max"), (v ptr, c src) {
+        }
+        _ if intrinsic.as_str().starts_with("atomic_max") => {
+            intrinsic_args!(fx, args => (ptr, src); intrinsic);
+            let ptr = ptr.load_scalar(fx);
+
             let layout = src.layout();
             match layout.ty.kind() {
                 ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {}
@@ -988,8 +1065,11 @@ fn codegen_regular_intrinsic_call<'tcx>(
 
             let old = CValue::by_val(old, layout);
             ret.write_cvalue(fx, old);
-        };
-        _ if intrinsic.as_str().starts_with("atomic_umax"), (v ptr, c src) {
+        }
+        _ if intrinsic.as_str().starts_with("atomic_umax") => {
+            intrinsic_args!(fx, args => (ptr, src); intrinsic);
+            let ptr = ptr.load_scalar(fx);
+
             let layout = src.layout();
             match layout.ty.kind() {
                 ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {}
@@ -1006,8 +1086,11 @@ fn codegen_regular_intrinsic_call<'tcx>(
 
             let old = CValue::by_val(old, layout);
             ret.write_cvalue(fx, old);
-        };
-        _ if intrinsic.as_str().starts_with("atomic_min"), (v ptr, c src) {
+        }
+        _ if intrinsic.as_str().starts_with("atomic_min") => {
+            intrinsic_args!(fx, args => (ptr, src); intrinsic);
+            let ptr = ptr.load_scalar(fx);
+
             let layout = src.layout();
             match layout.ty.kind() {
                 ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {}
@@ -1024,8 +1107,11 @@ fn codegen_regular_intrinsic_call<'tcx>(
 
             let old = CValue::by_val(old, layout);
             ret.write_cvalue(fx, old);
-        };
-        _ if intrinsic.as_str().starts_with("atomic_umin"), (v ptr, c src) {
+        }
+        _ if intrinsic.as_str().starts_with("atomic_umin") => {
+            intrinsic_args!(fx, args => (ptr, src); intrinsic);
+            let ptr = ptr.load_scalar(fx);
+
             let layout = src.layout();
             match layout.ty.kind() {
                 ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {}
@@ -1042,30 +1128,51 @@ fn codegen_regular_intrinsic_call<'tcx>(
 
             let old = CValue::by_val(old, layout);
             ret.write_cvalue(fx, old);
-        };
+        }
+
+        sym::minnumf32 => {
+            intrinsic_args!(fx, args => (a, b); intrinsic);
+            let a = a.load_scalar(fx);
+            let b = b.load_scalar(fx);
 
-        minnumf32, (v a, v b) {
             let val = crate::num::codegen_float_min(fx, a, b);
             let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f32));
             ret.write_cvalue(fx, val);
-        };
-        minnumf64, (v a, v b) {
+        }
+        sym::minnumf64 => {
+            intrinsic_args!(fx, args => (a, b); intrinsic);
+            let a = a.load_scalar(fx);
+            let b = b.load_scalar(fx);
+
             let val = crate::num::codegen_float_min(fx, a, b);
             let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f64));
             ret.write_cvalue(fx, val);
-        };
-        maxnumf32, (v a, v b) {
+        }
+        sym::maxnumf32 => {
+            intrinsic_args!(fx, args => (a, b); intrinsic);
+            let a = a.load_scalar(fx);
+            let b = b.load_scalar(fx);
+
             let val = crate::num::codegen_float_max(fx, a, b);
             let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f32));
             ret.write_cvalue(fx, val);
-        };
-        maxnumf64, (v a, v b) {
+        }
+        sym::maxnumf64 => {
+            intrinsic_args!(fx, args => (a, b); intrinsic);
+            let a = a.load_scalar(fx);
+            let b = b.load_scalar(fx);
+
             let val = crate::num::codegen_float_max(fx, a, b);
             let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f64));
             ret.write_cvalue(fx, val);
-        };
+        }
+
+        kw::Try => {
+            intrinsic_args!(fx, args => (f, data, catch_fn); intrinsic);
+            let f = f.load_scalar(fx);
+            let data = data.load_scalar(fx);
+            let _catch_fn = catch_fn.load_scalar(fx);
 
-        kw.Try, (v f, v data, v _catch_fn) {
             // FIXME once unwinding is supported, change this to actually catch panics
             let f_sig = fx.bcx.func.import_signature(Signature {
                 call_conv: fx.target_config.default_call_conv,
@@ -1078,20 +1185,30 @@ fn codegen_regular_intrinsic_call<'tcx>(
             let layout = ret.layout();
             let ret_val = CValue::const_val(fx, layout, ty::ScalarInt::null(layout.size));
             ret.write_cvalue(fx, ret_val);
-        };
+        }
 
-        fadd_fast | fsub_fast | fmul_fast | fdiv_fast | frem_fast, (c x, c y) {
-            let res = crate::num::codegen_float_binop(fx, match intrinsic {
-                sym::fadd_fast => BinOp::Add,
-                sym::fsub_fast => BinOp::Sub,
-                sym::fmul_fast => BinOp::Mul,
-                sym::fdiv_fast => BinOp::Div,
-                sym::frem_fast => BinOp::Rem,
-                _ => unreachable!(),
-            }, x, y);
+        sym::fadd_fast | sym::fsub_fast | sym::fmul_fast | sym::fdiv_fast | sym::frem_fast => {
+            intrinsic_args!(fx, args => (x, y); intrinsic);
+
+            let res = crate::num::codegen_float_binop(
+                fx,
+                match intrinsic {
+                    sym::fadd_fast => BinOp::Add,
+                    sym::fsub_fast => BinOp::Sub,
+                    sym::fmul_fast => BinOp::Mul,
+                    sym::fdiv_fast => BinOp::Div,
+                    sym::frem_fast => BinOp::Rem,
+                    _ => unreachable!(),
+                },
+                x,
+                y,
+            );
             ret.write_cvalue(fx, res);
-        };
-        float_to_int_unchecked, (v f) {
+        }
+        sym::float_to_int_unchecked => {
+            intrinsic_args!(fx, args => (f); intrinsic);
+            let f = f.load_scalar(fx);
+
             let res = crate::cast::clif_int_or_float_cast(
                 fx,
                 f,
@@ -1100,52 +1217,74 @@ fn codegen_regular_intrinsic_call<'tcx>(
                 type_sign(ret.layout().ty),
             );
             ret.write_cvalue(fx, CValue::by_val(res, ret.layout()));
-        };
+        }
+
+        sym::raw_eq => {
+            intrinsic_args!(fx, args => (lhs_ref, rhs_ref); intrinsic);
+            let lhs_ref = lhs_ref.load_scalar(fx);
+            let rhs_ref = rhs_ref.load_scalar(fx);
 
-        raw_eq, (v lhs_ref, v rhs_ref) {
             let size = fx.layout_of(substs.type_at(0)).layout.size();
             // FIXME add and use emit_small_memcmp
-            let is_eq_value =
-                if size == Size::ZERO {
-                    // No bytes means they're trivially equal
-                    fx.bcx.ins().iconst(types::I8, 1)
-                } else if let Some(clty) = size.bits().try_into().ok().and_then(Type::int) {
-                    // Can't use `trusted` for these loads; they could be unaligned.
-                    let mut flags = MemFlags::new();
-                    flags.set_notrap();
-                    let lhs_val = fx.bcx.ins().load(clty, flags, lhs_ref, 0);
-                    let rhs_val = fx.bcx.ins().load(clty, flags, rhs_ref, 0);
-                    let eq = fx.bcx.ins().icmp(IntCC::Equal, lhs_val, rhs_val);
-                    fx.bcx.ins().bint(types::I8, eq)
-                } else {
-                    // Just call `memcmp` (like slices do in core) when the
-                    // size is too large or it's not a power-of-two.
-                    let signed_bytes = i64::try_from(size.bytes()).unwrap();
-                    let bytes_val = fx.bcx.ins().iconst(fx.pointer_type, signed_bytes);
-                    let params = vec![AbiParam::new(fx.pointer_type); 3];
-                    let returns = vec![AbiParam::new(types::I32)];
-                    let args = &[lhs_ref, rhs_ref, bytes_val];
-                    let cmp = fx.lib_call("memcmp", params, returns, args)[0];
-                    let eq = fx.bcx.ins().icmp_imm(IntCC::Equal, cmp, 0);
-                    fx.bcx.ins().bint(types::I8, eq)
-                };
+            let is_eq_value = if size == Size::ZERO {
+                // No bytes means they're trivially equal
+                fx.bcx.ins().iconst(types::I8, 1)
+            } else if let Some(clty) = size.bits().try_into().ok().and_then(Type::int) {
+                // Can't use `trusted` for these loads; they could be unaligned.
+                let mut flags = MemFlags::new();
+                flags.set_notrap();
+                let lhs_val = fx.bcx.ins().load(clty, flags, lhs_ref, 0);
+                let rhs_val = fx.bcx.ins().load(clty, flags, rhs_ref, 0);
+                let eq = fx.bcx.ins().icmp(IntCC::Equal, lhs_val, rhs_val);
+                fx.bcx.ins().bint(types::I8, eq)
+            } else {
+                // Just call `memcmp` (like slices do in core) when the
+                // size is too large or it's not a power-of-two.
+                let signed_bytes = i64::try_from(size.bytes()).unwrap();
+                let bytes_val = fx.bcx.ins().iconst(fx.pointer_type, signed_bytes);
+                let params = vec![AbiParam::new(fx.pointer_type); 3];
+                let returns = vec![AbiParam::new(types::I32)];
+                let args = &[lhs_ref, rhs_ref, bytes_val];
+                let cmp = fx.lib_call("memcmp", params, returns, args)[0];
+                let eq = fx.bcx.ins().icmp_imm(IntCC::Equal, cmp, 0);
+                fx.bcx.ins().bint(types::I8, eq)
+            };
             ret.write_cvalue(fx, CValue::by_val(is_eq_value, ret.layout()));
-        };
+        }
+
+        sym::const_allocate => {
+            intrinsic_args!(fx, args => (_size, _align); intrinsic);
 
-        const_allocate, (c _size, c _align) {
             // returns a null pointer at runtime.
             let null = fx.bcx.ins().iconst(fx.pointer_type, 0);
             ret.write_cvalue(fx, CValue::by_val(null, ret.layout()));
-        };
+        }
 
-        const_deallocate, (c _ptr, c _size, c _align) {
+        sym::const_deallocate => {
+            intrinsic_args!(fx, args => (_ptr, _size, _align); intrinsic);
             // nop at runtime.
-        };
+        }
+
+        sym::black_box => {
+            intrinsic_args!(fx, args => (a); intrinsic);
 
-        black_box, (c a) {
             // FIXME implement black_box semantics
             ret.write_cvalue(fx, a);
-        };
+        }
+
+        // FIXME implement variadics in cranelift
+        sym::va_copy | sym::va_arg | sym::va_end => {
+            fx.tcx.sess.span_fatal(
+                source_info.span,
+                "Defining variadic functions is not yet supported by Cranelift",
+            );
+        }
+
+        _ => {
+            fx.tcx
+                .sess
+                .span_fatal(source_info.span, &format!("unsupported intrinsic {}", intrinsic));
+        }
     }
 
     let ret_block = fx.get_block(destination.unwrap());
diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs
index d1ca9edf2e0..30e3d112594 100644
--- a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs
+++ b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs
@@ -25,13 +25,10 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
     ret: CPlace<'tcx>,
     span: Span,
 ) {
-    intrinsic_match! {
-        fx, intrinsic, args,
-        _ => {
-            fx.tcx.sess.span_fatal(span, &format!("Unknown SIMD intrinsic {}", intrinsic));
-        };
+    match intrinsic {
+        sym::simd_cast => {
+            intrinsic_args!(fx, args => (a); intrinsic);
 
-        simd_cast, (c a) {
             if !a.layout().ty.is_simd() {
                 report_simd_type_validation_error(fx, intrinsic, span, a.layout().ty);
                 return;
@@ -45,9 +42,11 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
 
                 clif_int_or_float_cast(fx, lane, from_signed, ret_lane_clif_ty, to_signed)
             });
-        };
+        }
+
+        sym::simd_eq | sym::simd_ne | sym::simd_lt | sym::simd_le | sym::simd_gt | sym::simd_ge => {
+            intrinsic_args!(fx, args => (x, y); intrinsic);
 
-        simd_eq | simd_ne | simd_lt | simd_le | simd_gt | simd_ge, (c x, c y) {
             if !x.layout().ty.is_simd() {
                 report_simd_type_validation_error(fx, intrinsic, span, x.layout().ty);
                 return;
@@ -57,7 +56,9 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
             simd_pair_for_each_lane(fx, x, y, ret, &|fx, lane_ty, res_lane_ty, x_lane, y_lane| {
                 let res_lane = match (lane_ty.kind(), intrinsic) {
                     (ty::Uint(_), sym::simd_eq) => fx.bcx.ins().icmp(IntCC::Equal, x_lane, y_lane),
-                    (ty::Uint(_), sym::simd_ne) => fx.bcx.ins().icmp(IntCC::NotEqual, x_lane, y_lane),
+                    (ty::Uint(_), sym::simd_ne) => {
+                        fx.bcx.ins().icmp(IntCC::NotEqual, x_lane, y_lane)
+                    }
                     (ty::Uint(_), sym::simd_lt) => {
                         fx.bcx.ins().icmp(IntCC::UnsignedLessThan, x_lane, y_lane)
                     }
@@ -72,8 +73,12 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
                     }
 
                     (ty::Int(_), sym::simd_eq) => fx.bcx.ins().icmp(IntCC::Equal, x_lane, y_lane),
-                    (ty::Int(_), sym::simd_ne) => fx.bcx.ins().icmp(IntCC::NotEqual, x_lane, y_lane),
-                    (ty::Int(_), sym::simd_lt) => fx.bcx.ins().icmp(IntCC::SignedLessThan, x_lane, y_lane),
+                    (ty::Int(_), sym::simd_ne) => {
+                        fx.bcx.ins().icmp(IntCC::NotEqual, x_lane, y_lane)
+                    }
+                    (ty::Int(_), sym::simd_lt) => {
+                        fx.bcx.ins().icmp(IntCC::SignedLessThan, x_lane, y_lane)
+                    }
                     (ty::Int(_), sym::simd_le) => {
                         fx.bcx.ins().icmp(IntCC::SignedLessThanOrEqual, x_lane, y_lane)
                     }
@@ -84,13 +89,21 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
                         fx.bcx.ins().icmp(IntCC::SignedGreaterThanOrEqual, x_lane, y_lane)
                     }
 
-                    (ty::Float(_), sym::simd_eq) => fx.bcx.ins().fcmp(FloatCC::Equal, x_lane, y_lane),
-                    (ty::Float(_), sym::simd_ne) => fx.bcx.ins().fcmp(FloatCC::NotEqual, x_lane, y_lane),
-                    (ty::Float(_), sym::simd_lt) => fx.bcx.ins().fcmp(FloatCC::LessThan, x_lane, y_lane),
+                    (ty::Float(_), sym::simd_eq) => {
+                        fx.bcx.ins().fcmp(FloatCC::Equal, x_lane, y_lane)
+                    }
+                    (ty::Float(_), sym::simd_ne) => {
+                        fx.bcx.ins().fcmp(FloatCC::NotEqual, x_lane, y_lane)
+                    }
+                    (ty::Float(_), sym::simd_lt) => {
+                        fx.bcx.ins().fcmp(FloatCC::LessThan, x_lane, y_lane)
+                    }
                     (ty::Float(_), sym::simd_le) => {
                         fx.bcx.ins().fcmp(FloatCC::LessThanOrEqual, x_lane, y_lane)
                     }
-                    (ty::Float(_), sym::simd_gt) => fx.bcx.ins().fcmp(FloatCC::GreaterThan, x_lane, y_lane),
+                    (ty::Float(_), sym::simd_gt) => {
+                        fx.bcx.ins().fcmp(FloatCC::GreaterThan, x_lane, y_lane)
+                    }
                     (ty::Float(_), sym::simd_ge) => {
                         fx.bcx.ins().fcmp(FloatCC::GreaterThanOrEqual, x_lane, y_lane)
                     }
@@ -103,10 +116,19 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
                 let res_lane = fx.bcx.ins().bint(ty, res_lane);
                 fx.bcx.ins().ineg(res_lane)
             });
-        };
+        }
 
         // simd_shuffle32<T, U>(x: T, y: T, idx: [u32; 32]) -> U
-        _ if intrinsic.as_str().starts_with("simd_shuffle"), (c x, c y, o idx) {
+        _ if intrinsic.as_str().starts_with("simd_shuffle") => {
+            let (x, y, idx) = match args {
+                [x, y, idx] => (x, y, idx),
+                _ => {
+                    bug!("wrong number of args for intrinsic {intrinsic}");
+                }
+            };
+            let x = codegen_operand(fx, x);
+            let y = codegen_operand(fx, y);
+
             if !x.layout().ty.is_simd() {
                 report_simd_type_validation_error(fx, intrinsic, span, x.layout().ty);
                 return;
@@ -119,11 +141,13 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
                 // version of this intrinsic.
                 let idx_ty = fx.monomorphize(idx.ty(fx.mir, fx.tcx));
                 match idx_ty.kind() {
-                    ty::Array(ty, len) if matches!(ty.kind(), ty::Uint(ty::UintTy::U32)) => {
-                        len.try_eval_usize(fx.tcx, ty::ParamEnv::reveal_all()).unwrap_or_else(|| {
+                    ty::Array(ty, len) if matches!(ty.kind(), ty::Uint(ty::UintTy::U32)) => len
+                        .try_eval_usize(fx.tcx, ty::ParamEnv::reveal_all())
+                        .unwrap_or_else(|| {
                             span_bug!(span, "could not evaluate shuffle index array length")
-                        }).try_into().unwrap()
-                    }
+                        })
+                        .try_into()
+                        .unwrap(),
                     _ => {
                         fx.tcx.sess.span_err(
                             span,
@@ -154,24 +178,30 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
 
             let indexes = {
                 use rustc_middle::mir::interpret::*;
-                let idx_const = crate::constant::mir_operand_get_const_val(fx, idx).expect("simd_shuffle* idx not const");
+                let idx_const = crate::constant::mir_operand_get_const_val(fx, idx)
+                    .expect("simd_shuffle* idx not const");
 
                 let idx_bytes = match idx_const {
                     ConstValue::ByRef { alloc, offset } => {
-                        let size = Size::from_bytes(4 * ret_lane_count /* size_of([u32; ret_lane_count]) */);
+                        let size = Size::from_bytes(
+                            4 * ret_lane_count, /* size_of([u32; ret_lane_count]) */
+                        );
                         alloc.inner().get_bytes(fx, alloc_range(offset, size)).unwrap()
                     }
                     _ => unreachable!("{:?}", idx_const),
                 };
 
-                (0..ret_lane_count).map(|i| {
-                    let i = usize::try_from(i).unwrap();
-                    let idx = rustc_middle::mir::interpret::read_target_uint(
-                        fx.tcx.data_layout.endian,
-                        &idx_bytes[4*i.. 4*i + 4],
-                    ).expect("read_target_uint");
-                    u16::try_from(idx).expect("try_from u32")
-                }).collect::<Vec<u16>>()
+                (0..ret_lane_count)
+                    .map(|i| {
+                        let i = usize::try_from(i).unwrap();
+                        let idx = rustc_middle::mir::interpret::read_target_uint(
+                            fx.tcx.data_layout.endian,
+                            &idx_bytes[4 * i..4 * i + 4],
+                        )
+                        .expect("read_target_uint");
+                        u16::try_from(idx).expect("try_from u32")
+                    })
+                    .collect::<Vec<u16>>()
             };
 
             for &idx in &indexes {
@@ -187,43 +217,63 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
                 let out_lane = ret.place_lane(fx, u64::try_from(out_idx).unwrap());
                 out_lane.write_cvalue(fx, in_lane);
             }
-        };
+        }
+
+        sym::simd_insert => {
+            let (base, idx, val) = match args {
+                [base, idx, val] => (base, idx, val),
+                _ => {
+                    bug!("wrong number of args for intrinsic {intrinsic}");
+                }
+            };
+            let base = codegen_operand(fx, base);
+            let val = codegen_operand(fx, val);
 
-        simd_insert, (c base, o idx, c val) {
             // FIXME validate
-            let idx_const = if let Some(idx_const) = crate::constant::mir_operand_get_const_val(fx, idx) {
+            let idx_const = if let Some(idx_const) =
+                crate::constant::mir_operand_get_const_val(fx, idx)
+            {
                 idx_const
             } else {
-                fx.tcx.sess.span_fatal(
-                    span,
-                    "Index argument for `simd_insert` is not a constant",
-                );
+                fx.tcx.sess.span_fatal(span, "Index argument for `simd_insert` is not a constant");
             };
 
-            let idx = idx_const.try_to_bits(Size::from_bytes(4 /* u32*/)).unwrap_or_else(|| panic!("kind not scalar: {:?}", idx_const));
+            let idx = idx_const
+                .try_to_bits(Size::from_bytes(4 /* u32*/))
+                .unwrap_or_else(|| panic!("kind not scalar: {:?}", idx_const));
             let (lane_count, _lane_ty) = base.layout().ty.simd_size_and_type(fx.tcx);
             if idx >= lane_count.into() {
-                fx.tcx.sess.span_fatal(fx.mir.span, &format!("[simd_insert] idx {} >= lane_count {}", idx, lane_count));
+                fx.tcx.sess.span_fatal(
+                    fx.mir.span,
+                    &format!("[simd_insert] idx {} >= lane_count {}", idx, lane_count),
+                );
             }
 
             ret.write_cvalue(fx, base);
             let ret_lane = ret.place_field(fx, mir::Field::new(idx.try_into().unwrap()));
             ret_lane.write_cvalue(fx, val);
-        };
+        }
+
+        sym::simd_extract => {
+            let (v, idx) = match args {
+                [v, idx] => (v, idx),
+                _ => {
+                    bug!("wrong number of args for intrinsic {intrinsic}");
+                }
+            };
+            let v = codegen_operand(fx, v);
 
-        simd_extract, (c v, o idx) {
             if !v.layout().ty.is_simd() {
                 report_simd_type_validation_error(fx, intrinsic, span, v.layout().ty);
                 return;
             }
 
-            let idx_const = if let Some(idx_const) = crate::constant::mir_operand_get_const_val(fx, idx) {
+            let idx_const = if let Some(idx_const) =
+                crate::constant::mir_operand_get_const_val(fx, idx)
+            {
                 idx_const
             } else {
-                fx.tcx.sess.span_warn(
-                    span,
-                    "Index argument for `simd_extract` is not a constant",
-                );
+                fx.tcx.sess.span_warn(span, "Index argument for `simd_extract` is not a constant");
                 let res = crate::trap::trap_unimplemented_ret_value(
                     fx,
                     ret.layout(),
@@ -233,89 +283,105 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
                 return;
             };
 
-            let idx = idx_const.try_to_bits(Size::from_bytes(4 /* u32*/)).unwrap_or_else(|| panic!("kind not scalar: {:?}", idx_const));
+            let idx = idx_const
+                .try_to_bits(Size::from_bytes(4 /* u32*/))
+                .unwrap_or_else(|| panic!("kind not scalar: {:?}", idx_const));
             let (lane_count, _lane_ty) = v.layout().ty.simd_size_and_type(fx.tcx);
             if idx >= lane_count.into() {
-                fx.tcx.sess.span_fatal(fx.mir.span, &format!("[simd_extract] idx {} >= lane_count {}", idx, lane_count));
+                fx.tcx.sess.span_fatal(
+                    fx.mir.span,
+                    &format!("[simd_extract] idx {} >= lane_count {}", idx, lane_count),
+                );
             }
 
             let ret_lane = v.value_lane(fx, idx.try_into().unwrap());
             ret.write_cvalue(fx, ret_lane);
-        };
+        }
+
+        sym::simd_neg => {
+            intrinsic_args!(fx, args => (a); intrinsic);
 
-        simd_neg, (c a) {
             if !a.layout().ty.is_simd() {
                 report_simd_type_validation_error(fx, intrinsic, span, a.layout().ty);
                 return;
             }
 
-            simd_for_each_lane(fx, a, ret, &|fx, lane_ty, _ret_lane_ty, lane| {
-                match lane_ty.kind() {
+            simd_for_each_lane(
+                fx,
+                a,
+                ret,
+                &|fx, lane_ty, _ret_lane_ty, lane| match lane_ty.kind() {
                     ty::Int(_) => fx.bcx.ins().ineg(lane),
                     ty::Float(_) => fx.bcx.ins().fneg(lane),
                     _ => unreachable!(),
-                }
-            });
-        };
-
-        simd_add | simd_sub | simd_mul | simd_div | simd_rem
-        | simd_shl | simd_shr | simd_and | simd_or | simd_xor, (c x, c y) {
-            if !x.layout().ty.is_simd() {
-                report_simd_type_validation_error(fx, intrinsic, span, x.layout().ty);
-                return;
-            }
+                },
+            );
+        }
+
+        sym::simd_add
+        | sym::simd_sub
+        | sym::simd_mul
+        | sym::simd_div
+        | sym::simd_rem
+        | sym::simd_shl
+        | sym::simd_shr
+        | sym::simd_and
+        | sym::simd_or
+        | sym::simd_xor => {
+            intrinsic_args!(fx, args => (x, y); intrinsic);
 
             // FIXME use vector instructions when possible
-            simd_pair_for_each_lane(fx, x, y, ret, &|fx, lane_ty, _ret_lane_ty, x_lane, y_lane| match (
-                lane_ty.kind(),
-                intrinsic,
-            ) {
-                (ty::Uint(_), sym::simd_add) => fx.bcx.ins().iadd(x_lane, y_lane),
-                (ty::Uint(_), sym::simd_sub) => fx.bcx.ins().isub(x_lane, y_lane),
-                (ty::Uint(_), sym::simd_mul) => fx.bcx.ins().imul(x_lane, y_lane),
-                (ty::Uint(_), sym::simd_div) => fx.bcx.ins().udiv(x_lane, y_lane),
-                (ty::Uint(_), sym::simd_rem) => fx.bcx.ins().urem(x_lane, y_lane),
-
-                (ty::Int(_), sym::simd_add) => fx.bcx.ins().iadd(x_lane, y_lane),
-                (ty::Int(_), sym::simd_sub) => fx.bcx.ins().isub(x_lane, y_lane),
-                (ty::Int(_), sym::simd_mul) => fx.bcx.ins().imul(x_lane, y_lane),
-                (ty::Int(_), sym::simd_div) => fx.bcx.ins().sdiv(x_lane, y_lane),
-                (ty::Int(_), sym::simd_rem) => fx.bcx.ins().srem(x_lane, y_lane),
-
-                (ty::Float(_), sym::simd_add) => fx.bcx.ins().fadd(x_lane, y_lane),
-                (ty::Float(_), sym::simd_sub) => fx.bcx.ins().fsub(x_lane, y_lane),
-                (ty::Float(_), sym::simd_mul) => fx.bcx.ins().fmul(x_lane, y_lane),
-                (ty::Float(_), sym::simd_div) => fx.bcx.ins().fdiv(x_lane, y_lane),
-                (ty::Float(FloatTy::F32), sym::simd_rem) => fx.lib_call(
-                    "fmodf",
-                    vec![AbiParam::new(types::F32), AbiParam::new(types::F32)],
-                    vec![AbiParam::new(types::F32)],
-                    &[x_lane, y_lane],
-                )[0],
-                (ty::Float(FloatTy::F64), sym::simd_rem) => fx.lib_call(
-                    "fmod",
-                    vec![AbiParam::new(types::F64), AbiParam::new(types::F64)],
-                    vec![AbiParam::new(types::F64)],
-                    &[x_lane, y_lane],
-                )[0],
-
-                (ty::Uint(_), sym::simd_shl) => fx.bcx.ins().ishl(x_lane, y_lane),
-                (ty::Uint(_), sym::simd_shr) => fx.bcx.ins().ushr(x_lane, y_lane),
-                (ty::Uint(_), sym::simd_and) => fx.bcx.ins().band(x_lane, y_lane),
-                (ty::Uint(_), sym::simd_or) => fx.bcx.ins().bor(x_lane, y_lane),
-                (ty::Uint(_), sym::simd_xor) => fx.bcx.ins().bxor(x_lane, y_lane),
-
-                (ty::Int(_), sym::simd_shl) => fx.bcx.ins().ishl(x_lane, y_lane),
-                (ty::Int(_), sym::simd_shr) => fx.bcx.ins().sshr(x_lane, y_lane),
-                (ty::Int(_), sym::simd_and) => fx.bcx.ins().band(x_lane, y_lane),
-                (ty::Int(_), sym::simd_or) => fx.bcx.ins().bor(x_lane, y_lane),
-                (ty::Int(_), sym::simd_xor) => fx.bcx.ins().bxor(x_lane, y_lane),
-
-                _ => unreachable!(),
+            simd_pair_for_each_lane(fx, x, y, ret, &|fx, lane_ty, _ret_lane_ty, x_lane, y_lane| {
+                match (lane_ty.kind(), intrinsic) {
+                    (ty::Uint(_), sym::simd_add) => fx.bcx.ins().iadd(x_lane, y_lane),
+                    (ty::Uint(_), sym::simd_sub) => fx.bcx.ins().isub(x_lane, y_lane),
+                    (ty::Uint(_), sym::simd_mul) => fx.bcx.ins().imul(x_lane, y_lane),
+                    (ty::Uint(_), sym::simd_div) => fx.bcx.ins().udiv(x_lane, y_lane),
+                    (ty::Uint(_), sym::simd_rem) => fx.bcx.ins().urem(x_lane, y_lane),
+
+                    (ty::Int(_), sym::simd_add) => fx.bcx.ins().iadd(x_lane, y_lane),
+                    (ty::Int(_), sym::simd_sub) => fx.bcx.ins().isub(x_lane, y_lane),
+                    (ty::Int(_), sym::simd_mul) => fx.bcx.ins().imul(x_lane, y_lane),
+                    (ty::Int(_), sym::simd_div) => fx.bcx.ins().sdiv(x_lane, y_lane),
+                    (ty::Int(_), sym::simd_rem) => fx.bcx.ins().srem(x_lane, y_lane),
+
+                    (ty::Float(_), sym::simd_add) => fx.bcx.ins().fadd(x_lane, y_lane),
+                    (ty::Float(_), sym::simd_sub) => fx.bcx.ins().fsub(x_lane, y_lane),
+                    (ty::Float(_), sym::simd_mul) => fx.bcx.ins().fmul(x_lane, y_lane),
+                    (ty::Float(_), sym::simd_div) => fx.bcx.ins().fdiv(x_lane, y_lane),
+                    (ty::Float(FloatTy::F32), sym::simd_rem) => fx.lib_call(
+                        "fmodf",
+                        vec![AbiParam::new(types::F32), AbiParam::new(types::F32)],
+                        vec![AbiParam::new(types::F32)],
+                        &[x_lane, y_lane],
+                    )[0],
+                    (ty::Float(FloatTy::F64), sym::simd_rem) => fx.lib_call(
+                        "fmod",
+                        vec![AbiParam::new(types::F64), AbiParam::new(types::F64)],
+                        vec![AbiParam::new(types::F64)],
+                        &[x_lane, y_lane],
+                    )[0],
+
+                    (ty::Uint(_), sym::simd_shl) => fx.bcx.ins().ishl(x_lane, y_lane),
+                    (ty::Uint(_), sym::simd_shr) => fx.bcx.ins().ushr(x_lane, y_lane),
+                    (ty::Uint(_), sym::simd_and) => fx.bcx.ins().band(x_lane, y_lane),
+                    (ty::Uint(_), sym::simd_or) => fx.bcx.ins().bor(x_lane, y_lane),
+                    (ty::Uint(_), sym::simd_xor) => fx.bcx.ins().bxor(x_lane, y_lane),
+
+                    (ty::Int(_), sym::simd_shl) => fx.bcx.ins().ishl(x_lane, y_lane),
+                    (ty::Int(_), sym::simd_shr) => fx.bcx.ins().sshr(x_lane, y_lane),
+                    (ty::Int(_), sym::simd_and) => fx.bcx.ins().band(x_lane, y_lane),
+                    (ty::Int(_), sym::simd_or) => fx.bcx.ins().bor(x_lane, y_lane),
+                    (ty::Int(_), sym::simd_xor) => fx.bcx.ins().bxor(x_lane, y_lane),
+
+                    _ => unreachable!(),
+                }
             });
-        };
+        }
+
+        sym::simd_fma => {
+            intrinsic_args!(fx, args => (a, b, c); intrinsic);
 
-        simd_fma, (c a, c b, c c) {
             if !a.layout().ty.is_simd() {
                 report_simd_type_validation_error(fx, intrinsic, span, a.layout().ty);
                 return;
@@ -333,16 +399,22 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
                 let c_lane = c.value_lane(fx, lane);
 
                 let res_lane = match lane_ty.kind() {
-                    ty::Float(FloatTy::F32) => fx.easy_call("fmaf", &[a_lane, b_lane, c_lane], lane_ty),
-                    ty::Float(FloatTy::F64) => fx.easy_call("fma", &[a_lane, b_lane, c_lane], lane_ty),
+                    ty::Float(FloatTy::F32) => {
+                        fx.easy_call("fmaf", &[a_lane, b_lane, c_lane], lane_ty)
+                    }
+                    ty::Float(FloatTy::F64) => {
+                        fx.easy_call("fma", &[a_lane, b_lane, c_lane], lane_ty)
+                    }
                     _ => unreachable!(),
                 };
 
                 ret.place_lane(fx, lane).write_cvalue(fx, res_lane);
             }
-        };
+        }
+
+        sym::simd_fmin | sym::simd_fmax => {
+            intrinsic_args!(fx, args => (x, y); intrinsic);
 
-        simd_fmin | simd_fmax, (c x, c y) {
             if !x.layout().ty.is_simd() {
                 report_simd_type_validation_error(fx, intrinsic, span, x.layout().ty);
                 return;
@@ -351,7 +423,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
             // FIXME use vector instructions when possible
             simd_pair_for_each_lane(fx, x, y, ret, &|fx, lane_ty, _ret_lane_ty, x_lane, y_lane| {
                 match lane_ty.kind() {
-                    ty::Float(_) => {},
+                    ty::Float(_) => {}
                     _ => unreachable!("{:?}", lane_ty),
                 }
                 match intrinsic {
@@ -360,16 +432,21 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
                     _ => unreachable!(),
                 }
             });
-        };
+        }
+
+        sym::simd_round => {
+            intrinsic_args!(fx, args => (a); intrinsic);
 
-        simd_round, (c a) {
             if !a.layout().ty.is_simd() {
                 report_simd_type_validation_error(fx, intrinsic, span, a.layout().ty);
                 return;
             }
 
-            simd_for_each_lane(fx, a, ret, &|fx, lane_ty, _ret_lane_ty, lane| {
-                match lane_ty.kind() {
+            simd_for_each_lane(
+                fx,
+                a,
+                ret,
+                &|fx, lane_ty, _ret_lane_ty, lane| match lane_ty.kind() {
                     ty::Float(FloatTy::F32) => fx.lib_call(
                         "roundf",
                         vec![AbiParam::new(types::F32)],
@@ -383,11 +460,13 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
                         &[lane],
                     )[0],
                     _ => unreachable!("{:?}", lane_ty),
-                }
-            });
-        };
+                },
+            );
+        }
+
+        sym::simd_fabs | sym::simd_fsqrt | sym::simd_ceil | sym::simd_floor | sym::simd_trunc => {
+            intrinsic_args!(fx, args => (a); intrinsic);
 
-        simd_fabs | simd_fsqrt | simd_ceil | simd_floor | simd_trunc, (c a) {
             if !a.layout().ty.is_simd() {
                 report_simd_type_validation_error(fx, intrinsic, span, a.layout().ty);
                 return;
@@ -395,7 +474,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
 
             simd_for_each_lane(fx, a, ret, &|fx, lane_ty, _ret_lane_ty, lane| {
                 match lane_ty.kind() {
-                    ty::Float(_) => {},
+                    ty::Float(_) => {}
                     _ => unreachable!("{:?}", lane_ty),
                 }
                 match intrinsic {
@@ -407,9 +486,12 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
                     _ => unreachable!(),
                 }
             });
-        };
+        }
+
+        sym::simd_reduce_add_ordered | sym::simd_reduce_add_unordered => {
+            intrinsic_args!(fx, args => (v, acc); intrinsic);
+            let acc = acc.load_scalar(fx);
 
-        simd_reduce_add_ordered | simd_reduce_add_unordered, (c v, v acc) {
             // FIXME there must be no acc param for integer vectors
             if !v.layout().ty.is_simd() {
                 report_simd_type_validation_error(fx, intrinsic, span, v.layout().ty);
@@ -423,9 +505,12 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
                     fx.bcx.ins().iadd(a, b)
                 }
             });
-        };
+        }
+
+        sym::simd_reduce_mul_ordered | sym::simd_reduce_mul_unordered => {
+            intrinsic_args!(fx, args => (v, acc); intrinsic);
+            let acc = acc.load_scalar(fx);
 
-        simd_reduce_mul_ordered | simd_reduce_mul_unordered, (c v, v acc) {
             // FIXME there must be no acc param for integer vectors
             if !v.layout().ty.is_simd() {
                 report_simd_type_validation_error(fx, intrinsic, span, v.layout().ty);
@@ -439,54 +524,66 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
                     fx.bcx.ins().imul(a, b)
                 }
             });
-        };
+        }
+
+        sym::simd_reduce_all => {
+            intrinsic_args!(fx, args => (v); intrinsic);
 
-        simd_reduce_all, (c v) {
             if !v.layout().ty.is_simd() {
                 report_simd_type_validation_error(fx, intrinsic, span, v.layout().ty);
                 return;
             }
 
             simd_reduce_bool(fx, v, ret, &|fx, a, b| fx.bcx.ins().band(a, b));
-        };
+        }
+
+        sym::simd_reduce_any => {
+            intrinsic_args!(fx, args => (v); intrinsic);
 
-        simd_reduce_any, (c v) {
             if !v.layout().ty.is_simd() {
                 report_simd_type_validation_error(fx, intrinsic, span, v.layout().ty);
                 return;
             }
 
             simd_reduce_bool(fx, v, ret, &|fx, a, b| fx.bcx.ins().bor(a, b));
-        };
+        }
+
+        sym::simd_reduce_and => {
+            intrinsic_args!(fx, args => (v); intrinsic);
 
-        simd_reduce_and, (c v) {
             if !v.layout().ty.is_simd() {
                 report_simd_type_validation_error(fx, intrinsic, span, v.layout().ty);
                 return;
             }
 
             simd_reduce(fx, v, None, ret, &|fx, _ty, a, b| fx.bcx.ins().band(a, b));
-        };
+        }
+
+        sym::simd_reduce_or => {
+            intrinsic_args!(fx, args => (v); intrinsic);
 
-        simd_reduce_or, (c v) {
             if !v.layout().ty.is_simd() {
                 report_simd_type_validation_error(fx, intrinsic, span, v.layout().ty);
                 return;
             }
 
             simd_reduce(fx, v, None, ret, &|fx, _ty, a, b| fx.bcx.ins().bor(a, b));
-        };
+        }
+
+        sym::simd_reduce_xor => {
+            intrinsic_args!(fx, args => (v); intrinsic);
 
-        simd_reduce_xor, (c v) {
             if !v.layout().ty.is_simd() {
                 report_simd_type_validation_error(fx, intrinsic, span, v.layout().ty);
                 return;
             }
 
             simd_reduce(fx, v, None, ret, &|fx, _ty, a, b| fx.bcx.ins().bxor(a, b));
-        };
+        }
+
+        sym::simd_reduce_min => {
+            intrinsic_args!(fx, args => (v); intrinsic);
 
-        simd_reduce_min, (c v) {
             if !v.layout().ty.is_simd() {
                 report_simd_type_validation_error(fx, intrinsic, span, v.layout().ty);
                 return;
@@ -501,9 +598,11 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
                 };
                 fx.bcx.ins().select(lt, a, b)
             });
-        };
+        }
+
+        sym::simd_reduce_max => {
+            intrinsic_args!(fx, args => (v); intrinsic);
 
-        simd_reduce_max, (c v) {
             if !v.layout().ty.is_simd() {
                 report_simd_type_validation_error(fx, intrinsic, span, v.layout().ty);
                 return;
@@ -518,9 +617,11 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
                 };
                 fx.bcx.ins().select(gt, a, b)
             });
-        };
+        }
+
+        sym::simd_select => {
+            intrinsic_args!(fx, args => (m, a, b); intrinsic);
 
-        simd_select, (c m, c a, c b) {
             if !m.layout().ty.is_simd() {
                 report_simd_type_validation_error(fx, intrinsic, span, m.layout().ty);
                 return;
@@ -540,15 +641,19 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
                 let b_lane = b.value_lane(fx, lane).load_scalar(fx);
 
                 let m_lane = fx.bcx.ins().icmp_imm(IntCC::Equal, m_lane, 0);
-                let res_lane = CValue::by_val(fx.bcx.ins().select(m_lane, b_lane, a_lane), lane_layout);
+                let res_lane =
+                    CValue::by_val(fx.bcx.ins().select(m_lane, b_lane, a_lane), lane_layout);
 
                 ret.place_lane(fx, lane).write_cvalue(fx, res_lane);
             }
-        };
+        }
 
         // simd_saturating_*
         // simd_bitmask
         // simd_scatter
         // simd_gather
+        _ => {
+            fx.tcx.sess.span_fatal(span, &format!("Unknown SIMD intrinsic {}", intrinsic));
+        }
     }
 }
diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs
index be2d3108c5f..568bb20a3f4 100644
--- a/compiler/rustc_codegen_cranelift/src/lib.rs
+++ b/compiler/rustc_codegen_cranelift/src/lib.rs
@@ -141,7 +141,11 @@ impl<'tcx> CodegenCx<'tcx> {
 
         let unwind_context =
             UnwindContext::new(isa, matches!(backend_config.codegen_mode, CodegenMode::Aot));
-        let debug_context = if debug_info { Some(DebugContext::new(tcx, isa)) } else { None };
+        let debug_context = if debug_info && !tcx.sess.target.options.is_like_windows {
+            Some(DebugContext::new(tcx, isa))
+        } else {
+            None
+        };
         CodegenCx {
             tcx,
             global_asm: String::new(),
@@ -167,7 +171,7 @@ impl CodegenBackend for CraneliftCodegenBackend {
         }
     }
 
-    fn target_features(&self, _sess: &Session) -> Vec<rustc_span::Symbol> {
+    fn target_features(&self, _sess: &Session, _allow_unstable: bool) -> Vec<rustc_span::Symbol> {
         vec![]
     }
 
@@ -243,6 +247,7 @@ fn build_isa(sess: &Session, backend_config: &BackendConfig) -> Box<dyn isa::Tar
     flags_builder.set("enable_probestack", "false").unwrap(); // __cranelift_probestack is not provided
     let enable_verifier = if backend_config.enable_verifier { "true" } else { "false" };
     flags_builder.set("enable_verifier", enable_verifier).unwrap();
+    flags_builder.set("regalloc_checker", enable_verifier).unwrap();
 
     let tls_model = match target_triple.binary_format {
         BinaryFormat::Elf => "elf_gd",
diff --git a/compiler/rustc_codegen_cranelift/src/main_shim.rs b/compiler/rustc_codegen_cranelift/src/main_shim.rs
index 2f71a70a449..c67b6e98b32 100644
--- a/compiler/rustc_codegen_cranelift/src/main_shim.rs
+++ b/compiler/rustc_codegen_cranelift/src/main_shim.rs
@@ -109,7 +109,8 @@ pub(crate) fn maybe_create_entry_wrapper(
                     tcx.mk_substs([GenericArg::from(main_ret_ty)].iter()),
                 )
                 .unwrap()
-                .unwrap();
+                .unwrap()
+                .polymorphize(tcx);
 
                 let report_name = tcx.symbol_name(report).name;
                 let report_sig = get_function_sig(tcx, m.isa().triple(), report);
diff --git a/compiler/rustc_codegen_cranelift/src/pretty_clif.rs b/compiler/rustc_codegen_cranelift/src/pretty_clif.rs
index ca7116b887d..1d1ec21680e 100644
--- a/compiler/rustc_codegen_cranelift/src/pretty_clif.rs
+++ b/compiler/rustc_codegen_cranelift/src/pretty_clif.rs
@@ -66,7 +66,7 @@ use rustc_session::config::OutputType;
 
 use crate::prelude::*;
 
-#[derive(Debug)]
+#[derive(Clone, Debug)]
 pub(crate) struct CommentWriter {
     enabled: bool,
     global_comments: Vec<String>,
@@ -237,6 +237,7 @@ pub(crate) fn write_clif_file<'tcx>(
     func: &cranelift_codegen::ir::Function,
     mut clif_comments: &CommentWriter,
 ) {
+    // FIXME work around filename too long errors
     write_ir_file(
         tcx,
         || format!("{}.{}.clif", tcx.symbol_name(instance).name, postfix),
diff --git a/compiler/rustc_codegen_cranelift/src/unsize.rs b/compiler/rustc_codegen_cranelift/src/unsize.rs
index fd63c3ecddb..052ca0a082b 100644
--- a/compiler/rustc_codegen_cranelift/src/unsize.rs
+++ b/compiler/rustc_codegen_cranelift/src/unsize.rs
@@ -153,11 +153,7 @@ pub(crate) fn size_and_align_of_dst<'tcx>(
     layout: TyAndLayout<'tcx>,
     info: Value,
 ) -> (Value, Value) {
-    if !layout.is_unsized() {
-        let size = fx.bcx.ins().iconst(fx.pointer_type, layout.size.bytes() as i64);
-        let align = fx.bcx.ins().iconst(fx.pointer_type, layout.align.abi.bytes() as i64);
-        return (size, align);
-    }
+    assert!(layout.is_unsized() || layout.abi == Abi::Uninhabited);
     match layout.ty.kind() {
         ty::Dynamic(..) => {
             // load size/align from vtable
diff --git a/compiler/rustc_codegen_cranelift/src/value_and_place.rs b/compiler/rustc_codegen_cranelift/src/value_and_place.rs
index a68225de58b..45ae2bd8f07 100644
--- a/compiler/rustc_codegen_cranelift/src/value_and_place.rs
+++ b/compiler/rustc_codegen_cranelift/src/value_and_place.rs
@@ -324,6 +324,12 @@ impl<'tcx> CPlace<'tcx> {
             };
         }
 
+        if layout.size.bytes() >= u64::from(u32::MAX - 16) {
+            fx.tcx
+                .sess
+                .fatal(&format!("values of type {} are too big to store on the stack", layout.ty));
+        }
+
         let stack_slot = fx.bcx.create_stack_slot(StackSlotData {
             kind: StackSlotKind::ExplicitSlot,
             // FIXME Don't force the size to a multiple of 16 bytes once Cranelift gets a way to
@@ -420,7 +426,7 @@ impl<'tcx> CPlace<'tcx> {
     }
 
     pub(crate) fn write_cvalue(self, fx: &mut FunctionCx<'_, '_, 'tcx>, from: CValue<'tcx>) {
-        assert_assignable(fx, from.layout().ty, self.layout().ty);
+        assert_assignable(fx, from.layout().ty, self.layout().ty, 16);
 
         self.write_cvalue_maybe_transmute(fx, from, "write_cvalue");
     }
@@ -774,18 +780,25 @@ pub(crate) fn assert_assignable<'tcx>(
     fx: &FunctionCx<'_, '_, 'tcx>,
     from_ty: Ty<'tcx>,
     to_ty: Ty<'tcx>,
+    limit: usize,
 ) {
+    if limit == 0 {
+        // assert_assignable exists solely to catch bugs in cg_clif. it isn't necessary for
+        // soundness. don't attempt to check deep types to avoid exponential behavior in certain
+        // cases.
+        return;
+    }
     match (from_ty.kind(), to_ty.kind()) {
         (ty::Ref(_, a, _), ty::Ref(_, b, _))
         | (
             ty::RawPtr(TypeAndMut { ty: a, mutbl: _ }),
             ty::RawPtr(TypeAndMut { ty: b, mutbl: _ }),
         ) => {
-            assert_assignable(fx, *a, *b);
+            assert_assignable(fx, *a, *b, limit - 1);
         }
         (ty::Ref(_, a, _), ty::RawPtr(TypeAndMut { ty: b, mutbl: _ }))
         | (ty::RawPtr(TypeAndMut { ty: a, mutbl: _ }), ty::Ref(_, b, _)) => {
-            assert_assignable(fx, *a, *b);
+            assert_assignable(fx, *a, *b, limit - 1);
         }
         (ty::FnPtr(_), ty::FnPtr(_)) => {
             let from_sig = fx.tcx.normalize_erasing_late_bound_regions(
@@ -815,6 +828,17 @@ pub(crate) fn assert_assignable<'tcx>(
             }
             // dyn for<'r> Trait<'r> -> dyn Trait<'_> is allowed
         }
+        (&ty::Tuple(types_a), &ty::Tuple(types_b)) => {
+            let mut types_a = types_a.iter();
+            let mut types_b = types_b.iter();
+            loop {
+                match (types_a.next(), types_b.next()) {
+                    (Some(a), Some(b)) => assert_assignable(fx, a, b, limit - 1),
+                    (None, None) => return,
+                    (Some(_), None) | (None, Some(_)) => panic!("{:#?}/{:#?}", from_ty, to_ty),
+                }
+            }
+        }
         (&ty::Adt(adt_def_a, substs_a), &ty::Adt(adt_def_b, substs_b))
             if adt_def_a.did() == adt_def_b.did() =>
         {
@@ -822,18 +846,37 @@ pub(crate) fn assert_assignable<'tcx>(
             let mut types_b = substs_b.types();
             loop {
                 match (types_a.next(), types_b.next()) {
-                    (Some(a), Some(b)) => assert_assignable(fx, a, b),
+                    (Some(a), Some(b)) => assert_assignable(fx, a, b, limit - 1),
                     (None, None) => return,
                     (Some(_), None) | (None, Some(_)) => panic!("{:#?}/{:#?}", from_ty, to_ty),
                 }
             }
         }
-        (ty::Array(a, _), ty::Array(b, _)) => assert_assignable(fx, *a, *b),
+        (ty::Array(a, _), ty::Array(b, _)) => assert_assignable(fx, *a, *b, limit - 1),
+        (&ty::Closure(def_id_a, substs_a), &ty::Closure(def_id_b, substs_b))
+            if def_id_a == def_id_b =>
+        {
+            let mut types_a = substs_a.types();
+            let mut types_b = substs_b.types();
+            loop {
+                match (types_a.next(), types_b.next()) {
+                    (Some(a), Some(b)) => assert_assignable(fx, a, b, limit - 1),
+                    (None, None) => return,
+                    (Some(_), None) | (None, Some(_)) => panic!("{:#?}/{:#?}", from_ty, to_ty),
+                }
+            }
+        }
+        (ty::Param(_), _) | (_, ty::Param(_)) if fx.tcx.sess.opts.unstable_opts.polymorphize => {
+            // No way to check if it is correct or not with polymorphization enabled
+        }
         _ => {
             assert_eq!(
-                from_ty, to_ty,
+                from_ty,
+                to_ty,
                 "Can't write value with incompatible type {:?} to place with type {:?}\n\n{:#?}",
-                from_ty, to_ty, fx,
+                from_ty.kind(),
+                to_ty.kind(),
+                fx,
             );
         }
     }
diff --git a/compiler/rustc_codegen_gcc/src/abi.rs b/compiler/rustc_codegen_gcc/src/abi.rs
index 2c796d0f69e..0ed3e1fbe93 100644
--- a/compiler/rustc_codegen_gcc/src/abi.rs
+++ b/compiler/rustc_codegen_gcc/src/abi.rs
@@ -1,6 +1,6 @@
 use gccjit::{ToLValue, ToRValue, Type};
 use rustc_codegen_ssa::traits::{AbiBuilderMethods, BaseTypeMethods};
-use rustc_data_structures::stable_set::FxHashSet;
+use rustc_data_structures::fx::FxHashSet;
 use rustc_middle::bug;
 use rustc_middle::ty::Ty;
 use rustc_target::abi::call::{CastTarget, FnAbi, PassMode, Reg, RegKind};
diff --git a/compiler/rustc_codegen_gcc/src/allocator.rs b/compiler/rustc_codegen_gcc/src/allocator.rs
index c761e5aabd1..58efb81e800 100644
--- a/compiler/rustc_codegen_gcc/src/allocator.rs
+++ b/compiler/rustc_codegen_gcc/src/allocator.rs
@@ -117,7 +117,7 @@ pub(crate) unsafe fn codegen(tcx: TyCtxt<'_>, mods: &mut GccContext, _module_nam
 
     let name = OomStrategy::SYMBOL.to_string();
     let global = context.new_global(None, GlobalKind::Exported, i8, name);
-    let value = tcx.sess.opts.debugging_opts.oom.should_panic();
+    let value = tcx.sess.opts.unstable_opts.oom.should_panic();
     let value = context.new_rvalue_from_int(i8, value as i32);
     global.global_set_initializer_rvalue(value);
 }
diff --git a/compiler/rustc_codegen_gcc/src/archive.rs b/compiler/rustc_codegen_gcc/src/archive.rs
index 411ec27139e..21f62a6b009 100644
--- a/compiler/rustc_codegen_gcc/src/archive.rs
+++ b/compiler/rustc_codegen_gcc/src/archive.rs
@@ -4,7 +4,6 @@ use std::path::{Path, PathBuf};
 use rustc_codegen_ssa::back::archive::ArchiveBuilder;
 use rustc_session::Session;
 
-use rustc_data_structures::temp_dir::MaybeTempDir;
 use rustc_session::cstore::DllImport;
 
 struct ArchiveConfig<'a> {
@@ -177,7 +176,16 @@ impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> {
         any_members
     }
 
-    fn inject_dll_import_lib(&mut self, _lib_name: &str, _dll_imports: &[DllImport], _tmpdir: &MaybeTempDir) {
+    fn sess(&self) -> &Session {
+        self.config.sess
+    }
+
+    fn create_dll_import_lib(
+        _sess: &Session,
+        _lib_name: &str,
+        _dll_imports: &[DllImport],
+        _tmpdir: &Path,
+    ) -> PathBuf {
         unimplemented!();
     }
 }
diff --git a/compiler/rustc_codegen_gcc/src/base.rs b/compiler/rustc_codegen_gcc/src/base.rs
index e4ecbd46f0c..8f9f6f98faf 100644
--- a/compiler/rustc_codegen_gcc/src/base.rs
+++ b/compiler/rustc_codegen_gcc/src/base.rs
@@ -96,7 +96,7 @@ pub fn compile_codegen_unit<'tcx>(tcx: TyCtxt<'tcx>, cgu_name: Symbol, supports_
         // NOTE: Rust relies on LLVM not doing TBAA (https://github.com/rust-lang/unsafe-code-guidelines/issues/292).
         context.add_command_line_option("-fno-strict-aliasing");
 
-        if tcx.sess.opts.debugging_opts.function_sections.unwrap_or(tcx.sess.target.function_sections) {
+        if tcx.sess.opts.unstable_opts.function_sections.unwrap_or(tcx.sess.target.function_sections) {
             context.add_command_line_option("-ffunction-sections");
             context.add_command_line_option("-fdata-sections");
         }
diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs
index fa490fe3f22..4d40dd0994d 100644
--- a/compiler/rustc_codegen_gcc/src/builder.rs
+++ b/compiler/rustc_codegen_gcc/src/builder.rs
@@ -30,7 +30,7 @@ use rustc_codegen_ssa::traits::{
     OverflowOp,
     StaticBuilderMethods,
 };
-use rustc_data_structures::stable_set::FxHashSet;
+use rustc_data_structures::fx::FxHashSet;
 use rustc_middle::ty::{ParamEnv, Ty, TyCtxt};
 use rustc_middle::ty::layout::{FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasParamEnv, HasTyCtxt, LayoutError, LayoutOfHelpers, TyAndLayout};
 use rustc_span::Span;
@@ -784,16 +784,6 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
         // TODO(antoyo)
     }
 
-    fn type_metadata(&mut self, _function: RValue<'gcc>, _typeid: String) {
-        // Unsupported.
-    }
-
-    fn typeid_metadata(&mut self, _typeid: String) -> RValue<'gcc> {
-        // Unsupported.
-        self.context.new_rvalue_from_int(self.int_type, 0)
-    }
-
-
     fn store(&mut self, val: RValue<'gcc>, ptr: RValue<'gcc>, align: Align) -> RValue<'gcc> {
         self.store_with_flags(val, ptr, align, MemFlags::empty())
     }
diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs
index fc391f53f18..ccb6cbbc2c8 100644
--- a/compiler/rustc_codegen_gcc/src/common.rs
+++ b/compiler/rustc_codegen_gcc/src/common.rs
@@ -201,6 +201,11 @@ impl<'gcc, 'tcx> ConstMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
                         GlobalAlloc::Function(fn_instance) => {
                             self.get_fn_addr(fn_instance)
                         },
+                        GlobalAlloc::VTable(ty, trait_ref) => {
+                            let alloc = self.tcx.global_alloc(self.tcx.vtable_allocation((ty, trait_ref))).unwrap_memory();
+                            let init = const_alloc_to_gcc(self, alloc);
+                            self.static_addr_of(init, alloc.inner().align, None)
+                        }
                         GlobalAlloc::Static(def_id) => {
                             assert!(self.tcx.is_static(def_id));
                             self.get_static(def_id).get_address(None)
diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs
index 399830de84c..c21e0c5a35b 100644
--- a/compiler/rustc_codegen_gcc/src/lib.rs
+++ b/compiler/rustc_codegen_gcc/src/lib.rs
@@ -140,8 +140,8 @@ impl CodegenBackend for GccCodegenBackend {
         )
     }
 
-    fn target_features(&self, sess: &Session) -> Vec<Symbol> {
-        target_features(sess)
+    fn target_features(&self, sess: &Session, allow_unstable: bool) -> Vec<Symbol> {
+        target_features(sess, allow_unstable)
     }
 }
 
@@ -298,12 +298,12 @@ pub fn target_cpu(sess: &Session) -> &str {
     }
 }
 
-pub fn target_features(sess: &Session) -> Vec<Symbol> {
+pub fn target_features(sess: &Session, allow_unstable: bool) -> Vec<Symbol> {
     supported_target_features(sess)
         .iter()
         .filter_map(
             |&(feature, gate)| {
-                if sess.is_nightly_build() || gate.is_none() { Some(feature) } else { None }
+                if sess.is_nightly_build() || allow_unstable || gate.is_none() { Some(feature) } else { None }
             },
         )
         .filter(|_feature| {
diff --git a/compiler/rustc_codegen_gcc/src/type_.rs b/compiler/rustc_codegen_gcc/src/type_.rs
index 002b95db36d..68bdb8d4e55 100644
--- a/compiler/rustc_codegen_gcc/src/type_.rs
+++ b/compiler/rustc_codegen_gcc/src/type_.rs
@@ -1,7 +1,7 @@
 use std::convert::TryInto;
 
 use gccjit::{RValue, Struct, Type};
-use rustc_codegen_ssa::traits::{BaseTypeMethods, DerivedTypeMethods};
+use rustc_codegen_ssa::traits::{BaseTypeMethods, DerivedTypeMethods, TypeMembershipMethods};
 use rustc_codegen_ssa::common::TypeKind;
 use rustc_middle::{bug, ty};
 use rustc_middle::ty::layout::TyAndLayout;
@@ -290,3 +290,14 @@ pub fn struct_fields<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, layout: TyAndLayout
 
     (result, packed)
 }
+
+impl<'gcc, 'tcx> TypeMembershipMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
+    fn set_type_metadata(&self, _function: RValue<'gcc>, _typeid: String) {
+        // Unsupported.
+    }
+
+    fn typeid_metadata(&self, _typeid: String) -> RValue<'gcc> {
+        // Unsupported.
+        self.context.new_rvalue_from_int(self.int_type, 0)
+    }
+}
diff --git a/compiler/rustc_codegen_llvm/Cargo.toml b/compiler/rustc_codegen_llvm/Cargo.toml
index d8fbd0a84fb..f9a5463efcd 100644
--- a/compiler/rustc_codegen_llvm/Cargo.toml
+++ b/compiler/rustc_codegen_llvm/Cargo.toml
@@ -18,7 +18,6 @@ rustc_middle = { path = "../rustc_middle" }
 rustc-demangle = "0.1.21"
 rustc_attr = { path = "../rustc_attr" }
 rustc_codegen_ssa = { path = "../rustc_codegen_ssa" }
-rustc_symbol_mangling = { path = "../rustc_symbol_mangling" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_errors = { path = "../rustc_errors" }
 rustc_fs_util = { path = "../rustc_fs_util" }
@@ -30,6 +29,7 @@ rustc_metadata = { path = "../rustc_metadata" }
 rustc_query_system = { path = "../rustc_query_system" }
 rustc_session = { path = "../rustc_session" }
 rustc_serialize = { path = "../rustc_serialize" }
+rustc_symbol_mangling = { path = "../rustc_symbol_mangling" }
 rustc_target = { path = "../rustc_target" }
 smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
 rustc_ast = { path = "../rustc_ast" }
diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs
index cc8b3a1a4e4..9eb3574e77b 100644
--- a/compiler/rustc_codegen_llvm/src/abi.rs
+++ b/compiler/rustc_codegen_llvm/src/abi.rs
@@ -37,7 +37,7 @@ fn should_use_mutable_noalias(cx: &CodegenCx<'_, '_>) -> bool {
     // LLVM prior to version 12 had known miscompiles in the presence of
     // noalias attributes (see #54878), but we don't support earlier
     // versions at all anymore. We now enable mutable noalias by default.
-    cx.tcx.sess.opts.debugging_opts.mutable_noalias.unwrap_or(true)
+    cx.tcx.sess.opts.unstable_opts.mutable_noalias.unwrap_or(true)
 }
 
 const ABI_AFFECTING_ATTRIBUTES: [(ArgAttribute, llvm::AttributeKind); 1] =
@@ -569,6 +569,22 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
                 &[cmse_nonsecure_call],
             );
         }
+
+        // Some intrinsics require that an elementtype attribute (with the pointee type of a
+        // pointer argument) is added to the callsite.
+        let element_type_index = unsafe { llvm::LLVMRustGetElementTypeArgIndex(callsite) };
+        if element_type_index >= 0 {
+            let arg_ty = self.args[element_type_index as usize].layout.ty;
+            let pointee_ty = arg_ty.builtin_deref(true).expect("Must be pointer argument").ty;
+            let element_type_attr = unsafe {
+                llvm::LLVMRustCreateElementTypeAttr(bx.llcx, bx.layout_of(pointee_ty).llvm_type(bx))
+            };
+            attributes::apply_to_callsite(
+                callsite,
+                llvm::AttributePlace::Argument(element_type_index as u32),
+                &[element_type_attr],
+            );
+        }
     }
 }
 
diff --git a/compiler/rustc_codegen_llvm/src/allocator.rs b/compiler/rustc_codegen_llvm/src/allocator.rs
index f935acb1a7e..72961ae888e 100644
--- a/compiler/rustc_codegen_llvm/src/allocator.rs
+++ b/compiler/rustc_codegen_llvm/src/allocator.rs
@@ -145,7 +145,7 @@ pub(crate) unsafe fn codegen(
     if tcx.sess.target.default_hidden_visibility {
         llvm::LLVMRustSetVisibility(ll_g, llvm::Visibility::Hidden);
     }
-    let val = tcx.sess.opts.debugging_opts.oom.should_panic();
+    let val = tcx.sess.opts.unstable_opts.oom.should_panic();
     let llval = llvm::LLVMConstInt(i8, val as u64, False);
     llvm::LLVMSetInitializer(ll_g, llval);
 
diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs
index 9394d60134f..aabbe8ac276 100644
--- a/compiler/rustc_codegen_llvm/src/attributes.rs
+++ b/compiler/rustc_codegen_llvm/src/attributes.rs
@@ -13,7 +13,7 @@ use smallvec::SmallVec;
 
 use crate::attributes;
 use crate::llvm::AttributePlace::Function;
-use crate::llvm::{self, Attribute, AttributeKind, AttributePlace};
+use crate::llvm::{self, AllocKindFlags, Attribute, AttributeKind, AttributePlace};
 use crate::llvm_util;
 pub use rustc_attr::{InlineAttr, InstructionSetAttr, OptimizeAttr};
 
@@ -56,7 +56,7 @@ pub fn sanitize_attrs<'ll>(
     no_sanitize: SanitizerSet,
 ) -> SmallVec<[&'ll Attribute; 4]> {
     let mut attrs = SmallVec::new();
-    let enabled = cx.tcx.sess.opts.debugging_opts.sanitizer - no_sanitize;
+    let enabled = cx.tcx.sess.opts.unstable_opts.sanitizer - no_sanitize;
     if enabled.contains(SanitizerSet::ADDRESS) {
         attrs.push(llvm::AttributeKind::SanitizeAddress.create_attr(cx.llcx));
     }
@@ -69,6 +69,9 @@ pub fn sanitize_attrs<'ll>(
     if enabled.contains(SanitizerSet::HWADDRESS) {
         attrs.push(llvm::AttributeKind::SanitizeHWAddress.create_attr(cx.llcx));
     }
+    if enabled.contains(SanitizerSet::SHADOWCALLSTACK) {
+        attrs.push(llvm::AttributeKind::ShadowCallStack.create_attr(cx.llcx));
+    }
     if enabled.contains(SanitizerSet::MEMTAG) {
         // Check to make sure the mte target feature is actually enabled.
         let features = cx.tcx.global_backend_features(());
@@ -136,7 +139,7 @@ fn probestack_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> {
     if cx
         .sess()
         .opts
-        .debugging_opts
+        .unstable_opts
         .sanitizer
         .intersects(SanitizerSet::ADDRESS | SanitizerSet::THREAD)
     {
@@ -149,7 +152,7 @@ fn probestack_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> {
     }
 
     // probestack doesn't play nice either with gcov profiling.
-    if cx.sess().opts.debugging_opts.profile {
+    if cx.sess().opts.unstable_opts.profile {
         return None;
     }
 
@@ -224,6 +227,10 @@ pub(crate) fn default_optimisation_attrs<'ll>(
     attrs
 }
 
+fn create_alloc_family_attr(llcx: &llvm::Context) -> &llvm::Attribute {
+    llvm::CreateAttrStringValue(llcx, "alloc-family", "__rust_alloc")
+}
+
 /// Composite function which sets LLVM attributes for function depending on its AST (`#[attribute]`)
 /// attributes.
 pub fn from_fn_attrs<'ll, 'tcx>(
@@ -275,7 +282,7 @@ pub fn from_fn_attrs<'ll, 'tcx>(
         to_add.push(uwtable_attr(cx.llcx));
     }
 
-    if cx.sess().opts.debugging_opts.profile_sample_use.is_some() {
+    if cx.sess().opts.unstable_opts.profile_sample_use.is_some() {
         to_add.push(llvm::CreateAttrString(cx.llcx, "use-sample-profile"));
     }
 
@@ -299,12 +306,61 @@ pub fn from_fn_attrs<'ll, 'tcx>(
     }
     if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
         to_add.push(AttributeKind::Naked.create_attr(cx.llcx));
+        // HACK(jubilee): "indirect branch tracking" works by attaching prologues to functions.
+        // And it is a module-level attribute, so the alternative is pulling naked functions into new LLVM modules.
+        // Otherwise LLVM's "naked" functions come with endbr prefixes per https://github.com/rust-lang/rust/issues/98768
+        to_add.push(AttributeKind::NoCfCheck.create_attr(cx.llcx));
+        // Need this for AArch64.
+        to_add.push(llvm::CreateAttrStringValue(cx.llcx, "branch-target-enforcement", "false"));
     }
-    if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR) {
+    if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR)
+        || codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR_ZEROED)
+    {
+        if llvm_util::get_version() >= (15, 0, 0) {
+            to_add.push(create_alloc_family_attr(cx.llcx));
+            // apply to argument place instead of function
+            let alloc_align = AttributeKind::AllocAlign.create_attr(cx.llcx);
+            attributes::apply_to_llfn(llfn, AttributePlace::Argument(1), &[alloc_align]);
+            to_add.push(llvm::CreateAllocSizeAttr(cx.llcx, 0));
+            let mut flags = AllocKindFlags::Alloc | AllocKindFlags::Aligned;
+            if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR) {
+                flags |= AllocKindFlags::Uninitialized;
+            } else {
+                flags |= AllocKindFlags::Zeroed;
+            }
+            to_add.push(llvm::CreateAllocKindAttr(cx.llcx, flags));
+        }
         // apply to return place instead of function (unlike all other attributes applied in this function)
         let no_alias = AttributeKind::NoAlias.create_attr(cx.llcx);
         attributes::apply_to_llfn(llfn, AttributePlace::ReturnValue, &[no_alias]);
     }
+    if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::REALLOCATOR) {
+        if llvm_util::get_version() >= (15, 0, 0) {
+            to_add.push(create_alloc_family_attr(cx.llcx));
+            to_add.push(llvm::CreateAllocKindAttr(
+                cx.llcx,
+                AllocKindFlags::Realloc | AllocKindFlags::Aligned,
+            ));
+            // applies to argument place instead of function place
+            let allocated_pointer = AttributeKind::AllocatedPointer.create_attr(cx.llcx);
+            attributes::apply_to_llfn(llfn, AttributePlace::Argument(0), &[allocated_pointer]);
+            // apply to argument place instead of function
+            let alloc_align = AttributeKind::AllocAlign.create_attr(cx.llcx);
+            attributes::apply_to_llfn(llfn, AttributePlace::Argument(2), &[alloc_align]);
+            to_add.push(llvm::CreateAllocSizeAttr(cx.llcx, 3));
+        }
+        let no_alias = AttributeKind::NoAlias.create_attr(cx.llcx);
+        attributes::apply_to_llfn(llfn, AttributePlace::ReturnValue, &[no_alias]);
+    }
+    if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::DEALLOCATOR) {
+        if llvm_util::get_version() >= (15, 0, 0) {
+            to_add.push(create_alloc_family_attr(cx.llcx));
+            to_add.push(llvm::CreateAllocKindAttr(cx.llcx, AllocKindFlags::Free));
+            // applies to argument place instead of function place
+            let allocated_pointer = AttributeKind::AllocatedPointer.create_attr(cx.llcx);
+            attributes::apply_to_llfn(llfn, AttributePlace::Argument(0), &[allocated_pointer]);
+        }
+    }
     if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::CMSE_NONSECURE_ENTRY) {
         to_add.push(llvm::CreateAttrString(cx.llcx, "cmse_nonsecure_entry"));
     }
diff --git a/compiler/rustc_codegen_llvm/src/back/archive.rs b/compiler/rustc_codegen_llvm/src/back/archive.rs
index da9d8b5fb33..baa858709a0 100644
--- a/compiler/rustc_codegen_llvm/src/back/archive.rs
+++ b/compiler/rustc_codegen_llvm/src/back/archive.rs
@@ -11,7 +11,6 @@ use std::str;
 use crate::llvm::archive_ro::{ArchiveRO, Child};
 use crate::llvm::{self, ArchiveKind, LLVMMachineType, LLVMRustCOFFShortExport};
 use rustc_codegen_ssa::back::archive::ArchiveBuilder;
-use rustc_data_structures::temp_dir::MaybeTempDir;
 use rustc_session::cstore::{DllCallingConvention, DllImport};
 use rustc_session::Session;
 
@@ -96,19 +95,23 @@ impl<'a> ArchiveBuilder<'a> for LlvmArchiveBuilder<'a> {
         }
     }
 
-    fn inject_dll_import_lib(
-        &mut self,
+    fn sess(&self) -> &Session {
+        self.sess
+    }
+
+    fn create_dll_import_lib(
+        sess: &Session,
         lib_name: &str,
         dll_imports: &[DllImport],
-        tmpdir: &MaybeTempDir,
-    ) {
+        tmpdir: &Path,
+    ) -> PathBuf {
         let output_path = {
-            let mut output_path: PathBuf = tmpdir.as_ref().to_path_buf();
+            let mut output_path: PathBuf = tmpdir.to_path_buf();
             output_path.push(format!("{}_imports", lib_name));
             output_path.with_extension("lib")
         };
 
-        let target = &self.sess.target;
+        let target = &sess.target;
         let mingw_gnu_toolchain = target.vendor == "pc"
             && target.os == "windows"
             && target.env == "gnu"
@@ -117,7 +120,7 @@ impl<'a> ArchiveBuilder<'a> for LlvmArchiveBuilder<'a> {
         let import_name_and_ordinal_vector: Vec<(String, Option<u16>)> = dll_imports
             .iter()
             .map(|import: &DllImport| {
-                if self.sess.target.arch == "x86" {
+                if sess.target.arch == "x86" {
                     (
                         LlvmArchiveBuilder::i686_decorated_name(import, mingw_gnu_toolchain),
                         import.ordinal,
@@ -134,8 +137,7 @@ impl<'a> ArchiveBuilder<'a> for LlvmArchiveBuilder<'a> {
             // that loaded but crashed with an AV upon calling one of the imported
             // functions.  Therefore, use binutils to create the import library instead,
             // by writing a .DEF file to the temp dir and calling binutils's dlltool.
-            let def_file_path =
-                tmpdir.as_ref().join(format!("{}_imports", lib_name)).with_extension("def");
+            let def_file_path = tmpdir.join(format!("{}_imports", lib_name)).with_extension("def");
 
             let def_file_content = format!(
                 "EXPORTS\n{}",
@@ -154,11 +156,11 @@ impl<'a> ArchiveBuilder<'a> for LlvmArchiveBuilder<'a> {
             match std::fs::write(&def_file_path, def_file_content) {
                 Ok(_) => {}
                 Err(e) => {
-                    self.sess.fatal(&format!("Error writing .DEF file: {}", e));
+                    sess.fatal(&format!("Error writing .DEF file: {}", e));
                 }
             };
 
-            let dlltool = find_binutils_dlltool(self.sess);
+            let dlltool = find_binutils_dlltool(sess);
             let result = std::process::Command::new(dlltool)
                 .args([
                     "-d",
@@ -172,9 +174,9 @@ impl<'a> ArchiveBuilder<'a> for LlvmArchiveBuilder<'a> {
 
             match result {
                 Err(e) => {
-                    self.sess.fatal(&format!("Error calling dlltool: {}", e));
+                    sess.fatal(&format!("Error calling dlltool: {}", e));
                 }
-                Ok(output) if !output.status.success() => self.sess.fatal(&format!(
+                Ok(output) if !output.status.success() => sess.fatal(&format!(
                     "Dlltool could not create import library: {}\n{}",
                     String::from_utf8_lossy(&output.stdout),
                     String::from_utf8_lossy(&output.stderr)
@@ -220,13 +222,13 @@ impl<'a> ArchiveBuilder<'a> for LlvmArchiveBuilder<'a> {
                     output_path_z.as_ptr(),
                     ffi_exports.as_ptr(),
                     ffi_exports.len(),
-                    llvm_machine_type(&self.sess.target.arch) as u16,
-                    !self.sess.target.is_like_msvc,
+                    llvm_machine_type(&sess.target.arch) as u16,
+                    !sess.target.is_like_msvc,
                 )
             };
 
             if result == crate::llvm::LLVMRustResult::Failure {
-                self.sess.fatal(&format!(
+                sess.fatal(&format!(
                     "Error creating import library for {}: {}",
                     lib_name,
                     llvm::last_error().unwrap_or("unknown LLVM error".to_string())
@@ -234,13 +236,7 @@ impl<'a> ArchiveBuilder<'a> for LlvmArchiveBuilder<'a> {
             }
         };
 
-        self.add_archive(&output_path, |_| false).unwrap_or_else(|e| {
-            self.sess.fatal(&format!(
-                "failed to add native library {}: {}",
-                output_path.display(),
-                e
-            ));
-        });
+        output_path
     }
 }
 
@@ -351,7 +347,7 @@ fn string_to_io_error(s: String) -> io::Error {
 
 fn find_binutils_dlltool(sess: &Session) -> OsString {
     assert!(sess.target.options.is_like_windows && !sess.target.options.is_like_msvc);
-    if let Some(dlltool_path) = &sess.opts.debugging_opts.dlltool {
+    if let Some(dlltool_path) = &sess.opts.unstable_opts.dlltool {
         return dlltool_path.clone().into_os_string();
     }
 
diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs
index 38402e04313..3731c6bcfe7 100644
--- a/compiler/rustc_codegen_llvm/src/back/lto.rs
+++ b/compiler/rustc_codegen_llvm/src/back/lto.rs
@@ -199,7 +199,7 @@ pub(crate) fn run_thin(
 
 pub(crate) fn prepare_thin(module: ModuleCodegen<ModuleLlvm>) -> (String, ThinBuffer) {
     let name = module.name.clone();
-    let buffer = ThinBuffer::new(module.module_llvm.llmod());
+    let buffer = ThinBuffer::new(module.module_llvm.llmod(), true);
     (name, buffer)
 }
 
@@ -325,20 +325,6 @@ fn fat_lto(
         drop(linker);
         save_temp_bitcode(cgcx, &module, "lto.input");
 
-        // Fat LTO also suffers from the invalid DWARF issue similar to Thin LTO.
-        // Here we rewrite all `DICompileUnit` pointers if there is only one `DICompileUnit`.
-        // This only works around the problem when codegen-units = 1.
-        // Refer to the comments in the `optimize_thin_module` function for more details.
-        let mut cu1 = ptr::null_mut();
-        let mut cu2 = ptr::null_mut();
-        unsafe { llvm::LLVMRustLTOGetDICompileUnit(llmod, &mut cu1, &mut cu2) };
-        if !cu2.is_null() {
-            let _timer =
-                cgcx.prof.generic_activity_with_arg("LLVM_fat_lto_patch_debuginfo", &*module.name);
-            unsafe { llvm::LLVMRustLTOPatchDICompileUnit(llmod, cu1) };
-            save_temp_bitcode(cgcx, &module, "fat-lto-after-patch");
-        }
-
         // Internalize everything below threshold to help strip out more modules and such.
         unsafe {
             let ptr = symbols_below_threshold.as_ptr();
@@ -709,9 +695,9 @@ unsafe impl Send for ThinBuffer {}
 unsafe impl Sync for ThinBuffer {}
 
 impl ThinBuffer {
-    pub fn new(m: &llvm::Module) -> ThinBuffer {
+    pub fn new(m: &llvm::Module, is_thin: bool) -> ThinBuffer {
         unsafe {
-            let buffer = llvm::LLVMRustThinLTOBufferCreate(m);
+            let buffer = llvm::LLVMRustThinLTOBufferCreate(m, is_thin);
             ThinBuffer(buffer)
         }
     }
@@ -769,7 +755,7 @@ pub unsafe fn optimize_thin_module(
         // an error.
         let mut cu1 = ptr::null_mut();
         let mut cu2 = ptr::null_mut();
-        llvm::LLVMRustLTOGetDICompileUnit(llmod, &mut cu1, &mut cu2);
+        llvm::LLVMRustThinLTOGetDICompileUnit(llmod, &mut cu1, &mut cu2);
         if !cu2.is_null() {
             let msg = "multiple source DICompileUnits found";
             return Err(write::llvm_err(&diag_handler, msg));
@@ -858,7 +844,7 @@ pub unsafe fn optimize_thin_module(
             let _timer = cgcx
                 .prof
                 .generic_activity_with_arg("LLVM_thin_lto_patch_debuginfo", thin_module.name());
-            llvm::LLVMRustLTOPatchDICompileUnit(llmod, cu1);
+            llvm::LLVMRustThinLTOPatchDICompileUnit(llmod, cu1);
             save_temp_bitcode(cgcx, &module, "thin-lto-after-patch");
         }
 
diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs
index ab8874d796d..534d32e8a90 100644
--- a/compiler/rustc_codegen_llvm/src/back/write.rs
+++ b/compiler/rustc_codegen_llvm/src/back/write.rs
@@ -107,7 +107,7 @@ pub fn create_target_machine(tcx: TyCtxt<'_>, mod_name: &str) -> &'static mut ll
     let split_dwarf_file = if tcx.sess.target_can_use_split_dwarf() {
         tcx.output_filenames(()).split_dwarf_path(
             tcx.sess.split_debuginfo(),
-            tcx.sess.opts.debugging_opts.split_dwarf_kind,
+            tcx.sess.opts.unstable_opts.split_dwarf_kind,
             Some(mod_name),
         )
     } else {
@@ -182,9 +182,9 @@ pub fn target_machine_factory(
     let use_softfp = sess.opts.cg.soft_float;
 
     let ffunction_sections =
-        sess.opts.debugging_opts.function_sections.unwrap_or(sess.target.function_sections);
+        sess.opts.unstable_opts.function_sections.unwrap_or(sess.target.function_sections);
     let fdata_sections = ffunction_sections;
-    let funique_section_names = !sess.opts.debugging_opts.no_unique_section_names;
+    let funique_section_names = !sess.opts.unstable_opts.no_unique_section_names;
 
     let code_model = to_llvm_code_model(sess.code_model());
 
@@ -202,15 +202,15 @@ pub fn target_machine_factory(
     let features = CString::new(target_features.join(",")).unwrap();
     let abi = SmallCStr::new(&sess.target.llvm_abiname);
     let trap_unreachable =
-        sess.opts.debugging_opts.trap_unreachable.unwrap_or(sess.target.trap_unreachable);
-    let emit_stack_size_section = sess.opts.debugging_opts.emit_stack_sizes;
+        sess.opts.unstable_opts.trap_unreachable.unwrap_or(sess.target.trap_unreachable);
+    let emit_stack_size_section = sess.opts.unstable_opts.emit_stack_sizes;
 
     let asm_comments = sess.asm_comments();
     let relax_elf_relocations =
-        sess.opts.debugging_opts.relax_elf_relocations.unwrap_or(sess.target.relax_elf_relocations);
+        sess.opts.unstable_opts.relax_elf_relocations.unwrap_or(sess.target.relax_elf_relocations);
 
     let use_init_array =
-        !sess.opts.debugging_opts.use_ctors_section.unwrap_or(sess.target.use_ctors_section);
+        !sess.opts.unstable_opts.use_ctors_section.unwrap_or(sess.target.use_ctors_section);
 
     let path_mapping = sess.source_map().path_mapping().clone();
 
@@ -790,7 +790,7 @@ pub(crate) unsafe fn codegen(
             let _timer = cgcx
                 .prof
                 .generic_activity_with_arg("LLVM_module_codegen_make_bitcode", &*module.name);
-            let thin = ThinBuffer::new(llmod);
+            let thin = ThinBuffer::new(llmod, config.emit_thin_lto);
             let data = thin.data();
 
             if let Some(bitcode_filename) = bc_out.file_name() {
diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs
index 4a4cccb490d..d3096c73a8a 100644
--- a/compiler/rustc_codegen_llvm/src/builder.rs
+++ b/compiler/rustc_codegen_llvm/src/builder.rs
@@ -626,32 +626,6 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
         }
     }
 
-    fn type_metadata(&mut self, function: &'ll Value, typeid: String) {
-        let typeid_metadata = self.typeid_metadata(typeid);
-        let v = [self.const_usize(0), typeid_metadata];
-        unsafe {
-            llvm::LLVMGlobalSetMetadata(
-                function,
-                llvm::MD_type as c_uint,
-                llvm::LLVMValueAsMetadata(llvm::LLVMMDNodeInContext(
-                    self.cx.llcx,
-                    v.as_ptr(),
-                    v.len() as c_uint,
-                )),
-            )
-        }
-    }
-
-    fn typeid_metadata(&mut self, typeid: String) -> Self::Value {
-        unsafe {
-            llvm::LLVMMDStringInContext(
-                self.cx.llcx,
-                typeid.as_ptr() as *const c_char,
-                typeid.as_bytes().len() as c_uint,
-            )
-        }
-    }
-
     fn store(&mut self, val: &'ll Value, ptr: &'ll Value, align: Align) -> &'ll Value {
         self.store_with_flags(val, ptr, align, MemFlags::empty())
     }
diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs
index 77cbbf4c6ca..fb4da9a5f33 100644
--- a/compiler/rustc_codegen_llvm/src/common.rs
+++ b/compiler/rustc_codegen_llvm/src/common.rs
@@ -257,6 +257,15 @@ impl<'ll, 'tcx> ConstMethods<'tcx> for CodegenCx<'ll, 'tcx> {
                         self.get_fn_addr(fn_instance.polymorphize(self.tcx)),
                         self.data_layout().instruction_address_space,
                     ),
+                    GlobalAlloc::VTable(ty, trait_ref) => {
+                        let alloc = self
+                            .tcx
+                            .global_alloc(self.tcx.vtable_allocation((ty, trait_ref)))
+                            .unwrap_memory();
+                        let init = const_alloc_to_llvm(self, alloc);
+                        let value = self.static_addr_of(init, alloc.inner().align, None);
+                        (value, AddressSpace::DATA)
+                    }
                     GlobalAlloc::Static(def_id) => {
                         assert!(self.tcx.is_static(def_id));
                         assert!(!self.tcx.is_thread_local_static(def_id));
diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs
index 5bbbfe9a4ab..18467e37082 100644
--- a/compiler/rustc_codegen_llvm/src/consts.rs
+++ b/compiler/rustc_codegen_llvm/src/consts.rs
@@ -64,7 +64,7 @@ pub fn const_alloc_to_llvm<'ll>(cx: &CodegenCx<'ll, '_>, alloc: ConstAllocation<
             // See https://github.com/rust-lang/rust/issues/84565.
             1
         } else {
-            cx.sess().opts.debugging_opts.uninit_const_chunk_threshold
+            cx.sess().opts.unstable_opts.uninit_const_chunk_threshold
         };
         let allow_uninit_chunks = chunks.clone().take(max.saturating_add(1)).count() <= max;
 
@@ -101,7 +101,9 @@ pub fn const_alloc_to_llvm<'ll>(cx: &CodegenCx<'ll, '_>, alloc: ConstAllocation<
 
         let address_space = match cx.tcx.global_alloc(alloc_id) {
             GlobalAlloc::Function(..) => cx.data_layout().instruction_address_space,
-            GlobalAlloc::Static(..) | GlobalAlloc::Memory(..) => AddressSpace::DATA,
+            GlobalAlloc::Static(..) | GlobalAlloc::Memory(..) | GlobalAlloc::VTable(..) => {
+                AddressSpace::DATA
+            }
         };
 
         llvals.push(cx.scalar_to_backend(
@@ -535,10 +537,20 @@ impl<'ll> StaticMethods for CodegenCx<'ll, '_> {
 
                 // The semantics of #[used] in Rust only require the symbol to make it into the
                 // object file. It is explicitly allowed for the linker to strip the symbol if it
-                // is dead. As such, use llvm.compiler.used instead of llvm.used.
+                // is dead, which means we are allowed use `llvm.compiler.used` instead of
+                // `llvm.used` here.
+                //
                 // Additionally, https://reviews.llvm.org/D97448 in LLVM 13 started emitting unique
                 // sections with SHF_GNU_RETAIN flag for llvm.used symbols, which may trigger bugs
-                // in some versions of the gold linker.
+                // in the handling of `.init_array` (the static constructor list) in versions of
+                // the gold linker (prior to the one released with binutils 2.36).
+                //
+                // That said, we only ever emit these when compiling for ELF targets, unless
+                // `#[used(compiler)]` is explicitly requested. This is to avoid similar breakage
+                // on other targets, in particular MachO targets have *their* static constructor
+                // lists broken if `llvm.compiler.used` is emitted rather than llvm.used. However,
+                // that check happens when assigning the `CodegenFnAttrFlags` in `rustc_typeck`,
+                // so we don't need to take care of it here.
                 self.add_compiler_used_global(g);
             }
             if attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER) {
diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs
index 55e4a4a7255..5857b83f6c9 100644
--- a/compiler/rustc_codegen_llvm/src/context.rs
+++ b/compiler/rustc_codegen_llvm/src/context.rs
@@ -275,7 +275,7 @@ pub unsafe fn create_module<'ll>(
         }
     }
 
-    if let Some(BranchProtection { bti, pac_ret }) = sess.opts.debugging_opts.branch_protection {
+    if let Some(BranchProtection { bti, pac_ret }) = sess.opts.unstable_opts.branch_protection {
         if sess.target.arch != "aarch64" {
             sess.err("-Zbranch-protection is only supported on aarch64");
         } else {
@@ -308,7 +308,7 @@ pub unsafe fn create_module<'ll>(
     }
 
     // Pass on the control-flow protection flags to LLVM (equivalent to `-fcf-protection` in Clang).
-    if let CFProtection::Branch | CFProtection::Full = sess.opts.debugging_opts.cf_protection {
+    if let CFProtection::Branch | CFProtection::Full = sess.opts.unstable_opts.cf_protection {
         llvm::LLVMRustAddModuleFlag(
             llmod,
             llvm::LLVMModFlagBehavior::Override,
@@ -316,7 +316,7 @@ pub unsafe fn create_module<'ll>(
             1,
         )
     }
-    if let CFProtection::Return | CFProtection::Full = sess.opts.debugging_opts.cf_protection {
+    if let CFProtection::Return | CFProtection::Full = sess.opts.unstable_opts.cf_protection {
         llvm::LLVMRustAddModuleFlag(
             llmod,
             llvm::LLVMModFlagBehavior::Override,
@@ -325,7 +325,7 @@ pub unsafe fn create_module<'ll>(
         )
     }
 
-    if sess.opts.debugging_opts.virtual_function_elimination {
+    if sess.opts.unstable_opts.virtual_function_elimination {
         llvm::LLVMRustAddModuleFlag(
             llmod,
             llvm::LLVMModFlagBehavior::Error,
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs b/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs
index 5186aee57fb..80fd9726fc7 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs
@@ -55,7 +55,7 @@ pub fn get_or_insert_gdb_debug_scripts_section_global<'ll>(cx: &CodegenCx<'ll, '
             // The initial byte `4` instructs GDB that the following pretty printer
             // is defined inline as opposed to in a standalone file.
             section_contents.extend_from_slice(b"\x04");
-            let vis_name = format!("pretty-printer-{}-{}\n", crate_name.as_str(), index);
+            let vis_name = format!("pretty-printer-{}-{}\n", crate_name, index);
             section_contents.extend_from_slice(vis_name.as_bytes());
             section_contents.extend_from_slice(&visualizer.src);
 
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
index d5f39a45670..bd84100e0e8 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
@@ -824,7 +824,7 @@ pub fn build_compile_unit_di_node<'ll, 'tcx>(
         output_filenames
             .split_dwarf_path(
                 tcx.sess.split_debuginfo(),
-                tcx.sess.opts.debugging_opts.split_dwarf_kind,
+                tcx.sess.opts.unstable_opts.split_dwarf_kind,
                 Some(codegen_unit_name),
             )
             // We get a path relative to the working directory from split_dwarf_path
@@ -881,15 +881,15 @@ pub fn build_compile_unit_di_node<'ll, 'tcx>(
             split_name.len(),
             kind,
             0,
-            tcx.sess.opts.debugging_opts.split_dwarf_inlining,
+            tcx.sess.opts.unstable_opts.split_dwarf_inlining,
         );
 
-        if tcx.sess.opts.debugging_opts.profile {
+        if tcx.sess.opts.unstable_opts.profile {
             let cu_desc_metadata =
                 llvm::LLVMRustMetadataAsValue(debug_context.llcontext, unit_metadata);
             let default_gcda_path = &output_filenames.with_extension("gcda");
             let gcda_path =
-                tcx.sess.opts.debugging_opts.profile_emit.as_ref().unwrap_or(default_gcda_path);
+                tcx.sess.opts.unstable_opts.profile_emit.as_ref().unwrap_or(default_gcda_path);
 
             let gcov_cu_info = [
                 path_to_mdstring(debug_context.llcontext, &output_filenames.with_extension("gcno")),
@@ -1420,7 +1420,7 @@ fn build_vtable_type_di_node<'ll, 'tcx>(
         cx,
         type_map::stub(
             cx,
-            Stub::VtableTy { vtable_holder },
+            Stub::VTableTy { vtable_holder },
             unique_type_id,
             &vtable_type_name,
             (size, pointer_align),
@@ -1559,7 +1559,7 @@ pub fn create_vtable_di_node<'ll, 'tcx>(
 ) {
     // FIXME(flip1995): The virtual function elimination optimization only works with full LTO in
     // LLVM at the moment.
-    if cx.sess().opts.debugging_opts.virtual_function_elimination && cx.sess().lto() == Lto::Fat {
+    if cx.sess().opts.unstable_opts.virtual_function_elimination && cx.sess().lto() == Lto::Fat {
         vcall_visibility_metadata(cx, ty, poly_trait_ref, vtable);
     }
 
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs
index 8fc8118849b..ce2f419c4ac 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs
@@ -146,7 +146,7 @@ impl<'ll> DINodeCreationResult<'ll> {
 pub enum Stub<'ll> {
     Struct,
     Union,
-    VtableTy { vtable_holder: &'ll DIType },
+    VTableTy { vtable_holder: &'ll DIType },
 }
 
 pub struct StubInfo<'ll, 'tcx> {
@@ -180,9 +180,9 @@ pub(super) fn stub<'ll, 'tcx>(
     let unique_type_id_str = unique_type_id.generate_unique_id_string(cx.tcx);
 
     let metadata = match kind {
-        Stub::Struct | Stub::VtableTy { .. } => {
+        Stub::Struct | Stub::VTableTy { .. } => {
             let vtable_holder = match kind {
-                Stub::VtableTy { vtable_holder } => Some(vtable_holder),
+                Stub::VTableTy { vtable_holder } => Some(vtable_holder),
                 _ => None,
             };
             unsafe {
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
index 730048d061b..cf591295b84 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
@@ -97,23 +97,26 @@ impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> {
         unsafe {
             llvm::LLVMRustDIBuilderFinalize(self.builder);
 
-            // Debuginfo generation in LLVM by default uses a higher
-            // version of dwarf than macOS currently understands. We can
-            // instruct LLVM to emit an older version of dwarf, however,
-            // for macOS to understand. For more info see #11352
-            // This can be overridden using --llvm-opts -dwarf-version,N.
-            // Android has the same issue (#22398)
-            let dwarf_version =
-                sess.opts.debugging_opts.dwarf_version.unwrap_or(sess.target.default_dwarf_version);
-            llvm::LLVMRustAddModuleFlag(
-                self.llmod,
-                llvm::LLVMModFlagBehavior::Warning,
-                "Dwarf Version\0".as_ptr().cast(),
-                dwarf_version,
-            );
-
-            // Indicate that we want CodeView debug information on MSVC
-            if sess.target.is_like_msvc {
+            if !sess.target.is_like_msvc {
+                // Debuginfo generation in LLVM by default uses a higher
+                // version of dwarf than macOS currently understands. We can
+                // instruct LLVM to emit an older version of dwarf, however,
+                // for macOS to understand. For more info see #11352
+                // This can be overridden using --llvm-opts -dwarf-version,N.
+                // Android has the same issue (#22398)
+                let dwarf_version = sess
+                    .opts
+                    .unstable_opts
+                    .dwarf_version
+                    .unwrap_or(sess.target.default_dwarf_version);
+                llvm::LLVMRustAddModuleFlag(
+                    self.llmod,
+                    llvm::LLVMModFlagBehavior::Warning,
+                    "Dwarf Version\0".as_ptr().cast(),
+                    dwarf_version,
+                );
+            } else {
+                // Indicate that we want CodeView debug information on MSVC
                 llvm::LLVMRustAddModuleFlag(
                     self.llmod,
                     llvm::LLVMModFlagBehavior::Warning,
diff --git a/compiler/rustc_codegen_llvm/src/declare.rs b/compiler/rustc_codegen_llvm/src/declare.rs
index 5a5c4f7f860..fa0ecd18fc8 100644
--- a/compiler/rustc_codegen_llvm/src/declare.rs
+++ b/compiler/rustc_codegen_llvm/src/declare.rs
@@ -18,7 +18,9 @@ use crate::llvm;
 use crate::llvm::AttributePlace::Function;
 use crate::type_::Type;
 use crate::value::Value;
+use rustc_codegen_ssa::traits::TypeMembershipMethods;
 use rustc_middle::ty::Ty;
+use rustc_symbol_mangling::typeid::typeid_for_fnabi;
 use smallvec::SmallVec;
 use tracing::debug;
 
@@ -97,6 +99,12 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
             fn_abi.llvm_type(self),
         );
         fn_abi.apply_attrs_llfn(self, llfn);
+
+        if self.tcx.sess.is_sanitizer_cfi_enabled() {
+            let typeid = typeid_for_fnabi(self.tcx, fn_abi);
+            self.set_type_metadata(llfn, typeid);
+        }
+
         llfn
     }
 
diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs
index a7dd8e16d28..25ce1cef944 100644
--- a/compiler/rustc_codegen_llvm/src/lib.rs
+++ b/compiler/rustc_codegen_llvm/src/lib.rs
@@ -6,7 +6,7 @@
 
 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
 #![feature(hash_raw_entry)]
-#![feature(let_chains)]
+#![cfg_attr(bootstrap, feature(let_chains))]
 #![feature(let_else)]
 #![feature(extern_types)]
 #![feature(once_cell)]
@@ -324,8 +324,8 @@ impl CodegenBackend for LlvmCodegenBackend {
         llvm_util::print_version();
     }
 
-    fn target_features(&self, sess: &Session) -> Vec<Symbol> {
-        target_features(sess)
+    fn target_features(&self, sess: &Session, allow_unstable: bool) -> Vec<Symbol> {
+        target_features(sess, allow_unstable)
     }
 
     fn codegen_crate<'tcx>(
@@ -355,7 +355,7 @@ impl CodegenBackend for LlvmCodegenBackend {
             .join(sess);
 
         sess.time("llvm_dump_timing_file", || {
-            if sess.opts.debugging_opts.llvm_time_trace {
+            if sess.opts.unstable_opts.llvm_time_trace {
                 let file_name = outputs.with_extension("llvm_timings.json");
                 llvm_util::time_trace_profiler_finish(&file_name);
             }
diff --git a/compiler/rustc_codegen_llvm/src/llvm/diagnostic.rs b/compiler/rustc_codegen_llvm/src/llvm/diagnostic.rs
index e2fa5e488ed..45de284d22a 100644
--- a/compiler/rustc_codegen_llvm/src/llvm/diagnostic.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm/diagnostic.rs
@@ -20,19 +20,6 @@ pub enum OptimizationDiagnosticKind {
     OptimizationRemarkOther,
 }
 
-impl OptimizationDiagnosticKind {
-    pub fn describe(self) -> &'static str {
-        match self {
-            OptimizationRemark | OptimizationRemarkOther => "remark",
-            OptimizationMissed => "missed",
-            OptimizationAnalysis => "analysis",
-            OptimizationAnalysisFPCommute => "floating-point",
-            OptimizationAnalysisAliasing => "aliasing",
-            OptimizationFailure => "failure",
-        }
-    }
-}
-
 pub struct OptimizationDiagnostic<'ll> {
     pub kind: OptimizationDiagnosticKind,
     pub pass_name: String,
diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
index 5ebc2d6139f..3139f93bfef 100644
--- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
@@ -191,6 +191,11 @@ pub enum AttributeKind {
     StackProtect = 32,
     NoUndef = 33,
     SanitizeMemTag = 34,
+    NoCfCheck = 35,
+    ShadowCallStack = 36,
+    AllocSize = 37,
+    AllocatedPointer = 38,
+    AllocAlign = 39,
 }
 
 /// LLVMIntPredicate
@@ -567,16 +572,6 @@ pub enum ArchiveKind {
     K_COFF,
 }
 
-/// LLVMRustPassKind
-#[derive(Copy, Clone, PartialEq, Debug)]
-#[repr(C)]
-#[allow(dead_code)] // Variants constructed by C++.
-pub enum PassKind {
-    Other,
-    Function,
-    Module,
-}
-
 // LLVMRustThinLTOData
 extern "C" {
     pub type ThinLTOData;
@@ -587,10 +582,6 @@ extern "C" {
     pub type ThinLTOBuffer;
 }
 
-// LLVMRustModuleNameCallback
-pub type ThinLTOModuleNameCallback =
-    unsafe extern "C" fn(*mut c_void, *const c_char, *const c_char);
-
 /// LLVMRustThinLTOModule
 #[repr(C)]
 pub struct ThinLTOModule {
@@ -656,9 +647,6 @@ extern "C" {
 }
 #[repr(C)]
 pub struct Builder<'a>(InvariantOpaque<'a>);
-extern "C" {
-    pub type MemoryBuffer;
-}
 #[repr(C)]
 pub struct PassManager<'a>(InvariantOpaque<'a>);
 extern "C" {
@@ -984,6 +972,22 @@ pub mod debuginfo {
     }
 }
 
+use bitflags::bitflags;
+// These values **must** match with LLVMRustAllocKindFlags
+bitflags! {
+    #[repr(transparent)]
+    #[derive(Default)]
+    pub struct AllocKindFlags : u64 {
+        const Unknown = 0;
+        const Alloc = 1;
+        const Realloc = 1 << 1;
+        const Free = 1 << 2;
+        const Uninitialized = 1 << 3;
+        const Zeroed = 1 << 4;
+        const Aligned = 1 << 5;
+    }
+}
+
 extern "C" {
     pub type ModuleBuffer;
 }
@@ -1011,7 +1015,6 @@ extern "C" {
     pub fn LLVMSetDataLayout(M: &Module, Triple: *const c_char);
 
     /// See Module::setModuleInlineAsm.
-    pub fn LLVMSetModuleInlineAsm2(M: &Module, Asm: *const c_char, AsmLen: size_t);
     pub fn LLVMRustAppendModuleInlineAsm(M: &Module, Asm: *const c_char, AsmLen: size_t);
 
     /// See llvm::LLVMTypeKind::getTypeID.
@@ -1165,7 +1168,6 @@ extern "C" {
     pub fn LLVMGetInitializer(GlobalVar: &Value) -> Option<&Value>;
     pub fn LLVMSetInitializer<'a>(GlobalVar: &'a Value, ConstantVal: &'a Value);
     pub fn LLVMIsThreadLocal(GlobalVar: &Value) -> Bool;
-    pub fn LLVMSetThreadLocal(GlobalVar: &Value, IsThreadLocal: Bool);
     pub fn LLVMSetThreadLocalMode(GlobalVar: &Value, Mode: ThreadLocalMode);
     pub fn LLVMIsGlobalConstant(GlobalVar: &Value) -> Bool;
     pub fn LLVMSetGlobalConstant(GlobalVar: &Value, IsConstant: Bool);
@@ -1190,7 +1192,10 @@ extern "C" {
     pub fn LLVMRustCreateDereferenceableOrNullAttr(C: &Context, bytes: u64) -> &Attribute;
     pub fn LLVMRustCreateByValAttr<'a>(C: &'a Context, ty: &'a Type) -> &'a Attribute;
     pub fn LLVMRustCreateStructRetAttr<'a>(C: &'a Context, ty: &'a Type) -> &'a Attribute;
+    pub fn LLVMRustCreateElementTypeAttr<'a>(C: &'a Context, ty: &'a Type) -> &'a Attribute;
     pub fn LLVMRustCreateUWTableAttr(C: &Context, async_: bool) -> &Attribute;
+    pub fn LLVMRustCreateAllocSizeAttr(C: &Context, size_arg: u32) -> &Attribute;
+    pub fn LLVMRustCreateAllocKindAttr(C: &Context, size_arg: u64) -> &Attribute;
 
     // Operations on functions
     pub fn LLVMRustGetOrInsertFunction<'a>(
@@ -2244,7 +2249,6 @@ extern "C" {
 
     pub fn LLVMIsAConstantInt(value_ref: &Value) -> Option<&ConstantInt>;
 
-    pub fn LLVMRustPassKind(Pass: &Pass) -> PassKind;
     pub fn LLVMRustFindAndCreatePass(Pass: *const c_char) -> Option<&'static mut Pass>;
     pub fn LLVMRustCreateAddressSanitizerFunctionPass(Recover: bool) -> &'static mut Pass;
     pub fn LLVMRustCreateModuleAddressSanitizerPass(Recover: bool) -> &'static mut Pass;
@@ -2361,7 +2365,6 @@ extern "C" {
     ) -> LLVMRustResult;
     pub fn LLVMRustSetLLVMOptions(Argc: c_int, Argv: *const *const c_char);
     pub fn LLVMRustPrintPasses();
-    pub fn LLVMRustGetInstructionCount(M: &Module) -> u32;
     pub fn LLVMRustSetNormalizedTarget(M: &Module, triple: *const c_char);
     pub fn LLVMRustAddAlwaysInlinePass(P: &PassManagerBuilder, AddLifetimes: bool);
     pub fn LLVMRustRunRestrictionPass(M: &Module, syms: *const *const c_char, len: size_t);
@@ -2459,7 +2462,6 @@ extern "C" {
     pub fn LLVMRustPositionBuilderAtStart<'a>(B: &Builder<'a>, BB: &'a BasicBlock);
 
     pub fn LLVMRustSetComdat<'a>(M: &'a Module, V: &'a Value, Name: *const c_char, NameLen: size_t);
-    pub fn LLVMRustUnsetComdat(V: &Value);
     pub fn LLVMRustSetModulePICLevel(M: &Module);
     pub fn LLVMRustSetModulePIELevel(M: &Module);
     pub fn LLVMRustSetModuleCodeModel(M: &Module, Model: CodeModel);
@@ -2469,7 +2471,7 @@ extern "C" {
     pub fn LLVMRustModuleBufferFree(p: &'static mut ModuleBuffer);
     pub fn LLVMRustModuleCost(M: &Module) -> u64;
 
-    pub fn LLVMRustThinLTOBufferCreate(M: &Module) -> &'static mut ThinLTOBuffer;
+    pub fn LLVMRustThinLTOBufferCreate(M: &Module, is_thin: bool) -> &'static mut ThinLTOBuffer;
     pub fn LLVMRustThinLTOBufferFree(M: &'static mut ThinLTOBuffer);
     pub fn LLVMRustThinLTOBufferPtr(M: &ThinLTOBuffer) -> *const c_char;
     pub fn LLVMRustThinLTOBufferLen(M: &ThinLTOBuffer) -> size_t;
@@ -2491,11 +2493,6 @@ extern "C" {
         Module: &Module,
         Target: &TargetMachine,
     ) -> bool;
-    pub fn LLVMRustGetThinLTOModuleImports(
-        Data: *const ThinLTOData,
-        ModuleNameCallback: ThinLTOModuleNameCallback,
-        CallbackPayload: *mut c_void,
-    );
     pub fn LLVMRustFreeThinLTOData(Data: &'static mut ThinLTOData);
     pub fn LLVMRustParseBitcodeForLTO(
         Context: &Context,
@@ -2508,8 +2505,12 @@ extern "C" {
         len: usize,
         out_len: &mut usize,
     ) -> *const u8;
-    pub fn LLVMRustLTOGetDICompileUnit(M: &Module, CU1: &mut *mut c_void, CU2: &mut *mut c_void);
-    pub fn LLVMRustLTOPatchDICompileUnit(M: &Module, CU: *mut c_void);
+    pub fn LLVMRustThinLTOGetDICompileUnit(
+        M: &Module,
+        CU1: &mut *mut c_void,
+        CU2: &mut *mut c_void,
+    );
+    pub fn LLVMRustThinLTOPatchDICompileUnit(M: &Module, CU: *mut c_void);
 
     pub fn LLVMRustLinkerNew(M: &Module) -> &mut Linker<'_>;
     pub fn LLVMRustLinkerAdd(
@@ -2541,4 +2542,6 @@ extern "C" {
 
     #[allow(improper_ctypes)]
     pub fn LLVMRustGetMangledName(V: &Value, out: &RustString);
+
+    pub fn LLVMRustGetElementTypeArgIndex(CallSite: &Value) -> i32;
 }
diff --git a/compiler/rustc_codegen_llvm/src/llvm/mod.rs b/compiler/rustc_codegen_llvm/src/llvm/mod.rs
index 48fbc1de8ee..6602a4ab863 100644
--- a/compiler/rustc_codegen_llvm/src/llvm/mod.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm/mod.rs
@@ -95,6 +95,14 @@ pub fn CreateUWTableAttr(llcx: &Context, async_: bool) -> &Attribute {
     unsafe { LLVMRustCreateUWTableAttr(llcx, async_) }
 }
 
+pub fn CreateAllocSizeAttr(llcx: &Context, size_arg: u32) -> &Attribute {
+    unsafe { LLVMRustCreateAllocSizeAttr(llcx, size_arg) }
+}
+
+pub fn CreateAllocKindAttr(llcx: &Context, kind_arg: AllocKindFlags) -> &Attribute {
+    unsafe { LLVMRustCreateAllocKindAttr(llcx, kind_arg.bits()) }
+}
+
 #[derive(Copy, Clone)]
 pub enum AttributePlace {
     ReturnValue,
@@ -158,12 +166,6 @@ pub fn SetUniqueComdat(llmod: &Module, val: &Value) {
     }
 }
 
-pub fn UnsetComdat(val: &Value) {
-    unsafe {
-        LLVMRustUnsetComdat(val);
-    }
-}
-
 pub fn SetUnnamedAddress(global: &Value, unnamed: UnnamedAddr) {
     unsafe {
         LLVMSetUnnamedAddress(global, unnamed);
diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs
index ce6c6e3215c..a0a640473eb 100644
--- a/compiler/rustc_codegen_llvm/src/llvm_util.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs
@@ -87,7 +87,7 @@ unsafe fn configure_llvm(sess: &Session) {
             add("-debug-pass=Structure", false);
         }
         if sess.target.generate_arange_section
-            && !sess.opts.debugging_opts.no_generate_arange_section
+            && !sess.opts.unstable_opts.no_generate_arange_section
         {
             add("-generate-arange-section", false);
         }
@@ -102,7 +102,7 @@ unsafe fn configure_llvm(sess: &Session) {
             add("-enable-machine-outliner=never", false);
         }
 
-        match sess.opts.debugging_opts.merge_functions.unwrap_or(sess.target.merge_functions) {
+        match sess.opts.unstable_opts.merge_functions.unwrap_or(sess.target.merge_functions) {
             MergeFunctions::Disabled | MergeFunctions::Trampolines => {}
             MergeFunctions::Aliases => {
                 add("-mergefunc-use-aliases", false);
@@ -125,7 +125,7 @@ unsafe fn configure_llvm(sess: &Session) {
         }
     }
 
-    if sess.opts.debugging_opts.llvm_time_trace {
+    if sess.opts.unstable_opts.llvm_time_trace {
         llvm::LLVMTimeTraceProfilerInitialize();
     }
 
@@ -133,11 +133,11 @@ unsafe fn configure_llvm(sess: &Session) {
 
     // Use the legacy plugin registration if we don't use the new pass manager
     if !should_use_new_llvm_pass_manager(
-        &sess.opts.debugging_opts.new_llvm_pass_manager,
+        &sess.opts.unstable_opts.new_llvm_pass_manager,
         &sess.target.arch,
     ) {
         // Register LLVM plugins by loading them into the compiler process.
-        for plugin in &sess.opts.debugging_opts.llvm_plugins {
+        for plugin in &sess.opts.unstable_opts.llvm_plugins {
             let lib = Library::new(plugin).unwrap_or_else(|e| bug!("couldn't load plugin: {}", e));
             debug!("LLVM plugin loaded successfully {:?} ({})", lib, plugin);
 
@@ -233,26 +233,29 @@ pub fn check_tied_features(
 
 // Used to generate cfg variables and apply features
 // Must express features in the way Rust understands them
-pub fn target_features(sess: &Session) -> Vec<Symbol> {
+pub fn target_features(sess: &Session, allow_unstable: bool) -> Vec<Symbol> {
     let target_machine = create_informational_target_machine(sess);
-    let mut features: Vec<Symbol> =
-        supported_target_features(sess)
-            .iter()
-            .filter_map(|&(feature, gate)| {
-                if sess.is_nightly_build() || gate.is_none() { Some(feature) } else { None }
-            })
-            .filter(|feature| {
-                // check that all features in a given smallvec are enabled
-                for llvm_feature in to_llvm_features(sess, feature) {
-                    let cstr = SmallCStr::new(llvm_feature);
-                    if !unsafe { llvm::LLVMRustHasFeature(target_machine, cstr.as_ptr()) } {
-                        return false;
-                    }
+    let mut features: Vec<Symbol> = supported_target_features(sess)
+        .iter()
+        .filter_map(|&(feature, gate)| {
+            if sess.is_nightly_build() || allow_unstable || gate.is_none() {
+                Some(feature)
+            } else {
+                None
+            }
+        })
+        .filter(|feature| {
+            // check that all features in a given smallvec are enabled
+            for llvm_feature in to_llvm_features(sess, feature) {
+                let cstr = SmallCStr::new(llvm_feature);
+                if !unsafe { llvm::LLVMRustHasFeature(target_machine, cstr.as_ptr()) } {
+                    return false;
                 }
-                true
-            })
-            .map(|feature| Symbol::intern(feature))
-            .collect();
+            }
+            true
+        })
+        .map(|feature| Symbol::intern(feature))
+        .collect();
 
     // LLVM 14 changed the ABI for i128 arguments to __float/__fix builtins on Win64
     // (see https://reviews.llvm.org/D110413). This unstable target feature is intended for use
@@ -538,7 +541,7 @@ fn backend_feature_name(s: &str) -> Option<&str> {
 }
 
 pub fn tune_cpu(sess: &Session) -> Option<&str> {
-    let name = sess.opts.debugging_opts.tune_cpu.as_ref()?;
+    let name = sess.opts.unstable_opts.tune_cpu.as_ref()?;
     Some(handle_native(name))
 }
 
diff --git a/compiler/rustc_codegen_llvm/src/type_.rs b/compiler/rustc_codegen_llvm/src/type_.rs
index cf2d3c423c3..eeb38d4ecf5 100644
--- a/compiler/rustc_codegen_llvm/src/type_.rs
+++ b/compiler/rustc_codegen_llvm/src/type_.rs
@@ -19,7 +19,7 @@ use rustc_target::abi::{AddressSpace, Align, Integer, Size};
 use std::fmt;
 use std::ptr;
 
-use libc::c_uint;
+use libc::{c_char, c_uint};
 
 impl PartialEq for Type {
     fn eq(&self, other: &Self) -> bool {
@@ -289,3 +289,31 @@ impl<'ll, 'tcx> LayoutTypeMethods<'tcx> for CodegenCx<'ll, 'tcx> {
         ty.llvm_type(self)
     }
 }
+
+impl<'ll, 'tcx> TypeMembershipMethods<'tcx> for CodegenCx<'ll, 'tcx> {
+    fn set_type_metadata(&self, function: &'ll Value, typeid: String) {
+        let typeid_metadata = self.typeid_metadata(typeid);
+        let v = [self.const_usize(0), typeid_metadata];
+        unsafe {
+            llvm::LLVMGlobalSetMetadata(
+                function,
+                llvm::MD_type as c_uint,
+                llvm::LLVMValueAsMetadata(llvm::LLVMMDNodeInContext(
+                    self.llcx,
+                    v.as_ptr(),
+                    v.len() as c_uint,
+                )),
+            )
+        }
+    }
+
+    fn typeid_metadata(&self, typeid: String) -> &'ll Value {
+        unsafe {
+            llvm::LLVMMDStringInContext(
+                self.llcx,
+                typeid.as_ptr() as *const c_char,
+                typeid.len() as c_uint,
+            )
+        }
+    }
+}
diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml
index faabea92f5a..46d6344dbb2 100644
--- a/compiler/rustc_codegen_ssa/Cargo.toml
+++ b/compiler/rustc_codegen_ssa/Cargo.toml
@@ -14,7 +14,7 @@ tracing = "0.1"
 libc = "0.2.50"
 jobserver = "0.1.22"
 tempfile = "3.2"
-thorin-dwp = "0.2"
+thorin-dwp = "0.3"
 pathdiff = "0.2.0"
 serde_json = "1.0.59"
 snap = "1"
@@ -40,6 +40,7 @@ rustc_metadata = { path = "../rustc_metadata" }
 rustc_query_system = { path = "../rustc_query_system" }
 rustc_target = { path = "../rustc_target" }
 rustc_session = { path = "../rustc_session" }
+rustc_const_eval = { path = "../rustc_const_eval" }
 
 [dependencies.object]
 version = "0.29.0"
diff --git a/compiler/rustc_codegen_ssa/src/back/archive.rs b/compiler/rustc_codegen_ssa/src/back/archive.rs
index 1cb8d342381..53550b049db 100644
--- a/compiler/rustc_codegen_ssa/src/back/archive.rs
+++ b/compiler/rustc_codegen_ssa/src/back/archive.rs
@@ -51,10 +51,37 @@ pub trait ArchiveBuilder<'a> {
 
     fn build(self) -> bool;
 
+    fn sess(&self) -> &Session;
+
+    /// Creates a DLL Import Library <https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-creation#creating-an-import-library>.
+    /// and returns the path on disk to that import library.
+    /// This functions doesn't take `self` so that it can be called from
+    /// `linker_with_args`, which is specialized on `ArchiveBuilder` but
+    /// doesn't take or create an instance of that type.
+    fn create_dll_import_lib(
+        sess: &Session,
+        lib_name: &str,
+        dll_imports: &[DllImport],
+        tmpdir: &Path,
+    ) -> PathBuf;
+
+    /// Creates a DLL Import Library <https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-creation#creating-an-import-library>
+    /// and adds it to the current compilation's set of archives.
     fn inject_dll_import_lib(
         &mut self,
         lib_name: &str,
         dll_imports: &[DllImport],
         tmpdir: &MaybeTempDir,
-    );
+    ) {
+        let output_path =
+            Self::create_dll_import_lib(self.sess(), lib_name, dll_imports, tmpdir.as_ref());
+
+        self.add_archive(&output_path, |_| false).unwrap_or_else(|e| {
+            self.sess().fatal(&format!(
+                "failed to add native library {}: {}",
+                output_path.display(),
+                e
+            ));
+        });
+    }
 }
diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs
index 1628d580b88..f0d320c7c21 100644
--- a/compiler/rustc_codegen_ssa/src/back/link.rs
+++ b/compiler/rustc_codegen_ssa/src/back/link.rs
@@ -6,6 +6,7 @@ use rustc_data_structures::temp_dir::MaybeTempDir;
 use rustc_errors::{ErrorGuaranteed, Handler};
 use rustc_fs_util::fix_windows_verbatim_for_gcc;
 use rustc_hir::def_id::CrateNum;
+use rustc_metadata::fs::{emit_metadata, METADATA_FILENAME};
 use rustc_middle::middle::dependency_format::Linkage;
 use rustc_middle::middle::exported_symbols::SymbolExportKind;
 use rustc_session::config::{self, CFGuard, CrateType, DebugInfo, LdImpl, Strip};
@@ -28,10 +29,7 @@ use super::command::Command;
 use super::linker::{self, Linker};
 use super::metadata::{create_rmeta_file, MetadataPosition};
 use super::rpath::{self, RPathConfig};
-use crate::{
-    looks_like_rust_object_file, CodegenResults, CompiledModule, CrateInfo, NativeLib,
-    METADATA_FILENAME,
-};
+use crate::{looks_like_rust_object_file, CodegenResults, CompiledModule, CrateInfo, NativeLib};
 
 use cc::windows_registry;
 use regex::Regex;
@@ -67,7 +65,7 @@ pub fn link_binary<'a, B: ArchiveBuilder<'a>>(
     let output_metadata = sess.opts.output_types.contains_key(&OutputType::Metadata);
     for &crate_type in sess.crate_types().iter() {
         // Ignore executable crates if we have -Z no-codegen, as they will error.
-        if (sess.opts.debugging_opts.no_codegen || !sess.opts.output_types.should_codegen())
+        if (sess.opts.unstable_opts.no_codegen || !sess.opts.output_types.should_codegen())
             && !output_metadata
             && crate_type == CrateType::Executable
         {
@@ -122,7 +120,7 @@ pub fn link_binary<'a, B: ArchiveBuilder<'a>>(
                         &out_filename,
                         codegen_results,
                         path.as_ref(),
-                    );
+                    )?;
                 }
             }
             if sess.opts.json_artifact_notifications {
@@ -241,22 +239,6 @@ pub fn each_linked_rlib(
     Ok(())
 }
 
-/// We use a temp directory here to avoid races between concurrent rustc processes,
-/// such as builds in the same directory using the same filename for metadata while
-/// building an `.rlib` (stomping over one another), or writing an `.rmeta` into a
-/// directory being searched for `extern crate` (observing an incomplete file).
-/// The returned path is the temporary file containing the complete metadata.
-pub fn emit_metadata(sess: &Session, metadata: &[u8], tmpdir: &MaybeTempDir) -> PathBuf {
-    let out_filename = tmpdir.as_ref().join(METADATA_FILENAME);
-    let result = fs::write(&out_filename, metadata);
-
-    if let Err(e) = result {
-        sess.fatal(&format!("failed to write {}: {}", out_filename.display(), e));
-    }
-
-    out_filename
-}
-
 /// Create an 'rlib'.
 ///
 /// An rlib in its current incarnation is essentially a renamed .a file. The rlib primarily contains
@@ -595,7 +577,7 @@ fn link_dwarf_object<'a>(
         let mut package = thorin::DwarfPackage::new(&thorin_sess);
 
         // Input objs contain .o/.dwo files from the current crate.
-        match sess.opts.debugging_opts.split_dwarf_kind {
+        match sess.opts.unstable_opts.split_dwarf_kind {
             SplitDwarfKind::Single => {
                 for input_obj in cg_results.modules.iter().filter_map(|m| m.object.as_ref()) {
                     package.add_input_object(input_obj)?;
@@ -668,7 +650,7 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>(
     out_filename: &Path,
     codegen_results: &CodegenResults,
     tmpdir: &Path,
-) {
+) -> Result<(), ErrorGuaranteed> {
     info!("preparing {:?} to {:?}", crate_type, out_filename);
     let (linker_path, flavor) = linker_and_flavor(sess);
     let mut cmd = linker_with_args::<B>(
@@ -679,7 +661,7 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>(
         tmpdir,
         out_filename,
         codegen_results,
-    );
+    )?;
 
     linker::disable_localization(&mut cmd);
 
@@ -1018,11 +1000,13 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>(
             (Strip::None, _) => {}
         }
     }
+
+    Ok(())
 }
 
 // Temporarily support both -Z strip and -C strip
 fn strip_value(sess: &Session) -> Strip {
-    match (sess.opts.debugging_opts.strip, sess.opts.cg.strip) {
+    match (sess.opts.unstable_opts.strip, sess.opts.cg.strip) {
         (s, Strip::None) => s,
         (_, s) => s,
     }
@@ -1074,7 +1058,7 @@ fn add_sanitizer_libraries(sess: &Session, crate_type: CrateType, linker: &mut d
         return;
     }
 
-    let sanitizer = sess.opts.debugging_opts.sanitizer;
+    let sanitizer = sess.opts.unstable_opts.sanitizer;
     if sanitizer.contains(SanitizerSet::ADDRESS) {
         link_sanitizer_runtime(sess, linker, "asan");
     }
@@ -1253,7 +1237,7 @@ fn preserve_objects_for_their_debuginfo(sess: &Session) -> (bool, bool) {
         return (false, false);
     }
 
-    match (sess.split_debuginfo(), sess.opts.debugging_opts.split_dwarf_kind) {
+    match (sess.split_debuginfo(), sess.opts.unstable_opts.split_dwarf_kind) {
         // If there is no split debuginfo then do not preserve objects.
         (SplitDebuginfo::Off, _) => (false, false),
         // If there is packed split debuginfo, then the debuginfo in the objects
@@ -1601,7 +1585,7 @@ fn add_pre_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor)
     if let Some(args) = sess.target.pre_link_args.get(&flavor) {
         cmd.args(args.iter().map(Deref::deref));
     }
-    cmd.args(&sess.opts.debugging_opts.pre_link_args);
+    cmd.args(&sess.opts.unstable_opts.pre_link_args);
 }
 
 /// Add a link script embedded in the target, if applicable.
@@ -1809,7 +1793,7 @@ fn add_library_search_dirs(cmd: &mut dyn Linker, sess: &Session, self_contained:
 /// Add options making relocation sections in the produced ELF files read-only
 /// and suppressing lazy binding.
 fn add_relro_args(cmd: &mut dyn Linker, sess: &Session) {
-    match sess.opts.debugging_opts.relro_level.unwrap_or(sess.target.relro_level) {
+    match sess.opts.unstable_opts.relro_level.unwrap_or(sess.target.relro_level) {
         RelroLevel::Full => cmd.full_relro(),
         RelroLevel::Partial => cmd.partial_relro(),
         RelroLevel::Off => cmd.no_relro(),
@@ -1866,7 +1850,7 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>(
     tmpdir: &Path,
     out_filename: &Path,
     codegen_results: &CodegenResults,
-) -> Command {
+) -> Result<Command, ErrorGuaranteed> {
     let crt_objects_fallback = crt_objects_fallback(sess, crate_type);
     let cmd = &mut *super::linker::get_linker(
         sess,
@@ -1957,7 +1941,7 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>(
     // (or alternatively link all native libraries after their respective crates).
     // This change is somewhat breaking in practice due to local static libraries being linked
     // as whole-archive (#85144), so removing whole-archive may be a pre-requisite.
-    if sess.opts.debugging_opts.link_native_libraries {
+    if sess.opts.unstable_opts.link_native_libraries {
         add_local_native_libraries(cmd, sess, codegen_results);
     }
 
@@ -1969,10 +1953,22 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>(
     // If -Zlink-native-libraries=false is set, then the assumption is that an
     // external build system already has the native dependencies defined, and it
     // will provide them to the linker itself.
-    if sess.opts.debugging_opts.link_native_libraries {
+    if sess.opts.unstable_opts.link_native_libraries {
         add_upstream_native_libraries(cmd, sess, codegen_results);
     }
 
+    // Link with the import library generated for any raw-dylib functions.
+    for (raw_dylib_name, raw_dylib_imports) in
+        collate_raw_dylibs(sess, &codegen_results.crate_info.used_libraries)?
+    {
+        cmd.add_object(&B::create_dll_import_lib(
+            sess,
+            &raw_dylib_name,
+            &raw_dylib_imports,
+            tmpdir,
+        ));
+    }
+
     // Library linking above uses some global state for things like `-Bstatic`/`-Bdynamic` to make
     // command line shorter, reset it to default here before adding more libraries.
     cmd.reset_per_library_state();
@@ -2016,7 +2012,7 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>(
     // to it and remove the option.
     add_post_link_args(cmd, sess, flavor);
 
-    cmd.take_cmd()
+    Ok(cmd.take_cmd())
 }
 
 fn add_order_independent_options(
@@ -2037,7 +2033,7 @@ fn add_order_independent_options(
     add_link_script(cmd, sess, tmpdir, crate_type);
 
     if sess.target.os == "fuchsia" && crate_type == CrateType::Executable {
-        let prefix = if sess.opts.debugging_opts.sanitizer.contains(SanitizerSet::ADDRESS) {
+        let prefix = if sess.opts.unstable_opts.sanitizer.contains(SanitizerSet::ADDRESS) {
             "asan/"
         } else {
             ""
@@ -2100,7 +2096,12 @@ fn add_order_independent_options(
         // sections to ensure we have all the data for PGO.
         let keep_metadata =
             crate_type == CrateType::Dylib || sess.opts.cg.profile_generate.enabled();
-        cmd.gc_sections(keep_metadata);
+        if crate_type != CrateType::Executable || !sess.opts.unstable_opts.export_executable_symbols
+        {
+            cmd.gc_sections(keep_metadata);
+        } else {
+            cmd.no_gc_sections();
+        }
     }
 
     cmd.set_output_kind(link_output_kind, out_filename);
@@ -2240,8 +2241,7 @@ fn add_local_native_libraries(
                 }
             }
             NativeLibKind::RawDylib => {
-                // FIXME(#58713): Proper handling for raw dylibs.
-                bug!("raw_dylib feature not yet implemented");
+                // Ignore RawDylib here, they are handled separately in linker_with_args().
             }
         }
     }
@@ -2362,7 +2362,7 @@ fn add_upstream_rust_crates<'a, B: ArchiveBuilder<'a>>(
                 // If -Zlink-native-libraries=false is set, then the assumption is that an
                 // external build system already has the native dependencies defined, and it
                 // will provide them to the linker itself.
-                if sess.opts.debugging_opts.link_native_libraries {
+                if sess.opts.unstable_opts.link_native_libraries {
                     let mut last = (None, NativeLibKind::Unspecified, None);
                     for lib in &codegen_results.crate_info.native_libraries[&cnum] {
                         let Some(name) = lib.name else {
@@ -2709,7 +2709,7 @@ fn get_apple_sdk_root(sdk_name: &str) -> Result<String, String> {
 }
 
 fn add_gcc_ld_path(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) {
-    if let Some(ld_impl) = sess.opts.debugging_opts.gcc_ld {
+    if let Some(ld_impl) = sess.opts.unstable_opts.gcc_ld {
         if let LinkerFlavor::Gcc = flavor {
             match ld_impl {
                 LdImpl::Lld => {
diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs
index 955ee245b28..a4bbdefbb87 100644
--- a/compiler/rustc_codegen_ssa/src/back/linker.rs
+++ b/compiler/rustc_codegen_ssa/src/back/linker.rs
@@ -303,7 +303,7 @@ impl<'a> GccLinker<'a> {
             config::OptLevel::Aggressive => "O3",
         };
 
-        if let Some(path) = &self.sess.opts.debugging_opts.profile_sample_use {
+        if let Some(path) = &self.sess.opts.unstable_opts.profile_sample_use {
             self.linker_arg(&format!("-plugin-opt=sample-profile={}", path.display()));
         };
         self.linker_args(&[
@@ -325,7 +325,7 @@ impl<'a> GccLinker<'a> {
             // purely to support rustbuild right now, we should get a more
             // principled solution at some point to force the compiler to pass
             // the right `-Wl,-install_name` with an `@rpath` in it.
-            if self.sess.opts.cg.rpath || self.sess.opts.debugging_opts.osx_rpath_install_name {
+            if self.sess.opts.cg.rpath || self.sess.opts.unstable_opts.osx_rpath_install_name {
                 let mut rpath = OsString::from("@rpath/");
                 rpath.push(out_filename.file_name().unwrap());
                 self.linker_args(&[OsString::from("-install_name"), rpath]);
@@ -566,9 +566,7 @@ impl<'a> Linker for GccLinker<'a> {
     }
 
     fn no_gc_sections(&mut self) {
-        if self.sess.target.is_like_osx {
-            self.linker_arg("-no_dead_strip");
-        } else if self.sess.target.linker_is_gnu || self.sess.target.is_like_wasm {
+        if self.sess.target.linker_is_gnu || self.sess.target.is_like_wasm {
             self.linker_arg("--no-gc-sections");
         }
     }
@@ -640,9 +638,14 @@ impl<'a> Linker for GccLinker<'a> {
 
     fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType, symbols: &[String]) {
         // Symbol visibility in object files typically takes care of this.
-        if crate_type == CrateType::Executable && self.sess.target.override_export_symbols.is_none()
-        {
-            return;
+        if crate_type == CrateType::Executable {
+            let should_export_executable_symbols =
+                self.sess.opts.unstable_opts.export_executable_symbols;
+            if self.sess.target.override_export_symbols.is_none()
+                && !should_export_executable_symbols
+            {
+                return;
+            }
         }
 
         // We manually create a list of exported symbols to ensure we don't expose any more.
@@ -969,7 +972,11 @@ impl<'a> Linker for MsvcLinker<'a> {
     fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType, symbols: &[String]) {
         // Symbol visibility takes care of this typically
         if crate_type == CrateType::Executable {
-            return;
+            let should_export_executable_symbols =
+                self.sess.opts.unstable_opts.export_executable_symbols;
+            if !should_export_executable_symbols {
+                return;
+            }
         }
 
         let path = tmpdir.join("lib.def");
diff --git a/compiler/rustc_codegen_ssa/src/back/metadata.rs b/compiler/rustc_codegen_ssa/src/back/metadata.rs
index 3dd607adee5..0302c28815a 100644
--- a/compiler/rustc_codegen_ssa/src/back/metadata.rs
+++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs
@@ -16,14 +16,13 @@ use rustc_data_structures::memmap::Mmap;
 use rustc_data_structures::owning_ref::OwningRef;
 use rustc_data_structures::rustc_erase_owner;
 use rustc_data_structures::sync::MetadataRef;
+use rustc_metadata::fs::METADATA_FILENAME;
 use rustc_metadata::EncodedMetadata;
 use rustc_session::cstore::MetadataLoader;
 use rustc_session::Session;
 use rustc_target::abi::Endian;
 use rustc_target::spec::{RelocModel, Target};
 
-use crate::METADATA_FILENAME;
-
 /// The default metadata loader. This is used by cg_llvm and cg_clif.
 ///
 /// # Metadata location
diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs
index f651814be7e..d1a267f7643 100644
--- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs
+++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs
@@ -232,15 +232,15 @@ fn exported_symbols_provider_local<'tcx>(
         }));
     }
 
-    if tcx.sess.opts.debugging_opts.sanitizer.contains(SanitizerSet::MEMORY) {
+    if tcx.sess.opts.unstable_opts.sanitizer.contains(SanitizerSet::MEMORY) {
         let mut msan_weak_symbols = Vec::new();
 
         // Similar to profiling, preserve weak msan symbol during LTO.
-        if tcx.sess.opts.debugging_opts.sanitizer_recover.contains(SanitizerSet::MEMORY) {
+        if tcx.sess.opts.unstable_opts.sanitizer_recover.contains(SanitizerSet::MEMORY) {
             msan_weak_symbols.push("__msan_keep_going");
         }
 
-        if tcx.sess.opts.debugging_opts.sanitizer_memory_track_origins != 0 {
+        if tcx.sess.opts.unstable_opts.sanitizer_memory_track_origins != 0 {
             msan_weak_symbols.push("__msan_track_origins");
         }
 
diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs
index f4a5cac872e..1b5ad87107a 100644
--- a/compiler/rustc_codegen_ssa/src/back/write.rs
+++ b/compiler/rustc_codegen_ssa/src/back/write.rs
@@ -37,6 +37,7 @@ use rustc_target::spec::{MergeFunctions, SanitizerSet};
 use std::any::Any;
 use std::fs;
 use std::io;
+use std::marker::PhantomData;
 use std::mem;
 use std::path::{Path, PathBuf};
 use std::str;
@@ -99,6 +100,7 @@ pub struct ModuleConfig {
     pub emit_ir: bool,
     pub emit_asm: bool,
     pub emit_obj: EmitObj,
+    pub emit_thin_lto: bool,
     pub bc_cmdline: String,
 
     // Miscellaneous flags.  These are mostly copied from command-line
@@ -179,24 +181,24 @@ impl ModuleConfig {
                 SwitchWithOptPath::Disabled
             ),
             pgo_use: if_regular!(sess.opts.cg.profile_use.clone(), None),
-            pgo_sample_use: if_regular!(sess.opts.debugging_opts.profile_sample_use.clone(), None),
-            debug_info_for_profiling: sess.opts.debugging_opts.debug_info_for_profiling,
+            pgo_sample_use: if_regular!(sess.opts.unstable_opts.profile_sample_use.clone(), None),
+            debug_info_for_profiling: sess.opts.unstable_opts.debug_info_for_profiling,
             instrument_coverage: if_regular!(sess.instrument_coverage(), false),
             instrument_gcov: if_regular!(
                 // compiler_builtins overrides the codegen-units settings,
                 // which is incompatible with -Zprofile which requires that
                 // only a single codegen unit is used per crate.
-                sess.opts.debugging_opts.profile && !is_compiler_builtins,
+                sess.opts.unstable_opts.profile && !is_compiler_builtins,
                 false
             ),
 
-            sanitizer: if_regular!(sess.opts.debugging_opts.sanitizer, SanitizerSet::empty()),
+            sanitizer: if_regular!(sess.opts.unstable_opts.sanitizer, SanitizerSet::empty()),
             sanitizer_recover: if_regular!(
-                sess.opts.debugging_opts.sanitizer_recover,
+                sess.opts.unstable_opts.sanitizer_recover,
                 SanitizerSet::empty()
             ),
             sanitizer_memory_track_origins: if_regular!(
-                sess.opts.debugging_opts.sanitizer_memory_track_origins,
+                sess.opts.unstable_opts.sanitizer_memory_track_origins,
                 0
             ),
 
@@ -218,6 +220,7 @@ impl ModuleConfig {
                 false
             ),
             emit_obj,
+            emit_thin_lto: sess.opts.unstable_opts.emit_thin_lto,
             bc_cmdline: sess.target.bitcode_llvm_cmdline.to_string(),
 
             verify_llvm_ir: sess.verify_llvm_ir(),
@@ -247,7 +250,7 @@ impl ModuleConfig {
             // O2 and O3) since it can be useful for reducing code size.
             merge_functions: match sess
                 .opts
-                .debugging_opts
+                .unstable_opts
                 .merge_functions
                 .unwrap_or(sess.target.merge_functions)
             {
@@ -259,9 +262,9 @@ impl ModuleConfig {
             },
 
             inline_threshold: sess.opts.cg.inline_threshold,
-            new_llvm_pass_manager: sess.opts.debugging_opts.new_llvm_pass_manager,
+            new_llvm_pass_manager: sess.opts.unstable_opts.new_llvm_pass_manager,
             emit_lifetime_markers: sess.emit_lifetime_markers(),
-            llvm_plugins: if_regular!(sess.opts.debugging_opts.llvm_plugins.clone(), vec![]),
+            llvm_plugins: if_regular!(sess.opts.unstable_opts.llvm_plugins.clone(), vec![]),
         }
     }
 
@@ -473,10 +476,13 @@ pub fn start_async_codegen<B: ExtraBackendMethods>(
         metadata_module,
         crate_info,
 
-        coordinator_send,
         codegen_worker_receive,
         shared_emitter_main,
-        future: coordinator_thread,
+        coordinator: Coordinator {
+            sender: coordinator_send,
+            future: Some(coordinator_thread),
+            phantom: PhantomData,
+        },
         output_filenames: tcx.output_filenames(()).clone(),
     }
 }
@@ -926,7 +932,7 @@ fn finish_intra_module_work<B: ExtraBackendMethods>(
 ) -> Result<WorkItemResult<B>, FatalError> {
     let diag_handler = cgcx.create_diag_handler();
 
-    if !cgcx.opts.debugging_opts.combine_cgu
+    if !cgcx.opts.unstable_opts.combine_cgu
         || module.kind == ModuleKind::Metadata
         || module.kind == ModuleKind::Allocator
     {
@@ -1048,14 +1054,13 @@ fn start_executing_work<B: ExtraBackendMethods>(
         each_linked_rlib_for_lto.push((cnum, path.to_path_buf()));
     }));
 
-    let ol = if tcx.sess.opts.debugging_opts.no_codegen
-        || !tcx.sess.opts.output_types.should_codegen()
-    {
-        // If we know that we won’t be doing codegen, create target machines without optimisation.
-        config::OptLevel::No
-    } else {
-        tcx.backend_optimization_level(())
-    };
+    let ol =
+        if tcx.sess.opts.unstable_opts.no_codegen || !tcx.sess.opts.output_types.should_codegen() {
+            // If we know that we won’t be doing codegen, create target machines without optimisation.
+            config::OptLevel::No
+        } else {
+            tcx.backend_optimization_level(())
+        };
     let backend_features = tcx.global_backend_features(());
     let cgcx = CodegenContext::<B> {
         backend: backend.clone(),
@@ -1064,7 +1069,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
         lto: sess.lto(),
         fewer_names: sess.fewer_names(),
         save_temps: sess.opts.cg.save_temps,
-        time_trace: sess.opts.debugging_opts.llvm_time_trace,
+        time_trace: sess.opts.unstable_opts.llvm_time_trace,
         opts: Arc::new(sess.opts.clone()),
         prof: sess.prof.clone(),
         exported_symbols,
@@ -1087,7 +1092,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
         target_arch: tcx.sess.target.arch.to_string(),
         debuginfo: tcx.sess.opts.debuginfo,
         split_debuginfo: tcx.sess.split_debuginfo(),
-        split_dwarf_kind: tcx.sess.opts.debugging_opts.split_dwarf_kind,
+        split_dwarf_kind: tcx.sess.opts.unstable_opts.split_dwarf_kind,
     };
 
     // This is the "main loop" of parallel work happening for parallel codegen.
@@ -1272,6 +1277,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
         // work to be done.
         while !codegen_done
             || running > 0
+            || main_thread_worker_state == MainThreadWorkerState::LLVMing
             || (!codegen_aborted
                 && !(work_items.is_empty()
                     && needs_fat_lto.is_empty()
@@ -1346,7 +1352,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
                             .binary_search_by_key(&cost, |&(_, cost)| cost)
                             .unwrap_or_else(|e| e);
                         work_items.insert(insertion_index, (work, cost));
-                        if !cgcx.opts.debugging_opts.no_parallel_llvm {
+                        if !cgcx.opts.unstable_opts.no_parallel_llvm {
                             helper.request_token();
                         }
                     }
@@ -1466,17 +1472,15 @@ fn start_executing_work<B: ExtraBackendMethods>(
                     };
                     work_items.insert(insertion_index, (llvm_work_item, cost));
 
-                    if !cgcx.opts.debugging_opts.no_parallel_llvm {
+                    if !cgcx.opts.unstable_opts.no_parallel_llvm {
                         helper.request_token();
                     }
-                    assert!(!codegen_aborted);
                     assert_eq!(main_thread_worker_state, MainThreadWorkerState::Codegenning);
                     main_thread_worker_state = MainThreadWorkerState::Idle;
                 }
 
                 Message::CodegenComplete => {
                     codegen_done = true;
-                    assert!(!codegen_aborted);
                     assert_eq!(main_thread_worker_state, MainThreadWorkerState::Codegenning);
                     main_thread_worker_state = MainThreadWorkerState::Idle;
                 }
@@ -1488,10 +1492,8 @@ fn start_executing_work<B: ExtraBackendMethods>(
                 // then conditions above will ensure no more work is spawned but
                 // we'll keep executing this loop until `running` hits 0.
                 Message::CodegenAborted => {
-                    assert!(!codegen_aborted);
                     codegen_done = true;
                     codegen_aborted = true;
-                    assert_eq!(main_thread_worker_state, MainThreadWorkerState::Codegenning);
                 }
                 Message::Done { result: Ok(compiled_module), worker_id } => {
                     free_worker(worker_id);
@@ -1531,13 +1533,20 @@ fn start_executing_work<B: ExtraBackendMethods>(
                 Message::Done { result: Err(None), worker_id: _ } => {
                     bug!("worker thread panicked");
                 }
-                Message::Done { result: Err(Some(WorkerFatalError)), worker_id: _ } => {
-                    return Err(());
+                Message::Done { result: Err(Some(WorkerFatalError)), worker_id } => {
+                    // Similar to CodegenAborted, wait for remaining work to finish.
+                    free_worker(worker_id);
+                    codegen_done = true;
+                    codegen_aborted = true;
                 }
                 Message::CodegenItem => bug!("the coordinator should not receive codegen requests"),
             }
         }
 
+        if codegen_aborted {
+            return Err(());
+        }
+
         let needs_link = mem::take(&mut needs_link);
         if !needs_link.is_empty() {
             assert!(compiled_modules.is_empty());
@@ -1827,16 +1836,39 @@ impl SharedEmitterMain {
     }
 }
 
+pub struct Coordinator<B: ExtraBackendMethods> {
+    pub sender: Sender<Box<dyn Any + Send>>,
+    future: Option<thread::JoinHandle<Result<CompiledModules, ()>>>,
+    // Only used for the Message type.
+    phantom: PhantomData<B>,
+}
+
+impl<B: ExtraBackendMethods> Coordinator<B> {
+    fn join(mut self) -> std::thread::Result<Result<CompiledModules, ()>> {
+        self.future.take().unwrap().join()
+    }
+}
+
+impl<B: ExtraBackendMethods> Drop for Coordinator<B> {
+    fn drop(&mut self) {
+        if let Some(future) = self.future.take() {
+            // If we haven't joined yet, signal to the coordinator that it should spawn no more
+            // work, and wait for worker threads to finish.
+            drop(self.sender.send(Box::new(Message::CodegenAborted::<B>)));
+            drop(future.join());
+        }
+    }
+}
+
 pub struct OngoingCodegen<B: ExtraBackendMethods> {
     pub backend: B,
     pub metadata: EncodedMetadata,
     pub metadata_module: Option<CompiledModule>,
     pub crate_info: CrateInfo,
-    pub coordinator_send: Sender<Box<dyn Any + Send>>,
     pub codegen_worker_receive: Receiver<Message<B>>,
     pub shared_emitter_main: SharedEmitterMain,
-    pub future: thread::JoinHandle<Result<CompiledModules, ()>>,
     pub output_filenames: Arc<OutputFilenames>,
+    pub coordinator: Coordinator<B>,
 }
 
 impl<B: ExtraBackendMethods> OngoingCodegen<B> {
@@ -1844,8 +1876,7 @@ impl<B: ExtraBackendMethods> OngoingCodegen<B> {
         let _timer = sess.timer("finish_ongoing_codegen");
 
         self.shared_emitter_main.check(sess, true);
-        let future = self.future;
-        let compiled_modules = sess.time("join_worker_thread", || match future.join() {
+        let compiled_modules = sess.time("join_worker_thread", || match self.coordinator.join() {
             Ok(Ok(compiled_modules)) => compiled_modules,
             Ok(Err(())) => {
                 sess.abort_if_errors();
@@ -1893,26 +1924,13 @@ impl<B: ExtraBackendMethods> OngoingCodegen<B> {
 
         // These are generally cheap and won't throw off scheduling.
         let cost = 0;
-        submit_codegened_module_to_llvm(&self.backend, &self.coordinator_send, module, cost);
+        submit_codegened_module_to_llvm(&self.backend, &self.coordinator.sender, module, cost);
     }
 
     pub fn codegen_finished(&self, tcx: TyCtxt<'_>) {
         self.wait_for_signal_to_codegen_item();
         self.check_for_errors(tcx.sess);
-        drop(self.coordinator_send.send(Box::new(Message::CodegenComplete::<B>)));
-    }
-
-    /// Consumes this context indicating that codegen was entirely aborted, and
-    /// we need to exit as quickly as possible.
-    ///
-    /// This method blocks the current thread until all worker threads have
-    /// finished, and all worker threads should have exited or be real close to
-    /// exiting at this point.
-    pub fn codegen_aborted(self) {
-        // Signal to the coordinator it should spawn no more work and start
-        // shutdown.
-        drop(self.coordinator_send.send(Box::new(Message::CodegenAborted::<B>)));
-        drop(self.future.join());
+        drop(self.coordinator.sender.send(Box::new(Message::CodegenComplete::<B>)));
     }
 
     pub fn check_for_errors(&self, sess: &Session) {
diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs
index 7e2e85ead54..a840b270974 100644
--- a/compiler/rustc_codegen_ssa/src/base.rs
+++ b/compiler/rustc_codegen_ssa/src/base.rs
@@ -39,7 +39,6 @@ use rustc_target::abi::{Align, VariantIdx};
 
 use std::collections::BTreeSet;
 use std::convert::TryFrom;
-use std::ops::{Deref, DerefMut};
 use std::time::{Duration, Instant};
 
 use itertools::Itertools;
@@ -171,7 +170,7 @@ pub fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
                 );
                 let new_vptr = bx.load(ptr_ty, gep, ptr_align);
                 bx.nonnull_metadata(new_vptr);
-                // Vtable loads are invariant.
+                // VTable loads are invariant.
                 bx.set_invariant_load(new_vptr);
                 new_vptr
             } else {
@@ -521,7 +520,7 @@ pub fn codegen_crate<B: ExtraBackendMethods>(
     need_metadata_module: bool,
 ) -> OngoingCodegen<B> {
     // Skip crate items and just output metadata in -Z no-codegen mode.
-    if tcx.sess.opts.debugging_opts.no_codegen || !tcx.sess.opts.output_types.should_codegen() {
+    if tcx.sess.opts.unstable_opts.no_codegen || !tcx.sess.opts.output_types.should_codegen() {
         let ongoing_codegen = start_async_codegen(backend, tcx, target_cpu, metadata, None, 1);
 
         ongoing_codegen.codegen_finished(tcx);
@@ -583,7 +582,6 @@ pub fn codegen_crate<B: ExtraBackendMethods>(
         metadata_module,
         codegen_units.len(),
     );
-    let ongoing_codegen = AbortCodegenOnDrop::<B>(Some(ongoing_codegen));
 
     // Codegen an allocator shim, if necessary.
     //
@@ -704,7 +702,7 @@ pub fn codegen_crate<B: ExtraBackendMethods>(
 
                 submit_codegened_module_to_llvm(
                     &backend,
-                    &ongoing_codegen.coordinator_send,
+                    &ongoing_codegen.coordinator.sender,
                     module,
                     cost,
                 );
@@ -714,7 +712,7 @@ pub fn codegen_crate<B: ExtraBackendMethods>(
                 submit_pre_lto_module_to_llvm(
                     &backend,
                     tcx,
-                    &ongoing_codegen.coordinator_send,
+                    &ongoing_codegen.coordinator.sender,
                     CachedModuleCodegen {
                         name: cgu.name().to_string(),
                         source: cgu.previous_work_product(tcx),
@@ -725,7 +723,7 @@ pub fn codegen_crate<B: ExtraBackendMethods>(
             CguReuse::PostLto => {
                 submit_post_lto_module_to_llvm(
                     &backend,
-                    &ongoing_codegen.coordinator_send,
+                    &ongoing_codegen.coordinator.sender,
                     CachedModuleCodegen {
                         name: cgu.name().to_string(),
                         source: cgu.previous_work_product(tcx),
@@ -752,55 +750,7 @@ pub fn codegen_crate<B: ExtraBackendMethods>(
     }
 
     ongoing_codegen.check_for_errors(tcx.sess);
-
-    ongoing_codegen.into_inner()
-}
-
-/// A curious wrapper structure whose only purpose is to call `codegen_aborted`
-/// when it's dropped abnormally.
-///
-/// In the process of working on rust-lang/rust#55238 a mysterious segfault was
-/// stumbled upon. The segfault was never reproduced locally, but it was
-/// suspected to be related to the fact that codegen worker threads were
-/// sticking around by the time the main thread was exiting, causing issues.
-///
-/// This structure is an attempt to fix that issue where the `codegen_aborted`
-/// message will block until all workers have finished. This should ensure that
-/// even if the main codegen thread panics we'll wait for pending work to
-/// complete before returning from the main thread, hopefully avoiding
-/// segfaults.
-///
-/// If you see this comment in the code, then it means that this workaround
-/// worked! We may yet one day track down the mysterious cause of that
-/// segfault...
-struct AbortCodegenOnDrop<B: ExtraBackendMethods>(Option<OngoingCodegen<B>>);
-
-impl<B: ExtraBackendMethods> AbortCodegenOnDrop<B> {
-    fn into_inner(mut self) -> OngoingCodegen<B> {
-        self.0.take().unwrap()
-    }
-}
-
-impl<B: ExtraBackendMethods> Deref for AbortCodegenOnDrop<B> {
-    type Target = OngoingCodegen<B>;
-
-    fn deref(&self) -> &OngoingCodegen<B> {
-        self.0.as_ref().unwrap()
-    }
-}
-
-impl<B: ExtraBackendMethods> DerefMut for AbortCodegenOnDrop<B> {
-    fn deref_mut(&mut self) -> &mut OngoingCodegen<B> {
-        self.0.as_mut().unwrap()
-    }
-}
-
-impl<B: ExtraBackendMethods> Drop for AbortCodegenOnDrop<B> {
-    fn drop(&mut self) {
-        if let Some(codegen) = self.0.take() {
-            codegen.codegen_aborted();
-        }
-    }
+    ongoing_codegen
 }
 
 impl CrateInfo {
diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs
index 4be3ae11e4e..1802eedf193 100644
--- a/compiler/rustc_codegen_ssa/src/lib.rs
+++ b/compiler/rustc_codegen_ssa/src/lib.rs
@@ -64,9 +64,6 @@ pub struct ModuleCodegen<M> {
     pub kind: ModuleKind,
 }
 
-// FIXME(eddyb) maybe include the crate name in this?
-pub const METADATA_FILENAME: &str = "lib.rmeta";
-
 impl<M> ModuleCodegen<M> {
     pub fn into_compiled_module(
         self,
diff --git a/compiler/rustc_codegen_ssa/src/meth.rs b/compiler/rustc_codegen_ssa/src/meth.rs
index 5203ebfad75..27d791d90a5 100644
--- a/compiler/rustc_codegen_ssa/src/meth.rs
+++ b/compiler/rustc_codegen_ssa/src/meth.rs
@@ -25,7 +25,7 @@ impl<'a, 'tcx> VirtualIndex {
         let llty = bx.fn_ptr_backend_type(fn_abi);
         let llvtable = bx.pointercast(llvtable, bx.type_ptr_to(llty));
 
-        if bx.cx().sess().opts.debugging_opts.virtual_function_elimination
+        if bx.cx().sess().opts.unstable_opts.virtual_function_elimination
             && bx.cx().sess().lto() == Lto::Fat
         {
             let typeid =
@@ -39,7 +39,7 @@ impl<'a, 'tcx> VirtualIndex {
             let gep = bx.inbounds_gep(llty, llvtable, &[bx.const_usize(self.0)]);
             let ptr = bx.load(llty, gep, ptr_align);
             bx.nonnull_metadata(ptr);
-            // Vtable loads are invariant.
+            // VTable loads are invariant.
             bx.set_invariant_load(ptr);
             ptr
         }
@@ -58,7 +58,7 @@ impl<'a, 'tcx> VirtualIndex {
         let usize_align = bx.tcx().data_layout.pointer_align.abi;
         let gep = bx.inbounds_gep(llty, llvtable, &[bx.const_usize(self.0)]);
         let ptr = bx.load(llty, gep, usize_align);
-        // Vtable loads are invariant.
+        // VTable loads are invariant.
         bx.set_invariant_load(ptr);
         ptr
     }
diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs
index b8e3cb32ef6..3eee58d9d1c 100644
--- a/compiler/rustc_codegen_ssa/src/mir/block.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/block.rs
@@ -20,9 +20,9 @@ use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths};
 use rustc_middle::ty::{self, Instance, Ty, TypeVisitable};
 use rustc_span::source_map::Span;
 use rustc_span::{sym, Symbol};
-use rustc_symbol_mangling::typeid_for_fnabi;
+use rustc_symbol_mangling::typeid::typeid_for_fnabi;
 use rustc_target::abi::call::{ArgAbi, FnAbi, PassMode};
-use rustc_target::abi::{self, HasDataLayout, InitKind, WrappingRange};
+use rustc_target::abi::{self, HasDataLayout, WrappingRange};
 use rustc_target::spec::abi::Abi;
 
 /// Used by `FunctionCx::codegen_terminator` for emitting common patterns
@@ -528,7 +528,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
         source_info: mir::SourceInfo,
         target: Option<mir::BasicBlock>,
         cleanup: Option<mir::BasicBlock>,
-        strict_validity: bool,
     ) -> bool {
         // Emit a panic or a no-op for `assert_*` intrinsics.
         // These are intrinsics that compile to panics so that we can get a message
@@ -547,12 +546,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
         });
         if let Some(intrinsic) = panic_intrinsic {
             use AssertIntrinsic::*;
+
             let ty = instance.unwrap().substs.type_at(0);
             let layout = bx.layout_of(ty);
             let do_panic = match intrinsic {
                 Inhabited => layout.abi.is_uninhabited(),
-                ZeroValid => !layout.might_permit_raw_init(bx, InitKind::Zero, strict_validity),
-                UninitValid => !layout.might_permit_raw_init(bx, InitKind::Uninit, strict_validity),
+                ZeroValid => !bx.tcx().permits_zero_init(layout),
+                UninitValid => !bx.tcx().permits_uninit_init(layout),
             };
             if do_panic {
                 let msg_str = with_no_visible_paths!({
@@ -687,7 +687,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
             source_info,
             target,
             cleanup,
-            self.cx.tcx().sess.opts.debugging_opts.strict_init_checks,
         ) {
             return;
         }
@@ -919,7 +918,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
             // FIXME(rcvalle): Add support for generalized identifiers.
             // FIXME(rcvalle): Create distinct unnamed MDNodes for internal identifiers.
             let typeid = typeid_for_fnabi(bx.tcx(), fn_abi);
-            let typeid_metadata = bx.typeid_metadata(typeid);
+            let typeid_metadata = self.cx.typeid_metadata(typeid);
 
             // Test whether the function pointer is associated with the type identifier.
             let cond = bx.type_test(fn_ptr, typeid_metadata);
diff --git a/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs b/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs
index a283bf1de76..f1fe495282a 100644
--- a/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs
@@ -9,11 +9,8 @@ use super::FunctionCx;
 impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
     pub fn codegen_coverage(&self, bx: &mut Bx, coverage: Coverage, scope: SourceScope) {
         // Determine the instance that coverage data was originally generated for.
-        let scope_data = &self.mir.source_scopes[scope];
-        let instance = if let Some((inlined_instance, _)) = scope_data.inlined {
-            self.monomorphize(inlined_instance)
-        } else if let Some(inlined_scope) = scope_data.inlined_parent_scope {
-            self.monomorphize(self.mir.source_scopes[inlined_scope].inlined.unwrap().0)
+        let instance = if let Some(inlined) = scope.inlined_instance(&self.mir.source_scopes) {
+            self.monomorphize(inlined)
         } else {
             self.instance
         };
diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
index f2d1827c792..8c3186efc63 100644
--- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
@@ -101,7 +101,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
             return span;
         }
 
-        if span.from_expansion() && !self.cx.sess().opts.debugging_opts.debug_macros {
+        if span.from_expansion() && !self.cx.sess().opts.unstable_opts.debug_macros {
             // Walk up the macro expansion chain until we reach a non-expanded span.
             // We also stop at the function body level because no line stepping can occur
             // at the level above that.
diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs
index 645afae30d8..94ac71a4dd2 100644
--- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs
@@ -3,12 +3,16 @@ use super::place::PlaceRef;
 use super::FunctionCx;
 use crate::common::{span_invalid_monomorphization_error, IntPredicate};
 use crate::glue;
+use crate::meth;
 use crate::traits::*;
 use crate::MemFlags;
 
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_span::{sym, Span};
-use rustc_target::abi::call::{FnAbi, PassMode};
+use rustc_target::abi::{
+    call::{FnAbi, PassMode},
+    WrappingRange,
+};
 
 fn copy_intrinsic<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
     bx: &mut Bx,
@@ -102,6 +106,20 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                     bx.const_usize(bx.layout_of(tp_ty).align.abi.bytes())
                 }
             }
+            sym::vtable_size | sym::vtable_align => {
+                let vtable = args[0].immediate();
+                let idx = match name {
+                    sym::vtable_size => ty::COMMON_VTABLE_ENTRIES_SIZE,
+                    sym::vtable_align => ty::COMMON_VTABLE_ENTRIES_ALIGN,
+                    _ => bug!(),
+                };
+                let value = meth::VirtualIndex::from_index(idx).get_usize(bx, vtable);
+                if name == sym::vtable_align {
+                    // Alignment is always nonzero.
+                    bx.range_metadata(value, WrappingRange { start: 1, end: !0 });
+                };
+                value
+            }
             sym::pref_align_of
             | sym::needs_drop
             | sym::type_id
diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs
index ec3f7a2156a..8ee375fa9e3 100644
--- a/compiler/rustc_codegen_ssa/src/mir/mod.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs
@@ -3,7 +3,6 @@ use rustc_middle::mir;
 use rustc_middle::mir::interpret::ErrorHandled;
 use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, TyAndLayout};
 use rustc_middle::ty::{self, Instance, Ty, TypeFoldable, TypeVisitable};
-use rustc_symbol_mangling::typeid_for_fnabi;
 use rustc_target::abi::call::{FnAbi, PassMode};
 
 use std::iter;
@@ -247,13 +246,6 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
     for (bb, _) in traversal::reverse_postorder(&mir) {
         fx.codegen_block(bb);
     }
-
-    // For backends that support CFI using type membership (i.e., testing whether a given  pointer
-    // is associated with a type identifier).
-    if cx.tcx().sess.is_sanitizer_cfi_enabled() {
-        let typeid = typeid_for_fnabi(cx.tcx(), fn_abi);
-        bx.type_metadata(llfn, typeid);
-    }
 }
 
 /// Produces, for each argument, a `Value` pointing at the
diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs
index 58cee0c8bb0..268c4d76503 100644
--- a/compiler/rustc_codegen_ssa/src/mir/place.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/place.rs
@@ -435,18 +435,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
             LocalRef::Place(place) => place,
             LocalRef::UnsizedPlace(place) => bx.load_operand(place).deref(cx),
             LocalRef::Operand(..) => {
-                if let Some(elem) = place_ref
-                    .projection
-                    .iter()
-                    .enumerate()
-                    .find(|elem| matches!(elem.1, mir::ProjectionElem::Deref))
-                {
-                    base = elem.0 + 1;
+                if place_ref.has_deref() {
+                    base = 1;
                     let cg_base = self.codegen_consume(
                         bx,
-                        mir::PlaceRef { projection: &place_ref.projection[..elem.0], ..place_ref },
+                        mir::PlaceRef { projection: &place_ref.projection[..0], ..place_ref },
                     );
-
                     cg_base.deref(bx.cx())
                 } else {
                     bug!("using operand local {:?} as place", place_ref);
diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs
index bfdef2dc0e8..ecad0518533 100644
--- a/compiler/rustc_codegen_ssa/src/target_features.rs
+++ b/compiler/rustc_codegen_ssa/src/target_features.rs
@@ -249,6 +249,7 @@ const WASM_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[
     ("bulk-memory", Some(sym::wasm_target_feature)),
     ("mutable-globals", Some(sym::wasm_target_feature)),
     ("reference-types", Some(sym::wasm_target_feature)),
+    ("sign-ext", Some(sym::wasm_target_feature)),
 ];
 
 const BPF_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[("alu32", Some(sym::bpf_target_feature))];
diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs
index 1e53c73d1bb..779bd3ea278 100644
--- a/compiler/rustc_codegen_ssa/src/traits/backend.rs
+++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs
@@ -59,7 +59,7 @@ impl<'tcx, T> Backend<'tcx> for T where
 pub trait CodegenBackend {
     fn init(&self, _sess: &Session) {}
     fn print(&self, _req: PrintRequest, _sess: &Session) {}
-    fn target_features(&self, _sess: &Session) -> Vec<Symbol> {
+    fn target_features(&self, _sess: &Session, _allow_unstable: bool) -> Vec<Symbol> {
         vec![]
     }
     fn print_passes(&self) {}
diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs
index 37f2bfd3c4f..9f49749bb41 100644
--- a/compiler/rustc_codegen_ssa/src/traits/builder.rs
+++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs
@@ -160,8 +160,6 @@ pub trait BuilderMethods<'a, 'tcx>:
 
     fn range_metadata(&mut self, load: Self::Value, range: WrappingRange);
     fn nonnull_metadata(&mut self, load: Self::Value);
-    fn type_metadata(&mut self, function: Self::Function, typeid: String);
-    fn typeid_metadata(&mut self, typeid: String) -> Self::Value;
 
     fn store(&mut self, val: Self::Value, ptr: Self::Value, align: Align) -> Self::Value;
     fn store_with_flags(
@@ -221,7 +219,7 @@ pub trait BuilderMethods<'a, 'tcx>:
         assert!(matches!(self.cx().type_kind(float_ty), TypeKind::Float | TypeKind::Double));
         assert_eq!(self.cx().type_kind(int_ty), TypeKind::Integer);
 
-        if let Some(false) = self.cx().sess().opts.debugging_opts.saturating_float_casts {
+        if let Some(false) = self.cx().sess().opts.unstable_opts.saturating_float_casts {
             return if signed { self.fptosi(x, dest_ty) } else { self.fptoui(x, dest_ty) };
         }
 
diff --git a/compiler/rustc_codegen_ssa/src/traits/mod.rs b/compiler/rustc_codegen_ssa/src/traits/mod.rs
index 396768e0a42..782fdadbfb8 100644
--- a/compiler/rustc_codegen_ssa/src/traits/mod.rs
+++ b/compiler/rustc_codegen_ssa/src/traits/mod.rs
@@ -40,7 +40,8 @@ pub use self::intrinsic::IntrinsicCallMethods;
 pub use self::misc::MiscMethods;
 pub use self::statics::{StaticBuilderMethods, StaticMethods};
 pub use self::type_::{
-    ArgAbiMethods, BaseTypeMethods, DerivedTypeMethods, LayoutTypeMethods, TypeMethods,
+    ArgAbiMethods, BaseTypeMethods, DerivedTypeMethods, LayoutTypeMethods, TypeMembershipMethods,
+    TypeMethods,
 };
 pub use self::write::{ModuleBufferMethods, ThinBufferMethods, WriteBackendMethods};
 
diff --git a/compiler/rustc_codegen_ssa/src/traits/statics.rs b/compiler/rustc_codegen_ssa/src/traits/statics.rs
index a2a3cb56c78..413d31bb942 100644
--- a/compiler/rustc_codegen_ssa/src/traits/statics.rs
+++ b/compiler/rustc_codegen_ssa/src/traits/statics.rs
@@ -13,7 +13,9 @@ pub trait StaticMethods: BackendTypes {
     /// Same as add_used_global(), but only prevent the compiler from potentially removing an
     /// otherwise unused symbol. The linker is still permitted to drop it.
     ///
-    /// This corresponds to the semantics of the `#[used]` attribute.
+    /// This corresponds to the documented semantics of the `#[used]` attribute, although
+    /// on some targets (non-ELF), we may use `add_used_global` for `#[used]` statics
+    /// instead.
     fn add_compiler_used_global(&self, global: Self::Value);
 }
 
diff --git a/compiler/rustc_codegen_ssa/src/traits/type_.rs b/compiler/rustc_codegen_ssa/src/traits/type_.rs
index 5d3f07317a3..8158e8dd011 100644
--- a/compiler/rustc_codegen_ssa/src/traits/type_.rs
+++ b/compiler/rustc_codegen_ssa/src/traits/type_.rs
@@ -117,6 +117,13 @@ pub trait LayoutTypeMethods<'tcx>: Backend<'tcx> {
     ) -> Self::Type;
 }
 
+// For backends that support CFI using type membership (i.e., testing whether a given  pointer is
+// associated with a type identifier).
+pub trait TypeMembershipMethods<'tcx>: Backend<'tcx> {
+    fn set_type_metadata(&self, function: Self::Function, typeid: String);
+    fn typeid_metadata(&self, typeid: String) -> Self::Value;
+}
+
 pub trait ArgAbiMethods<'tcx>: HasCodegen<'tcx> {
     fn store_fn_arg(
         &mut self,
@@ -133,6 +140,12 @@ pub trait ArgAbiMethods<'tcx>: HasCodegen<'tcx> {
     fn arg_memory_ty(&self, arg_abi: &ArgAbi<'tcx, Ty<'tcx>>) -> Self::Type;
 }
 
-pub trait TypeMethods<'tcx>: DerivedTypeMethods<'tcx> + LayoutTypeMethods<'tcx> {}
+pub trait TypeMethods<'tcx>:
+    DerivedTypeMethods<'tcx> + LayoutTypeMethods<'tcx> + TypeMembershipMethods<'tcx>
+{
+}
 
-impl<'tcx, T> TypeMethods<'tcx> for T where Self: DerivedTypeMethods<'tcx> + LayoutTypeMethods<'tcx> {}
+impl<'tcx, T> TypeMethods<'tcx> for T where
+    Self: DerivedTypeMethods<'tcx> + LayoutTypeMethods<'tcx> + TypeMembershipMethods<'tcx>
+{
+}
diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
index b18976302b4..ba8222dc152 100644
--- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
+++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
@@ -165,6 +165,7 @@ pub(super) fn op_to_const<'tcx>(
         Ok(ref mplace) => to_const_value(mplace),
         // see comment on `let try_as_immediate` above
         Err(imm) => match *imm {
+            _ if imm.layout.is_zst() => ConstValue::ZeroSized,
             Immediate::Scalar(x) => match x {
                 ScalarMaybeUninit::Scalar(s) => ConstValue::Scalar(s),
                 ScalarMaybeUninit::Uninit => to_const_value(&op.assert_mem_place()),
@@ -172,18 +173,17 @@ pub(super) fn op_to_const<'tcx>(
             Immediate::ScalarPair(a, b) => {
                 debug!("ScalarPair(a: {:?}, b: {:?})", a, b);
                 // We know `offset` is relative to the allocation, so we can use `into_parts`.
-                let (data, start) =
-                    match ecx.scalar_to_ptr(a.check_init().unwrap()).unwrap().into_parts() {
-                        (Some(alloc_id), offset) => {
-                            (ecx.tcx.global_alloc(alloc_id).unwrap_memory(), offset.bytes())
-                        }
-                        (None, _offset) => (
-                            ecx.tcx.intern_const_alloc(
-                                Allocation::from_bytes_byte_aligned_immutable(b"" as &[u8]),
-                            ),
-                            0,
-                        ),
-                    };
+                let (data, start) = match a.to_pointer(ecx).unwrap().into_parts() {
+                    (Some(alloc_id), offset) => {
+                        (ecx.tcx.global_alloc(alloc_id).unwrap_memory(), offset.bytes())
+                    }
+                    (None, _offset) => (
+                        ecx.tcx.intern_const_alloc(Allocation::from_bytes_byte_aligned_immutable(
+                            b"" as &[u8],
+                        )),
+                        0,
+                    ),
+                };
                 let len = b.to_machine_usize(ecx).unwrap();
                 let start = start.try_into().unwrap();
                 let len: usize = len.try_into().unwrap();
diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs
index 29ab1d18771..fc2e6652a3d 100644
--- a/compiler/rustc_const_eval/src/const_eval/machine.rs
+++ b/compiler/rustc_const_eval/src/const_eval/machine.rs
@@ -104,7 +104,7 @@ pub struct CompileTimeInterpreter<'mir, 'tcx> {
 }
 
 impl<'mir, 'tcx> CompileTimeInterpreter<'mir, 'tcx> {
-    pub(super) fn new(const_eval_limit: Limit, can_access_statics: bool) -> Self {
+    pub(crate) fn new(const_eval_limit: Limit, can_access_statics: bool) -> Self {
         CompileTimeInterpreter {
             steps_remaining: const_eval_limit.0,
             stack: Vec::new(),
@@ -309,7 +309,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
         ecx: &mut InterpCx<'mir, 'tcx, Self>,
         instance: ty::Instance<'tcx>,
         args: &[OpTy<'tcx>],
-        dest: &PlaceTy<'tcx, Self::PointerTag>,
+        dest: &PlaceTy<'tcx, Self::Provenance>,
         target: Option<mir::BasicBlock>,
         _unwind: StackPopUnwind,
     ) -> InterpResult<'tcx> {
@@ -369,7 +369,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
                 // we don't deallocate it.
                 let (alloc_id, _, _) = ecx.ptr_get_alloc_id(ptr)?;
                 let is_allocated_in_another_const = matches!(
-                    ecx.tcx.get_global_alloc(alloc_id),
+                    ecx.tcx.try_get_global_alloc(alloc_id),
                     Some(interpret::GlobalAlloc::Memory(_))
                 );
 
@@ -470,14 +470,14 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
     #[inline(always)]
     fn stack<'a>(
         ecx: &'a InterpCx<'mir, 'tcx, Self>,
-    ) -> &'a [Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>] {
+    ) -> &'a [Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>] {
         &ecx.machine.stack
     }
 
     #[inline(always)]
     fn stack_mut<'a>(
         ecx: &'a mut InterpCx<'mir, 'tcx, Self>,
-    ) -> &'a mut Vec<Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>> {
+    ) -> &'a mut Vec<Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>> {
         &mut ecx.machine.stack
     }
 
diff --git a/compiler/rustc_const_eval/src/const_eval/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs
index bf65fdc54ca..948c3349498 100644
--- a/compiler/rustc_const_eval/src/const_eval/mod.rs
+++ b/compiler/rustc_const_eval/src/const_eval/mod.rs
@@ -138,7 +138,7 @@ pub(crate) fn deref_mir_constant<'tcx>(
     let mplace = ecx.deref_operand(&op).unwrap();
     if let Some(alloc_id) = mplace.ptr.provenance {
         assert_eq!(
-            tcx.get_global_alloc(alloc_id).unwrap().unwrap_memory().0.0.mutability,
+            tcx.global_alloc(alloc_id).unwrap_memory().0.0.mutability,
             Mutability::Not,
             "deref_mir_constant cannot be used with mutable allocations as \
             that could allow pattern matching to observe mutable statics",
@@ -147,7 +147,6 @@ pub(crate) fn deref_mir_constant<'tcx>(
 
     let ty = match mplace.meta {
         MemPlaceMeta::None => mplace.layout.ty,
-        MemPlaceMeta::Poison => bug!("poison metadata in `deref_mir_constant`: {:#?}", mplace),
         // In case of unsized types, figure out the real type behind.
         MemPlaceMeta::Meta(scalar) => match mplace.layout.ty.kind() {
             ty::Str => bug!("there's no sized equivalent of a `str`"),
diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs
index 2288a4e7b6c..8fff4571d12 100644
--- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs
+++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs
@@ -436,7 +436,7 @@ fn valtree_into_mplace<'tcx>(
 
                         let offset = place_adjusted.layout.fields.offset(i);
                         place
-                            .offset(
+                            .offset_with_meta(
                                 offset,
                                 MemPlaceMeta::Meta(Scalar::from_machine_usize(
                                     num_elems as u64,
diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs
index 5d598b65c72..c97c31eb9da 100644
--- a/compiler/rustc_const_eval/src/interpret/cast.rs
+++ b/compiler/rustc_const_eval/src/interpret/cast.rs
@@ -18,10 +18,10 @@ use super::{
 impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     pub fn cast(
         &mut self,
-        src: &OpTy<'tcx, M::PointerTag>,
+        src: &OpTy<'tcx, M::Provenance>,
         cast_kind: CastKind,
         cast_ty: Ty<'tcx>,
-        dest: &PlaceTy<'tcx, M::PointerTag>,
+        dest: &PlaceTy<'tcx, M::Provenance>,
     ) -> InterpResult<'tcx> {
         use rustc_middle::mir::CastKind::*;
         // FIXME: In which cases should we trigger UB when the source is uninit?
@@ -114,9 +114,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
 
     pub fn misc_cast(
         &mut self,
-        src: &ImmTy<'tcx, M::PointerTag>,
+        src: &ImmTy<'tcx, M::Provenance>,
         cast_ty: Ty<'tcx>,
-    ) -> InterpResult<'tcx, Immediate<M::PointerTag>> {
+    ) -> InterpResult<'tcx, Immediate<M::Provenance>> {
         use rustc_type_ir::sty::TyKind::*;
         trace!("Casting {:?}: {:?} to {:?}", *src, src.layout.ty, cast_ty);
 
@@ -173,14 +173,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
 
     pub fn pointer_expose_address_cast(
         &mut self,
-        src: &ImmTy<'tcx, M::PointerTag>,
+        src: &ImmTy<'tcx, M::Provenance>,
         cast_ty: Ty<'tcx>,
-    ) -> InterpResult<'tcx, Immediate<M::PointerTag>> {
+    ) -> InterpResult<'tcx, Immediate<M::Provenance>> {
         assert_matches!(src.layout.ty.kind(), ty::RawPtr(_) | ty::FnPtr(_));
         assert!(cast_ty.is_integral());
 
         let scalar = src.to_scalar()?;
-        let ptr = self.scalar_to_ptr(scalar)?;
+        let ptr = scalar.to_pointer(self)?;
         match ptr.into_pointer_or_addr() {
             Ok(ptr) => M::expose_ptr(self, ptr)?,
             Err(_) => {} // Do nothing, exposing an invalid pointer (`None` provenance) is a NOP.
@@ -190,9 +190,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
 
     pub fn pointer_from_exposed_address_cast(
         &mut self,
-        src: &ImmTy<'tcx, M::PointerTag>,
+        src: &ImmTy<'tcx, M::Provenance>,
         cast_ty: Ty<'tcx>,
-    ) -> InterpResult<'tcx, Immediate<M::PointerTag>> {
+    ) -> InterpResult<'tcx, Immediate<M::Provenance>> {
         assert!(src.layout.ty.is_integral());
         assert_matches!(cast_ty.kind(), ty::RawPtr(_));
 
@@ -208,10 +208,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
 
     pub fn cast_from_int_like(
         &self,
-        scalar: Scalar<M::PointerTag>, // input value (there is no ScalarTy so we separate data+layout)
+        scalar: Scalar<M::Provenance>, // input value (there is no ScalarTy so we separate data+layout)
         src_layout: TyAndLayout<'tcx>,
         cast_ty: Ty<'tcx>,
-    ) -> InterpResult<'tcx, Scalar<M::PointerTag>> {
+    ) -> InterpResult<'tcx, Scalar<M::Provenance>> {
         // Let's make sure v is sign-extended *if* it has a signed type.
         let signed = src_layout.abi.is_signed(); // Also asserts that abi is `Scalar`.
 
@@ -245,9 +245,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         })
     }
 
-    fn cast_from_float<F>(&self, f: F, dest_ty: Ty<'tcx>) -> Scalar<M::PointerTag>
+    fn cast_from_float<F>(&self, f: F, dest_ty: Ty<'tcx>) -> Scalar<M::Provenance>
     where
-        F: Float + Into<Scalar<M::PointerTag>> + FloatConvert<Single> + FloatConvert<Double>,
+        F: Float + Into<Scalar<M::Provenance>> + FloatConvert<Single> + FloatConvert<Double>,
     {
         use rustc_type_ir::sty::TyKind::*;
         match *dest_ty.kind() {
@@ -279,8 +279,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
 
     fn unsize_into_ptr(
         &mut self,
-        src: &OpTy<'tcx, M::PointerTag>,
-        dest: &PlaceTy<'tcx, M::PointerTag>,
+        src: &OpTy<'tcx, M::Provenance>,
+        dest: &PlaceTy<'tcx, M::Provenance>,
         // The pointee types
         source_ty: Ty<'tcx>,
         cast_ty: Ty<'tcx>,
@@ -298,30 +298,18 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 self.write_immediate(val, dest)
             }
             (&ty::Dynamic(ref data_a, ..), &ty::Dynamic(ref data_b, ..)) => {
-                let val = self.read_immediate(src)?;
-                if data_a.principal_def_id() == data_b.principal_def_id() {
-                    return self.write_immediate(*val, dest);
-                }
-                // trait upcasting coercion
-                let vptr_entry_idx = self.tcx.vtable_trait_upcasting_coercion_new_vptr_slot((
-                    src_pointee_ty,
-                    dest_pointee_ty,
-                ));
-
-                if let Some(entry_idx) = vptr_entry_idx {
-                    let entry_idx = u64::try_from(entry_idx).unwrap();
-                    let (old_data, old_vptr) = val.to_scalar_pair()?;
-                    let old_vptr = self.scalar_to_ptr(old_vptr)?;
-                    let new_vptr = self
-                        .read_new_vtable_after_trait_upcasting_from_vtable(old_vptr, entry_idx)?;
-                    self.write_immediate(Immediate::new_dyn_trait(old_data, new_vptr, self), dest)
-                } else {
-                    self.write_immediate(*val, dest)
+                let (old_data, old_vptr) = self.read_immediate(src)?.to_scalar_pair()?;
+                let old_vptr = old_vptr.to_pointer(self)?;
+                let (ty, old_trait) = self.get_ptr_vtable(old_vptr)?;
+                if old_trait != data_a.principal() {
+                    throw_ub_format!("upcast on a pointer whose vtable does not match its type");
                 }
+                let new_vptr = self.get_vtable_ptr(ty, data_b.principal())?;
+                self.write_immediate(Immediate::new_dyn_trait(old_data, new_vptr, self), dest)
             }
             (_, &ty::Dynamic(ref data, _)) => {
                 // Initial cast from sized to dyn trait
-                let vtable = self.get_vtable(src_pointee_ty, data.principal())?;
+                let vtable = self.get_vtable_ptr(src_pointee_ty, data.principal())?;
                 let ptr = self.read_immediate(src)?.to_scalar()?;
                 let val = Immediate::new_dyn_trait(ptr, vtable, &*self.tcx);
                 self.write_immediate(val, dest)
@@ -335,9 +323,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
 
     fn unsize_into(
         &mut self,
-        src: &OpTy<'tcx, M::PointerTag>,
+        src: &OpTy<'tcx, M::Provenance>,
         cast_ty: TyAndLayout<'tcx>,
-        dest: &PlaceTy<'tcx, M::PointerTag>,
+        dest: &PlaceTy<'tcx, M::Provenance>,
     ) -> InterpResult<'tcx> {
         trace!("Unsizing {:?} of type {} into {:?}", *src, src.layout.ty, cast_ty.ty);
         match (&src.layout.ty.kind(), &cast_ty.ty.kind()) {
diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs
index 2e47cf89210..150d6589b08 100644
--- a/compiler/rustc_const_eval/src/interpret/eval_context.rs
+++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs
@@ -2,10 +2,8 @@ use std::cell::Cell;
 use std::fmt;
 use std::mem;
 
-use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 use rustc_hir::{self as hir, def_id::DefId, definitions::DefPathData};
 use rustc_index::vec::IndexVec;
-use rustc_macros::HashStable;
 use rustc_middle::mir;
 use rustc_middle::mir::interpret::{InterpError, InvalidProgramInfo};
 use rustc_middle::ty::layout::{
@@ -16,7 +14,6 @@ use rustc_middle::ty::{
     self, query::TyCtxtAt, subst::SubstsRef, ParamEnv, Ty, TyCtxt, TypeFoldable,
 };
 use rustc_mir_dataflow::storage::always_storage_live_locals;
-use rustc_query_system::ich::StableHashingContext;
 use rustc_session::Limit;
 use rustc_span::{Pos, Span};
 use rustc_target::abi::{call::FnAbi, Align, HasDataLayout, Size, TargetDataLayout};
@@ -84,7 +81,7 @@ impl Drop for SpanGuard {
 }
 
 /// A stack frame.
-pub struct Frame<'mir, 'tcx, Tag: Provenance = AllocId, Extra = ()> {
+pub struct Frame<'mir, 'tcx, Prov: Provenance = AllocId, Extra = ()> {
     ////////////////////////////////////////////////////////////////////////////////
     // Function and callsite information
     ////////////////////////////////////////////////////////////////////////////////
@@ -105,7 +102,7 @@ pub struct Frame<'mir, 'tcx, Tag: Provenance = AllocId, Extra = ()> {
 
     /// The location where the result of the current stack frame should be written to,
     /// and its layout in the caller.
-    pub return_place: PlaceTy<'tcx, Tag>,
+    pub return_place: PlaceTy<'tcx, Prov>,
 
     /// The list of locals for this stack frame, stored in order as
     /// `[return_ptr, arguments..., variables..., temporaries...]`.
@@ -114,7 +111,7 @@ pub struct Frame<'mir, 'tcx, Tag: Provenance = AllocId, Extra = ()> {
     /// can either directly contain `Scalar` or refer to some part of an `Allocation`.
     ///
     /// Do *not* access this directly; always go through the machine hook!
-    pub locals: IndexVec<mir::Local, LocalState<'tcx, Tag>>,
+    pub locals: IndexVec<mir::Local, LocalState<'tcx, Prov>>,
 
     /// The span of the `tracing` crate is stored here.
     /// When the guard is dropped, the span is exited. This gives us
@@ -142,7 +139,7 @@ pub struct FrameInfo<'tcx> {
 }
 
 /// Unwind information.
-#[derive(Clone, Copy, Eq, PartialEq, Debug, HashStable)]
+#[derive(Clone, Copy, Eq, PartialEq, Debug)]
 pub enum StackPopUnwind {
     /// The cleanup block.
     Cleanup(mir::BasicBlock),
@@ -152,7 +149,7 @@ pub enum StackPopUnwind {
     NotAllowed,
 }
 
-#[derive(Clone, Copy, Eq, PartialEq, Debug, HashStable)] // Miri debug-prints these
+#[derive(Clone, Copy, Eq, PartialEq, Debug)] // Miri debug-prints these
 pub enum StackPopCleanup {
     /// Jump to the next block in the caller, or cause UB if None (that's a function
     /// that may never return). Also store layout of return place so
@@ -168,17 +165,16 @@ pub enum StackPopCleanup {
 }
 
 /// State of a local variable including a memoized layout
-#[derive(Clone, Debug, PartialEq, Eq, HashStable)]
-pub struct LocalState<'tcx, Tag: Provenance = AllocId> {
-    pub value: LocalValue<Tag>,
+#[derive(Clone, Debug)]
+pub struct LocalState<'tcx, Prov: Provenance = AllocId> {
+    pub value: LocalValue<Prov>,
     /// Don't modify if `Some`, this is only used to prevent computing the layout twice
-    #[stable_hasher(ignore)]
     pub layout: Cell<Option<TyAndLayout<'tcx>>>,
 }
 
 /// Current value of a local variable
-#[derive(Copy, Clone, PartialEq, Eq, HashStable, Debug)] // Miri debug-prints these
-pub enum LocalValue<Tag: Provenance = AllocId> {
+#[derive(Copy, Clone, Debug)] // Miri debug-prints these
+pub enum LocalValue<Prov: Provenance = AllocId> {
     /// This local is not currently alive, and cannot be used at all.
     Dead,
     /// A normal, live local.
@@ -186,16 +182,16 @@ pub enum LocalValue<Tag: Provenance = AllocId> {
     /// This is an optimization over just always having a pointer here;
     /// we can thus avoid doing an allocation when the local just stores
     /// immediate values *and* never has its address taken.
-    Live(Operand<Tag>),
+    Live(Operand<Prov>),
 }
 
-impl<'tcx, Tag: Provenance + 'static> LocalState<'tcx, Tag> {
+impl<'tcx, Prov: Provenance + 'static> LocalState<'tcx, Prov> {
     /// Read the local's value or error if the local is not yet live or not live anymore.
     ///
     /// Note: This may only be invoked from the `Machine::access_local` hook and not from
     /// anywhere else. You may be invalidating machine invariants if you do!
     #[inline]
-    pub fn access(&self) -> InterpResult<'tcx, &Operand<Tag>> {
+    pub fn access(&self) -> InterpResult<'tcx, &Operand<Prov>> {
         match &self.value {
             LocalValue::Dead => throw_ub!(DeadLocal), // could even be "invalid program"?
             LocalValue::Live(val) => Ok(val),
@@ -208,7 +204,7 @@ impl<'tcx, Tag: Provenance + 'static> LocalState<'tcx, Tag> {
     /// Note: This may only be invoked from the `Machine::access_local_mut` hook and not from
     /// anywhere else. You may be invalidating machine invariants if you do!
     #[inline]
-    pub fn access_mut(&mut self) -> InterpResult<'tcx, &mut Operand<Tag>> {
+    pub fn access_mut(&mut self) -> InterpResult<'tcx, &mut Operand<Prov>> {
         match &mut self.value {
             LocalValue::Dead => throw_ub!(DeadLocal), // could even be "invalid program"?
             LocalValue::Live(val) => Ok(val),
@@ -216,8 +212,8 @@ impl<'tcx, Tag: Provenance + 'static> LocalState<'tcx, Tag> {
     }
 }
 
-impl<'mir, 'tcx, Tag: Provenance> Frame<'mir, 'tcx, Tag> {
-    pub fn with_extra<Extra>(self, extra: Extra) -> Frame<'mir, 'tcx, Tag, Extra> {
+impl<'mir, 'tcx, Prov: Provenance> Frame<'mir, 'tcx, Prov> {
+    pub fn with_extra<Extra>(self, extra: Extra) -> Frame<'mir, 'tcx, Prov, Extra> {
         Frame {
             body: self.body,
             instance: self.instance,
@@ -231,7 +227,7 @@ impl<'mir, 'tcx, Tag: Provenance> Frame<'mir, 'tcx, Tag> {
     }
 }
 
-impl<'mir, 'tcx, Tag: Provenance, Extra> Frame<'mir, 'tcx, Tag, Extra> {
+impl<'mir, 'tcx, Prov: Provenance, Extra> Frame<'mir, 'tcx, Prov, Extra> {
     /// Get the current location within the Frame.
     ///
     /// If this is `Err`, we are not currently executing any particular statement in
@@ -426,14 +422,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     }
 
     #[inline(always)]
-    pub(crate) fn stack(&self) -> &[Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra>] {
+    pub(crate) fn stack(&self) -> &[Frame<'mir, 'tcx, M::Provenance, M::FrameExtra>] {
         M::stack(self)
     }
 
     #[inline(always)]
     pub(crate) fn stack_mut(
         &mut self,
-    ) -> &mut Vec<Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra>> {
+    ) -> &mut Vec<Frame<'mir, 'tcx, M::Provenance, M::FrameExtra>> {
         M::stack_mut(self)
     }
 
@@ -445,12 +441,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     }
 
     #[inline(always)]
-    pub fn frame(&self) -> &Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra> {
+    pub fn frame(&self) -> &Frame<'mir, 'tcx, M::Provenance, M::FrameExtra> {
         self.stack().last().expect("no call frames exist")
     }
 
     #[inline(always)]
-    pub fn frame_mut(&mut self) -> &mut Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra> {
+    pub fn frame_mut(&mut self) -> &mut Frame<'mir, 'tcx, M::Provenance, M::FrameExtra> {
         self.stack_mut().last_mut().expect("no call frames exist")
     }
 
@@ -507,7 +503,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     /// stack frame), to bring it into the proper environment for this interpreter.
     pub(super) fn subst_from_frame_and_normalize_erasing_regions<T: TypeFoldable<'tcx>>(
         &self,
-        frame: &Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra>,
+        frame: &Frame<'mir, 'tcx, M::Provenance, M::FrameExtra>,
         value: T,
     ) -> Result<T, InterpError<'tcx>> {
         frame
@@ -544,7 +540,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     #[inline(always)]
     pub fn layout_of_local(
         &self,
-        frame: &Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra>,
+        frame: &Frame<'mir, 'tcx, M::Provenance, M::FrameExtra>,
         local: mir::Local,
         layout: Option<TyAndLayout<'tcx>>,
     ) -> InterpResult<'tcx, TyAndLayout<'tcx>> {
@@ -573,7 +569,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     /// This can fail to provide an answer for extern types.
     pub(super) fn size_and_align_of(
         &self,
-        metadata: &MemPlaceMeta<M::PointerTag>,
+        metadata: &MemPlaceMeta<M::Provenance>,
         layout: &TyAndLayout<'tcx>,
     ) -> InterpResult<'tcx, Option<(Size, Align)>> {
         if !layout.is_unsized() {
@@ -633,9 +629,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 Ok(Some((size, align)))
             }
             ty::Dynamic(..) => {
-                let vtable = self.scalar_to_ptr(metadata.unwrap_meta())?;
+                let vtable = metadata.unwrap_meta().to_pointer(self)?;
                 // Read size and align from vtable (already checks size).
-                Ok(Some(self.read_size_and_align_from_vtable(vtable)?))
+                Ok(Some(self.get_vtable_size_and_align(vtable)?))
             }
 
             ty::Slice(_) | ty::Str => {
@@ -659,7 +655,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     #[inline]
     pub fn size_and_align_of_mplace(
         &self,
-        mplace: &MPlaceTy<'tcx, M::PointerTag>,
+        mplace: &MPlaceTy<'tcx, M::Provenance>,
     ) -> InterpResult<'tcx, Option<(Size, Align)>> {
         self.size_and_align_of(&mplace.meta, &mplace.layout)
     }
@@ -669,7 +665,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         &mut self,
         instance: ty::Instance<'tcx>,
         body: &'mir mir::Body<'tcx>,
-        return_place: &PlaceTy<'tcx, M::PointerTag>,
+        return_place: &PlaceTy<'tcx, M::Provenance>,
         return_to_block: StackPopCleanup,
     ) -> InterpResult<'tcx> {
         trace!("body: {:#?}", body);
@@ -678,7 +674,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             body,
             loc: Err(body.span), // Span used for errors caused during preamble.
             return_to_block,
-            return_place: *return_place,
+            return_place: return_place.clone(),
             // empty local array, we fill it in below, after we are inside the stack frame and
             // all methods actually know about the frame
             locals: IndexVec::new(),
@@ -799,7 +795,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             let op = self
                 .local_to_op(self.frame(), mir::RETURN_PLACE, None)
                 .expect("return place should always be live");
-            let dest = self.frame().return_place;
+            let dest = self.frame().return_place.clone();
             let err = self.copy_op(&op, &dest, /*allow_transmute*/ true);
             trace!("return value: {:?}", self.dump_place(*dest));
             // We delay actually short-circuiting on this error until *after* the stack frame is
@@ -895,7 +891,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     }
 
     #[instrument(skip(self), level = "debug")]
-    fn deallocate_local(&mut self, local: LocalValue<M::PointerTag>) -> InterpResult<'tcx> {
+    fn deallocate_local(&mut self, local: LocalValue<M::Provenance>) -> InterpResult<'tcx> {
         if let LocalValue::Live(Operand::Indirect(MemPlace { ptr, .. })) = local {
             // All locals have a backing allocation, even if the allocation is empty
             // due to the local having ZST type. Hence we can `unwrap`.
@@ -913,7 +909,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     pub fn eval_to_allocation(
         &self,
         gid: GlobalId<'tcx>,
-    ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
+    ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
         // For statics we pick `ParamEnv::reveal_all`, because statics don't have generics
         // and thus don't care about the parameter environment. While we could just use
         // `self.param_env`, that would mean we invoke the query to evaluate the static
@@ -931,7 +927,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     }
 
     #[must_use]
-    pub fn dump_place(&self, place: Place<M::PointerTag>) -> PlacePrinter<'_, 'mir, 'tcx, M> {
+    pub fn dump_place(&self, place: Place<M::Provenance>) -> PlacePrinter<'_, 'mir, 'tcx, M> {
         PlacePrinter { ecx: self, place }
     }
 
@@ -960,7 +956,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
 /// Helper struct for the `dump_place` function.
 pub struct PlacePrinter<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> {
     ecx: &'a InterpCx<'mir, 'tcx, M>,
-    place: Place<M::PointerTag>,
+    place: Place<M::Provenance>,
 }
 
 impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> std::fmt::Debug
@@ -987,7 +983,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> std::fmt::Debug
                             " by {} ref {:?}:",
                             match mplace.meta {
                                 MemPlaceMeta::Meta(meta) => format!(" meta({:?})", meta),
-                                MemPlaceMeta::Poison | MemPlaceMeta::None => String::new(),
+                                MemPlaceMeta::None => String::new(),
                             },
                             mplace.ptr,
                         )?;
@@ -1021,31 +1017,3 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> std::fmt::Debug
         }
     }
 }
-
-impl<'ctx, 'mir, 'tcx, Tag: Provenance, Extra> HashStable<StableHashingContext<'ctx>>
-    for Frame<'mir, 'tcx, Tag, Extra>
-where
-    Extra: HashStable<StableHashingContext<'ctx>>,
-    Tag: HashStable<StableHashingContext<'ctx>>,
-{
-    fn hash_stable(&self, hcx: &mut StableHashingContext<'ctx>, hasher: &mut StableHasher) {
-        // Exhaustive match on fields to make sure we forget no field.
-        let Frame {
-            body,
-            instance,
-            return_to_block,
-            return_place,
-            locals,
-            loc,
-            extra,
-            tracing_span: _,
-        } = self;
-        body.hash_stable(hcx, hasher);
-        instance.hash_stable(hcx, hasher);
-        return_to_block.hash_stable(hcx, hasher);
-        return_place.hash_stable(hcx, hasher);
-        locals.hash_stable(hcx, hasher);
-        loc.hash_stable(hcx, hasher);
-        extra.hash_stable(hcx, hasher);
-    }
-}
diff --git a/compiler/rustc_const_eval/src/interpret/intern.rs b/compiler/rustc_const_eval/src/interpret/intern.rs
index 9dfdafcb38e..376b8872c90 100644
--- a/compiler/rustc_const_eval/src/interpret/intern.rs
+++ b/compiler/rustc_const_eval/src/interpret/intern.rs
@@ -33,7 +33,7 @@ pub trait CompileTimeMachine<'mir, 'tcx, T> = Machine<
     'mir,
     'tcx,
     MemoryKind = T,
-    PointerTag = AllocId,
+    Provenance = AllocId,
     ExtraFnVal = !,
     FrameExtra = (),
     AllocExtra = (),
@@ -94,7 +94,7 @@ fn intern_shallow<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx, const_eval:
         // to validation to error -- it has the much better error messages, pointing out where
         // in the value the dangling reference lies.
         // The `delay_span_bug` ensures that we don't forget such a check in validation.
-        if tcx.get_global_alloc(alloc_id).is_none() {
+        if tcx.try_get_global_alloc(alloc_id).is_none() {
             tcx.sess.delay_span_bug(ecx.tcx.span, "tried to intern dangling pointer");
         }
         // treat dangling pointers like other statics
@@ -217,7 +217,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx, const_eval::Memory
         }
 
         if let Some(def) = mplace.layout.ty.ty_adt_def() {
-            if Some(def.did()) == self.ecx.tcx.lang_items().unsafe_cell_type() {
+            if def.is_unsafe_cell() {
                 // We are crossing over an `UnsafeCell`, we can mutate again. This means that
                 // References we encounter inside here are interned as pointing to mutable
                 // allocations.
@@ -245,7 +245,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx, const_eval::Memory
             if let ty::Dynamic(..) =
                 tcx.struct_tail_erasing_lifetimes(referenced_ty, self.ecx.param_env).kind()
             {
-                let ptr = self.ecx.scalar_to_ptr(mplace.meta.unwrap_meta())?;
+                let ptr = mplace.meta.unwrap_meta().to_pointer(&tcx)?;
                 if let Some(alloc_id) = ptr.provenance {
                     // Explicitly choose const mode here, since vtables are immutable, even
                     // if the reference of the fat pointer is mutable.
@@ -454,7 +454,7 @@ pub fn intern_const_alloc_recursive<
                 .sess
                 .span_err(ecx.tcx.span, "encountered dangling pointer in final constant");
             return Err(reported);
-        } else if ecx.tcx.get_global_alloc(alloc_id).is_none() {
+        } else if ecx.tcx.try_get_global_alloc(alloc_id).is_none() {
             // We have hit an `AllocId` that is neither in local or global memory and isn't
             // marked as dangling by local memory.  That should be impossible.
             span_bug!(ecx.tcx.span, "encountered unknown alloc id {:?}", alloc_id);
@@ -474,7 +474,7 @@ impl<'mir, 'tcx: 'mir, M: super::intern::CompileTimeMachine<'mir, 'tcx, !>>
         layout: TyAndLayout<'tcx>,
         f: impl FnOnce(
             &mut InterpCx<'mir, 'tcx, M>,
-            &PlaceTy<'tcx, M::PointerTag>,
+            &PlaceTy<'tcx, M::Provenance>,
         ) -> InterpResult<'tcx, ()>,
     ) -> InterpResult<'tcx, ConstAllocation<'tcx>> {
         let dest = self.allocate(layout, MemoryKind::Stack)?;
diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
index 93b64d9d37a..08209eb7932 100644
--- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs
+++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
@@ -7,7 +7,7 @@ use std::convert::TryFrom;
 use rustc_hir::def_id::DefId;
 use rustc_middle::mir::{
     self,
-    interpret::{ConstValue, GlobalId, InterpResult, Scalar},
+    interpret::{ConstValue, GlobalId, InterpResult, PointerArithmetic, Scalar},
     BinOp,
 };
 use rustc_middle::ty;
@@ -15,7 +15,7 @@ use rustc_middle::ty::layout::LayoutOf as _;
 use rustc_middle::ty::subst::SubstsRef;
 use rustc_middle::ty::{Ty, TyCtxt};
 use rustc_span::symbol::{sym, Symbol};
-use rustc_target::abi::{Abi, Align, InitKind, Primitive, Size};
+use rustc_target::abi::{Abi, Align, Primitive, Size};
 
 use super::{
     util::ensure_monomorphic_enough, CheckInAllocMsg, ImmTy, InterpCx, Machine, OpTy, PlaceTy,
@@ -25,7 +25,7 @@ use super::{
 mod caller_location;
 mod type_name;
 
-fn numeric_intrinsic<Tag>(name: Symbol, bits: u128, kind: Primitive) -> Scalar<Tag> {
+fn numeric_intrinsic<Prov>(name: Symbol, bits: u128, kind: Primitive) -> Scalar<Prov> {
     let size = match kind {
         Primitive::Int(integer, _) => integer.size(),
         _ => bug!("invalid `{}` argument: {:?}", name, bits),
@@ -114,8 +114,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     pub fn emulate_intrinsic(
         &mut self,
         instance: ty::Instance<'tcx>,
-        args: &[OpTy<'tcx, M::PointerTag>],
-        dest: &PlaceTy<'tcx, M::PointerTag>,
+        args: &[OpTy<'tcx, M::Provenance>],
+        dest: &PlaceTy<'tcx, M::Provenance>,
         ret: Option<mir::BasicBlock>,
     ) -> InterpResult<'tcx, bool> {
         let substs = instance.substs;
@@ -132,8 +132,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             Some(p) => p,
         };
 
-        // Keep the patterns in this match ordered the same as the list in
-        // `src/librustc_middle/ty/constness.rs`
         match intrinsic_name {
             sym::caller_location => {
                 let span = self.find_closest_untracked_caller_location();
@@ -330,7 +328,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                             // We managed to find a valid allocation for one pointer, but not the other.
                             // That means they are definitely not pointing to the same allocation.
                             throw_ub_format!(
-                                "{} called on pointers into different allocations",
+                                "`{}` called on pointers into different allocations",
                                 intrinsic_name
                             );
                         }
@@ -338,7 +336,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                             // Found allocation for both. They must be into the same allocation.
                             if a_alloc_id != b_alloc_id {
                                 throw_ub_format!(
-                                    "{} called on pointers into different allocations",
+                                    "`{}` called on pointers into different allocations",
                                     intrinsic_name
                                 );
                             }
@@ -348,47 +346,71 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                     };
 
                 // Compute distance.
-                let distance = {
-                    // The subtraction is always done in `isize` to enforce
-                    // the "no more than `isize::MAX` apart" requirement.
-                    let a_offset = ImmTy::from_uint(a_offset, isize_layout);
-                    let b_offset = ImmTy::from_uint(b_offset, isize_layout);
-                    let (val, overflowed, _ty) =
-                        self.overflowing_binary_op(BinOp::Sub, &a_offset, &b_offset)?;
+                let dist = {
+                    // Addresses are unsigned, so this is a `usize` computation. We have to do the
+                    // overflow check separately anyway.
+                    let (val, overflowed, _ty) = {
+                        let a_offset = ImmTy::from_uint(a_offset, usize_layout);
+                        let b_offset = ImmTy::from_uint(b_offset, usize_layout);
+                        self.overflowing_binary_op(BinOp::Sub, &a_offset, &b_offset)?
+                    };
                     if overflowed {
-                        throw_ub_format!("pointers were too far apart for {}", intrinsic_name);
+                        // a < b
+                        if intrinsic_name == sym::ptr_offset_from_unsigned {
+                            throw_ub_format!(
+                                "`{}` called when first pointer has smaller offset than second: {} < {}",
+                                intrinsic_name,
+                                a_offset,
+                                b_offset,
+                            );
+                        }
+                        // The signed form of the intrinsic allows this. If we interpret the
+                        // difference as isize, we'll get the proper signed difference. If that
+                        // seems *positive*, they were more than isize::MAX apart.
+                        let dist = val.to_machine_isize(self)?;
+                        if dist >= 0 {
+                            throw_ub_format!(
+                                "`{}` called when first pointer is too far before second",
+                                intrinsic_name
+                            );
+                        }
+                        dist
+                    } else {
+                        // b >= a
+                        let dist = val.to_machine_isize(self)?;
+                        // If converting to isize produced a *negative* result, we had an overflow
+                        // because they were more than isize::MAX apart.
+                        if dist < 0 {
+                            throw_ub_format!(
+                                "`{}` called when first pointer is too far ahead of second",
+                                intrinsic_name
+                            );
+                        }
+                        dist
                     }
-                    val.to_machine_isize(self)?
                 };
 
                 // Check that the range between them is dereferenceable ("in-bounds or one past the
                 // end of the same allocation"). This is like the check in ptr_offset_inbounds.
-                let min_ptr = if distance >= 0 { b } else { a };
+                let min_ptr = if dist >= 0 { b } else { a };
                 self.check_ptr_access_align(
                     min_ptr,
-                    Size::from_bytes(distance.unsigned_abs()),
+                    Size::from_bytes(dist.unsigned_abs()),
                     Align::ONE,
                     CheckInAllocMsg::OffsetFromTest,
                 )?;
 
-                if intrinsic_name == sym::ptr_offset_from_unsigned && distance < 0 {
-                    throw_ub_format!(
-                        "{} called when first pointer has smaller offset than second: {} < {}",
-                        intrinsic_name,
-                        a_offset,
-                        b_offset,
-                    );
-                }
-
                 // Perform division by size to compute return value.
                 let ret_layout = if intrinsic_name == sym::ptr_offset_from_unsigned {
+                    assert!(0 <= dist && dist <= self.machine_isize_max());
                     usize_layout
                 } else {
+                    assert!(self.machine_isize_min() <= dist && dist <= self.machine_isize_max());
                     isize_layout
                 };
                 let pointee_layout = self.layout_of(substs.type_at(0))?;
                 // If ret_layout is unsigned, we checked that so is the distance, so we are good.
-                let val = ImmTy::from_int(distance, ret_layout);
+                let val = ImmTy::from_int(dist, ret_layout);
                 let size = ImmTy::from_int(pointee_layout.size.bytes(), ret_layout);
                 self.exact_div(&val, &size, dest)?;
             }
@@ -413,35 +435,33 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                         ),
                     )?;
                 }
-                if intrinsic_name == sym::assert_zero_valid
-                    && !layout.might_permit_raw_init(
-                        self,
-                        InitKind::Zero,
-                        self.tcx.sess.opts.debugging_opts.strict_init_checks,
-                    )
-                {
-                    M::abort(
-                        self,
-                        format!(
-                            "aborted execution: attempted to zero-initialize type `{}`, which is invalid",
-                            ty
-                        ),
-                    )?;
+
+                if intrinsic_name == sym::assert_zero_valid {
+                    let should_panic = !self.tcx.permits_zero_init(layout);
+
+                    if should_panic {
+                        M::abort(
+                            self,
+                            format!(
+                                "aborted execution: attempted to zero-initialize type `{}`, which is invalid",
+                                ty
+                            ),
+                        )?;
+                    }
                 }
-                if intrinsic_name == sym::assert_uninit_valid
-                    && !layout.might_permit_raw_init(
-                        self,
-                        InitKind::Uninit,
-                        self.tcx.sess.opts.debugging_opts.strict_init_checks,
-                    )
-                {
-                    M::abort(
-                        self,
-                        format!(
-                            "aborted execution: attempted to leave type `{}` uninitialized, which is invalid",
-                            ty
-                        ),
-                    )?;
+
+                if intrinsic_name == sym::assert_uninit_valid {
+                    let should_panic = !self.tcx.permits_uninit_init(layout);
+
+                    if should_panic {
+                        M::abort(
+                            self,
+                            format!(
+                                "aborted execution: attempted to leave type `{}` uninitialized, which is invalid",
+                                ty
+                            ),
+                        )?;
+                    }
                 }
             }
             sym::simd_insert => {
@@ -459,8 +479,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
 
                 for i in 0..dest_len {
                     let place = self.mplace_index(&dest, i)?;
-                    let value =
-                        if i == index { *elem } else { self.mplace_index(&input, i)?.into() };
+                    let value = if i == index {
+                        elem.clone()
+                    } else {
+                        self.mplace_index(&input, i)?.into()
+                    };
                     self.copy_op(&value, &place.into(), /*allow_transmute*/ false)?;
                 }
             }
@@ -493,6 +516,18 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 let result = self.raw_eq_intrinsic(&args[0], &args[1])?;
                 self.write_scalar(result, dest)?;
             }
+
+            sym::vtable_size => {
+                let ptr = self.read_pointer(&args[0])?;
+                let (size, _align) = self.get_vtable_size_and_align(ptr)?;
+                self.write_scalar(Scalar::from_machine_usize(size.bytes(), self), dest)?;
+            }
+            sym::vtable_align => {
+                let ptr = self.read_pointer(&args[0])?;
+                let (_size, align) = self.get_vtable_size_and_align(ptr)?;
+                self.write_scalar(Scalar::from_machine_usize(align.bytes(), self), dest)?;
+            }
+
             _ => return Ok(false),
         }
 
@@ -503,9 +538,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
 
     pub fn exact_div(
         &mut self,
-        a: &ImmTy<'tcx, M::PointerTag>,
-        b: &ImmTy<'tcx, M::PointerTag>,
-        dest: &PlaceTy<'tcx, M::PointerTag>,
+        a: &ImmTy<'tcx, M::Provenance>,
+        b: &ImmTy<'tcx, M::Provenance>,
+        dest: &PlaceTy<'tcx, M::Provenance>,
     ) -> InterpResult<'tcx> {
         // Performs an exact division, resulting in undefined behavior where
         // `x % y != 0` or `y == 0` or `x == T::MIN && y == -1`.
@@ -522,9 +557,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     pub fn saturating_arith(
         &self,
         mir_op: BinOp,
-        l: &ImmTy<'tcx, M::PointerTag>,
-        r: &ImmTy<'tcx, M::PointerTag>,
-    ) -> InterpResult<'tcx, Scalar<M::PointerTag>> {
+        l: &ImmTy<'tcx, M::Provenance>,
+        r: &ImmTy<'tcx, M::Provenance>,
+    ) -> InterpResult<'tcx, Scalar<M::Provenance>> {
         assert!(matches!(mir_op, BinOp::Add | BinOp::Sub));
         let (val, overflowed, _ty) = self.overflowing_binary_op(mir_op, l, r)?;
         Ok(if overflowed {
@@ -567,10 +602,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     /// 0, so offset-by-0 (and only 0) is okay -- except that null cannot be offset by _any_ value.
     pub fn ptr_offset_inbounds(
         &self,
-        ptr: Pointer<Option<M::PointerTag>>,
+        ptr: Pointer<Option<M::Provenance>>,
         pointee_ty: Ty<'tcx>,
         offset_count: i64,
-    ) -> InterpResult<'tcx, Pointer<Option<M::PointerTag>>> {
+    ) -> InterpResult<'tcx, Pointer<Option<M::Provenance>>> {
         // We cannot overflow i64 as a type's size must be <= isize::MAX.
         let pointee_size = i64::try_from(self.layout_of(pointee_ty)?.size.bytes()).unwrap();
         // The computed offset, in bytes, must not overflow an isize.
@@ -598,9 +633,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     /// Copy `count*size_of::<T>()` many bytes from `*src` to `*dst`.
     pub(crate) fn copy_intrinsic(
         &mut self,
-        src: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::PointerTag>,
-        dst: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::PointerTag>,
-        count: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::PointerTag>,
+        src: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::Provenance>,
+        dst: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::Provenance>,
+        count: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::Provenance>,
         nonoverlapping: bool,
     ) -> InterpResult<'tcx> {
         let count = self.read_scalar(&count)?.to_machine_usize(self)?;
@@ -623,9 +658,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
 
     pub(crate) fn write_bytes_intrinsic(
         &mut self,
-        dst: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::PointerTag>,
-        byte: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::PointerTag>,
-        count: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::PointerTag>,
+        dst: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::Provenance>,
+        byte: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::Provenance>,
+        count: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::Provenance>,
     ) -> InterpResult<'tcx> {
         let layout = self.layout_of(dst.layout.ty.builtin_deref(true).unwrap().ty)?;
 
@@ -646,9 +681,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
 
     pub(crate) fn raw_eq_intrinsic(
         &mut self,
-        lhs: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::PointerTag>,
-        rhs: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::PointerTag>,
-    ) -> InterpResult<'tcx, Scalar<M::PointerTag>> {
+        lhs: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::Provenance>,
+        rhs: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::Provenance>,
+    ) -> InterpResult<'tcx, Scalar<M::Provenance>> {
         let layout = self.layout_of(lhs.layout.ty.builtin_deref(true).unwrap().ty)?;
         assert!(!layout.is_unsized());
 
diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics/caller_location.rs b/compiler/rustc_const_eval/src/interpret/intrinsics/caller_location.rs
index 23ae2db6438..5864b921552 100644
--- a/compiler/rustc_const_eval/src/interpret/intrinsics/caller_location.rs
+++ b/compiler/rustc_const_eval/src/interpret/intrinsics/caller_location.rs
@@ -79,8 +79,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         filename: Symbol,
         line: u32,
         col: u32,
-    ) -> MPlaceTy<'tcx, M::PointerTag> {
-        let loc_details = &self.tcx.sess.opts.debugging_opts.location_detail;
+    ) -> MPlaceTy<'tcx, M::Provenance> {
+        let loc_details = &self.tcx.sess.opts.unstable_opts.location_detail;
         let file = if loc_details.file {
             self.allocate_str(filename.as_str(), MemoryKind::CallerLocation, Mutability::Not)
         } else {
@@ -123,7 +123,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         )
     }
 
-    pub fn alloc_caller_location_for_span(&mut self, span: Span) -> MPlaceTy<'tcx, M::PointerTag> {
+    pub fn alloc_caller_location_for_span(&mut self, span: Span) -> MPlaceTy<'tcx, M::Provenance> {
         let (file, line, column) = self.location_triple_for_span(span);
         self.alloc_caller_location(file, line, column)
     }
diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs
index 7f8eea94aee..71ccd1799fa 100644
--- a/compiler/rustc_const_eval/src/interpret/machine.rs
+++ b/compiler/rustc_const_eval/src/interpret/machine.rs
@@ -85,11 +85,11 @@ pub trait Machine<'mir, 'tcx>: Sized {
     type MemoryKind: Debug + std::fmt::Display + MayLeak + Eq + 'static;
 
     /// Pointers are "tagged" with provenance information; typically the `AllocId` they belong to.
-    type PointerTag: Provenance + Eq + Hash + 'static;
+    type Provenance: Provenance + Eq + Hash + 'static;
 
-    /// When getting the AllocId of a pointer, some extra data is also obtained from the tag
+    /// When getting the AllocId of a pointer, some extra data is also obtained from the provenance
     /// that is passed to memory access hooks so they can do things with it.
-    type TagExtra: Copy + 'static;
+    type ProvenanceExtra: Copy + 'static;
 
     /// Machines can define extra (non-instance) things that represent values of function pointers.
     /// For example, Miri uses this to return a function pointer from `dlsym`
@@ -105,7 +105,7 @@ pub trait Machine<'mir, 'tcx>: Sized {
     /// Memory's allocation map
     type MemoryMap: AllocMap<
             AllocId,
-            (MemoryKind<Self::MemoryKind>, Allocation<Self::PointerTag, Self::AllocExtra>),
+            (MemoryKind<Self::MemoryKind>, Allocation<Self::Provenance, Self::AllocExtra>),
         > + Default
         + Clone;
 
@@ -113,7 +113,7 @@ pub trait Machine<'mir, 'tcx>: Sized {
     /// or None if such memory should not be mutated and thus any such attempt will cause
     /// a `ModifiedStatic` error to be raised.
     /// Statics are copied under two circumstances: When they are mutated, and when
-    /// `tag_allocation` (see below) returns an owned allocation
+    /// `adjust_allocation` (see below) returns an owned allocation
     /// that is added to the memory so that the work is not done twice.
     const GLOBAL_KIND: Option<Self::MemoryKind>;
 
@@ -126,7 +126,7 @@ pub trait Machine<'mir, 'tcx>: Sized {
     /// Whether, when checking alignment, we should `force_int` and thus support
     /// custom alignment logic based on whatever the integer address happens to be.
     ///
-    /// Requires PointerTag::OFFSET_IS_ADDR to be true.
+    /// Requires Provenance::OFFSET_IS_ADDR to be true.
     fn force_int_for_alignment_check(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool;
 
     /// Whether to enforce the validity invariant
@@ -135,9 +135,6 @@ pub trait Machine<'mir, 'tcx>: Sized {
     /// Whether to enforce integers and floats being initialized.
     fn enforce_number_init(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool;
 
-    /// Whether to enforce integers and floats not having provenance.
-    fn enforce_number_no_provenance(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool;
-
     /// Whether function calls should be [ABI](CallAbi)-checked.
     fn enforce_abi(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool {
         true
@@ -170,8 +167,8 @@ pub trait Machine<'mir, 'tcx>: Sized {
         ecx: &mut InterpCx<'mir, 'tcx, Self>,
         instance: ty::Instance<'tcx>,
         abi: CallAbi,
-        args: &[OpTy<'tcx, Self::PointerTag>],
-        destination: &PlaceTy<'tcx, Self::PointerTag>,
+        args: &[OpTy<'tcx, Self::Provenance>],
+        destination: &PlaceTy<'tcx, Self::Provenance>,
         target: Option<mir::BasicBlock>,
         unwind: StackPopUnwind,
     ) -> InterpResult<'tcx, Option<(&'mir mir::Body<'tcx>, ty::Instance<'tcx>)>>;
@@ -182,8 +179,8 @@ pub trait Machine<'mir, 'tcx>: Sized {
         ecx: &mut InterpCx<'mir, 'tcx, Self>,
         fn_val: Self::ExtraFnVal,
         abi: CallAbi,
-        args: &[OpTy<'tcx, Self::PointerTag>],
-        destination: &PlaceTy<'tcx, Self::PointerTag>,
+        args: &[OpTy<'tcx, Self::Provenance>],
+        destination: &PlaceTy<'tcx, Self::Provenance>,
         target: Option<mir::BasicBlock>,
         unwind: StackPopUnwind,
     ) -> InterpResult<'tcx>;
@@ -193,8 +190,8 @@ pub trait Machine<'mir, 'tcx>: Sized {
     fn call_intrinsic(
         ecx: &mut InterpCx<'mir, 'tcx, Self>,
         instance: ty::Instance<'tcx>,
-        args: &[OpTy<'tcx, Self::PointerTag>],
-        destination: &PlaceTy<'tcx, Self::PointerTag>,
+        args: &[OpTy<'tcx, Self::Provenance>],
+        destination: &PlaceTy<'tcx, Self::Provenance>,
         target: Option<mir::BasicBlock>,
         unwind: StackPopUnwind,
     ) -> InterpResult<'tcx>;
@@ -217,18 +214,18 @@ pub trait Machine<'mir, 'tcx>: Sized {
     fn binary_ptr_op(
         ecx: &InterpCx<'mir, 'tcx, Self>,
         bin_op: mir::BinOp,
-        left: &ImmTy<'tcx, Self::PointerTag>,
-        right: &ImmTy<'tcx, Self::PointerTag>,
-    ) -> InterpResult<'tcx, (Scalar<Self::PointerTag>, bool, Ty<'tcx>)>;
+        left: &ImmTy<'tcx, Self::Provenance>,
+        right: &ImmTy<'tcx, Self::Provenance>,
+    ) -> InterpResult<'tcx, (Scalar<Self::Provenance>, bool, Ty<'tcx>)>;
 
     /// Called to read the specified `local` from the `frame`.
     /// Since reading a ZST is not actually accessing memory or locals, this is never invoked
     /// for ZST reads.
     #[inline]
     fn access_local<'a>(
-        frame: &'a Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>,
+        frame: &'a Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>,
         local: mir::Local,
-    ) -> InterpResult<'tcx, &'a Operand<Self::PointerTag>>
+    ) -> InterpResult<'tcx, &'a Operand<Self::Provenance>>
     where
         'tcx: 'mir,
     {
@@ -243,7 +240,7 @@ pub trait Machine<'mir, 'tcx>: Sized {
         ecx: &'a mut InterpCx<'mir, 'tcx, Self>,
         frame: usize,
         local: mir::Local,
-    ) -> InterpResult<'tcx, &'a mut Operand<Self::PointerTag>>
+    ) -> InterpResult<'tcx, &'a mut Operand<Self::Provenance>>
     where
         'tcx: 'mir,
     {
@@ -275,7 +272,7 @@ pub trait Machine<'mir, 'tcx>: Sized {
     fn thread_local_static_base_pointer(
         _ecx: &mut InterpCx<'mir, 'tcx, Self>,
         def_id: DefId,
-    ) -> InterpResult<'tcx, Pointer<Self::PointerTag>> {
+    ) -> InterpResult<'tcx, Pointer<Self::Provenance>> {
         throw_unsup!(ThreadLocalStatic(def_id))
     }
 
@@ -283,35 +280,28 @@ pub trait Machine<'mir, 'tcx>: Sized {
     fn extern_static_base_pointer(
         ecx: &InterpCx<'mir, 'tcx, Self>,
         def_id: DefId,
-    ) -> InterpResult<'tcx, Pointer<Self::PointerTag>>;
+    ) -> InterpResult<'tcx, Pointer<Self::Provenance>>;
 
     /// Return a "base" pointer for the given allocation: the one that is used for direct
     /// accesses to this static/const/fn allocation, or the one returned from the heap allocator.
     ///
     /// Not called on `extern` or thread-local statics (those use the methods above).
-    fn tag_alloc_base_pointer(
+    fn adjust_alloc_base_pointer(
         ecx: &InterpCx<'mir, 'tcx, Self>,
         ptr: Pointer,
-    ) -> Pointer<Self::PointerTag>;
+    ) -> Pointer<Self::Provenance>;
 
     /// "Int-to-pointer cast"
     fn ptr_from_addr_cast(
         ecx: &InterpCx<'mir, 'tcx, Self>,
         addr: u64,
-    ) -> InterpResult<'tcx, Pointer<Option<Self::PointerTag>>>;
-
-    /// Hook for returning a pointer from a transmute-like operation on an addr.
-    /// This is only needed to support Miri's (unsound) "allow-ptr-int-transmute" flag.
-    fn ptr_from_addr_transmute(
-        ecx: &InterpCx<'mir, 'tcx, Self>,
-        addr: u64,
-    ) -> Pointer<Option<Self::PointerTag>>;
+    ) -> InterpResult<'tcx, Pointer<Option<Self::Provenance>>>;
 
     /// Marks a pointer as exposed, allowing it's provenance
     /// to be recovered. "Pointer-to-int cast"
     fn expose_ptr(
         ecx: &mut InterpCx<'mir, 'tcx, Self>,
-        ptr: Pointer<Self::PointerTag>,
+        ptr: Pointer<Self::Provenance>,
     ) -> InterpResult<'tcx>;
 
     /// Convert a pointer with provenance into an allocation-offset pair
@@ -322,30 +312,30 @@ pub trait Machine<'mir, 'tcx>: Sized {
     /// When this fails, that means the pointer does not point to a live allocation.
     fn ptr_get_alloc(
         ecx: &InterpCx<'mir, 'tcx, Self>,
-        ptr: Pointer<Self::PointerTag>,
-    ) -> Option<(AllocId, Size, Self::TagExtra)>;
-
-    /// Called to initialize the "extra" state of an allocation and make the pointers
-    /// it contains (in relocations) tagged.  The way we construct allocations is
-    /// to always first construct it without extra and then add the extra.
-    /// This keeps uniform code paths for handling both allocations created by CTFE
-    /// for globals, and allocations created by Miri during evaluation.
+        ptr: Pointer<Self::Provenance>,
+    ) -> Option<(AllocId, Size, Self::ProvenanceExtra)>;
+
+    /// Called to adjust allocations to the Provenance and AllocExtra of this machine.
+    ///
+    /// The way we construct allocations is to always first construct it without extra and then add
+    /// the extra. This keeps uniform code paths for handling both allocations created by CTFE for
+    /// globals, and allocations created by Miri during evaluation.
     ///
-    /// `kind` is the kind of the allocation being tagged; it can be `None` when
+    /// `kind` is the kind of the allocation being adjusted; it can be `None` when
     /// it's a global and `GLOBAL_KIND` is `None`.
     ///
     /// This should avoid copying if no work has to be done! If this returns an owned
-    /// allocation (because a copy had to be done to add tags or metadata), machine memory will
+    /// allocation (because a copy had to be done to adjust things), machine memory will
     /// cache the result. (This relies on `AllocMap::get_or` being able to add the
     /// owned allocation to the map even when the map is shared.)
     ///
     /// This must only fail if `alloc` contains relocations.
-    fn init_allocation_extra<'b>(
+    fn adjust_allocation<'b>(
         ecx: &InterpCx<'mir, 'tcx, Self>,
         id: AllocId,
         alloc: Cow<'b, Allocation>,
         kind: Option<MemoryKind<Self::MemoryKind>>,
-    ) -> InterpResult<'tcx, Cow<'b, Allocation<Self::PointerTag, Self::AllocExtra>>>;
+    ) -> InterpResult<'tcx, Cow<'b, Allocation<Self::Provenance, Self::AllocExtra>>>;
 
     /// Hook for performing extra checks on a memory read access.
     ///
@@ -357,7 +347,7 @@ pub trait Machine<'mir, 'tcx>: Sized {
         _tcx: TyCtxt<'tcx>,
         _machine: &Self,
         _alloc_extra: &Self::AllocExtra,
-        _tag: (AllocId, Self::TagExtra),
+        _prov: (AllocId, Self::ProvenanceExtra),
         _range: AllocRange,
     ) -> InterpResult<'tcx> {
         Ok(())
@@ -369,7 +359,7 @@ pub trait Machine<'mir, 'tcx>: Sized {
         _tcx: TyCtxt<'tcx>,
         _machine: &mut Self,
         _alloc_extra: &mut Self::AllocExtra,
-        _tag: (AllocId, Self::TagExtra),
+        _prov: (AllocId, Self::ProvenanceExtra),
         _range: AllocRange,
     ) -> InterpResult<'tcx> {
         Ok(())
@@ -381,7 +371,7 @@ pub trait Machine<'mir, 'tcx>: Sized {
         _tcx: TyCtxt<'tcx>,
         _machine: &mut Self,
         _alloc_extra: &mut Self::AllocExtra,
-        _tag: (AllocId, Self::TagExtra),
+        _prov: (AllocId, Self::ProvenanceExtra),
         _range: AllocRange,
     ) -> InterpResult<'tcx> {
         Ok(())
@@ -392,7 +382,7 @@ pub trait Machine<'mir, 'tcx>: Sized {
     fn retag(
         _ecx: &mut InterpCx<'mir, 'tcx, Self>,
         _kind: mir::RetagKind,
-        _place: &PlaceTy<'tcx, Self::PointerTag>,
+        _place: &PlaceTy<'tcx, Self::Provenance>,
     ) -> InterpResult<'tcx> {
         Ok(())
     }
@@ -400,18 +390,18 @@ pub trait Machine<'mir, 'tcx>: Sized {
     /// Called immediately before a new stack frame gets pushed.
     fn init_frame_extra(
         ecx: &mut InterpCx<'mir, 'tcx, Self>,
-        frame: Frame<'mir, 'tcx, Self::PointerTag>,
-    ) -> InterpResult<'tcx, Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>>;
+        frame: Frame<'mir, 'tcx, Self::Provenance>,
+    ) -> InterpResult<'tcx, Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>>;
 
     /// Borrow the current thread's stack.
     fn stack<'a>(
         ecx: &'a InterpCx<'mir, 'tcx, Self>,
-    ) -> &'a [Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>];
+    ) -> &'a [Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>];
 
     /// Mutably borrow the current thread's stack.
     fn stack_mut<'a>(
         ecx: &'a mut InterpCx<'mir, 'tcx, Self>,
-    ) -> &'a mut Vec<Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>>;
+    ) -> &'a mut Vec<Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>>;
 
     /// Called immediately after a stack frame got pushed and its locals got initialized.
     fn after_stack_push(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> {
@@ -422,7 +412,7 @@ pub trait Machine<'mir, 'tcx>: Sized {
     /// The `locals` have already been destroyed!
     fn after_stack_pop(
         _ecx: &mut InterpCx<'mir, 'tcx, Self>,
-        _frame: Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>,
+        _frame: Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>,
         unwinding: bool,
     ) -> InterpResult<'tcx, StackPopJump> {
         // By default, we do not support unwinding from panics
@@ -434,8 +424,8 @@ pub trait Machine<'mir, 'tcx>: Sized {
 // A lot of the flexibility above is just needed for `Miri`, but all "compile-time" machines
 // (CTFE and ConstProp) use the same instance.  Here, we share that code.
 pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) {
-    type PointerTag = AllocId;
-    type TagExtra = ();
+    type Provenance = AllocId;
+    type ProvenanceExtra = ();
 
     type ExtraFnVal = !;
 
@@ -470,11 +460,6 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) {
     }
 
     #[inline(always)]
-    fn enforce_number_no_provenance(_ecx: &InterpCx<$mir, $tcx, Self>) -> bool {
-        true
-    }
-
-    #[inline(always)]
     fn checked_binop_checks_overflow(_ecx: &InterpCx<$mir, $tcx, Self>) -> bool {
         true
     }
@@ -485,7 +470,7 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) {
         fn_val: !,
         _abi: CallAbi,
         _args: &[OpTy<$tcx>],
-        _destination: &PlaceTy<$tcx, Self::PointerTag>,
+        _destination: &PlaceTy<$tcx, Self::Provenance>,
         _target: Option<mir::BasicBlock>,
         _unwind: StackPopUnwind,
     ) -> InterpResult<$tcx> {
@@ -493,13 +478,12 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) {
     }
 
     #[inline(always)]
-    fn init_allocation_extra<'b>(
+    fn adjust_allocation<'b>(
         _ecx: &InterpCx<$mir, $tcx, Self>,
         _id: AllocId,
         alloc: Cow<'b, Allocation>,
         _kind: Option<MemoryKind<Self::MemoryKind>>,
-    ) -> InterpResult<$tcx, Cow<'b, Allocation<Self::PointerTag>>> {
-        // We do not use a tag so we can just cheaply forward the allocation
+    ) -> InterpResult<$tcx, Cow<'b, Allocation<Self::Provenance>>> {
         Ok(alloc)
     }
 
@@ -512,7 +496,7 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) {
     }
 
     #[inline(always)]
-    fn tag_alloc_base_pointer(
+    fn adjust_alloc_base_pointer(
         _ecx: &InterpCx<$mir, $tcx, Self>,
         ptr: Pointer<AllocId>,
     ) -> Pointer<AllocId> {
@@ -520,14 +504,6 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) {
     }
 
     #[inline(always)]
-    fn ptr_from_addr_transmute(
-        _ecx: &InterpCx<$mir, $tcx, Self>,
-        addr: u64,
-    ) -> Pointer<Option<AllocId>> {
-        Pointer::from_addr(addr)
-    }
-
-    #[inline(always)]
     fn ptr_from_addr_cast(
         _ecx: &InterpCx<$mir, $tcx, Self>,
         addr: u64,
@@ -541,7 +517,7 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) {
     fn ptr_get_alloc(
         _ecx: &InterpCx<$mir, $tcx, Self>,
         ptr: Pointer<AllocId>,
-    ) -> Option<(AllocId, Size, Self::TagExtra)> {
+    ) -> Option<(AllocId, Size, Self::ProvenanceExtra)> {
         // We know `offset` is relative to the allocation, so we can use `into_parts`.
         let (alloc_id, offset) = ptr.into_parts();
         Some((alloc_id, offset, ()))
diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs
index 509fe576893..ed2c4edf9dd 100644
--- a/compiler/rustc_const_eval/src/interpret/memory.rs
+++ b/compiler/rustc_const_eval/src/interpret/memory.rs
@@ -9,14 +9,13 @@
 use std::assert_matches::assert_matches;
 use std::borrow::Cow;
 use std::collections::VecDeque;
-use std::convert::TryFrom;
 use std::fmt;
 use std::ptr;
 
 use rustc_ast::Mutability;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_middle::mir::display_allocation;
-use rustc_middle::ty::{Instance, ParamEnv, TyCtxt};
+use rustc_middle::ty::{self, Instance, ParamEnv, Ty, TyCtxt};
 use rustc_target::abi::{Align, HasDataLayout, Size};
 
 use super::{
@@ -62,6 +61,8 @@ pub enum AllocKind {
     LiveData,
     /// A function allocation (that fn ptrs point to).
     Function,
+    /// A (symbolic) vtable allocation.
+    VTable,
     /// A dead allocation.
     Dead,
 }
@@ -112,16 +113,16 @@ pub struct Memory<'mir, 'tcx, M: Machine<'mir, 'tcx>> {
 /// A reference to some allocation that was already bounds-checked for the given region
 /// and had the on-access machine hooks run.
 #[derive(Copy, Clone)]
-pub struct AllocRef<'a, 'tcx, Tag, Extra> {
-    alloc: &'a Allocation<Tag, Extra>,
+pub struct AllocRef<'a, 'tcx, Prov, Extra> {
+    alloc: &'a Allocation<Prov, Extra>,
     range: AllocRange,
     tcx: TyCtxt<'tcx>,
     alloc_id: AllocId,
 }
 /// A reference to some allocation that was already bounds-checked for the given region
 /// and had the on-access machine hooks run.
-pub struct AllocRefMut<'a, 'tcx, Tag, Extra> {
-    alloc: &'a mut Allocation<Tag, Extra>,
+pub struct AllocRefMut<'a, 'tcx, Prov, Extra> {
+    alloc: &'a mut Allocation<Prov, Extra>,
     range: AllocRange,
     tcx: TyCtxt<'tcx>,
     alloc_id: AllocId,
@@ -156,10 +157,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     pub fn global_base_pointer(
         &self,
         ptr: Pointer<AllocId>,
-    ) -> InterpResult<'tcx, Pointer<M::PointerTag>> {
+    ) -> InterpResult<'tcx, Pointer<M::Provenance>> {
         let alloc_id = ptr.provenance;
         // We need to handle `extern static`.
-        match self.tcx.get_global_alloc(alloc_id) {
+        match self.tcx.try_get_global_alloc(alloc_id) {
             Some(GlobalAlloc::Static(def_id)) if self.tcx.is_thread_local_static(def_id) => {
                 bug!("global memory cannot point to thread-local static")
             }
@@ -168,14 +169,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             }
             _ => {}
         }
-        // And we need to get the tag.
-        Ok(M::tag_alloc_base_pointer(self, ptr))
+        // And we need to get the provenance.
+        Ok(M::adjust_alloc_base_pointer(self, ptr))
     }
 
     pub fn create_fn_alloc_ptr(
         &mut self,
         fn_val: FnVal<'tcx, M::ExtraFnVal>,
-    ) -> Pointer<M::PointerTag> {
+    ) -> Pointer<M::Provenance> {
         let id = match fn_val {
             FnVal::Instance(instance) => self.tcx.create_fn_alloc(instance),
             FnVal::Other(extra) => {
@@ -196,7 +197,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         size: Size,
         align: Align,
         kind: MemoryKind<M::MemoryKind>,
-    ) -> InterpResult<'tcx, Pointer<M::PointerTag>> {
+    ) -> InterpResult<'tcx, Pointer<M::Provenance>> {
         let alloc = Allocation::uninit(size, align, M::PANIC_ON_ALLOC_FAIL)?;
         // We can `unwrap` since `alloc` contains no pointers.
         Ok(self.allocate_raw_ptr(alloc, kind).unwrap())
@@ -208,7 +209,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         align: Align,
         kind: MemoryKind<M::MemoryKind>,
         mutability: Mutability,
-    ) -> Pointer<M::PointerTag> {
+    ) -> Pointer<M::Provenance> {
         let alloc = Allocation::from_bytes(bytes, align, mutability);
         // We can `unwrap` since `alloc` contains no pointers.
         self.allocate_raw_ptr(alloc, kind).unwrap()
@@ -219,27 +220,27 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         &mut self,
         alloc: Allocation,
         kind: MemoryKind<M::MemoryKind>,
-    ) -> InterpResult<'tcx, Pointer<M::PointerTag>> {
+    ) -> InterpResult<'tcx, Pointer<M::Provenance>> {
         let id = self.tcx.reserve_alloc_id();
         debug_assert_ne!(
             Some(kind),
             M::GLOBAL_KIND.map(MemoryKind::Machine),
             "dynamically allocating global memory"
         );
-        let alloc = M::init_allocation_extra(self, id, Cow::Owned(alloc), Some(kind))?;
+        let alloc = M::adjust_allocation(self, id, Cow::Owned(alloc), Some(kind))?;
         self.memory.alloc_map.insert(id, (kind, alloc.into_owned()));
-        Ok(M::tag_alloc_base_pointer(self, Pointer::from(id)))
+        Ok(M::adjust_alloc_base_pointer(self, Pointer::from(id)))
     }
 
     pub fn reallocate_ptr(
         &mut self,
-        ptr: Pointer<Option<M::PointerTag>>,
+        ptr: Pointer<Option<M::Provenance>>,
         old_size_and_align: Option<(Size, Align)>,
         new_size: Size,
         new_align: Align,
         kind: MemoryKind<M::MemoryKind>,
-    ) -> InterpResult<'tcx, Pointer<M::PointerTag>> {
-        let (alloc_id, offset, _tag) = self.ptr_get_alloc_id(ptr)?;
+    ) -> InterpResult<'tcx, Pointer<M::Provenance>> {
+        let (alloc_id, offset, _prov) = self.ptr_get_alloc_id(ptr)?;
         if offset.bytes() != 0 {
             throw_ub_format!(
                 "reallocating {:?} which does not point to the beginning of an object",
@@ -271,11 +272,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     #[instrument(skip(self), level = "debug")]
     pub fn deallocate_ptr(
         &mut self,
-        ptr: Pointer<Option<M::PointerTag>>,
+        ptr: Pointer<Option<M::Provenance>>,
         old_size_and_align: Option<(Size, Align)>,
         kind: MemoryKind<M::MemoryKind>,
     ) -> InterpResult<'tcx> {
-        let (alloc_id, offset, tag) = self.ptr_get_alloc_id(ptr)?;
+        let (alloc_id, offset, prov) = self.ptr_get_alloc_id(ptr)?;
         trace!("deallocating: {alloc_id:?}");
 
         if offset.bytes() != 0 {
@@ -287,10 +288,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
 
         let Some((alloc_kind, mut alloc)) = self.memory.alloc_map.remove(&alloc_id) else {
             // Deallocating global memory -- always an error
-            return Err(match self.tcx.get_global_alloc(alloc_id) {
+            return Err(match self.tcx.try_get_global_alloc(alloc_id) {
                 Some(GlobalAlloc::Function(..)) => {
                     err_ub_format!("deallocating {alloc_id:?}, which is a function")
                 }
+                Some(GlobalAlloc::VTable(..)) => {
+                    err_ub_format!("deallocating {alloc_id:?}, which is a vtable")
+                }
                 Some(GlobalAlloc::Static(..) | GlobalAlloc::Memory(..)) => {
                     err_ub_format!("deallocating {alloc_id:?}, which is static memory")
                 }
@@ -327,7 +331,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             *self.tcx,
             &mut self.machine,
             &mut alloc.extra,
-            (alloc_id, tag),
+            (alloc_id, prov),
             alloc_range(Size::ZERO, size),
         )?;
 
@@ -344,19 +348,19 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     #[inline(always)]
     fn get_ptr_access(
         &self,
-        ptr: Pointer<Option<M::PointerTag>>,
+        ptr: Pointer<Option<M::Provenance>>,
         size: Size,
         align: Align,
-    ) -> InterpResult<'tcx, Option<(AllocId, Size, M::TagExtra)>> {
+    ) -> InterpResult<'tcx, Option<(AllocId, Size, M::ProvenanceExtra)>> {
         let align = M::enforce_alignment(&self).then_some(align);
         self.check_and_deref_ptr(
             ptr,
             size,
             align,
             CheckInAllocMsg::MemoryAccessTest,
-            |alloc_id, offset, tag| {
+            |alloc_id, offset, prov| {
                 let (size, align) = self.get_live_alloc_size_and_align(alloc_id)?;
-                Ok((size, align, (alloc_id, offset, tag)))
+                Ok((size, align, (alloc_id, offset, prov)))
             },
         )
     }
@@ -367,7 +371,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     #[inline(always)]
     pub fn check_ptr_access_align(
         &self,
-        ptr: Pointer<Option<M::PointerTag>>,
+        ptr: Pointer<Option<M::Provenance>>,
         size: Size,
         align: Align,
         msg: CheckInAllocMsg,
@@ -385,11 +389,15 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     /// is done. Returns `None` for size 0, and otherwise `Some` of what `alloc_size` returned.
     fn check_and_deref_ptr<T>(
         &self,
-        ptr: Pointer<Option<M::PointerTag>>,
+        ptr: Pointer<Option<M::Provenance>>,
         size: Size,
         align: Option<Align>,
         msg: CheckInAllocMsg,
-        alloc_size: impl FnOnce(AllocId, Size, M::TagExtra) -> InterpResult<'tcx, (Size, Align, T)>,
+        alloc_size: impl FnOnce(
+            AllocId,
+            Size,
+            M::ProvenanceExtra,
+        ) -> InterpResult<'tcx, (Size, Align, T)>,
     ) -> InterpResult<'tcx, Option<T>> {
         fn check_offset_align<'tcx>(offset: u64, align: Align) -> InterpResult<'tcx> {
             if offset % align.bytes() == 0 {
@@ -417,8 +425,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 }
                 None
             }
-            Ok((alloc_id, offset, tag)) => {
-                let (alloc_size, alloc_align, ret_val) = alloc_size(alloc_id, offset, tag)?;
+            Ok((alloc_id, offset, prov)) => {
+                let (alloc_size, alloc_align, ret_val) = alloc_size(alloc_id, offset, prov)?;
                 // Test bounds. This also ensures non-null.
                 // It is sufficient to check this for the end pointer. Also check for overflow!
                 if offset.checked_add(size, &self.tcx).map_or(true, |end| end > alloc_size) {
@@ -431,7 +439,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                     })
                 }
                 // Ensure we never consider the null pointer dereferencable.
-                if M::PointerTag::OFFSET_IS_ADDR {
+                if M::Provenance::OFFSET_IS_ADDR {
                     assert_ne!(ptr.addr(), Size::ZERO);
                 }
                 // Test align. Check this last; if both bounds and alignment are violated
@@ -462,19 +470,20 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     /// Helper function to obtain a global (tcx) allocation.
     /// This attempts to return a reference to an existing allocation if
     /// one can be found in `tcx`. That, however, is only possible if `tcx` and
-    /// this machine use the same pointer tag, so it is indirected through
-    /// `M::tag_allocation`.
+    /// this machine use the same pointer provenance, so it is indirected through
+    /// `M::adjust_allocation`.
     fn get_global_alloc(
         &self,
         id: AllocId,
         is_write: bool,
-    ) -> InterpResult<'tcx, Cow<'tcx, Allocation<M::PointerTag, M::AllocExtra>>> {
-        let (alloc, def_id) = match self.tcx.get_global_alloc(id) {
+    ) -> InterpResult<'tcx, Cow<'tcx, Allocation<M::Provenance, M::AllocExtra>>> {
+        let (alloc, def_id) = match self.tcx.try_get_global_alloc(id) {
             Some(GlobalAlloc::Memory(mem)) => {
                 // Memory of a constant or promoted or anonymous memory referenced by a static.
                 (mem, None)
             }
             Some(GlobalAlloc::Function(..)) => throw_ub!(DerefFunctionPointer(id)),
+            Some(GlobalAlloc::VTable(..)) => throw_ub!(DerefVTablePointer(id)),
             None => throw_ub!(PointerUseAfterFree(id)),
             Some(GlobalAlloc::Static(def_id)) => {
                 assert!(self.tcx.is_static(def_id));
@@ -490,6 +499,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 // contains a reference to memory that was created during its evaluation (i.e., not
                 // to another static), those inner references only exist in "resolved" form.
                 if self.tcx.is_foreign_item(def_id) {
+                    // This is unreachable in Miri, but can happen in CTFE where we actually *do* support
+                    // referencing arbitrary (declared) extern statics.
                     throw_unsup!(ReadExternStatic(def_id));
                 }
 
@@ -499,7 +510,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         };
         M::before_access_global(*self.tcx, &self.machine, id, alloc, def_id, is_write)?;
         // We got tcx memory. Let the machine initialize its "extra" stuff.
-        M::init_allocation_extra(
+        M::adjust_allocation(
             self,
             id, // always use the ID we got as input, not the "hidden" one.
             Cow::Borrowed(alloc.inner()),
@@ -512,11 +523,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     fn get_alloc_raw(
         &self,
         id: AllocId,
-    ) -> InterpResult<'tcx, &Allocation<M::PointerTag, M::AllocExtra>> {
+    ) -> InterpResult<'tcx, &Allocation<M::Provenance, M::AllocExtra>> {
         // The error type of the inner closure here is somewhat funny.  We have two
         // ways of "erroring": An actual error, or because we got a reference from
         // `get_global_alloc` that we can actually use directly without inserting anything anywhere.
-        // So the error type is `InterpResult<'tcx, &Allocation<M::PointerTag>>`.
+        // So the error type is `InterpResult<'tcx, &Allocation<M::Provenance>>`.
         let a = self.memory.alloc_map.get_or(id, || {
             let alloc = self.get_global_alloc(id, /*is_write*/ false).map_err(Err)?;
             match alloc {
@@ -545,24 +556,24 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     /// "Safe" (bounds and align-checked) allocation access.
     pub fn get_ptr_alloc<'a>(
         &'a self,
-        ptr: Pointer<Option<M::PointerTag>>,
+        ptr: Pointer<Option<M::Provenance>>,
         size: Size,
         align: Align,
-    ) -> InterpResult<'tcx, Option<AllocRef<'a, 'tcx, M::PointerTag, M::AllocExtra>>> {
+    ) -> InterpResult<'tcx, Option<AllocRef<'a, 'tcx, M::Provenance, M::AllocExtra>>> {
         let align = M::enforce_alignment(self).then_some(align);
         let ptr_and_alloc = self.check_and_deref_ptr(
             ptr,
             size,
             align,
             CheckInAllocMsg::MemoryAccessTest,
-            |alloc_id, offset, tag| {
+            |alloc_id, offset, prov| {
                 let alloc = self.get_alloc_raw(alloc_id)?;
-                Ok((alloc.size(), alloc.align, (alloc_id, offset, tag, alloc)))
+                Ok((alloc.size(), alloc.align, (alloc_id, offset, prov, alloc)))
             },
         )?;
-        if let Some((alloc_id, offset, tag, alloc)) = ptr_and_alloc {
+        if let Some((alloc_id, offset, prov, alloc)) = ptr_and_alloc {
             let range = alloc_range(offset, size);
-            M::memory_read(*self.tcx, &self.machine, &alloc.extra, (alloc_id, tag), range)?;
+            M::memory_read(*self.tcx, &self.machine, &alloc.extra, (alloc_id, prov), range)?;
             Ok(Some(AllocRef { alloc, range, tcx: *self.tcx, alloc_id }))
         } else {
             // Even in this branch we have to be sure that we actually access the allocation, in
@@ -586,7 +597,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     fn get_alloc_raw_mut(
         &mut self,
         id: AllocId,
-    ) -> InterpResult<'tcx, (&mut Allocation<M::PointerTag, M::AllocExtra>, &mut M)> {
+    ) -> InterpResult<'tcx, (&mut Allocation<M::Provenance, M::AllocExtra>, &mut M)> {
         // We have "NLL problem case #3" here, which cannot be worked around without loss of
         // efficiency even for the common case where the key is in the map.
         // <https://rust-lang.github.io/rfcs/2094-nll.html#problem-case-3-conditional-control-flow-across-functions>
@@ -612,18 +623,18 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     /// "Safe" (bounds and align-checked) allocation access.
     pub fn get_ptr_alloc_mut<'a>(
         &'a mut self,
-        ptr: Pointer<Option<M::PointerTag>>,
+        ptr: Pointer<Option<M::Provenance>>,
         size: Size,
         align: Align,
-    ) -> InterpResult<'tcx, Option<AllocRefMut<'a, 'tcx, M::PointerTag, M::AllocExtra>>> {
+    ) -> InterpResult<'tcx, Option<AllocRefMut<'a, 'tcx, M::Provenance, M::AllocExtra>>> {
         let parts = self.get_ptr_access(ptr, size, align)?;
-        if let Some((alloc_id, offset, tag)) = parts {
+        if let Some((alloc_id, offset, prov)) = parts {
             let tcx = *self.tcx;
             // FIXME: can we somehow avoid looking up the allocation twice here?
             // We cannot call `get_raw_mut` inside `check_and_deref_ptr` as that would duplicate `&mut self`.
             let (alloc, machine) = self.get_alloc_raw_mut(alloc_id)?;
             let range = alloc_range(offset, size);
-            M::memory_written(tcx, machine, &mut alloc.extra, (alloc_id, tag), range)?;
+            M::memory_written(tcx, machine, &mut alloc.extra, (alloc_id, prov), range)?;
             Ok(Some(AllocRefMut { alloc, range, tcx, alloc_id }))
         } else {
             Ok(None)
@@ -659,12 +670,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         // # Statics
         // Can't do this in the match argument, we may get cycle errors since the lock would
         // be held throughout the match.
-        match self.tcx.get_global_alloc(id) {
-            Some(GlobalAlloc::Static(did)) => {
-                assert!(!self.tcx.is_thread_local_static(did));
+        match self.tcx.try_get_global_alloc(id) {
+            Some(GlobalAlloc::Static(def_id)) => {
+                assert!(self.tcx.is_static(def_id));
+                assert!(!self.tcx.is_thread_local_static(def_id));
                 // Use size and align of the type.
-                let ty = self.tcx.type_of(did);
+                let ty = self.tcx.type_of(def_id);
                 let layout = self.tcx.layout_of(ParamEnv::empty().and(ty)).unwrap();
+                assert!(!layout.is_unsized());
                 (layout.size, layout.align.abi, AllocKind::LiveData)
             }
             Some(GlobalAlloc::Memory(alloc)) => {
@@ -674,6 +687,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 (alloc.size(), alloc.align, AllocKind::LiveData)
             }
             Some(GlobalAlloc::Function(_)) => bug!("We already checked function pointers above"),
+            Some(GlobalAlloc::VTable(..)) => {
+                // No data to be accessed here. But vtables are pointer-aligned.
+                return (Size::ZERO, self.tcx.data_layout.pointer_align.abi, AllocKind::VTable);
+            }
             // The rest must be dead.
             None => {
                 // Deallocated pointers are allowed, we should be able to find
@@ -701,7 +718,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         if let Some(extra) = self.memory.extra_fn_ptr_map.get(&id) {
             Some(FnVal::Other(*extra))
         } else {
-            match self.tcx.get_global_alloc(id) {
+            match self.tcx.try_get_global_alloc(id) {
                 Some(GlobalAlloc::Function(instance)) => Some(FnVal::Instance(instance)),
                 _ => None,
             }
@@ -710,10 +727,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
 
     pub fn get_ptr_fn(
         &self,
-        ptr: Pointer<Option<M::PointerTag>>,
+        ptr: Pointer<Option<M::Provenance>>,
     ) -> InterpResult<'tcx, FnVal<'tcx, M::ExtraFnVal>> {
-        trace!("get_fn({:?})", ptr);
-        let (alloc_id, offset, _tag) = self.ptr_get_alloc_id(ptr)?;
+        trace!("get_ptr_fn({:?})", ptr);
+        let (alloc_id, offset, _prov) = self.ptr_get_alloc_id(ptr)?;
         if offset.bytes() != 0 {
             throw_ub!(InvalidFunctionPointer(Pointer::new(alloc_id, offset)))
         }
@@ -721,6 +738,21 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             .ok_or_else(|| err_ub!(InvalidFunctionPointer(Pointer::new(alloc_id, offset))).into())
     }
 
+    pub fn get_ptr_vtable(
+        &self,
+        ptr: Pointer<Option<M::Provenance>>,
+    ) -> InterpResult<'tcx, (Ty<'tcx>, Option<ty::PolyExistentialTraitRef<'tcx>>)> {
+        trace!("get_ptr_vtable({:?})", ptr);
+        let (alloc_id, offset, _tag) = self.ptr_get_alloc_id(ptr)?;
+        if offset.bytes() != 0 {
+            throw_ub!(InvalidVTablePointer(Pointer::new(alloc_id, offset)))
+        }
+        match self.tcx.try_get_global_alloc(alloc_id) {
+            Some(GlobalAlloc::VTable(ty, trait_ref)) => Ok((ty, trait_ref)),
+            _ => throw_ub!(InvalidVTablePointer(Pointer::new(alloc_id, offset))),
+        }
+    }
+
     pub fn alloc_mark_immutable(&mut self, id: AllocId) -> InterpResult<'tcx> {
         self.get_alloc_raw_mut(id)?.0.mutability = Mutability::Not;
         Ok(())
@@ -759,7 +791,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                     // This is a new allocation, add its relocations to `todo`.
                     if let Some((_, alloc)) = self.memory.alloc_map.get(id) {
                         todo.extend(
-                            alloc.relocations().values().filter_map(|tag| tag.get_alloc_id()),
+                            alloc.relocations().values().filter_map(|prov| prov.get_alloc_id()),
                         );
                     }
                 }
@@ -788,14 +820,14 @@ pub struct DumpAllocs<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> {
 
 impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> std::fmt::Debug for DumpAllocs<'a, 'mir, 'tcx, M> {
     fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        // Cannot be a closure because it is generic in `Tag`, `Extra`.
-        fn write_allocation_track_relocs<'tcx, Tag: Provenance, Extra>(
+        // Cannot be a closure because it is generic in `Prov`, `Extra`.
+        fn write_allocation_track_relocs<'tcx, Prov: Provenance, Extra>(
             fmt: &mut std::fmt::Formatter<'_>,
             tcx: TyCtxt<'tcx>,
             allocs_to_print: &mut VecDeque<AllocId>,
-            alloc: &Allocation<Tag, Extra>,
+            alloc: &Allocation<Prov, Extra>,
         ) -> std::fmt::Result {
-            for alloc_id in alloc.relocations().values().filter_map(|tag| tag.get_alloc_id()) {
+            for alloc_id in alloc.relocations().values().filter_map(|prov| prov.get_alloc_id()) {
                 allocs_to_print.push_back(alloc_id);
             }
             write!(fmt, "{}", display_allocation(tcx, alloc))
@@ -825,7 +857,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> std::fmt::Debug for DumpAllocs<'a,
                 }
                 None => {
                     // global alloc
-                    match self.ecx.tcx.get_global_alloc(id) {
+                    match self.ecx.tcx.try_get_global_alloc(id) {
                         Some(GlobalAlloc::Memory(alloc)) => {
                             write!(fmt, " (unchanged global, ")?;
                             write_allocation_track_relocs(
@@ -836,7 +868,13 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> std::fmt::Debug for DumpAllocs<'a,
                             )?;
                         }
                         Some(GlobalAlloc::Function(func)) => {
-                            write!(fmt, " (fn: {})", func)?;
+                            write!(fmt, " (fn: {func})")?;
+                        }
+                        Some(GlobalAlloc::VTable(ty, Some(trait_ref))) => {
+                            write!(fmt, " (vtable: impl {trait_ref} for {ty})")?;
+                        }
+                        Some(GlobalAlloc::VTable(ty, None)) => {
+                            write!(fmt, " (vtable: impl <auto trait> for {ty})")?;
                         }
                         Some(GlobalAlloc::Static(did)) => {
                             write!(fmt, " (static: {})", self.ecx.tcx.def_path_str(did))?;
@@ -854,12 +892,12 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> std::fmt::Debug for DumpAllocs<'a,
 }
 
 /// Reading and writing.
-impl<'tcx, 'a, Tag: Provenance, Extra> AllocRefMut<'a, 'tcx, Tag, Extra> {
+impl<'tcx, 'a, Prov: Provenance, Extra> AllocRefMut<'a, 'tcx, Prov, Extra> {
     /// `range` is relative to this allocation reference, not the base of the allocation.
     pub fn write_scalar(
         &mut self,
         range: AllocRange,
-        val: ScalarMaybeUninit<Tag>,
+        val: ScalarMaybeUninit<Prov>,
     ) -> InterpResult<'tcx> {
         let range = self.range.subrange(range);
         debug!("write_scalar at {:?}{range:?}: {val:?}", self.alloc_id);
@@ -873,7 +911,7 @@ impl<'tcx, 'a, Tag: Provenance, Extra> AllocRefMut<'a, 'tcx, Tag, Extra> {
     pub fn write_ptr_sized(
         &mut self,
         offset: Size,
-        val: ScalarMaybeUninit<Tag>,
+        val: ScalarMaybeUninit<Prov>,
     ) -> InterpResult<'tcx> {
         self.write_scalar(alloc_range(offset, self.tcx.data_layout().pointer_size), val)
     }
@@ -887,13 +925,13 @@ impl<'tcx, 'a, Tag: Provenance, Extra> AllocRefMut<'a, 'tcx, Tag, Extra> {
     }
 }
 
-impl<'tcx, 'a, Tag: Provenance, Extra> AllocRef<'a, 'tcx, Tag, Extra> {
+impl<'tcx, 'a, Prov: Provenance, Extra> AllocRef<'a, 'tcx, Prov, Extra> {
     /// `range` is relative to this allocation reference, not the base of the allocation.
     pub fn read_scalar(
         &self,
         range: AllocRange,
         read_provenance: bool,
-    ) -> InterpResult<'tcx, ScalarMaybeUninit<Tag>> {
+    ) -> InterpResult<'tcx, ScalarMaybeUninit<Prov>> {
         let range = self.range.subrange(range);
         let res = self
             .alloc
@@ -904,12 +942,12 @@ impl<'tcx, 'a, Tag: Provenance, Extra> AllocRef<'a, 'tcx, Tag, Extra> {
     }
 
     /// `range` is relative to this allocation reference, not the base of the allocation.
-    pub fn read_integer(&self, range: AllocRange) -> InterpResult<'tcx, ScalarMaybeUninit<Tag>> {
+    pub fn read_integer(&self, range: AllocRange) -> InterpResult<'tcx, ScalarMaybeUninit<Prov>> {
         self.read_scalar(range, /*read_provenance*/ false)
     }
 
     /// `offset` is relative to this allocation reference, not the base of the allocation.
-    pub fn read_pointer(&self, offset: Size) -> InterpResult<'tcx, ScalarMaybeUninit<Tag>> {
+    pub fn read_pointer(&self, offset: Size) -> InterpResult<'tcx, ScalarMaybeUninit<Prov>> {
         self.read_scalar(
             alloc_range(offset, self.tcx.data_layout().pointer_size),
             /*read_provenance*/ true,
@@ -941,7 +979,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     /// Performs appropriate bounds checks.
     pub fn read_bytes_ptr(
         &self,
-        ptr: Pointer<Option<M::PointerTag>>,
+        ptr: Pointer<Option<M::Provenance>>,
         size: Size,
     ) -> InterpResult<'tcx, &[u8]> {
         let Some(alloc_ref) = self.get_ptr_alloc(ptr, size, Align::ONE)? else {
@@ -961,7 +999,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     /// Performs appropriate bounds checks.
     pub fn write_bytes_ptr(
         &mut self,
-        ptr: Pointer<Option<M::PointerTag>>,
+        ptr: Pointer<Option<M::Provenance>>,
         src: impl IntoIterator<Item = u8>,
     ) -> InterpResult<'tcx> {
         let mut src = src.into_iter();
@@ -998,9 +1036,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
 
     pub fn mem_copy(
         &mut self,
-        src: Pointer<Option<M::PointerTag>>,
+        src: Pointer<Option<M::Provenance>>,
         src_align: Align,
-        dest: Pointer<Option<M::PointerTag>>,
+        dest: Pointer<Option<M::Provenance>>,
         dest_align: Align,
         size: Size,
         nonoverlapping: bool,
@@ -1010,9 +1048,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
 
     pub fn mem_copy_repeatedly(
         &mut self,
-        src: Pointer<Option<M::PointerTag>>,
+        src: Pointer<Option<M::Provenance>>,
         src_align: Align,
-        dest: Pointer<Option<M::PointerTag>>,
+        dest: Pointer<Option<M::Provenance>>,
         dest_align: Align,
         size: Size,
         num_copies: u64,
@@ -1027,16 +1065,16 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         // and once below to get the underlying `&[mut] Allocation`.
 
         // Source alloc preparations and access hooks.
-        let Some((src_alloc_id, src_offset, src_tag)) = src_parts else {
+        let Some((src_alloc_id, src_offset, src_prov)) = src_parts else {
             // Zero-sized *source*, that means dst is also zero-sized and we have nothing to do.
             return Ok(());
         };
         let src_alloc = self.get_alloc_raw(src_alloc_id)?;
         let src_range = alloc_range(src_offset, size);
-        M::memory_read(*tcx, &self.machine, &src_alloc.extra, (src_alloc_id, src_tag), src_range)?;
+        M::memory_read(*tcx, &self.machine, &src_alloc.extra, (src_alloc_id, src_prov), src_range)?;
         // We need the `dest` ptr for the next operation, so we get it now.
         // We already did the source checks and called the hooks so we are good to return early.
-        let Some((dest_alloc_id, dest_offset, dest_tag)) = dest_parts else {
+        let Some((dest_alloc_id, dest_offset, dest_prov)) = dest_parts else {
             // Zero-sized *destination*.
             return Ok(());
         };
@@ -1062,7 +1100,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             *tcx,
             extra,
             &mut dest_alloc.extra,
-            (dest_alloc_id, dest_tag),
+            (dest_alloc_id, dest_prov),
             dest_range,
         )?;
         let dest_bytes = dest_alloc
@@ -1133,34 +1171,14 @@ 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> {
-    pub fn scalar_to_ptr(
-        &self,
-        scalar: Scalar<M::PointerTag>,
-    ) -> InterpResult<'tcx, Pointer<Option<M::PointerTag>>> {
-        // We use `to_bits_or_ptr_internal` since we are just implementing the method people need to
-        // call to force getting out a pointer.
-        Ok(
-            match scalar
-                .to_bits_or_ptr_internal(self.pointer_size())
-                .map_err(|s| err_ub!(ScalarSizeMismatch(s)))?
-            {
-                Err(ptr) => ptr.into(),
-                Ok(bits) => {
-                    let addr = u64::try_from(bits).unwrap();
-                    M::ptr_from_addr_transmute(&self, addr)
-                }
-            },
-        )
-    }
-
     /// Test if this value might be null.
     /// If the machine does not support ptr-to-int casts, this is conservative.
-    pub fn scalar_may_be_null(&self, scalar: Scalar<M::PointerTag>) -> InterpResult<'tcx, bool> {
+    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 = self.scalar_to_ptr(scalar)?;
+                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);
@@ -1178,13 +1196,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     /// about where it points), or an absolute address.
     pub fn ptr_try_get_alloc_id(
         &self,
-        ptr: Pointer<Option<M::PointerTag>>,
-    ) -> Result<(AllocId, Size, M::TagExtra), u64> {
+        ptr: Pointer<Option<M::Provenance>>,
+    ) -> Result<(AllocId, Size, M::ProvenanceExtra), u64> {
         match ptr.into_pointer_or_addr() {
             Ok(ptr) => match M::ptr_get_alloc(self, ptr) {
                 Some((alloc_id, offset, extra)) => Ok((alloc_id, offset, extra)),
                 None => {
-                    assert!(M::PointerTag::OFFSET_IS_ADDR);
+                    assert!(M::Provenance::OFFSET_IS_ADDR);
                     let (_, addr) = ptr.into_parts();
                     Err(addr.bytes())
                 }
@@ -1197,8 +1215,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     #[inline(always)]
     pub fn ptr_get_alloc_id(
         &self,
-        ptr: Pointer<Option<M::PointerTag>>,
-    ) -> InterpResult<'tcx, (AllocId, Size, M::TagExtra)> {
+        ptr: Pointer<Option<M::Provenance>>,
+    ) -> InterpResult<'tcx, (AllocId, Size, M::ProvenanceExtra)> {
         self.ptr_try_get_alloc_id(ptr).map_err(|offset| {
             err_ub!(DanglingIntPointer(offset, CheckInAllocMsg::InboundsTest)).into()
         })
diff --git a/compiler/rustc_const_eval/src/interpret/mod.rs b/compiler/rustc_const_eval/src/interpret/mod.rs
index 92f0a7498e3..2e356f67bf3 100644
--- a/compiler/rustc_const_eval/src/interpret/mod.rs
+++ b/compiler/rustc_const_eval/src/interpret/mod.rs
@@ -9,6 +9,7 @@ mod memory;
 mod operand;
 mod operator;
 mod place;
+mod projection;
 mod step;
 mod terminator;
 mod traits;
diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs
index d11ae7b4925..de284bd3bae 100644
--- a/compiler/rustc_const_eval/src/interpret/operand.rs
+++ b/compiler/rustc_const_eval/src/interpret/operand.rs
@@ -1,11 +1,9 @@
 //! Functions concerning immediate values and operands, and reading from operands.
 //! All high-level functions to read from memory work on operands as sources.
 
-use std::convert::TryFrom;
 use std::fmt::Write;
 
 use rustc_hir::def::Namespace;
-use rustc_macros::HashStable;
 use rustc_middle::ty::layout::{LayoutOf, PrimitiveExt, TyAndLayout};
 use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter, Printer};
 use rustc_middle::ty::{ConstInt, DelaySpanBugEmitted, Ty};
@@ -15,8 +13,8 @@ use rustc_target::abi::{VariantIdx, Variants};
 
 use super::{
     alloc_range, from_known_layout, mir_assign_valid_types, AllocId, ConstValue, Frame, GlobalId,
-    InterpCx, InterpResult, MPlaceTy, Machine, MemPlace, Place, PlaceTy, Pointer,
-    PointerArithmetic, Provenance, Scalar, ScalarMaybeUninit,
+    InterpCx, InterpResult, MPlaceTy, Machine, MemPlace, MemPlaceMeta, Place, PlaceTy, Pointer,
+    Provenance, Scalar, ScalarMaybeUninit,
 };
 
 /// An `Immediate` represents a single immediate self-contained Rust value.
@@ -26,15 +24,15 @@ use super::{
 /// operations and wide pointers. This idea was taken from rustc's codegen.
 /// In particular, thanks to `ScalarPair`, arithmetic operations and casts can be entirely
 /// defined on `Immediate`, and do not have to work with a `Place`.
-#[derive(Copy, Clone, PartialEq, Eq, HashStable, Hash, Debug)]
-pub enum Immediate<Tag: Provenance = AllocId> {
+#[derive(Copy, Clone, Debug)]
+pub enum Immediate<Prov: Provenance = AllocId> {
     /// A single scalar value (must have *initialized* `Scalar` ABI).
     /// FIXME: we also currently often use this for ZST.
     /// `ScalarMaybeUninit` should reject ZST, and we should use `Uninit` for them instead.
-    Scalar(ScalarMaybeUninit<Tag>),
+    Scalar(ScalarMaybeUninit<Prov>),
     /// A pair of two scalar value (must have `ScalarPair` ABI where both fields are
     /// `Scalar::Initialized`).
-    ScalarPair(ScalarMaybeUninit<Tag>, ScalarMaybeUninit<Tag>),
+    ScalarPair(ScalarMaybeUninit<Prov>, ScalarMaybeUninit<Prov>),
     /// A value of fully uninitialized memory. Can have and size and layout.
     Uninit,
 }
@@ -42,36 +40,36 @@ pub enum Immediate<Tag: Provenance = AllocId> {
 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
 rustc_data_structures::static_assert_size!(Immediate, 56);
 
-impl<Tag: Provenance> From<ScalarMaybeUninit<Tag>> for Immediate<Tag> {
+impl<Prov: Provenance> From<ScalarMaybeUninit<Prov>> for Immediate<Prov> {
     #[inline(always)]
-    fn from(val: ScalarMaybeUninit<Tag>) -> Self {
+    fn from(val: ScalarMaybeUninit<Prov>) -> Self {
         Immediate::Scalar(val)
     }
 }
 
-impl<Tag: Provenance> From<Scalar<Tag>> for Immediate<Tag> {
+impl<Prov: Provenance> From<Scalar<Prov>> for Immediate<Prov> {
     #[inline(always)]
-    fn from(val: Scalar<Tag>) -> Self {
+    fn from(val: Scalar<Prov>) -> Self {
         Immediate::Scalar(val.into())
     }
 }
 
-impl<'tcx, Tag: Provenance> Immediate<Tag> {
-    pub fn from_pointer(p: Pointer<Tag>, cx: &impl HasDataLayout) -> Self {
+impl<'tcx, Prov: Provenance> Immediate<Prov> {
+    pub fn from_pointer(p: Pointer<Prov>, cx: &impl HasDataLayout) -> Self {
         Immediate::Scalar(ScalarMaybeUninit::from_pointer(p, cx))
     }
 
-    pub fn from_maybe_pointer(p: Pointer<Option<Tag>>, cx: &impl HasDataLayout) -> Self {
+    pub fn from_maybe_pointer(p: Pointer<Option<Prov>>, cx: &impl HasDataLayout) -> Self {
         Immediate::Scalar(ScalarMaybeUninit::from_maybe_pointer(p, cx))
     }
 
-    pub fn new_slice(val: Scalar<Tag>, len: u64, cx: &impl HasDataLayout) -> Self {
+    pub fn new_slice(val: Scalar<Prov>, len: u64, cx: &impl HasDataLayout) -> Self {
         Immediate::ScalarPair(val.into(), Scalar::from_machine_usize(len, cx).into())
     }
 
     pub fn new_dyn_trait(
-        val: Scalar<Tag>,
-        vtable: Pointer<Option<Tag>>,
+        val: Scalar<Prov>,
+        vtable: Pointer<Option<Prov>>,
         cx: &impl HasDataLayout,
     ) -> Self {
         Immediate::ScalarPair(val.into(), ScalarMaybeUninit::from_maybe_pointer(vtable, cx))
@@ -79,7 +77,7 @@ impl<'tcx, Tag: Provenance> Immediate<Tag> {
 
     #[inline]
     #[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980)
-    pub fn to_scalar_or_uninit(self) -> ScalarMaybeUninit<Tag> {
+    pub fn to_scalar_or_uninit(self) -> ScalarMaybeUninit<Prov> {
         match self {
             Immediate::Scalar(val) => val,
             Immediate::ScalarPair(..) => bug!("Got a scalar pair where a scalar was expected"),
@@ -89,13 +87,13 @@ impl<'tcx, Tag: Provenance> Immediate<Tag> {
 
     #[inline]
     #[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980)
-    pub fn to_scalar(self) -> InterpResult<'tcx, Scalar<Tag>> {
+    pub fn to_scalar(self) -> InterpResult<'tcx, Scalar<Prov>> {
         self.to_scalar_or_uninit().check_init()
     }
 
     #[inline]
     #[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980)
-    pub fn to_scalar_or_uninit_pair(self) -> (ScalarMaybeUninit<Tag>, ScalarMaybeUninit<Tag>) {
+    pub fn to_scalar_or_uninit_pair(self) -> (ScalarMaybeUninit<Prov>, ScalarMaybeUninit<Prov>) {
         match self {
             Immediate::ScalarPair(val1, val2) => (val1, val2),
             Immediate::Scalar(..) => bug!("Got a scalar where a scalar pair was expected"),
@@ -105,7 +103,7 @@ impl<'tcx, Tag: Provenance> Immediate<Tag> {
 
     #[inline]
     #[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980)
-    pub fn to_scalar_pair(self) -> InterpResult<'tcx, (Scalar<Tag>, Scalar<Tag>)> {
+    pub fn to_scalar_pair(self) -> InterpResult<'tcx, (Scalar<Prov>, Scalar<Prov>)> {
         let (val1, val2) = self.to_scalar_or_uninit_pair();
         Ok((val1.check_init()?, val2.check_init()?))
     }
@@ -113,21 +111,21 @@ impl<'tcx, Tag: Provenance> Immediate<Tag> {
 
 // ScalarPair needs a type to interpret, so we often have an immediate and a type together
 // as input for binary and cast operations.
-#[derive(Copy, Clone, Debug)]
-pub struct ImmTy<'tcx, Tag: Provenance = AllocId> {
-    imm: Immediate<Tag>,
+#[derive(Clone, Debug)]
+pub struct ImmTy<'tcx, Prov: Provenance = AllocId> {
+    imm: Immediate<Prov>,
     pub layout: TyAndLayout<'tcx>,
 }
 
 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
 rustc_data_structures::static_assert_size!(ImmTy<'_>, 72);
 
-impl<Tag: Provenance> std::fmt::Display for ImmTy<'_, Tag> {
+impl<Prov: Provenance> std::fmt::Display for ImmTy<'_, Prov> {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         /// Helper function for printing a scalar to a FmtPrinter
-        fn p<'a, 'tcx, Tag: Provenance>(
+        fn p<'a, 'tcx, Prov: Provenance>(
             cx: FmtPrinter<'a, 'tcx>,
-            s: ScalarMaybeUninit<Tag>,
+            s: ScalarMaybeUninit<Prov>,
             ty: Ty<'tcx>,
         ) -> Result<FmtPrinter<'a, 'tcx>, std::fmt::Error> {
             match s {
@@ -172,10 +170,10 @@ impl<Tag: Provenance> std::fmt::Display for ImmTy<'_, Tag> {
     }
 }
 
-impl<'tcx, Tag: Provenance> std::ops::Deref for ImmTy<'tcx, Tag> {
-    type Target = Immediate<Tag>;
+impl<'tcx, Prov: Provenance> std::ops::Deref for ImmTy<'tcx, Prov> {
+    type Target = Immediate<Prov>;
     #[inline(always)]
-    fn deref(&self) -> &Immediate<Tag> {
+    fn deref(&self) -> &Immediate<Prov> {
         &self.imm
     }
 }
@@ -183,15 +181,18 @@ impl<'tcx, Tag: Provenance> std::ops::Deref for ImmTy<'tcx, Tag> {
 /// An `Operand` is the result of computing a `mir::Operand`. It can be immediate,
 /// or still in memory. The latter is an optimization, to delay reading that chunk of
 /// memory and to avoid having to store arbitrary-sized data here.
-#[derive(Copy, Clone, PartialEq, Eq, HashStable, Hash, Debug)]
-pub enum Operand<Tag: Provenance = AllocId> {
-    Immediate(Immediate<Tag>),
-    Indirect(MemPlace<Tag>),
+#[derive(Copy, Clone, Debug)]
+pub enum Operand<Prov: Provenance = AllocId> {
+    Immediate(Immediate<Prov>),
+    Indirect(MemPlace<Prov>),
 }
 
-#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
-pub struct OpTy<'tcx, Tag: Provenance = AllocId> {
-    op: Operand<Tag>, // Keep this private; it helps enforce invariants.
+#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+rustc_data_structures::static_assert_size!(Operand, 64);
+
+#[derive(Clone, Debug)]
+pub struct OpTy<'tcx, Prov: Provenance = AllocId> {
+    op: Operand<Prov>, // Keep this private; it helps enforce invariants.
     pub layout: TyAndLayout<'tcx>,
     /// rustc does not have a proper way to represent the type of a field of a `repr(packed)` struct:
     /// it needs to have a different alignment than the field type would usually have.
@@ -206,54 +207,59 @@ pub struct OpTy<'tcx, Tag: Provenance = AllocId> {
 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
 rustc_data_structures::static_assert_size!(OpTy<'_>, 88);
 
-impl<'tcx, Tag: Provenance> std::ops::Deref for OpTy<'tcx, Tag> {
-    type Target = Operand<Tag>;
+impl<'tcx, Prov: Provenance> std::ops::Deref for OpTy<'tcx, Prov> {
+    type Target = Operand<Prov>;
     #[inline(always)]
-    fn deref(&self) -> &Operand<Tag> {
+    fn deref(&self) -> &Operand<Prov> {
         &self.op
     }
 }
 
-impl<'tcx, Tag: Provenance> From<MPlaceTy<'tcx, Tag>> for OpTy<'tcx, Tag> {
+impl<'tcx, Prov: Provenance> From<MPlaceTy<'tcx, Prov>> for OpTy<'tcx, Prov> {
     #[inline(always)]
-    fn from(mplace: MPlaceTy<'tcx, Tag>) -> Self {
+    fn from(mplace: MPlaceTy<'tcx, Prov>) -> Self {
         OpTy { op: Operand::Indirect(*mplace), layout: mplace.layout, align: Some(mplace.align) }
     }
 }
 
-impl<'tcx, Tag: Provenance> From<&'_ MPlaceTy<'tcx, Tag>> for OpTy<'tcx, Tag> {
+impl<'tcx, Prov: Provenance> From<&'_ MPlaceTy<'tcx, Prov>> for OpTy<'tcx, Prov> {
     #[inline(always)]
-    fn from(mplace: &MPlaceTy<'tcx, Tag>) -> Self {
+    fn from(mplace: &MPlaceTy<'tcx, Prov>) -> Self {
         OpTy { op: Operand::Indirect(**mplace), layout: mplace.layout, align: Some(mplace.align) }
     }
 }
 
-impl<'tcx, Tag: Provenance> From<&'_ mut MPlaceTy<'tcx, Tag>> for OpTy<'tcx, Tag> {
+impl<'tcx, Prov: Provenance> From<&'_ mut MPlaceTy<'tcx, Prov>> for OpTy<'tcx, Prov> {
     #[inline(always)]
-    fn from(mplace: &mut MPlaceTy<'tcx, Tag>) -> Self {
+    fn from(mplace: &mut MPlaceTy<'tcx, Prov>) -> Self {
         OpTy { op: Operand::Indirect(**mplace), layout: mplace.layout, align: Some(mplace.align) }
     }
 }
 
-impl<'tcx, Tag: Provenance> From<ImmTy<'tcx, Tag>> for OpTy<'tcx, Tag> {
+impl<'tcx, Prov: Provenance> From<ImmTy<'tcx, Prov>> for OpTy<'tcx, Prov> {
     #[inline(always)]
-    fn from(val: ImmTy<'tcx, Tag>) -> Self {
+    fn from(val: ImmTy<'tcx, Prov>) -> Self {
         OpTy { op: Operand::Immediate(val.imm), layout: val.layout, align: None }
     }
 }
 
-impl<'tcx, Tag: Provenance> ImmTy<'tcx, Tag> {
+impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> {
     #[inline]
-    pub fn from_scalar(val: Scalar<Tag>, layout: TyAndLayout<'tcx>) -> Self {
+    pub fn from_scalar(val: Scalar<Prov>, layout: TyAndLayout<'tcx>) -> Self {
         ImmTy { imm: val.into(), layout }
     }
 
     #[inline]
-    pub fn from_immediate(imm: Immediate<Tag>, layout: TyAndLayout<'tcx>) -> Self {
+    pub fn from_immediate(imm: Immediate<Prov>, layout: TyAndLayout<'tcx>) -> Self {
         ImmTy { imm, layout }
     }
 
     #[inline]
+    pub fn uninit(layout: TyAndLayout<'tcx>) -> Self {
+        ImmTy { imm: Immediate::Uninit, layout }
+    }
+
+    #[inline]
     pub fn try_from_uint(i: impl Into<u128>, layout: TyAndLayout<'tcx>) -> Option<Self> {
         Some(Self::from_scalar(Scalar::try_from_uint(i, layout.size)?, layout))
     }
@@ -280,6 +286,51 @@ impl<'tcx, Tag: Provenance> ImmTy<'tcx, Tag> {
     }
 }
 
+impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> {
+    pub fn len(&self, cx: &impl HasDataLayout) -> InterpResult<'tcx, u64> {
+        if self.layout.is_unsized() {
+            // There are no unsized immediates.
+            self.assert_mem_place().len(cx)
+        } else {
+            match self.layout.fields {
+                abi::FieldsShape::Array { count, .. } => Ok(count),
+                _ => bug!("len not supported on sized type {:?}", self.layout.ty),
+            }
+        }
+    }
+
+    pub fn offset_with_meta(
+        &self,
+        offset: Size,
+        meta: MemPlaceMeta<Prov>,
+        layout: TyAndLayout<'tcx>,
+        cx: &impl HasDataLayout,
+    ) -> InterpResult<'tcx, Self> {
+        match self.try_as_mplace() {
+            Ok(mplace) => Ok(mplace.offset_with_meta(offset, meta, layout, cx)?.into()),
+            Err(imm) => {
+                assert!(
+                    matches!(*imm, Immediate::Uninit),
+                    "Scalar/ScalarPair cannot be offset into"
+                );
+                assert!(!meta.has_meta()); // no place to store metadata here
+                // Every part of an uninit is uninit.
+                Ok(ImmTy::uninit(layout).into())
+            }
+        }
+    }
+
+    pub fn offset(
+        &self,
+        offset: Size,
+        layout: TyAndLayout<'tcx>,
+        cx: &impl HasDataLayout,
+    ) -> InterpResult<'tcx, Self> {
+        assert!(!layout.is_unsized());
+        self.offset_with_meta(offset, MemPlaceMeta::None, layout, cx)
+    }
+}
+
 impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     /// Try reading an immediate in memory; this is interesting particularly for `ScalarPair`.
     /// Returns `None` if the layout does not permit loading this as a value.
@@ -287,20 +338,17 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     /// This is an internal function; call `read_immediate` instead.
     fn read_immediate_from_mplace_raw(
         &self,
-        mplace: &MPlaceTy<'tcx, M::PointerTag>,
+        mplace: &MPlaceTy<'tcx, M::Provenance>,
         force: bool,
-    ) -> InterpResult<'tcx, Option<ImmTy<'tcx, M::PointerTag>>> {
+    ) -> InterpResult<'tcx, Option<ImmTy<'tcx, M::Provenance>>> {
         if mplace.layout.is_unsized() {
             // Don't touch unsized
             return Ok(None);
         }
 
         let Some(alloc) = self.get_place_alloc(mplace)? else {
-            return Ok(Some(ImmTy {
-                // zero-sized type can be left uninit
-                imm: Immediate::Uninit,
-                layout: mplace.layout,
-            }));
+            // zero-sized type can be left uninit
+            return Ok(Some(ImmTy::uninit(mplace.layout)));
         };
 
         // It may seem like all types with `Scalar` or `ScalarPair` ABI are fair game at this point.
@@ -315,17 +363,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             Abi::Scalar(s) if force => Some(s.primitive()),
             _ => None,
         };
-        let read_provenance = |s: abi::Primitive, size| {
-            // Should be just `s.is_ptr()`, but we support a Miri flag that accepts more
-            // questionable ptr-int transmutes.
-            let number_may_have_provenance = !M::enforce_number_no_provenance(self);
-            s.is_ptr() || (number_may_have_provenance && size == self.pointer_size())
-        };
         if let Some(s) = scalar_layout {
             let size = s.size(self);
             assert_eq!(size, mplace.layout.size, "abi::Scalar size does not match layout size");
-            let scalar =
-                alloc.read_scalar(alloc_range(Size::ZERO, size), read_provenance(s, size))?;
+            let scalar = alloc
+                .read_scalar(alloc_range(Size::ZERO, size), /*read_provenance*/ s.is_ptr())?;
             return Ok(Some(ImmTy { imm: scalar.into(), layout: mplace.layout }));
         }
         let scalar_pair_layout = match mplace.layout.abi {
@@ -343,10 +385,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             let (a_size, b_size) = (a.size(self), b.size(self));
             let b_offset = a_size.align_to(b.align(self).abi);
             assert!(b_offset.bytes() > 0); // in `operand_field` we use the offset to tell apart the fields
-            let a_val =
-                alloc.read_scalar(alloc_range(Size::ZERO, a_size), read_provenance(a, a_size))?;
-            let b_val =
-                alloc.read_scalar(alloc_range(b_offset, b_size), read_provenance(b, b_size))?;
+            let a_val = alloc.read_scalar(
+                alloc_range(Size::ZERO, a_size),
+                /*read_provenance*/ a.is_ptr(),
+            )?;
+            let b_val = alloc
+                .read_scalar(alloc_range(b_offset, b_size), /*read_provenance*/ b.is_ptr())?;
             return Ok(Some(ImmTy {
                 imm: Immediate::ScalarPair(a_val, b_val),
                 layout: mplace.layout,
@@ -367,11 +411,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     /// This flag exists only for validity checking.
     ///
     /// This is an internal function that should not usually be used; call `read_immediate` instead.
+    /// ConstProp needs it, though.
     pub fn read_immediate_raw(
         &self,
-        src: &OpTy<'tcx, M::PointerTag>,
+        src: &OpTy<'tcx, M::Provenance>,
         force: bool,
-    ) -> InterpResult<'tcx, Result<ImmTy<'tcx, M::PointerTag>, MPlaceTy<'tcx, M::PointerTag>>> {
+    ) -> InterpResult<'tcx, Result<ImmTy<'tcx, M::Provenance>, MPlaceTy<'tcx, M::Provenance>>> {
         Ok(match src.try_as_mplace() {
             Ok(ref mplace) => {
                 if let Some(val) = self.read_immediate_from_mplace_raw(mplace, force)? {
@@ -388,8 +433,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     #[inline(always)]
     pub fn read_immediate(
         &self,
-        op: &OpTy<'tcx, M::PointerTag>,
-    ) -> InterpResult<'tcx, ImmTy<'tcx, M::PointerTag>> {
+        op: &OpTy<'tcx, M::Provenance>,
+    ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> {
         if let Ok(imm) = self.read_immediate_raw(op, /*force*/ false)? {
             Ok(imm)
         } else {
@@ -400,144 +445,49 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     /// Read a scalar from a place
     pub fn read_scalar(
         &self,
-        op: &OpTy<'tcx, M::PointerTag>,
-    ) -> InterpResult<'tcx, ScalarMaybeUninit<M::PointerTag>> {
+        op: &OpTy<'tcx, M::Provenance>,
+    ) -> InterpResult<'tcx, ScalarMaybeUninit<M::Provenance>> {
         Ok(self.read_immediate(op)?.to_scalar_or_uninit())
     }
 
     /// Read a pointer from a place.
     pub fn read_pointer(
         &self,
-        op: &OpTy<'tcx, M::PointerTag>,
-    ) -> InterpResult<'tcx, Pointer<Option<M::PointerTag>>> {
-        self.scalar_to_ptr(self.read_scalar(op)?.check_init()?)
+        op: &OpTy<'tcx, M::Provenance>,
+    ) -> InterpResult<'tcx, Pointer<Option<M::Provenance>>> {
+        self.read_scalar(op)?.to_pointer(self)
     }
 
     /// Turn the wide MPlace into a string (must already be dereferenced!)
-    pub fn read_str(&self, mplace: &MPlaceTy<'tcx, M::PointerTag>) -> InterpResult<'tcx, &str> {
+    pub fn read_str(&self, mplace: &MPlaceTy<'tcx, M::Provenance>) -> InterpResult<'tcx, &str> {
         let len = mplace.len(self)?;
         let bytes = self.read_bytes_ptr(mplace.ptr, Size::from_bytes(len))?;
         let str = std::str::from_utf8(bytes).map_err(|err| err_ub!(InvalidStr(err)))?;
         Ok(str)
     }
 
-    /// Projection functions
-    pub fn operand_field(
-        &self,
-        op: &OpTy<'tcx, M::PointerTag>,
-        field: usize,
-    ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
-        let base = match op.try_as_mplace() {
-            Ok(ref mplace) => {
-                // We can reuse the mplace field computation logic for indirect operands.
-                let field = self.mplace_field(mplace, field)?;
-                return Ok(field.into());
-            }
-            Err(value) => value,
-        };
-
-        let field_layout = base.layout.field(self, field);
-        let offset = base.layout.fields.offset(field);
-        // This makes several assumptions about what layouts we will encounter; we match what
-        // codegen does as good as we can (see `extract_field` in `rustc_codegen_ssa/src/mir/operand.rs`).
-        let field_val: Immediate<_> = match (*base, base.layout.abi) {
-            // the field contains no information, can be left uninit
-            _ if field_layout.is_zst() => Immediate::Uninit,
-            // the field covers the entire type
-            _ if field_layout.size == base.layout.size => {
-                assert!(match (base.layout.abi, field_layout.abi) {
-                    (Abi::Scalar(..), Abi::Scalar(..)) => true,
-                    (Abi::ScalarPair(..), Abi::ScalarPair(..)) => true,
-                    _ => false,
-                });
-                assert!(offset.bytes() == 0);
-                *base
-            }
-            // extract fields from types with `ScalarPair` ABI
-            (Immediate::ScalarPair(a_val, b_val), Abi::ScalarPair(a, b)) => {
-                assert!(matches!(field_layout.abi, Abi::Scalar(..)));
-                Immediate::from(if offset.bytes() == 0 {
-                    debug_assert_eq!(field_layout.size, a.size(self));
-                    a_val
-                } else {
-                    debug_assert_eq!(offset, a.size(self).align_to(b.align(self).abi));
-                    debug_assert_eq!(field_layout.size, b.size(self));
-                    b_val
-                })
-            }
-            _ => span_bug!(
-                self.cur_span(),
-                "invalid field access on immediate {}, layout {:#?}",
-                base,
-                base.layout
-            ),
-        };
-
-        Ok(OpTy { op: Operand::Immediate(field_val), layout: field_layout, align: None })
-    }
-
-    pub fn operand_index(
-        &self,
-        op: &OpTy<'tcx, M::PointerTag>,
-        index: u64,
-    ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
-        if let Ok(index) = usize::try_from(index) {
-            // We can just treat this as a field.
-            self.operand_field(op, index)
-        } else {
-            // Indexing into a big array. This must be an mplace.
-            let mplace = op.assert_mem_place();
-            Ok(self.mplace_index(&mplace, index)?.into())
-        }
-    }
-
-    pub fn operand_downcast(
-        &self,
-        op: &OpTy<'tcx, M::PointerTag>,
-        variant: VariantIdx,
-    ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
-        Ok(match op.try_as_mplace() {
-            Ok(ref mplace) => self.mplace_downcast(mplace, variant)?.into(),
-            Err(..) => {
-                // Downcasts only change the layout.
-                // (In particular, no check about whether this is even the active variant -- that's by design,
-                // see https://github.com/rust-lang/rust/issues/93688#issuecomment-1032929496.)
-                let layout = op.layout.for_variant(self, variant);
-                OpTy { layout, ..*op }
-            }
-        })
-    }
-
-    #[instrument(skip(self), level = "debug")]
-    pub fn operand_projection(
-        &self,
-        base: &OpTy<'tcx, M::PointerTag>,
-        proj_elem: mir::PlaceElem<'tcx>,
-    ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
-        use rustc_middle::mir::ProjectionElem::*;
-        Ok(match proj_elem {
-            Field(field, _) => self.operand_field(base, field.index())?,
-            Downcast(_, variant) => self.operand_downcast(base, variant)?,
-            Deref => self.deref_operand(base)?.into(),
-            Subslice { .. } | ConstantIndex { .. } | Index(_) => {
-                // The rest should only occur as mplace, we do not use Immediates for types
-                // allowing such operations.  This matches place_projection forcing an allocation.
-                let mplace = base.assert_mem_place();
-                self.mplace_projection(&mplace, proj_elem)?.into()
-            }
-        })
-    }
-
     /// Converts a repr(simd) operand into an operand where `place_index` accesses the SIMD elements.
     /// Also returns the number of elements.
+    ///
+    /// Can (but does not always) trigger UB if `op` is uninitialized.
     pub fn operand_to_simd(
         &self,
-        base: &OpTy<'tcx, M::PointerTag>,
-    ) -> InterpResult<'tcx, (MPlaceTy<'tcx, M::PointerTag>, u64)> {
+        op: &OpTy<'tcx, M::Provenance>,
+    ) -> InterpResult<'tcx, (MPlaceTy<'tcx, M::Provenance>, u64)> {
         // Basically we just transmute this place into an array following simd_size_and_type.
         // This only works in memory, but repr(simd) types should never be immediates anyway.
-        assert!(base.layout.ty.is_simd());
-        self.mplace_to_simd(&base.assert_mem_place())
+        assert!(op.layout.ty.is_simd());
+        match op.try_as_mplace() {
+            Ok(mplace) => self.mplace_to_simd(&mplace),
+            Err(imm) => match *imm {
+                Immediate::Uninit => {
+                    throw_ub!(InvalidUninitBytes(None))
+                }
+                Immediate::Scalar(..) | Immediate::ScalarPair(..) => {
+                    bug!("arrays/slices can never have Scalar/ScalarPair layout")
+                }
+            },
+        }
     }
 
     /// Read from a local. Will not actually access the local if reading from a ZST.
@@ -547,10 +497,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     /// OpTy from a local.
     pub fn local_to_op(
         &self,
-        frame: &Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra>,
+        frame: &Frame<'mir, 'tcx, M::Provenance, M::FrameExtra>,
         local: mir::Local,
         layout: Option<TyAndLayout<'tcx>>,
-    ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
+    ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
         let layout = self.layout_of_local(frame, local, layout)?;
         let op = if layout.is_zst() {
             // Bypass `access_local` (helps in ConstProp)
@@ -567,8 +517,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     #[inline(always)]
     pub fn place_to_op(
         &self,
-        place: &PlaceTy<'tcx, M::PointerTag>,
-    ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
+        place: &PlaceTy<'tcx, M::Provenance>,
+    ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
         let op = match **place {
             Place::Ptr(mplace) => Operand::Indirect(mplace),
             Place::Local { frame, local } => {
@@ -582,30 +532,34 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     /// avoid allocations.
     pub fn eval_place_to_op(
         &self,
-        place: mir::Place<'tcx>,
+        mir_place: mir::Place<'tcx>,
         layout: Option<TyAndLayout<'tcx>>,
-    ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
+    ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
         // Do not use the layout passed in as argument if the base we are looking at
         // here is not the entire place.
-        let layout = if place.projection.is_empty() { layout } else { None };
-
-        let base_op = self.local_to_op(self.frame(), place.local, layout)?;
+        let layout = if mir_place.projection.is_empty() { layout } else { None };
 
-        let op = place
-            .projection
-            .iter()
-            .try_fold(base_op, |op, elem| self.operand_projection(&op, elem))?;
+        let mut op = self.local_to_op(self.frame(), mir_place.local, layout)?;
+        // Using `try_fold` turned out to be bad for performance, hence the loop.
+        for elem in mir_place.projection.iter() {
+            op = self.operand_projection(&op, elem)?
+        }
 
         trace!("eval_place_to_op: got {:?}", *op);
         // Sanity-check the type we ended up with.
-        debug_assert!(mir_assign_valid_types(
-            *self.tcx,
-            self.param_env,
-            self.layout_of(self.subst_from_current_frame_and_normalize_erasing_regions(
-                place.ty(&self.frame().body.local_decls, *self.tcx).ty
-            )?)?,
-            op.layout,
-        ));
+        debug_assert!(
+            mir_assign_valid_types(
+                *self.tcx,
+                self.param_env,
+                self.layout_of(self.subst_from_current_frame_and_normalize_erasing_regions(
+                    mir_place.ty(&self.frame().body.local_decls, *self.tcx).ty
+                )?)?,
+                op.layout,
+            ),
+            "eval_place of a MIR place with type {:?} produced an interpreter operand with type {:?}",
+            mir_place.ty(&self.frame().body.local_decls, *self.tcx).ty,
+            op.layout.ty,
+        );
         Ok(op)
     }
 
@@ -617,7 +571,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         &self,
         mir_op: &mir::Operand<'tcx>,
         layout: Option<TyAndLayout<'tcx>>,
-    ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
+    ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
         use rustc_middle::mir::Operand::*;
         let op = match *mir_op {
             // FIXME: do some more logic on `move` to invalidate the old location
@@ -642,7 +596,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     pub(super) fn eval_operands(
         &self,
         ops: &[mir::Operand<'tcx>],
-    ) -> InterpResult<'tcx, Vec<OpTy<'tcx, M::PointerTag>>> {
+    ) -> InterpResult<'tcx, Vec<OpTy<'tcx, M::Provenance>>> {
         ops.iter().map(|op| self.eval_operand(op, None)).collect()
     }
 
@@ -654,7 +608,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         &self,
         c: ty::Const<'tcx>,
         layout: Option<TyAndLayout<'tcx>>,
-    ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
+    ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
         match c.kind() {
             ty::ConstKind::Param(_) | ty::ConstKind::Bound(..) => throw_inval!(TooGeneric),
             ty::ConstKind::Error(DelaySpanBugEmitted { reported, .. }) => {
@@ -679,7 +633,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         &self,
         val: &mir::ConstantKind<'tcx>,
         layout: Option<TyAndLayout<'tcx>>,
-    ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
+    ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
         match val {
             mir::ConstantKind::Ty(ct) => self.const_to_op(*ct, layout),
             mir::ConstantKind::Val(val, ty) => self.const_val_to_op(*val, *ty, layout),
@@ -691,9 +645,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         val_val: ConstValue<'tcx>,
         ty: Ty<'tcx>,
         layout: Option<TyAndLayout<'tcx>>,
-    ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
+    ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
         // Other cases need layout.
-        let tag_scalar = |scalar| -> InterpResult<'tcx, _> {
+        let adjust_scalar = |scalar| -> InterpResult<'tcx, _> {
             Ok(match scalar {
                 Scalar::Ptr(ptr, size) => Scalar::Ptr(self.global_base_pointer(ptr)?, size),
                 Scalar::Int(int) => Scalar::Int(int),
@@ -708,7 +662,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 let ptr = self.global_base_pointer(Pointer::new(id, offset))?;
                 Operand::Indirect(MemPlace::from_ptr(ptr.into()))
             }
-            ConstValue::Scalar(x) => Operand::Immediate(tag_scalar(x)?.into()),
+            ConstValue::Scalar(x) => Operand::Immediate(adjust_scalar(x)?.into()),
             ConstValue::ZeroSized => Operand::Immediate(Immediate::Uninit),
             ConstValue::Slice { data, start, end } => {
                 // We rely on mutability being set correctly in `data` to prevent writes
@@ -731,8 +685,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     /// Can also legally be called on non-enums (e.g. through the discriminant_value intrinsic)!
     pub fn read_discriminant(
         &self,
-        op: &OpTy<'tcx, M::PointerTag>,
-    ) -> InterpResult<'tcx, (Scalar<M::PointerTag>, VariantIdx)> {
+        op: &OpTy<'tcx, M::Provenance>,
+    ) -> InterpResult<'tcx, (Scalar<M::Provenance>, VariantIdx)> {
         trace!("read_discriminant_value {:#?}", op.layout);
         // Get type and layout of the discriminant.
         let discr_layout = self.layout_of(op.layout.ty.discriminant_ty(*self.tcx))?;
diff --git a/compiler/rustc_const_eval/src/interpret/operator.rs b/compiler/rustc_const_eval/src/interpret/operator.rs
index f0c113376ea..f9912d706fb 100644
--- a/compiler/rustc_const_eval/src/interpret/operator.rs
+++ b/compiler/rustc_const_eval/src/interpret/operator.rs
@@ -19,9 +19,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         &mut self,
         op: mir::BinOp,
         force_overflow_checks: bool,
-        left: &ImmTy<'tcx, M::PointerTag>,
-        right: &ImmTy<'tcx, M::PointerTag>,
-        dest: &PlaceTy<'tcx, M::PointerTag>,
+        left: &ImmTy<'tcx, M::Provenance>,
+        right: &ImmTy<'tcx, M::Provenance>,
+        dest: &PlaceTy<'tcx, M::Provenance>,
     ) -> InterpResult<'tcx> {
         let (val, overflowed, ty) = self.overflowing_binary_op(op, &left, &right)?;
         debug_assert_eq!(
@@ -41,7 +41,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             let pair = Immediate::ScalarPair(val.into(), Scalar::from_bool(overflowed).into());
             self.write_immediate(pair, dest)?;
         } else {
-            assert!(self.tcx.sess.opts.debugging_opts.randomize_layout);
+            assert!(self.tcx.sess.opts.unstable_opts.randomize_layout);
             // With randomized layout, `(int, bool)` might cease to be a `ScalarPair`, so we have to
             // do a component-wise write here. This code path is slower than the above because
             // `place_field` will have to `force_allocate` locals here.
@@ -58,9 +58,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     pub fn binop_ignore_overflow(
         &mut self,
         op: mir::BinOp,
-        left: &ImmTy<'tcx, M::PointerTag>,
-        right: &ImmTy<'tcx, M::PointerTag>,
-        dest: &PlaceTy<'tcx, M::PointerTag>,
+        left: &ImmTy<'tcx, M::Provenance>,
+        right: &ImmTy<'tcx, M::Provenance>,
+        dest: &PlaceTy<'tcx, M::Provenance>,
     ) -> InterpResult<'tcx> {
         let (val, _overflowed, ty) = self.overflowing_binary_op(op, left, right)?;
         assert_eq!(ty, dest.layout.ty, "type mismatch for result of {:?}", op);
@@ -74,7 +74,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         bin_op: mir::BinOp,
         l: char,
         r: char,
-    ) -> (Scalar<M::PointerTag>, bool, Ty<'tcx>) {
+    ) -> (Scalar<M::Provenance>, bool, Ty<'tcx>) {
         use rustc_middle::mir::BinOp::*;
 
         let res = match bin_op {
@@ -94,7 +94,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         bin_op: mir::BinOp,
         l: bool,
         r: bool,
-    ) -> (Scalar<M::PointerTag>, bool, Ty<'tcx>) {
+    ) -> (Scalar<M::Provenance>, bool, Ty<'tcx>) {
         use rustc_middle::mir::BinOp::*;
 
         let res = match bin_op {
@@ -112,13 +112,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         (Scalar::from_bool(res), false, self.tcx.types.bool)
     }
 
-    fn binary_float_op<F: Float + Into<Scalar<M::PointerTag>>>(
+    fn binary_float_op<F: Float + Into<Scalar<M::Provenance>>>(
         &self,
         bin_op: mir::BinOp,
         ty: Ty<'tcx>,
         l: F,
         r: F,
-    ) -> (Scalar<M::PointerTag>, bool, Ty<'tcx>) {
+    ) -> (Scalar<M::Provenance>, bool, Ty<'tcx>) {
         use rustc_middle::mir::BinOp::*;
 
         let (val, ty) = match bin_op {
@@ -146,7 +146,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         left_layout: TyAndLayout<'tcx>,
         r: u128,
         right_layout: TyAndLayout<'tcx>,
-    ) -> InterpResult<'tcx, (Scalar<M::PointerTag>, bool, Ty<'tcx>)> {
+    ) -> InterpResult<'tcx, (Scalar<M::Provenance>, bool, Ty<'tcx>)> {
         use rustc_middle::mir::BinOp::*;
 
         // Shift ops can have an RHS with a different numeric type.
@@ -314,9 +314,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     pub fn overflowing_binary_op(
         &self,
         bin_op: mir::BinOp,
-        left: &ImmTy<'tcx, M::PointerTag>,
-        right: &ImmTy<'tcx, M::PointerTag>,
-    ) -> InterpResult<'tcx, (Scalar<M::PointerTag>, bool, Ty<'tcx>)> {
+        left: &ImmTy<'tcx, M::Provenance>,
+        right: &ImmTy<'tcx, M::Provenance>,
+    ) -> InterpResult<'tcx, (Scalar<M::Provenance>, bool, Ty<'tcx>)> {
         trace!(
             "Running binary op {:?}: {:?} ({:?}), {:?} ({:?})",
             bin_op,
@@ -393,9 +393,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     pub fn binary_op(
         &self,
         bin_op: mir::BinOp,
-        left: &ImmTy<'tcx, M::PointerTag>,
-        right: &ImmTy<'tcx, M::PointerTag>,
-    ) -> InterpResult<'tcx, ImmTy<'tcx, M::PointerTag>> {
+        left: &ImmTy<'tcx, M::Provenance>,
+        right: &ImmTy<'tcx, M::Provenance>,
+    ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> {
         let (val, _overflow, ty) = self.overflowing_binary_op(bin_op, left, right)?;
         Ok(ImmTy::from_scalar(val, self.layout_of(ty)?))
     }
@@ -405,8 +405,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     pub fn overflowing_unary_op(
         &self,
         un_op: mir::UnOp,
-        val: &ImmTy<'tcx, M::PointerTag>,
-    ) -> InterpResult<'tcx, (Scalar<M::PointerTag>, bool, Ty<'tcx>)> {
+        val: &ImmTy<'tcx, M::Provenance>,
+    ) -> InterpResult<'tcx, (Scalar<M::Provenance>, bool, Ty<'tcx>)> {
         use rustc_middle::mir::UnOp::*;
 
         let layout = val.layout;
@@ -455,8 +455,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     pub fn unary_op(
         &self,
         un_op: mir::UnOp,
-        val: &ImmTy<'tcx, M::PointerTag>,
-    ) -> InterpResult<'tcx, ImmTy<'tcx, M::PointerTag>> {
+        val: &ImmTy<'tcx, M::Provenance>,
+    ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> {
         let (val, _overflow, ty) = self.overflowing_unary_op(un_op, val)?;
         Ok(ImmTy::from_scalar(val, self.layout_of(ty)?))
     }
diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs
index 57ecad07b42..473da71a0ab 100644
--- a/compiler/rustc_const_eval/src/interpret/place.rs
+++ b/compiler/rustc_const_eval/src/interpret/place.rs
@@ -2,17 +2,13 @@
 //! into a place.
 //! All high-level functions to write to memory work on places as destinations.
 
-use std::convert::TryFrom;
 use std::hash::Hash;
 
 use rustc_ast::Mutability;
-use rustc_macros::HashStable;
 use rustc_middle::mir;
+use rustc_middle::ty;
 use rustc_middle::ty::layout::{LayoutOf, PrimitiveExt, TyAndLayout};
-use rustc_middle::ty::{self, Ty};
-use rustc_target::abi::{
-    Abi, Align, FieldsShape, HasDataLayout, Size, TagEncoding, VariantIdx, Variants,
-};
+use rustc_target::abi::{self, Abi, Align, HasDataLayout, Size, TagEncoding, VariantIdx};
 
 use super::{
     alloc_range, mir_assign_valid_types, AllocId, AllocRef, AllocRefMut, CheckInAllocMsg,
@@ -20,57 +16,68 @@ use super::{
     Pointer, Provenance, Scalar, ScalarMaybeUninit,
 };
 
-#[derive(Copy, Clone, Hash, PartialEq, Eq, HashStable, Debug)]
+#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)]
 /// Information required for the sound usage of a `MemPlace`.
-pub enum MemPlaceMeta<Tag: Provenance = AllocId> {
+pub enum MemPlaceMeta<Prov: Provenance = AllocId> {
     /// The unsized payload (e.g. length for slices or vtable pointer for trait objects).
-    Meta(Scalar<Tag>),
+    Meta(Scalar<Prov>),
     /// `Sized` types or unsized `extern type`
     None,
-    /// The address of this place may not be taken. This protects the `MemPlace` from coming from
-    /// a ZST Operand without a backing allocation and being converted to an integer address. This
-    /// should be impossible, because you can't take the address of an operand, but this is a second
-    /// protection layer ensuring that we don't mess up.
-    Poison,
 }
 
 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
 rustc_data_structures::static_assert_size!(MemPlaceMeta, 24);
 
-impl<Tag: Provenance> MemPlaceMeta<Tag> {
-    pub fn unwrap_meta(self) -> Scalar<Tag> {
+impl<Prov: Provenance> MemPlaceMeta<Prov> {
+    pub fn unwrap_meta(self) -> Scalar<Prov> {
         match self {
             Self::Meta(s) => s,
-            Self::None | Self::Poison => {
+            Self::None => {
                 bug!("expected wide pointer extra data (e.g. slice length or trait object vtable)")
             }
         }
     }
-    fn has_meta(self) -> bool {
+
+    pub fn has_meta(self) -> bool {
         match self {
             Self::Meta(_) => true,
-            Self::None | Self::Poison => false,
+            Self::None => false,
         }
     }
 }
 
-#[derive(Copy, Clone, Hash, PartialEq, Eq, HashStable, Debug)]
-pub struct MemPlace<Tag: Provenance = AllocId> {
-    /// The pointer can be a pure integer, with the `None` tag.
-    pub ptr: Pointer<Option<Tag>>,
+#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)]
+pub struct MemPlace<Prov: Provenance = AllocId> {
+    /// The pointer can be a pure integer, with the `None` provenance.
+    pub ptr: Pointer<Option<Prov>>,
     /// Metadata for unsized places. Interpretation is up to the type.
     /// Must not be present for sized types, but can be missing for unsized types
     /// (e.g., `extern type`).
-    pub meta: MemPlaceMeta<Tag>,
+    pub meta: MemPlaceMeta<Prov>,
 }
 
 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
 rustc_data_structures::static_assert_size!(MemPlace, 40);
 
-#[derive(Copy, Clone, Hash, PartialEq, Eq, HashStable, Debug)]
-pub enum Place<Tag: Provenance = AllocId> {
+/// A MemPlace with its layout. Constructing it is only possible in this module.
+#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)]
+pub struct MPlaceTy<'tcx, Prov: Provenance = AllocId> {
+    mplace: MemPlace<Prov>,
+    pub layout: TyAndLayout<'tcx>,
+    /// rustc does not have a proper way to represent the type of a field of a `repr(packed)` struct:
+    /// it needs to have a different alignment than the field type would usually have.
+    /// So we represent this here with a separate field that "overwrites" `layout.align`.
+    /// This means `layout.align` should never be used for a `MPlaceTy`!
+    pub align: Align,
+}
+
+#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+rustc_data_structures::static_assert_size!(MPlaceTy<'_>, 64);
+
+#[derive(Copy, Clone, Debug)]
+pub enum Place<Prov: Provenance = AllocId> {
     /// A place referring to a value allocated in the `Memory` system.
-    Ptr(MemPlace<Tag>),
+    Ptr(MemPlace<Prov>),
 
     /// To support alloc-free locals, we are able to write directly to a local.
     /// (Without that optimization, we'd just always be a `MemPlace`.)
@@ -80,9 +87,9 @@ pub enum Place<Tag: Provenance = AllocId> {
 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
 rustc_data_structures::static_assert_size!(Place, 48);
 
-#[derive(Copy, Clone, Debug)]
-pub struct PlaceTy<'tcx, Tag: Provenance = AllocId> {
-    place: Place<Tag>, // Keep this private; it helps enforce invariants.
+#[derive(Clone, Debug)]
+pub struct PlaceTy<'tcx, Prov: Provenance = AllocId> {
+    place: Place<Prov>, // Keep this private; it helps enforce invariants.
     pub layout: TyAndLayout<'tcx>,
     /// rustc does not have a proper way to represent the type of a field of a `repr(packed)` struct:
     /// it needs to have a different alignment than the field type would usually have.
@@ -94,100 +101,82 @@ pub struct PlaceTy<'tcx, Tag: Provenance = AllocId> {
 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
 rustc_data_structures::static_assert_size!(PlaceTy<'_>, 72);
 
-impl<'tcx, Tag: Provenance> std::ops::Deref for PlaceTy<'tcx, Tag> {
-    type Target = Place<Tag>;
+impl<'tcx, Prov: Provenance> std::ops::Deref for PlaceTy<'tcx, Prov> {
+    type Target = Place<Prov>;
     #[inline(always)]
-    fn deref(&self) -> &Place<Tag> {
+    fn deref(&self) -> &Place<Prov> {
         &self.place
     }
 }
 
-/// A MemPlace with its layout. Constructing it is only possible in this module.
-#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)]
-pub struct MPlaceTy<'tcx, Tag: Provenance = AllocId> {
-    mplace: MemPlace<Tag>,
-    pub layout: TyAndLayout<'tcx>,
-    /// rustc does not have a proper way to represent the type of a field of a `repr(packed)` struct:
-    /// it needs to have a different alignment than the field type would usually have.
-    /// So we represent this here with a separate field that "overwrites" `layout.align`.
-    /// This means `layout.align` should never be used for a `MPlaceTy`!
-    pub align: Align,
-}
-
-#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
-rustc_data_structures::static_assert_size!(MPlaceTy<'_>, 64);
-
-impl<'tcx, Tag: Provenance> std::ops::Deref for MPlaceTy<'tcx, Tag> {
-    type Target = MemPlace<Tag>;
+impl<'tcx, Prov: Provenance> std::ops::Deref for MPlaceTy<'tcx, Prov> {
+    type Target = MemPlace<Prov>;
     #[inline(always)]
-    fn deref(&self) -> &MemPlace<Tag> {
+    fn deref(&self) -> &MemPlace<Prov> {
         &self.mplace
     }
 }
 
-impl<'tcx, Tag: Provenance> From<MPlaceTy<'tcx, Tag>> for PlaceTy<'tcx, Tag> {
+impl<'tcx, Prov: Provenance> From<MPlaceTy<'tcx, Prov>> for PlaceTy<'tcx, Prov> {
     #[inline(always)]
-    fn from(mplace: MPlaceTy<'tcx, Tag>) -> Self {
+    fn from(mplace: MPlaceTy<'tcx, Prov>) -> Self {
         PlaceTy { place: Place::Ptr(*mplace), layout: mplace.layout, align: mplace.align }
     }
 }
 
-impl<'tcx, Tag: Provenance> From<&'_ MPlaceTy<'tcx, Tag>> for PlaceTy<'tcx, Tag> {
+impl<'tcx, Prov: Provenance> From<&'_ MPlaceTy<'tcx, Prov>> for PlaceTy<'tcx, Prov> {
     #[inline(always)]
-    fn from(mplace: &MPlaceTy<'tcx, Tag>) -> Self {
+    fn from(mplace: &MPlaceTy<'tcx, Prov>) -> Self {
         PlaceTy { place: Place::Ptr(**mplace), layout: mplace.layout, align: mplace.align }
     }
 }
 
-impl<'tcx, Tag: Provenance> From<&'_ mut MPlaceTy<'tcx, Tag>> for PlaceTy<'tcx, Tag> {
+impl<'tcx, Prov: Provenance> From<&'_ mut MPlaceTy<'tcx, Prov>> for PlaceTy<'tcx, Prov> {
     #[inline(always)]
-    fn from(mplace: &mut MPlaceTy<'tcx, Tag>) -> Self {
+    fn from(mplace: &mut MPlaceTy<'tcx, Prov>) -> Self {
         PlaceTy { place: Place::Ptr(**mplace), layout: mplace.layout, align: mplace.align }
     }
 }
 
-impl<Tag: Provenance> MemPlace<Tag> {
+impl<Prov: Provenance> MemPlace<Prov> {
     #[inline(always)]
-    pub fn from_ptr(ptr: Pointer<Option<Tag>>) -> Self {
+    pub fn from_ptr(ptr: Pointer<Option<Prov>>) -> Self {
         MemPlace { ptr, meta: MemPlaceMeta::None }
     }
 
     /// Adjust the provenance of the main pointer (metadata is unaffected).
-    pub fn map_provenance(self, f: impl FnOnce(Option<Tag>) -> Option<Tag>) -> Self {
+    pub fn map_provenance(self, f: impl FnOnce(Option<Prov>) -> Option<Prov>) -> Self {
         MemPlace { ptr: self.ptr.map_provenance(f), ..self }
     }
 
     /// Turn a mplace into a (thin or wide) pointer, as a reference, pointing to the same space.
     /// This is the inverse of `ref_to_mplace`.
     #[inline(always)]
-    pub fn to_ref(self, cx: &impl HasDataLayout) -> Immediate<Tag> {
+    pub fn to_ref(self, cx: &impl HasDataLayout) -> Immediate<Prov> {
         match self.meta {
             MemPlaceMeta::None => Immediate::from(Scalar::from_maybe_pointer(self.ptr, cx)),
             MemPlaceMeta::Meta(meta) => {
                 Immediate::ScalarPair(Scalar::from_maybe_pointer(self.ptr, cx).into(), meta.into())
             }
-            MemPlaceMeta::Poison => bug!(
-                "MPlaceTy::dangling may never be used to produce a \
-                place that will have the address of its pointee taken"
-            ),
         }
     }
 
     #[inline]
-    pub fn offset<'tcx>(
+    pub fn offset_with_meta<'tcx>(
         self,
         offset: Size,
-        meta: MemPlaceMeta<Tag>,
+        meta: MemPlaceMeta<Prov>,
         cx: &impl HasDataLayout,
     ) -> InterpResult<'tcx, Self> {
         Ok(MemPlace { ptr: self.ptr.offset(offset, cx)?, meta })
     }
 }
 
-impl<Tag: Provenance> Place<Tag> {
+impl<Prov: Provenance> Place<Prov> {
     /// Asserts that this points to some local variable.
     /// Returns the frame idx and the variable idx.
     #[inline]
+    #[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980)
     pub fn assert_local(&self) -> (usize, mir::Local) {
         match self {
             Place::Local { frame, local } => (*frame, *local),
@@ -196,41 +185,53 @@ impl<Tag: Provenance> Place<Tag> {
     }
 }
 
-impl<'tcx, Tag: Provenance> MPlaceTy<'tcx, Tag> {
-    /// Produces a MemPlace that works for ZST but nothing else
+impl<'tcx, Prov: Provenance> MPlaceTy<'tcx, Prov> {
+    /// Produces a MemPlace that works for ZST but nothing else.
+    /// Conceptually this is a new allocation, but it doesn't actually create an allocation so you
+    /// don't need to worry about memory leaks.
     #[inline]
-    pub fn dangling(layout: TyAndLayout<'tcx>) -> Self {
+    pub fn fake_alloc_zst(layout: TyAndLayout<'tcx>) -> Self {
+        assert!(layout.is_zst());
         let align = layout.align.abi;
         let ptr = Pointer::from_addr(align.bytes()); // no provenance, absolute address
-        // `Poison` this to make sure that the pointer value `ptr` is never observable by the program.
-        MPlaceTy { mplace: MemPlace { ptr, meta: MemPlaceMeta::Poison }, layout, align }
+        MPlaceTy { mplace: MemPlace { ptr, meta: MemPlaceMeta::None }, layout, align }
     }
 
     #[inline]
-    pub fn offset(
+    pub fn offset_with_meta(
         &self,
         offset: Size,
-        meta: MemPlaceMeta<Tag>,
+        meta: MemPlaceMeta<Prov>,
         layout: TyAndLayout<'tcx>,
         cx: &impl HasDataLayout,
     ) -> InterpResult<'tcx, Self> {
         Ok(MPlaceTy {
-            mplace: self.mplace.offset(offset, meta, cx)?,
+            mplace: self.mplace.offset_with_meta(offset, meta, cx)?,
             align: self.align.restrict_for_offset(offset),
             layout,
         })
     }
 
+    pub fn offset(
+        &self,
+        offset: Size,
+        layout: TyAndLayout<'tcx>,
+        cx: &impl HasDataLayout,
+    ) -> InterpResult<'tcx, Self> {
+        assert!(!layout.is_unsized());
+        self.offset_with_meta(offset, MemPlaceMeta::None, layout, cx)
+    }
+
     #[inline]
-    pub fn from_aligned_ptr(ptr: Pointer<Option<Tag>>, layout: TyAndLayout<'tcx>) -> Self {
+    pub fn from_aligned_ptr(ptr: Pointer<Option<Prov>>, layout: TyAndLayout<'tcx>) -> Self {
         MPlaceTy { mplace: MemPlace::from_ptr(ptr), layout, align: layout.align.abi }
     }
 
     #[inline]
     pub fn from_aligned_ptr_with_meta(
-        ptr: Pointer<Option<Tag>>,
+        ptr: Pointer<Option<Prov>>,
         layout: TyAndLayout<'tcx>,
-        meta: MemPlaceMeta<Tag>,
+        meta: MemPlaceMeta<Prov>,
     ) -> Self {
         let mut mplace = MemPlace::from_ptr(ptr);
         mplace.meta = meta;
@@ -250,14 +251,14 @@ impl<'tcx, Tag: Provenance> MPlaceTy<'tcx, Tag> {
             // Go through the layout.  There are lots of types that support a length,
             // e.g., SIMD types. (But not all repr(simd) types even have FieldsShape::Array!)
             match self.layout.fields {
-                FieldsShape::Array { count, .. } => Ok(count),
+                abi::FieldsShape::Array { count, .. } => Ok(count),
                 _ => bug!("len not supported on sized type {:?}", self.layout.ty),
             }
         }
     }
 
     #[inline]
-    pub(super) fn vtable(&self) -> Scalar<Tag> {
+    pub(super) fn vtable(&self) -> Scalar<Prov> {
         match self.layout.ty.kind() {
             ty::Dynamic(..) => self.mplace.meta.unwrap_meta(),
             _ => bug!("vtable not supported on type {:?}", self.layout.ty),
@@ -266,50 +267,50 @@ impl<'tcx, Tag: Provenance> MPlaceTy<'tcx, Tag> {
 }
 
 // These are defined here because they produce a place.
-impl<'tcx, Tag: Provenance> OpTy<'tcx, Tag> {
+impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> {
     #[inline(always)]
     /// Note: do not call `as_ref` on the resulting place. This function should only be used to
     /// read from the resulting mplace, not to get its address back.
-    pub fn try_as_mplace(&self) -> Result<MPlaceTy<'tcx, Tag>, ImmTy<'tcx, Tag>> {
+    pub fn try_as_mplace(&self) -> Result<MPlaceTy<'tcx, Prov>, ImmTy<'tcx, Prov>> {
         match **self {
             Operand::Indirect(mplace) => {
                 Ok(MPlaceTy { mplace, layout: self.layout, align: self.align.unwrap() })
             }
-            Operand::Immediate(_) if self.layout.is_zst() => Ok(MPlaceTy::dangling(self.layout)),
             Operand::Immediate(imm) => Err(ImmTy::from_immediate(imm, self.layout)),
         }
     }
 
     #[inline(always)]
+    #[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980)
     /// Note: do not call `as_ref` on the resulting place. This function should only be used to
     /// read from the resulting mplace, not to get its address back.
-    pub fn assert_mem_place(&self) -> MPlaceTy<'tcx, Tag> {
+    pub fn assert_mem_place(&self) -> MPlaceTy<'tcx, Prov> {
         self.try_as_mplace().unwrap()
     }
 }
 
-impl<'tcx, Tag: Provenance> PlaceTy<'tcx, Tag> {
+impl<'tcx, Prov: Provenance> PlaceTy<'tcx, Prov> {
     /// A place is either an mplace or some local.
     #[inline]
-    pub fn try_as_mplace(&self) -> Result<MPlaceTy<'tcx, Tag>, (usize, mir::Local)> {
+    pub fn try_as_mplace(&self) -> Result<MPlaceTy<'tcx, Prov>, (usize, mir::Local)> {
         match **self {
             Place::Ptr(mplace) => Ok(MPlaceTy { mplace, layout: self.layout, align: self.align }),
             Place::Local { frame, local } => Err((frame, local)),
         }
     }
 
-    #[inline]
-    pub fn assert_mem_place(&self) -> MPlaceTy<'tcx, Tag> {
+    #[inline(always)]
+    #[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980)
+    pub fn assert_mem_place(self) -> MPlaceTy<'tcx, Prov> {
         self.try_as_mplace().unwrap()
     }
 }
 
-// separating the pointer tag for `impl Trait`, see https://github.com/rust-lang/rust/issues/54385
-impl<'mir, 'tcx: 'mir, Tag, M> InterpCx<'mir, 'tcx, M>
+// FIXME: Working around https://github.com/rust-lang/rust/issues/54385
+impl<'mir, 'tcx: 'mir, Prov, M> InterpCx<'mir, 'tcx, M>
 where
-    // FIXME: Working around https://github.com/rust-lang/rust/issues/54385
-    Tag: Provenance + Eq + Hash + 'static,
-    M: Machine<'mir, 'tcx, PointerTag = Tag>,
+    Prov: Provenance + Eq + Hash + 'static,
+    M: Machine<'mir, 'tcx, Provenance = Prov>,
 {
     /// Take a value, which represents a (thin or wide) reference, and make it a place.
     /// Alignment is just based on the type.  This is the inverse of `MemPlace::to_ref()`.
@@ -319,8 +320,8 @@ where
     /// Generally prefer `deref_operand`.
     pub fn ref_to_mplace(
         &self,
-        val: &ImmTy<'tcx, M::PointerTag>,
-    ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
+        val: &ImmTy<'tcx, M::Provenance>,
+    ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
         let pointee_type =
             val.layout.ty.builtin_deref(true).expect("`ref_to_mplace` called on non-ptr type").ty;
         let layout = self.layout_of(pointee_type)?;
@@ -330,7 +331,7 @@ where
             Immediate::Uninit => throw_ub!(InvalidUninitBytes(None)),
         };
 
-        let mplace = MemPlace { ptr: self.scalar_to_ptr(ptr.check_init()?)?, meta };
+        let mplace = MemPlace { ptr: ptr.to_pointer(self)?, meta };
         // When deref'ing a pointer, the *static* alignment given by the type is what matters.
         let align = layout.align.abi;
         Ok(MPlaceTy { mplace, layout, align })
@@ -341,8 +342,8 @@ where
     #[instrument(skip(self), level = "debug")]
     pub fn deref_operand(
         &self,
-        src: &OpTy<'tcx, M::PointerTag>,
-    ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
+        src: &OpTy<'tcx, M::Provenance>,
+    ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
         let val = self.read_immediate(src)?;
         trace!("deref to {} on {:?}", val.layout.ty, *val);
 
@@ -358,8 +359,8 @@ where
     #[inline]
     pub(super) fn get_place_alloc(
         &self,
-        place: &MPlaceTy<'tcx, M::PointerTag>,
-    ) -> InterpResult<'tcx, Option<AllocRef<'_, 'tcx, M::PointerTag, M::AllocExtra>>> {
+        place: &MPlaceTy<'tcx, M::Provenance>,
+    ) -> InterpResult<'tcx, Option<AllocRef<'_, 'tcx, M::Provenance, M::AllocExtra>>> {
         assert!(!place.layout.is_unsized());
         assert!(!place.meta.has_meta());
         let size = place.layout.size;
@@ -369,8 +370,8 @@ where
     #[inline]
     pub(super) fn get_place_alloc_mut(
         &mut self,
-        place: &MPlaceTy<'tcx, M::PointerTag>,
-    ) -> InterpResult<'tcx, Option<AllocRefMut<'_, 'tcx, M::PointerTag, M::AllocExtra>>> {
+        place: &MPlaceTy<'tcx, M::Provenance>,
+    ) -> InterpResult<'tcx, Option<AllocRefMut<'_, 'tcx, M::Provenance, M::AllocExtra>>> {
         assert!(!place.layout.is_unsized());
         assert!(!place.meta.has_meta());
         let size = place.layout.size;
@@ -380,7 +381,7 @@ where
     /// Check if this mplace is dereferenceable and sufficiently aligned.
     fn check_mplace_access(
         &self,
-        mplace: MPlaceTy<'tcx, M::PointerTag>,
+        mplace: MPlaceTy<'tcx, M::Provenance>,
         msg: CheckInAllocMsg,
     ) -> InterpResult<'tcx> {
         let (size, align) = self
@@ -392,276 +393,29 @@ where
         Ok(())
     }
 
-    /// Offset a pointer to project to a field of a struct/union. Unlike `place_field`, this is
-    /// always possible without allocating, so it can take `&self`. Also return the field's layout.
-    /// This supports both struct and array fields.
-    ///
-    /// This also works for arrays, but then the `usize` index type is restricting.
-    /// For indexing into arrays, use `mplace_index`.
-    #[inline(always)]
-    pub fn mplace_field(
-        &self,
-        base: &MPlaceTy<'tcx, M::PointerTag>,
-        field: usize,
-    ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
-        let offset = base.layout.fields.offset(field);
-        let field_layout = base.layout.field(self, field);
-
-        // Offset may need adjustment for unsized fields.
-        let (meta, offset) = if field_layout.is_unsized() {
-            // Re-use parent metadata to determine dynamic field layout.
-            // With custom DSTS, this *will* execute user-defined code, but the same
-            // happens at run-time so that's okay.
-            match self.size_and_align_of(&base.meta, &field_layout)? {
-                Some((_, align)) => (base.meta, offset.align_to(align)),
-                None => {
-                    // For unsized types with an extern type tail we perform no adjustments.
-                    // NOTE: keep this in sync with `PlaceRef::project_field` in the codegen backend.
-                    assert!(matches!(base.meta, MemPlaceMeta::None));
-                    (base.meta, offset)
-                }
-            }
-        } else {
-            // base.meta could be present; we might be accessing a sized field of an unsized
-            // struct.
-            (MemPlaceMeta::None, offset)
-        };
-
-        // We do not look at `base.layout.align` nor `field_layout.align`, unlike
-        // codegen -- mostly to see if we can get away with that
-        base.offset(offset, meta, field_layout, self)
-    }
-
-    /// Index into an array.
-    #[inline(always)]
-    pub fn mplace_index(
-        &self,
-        base: &MPlaceTy<'tcx, M::PointerTag>,
-        index: u64,
-    ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
-        // Not using the layout method because we want to compute on u64
-        match base.layout.fields {
-            FieldsShape::Array { stride, .. } => {
-                let len = base.len(self)?;
-                if index >= len {
-                    // This can only be reached in ConstProp and non-rustc-MIR.
-                    throw_ub!(BoundsCheckFailed { len, index });
-                }
-                let offset = stride * index; // `Size` multiplication
-                // All fields have the same layout.
-                let field_layout = base.layout.field(self, 0);
-
-                assert!(!field_layout.is_unsized());
-                base.offset(offset, MemPlaceMeta::None, field_layout, self)
-            }
-            _ => span_bug!(
-                self.cur_span(),
-                "`mplace_index` called on non-array type {:?}",
-                base.layout.ty
-            ),
-        }
-    }
-
-    // Iterates over all fields of an array. Much more efficient than doing the
-    // same by repeatedly calling `mplace_array`.
-    pub(super) fn mplace_array_fields<'a>(
-        &self,
-        base: &'a MPlaceTy<'tcx, Tag>,
-    ) -> InterpResult<'tcx, impl Iterator<Item = InterpResult<'tcx, MPlaceTy<'tcx, Tag>>> + 'a>
-    {
-        let len = base.len(self)?; // also asserts that we have a type where this makes sense
-        let FieldsShape::Array { stride, .. } = base.layout.fields else {
-            span_bug!(self.cur_span(), "mplace_array_fields: expected an array layout");
-        };
-        let layout = base.layout.field(self, 0);
-        let dl = &self.tcx.data_layout;
-        // `Size` multiplication
-        Ok((0..len).map(move |i| base.offset(stride * i, MemPlaceMeta::None, layout, dl)))
-    }
-
-    fn mplace_subslice(
-        &self,
-        base: &MPlaceTy<'tcx, M::PointerTag>,
-        from: u64,
-        to: u64,
-        from_end: bool,
-    ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
-        let len = base.len(self)?; // also asserts that we have a type where this makes sense
-        let actual_to = if from_end {
-            if from.checked_add(to).map_or(true, |to| to > len) {
-                // This can only be reached in ConstProp and non-rustc-MIR.
-                throw_ub!(BoundsCheckFailed { len: len, index: from.saturating_add(to) });
-            }
-            len.checked_sub(to).unwrap()
-        } else {
-            to
-        };
-
-        // Not using layout method because that works with usize, and does not work with slices
-        // (that have count 0 in their layout).
-        let from_offset = match base.layout.fields {
-            FieldsShape::Array { stride, .. } => stride * from, // `Size` multiplication is checked
-            _ => {
-                span_bug!(self.cur_span(), "unexpected layout of index access: {:#?}", base.layout)
-            }
-        };
-
-        // Compute meta and new layout
-        let inner_len = actual_to.checked_sub(from).unwrap();
-        let (meta, ty) = match base.layout.ty.kind() {
-            // It is not nice to match on the type, but that seems to be the only way to
-            // implement this.
-            ty::Array(inner, _) => (MemPlaceMeta::None, self.tcx.mk_array(*inner, inner_len)),
-            ty::Slice(..) => {
-                let len = Scalar::from_machine_usize(inner_len, self);
-                (MemPlaceMeta::Meta(len), base.layout.ty)
-            }
-            _ => {
-                span_bug!(self.cur_span(), "cannot subslice non-array type: `{:?}`", base.layout.ty)
-            }
-        };
-        let layout = self.layout_of(ty)?;
-        base.offset(from_offset, meta, layout, self)
-    }
-
-    pub(crate) fn mplace_downcast(
-        &self,
-        base: &MPlaceTy<'tcx, M::PointerTag>,
-        variant: VariantIdx,
-    ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
-        // Downcasts only change the layout.
-        // (In particular, no check about whether this is even the active variant -- that's by design,
-        // see https://github.com/rust-lang/rust/issues/93688#issuecomment-1032929496.)
-        assert!(!base.meta.has_meta());
-        Ok(MPlaceTy { layout: base.layout.for_variant(self, variant), ..*base })
-    }
-
-    /// Project into an mplace
-    #[instrument(skip(self), level = "debug")]
-    pub(super) fn mplace_projection(
-        &self,
-        base: &MPlaceTy<'tcx, M::PointerTag>,
-        proj_elem: mir::PlaceElem<'tcx>,
-    ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
-        use rustc_middle::mir::ProjectionElem::*;
-        Ok(match proj_elem {
-            Field(field, _) => self.mplace_field(base, field.index())?,
-            Downcast(_, variant) => self.mplace_downcast(base, variant)?,
-            Deref => self.deref_operand(&base.into())?,
-
-            Index(local) => {
-                let layout = self.layout_of(self.tcx.types.usize)?;
-                let n = self.local_to_op(self.frame(), local, Some(layout))?;
-                let n = self.read_scalar(&n)?;
-                let n = n.to_machine_usize(self)?;
-                self.mplace_index(base, n)?
-            }
-
-            ConstantIndex { offset, min_length, from_end } => {
-                let n = base.len(self)?;
-                if n < min_length {
-                    // This can only be reached in ConstProp and non-rustc-MIR.
-                    throw_ub!(BoundsCheckFailed { len: min_length, index: n });
-                }
-
-                let index = if from_end {
-                    assert!(0 < offset && offset <= min_length);
-                    n.checked_sub(offset).unwrap()
-                } else {
-                    assert!(offset < min_length);
-                    offset
-                };
-
-                self.mplace_index(base, index)?
-            }
-
-            Subslice { from, to, from_end } => self.mplace_subslice(base, from, to, from_end)?,
-        })
-    }
-
     /// Converts a repr(simd) place into a place where `place_index` accesses the SIMD elements.
     /// Also returns the number of elements.
     pub fn mplace_to_simd(
         &self,
-        base: &MPlaceTy<'tcx, M::PointerTag>,
-    ) -> InterpResult<'tcx, (MPlaceTy<'tcx, M::PointerTag>, u64)> {
+        mplace: &MPlaceTy<'tcx, M::Provenance>,
+    ) -> InterpResult<'tcx, (MPlaceTy<'tcx, M::Provenance>, u64)> {
         // Basically we just transmute this place into an array following simd_size_and_type.
         // (Transmuting is okay since this is an in-memory place. We also double-check the size
         // stays the same.)
-        let (len, e_ty) = base.layout.ty.simd_size_and_type(*self.tcx);
+        let (len, e_ty) = mplace.layout.ty.simd_size_and_type(*self.tcx);
         let array = self.tcx.mk_array(e_ty, len);
         let layout = self.layout_of(array)?;
-        assert_eq!(layout.size, base.layout.size);
-        Ok((MPlaceTy { layout, ..*base }, len))
-    }
-
-    /// Gets the place of a field inside the place, and also the field's type.
-    /// Just a convenience function, but used quite a bit.
-    /// This is the only projection that might have a side-effect: We cannot project
-    /// into the field of a local `ScalarPair`, we have to first allocate it.
-    #[instrument(skip(self), level = "debug")]
-    pub fn place_field(
-        &mut self,
-        base: &PlaceTy<'tcx, M::PointerTag>,
-        field: usize,
-    ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> {
-        // FIXME: We could try to be smarter and avoid allocation for fields that span the
-        // entire place.
-        let mplace = self.force_allocation(base)?;
-        Ok(self.mplace_field(&mplace, field)?.into())
-    }
-
-    pub fn place_index(
-        &mut self,
-        base: &PlaceTy<'tcx, M::PointerTag>,
-        index: u64,
-    ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> {
-        let mplace = self.force_allocation(base)?;
-        Ok(self.mplace_index(&mplace, index)?.into())
-    }
-
-    pub fn place_downcast(
-        &self,
-        base: &PlaceTy<'tcx, M::PointerTag>,
-        variant: VariantIdx,
-    ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> {
-        // Downcast just changes the layout
-        Ok(match base.try_as_mplace() {
-            Ok(mplace) => self.mplace_downcast(&mplace, variant)?.into(),
-            Err(..) => {
-                let layout = base.layout.for_variant(self, variant);
-                PlaceTy { layout, ..*base }
-            }
-        })
-    }
-
-    /// Projects into a place.
-    pub fn place_projection(
-        &mut self,
-        base: &PlaceTy<'tcx, M::PointerTag>,
-        &proj_elem: &mir::ProjectionElem<mir::Local, Ty<'tcx>>,
-    ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> {
-        use rustc_middle::mir::ProjectionElem::*;
-        Ok(match proj_elem {
-            Field(field, _) => self.place_field(base, field.index())?,
-            Downcast(_, variant) => self.place_downcast(base, variant)?,
-            Deref => self.deref_operand(&self.place_to_op(base)?)?.into(),
-            // For the other variants, we have to force an allocation.
-            // This matches `operand_projection`.
-            Subslice { .. } | ConstantIndex { .. } | Index(_) => {
-                let mplace = self.force_allocation(base)?;
-                self.mplace_projection(&mplace, proj_elem)?.into()
-            }
-        })
+        assert_eq!(layout.size, mplace.layout.size);
+        Ok((MPlaceTy { layout, ..*mplace }, len))
     }
 
     /// Converts a repr(simd) place into a place where `place_index` accesses the SIMD elements.
     /// Also returns the number of elements.
     pub fn place_to_simd(
         &mut self,
-        base: &PlaceTy<'tcx, M::PointerTag>,
-    ) -> InterpResult<'tcx, (MPlaceTy<'tcx, M::PointerTag>, u64)> {
-        let mplace = self.force_allocation(base)?;
+        place: &PlaceTy<'tcx, M::Provenance>,
+    ) -> InterpResult<'tcx, (MPlaceTy<'tcx, M::Provenance>, u64)> {
+        let mplace = self.force_allocation(place)?;
         self.mplace_to_simd(&mplace)
     }
 
@@ -669,7 +423,7 @@ where
         &self,
         frame: usize,
         local: mir::Local,
-    ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> {
+    ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> {
         let layout = self.layout_of_local(&self.stack()[frame], local, None)?;
         let place = Place::Local { frame, local };
         Ok(PlaceTy { place, layout, align: layout.align.abi })
@@ -680,30 +434,30 @@ where
     #[instrument(skip(self), level = "debug")]
     pub fn eval_place(
         &mut self,
-        place: mir::Place<'tcx>,
-    ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> {
-        let mut place_ty = self.local_to_place(self.frame_idx(), place.local)?;
-
-        for elem in place.projection.iter() {
-            place_ty = self.place_projection(&place_ty, &elem)?
+        mir_place: mir::Place<'tcx>,
+    ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> {
+        let mut place = self.local_to_place(self.frame_idx(), mir_place.local)?;
+        // Using `try_fold` turned out to be bad for performance, hence the loop.
+        for elem in mir_place.projection.iter() {
+            place = self.place_projection(&place, elem)?
         }
 
-        trace!("{:?}", self.dump_place(place_ty.place));
+        trace!("{:?}", self.dump_place(place.place));
         // Sanity-check the type we ended up with.
         debug_assert!(
             mir_assign_valid_types(
                 *self.tcx,
                 self.param_env,
                 self.layout_of(self.subst_from_current_frame_and_normalize_erasing_regions(
-                    place.ty(&self.frame().body.local_decls, *self.tcx).ty
+                    mir_place.ty(&self.frame().body.local_decls, *self.tcx).ty
                 )?)?,
-                place_ty.layout,
+                place.layout,
             ),
-            "eval_place of a MIR place with type {:?} produced an interpret place with type {:?}",
-            place.ty(&self.frame().body.local_decls, *self.tcx).ty,
-            place_ty.layout.ty,
+            "eval_place of a MIR place with type {:?} produced an interpreter place with type {:?}",
+            mir_place.ty(&self.frame().body.local_decls, *self.tcx).ty,
+            place.layout.ty,
         );
-        Ok(place_ty)
+        Ok(place)
     }
 
     /// Write an immediate to a place
@@ -711,8 +465,8 @@ where
     #[instrument(skip(self), level = "debug")]
     pub fn write_immediate(
         &mut self,
-        src: Immediate<M::PointerTag>,
-        dest: &PlaceTy<'tcx, M::PointerTag>,
+        src: Immediate<M::Provenance>,
+        dest: &PlaceTy<'tcx, M::Provenance>,
     ) -> InterpResult<'tcx> {
         self.write_immediate_no_validate(src, dest)?;
 
@@ -728,8 +482,8 @@ where
     #[inline(always)]
     pub fn write_scalar(
         &mut self,
-        val: impl Into<ScalarMaybeUninit<M::PointerTag>>,
-        dest: &PlaceTy<'tcx, M::PointerTag>,
+        val: impl Into<ScalarMaybeUninit<M::Provenance>>,
+        dest: &PlaceTy<'tcx, M::Provenance>,
     ) -> InterpResult<'tcx> {
         self.write_immediate(Immediate::Scalar(val.into()), dest)
     }
@@ -738,8 +492,8 @@ where
     #[inline(always)]
     pub fn write_pointer(
         &mut self,
-        ptr: impl Into<Pointer<Option<M::PointerTag>>>,
-        dest: &PlaceTy<'tcx, M::PointerTag>,
+        ptr: impl Into<Pointer<Option<M::Provenance>>>,
+        dest: &PlaceTy<'tcx, M::Provenance>,
     ) -> InterpResult<'tcx> {
         self.write_scalar(Scalar::from_maybe_pointer(ptr.into(), self), dest)
     }
@@ -749,8 +503,8 @@ where
     /// right type.
     fn write_immediate_no_validate(
         &mut self,
-        src: Immediate<M::PointerTag>,
-        dest: &PlaceTy<'tcx, M::PointerTag>,
+        src: Immediate<M::Provenance>,
+        dest: &PlaceTy<'tcx, M::Provenance>,
     ) -> InterpResult<'tcx> {
         assert!(!dest.layout.is_unsized(), "Cannot write unsized data");
         trace!("write_immediate: {:?} <- {:?}: {}", *dest, src, dest.layout.ty);
@@ -783,10 +537,10 @@ where
     /// right layout.
     fn write_immediate_to_mplace_no_validate(
         &mut self,
-        value: Immediate<M::PointerTag>,
+        value: Immediate<M::Provenance>,
         layout: TyAndLayout<'tcx>,
         align: Align,
-        dest: MemPlace<M::PointerTag>,
+        dest: MemPlace<M::Provenance>,
     ) -> InterpResult<'tcx> {
         // Note that it is really important that the type here is the right one, and matches the
         // type things are read at. In case `value` is a `ScalarPair`, we don't do any magic here
@@ -835,7 +589,7 @@ where
         }
     }
 
-    pub fn write_uninit(&mut self, dest: &PlaceTy<'tcx, M::PointerTag>) -> InterpResult<'tcx> {
+    pub fn write_uninit(&mut self, dest: &PlaceTy<'tcx, M::Provenance>) -> InterpResult<'tcx> {
         let mplace = match dest.try_as_mplace() {
             Ok(mplace) => mplace,
             Err((frame, local)) => {
@@ -865,8 +619,8 @@ where
     #[instrument(skip(self), level = "debug")]
     pub fn copy_op(
         &mut self,
-        src: &OpTy<'tcx, M::PointerTag>,
-        dest: &PlaceTy<'tcx, M::PointerTag>,
+        src: &OpTy<'tcx, M::Provenance>,
+        dest: &PlaceTy<'tcx, M::Provenance>,
         allow_transmute: bool,
     ) -> InterpResult<'tcx> {
         self.copy_op_no_validate(src, dest, allow_transmute)?;
@@ -886,8 +640,8 @@ where
     #[instrument(skip(self), level = "debug")]
     fn copy_op_no_validate(
         &mut self,
-        src: &OpTy<'tcx, M::PointerTag>,
-        dest: &PlaceTy<'tcx, M::PointerTag>,
+        src: &OpTy<'tcx, M::Provenance>,
+        dest: &PlaceTy<'tcx, M::Provenance>,
         allow_transmute: bool,
     ) -> InterpResult<'tcx> {
         // We do NOT compare the types for equality, because well-typed code can
@@ -959,8 +713,8 @@ where
     #[instrument(skip(self), level = "debug")]
     pub fn force_allocation(
         &mut self,
-        place: &PlaceTy<'tcx, M::PointerTag>,
-    ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
+        place: &PlaceTy<'tcx, M::Provenance>,
+    ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
         let mplace = match place.place {
             Place::Local { frame, local } => {
                 match M::access_local_mut(self, frame, local)? {
@@ -1006,7 +760,7 @@ where
         &mut self,
         layout: TyAndLayout<'tcx>,
         kind: MemoryKind<M::MemoryKind>,
-    ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
+    ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
         assert!(!layout.is_unsized());
         let ptr = self.allocate_ptr(layout.size, layout.align.abi, kind)?;
         Ok(MPlaceTy::from_aligned_ptr(ptr.into(), layout))
@@ -1018,7 +772,7 @@ where
         str: &str,
         kind: MemoryKind<M::MemoryKind>,
         mutbl: Mutability,
-    ) -> MPlaceTy<'tcx, M::PointerTag> {
+    ) -> MPlaceTy<'tcx, M::Provenance> {
         let ptr = self.allocate_bytes_ptr(str.as_bytes(), Align::ONE, kind, mutbl);
         let meta = Scalar::from_machine_usize(u64::try_from(str.len()).unwrap(), self);
         let mplace = MemPlace { ptr: ptr.into(), meta: MemPlaceMeta::Meta(meta) };
@@ -1036,7 +790,7 @@ where
     pub fn write_discriminant(
         &mut self,
         variant_index: VariantIdx,
-        dest: &PlaceTy<'tcx, M::PointerTag>,
+        dest: &PlaceTy<'tcx, M::Provenance>,
     ) -> InterpResult<'tcx> {
         // This must be an enum or generator.
         match dest.layout.ty.kind() {
@@ -1058,10 +812,10 @@ where
         }
 
         match dest.layout.variants {
-            Variants::Single { index } => {
+            abi::Variants::Single { index } => {
                 assert_eq!(index, variant_index);
             }
-            Variants::Multiple {
+            abi::Variants::Multiple {
                 tag_encoding: TagEncoding::Direct,
                 tag: tag_layout,
                 tag_field,
@@ -1082,7 +836,7 @@ where
                 let tag_dest = self.place_field(dest, tag_field)?;
                 self.write_scalar(Scalar::from_uint(tag_val, size), &tag_dest)?;
             }
-            Variants::Multiple {
+            abi::Variants::Multiple {
                 tag_encoding:
                     TagEncoding::Niche { dataful_variant, ref niche_variants, niche_start },
                 tag: tag_layout,
@@ -1122,7 +876,7 @@ where
     pub fn raw_const_to_mplace(
         &self,
         raw: ConstAlloc<'tcx>,
-    ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
+    ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
         // This must be an allocation in `tcx`
         let _ = self.tcx.global_alloc(raw.alloc_id);
         let ptr = self.global_base_pointer(Pointer::from(raw.alloc_id))?;
@@ -1131,28 +885,19 @@ where
     }
 
     /// Turn a place with a `dyn Trait` type into a place with the actual dynamic type.
-    /// Also return some more information so drop doesn't have to run the same code twice.
     pub(super) fn unpack_dyn_trait(
         &self,
-        mplace: &MPlaceTy<'tcx, M::PointerTag>,
-    ) -> InterpResult<'tcx, (ty::Instance<'tcx>, MPlaceTy<'tcx, M::PointerTag>)> {
-        let vtable = self.scalar_to_ptr(mplace.vtable())?; // also sanity checks the type
-        let (instance, ty) = self.read_drop_type_from_vtable(vtable)?;
+        mplace: &MPlaceTy<'tcx, M::Provenance>,
+    ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
+        let vtable = mplace.vtable().to_pointer(self)?; // also sanity checks the type
+        let (ty, _) = self.get_ptr_vtable(vtable)?;
         let layout = self.layout_of(ty)?;
 
-        // More sanity checks
-        if cfg!(debug_assertions) {
-            let (size, align) = self.read_size_and_align_from_vtable(vtable)?;
-            assert_eq!(size, layout.size);
-            // only ABI alignment is preserved
-            assert_eq!(align, layout.align.abi);
-        }
-
         let mplace = MPlaceTy {
             mplace: MemPlace { meta: MemPlaceMeta::None, ..**mplace },
             layout,
             align: layout.align.abi,
         };
-        Ok((instance, mplace))
+        Ok(mplace)
     }
 }
diff --git a/compiler/rustc_const_eval/src/interpret/projection.rs b/compiler/rustc_const_eval/src/interpret/projection.rs
new file mode 100644
index 00000000000..742339f2b0a
--- /dev/null
+++ b/compiler/rustc_const_eval/src/interpret/projection.rs
@@ -0,0 +1,391 @@
+//! This file implements "place projections"; basically a symmetric API for 3 types: MPlaceTy, OpTy, PlaceTy.
+//!
+//! OpTy and PlaceTy genrally work by "let's see if we are actually an MPlaceTy, and do something custom if not".
+//! For PlaceTy, the custom thing is basically always to call `force_allocation` and then use the MPlaceTy logic anyway.
+//! For OpTy, the custom thing on field pojections has to be pretty clever (since `Operand::Immediate` can have fields),
+//! but for array/slice operations it only has to worry about `Operand::Uninit`. That makes the value part trivial,
+//! but we still need to do bounds checking and adjust the layout. To not duplicate that with MPlaceTy, we actually
+//! implement the logic on OpTy, and MPlaceTy calls that.
+
+use std::hash::Hash;
+
+use rustc_middle::mir;
+use rustc_middle::ty;
+use rustc_middle::ty::layout::LayoutOf;
+use rustc_target::abi::{self, Abi, VariantIdx};
+
+use super::{
+    ImmTy, Immediate, InterpCx, InterpResult, MPlaceTy, Machine, MemPlaceMeta, OpTy, PlaceTy,
+    Provenance, Scalar,
+};
+
+// FIXME: Working around https://github.com/rust-lang/rust/issues/54385
+impl<'mir, 'tcx: 'mir, Prov, M> InterpCx<'mir, 'tcx, M>
+where
+    Prov: Provenance + Eq + Hash + 'static,
+    M: Machine<'mir, 'tcx, Provenance = Prov>,
+{
+    //# Field access
+
+    /// Offset a pointer to project to a field of a struct/union. Unlike `place_field`, this is
+    /// always possible without allocating, so it can take `&self`. Also return the field's layout.
+    /// This supports both struct and array fields.
+    ///
+    /// This also works for arrays, but then the `usize` index type is restricting.
+    /// For indexing into arrays, use `mplace_index`.
+    pub fn mplace_field(
+        &self,
+        base: &MPlaceTy<'tcx, M::Provenance>,
+        field: usize,
+    ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
+        let offset = base.layout.fields.offset(field);
+        let field_layout = base.layout.field(self, field);
+
+        // Offset may need adjustment for unsized fields.
+        let (meta, offset) = if field_layout.is_unsized() {
+            // Re-use parent metadata to determine dynamic field layout.
+            // With custom DSTS, this *will* execute user-defined code, but the same
+            // happens at run-time so that's okay.
+            match self.size_and_align_of(&base.meta, &field_layout)? {
+                Some((_, align)) => (base.meta, offset.align_to(align)),
+                None => {
+                    // For unsized types with an extern type tail we perform no adjustments.
+                    // NOTE: keep this in sync with `PlaceRef::project_field` in the codegen backend.
+                    assert!(matches!(base.meta, MemPlaceMeta::None));
+                    (base.meta, offset)
+                }
+            }
+        } else {
+            // base.meta could be present; we might be accessing a sized field of an unsized
+            // struct.
+            (MemPlaceMeta::None, offset)
+        };
+
+        // We do not look at `base.layout.align` nor `field_layout.align`, unlike
+        // codegen -- mostly to see if we can get away with that
+        base.offset_with_meta(offset, meta, field_layout, self)
+    }
+
+    /// Gets the place of a field inside the place, and also the field's type.
+    /// Just a convenience function, but used quite a bit.
+    /// This is the only projection that might have a side-effect: We cannot project
+    /// into the field of a local `ScalarPair`, we have to first allocate it.
+    pub fn place_field(
+        &mut self,
+        base: &PlaceTy<'tcx, M::Provenance>,
+        field: usize,
+    ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> {
+        // FIXME: We could try to be smarter and avoid allocation for fields that span the
+        // entire place.
+        let base = self.force_allocation(base)?;
+        Ok(self.mplace_field(&base, field)?.into())
+    }
+
+    pub fn operand_field(
+        &self,
+        base: &OpTy<'tcx, M::Provenance>,
+        field: usize,
+    ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
+        let base = match base.try_as_mplace() {
+            Ok(ref mplace) => {
+                // We can reuse the mplace field computation logic for indirect operands.
+                let field = self.mplace_field(mplace, field)?;
+                return Ok(field.into());
+            }
+            Err(value) => value,
+        };
+
+        let field_layout = base.layout.field(self, field);
+        let offset = base.layout.fields.offset(field);
+        // This makes several assumptions about what layouts we will encounter; we match what
+        // codegen does as good as we can (see `extract_field` in `rustc_codegen_ssa/src/mir/operand.rs`).
+        let field_val: Immediate<_> = match (*base, base.layout.abi) {
+            // the field contains no information, can be left uninit
+            _ if field_layout.is_zst() => Immediate::Uninit,
+            // the field covers the entire type
+            _ if field_layout.size == base.layout.size => {
+                assert!(match (base.layout.abi, field_layout.abi) {
+                    (Abi::Scalar(..), Abi::Scalar(..)) => true,
+                    (Abi::ScalarPair(..), Abi::ScalarPair(..)) => true,
+                    _ => false,
+                });
+                assert!(offset.bytes() == 0);
+                *base
+            }
+            // extract fields from types with `ScalarPair` ABI
+            (Immediate::ScalarPair(a_val, b_val), Abi::ScalarPair(a, b)) => {
+                assert!(matches!(field_layout.abi, Abi::Scalar(..)));
+                Immediate::from(if offset.bytes() == 0 {
+                    debug_assert_eq!(field_layout.size, a.size(self));
+                    a_val
+                } else {
+                    debug_assert_eq!(offset, a.size(self).align_to(b.align(self).abi));
+                    debug_assert_eq!(field_layout.size, b.size(self));
+                    b_val
+                })
+            }
+            _ => span_bug!(
+                self.cur_span(),
+                "invalid field access on immediate {}, layout {:#?}",
+                base,
+                base.layout
+            ),
+        };
+
+        Ok(ImmTy::from_immediate(field_val, field_layout).into())
+    }
+
+    //# Downcasting
+
+    pub fn mplace_downcast(
+        &self,
+        base: &MPlaceTy<'tcx, M::Provenance>,
+        variant: VariantIdx,
+    ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
+        // Downcasts only change the layout.
+        // (In particular, no check about whether this is even the active variant -- that's by design,
+        // see https://github.com/rust-lang/rust/issues/93688#issuecomment-1032929496.)
+        assert!(!base.meta.has_meta());
+        let mut base = *base;
+        base.layout = base.layout.for_variant(self, variant);
+        Ok(base)
+    }
+
+    pub fn place_downcast(
+        &self,
+        base: &PlaceTy<'tcx, M::Provenance>,
+        variant: VariantIdx,
+    ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> {
+        // Downcast just changes the layout
+        let mut base = base.clone();
+        base.layout = base.layout.for_variant(self, variant);
+        Ok(base)
+    }
+
+    pub fn operand_downcast(
+        &self,
+        base: &OpTy<'tcx, M::Provenance>,
+        variant: VariantIdx,
+    ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
+        // Downcast just changes the layout
+        let mut base = base.clone();
+        base.layout = base.layout.for_variant(self, variant);
+        Ok(base)
+    }
+
+    //# Slice indexing
+
+    #[inline(always)]
+    pub fn operand_index(
+        &self,
+        base: &OpTy<'tcx, M::Provenance>,
+        index: u64,
+    ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
+        // Not using the layout method because we want to compute on u64
+        match base.layout.fields {
+            abi::FieldsShape::Array { stride, count: _ } => {
+                // `count` is nonsense for slices, use the dynamic length instead.
+                let len = base.len(self)?;
+                if index >= len {
+                    // This can only be reached in ConstProp and non-rustc-MIR.
+                    throw_ub!(BoundsCheckFailed { len, index });
+                }
+                let offset = stride * index; // `Size` multiplication
+                // All fields have the same layout.
+                let field_layout = base.layout.field(self, 0);
+                base.offset(offset, field_layout, self)
+            }
+            _ => span_bug!(
+                self.cur_span(),
+                "`mplace_index` called on non-array type {:?}",
+                base.layout.ty
+            ),
+        }
+    }
+
+    // Iterates over all fields of an array. Much more efficient than doing the
+    // same by repeatedly calling `operand_index`.
+    pub fn operand_array_fields<'a>(
+        &self,
+        base: &'a OpTy<'tcx, Prov>,
+    ) -> InterpResult<'tcx, impl Iterator<Item = InterpResult<'tcx, OpTy<'tcx, Prov>>> + 'a> {
+        let len = base.len(self)?; // also asserts that we have a type where this makes sense
+        let abi::FieldsShape::Array { stride, .. } = base.layout.fields else {
+            span_bug!(self.cur_span(), "operand_array_fields: expected an array layout");
+        };
+        let field_layout = base.layout.field(self, 0);
+        let dl = &self.tcx.data_layout;
+        // `Size` multiplication
+        Ok((0..len).map(move |i| base.offset(stride * i, field_layout, dl)))
+    }
+
+    /// Index into an array.
+    pub fn mplace_index(
+        &self,
+        base: &MPlaceTy<'tcx, M::Provenance>,
+        index: u64,
+    ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
+        Ok(self.operand_index(&base.into(), index)?.assert_mem_place())
+    }
+
+    pub fn place_index(
+        &mut self,
+        base: &PlaceTy<'tcx, M::Provenance>,
+        index: u64,
+    ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> {
+        // There's not a lot we can do here, since we cannot have a place to a part of a local. If
+        // we are accessing the only element of a 1-element array, it's still the entire local...
+        // that doesn't seem worth it.
+        let base = self.force_allocation(base)?;
+        Ok(self.mplace_index(&base, index)?.into())
+    }
+
+    //# ConstantIndex support
+
+    fn operand_constant_index(
+        &self,
+        base: &OpTy<'tcx, M::Provenance>,
+        offset: u64,
+        min_length: u64,
+        from_end: bool,
+    ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
+        let n = base.len(self)?;
+        if n < min_length {
+            // This can only be reached in ConstProp and non-rustc-MIR.
+            throw_ub!(BoundsCheckFailed { len: min_length, index: n });
+        }
+
+        let index = if from_end {
+            assert!(0 < offset && offset <= min_length);
+            n.checked_sub(offset).unwrap()
+        } else {
+            assert!(offset < min_length);
+            offset
+        };
+
+        self.operand_index(base, index)
+    }
+
+    fn place_constant_index(
+        &mut self,
+        base: &PlaceTy<'tcx, M::Provenance>,
+        offset: u64,
+        min_length: u64,
+        from_end: bool,
+    ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> {
+        let base = self.force_allocation(base)?;
+        Ok(self
+            .operand_constant_index(&base.into(), offset, min_length, from_end)?
+            .assert_mem_place()
+            .into())
+    }
+
+    //# Subslicing
+
+    fn operand_subslice(
+        &self,
+        base: &OpTy<'tcx, M::Provenance>,
+        from: u64,
+        to: u64,
+        from_end: bool,
+    ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
+        let len = base.len(self)?; // also asserts that we have a type where this makes sense
+        let actual_to = if from_end {
+            if from.checked_add(to).map_or(true, |to| to > len) {
+                // This can only be reached in ConstProp and non-rustc-MIR.
+                throw_ub!(BoundsCheckFailed { len: len, index: from.saturating_add(to) });
+            }
+            len.checked_sub(to).unwrap()
+        } else {
+            to
+        };
+
+        // Not using layout method because that works with usize, and does not work with slices
+        // (that have count 0 in their layout).
+        let from_offset = match base.layout.fields {
+            abi::FieldsShape::Array { stride, .. } => stride * from, // `Size` multiplication is checked
+            _ => {
+                span_bug!(self.cur_span(), "unexpected layout of index access: {:#?}", base.layout)
+            }
+        };
+
+        // Compute meta and new layout
+        let inner_len = actual_to.checked_sub(from).unwrap();
+        let (meta, ty) = match base.layout.ty.kind() {
+            // It is not nice to match on the type, but that seems to be the only way to
+            // implement this.
+            ty::Array(inner, _) => (MemPlaceMeta::None, self.tcx.mk_array(*inner, inner_len)),
+            ty::Slice(..) => {
+                let len = Scalar::from_machine_usize(inner_len, self);
+                (MemPlaceMeta::Meta(len), base.layout.ty)
+            }
+            _ => {
+                span_bug!(self.cur_span(), "cannot subslice non-array type: `{:?}`", base.layout.ty)
+            }
+        };
+        let layout = self.layout_of(ty)?;
+        base.offset_with_meta(from_offset, meta, layout, self)
+    }
+
+    pub fn place_subslice(
+        &mut self,
+        base: &PlaceTy<'tcx, M::Provenance>,
+        from: u64,
+        to: u64,
+        from_end: bool,
+    ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> {
+        let base = self.force_allocation(base)?;
+        Ok(self.operand_subslice(&base.into(), from, to, from_end)?.assert_mem_place().into())
+    }
+
+    //# Applying a general projection
+
+    /// Projects into a place.
+    #[instrument(skip(self), level = "trace")]
+    pub fn place_projection(
+        &mut self,
+        base: &PlaceTy<'tcx, M::Provenance>,
+        proj_elem: mir::PlaceElem<'tcx>,
+    ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> {
+        use rustc_middle::mir::ProjectionElem::*;
+        Ok(match proj_elem {
+            Field(field, _) => self.place_field(base, field.index())?,
+            Downcast(_, variant) => self.place_downcast(base, variant)?,
+            Deref => self.deref_operand(&self.place_to_op(base)?)?.into(),
+            Index(local) => {
+                let layout = self.layout_of(self.tcx.types.usize)?;
+                let n = self.local_to_op(self.frame(), local, Some(layout))?;
+                let n = self.read_scalar(&n)?.to_machine_usize(self)?;
+                self.place_index(base, n)?
+            }
+            ConstantIndex { offset, min_length, from_end } => {
+                self.place_constant_index(base, offset, min_length, from_end)?
+            }
+            Subslice { from, to, from_end } => self.place_subslice(base, from, to, from_end)?,
+        })
+    }
+
+    #[instrument(skip(self), level = "trace")]
+    pub fn operand_projection(
+        &self,
+        base: &OpTy<'tcx, M::Provenance>,
+        proj_elem: mir::PlaceElem<'tcx>,
+    ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
+        use rustc_middle::mir::ProjectionElem::*;
+        Ok(match proj_elem {
+            Field(field, _) => self.operand_field(base, field.index())?,
+            Downcast(_, variant) => self.operand_downcast(base, variant)?,
+            Deref => self.deref_operand(base)?.into(),
+            Index(local) => {
+                let layout = self.layout_of(self.tcx.types.usize)?;
+                let n = self.local_to_op(self.frame(), local, Some(layout))?;
+                let n = self.read_scalar(&n)?.to_machine_usize(self)?;
+                self.operand_index(base, n)?
+            }
+            ConstantIndex { offset, min_length, from_end } => {
+                self.operand_constant_index(base, offset, min_length, from_end)?
+            }
+            Subslice { from, to, from_end } => self.operand_subslice(base, from, to, from_end)?,
+        })
+    }
+}
diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs
index 515cc222dc6..d563e35f910 100644
--- a/compiler/rustc_const_eval/src/interpret/terminator.rs
+++ b/compiler/rustc_const_eval/src/interpret/terminator.rs
@@ -1,5 +1,4 @@
 use std::borrow::Cow;
-use std::convert::TryFrom;
 
 use rustc_middle::ty::layout::{FnAbiOf, LayoutOf};
 use rustc_middle::ty::Instance;
@@ -267,10 +266,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     fn pass_argument<'x, 'y>(
         &mut self,
         caller_args: &mut impl Iterator<
-            Item = (&'x OpTy<'tcx, M::PointerTag>, &'y ArgAbi<'tcx, Ty<'tcx>>),
+            Item = (&'x OpTy<'tcx, M::Provenance>, &'y ArgAbi<'tcx, Ty<'tcx>>),
         >,
         callee_abi: &ArgAbi<'tcx, Ty<'tcx>>,
-        callee_arg: &PlaceTy<'tcx, M::PointerTag>,
+        callee_arg: &PlaceTy<'tcx, M::Provenance>,
     ) -> InterpResult<'tcx>
     where
         'tcx: 'x,
@@ -336,9 +335,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         &mut self,
         fn_val: FnVal<'tcx, M::ExtraFnVal>,
         (caller_abi, caller_fn_abi): (Abi, &FnAbi<'tcx, Ty<'tcx>>),
-        args: &[OpTy<'tcx, M::PointerTag>],
+        args: &[OpTy<'tcx, M::Provenance>],
         with_caller_location: bool,
-        destination: &PlaceTy<'tcx, M::PointerTag>,
+        destination: &PlaceTy<'tcx, M::Provenance>,
         target: Option<mir::BasicBlock>,
         mut unwind: StackPopUnwind,
     ) -> InterpResult<'tcx> {
@@ -365,7 +364,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 // caller_fn_abi is not relevant here, we interpret the arguments directly for each intrinsic.
                 M::call_intrinsic(self, instance, args, destination, target, unwind)
             }
-            ty::InstanceDef::VtableShim(..)
+            ty::InstanceDef::VTableShim(..)
             | ty::InstanceDef::ReifyShim(..)
             | ty::InstanceDef::ClosureOnceShim { .. }
             | ty::InstanceDef::FnPtrShim(..)
@@ -437,19 +436,19 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                     // last incoming argument.  These two iterators do not have the same type,
                     // so to keep the code paths uniform we accept an allocation
                     // (for RustCall ABI only).
-                    let caller_args: Cow<'_, [OpTy<'tcx, M::PointerTag>]> =
+                    let caller_args: Cow<'_, [OpTy<'tcx, M::Provenance>]> =
                         if caller_abi == Abi::RustCall && !args.is_empty() {
                             // Untuple
                             let (untuple_arg, args) = args.split_last().unwrap();
                             trace!("eval_fn_call: Will pass last argument by untupling");
                             Cow::from(
                                 args.iter()
-                                    .map(|&a| Ok(a))
+                                    .map(|a| Ok(a.clone()))
                                     .chain(
                                         (0..untuple_arg.layout.fields.count())
                                             .map(|i| self.operand_field(untuple_arg, i)),
                                     )
-                                    .collect::<InterpResult<'_, Vec<OpTy<'tcx, M::PointerTag>>>>(
+                                    .collect::<InterpResult<'_, Vec<OpTy<'tcx, M::Provenance>>>>(
                                     )?,
                             )
                         } else {
@@ -520,16 +519,16 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 }
             }
             // cannot use the shim here, because that will only result in infinite recursion
-            ty::InstanceDef::Virtual(_, idx) => {
+            ty::InstanceDef::Virtual(def_id, idx) => {
                 let mut args = args.to_vec();
                 // We have to implement all "object safe receivers". So we have to go search for a
                 // pointer or `dyn Trait` type, but it could be wrapped in newtypes. So recursively
                 // unwrap those newtypes until we are there.
-                let mut receiver = args[0];
+                let mut receiver = args[0].clone();
                 let receiver_place = loop {
                     match receiver.layout.ty.kind() {
                         ty::Ref(..) | ty::RawPtr(..) => break self.deref_operand(&receiver)?,
-                        ty::Dynamic(..) => break receiver.assert_mem_place(),
+                        ty::Dynamic(..) => break receiver.assert_mem_place(), // no immediate unsized values
                         _ => {
                             // Not there yet, search for the only non-ZST field.
                             let mut non_zst_field = None;
@@ -553,17 +552,52 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                         }
                     }
                 };
-                // Find and consult vtable. The type now could be something like RcBox<dyn Trait>,
-                // i.e., it is still not necessarily `ty::Dynamic` (so we cannot use
-                // `place.vtable()`), but it should have a `dyn Trait` tail.
-                assert!(matches!(
-                    self.tcx
-                        .struct_tail_erasing_lifetimes(receiver_place.layout.ty, self.param_env)
-                        .kind(),
-                    ty::Dynamic(..)
-                ));
-                let vtable = self.scalar_to_ptr(receiver_place.meta.unwrap_meta())?;
-                let fn_val = self.get_vtable_slot(vtable, u64::try_from(idx).unwrap())?;
+                // Obtain the underlying trait we are working on.
+                let receiver_tail = self
+                    .tcx
+                    .struct_tail_erasing_lifetimes(receiver_place.layout.ty, self.param_env);
+                let ty::Dynamic(data, ..) = receiver_tail.kind() else {
+                    span_bug!(self.cur_span(), "dyanmic call on non-`dyn` type {}", receiver_tail)
+                };
+
+                // Get the required information from the vtable.
+                let vptr = receiver_place.meta.unwrap_meta().to_pointer(self)?;
+                let (dyn_ty, dyn_trait) = self.get_ptr_vtable(vptr)?;
+                if dyn_trait != data.principal() {
+                    throw_ub_format!(
+                        "`dyn` call on a pointer whose vtable does not match its type"
+                    );
+                }
+
+                // Now determine the actual method to call. We can do that in two different ways and
+                // compare them to ensure everything fits.
+                let Some(ty::VtblEntry::Method(fn_inst)) = self.get_vtable_entries(vptr)?.get(idx).copied() else {
+                    throw_ub_format!("`dyn` call trying to call something that is not a method")
+                };
+                if cfg!(debug_assertions) {
+                    let tcx = *self.tcx;
+
+                    let trait_def_id = tcx.trait_of_item(def_id).unwrap();
+                    let virtual_trait_ref =
+                        ty::TraitRef::from_method(tcx, trait_def_id, instance.substs);
+                    assert_eq!(
+                        receiver_tail,
+                        virtual_trait_ref.self_ty(),
+                        "mismatch in underlying dyn trait computation within Miri and MIR building",
+                    );
+                    let existential_trait_ref =
+                        ty::ExistentialTraitRef::erase_self_ty(tcx, virtual_trait_ref);
+                    let concrete_trait_ref = existential_trait_ref.with_self_ty(tcx, dyn_ty);
+
+                    let concrete_method = Instance::resolve_for_vtable(
+                        tcx,
+                        self.param_env,
+                        def_id,
+                        instance.substs.rebase_onto(tcx, trait_def_id, concrete_trait_ref.substs),
+                    )
+                    .unwrap();
+                    assert_eq!(fn_inst, concrete_method);
+                }
 
                 // `*mut receiver_place.layout.ty` is almost the layout that we
                 // want for args[0]: We have to project to field 0 because we want
@@ -579,7 +613,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 trace!("Patched receiver operand to {:#?}", args[0]);
                 // recurse with concrete function
                 self.eval_fn_call(
-                    fn_val,
+                    FnVal::Instance(fn_inst),
                     (caller_abi, caller_fn_abi),
                     &args,
                     with_caller_location,
@@ -593,7 +627,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
 
     fn drop_in_place(
         &mut self,
-        place: &PlaceTy<'tcx, M::PointerTag>,
+        place: &PlaceTy<'tcx, M::Provenance>,
         instance: ty::Instance<'tcx>,
         target: mir::BasicBlock,
         unwind: Option<mir::BasicBlock>,
@@ -606,8 +640,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
 
         let (instance, place) = match place.layout.ty.kind() {
             ty::Dynamic(..) => {
-                // Dropping a trait object.
-                self.unpack_dyn_trait(&place)?
+                // Dropping a trait object. Need to find actual drop fn.
+                let place = self.unpack_dyn_trait(&place)?;
+                let instance = ty::Instance::resolve_drop_in_place(*self.tcx, place.layout.ty);
+                (instance, place)
             }
             _ => (instance, place),
         };
@@ -617,16 +653,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             place.to_ref(self),
             self.layout_of(self.tcx.mk_mut_ptr(place.layout.ty))?,
         );
-
-        let ty = self.tcx.mk_unit(); // return type is ()
-        let dest = MPlaceTy::dangling(self.layout_of(ty)?);
+        let ret = MPlaceTy::fake_alloc_zst(self.layout_of(self.tcx.types.unit)?);
 
         self.eval_fn_call(
             FnVal::Instance(instance),
             (Abi::Rust, fn_abi),
             &[arg.into()],
             false,
-            &dest.into(),
+            &ret.into(),
             Some(target),
             match unwind {
                 Some(cleanup) => StackPopUnwind::Cleanup(cleanup),
diff --git a/compiler/rustc_const_eval/src/interpret/traits.rs b/compiler/rustc_const_eval/src/interpret/traits.rs
index 22c23df7b1a..b3a511d5a49 100644
--- a/compiler/rustc_const_eval/src/interpret/traits.rs
+++ b/compiler/rustc_const_eval/src/interpret/traits.rs
@@ -1,27 +1,24 @@
-use std::convert::TryFrom;
-
-use rustc_middle::mir::interpret::{alloc_range, InterpResult, Pointer, PointerArithmetic};
-use rustc_middle::ty::{
-    self, Ty, TyCtxt, COMMON_VTABLE_ENTRIES_ALIGN, COMMON_VTABLE_ENTRIES_DROPINPLACE,
-    COMMON_VTABLE_ENTRIES_SIZE,
-};
+use rustc_middle::mir::interpret::{InterpResult, Pointer};
+use rustc_middle::ty::layout::LayoutOf;
+use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_target::abi::{Align, Size};
 
 use super::util::ensure_monomorphic_enough;
-use super::{FnVal, InterpCx, Machine};
+use super::{InterpCx, Machine};
 
 impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     /// Creates a dynamic vtable for the given type and vtable origin. This is used only for
     /// objects.
     ///
-    /// The `trait_ref` encodes the erased self type. Hence, if we are
-    /// making an object `Foo<Trait>` from a value of type `Foo<T>`, then
-    /// `trait_ref` would map `T: Trait`.
-    pub fn get_vtable(
-        &mut self,
+    /// The `trait_ref` encodes the erased self type. Hence, if we are making an object `Foo<Trait>`
+    /// from a value of type `Foo<T>`, then `trait_ref` would map `T: Trait`. `None` here means that
+    /// this is an auto trait without any methods, so we only need the basic vtable (drop, size,
+    /// align).
+    pub fn get_vtable_ptr(
+        &self,
         ty: Ty<'tcx>,
         poly_trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>,
-    ) -> InterpResult<'tcx, Pointer<Option<M::PointerTag>>> {
+    ) -> InterpResult<'tcx, Pointer<Option<M::Provenance>>> {
         trace!("get_vtable(trait_ref={:?})", poly_trait_ref);
 
         let (ty, poly_trait_ref) = self.tcx.erase_regions((ty, poly_trait_ref));
@@ -30,114 +27,33 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         ensure_monomorphic_enough(*self.tcx, ty)?;
         ensure_monomorphic_enough(*self.tcx, poly_trait_ref)?;
 
-        let vtable_allocation = self.tcx.vtable_allocation((ty, poly_trait_ref));
-
-        let vtable_ptr = self.global_base_pointer(Pointer::from(vtable_allocation))?;
-
+        let vtable_symbolic_allocation = self.tcx.create_vtable_alloc(ty, poly_trait_ref);
+        let vtable_ptr = self.global_base_pointer(Pointer::from(vtable_symbolic_allocation))?;
         Ok(vtable_ptr.into())
     }
 
-    /// Resolves the function at the specified slot in the provided
-    /// vtable. Currently an index of '3' (`TyCtxt::COMMON_VTABLE_ENTRIES.len()`)
-    /// corresponds to the first method declared in the trait of the provided vtable.
-    pub fn get_vtable_slot(
-        &self,
-        vtable: Pointer<Option<M::PointerTag>>,
-        idx: u64,
-    ) -> InterpResult<'tcx, FnVal<'tcx, M::ExtraFnVal>> {
-        let ptr_size = self.pointer_size();
-        let vtable_slot = vtable.offset(ptr_size * idx, self)?;
-        let vtable_slot = self
-            .get_ptr_alloc(vtable_slot, ptr_size, self.tcx.data_layout.pointer_align.abi)?
-            .expect("cannot be a ZST");
-        let fn_ptr = self.scalar_to_ptr(vtable_slot.read_pointer(Size::ZERO)?.check_init()?)?;
-        self.get_ptr_fn(fn_ptr)
-    }
-
-    /// Returns the drop fn instance as well as the actual dynamic type.
-    pub fn read_drop_type_from_vtable(
+    /// Returns a high-level representation of the entires of the given vtable.
+    pub fn get_vtable_entries(
         &self,
-        vtable: Pointer<Option<M::PointerTag>>,
-    ) -> InterpResult<'tcx, (ty::Instance<'tcx>, Ty<'tcx>)> {
-        let pointer_size = self.pointer_size();
-        // We don't care about the pointee type; we just want a pointer.
-        let vtable = self
-            .get_ptr_alloc(
-                vtable,
-                pointer_size * u64::try_from(TyCtxt::COMMON_VTABLE_ENTRIES.len()).unwrap(),
-                self.tcx.data_layout.pointer_align.abi,
-            )?
-            .expect("cannot be a ZST");
-        let drop_fn = vtable
-            .read_pointer(pointer_size * u64::try_from(COMMON_VTABLE_ENTRIES_DROPINPLACE).unwrap())?
-            .check_init()?;
-        // We *need* an instance here, no other kind of function value, to be able
-        // to determine the type.
-        let drop_instance = self.get_ptr_fn(self.scalar_to_ptr(drop_fn)?)?.as_instance()?;
-        trace!("Found drop fn: {:?}", drop_instance);
-        let fn_sig = drop_instance.ty(*self.tcx, self.param_env).fn_sig(*self.tcx);
-        let fn_sig = self.tcx.normalize_erasing_late_bound_regions(self.param_env, fn_sig);
-        // The drop function takes `*mut T` where `T` is the type being dropped, so get that.
-        let args = fn_sig.inputs();
-        if args.len() != 1 {
-            throw_ub!(InvalidVtableDropFn(fn_sig));
-        }
-        let ty =
-            args[0].builtin_deref(true).ok_or_else(|| err_ub!(InvalidVtableDropFn(fn_sig)))?.ty;
-        Ok((drop_instance, ty))
+        vtable: Pointer<Option<M::Provenance>>,
+    ) -> InterpResult<'tcx, &'tcx [ty::VtblEntry<'tcx>]> {
+        let (ty, poly_trait_ref) = self.get_ptr_vtable(vtable)?;
+        Ok(if let Some(poly_trait_ref) = poly_trait_ref {
+            let trait_ref = poly_trait_ref.with_self_ty(*self.tcx, ty);
+            let trait_ref = self.tcx.erase_regions(trait_ref);
+            self.tcx.vtable_entries(trait_ref)
+        } else {
+            TyCtxt::COMMON_VTABLE_ENTRIES
+        })
     }
 
-    pub fn read_size_and_align_from_vtable(
+    pub fn get_vtable_size_and_align(
         &self,
-        vtable: Pointer<Option<M::PointerTag>>,
+        vtable: Pointer<Option<M::Provenance>>,
     ) -> InterpResult<'tcx, (Size, Align)> {
-        let pointer_size = self.pointer_size();
-        // We check for `size = 3 * ptr_size`, which covers the drop fn (unused here),
-        // the size, and the align (which we read below).
-        let vtable = self
-            .get_ptr_alloc(
-                vtable,
-                pointer_size * u64::try_from(TyCtxt::COMMON_VTABLE_ENTRIES.len()).unwrap(),
-                self.tcx.data_layout.pointer_align.abi,
-            )?
-            .expect("cannot be a ZST");
-        let size = vtable
-            .read_integer(alloc_range(
-                pointer_size * u64::try_from(COMMON_VTABLE_ENTRIES_SIZE).unwrap(),
-                pointer_size,
-            ))?
-            .check_init()?;
-        let size = size.to_machine_usize(self)?;
-        let size = Size::from_bytes(size);
-        let align = vtable
-            .read_integer(alloc_range(
-                pointer_size * u64::try_from(COMMON_VTABLE_ENTRIES_ALIGN).unwrap(),
-                pointer_size,
-            ))?
-            .check_init()?;
-        let align = align.to_machine_usize(self)?;
-        let align = Align::from_bytes(align).map_err(|e| err_ub!(InvalidVtableAlignment(e)))?;
-
-        if size > self.max_size_of_val() {
-            throw_ub!(InvalidVtableSize);
-        }
-        Ok((size, align))
-    }
-
-    pub fn read_new_vtable_after_trait_upcasting_from_vtable(
-        &self,
-        vtable: Pointer<Option<M::PointerTag>>,
-        idx: u64,
-    ) -> InterpResult<'tcx, Pointer<Option<M::PointerTag>>> {
-        let pointer_size = self.pointer_size();
-
-        let vtable_slot = vtable.offset(pointer_size * idx, self)?;
-        let new_vtable = self
-            .get_ptr_alloc(vtable_slot, pointer_size, self.tcx.data_layout.pointer_align.abi)?
-            .expect("cannot be a ZST");
-
-        let new_vtable = self.scalar_to_ptr(new_vtable.read_pointer(Size::ZERO)?.check_init()?)?;
-
-        Ok(new_vtable)
+        let (ty, _trait_ref) = self.get_ptr_vtable(vtable)?;
+        let layout = self.layout_of(ty)?;
+        assert!(!layout.is_unsized(), "there are no vtables for unsized types");
+        Ok((layout.size, layout.align.abi))
     }
 }
diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs
index 08102585a7b..d20f16755c3 100644
--- a/compiler/rustc_const_eval/src/interpret/validity.rs
+++ b/compiler/rustc_const_eval/src/interpret/validity.rs
@@ -206,7 +206,7 @@ struct ValidityVisitor<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> {
     /// starts must not be changed!  `visit_fields` and `visit_array` rely on
     /// this stack discipline.
     path: Vec<PathElem>,
-    ref_tracking: Option<&'rt mut RefTracking<MPlaceTy<'tcx, M::PointerTag>, Vec<PathElem>>>,
+    ref_tracking: Option<&'rt mut RefTracking<MPlaceTy<'tcx, M::Provenance>, Vec<PathElem>>>,
     /// `None` indicates this is not validating for CTFE (but for runtime).
     ctfe_mode: Option<CtfeValidationMode>,
     ecx: &'rt InterpCx<'mir, 'tcx, M>,
@@ -306,57 +306,22 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
 
     fn check_wide_ptr_meta(
         &mut self,
-        meta: MemPlaceMeta<M::PointerTag>,
+        meta: MemPlaceMeta<M::Provenance>,
         pointee: TyAndLayout<'tcx>,
     ) -> InterpResult<'tcx> {
         let tail = self.ecx.tcx.struct_tail_erasing_lifetimes(pointee.ty, self.ecx.param_env);
         match tail.kind() {
             ty::Dynamic(..) => {
-                let vtable = self.ecx.scalar_to_ptr(meta.unwrap_meta())?;
-                // Direct call to `check_ptr_access_align` checks alignment even on CTFE machines.
-                try_validation!(
-                    self.ecx.check_ptr_access_align(
-                        vtable,
-                        3 * self.ecx.tcx.data_layout.pointer_size, // drop, size, align
-                        self.ecx.tcx.data_layout.pointer_align.abi,
-                        CheckInAllocMsg::InboundsTest, // will anyway be replaced by validity message
-                    ),
-                    self.path,
-                    err_ub!(DanglingIntPointer(..)) |
-                    err_ub!(PointerUseAfterFree(..)) =>
-                        { "dangling vtable pointer in wide pointer" },
-                    err_ub!(AlignmentCheckFailed { .. }) =>
-                        { "unaligned vtable pointer in wide pointer" },
-                    err_ub!(PointerOutOfBounds { .. }) =>
-                        { "too small vtable" },
-                );
-                try_validation!(
-                    self.ecx.read_drop_type_from_vtable(vtable),
+                let vtable = meta.unwrap_meta().to_pointer(self.ecx)?;
+                // Make sure it is a genuine vtable pointer.
+                let (_ty, _trait) = try_validation!(
+                    self.ecx.get_ptr_vtable(vtable),
                     self.path,
                     err_ub!(DanglingIntPointer(..)) |
-                    err_ub!(InvalidFunctionPointer(..)) =>
-                        { "invalid drop function pointer in vtable (not pointing to a function)" },
-                    err_ub!(InvalidVtableDropFn(..)) =>
-                        { "invalid drop function pointer in vtable (function has incompatible signature)" },
-                    // Stacked Borrows errors can happen here, see https://github.com/rust-lang/miri/issues/2123.
-                    // (We assume there are no other MachineStop errors possible here.)
-                    InterpError::MachineStop(_) =>
-                        { "vtable pointer does not have permission to read drop function pointer" },
+                    err_ub!(InvalidVTablePointer(..)) =>
+                        { "{vtable}" } expected { "a vtable pointer" },
                 );
-                try_validation!(
-                    self.ecx.read_size_and_align_from_vtable(vtable),
-                    self.path,
-                    err_ub!(InvalidVtableSize) =>
-                        { "invalid vtable: size is bigger than largest supported object" },
-                    err_ub!(InvalidVtableAlignment(msg)) =>
-                        { "invalid vtable: alignment {}", msg },
-                    err_unsup!(ReadPointerAsBytes) => { "invalid size or align in vtable" },
-                    // Stacked Borrows errors can happen here, see https://github.com/rust-lang/miri/issues/2123.
-                    // (We assume there are no other MachineStop errors possible here.)
-                    InterpError::MachineStop(_) =>
-                        { "vtable pointer does not have permission to read size and alignment" },
-                );
-                // FIXME: More checks for the vtable.
+                // FIXME: check if the type/trait match what ty::Dynamic says?
             }
             ty::Slice(..) | ty::Str => {
                 let _len = try_validation!(
@@ -380,7 +345,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
     /// Check a reference or `Box`.
     fn check_safe_pointer(
         &mut self,
-        value: &OpTy<'tcx, M::PointerTag>,
+        value: &OpTy<'tcx, M::Provenance>,
         kind: &str,
     ) -> InterpResult<'tcx> {
         let value = try_validation!(
@@ -445,9 +410,9 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
         if let Some(ref mut ref_tracking) = self.ref_tracking {
             // Proceed recursively even for ZST, no reason to skip them!
             // `!` is a ZST and we want to validate it.
-            if let Ok((alloc_id, _offset, _tag)) = self.ecx.ptr_try_get_alloc_id(place.ptr) {
+            if let Ok((alloc_id, _offset, _prov)) = self.ecx.ptr_try_get_alloc_id(place.ptr) {
                 // Special handling for pointers to statics (irrespective of their type).
-                let alloc_kind = self.ecx.tcx.get_global_alloc(alloc_id);
+                let alloc_kind = self.ecx.tcx.try_get_global_alloc(alloc_id);
                 if let Some(GlobalAlloc::Static(did)) = alloc_kind {
                     assert!(!self.ecx.tcx.is_thread_local_static(did));
                     assert!(self.ecx.tcx.is_static(did));
@@ -491,8 +456,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
 
     fn read_scalar(
         &self,
-        op: &OpTy<'tcx, M::PointerTag>,
-    ) -> InterpResult<'tcx, ScalarMaybeUninit<M::PointerTag>> {
+        op: &OpTy<'tcx, M::Provenance>,
+    ) -> InterpResult<'tcx, ScalarMaybeUninit<M::Provenance>> {
         Ok(try_validation!(
             self.ecx.read_scalar(op),
             self.path,
@@ -502,8 +467,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
 
     fn read_immediate_forced(
         &self,
-        op: &OpTy<'tcx, M::PointerTag>,
-    ) -> InterpResult<'tcx, Immediate<M::PointerTag>> {
+        op: &OpTy<'tcx, M::Provenance>,
+    ) -> InterpResult<'tcx, Immediate<M::Provenance>> {
         Ok(*try_validation!(
             self.ecx.read_immediate_raw(op, /*force*/ true),
             self.path,
@@ -515,7 +480,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
     /// at that type.  Return `true` if the type is indeed primitive.
     fn try_visit_primitive(
         &mut self,
-        value: &OpTy<'tcx, M::PointerTag>,
+        value: &OpTy<'tcx, M::Provenance>,
     ) -> InterpResult<'tcx, bool> {
         // Go over all the primitive types
         let ty = value.layout.ty;
@@ -552,15 +517,13 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
                             { "{:x}", value } expected { "initialized bytes" }
                     );
                 }
-                if M::enforce_number_no_provenance(self.ecx) {
-                    // As a special exception we *do* match on a `Scalar` here, since we truly want
-                    // to know its underlying representation (and *not* cast it to an integer).
-                    let is_ptr = value.check_init().map_or(false, |v| matches!(v, Scalar::Ptr(..)));
-                    if is_ptr {
-                        throw_validation_failure!(self.path,
-                            { "{:x}", value } expected { "plain (non-pointer) bytes" }
-                        )
-                    }
+                // As a special exception we *do* match on a `Scalar` here, since we truly want
+                // to know its underlying representation (and *not* cast it to an integer).
+                let is_ptr = value.check_init().map_or(false, |v| matches!(v, Scalar::Ptr(..)));
+                if is_ptr {
+                    throw_validation_failure!(self.path,
+                        { "{:x}", value } expected { "plain (non-pointer) bytes" }
+                    )
                 }
                 Ok(true)
             }
@@ -603,15 +566,13 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
 
                 // If we check references recursively, also check that this points to a function.
                 if let Some(_) = self.ref_tracking {
-                    let ptr = self.ecx.scalar_to_ptr(value)?;
+                    let ptr = value.to_pointer(self.ecx)?;
                     let _fn = try_validation!(
                         self.ecx.get_ptr_fn(ptr),
                         self.path,
-                        err_ub!(DanglingIntPointer(0, _)) =>
-                            { "a null function pointer" },
                         err_ub!(DanglingIntPointer(..)) |
                         err_ub!(InvalidFunctionPointer(..)) =>
-                            { "{:x}", value } expected { "a function pointer" },
+                            { "{ptr}" } expected { "a function pointer" },
                     );
                     // FIXME: Check if the signature matches
                 } else {
@@ -652,7 +613,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
 
     fn visit_scalar(
         &mut self,
-        scalar: ScalarMaybeUninit<M::PointerTag>,
+        scalar: ScalarMaybeUninit<M::Provenance>,
         scalar_layout: ScalarAbi,
     ) -> InterpResult<'tcx> {
         // We check `is_full_range` in a slightly complicated way because *if* we are checking
@@ -735,7 +696,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
 impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
     for ValidityVisitor<'rt, 'mir, 'tcx, M>
 {
-    type V = OpTy<'tcx, M::PointerTag>;
+    type V = OpTy<'tcx, M::Provenance>;
 
     #[inline(always)]
     fn ecx(&self) -> &InterpCx<'mir, 'tcx, M> {
@@ -744,7 +705,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
 
     fn read_discriminant(
         &mut self,
-        op: &OpTy<'tcx, M::PointerTag>,
+        op: &OpTy<'tcx, M::Provenance>,
     ) -> InterpResult<'tcx, VariantIdx> {
         self.with_elem(PathElem::EnumTag, move |this| {
             Ok(try_validation!(
@@ -764,9 +725,9 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
     #[inline]
     fn visit_field(
         &mut self,
-        old_op: &OpTy<'tcx, M::PointerTag>,
+        old_op: &OpTy<'tcx, M::Provenance>,
         field: usize,
-        new_op: &OpTy<'tcx, M::PointerTag>,
+        new_op: &OpTy<'tcx, M::Provenance>,
     ) -> InterpResult<'tcx> {
         let elem = self.aggregate_field_path_elem(old_op.layout, field);
         self.with_elem(elem, move |this| this.visit_value(new_op))
@@ -775,9 +736,9 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
     #[inline]
     fn visit_variant(
         &mut self,
-        old_op: &OpTy<'tcx, M::PointerTag>,
+        old_op: &OpTy<'tcx, M::Provenance>,
         variant_id: VariantIdx,
-        new_op: &OpTy<'tcx, M::PointerTag>,
+        new_op: &OpTy<'tcx, M::Provenance>,
     ) -> InterpResult<'tcx> {
         let name = match old_op.layout.ty.kind() {
             ty::Adt(adt, _) => PathElem::Variant(adt.variant(variant_id).name),
@@ -791,7 +752,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
     #[inline(always)]
     fn visit_union(
         &mut self,
-        op: &OpTy<'tcx, M::PointerTag>,
+        op: &OpTy<'tcx, M::Provenance>,
         _fields: NonZeroUsize,
     ) -> InterpResult<'tcx> {
         // Special check preventing `UnsafeCell` inside unions in the inner part of constants.
@@ -804,13 +765,13 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
     }
 
     #[inline]
-    fn visit_box(&mut self, op: &OpTy<'tcx, M::PointerTag>) -> InterpResult<'tcx> {
+    fn visit_box(&mut self, op: &OpTy<'tcx, M::Provenance>) -> InterpResult<'tcx> {
         self.check_safe_pointer(op, "box")?;
         Ok(())
     }
 
     #[inline]
-    fn visit_value(&mut self, op: &OpTy<'tcx, M::PointerTag>) -> InterpResult<'tcx> {
+    fn visit_value(&mut self, op: &OpTy<'tcx, M::Provenance>) -> InterpResult<'tcx> {
         trace!("visit_value: {:?}, {:?}", *op, op.layout);
 
         // Check primitive types -- the leaves of our recursive descent.
@@ -821,7 +782,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
         // Special check preventing `UnsafeCell` in the inner part of constants
         if let Some(def) = op.layout.ty.ty_adt_def() {
             if matches!(self.ctfe_mode, Some(CtfeValidationMode::Const { inner: true, .. }))
-                && Some(def.did()) == self.ecx.tcx.lang_items().unsafe_cell_type()
+                && def.is_unsafe_cell()
             {
                 throw_validation_failure!(self.path, { "`UnsafeCell` in a `const`" });
             }
@@ -847,15 +808,20 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
                 );
             }
             Abi::Scalar(scalar_layout) => {
+                // We use a 'forced' read because we always need a `Immediate` here
+                // and treating "partially uninit" as "fully uninit" is fine for us.
                 let scalar = self.read_immediate_forced(op)?.to_scalar_or_uninit();
                 self.visit_scalar(scalar, scalar_layout)?;
             }
             Abi::ScalarPair(a_layout, b_layout) => {
-                // We would validate these things as we descend into the fields,
+                // There is no `rustc_layout_scalar_valid_range_start` for pairs, so
+                // we would validate these things as we descend into the fields,
                 // but that can miss bugs in layout computation. Layout computation
                 // is subtle due to enums having ScalarPair layout, where one field
                 // is the discriminant.
                 if cfg!(debug_assertions) {
+                    // We use a 'forced' read because we always need a `Immediate` here
+                    // and treating "partially uninit" as "fully uninit" is fine for us.
                     let (a, b) = self.read_immediate_forced(op)?.to_scalar_or_uninit_pair();
                     self.visit_scalar(a, a_layout)?;
                     self.visit_scalar(b, b_layout)?;
@@ -863,7 +829,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
             }
             Abi::Vector { .. } => {
                 // No checks here, we assume layout computation gets this right.
-                // (This is harder to check since Miri does not represent these as `Immediate`.)
+                // (This is harder to check since Miri does not represent these as `Immediate`. We
+                // also cannot use field projections since this might be a newtype around a vector.)
             }
             Abi::Aggregate { .. } => {
                 // Nothing to do.
@@ -875,12 +842,12 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
 
     fn visit_aggregate(
         &mut self,
-        op: &OpTy<'tcx, M::PointerTag>,
+        op: &OpTy<'tcx, M::Provenance>,
         fields: impl Iterator<Item = InterpResult<'tcx, Self::V>>,
     ) -> InterpResult<'tcx> {
         match op.layout.ty.kind() {
             ty::Str => {
-                let mplace = op.assert_mem_place(); // strings are never immediate
+                let mplace = op.assert_mem_place(); // strings are unsized and hence never immediate
                 let len = mplace.len(self.ecx)?;
                 try_validation!(
                     self.ecx.read_bytes_ptr(mplace.ptr, Size::from_bytes(len)),
@@ -900,14 +867,27 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
             {
                 // Optimized handling for arrays of integer/float type.
 
-                // Arrays cannot be immediate, slices are never immediate.
-                let mplace = op.assert_mem_place();
                 // This is the length of the array/slice.
-                let len = mplace.len(self.ecx)?;
+                let len = op.len(self.ecx)?;
                 // This is the element type size.
                 let layout = self.ecx.layout_of(*tys)?;
                 // This is the size in bytes of the whole array. (This checks for overflow.)
                 let size = layout.size * len;
+                // If the size is 0, there is nothing to check.
+                // (`size` can only be 0 of `len` is 0, and empty arrays are always valid.)
+                if size == Size::ZERO {
+                    return Ok(());
+                }
+                // Now that we definitely have a non-ZST array, we know it lives in memory.
+                let mplace = match op.try_as_mplace() {
+                    Ok(mplace) => mplace,
+                    Err(imm) => match *imm {
+                        Immediate::Uninit =>
+                            throw_validation_failure!(self.path, { "uninitialized bytes" }),
+                        Immediate::Scalar(..) | Immediate::ScalarPair(..) =>
+                            bug!("arrays/slices can never have Scalar/ScalarPair layout"),
+                    }
+                };
 
                 // Optimization: we just check the entire range at once.
                 // NOTE: Keep this in sync with the handling of integer and float
@@ -919,15 +899,12 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
                 // to reject those pointers, we just do not have the machinery to
                 // talk about parts of a pointer.
                 // We also accept uninit, for consistency with the slow path.
-                let Some(alloc) = self.ecx.get_ptr_alloc(mplace.ptr, size, mplace.align)? else {
-                    // Size 0, nothing more to check.
-                    return Ok(());
-                };
+                let alloc = self.ecx.get_ptr_alloc(mplace.ptr, size, mplace.align)?.expect("we already excluded size 0");
 
                 match alloc.check_bytes(
                     alloc_range(Size::ZERO, size),
                     /*allow_uninit*/ !M::enforce_number_init(self.ecx),
-                    /*allow_ptr*/ !M::enforce_number_no_provenance(self.ecx),
+                    /*allow_ptr*/ false,
                 ) {
                     // In the happy case, we needn't check anything else.
                     Ok(()) => {}
@@ -976,9 +953,9 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
 impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     fn validate_operand_internal(
         &self,
-        op: &OpTy<'tcx, M::PointerTag>,
+        op: &OpTy<'tcx, M::Provenance>,
         path: Vec<PathElem>,
-        ref_tracking: Option<&mut RefTracking<MPlaceTy<'tcx, M::PointerTag>, Vec<PathElem>>>,
+        ref_tracking: Option<&mut RefTracking<MPlaceTy<'tcx, M::Provenance>, Vec<PathElem>>>,
         ctfe_mode: Option<CtfeValidationMode>,
     ) -> InterpResult<'tcx> {
         trace!("validate_operand_internal: {:?}, {:?}", *op, op.layout.ty);
@@ -1015,9 +992,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     #[inline(always)]
     pub fn const_validate_operand(
         &self,
-        op: &OpTy<'tcx, M::PointerTag>,
+        op: &OpTy<'tcx, M::Provenance>,
         path: Vec<PathElem>,
-        ref_tracking: &mut RefTracking<MPlaceTy<'tcx, M::PointerTag>, Vec<PathElem>>,
+        ref_tracking: &mut RefTracking<MPlaceTy<'tcx, M::Provenance>, Vec<PathElem>>,
         ctfe_mode: CtfeValidationMode,
     ) -> InterpResult<'tcx> {
         self.validate_operand_internal(op, path, Some(ref_tracking), Some(ctfe_mode))
@@ -1027,7 +1004,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     /// `op` is assumed to cover valid memory if it is an indirect operand.
     /// It will error if the bits at the destination do not match the ones described by the layout.
     #[inline(always)]
-    pub fn validate_operand(&self, op: &OpTy<'tcx, M::PointerTag>) -> InterpResult<'tcx> {
+    pub fn validate_operand(&self, op: &OpTy<'tcx, M::Provenance>) -> InterpResult<'tcx> {
         self.validate_operand_internal(op, vec![], None, None)
     }
 }
diff --git a/compiler/rustc_const_eval/src/interpret/visitor.rs b/compiler/rustc_const_eval/src/interpret/visitor.rs
index ded4c6a557a..aee1f93b1a3 100644
--- a/compiler/rustc_const_eval/src/interpret/visitor.rs
+++ b/compiler/rustc_const_eval/src/interpret/visitor.rs
@@ -8,21 +8,33 @@ use rustc_target::abi::{FieldsShape, VariantIdx, Variants};
 
 use std::num::NonZeroUsize;
 
-use super::{InterpCx, MPlaceTy, Machine, OpTy};
+use super::{InterpCx, MPlaceTy, Machine, OpTy, PlaceTy};
 
-// A thing that we can project into, and that has a layout.
-// This wouldn't have to depend on `Machine` but with the current type inference,
-// that's just more convenient to work with (avoids repeating all the `Machine` bounds).
-pub trait Value<'mir, 'tcx, M: Machine<'mir, 'tcx>>: Copy {
+/// A thing that we can project into, and that has a layout.
+/// This wouldn't have to depend on `Machine` but with the current type inference,
+/// that's just more convenient to work with (avoids repeating all the `Machine` bounds).
+pub trait Value<'mir, 'tcx, M: Machine<'mir, 'tcx>>: Sized {
     /// Gets this value's layout.
     fn layout(&self) -> TyAndLayout<'tcx>;
 
-    /// Makes this into an `OpTy`.
-    fn to_op(&self, ecx: &InterpCx<'mir, 'tcx, M>)
-    -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>>;
+    /// Makes this into an `OpTy`, in a cheap way that is good for reading.
+    fn to_op_for_read(
+        &self,
+        ecx: &InterpCx<'mir, 'tcx, M>,
+    ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>>;
 
-    /// Creates this from an `MPlaceTy`.
-    fn from_mem_place(mplace: MPlaceTy<'tcx, M::PointerTag>) -> Self;
+    /// Makes this into an `OpTy`, in a potentially more expensive way that is good for projections.
+    fn to_op_for_proj(
+        &self,
+        ecx: &InterpCx<'mir, 'tcx, M>,
+    ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
+        self.to_op_for_read(ecx)
+    }
+
+    /// Creates this from an `OpTy`.
+    ///
+    /// If `to_op_for_proj` only ever produces `Indirect` operands, then this one is definitely `Indirect`.
+    fn from_op(op: &OpTy<'tcx, M::Provenance>) -> Self;
 
     /// Projects to the given enum variant.
     fn project_downcast(
@@ -39,25 +51,67 @@ pub trait Value<'mir, 'tcx, M: Machine<'mir, 'tcx>>: Copy {
     ) -> InterpResult<'tcx, Self>;
 }
 
-// Operands and memory-places are both values.
-// Places in general are not due to `place_field` having to do `force_allocation`.
-impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M> for OpTy<'tcx, M::PointerTag> {
+/// A thing that we can project into given *mutable* access to `ecx`, and that has a layout.
+/// This wouldn't have to depend on `Machine` but with the current type inference,
+/// that's just more convenient to work with (avoids repeating all the `Machine` bounds).
+pub trait ValueMut<'mir, 'tcx, M: Machine<'mir, 'tcx>>: Sized {
+    /// Gets this value's layout.
+    fn layout(&self) -> TyAndLayout<'tcx>;
+
+    /// Makes this into an `OpTy`, in a cheap way that is good for reading.
+    fn to_op_for_read(
+        &self,
+        ecx: &InterpCx<'mir, 'tcx, M>,
+    ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>>;
+
+    /// Makes this into an `OpTy`, in a potentially more expensive way that is good for projections.
+    fn to_op_for_proj(
+        &self,
+        ecx: &mut InterpCx<'mir, 'tcx, M>,
+    ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>>;
+
+    /// Creates this from an `OpTy`.
+    ///
+    /// If `to_op_for_proj` only ever produces `Indirect` operands, then this one is definitely `Indirect`.
+    fn from_op(op: &OpTy<'tcx, M::Provenance>) -> Self;
+
+    /// Projects to the given enum variant.
+    fn project_downcast(
+        &self,
+        ecx: &mut InterpCx<'mir, 'tcx, M>,
+        variant: VariantIdx,
+    ) -> InterpResult<'tcx, Self>;
+
+    /// Projects to the n-th field.
+    fn project_field(
+        &self,
+        ecx: &mut InterpCx<'mir, 'tcx, M>,
+        field: usize,
+    ) -> InterpResult<'tcx, Self>;
+}
+
+// We cannot have a general impl which shows that Value implies ValueMut. (When we do, it says we
+// cannot `impl ValueMut for PlaceTy` because some downstream crate could `impl Value for PlaceTy`.)
+// So we have some copy-paste here. (We could have a macro but since we only have 2 types with this
+// double-impl, that would barely make the code shorter, if at all.)
+
+impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M> for OpTy<'tcx, M::Provenance> {
     #[inline(always)]
     fn layout(&self) -> TyAndLayout<'tcx> {
         self.layout
     }
 
     #[inline(always)]
-    fn to_op(
+    fn to_op_for_read(
         &self,
         _ecx: &InterpCx<'mir, 'tcx, M>,
-    ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
-        Ok(*self)
+    ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
+        Ok(self.clone())
     }
 
     #[inline(always)]
-    fn from_mem_place(mplace: MPlaceTy<'tcx, M::PointerTag>) -> Self {
-        mplace.into()
+    fn from_op(op: &OpTy<'tcx, M::Provenance>) -> Self {
+        op.clone()
     }
 
     #[inline(always)]
@@ -79,8 +133,56 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M> for OpTy<'tc
     }
 }
 
+impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueMut<'mir, 'tcx, M>
+    for OpTy<'tcx, M::Provenance>
+{
+    #[inline(always)]
+    fn layout(&self) -> TyAndLayout<'tcx> {
+        self.layout
+    }
+
+    #[inline(always)]
+    fn to_op_for_read(
+        &self,
+        _ecx: &InterpCx<'mir, 'tcx, M>,
+    ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
+        Ok(self.clone())
+    }
+
+    #[inline(always)]
+    fn to_op_for_proj(
+        &self,
+        _ecx: &mut InterpCx<'mir, 'tcx, M>,
+    ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
+        Ok(self.clone())
+    }
+
+    #[inline(always)]
+    fn from_op(op: &OpTy<'tcx, M::Provenance>) -> Self {
+        op.clone()
+    }
+
+    #[inline(always)]
+    fn project_downcast(
+        &self,
+        ecx: &mut InterpCx<'mir, 'tcx, M>,
+        variant: VariantIdx,
+    ) -> InterpResult<'tcx, Self> {
+        ecx.operand_downcast(self, variant)
+    }
+
+    #[inline(always)]
+    fn project_field(
+        &self,
+        ecx: &mut InterpCx<'mir, 'tcx, M>,
+        field: usize,
+    ) -> InterpResult<'tcx, Self> {
+        ecx.operand_field(self, field)
+    }
+}
+
 impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M>
-    for MPlaceTy<'tcx, M::PointerTag>
+    for MPlaceTy<'tcx, M::Provenance>
 {
     #[inline(always)]
     fn layout(&self) -> TyAndLayout<'tcx> {
@@ -88,16 +190,17 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M>
     }
 
     #[inline(always)]
-    fn to_op(
+    fn to_op_for_read(
         &self,
         _ecx: &InterpCx<'mir, 'tcx, M>,
-    ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
+    ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
         Ok(self.into())
     }
 
     #[inline(always)]
-    fn from_mem_place(mplace: MPlaceTy<'tcx, M::PointerTag>) -> Self {
-        mplace
+    fn from_op(op: &OpTy<'tcx, M::Provenance>) -> Self {
+        // assert is justified because our `to_op_for_read` only ever produces `Indirect` operands.
+        op.assert_mem_place()
     }
 
     #[inline(always)]
@@ -119,11 +222,111 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M>
     }
 }
 
+impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueMut<'mir, 'tcx, M>
+    for MPlaceTy<'tcx, M::Provenance>
+{
+    #[inline(always)]
+    fn layout(&self) -> TyAndLayout<'tcx> {
+        self.layout
+    }
+
+    #[inline(always)]
+    fn to_op_for_read(
+        &self,
+        _ecx: &InterpCx<'mir, 'tcx, M>,
+    ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
+        Ok(self.into())
+    }
+
+    #[inline(always)]
+    fn to_op_for_proj(
+        &self,
+        _ecx: &mut InterpCx<'mir, 'tcx, M>,
+    ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
+        Ok(self.into())
+    }
+
+    #[inline(always)]
+    fn from_op(op: &OpTy<'tcx, M::Provenance>) -> Self {
+        // assert is justified because our `to_op_for_proj` only ever produces `Indirect` operands.
+        op.assert_mem_place()
+    }
+
+    #[inline(always)]
+    fn project_downcast(
+        &self,
+        ecx: &mut InterpCx<'mir, 'tcx, M>,
+        variant: VariantIdx,
+    ) -> InterpResult<'tcx, Self> {
+        ecx.mplace_downcast(self, variant)
+    }
+
+    #[inline(always)]
+    fn project_field(
+        &self,
+        ecx: &mut InterpCx<'mir, 'tcx, M>,
+        field: usize,
+    ) -> InterpResult<'tcx, Self> {
+        ecx.mplace_field(self, field)
+    }
+}
+
+impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueMut<'mir, 'tcx, M>
+    for PlaceTy<'tcx, M::Provenance>
+{
+    #[inline(always)]
+    fn layout(&self) -> TyAndLayout<'tcx> {
+        self.layout
+    }
+
+    #[inline(always)]
+    fn to_op_for_read(
+        &self,
+        ecx: &InterpCx<'mir, 'tcx, M>,
+    ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
+        // We `force_allocation` here so that `from_op` below can work.
+        ecx.place_to_op(self)
+    }
+
+    #[inline(always)]
+    fn to_op_for_proj(
+        &self,
+        ecx: &mut InterpCx<'mir, 'tcx, M>,
+    ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
+        // We `force_allocation` here so that `from_op` below can work.
+        Ok(ecx.force_allocation(self)?.into())
+    }
+
+    #[inline(always)]
+    fn from_op(op: &OpTy<'tcx, M::Provenance>) -> Self {
+        // assert is justified because our `to_op` only ever produces `Indirect` operands.
+        op.assert_mem_place().into()
+    }
+
+    #[inline(always)]
+    fn project_downcast(
+        &self,
+        ecx: &mut InterpCx<'mir, 'tcx, M>,
+        variant: VariantIdx,
+    ) -> InterpResult<'tcx, Self> {
+        ecx.place_downcast(self, variant)
+    }
+
+    #[inline(always)]
+    fn project_field(
+        &self,
+        ecx: &mut InterpCx<'mir, 'tcx, M>,
+        field: usize,
+    ) -> InterpResult<'tcx, Self> {
+        ecx.place_field(self, field)
+    }
+}
+
 macro_rules! make_value_visitor {
-    ($visitor_trait_name:ident, $($mutability:ident)?) => {
+    ($visitor_trait:ident, $value_trait:ident, $($mutability:ident)?) => {
         // How to traverse a value and what to do when we are at the leaves.
-        pub trait $visitor_trait_name<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>>: Sized {
-            type V: Value<'mir, 'tcx, M>;
+        pub trait $visitor_trait<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>>: Sized {
+            type V: $value_trait<'mir, 'tcx, M>;
 
             /// The visitor must have an `InterpCx` in it.
             fn ecx(&$($mutability)? self)
@@ -133,7 +336,7 @@ macro_rules! make_value_visitor {
             #[inline(always)]
             fn read_discriminant(
                 &mut self,
-                op: &OpTy<'tcx, M::PointerTag>,
+                op: &OpTy<'tcx, M::Provenance>,
             ) -> InterpResult<'tcx, VariantIdx> {
                 Ok(self.ecx().read_discriminant(op)?.1)
             }
@@ -212,19 +415,20 @@ macro_rules! make_value_visitor {
             }
             fn walk_value(&mut self, v: &Self::V) -> InterpResult<'tcx>
             {
-                trace!("walk_value: type: {}", v.layout().ty);
+                let ty = v.layout().ty;
+                trace!("walk_value: type: {ty}");
 
                 // Special treatment for special types, where the (static) layout is not sufficient.
-                match *v.layout().ty.kind() {
+                match *ty.kind() {
                     // If it is a trait object, switch to the real type that was used to create it.
                     ty::Dynamic(..) => {
-                        // immediate trait objects are not a thing
-                        let op = v.to_op(self.ecx())?;
+                        // unsized values are never immediate, so we can assert_mem_place
+                        let op = v.to_op_for_read(self.ecx())?;
                         let dest = op.assert_mem_place();
-                        let inner = self.ecx().unpack_dyn_trait(&dest)?.1;
-                        trace!("walk_value: dyn object layout: {:#?}", inner.layout);
+                        let inner_mplace = self.ecx().unpack_dyn_trait(&dest)?;
+                        trace!("walk_value: dyn object layout: {:#?}", inner_mplace.layout);
                         // recurse with the inner type
-                        return self.visit_field(&v, 0, &Value::from_mem_place(inner));
+                        return self.visit_field(&v, 0, &$value_trait::from_op(&inner_mplace.into()));
                     },
                     // Slices do not need special handling here: they have `Array` field
                     // placement with length 0, so we enter the `Array` case below which
@@ -269,16 +473,19 @@ macro_rules! make_value_visitor {
                         // The second `Box` field is the allocator, which we recursively check for validity
                         // like in regular structs.
                         self.visit_field(v, 1, &alloc)?;
+
+                        // We visited all parts of this one.
+                        return Ok(());
                     }
                     _ => {},
                 };
 
                 // Visit the fields of this value.
                 match v.layout().fields {
-                    FieldsShape::Primitive => {},
+                    FieldsShape::Primitive => {}
                     FieldsShape::Union(fields) => {
                         self.visit_union(v, fields)?;
-                    },
+                    }
                     FieldsShape::Arbitrary { ref offsets, .. } => {
                         // FIXME: We collect in a vec because otherwise there are lifetime
                         // errors: Projecting to a field needs access to `ecx`.
@@ -288,17 +495,17 @@ macro_rules! make_value_visitor {
                             })
                             .collect();
                         self.visit_aggregate(v, fields.into_iter())?;
-                    },
+                    }
                     FieldsShape::Array { .. } => {
-                        // Let's get an mplace first.
-                        let op = v.to_op(self.ecx())?;
-                        let mplace = op.assert_mem_place();
+                        // Let's get an mplace (or immediate) first.
+                        // This might `force_allocate` if `v` is a `PlaceTy`, but `place_index` does that anyway.
+                        let op = v.to_op_for_proj(self.ecx())?;
                         // Now we can go over all the fields.
                         // This uses the *run-time length*, i.e., if we are a slice,
                         // the dynamic info from the metadata is used.
-                        let iter = self.ecx().mplace_array_fields(&mplace)?
+                        let iter = self.ecx().operand_array_fields(&op)?
                             .map(|f| f.and_then(|f| {
-                                Ok(Value::from_mem_place(f))
+                                Ok($value_trait::from_op(&f))
                             }));
                         self.visit_aggregate(v, iter)?;
                     }
@@ -308,7 +515,7 @@ macro_rules! make_value_visitor {
                     // If this is a multi-variant layout, find the right variant and proceed
                     // with *its* fields.
                     Variants::Multiple { .. } => {
-                        let op = v.to_op(self.ecx())?;
+                        let op = v.to_op_for_read(self.ecx())?;
                         let idx = self.read_discriminant(&op)?;
                         let inner = v.project_downcast(self.ecx(), idx)?;
                         trace!("walk_value: variant layout: {:#?}", inner.layout());
@@ -323,5 +530,5 @@ macro_rules! make_value_visitor {
     }
 }
 
-make_value_visitor!(ValueVisitor,);
-make_value_visitor!(MutValueVisitor, mut);
+make_value_visitor!(ValueVisitor, Value,);
+make_value_visitor!(MutValueVisitor, ValueMut, mut);
diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs
index d65d4f7eb72..1063f03d0e2 100644
--- a/compiler/rustc_const_eval/src/lib.rs
+++ b/compiler/rustc_const_eval/src/lib.rs
@@ -9,7 +9,7 @@ Rust MIR: a lowered representation of Rust.
 #![feature(control_flow_enum)]
 #![feature(decl_macro)]
 #![feature(exact_size_is_empty)]
-#![feature(let_chains)]
+#![cfg_attr(bootstrap, feature(let_chains))]
 #![feature(let_else)]
 #![feature(map_try_insert)]
 #![feature(min_specialization)]
@@ -33,11 +33,13 @@ extern crate rustc_middle;
 pub mod const_eval;
 mod errors;
 pub mod interpret;
+mod might_permit_raw_init;
 pub mod transform;
 pub mod util;
 
 use rustc_middle::ty;
 use rustc_middle::ty::query::Providers;
+use rustc_target::abi::InitKind;
 
 pub fn provide(providers: &mut Providers) {
     const_eval::provide(providers);
@@ -59,4 +61,8 @@ pub fn provide(providers: &mut Providers) {
         let (param_env, value) = param_env_and_value.into_parts();
         const_eval::deref_mir_constant(tcx, param_env, value)
     };
+    providers.permits_uninit_init =
+        |tcx, ty| might_permit_raw_init::might_permit_raw_init(tcx, ty, InitKind::Uninit);
+    providers.permits_zero_init =
+        |tcx, ty| might_permit_raw_init::might_permit_raw_init(tcx, ty, InitKind::Zero);
 }
diff --git a/compiler/rustc_const_eval/src/might_permit_raw_init.rs b/compiler/rustc_const_eval/src/might_permit_raw_init.rs
new file mode 100644
index 00000000000..f971c2238c7
--- /dev/null
+++ b/compiler/rustc_const_eval/src/might_permit_raw_init.rs
@@ -0,0 +1,40 @@
+use crate::const_eval::CompileTimeInterpreter;
+use crate::interpret::{InterpCx, MemoryKind, OpTy};
+use rustc_middle::ty::layout::LayoutCx;
+use rustc_middle::ty::{layout::TyAndLayout, ParamEnv, TyCtxt};
+use rustc_session::Limit;
+use rustc_target::abi::InitKind;
+
+pub fn might_permit_raw_init<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    ty: TyAndLayout<'tcx>,
+    kind: InitKind,
+) -> bool {
+    let strict = tcx.sess.opts.unstable_opts.strict_init_checks;
+
+    if strict {
+        let machine = CompileTimeInterpreter::new(Limit::new(0), false);
+
+        let mut cx = InterpCx::new(tcx, rustc_span::DUMMY_SP, ParamEnv::reveal_all(), machine);
+
+        let allocated = cx
+            .allocate(ty, MemoryKind::Machine(crate::const_eval::MemoryKind::Heap))
+            .expect("OOM: failed to allocate for uninit check");
+
+        if kind == InitKind::Zero {
+            cx.write_bytes_ptr(
+                allocated.ptr,
+                std::iter::repeat(0_u8).take(ty.layout.size().bytes_usize()),
+            )
+            .expect("failed to write bytes for zero valid check");
+        }
+
+        let ot: OpTy<'_, _> = allocated.into();
+
+        // Assume that if it failed, it's a validation failure.
+        cx.validate_operand(&ot).is_ok()
+    } else {
+        let layout_cx = LayoutCx { tcx, param_env: ParamEnv::reveal_all() };
+        ty.might_permit_raw_init(&layout_cx, kind)
+    }
+}
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs
index 39fabd17c52..628298df473 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs
@@ -312,7 +312,7 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
             Status::Forbidden => None,
         };
 
-        if self.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you {
+        if self.tcx.sess.opts.unstable_opts.unleash_the_miri_inside_of_you {
             self.tcx.sess.miri_unleashed_feature(span, gate);
             return;
         }
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs
index 1d083b0bf82..33802261644 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs
@@ -23,8 +23,8 @@ use rustc_trait_selection::traits::SelectionContext;
 
 use super::ConstCx;
 use crate::errors::{
-    MutDerefErr, NonConstOpErr, PanicNonStrErr, RawPtrComparisonErr, RawPtrToIntErr,
-    StaticAccessErr, TransientMutBorrowErr, TransientMutBorrowErrRaw,
+    MutDerefErr, NonConstOpErr, PanicNonStrErr, RawPtrToIntErr, StaticAccessErr,
+    TransientMutBorrowErr, TransientMutBorrowErrRaw,
 };
 use crate::util::{call_kind, CallDesugaringKind, CallKind};
 
@@ -654,10 +654,10 @@ pub struct RawPtrComparison;
 impl<'tcx> NonConstOp<'tcx> for RawPtrComparison {
     fn build_error(
         &self,
-        ccx: &ConstCx<'_, 'tcx>,
+        _: &ConstCx<'_, 'tcx>,
         span: Span,
     ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
-        ccx.tcx.sess.create_err(RawPtrComparisonErr { span })
+        span_bug!(span, "raw ptr comparison should already be caught in the trait system");
     }
 }
 
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs
index 9941afe5a12..e0994451172 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs
@@ -96,13 +96,13 @@ impl Qualif for HasMutInterior {
     }
 
     fn in_adt_inherently<'tcx>(
-        cx: &ConstCx<'_, 'tcx>,
+        _cx: &ConstCx<'_, 'tcx>,
         adt: AdtDef<'tcx>,
         _: SubstsRef<'tcx>,
     ) -> bool {
         // Exactly one type, `UnsafeCell`, has the `HasMutInterior` qualif inherently.
         // It arises structurally for all other types.
-        Some(adt.did()) == cx.tcx.lang_items().unsafe_cell_type()
+        adt.is_unsafe_cell()
     }
 }
 
@@ -226,7 +226,7 @@ impl Qualif for CustomEq {
         // because that component may be part of an enum variant (e.g.,
         // `Option::<NonStructuralMatchTy>::Some`), in which case some values of this type may be
         // structural-match (`Option::None`).
-        traits::search_for_structural_match_violation(cx.body.span, cx.tcx, ty, true).is_some()
+        traits::search_for_structural_match_violation(cx.body.span, cx.tcx, ty).is_some()
     }
 
     fn in_adt_inherently<'tcx>(
diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs
index cd2b8be0acd..15e820f2d19 100644
--- a/compiler/rustc_const_eval/src/transform/validate.rs
+++ b/compiler/rustc_const_eval/src/transform/validate.rs
@@ -223,7 +223,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
 
     fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
         // This check is somewhat expensive, so only run it when -Zvalidate-mir is passed.
-        if self.tcx.sess.opts.debugging_opts.validate_mir && self.mir_phase < MirPhase::DropsLowered
+        if self.tcx.sess.opts.unstable_opts.validate_mir && self.mir_phase < MirPhase::DropsLowered
         {
             // `Operand::Copy` is only supposed to be used with `Copy` types.
             if let Operand::Copy(place) = operand {
diff --git a/compiler/rustc_data_structures/Cargo.toml b/compiler/rustc_data_structures/Cargo.toml
index 2a801d0e702..5c641f54f68 100644
--- a/compiler/rustc_data_structures/Cargo.toml
+++ b/compiler/rustc_data_structures/Cargo.toml
@@ -9,7 +9,7 @@ doctest = false
 [dependencies]
 arrayvec = { version = "0.7", default-features = false }
 ena = "0.14"
-indexmap = { version = "1.8.2" }
+indexmap = { version = "1.9.1" }
 tracing = "0.1"
 jobserver_crate = { version = "0.1.13", package = "jobserver" }
 rustc_serialize = { path = "../rustc_serialize" }
diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs
index 0a2d2b40709..265f45b72d1 100644
--- a/compiler/rustc_data_structures/src/lib.rs
+++ b/compiler/rustc_data_structures/src/lib.rs
@@ -61,12 +61,10 @@ pub mod sip128;
 pub mod small_c_str;
 pub mod small_str;
 pub mod snapshot_map;
-pub mod stable_map;
 pub mod svh;
 pub use ena::snapshot_vec;
 pub mod memmap;
 pub mod sorted_map;
-pub mod stable_set;
 #[macro_use]
 pub mod stable_hasher;
 mod atomic_ref;
diff --git a/compiler/rustc_data_structures/src/memmap.rs b/compiler/rustc_data_structures/src/memmap.rs
index 26b26415eea..917416df6b8 100644
--- a/compiler/rustc_data_structures/src/memmap.rs
+++ b/compiler/rustc_data_structures/src/memmap.rs
@@ -1,6 +1,6 @@
 use std::fs::File;
 use std::io;
-use std::ops::Deref;
+use std::ops::{Deref, DerefMut};
 
 use crate::owning_ref::StableAddress;
 
@@ -45,3 +45,64 @@ impl Deref for Mmap {
 // export any function that can cause the `Vec` to be re-allocated. As such the address of the
 // bytes inside this `Vec` is stable.
 unsafe impl StableAddress for Mmap {}
+
+#[cfg(not(target_arch = "wasm32"))]
+pub struct MmapMut(memmap2::MmapMut);
+
+#[cfg(target_arch = "wasm32")]
+pub struct MmapMut(Vec<u8>);
+
+#[cfg(not(target_arch = "wasm32"))]
+impl MmapMut {
+    #[inline]
+    pub fn map_anon(len: usize) -> io::Result<Self> {
+        let mmap = memmap2::MmapMut::map_anon(len)?;
+        Ok(MmapMut(mmap))
+    }
+
+    #[inline]
+    pub fn flush(&mut self) -> io::Result<()> {
+        self.0.flush()
+    }
+
+    #[inline]
+    pub fn make_read_only(self) -> std::io::Result<Mmap> {
+        let mmap = self.0.make_read_only()?;
+        Ok(Mmap(mmap))
+    }
+}
+
+#[cfg(target_arch = "wasm32")]
+impl MmapMut {
+    #[inline]
+    pub fn map_anon(len: usize) -> io::Result<Self> {
+        let data = Vec::with_capacity(len);
+        Ok(MmapMut(data))
+    }
+
+    #[inline]
+    pub fn flush(&mut self) -> io::Result<()> {
+        Ok(())
+    }
+
+    #[inline]
+    pub fn make_read_only(self) -> std::io::Result<Mmap> {
+        Ok(Mmap(self.0))
+    }
+}
+
+impl Deref for MmapMut {
+    type Target = [u8];
+
+    #[inline]
+    fn deref(&self) -> &[u8] {
+        &*self.0
+    }
+}
+
+impl DerefMut for MmapMut {
+    #[inline]
+    fn deref_mut(&mut self) -> &mut [u8] {
+        &mut *self.0
+    }
+}
diff --git a/compiler/rustc_data_structures/src/stable_map.rs b/compiler/rustc_data_structures/src/stable_map.rs
deleted file mode 100644
index 670452d0d8c..00000000000
--- a/compiler/rustc_data_structures/src/stable_map.rs
+++ /dev/null
@@ -1,100 +0,0 @@
-pub use rustc_hash::FxHashMap;
-use std::borrow::Borrow;
-use std::collections::hash_map::Entry;
-use std::fmt;
-use std::hash::Hash;
-
-/// A deterministic wrapper around FxHashMap that does not provide iteration support.
-///
-/// It supports insert, remove, get and get_mut functions from FxHashMap.
-/// It also allows to convert hashmap to a sorted vector with the method `into_sorted_vector()`.
-#[derive(Clone)]
-pub struct StableMap<K, V> {
-    base: FxHashMap<K, V>,
-}
-
-impl<K, V> Default for StableMap<K, V>
-where
-    K: Eq + Hash,
-{
-    fn default() -> StableMap<K, V> {
-        StableMap::new()
-    }
-}
-
-impl<K, V> fmt::Debug for StableMap<K, V>
-where
-    K: Eq + Hash + fmt::Debug,
-    V: fmt::Debug,
-{
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "{:?}", self.base)
-    }
-}
-
-impl<K, V> PartialEq for StableMap<K, V>
-where
-    K: Eq + Hash,
-    V: PartialEq,
-{
-    fn eq(&self, other: &StableMap<K, V>) -> bool {
-        self.base == other.base
-    }
-}
-
-impl<K, V> Eq for StableMap<K, V>
-where
-    K: Eq + Hash,
-    V: Eq,
-{
-}
-
-impl<K, V> StableMap<K, V>
-where
-    K: Eq + Hash,
-{
-    pub fn new() -> StableMap<K, V> {
-        StableMap { base: FxHashMap::default() }
-    }
-
-    pub fn into_sorted_vector(self) -> Vec<(K, V)>
-    where
-        K: Ord + Copy,
-    {
-        let mut vector = self.base.into_iter().collect::<Vec<_>>();
-        vector.sort_unstable_by_key(|pair| pair.0);
-        vector
-    }
-
-    pub fn entry(&mut self, k: K) -> Entry<'_, K, V> {
-        self.base.entry(k)
-    }
-
-    pub fn get<Q: ?Sized>(&self, k: &Q) -> Option<&V>
-    where
-        K: Borrow<Q>,
-        Q: Hash + Eq,
-    {
-        self.base.get(k)
-    }
-
-    pub fn get_mut<Q: ?Sized>(&mut self, k: &Q) -> Option<&mut V>
-    where
-        K: Borrow<Q>,
-        Q: Hash + Eq,
-    {
-        self.base.get_mut(k)
-    }
-
-    pub fn insert(&mut self, k: K, v: V) -> Option<V> {
-        self.base.insert(k, v)
-    }
-
-    pub fn remove<Q: ?Sized>(&mut self, k: &Q) -> Option<V>
-    where
-        K: Borrow<Q>,
-        Q: Hash + Eq,
-    {
-        self.base.remove(k)
-    }
-}
diff --git a/compiler/rustc_data_structures/src/stable_set.rs b/compiler/rustc_data_structures/src/stable_set.rs
deleted file mode 100644
index c7ca74f5fbd..00000000000
--- a/compiler/rustc_data_structures/src/stable_set.rs
+++ /dev/null
@@ -1,77 +0,0 @@
-pub use rustc_hash::FxHashSet;
-use std::borrow::Borrow;
-use std::fmt;
-use std::hash::Hash;
-
-/// A deterministic wrapper around FxHashSet that does not provide iteration support.
-///
-/// It supports insert, remove, get functions from FxHashSet.
-/// It also allows to convert hashset to a sorted vector with the method `into_sorted_vector()`.
-#[derive(Clone)]
-pub struct StableSet<T> {
-    base: FxHashSet<T>,
-}
-
-impl<T> Default for StableSet<T>
-where
-    T: Eq + Hash,
-{
-    fn default() -> StableSet<T> {
-        StableSet::new()
-    }
-}
-
-impl<T> fmt::Debug for StableSet<T>
-where
-    T: Eq + Hash + fmt::Debug,
-{
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "{:?}", self.base)
-    }
-}
-
-impl<T> PartialEq<StableSet<T>> for StableSet<T>
-where
-    T: Eq + Hash,
-{
-    fn eq(&self, other: &StableSet<T>) -> bool {
-        self.base == other.base
-    }
-}
-
-impl<T> Eq for StableSet<T> where T: Eq + Hash {}
-
-impl<T: Hash + Eq> StableSet<T> {
-    pub fn new() -> StableSet<T> {
-        StableSet { base: FxHashSet::default() }
-    }
-
-    pub fn into_sorted_vector(self) -> Vec<T>
-    where
-        T: Ord,
-    {
-        let mut vector = self.base.into_iter().collect::<Vec<_>>();
-        vector.sort_unstable();
-        vector
-    }
-
-    pub fn get<Q: ?Sized>(&self, value: &Q) -> Option<&T>
-    where
-        T: Borrow<Q>,
-        Q: Hash + Eq,
-    {
-        self.base.get(value)
-    }
-
-    pub fn insert(&mut self, value: T) -> bool {
-        self.base.insert(value)
-    }
-
-    pub fn remove<Q: ?Sized>(&mut self, value: &Q) -> bool
-    where
-        T: Borrow<Q>,
-        Q: Hash + Eq,
-    {
-        self.base.remove(value)
-    }
-}
diff --git a/compiler/rustc_data_structures/src/sync.rs b/compiler/rustc_data_structures/src/sync.rs
index cf0940df9e4..52952a7932d 100644
--- a/compiler/rustc_data_structures/src/sync.rs
+++ b/compiler/rustc_data_structures/src/sync.rs
@@ -146,7 +146,7 @@ cfg_if! {
             t.into_iter()
         }
 
-        pub fn par_for_each_in<T: IntoIterator>(t: T, for_each: impl Fn(T::Item) + Sync + Send) {
+        pub fn par_for_each_in<T: IntoIterator>(t: T, mut for_each: impl FnMut(T::Item) + Sync + Send) {
             // We catch panics here ensuring that all the loop iterations execute.
             // This makes behavior consistent with the parallel compiler.
             let mut panic = None;
diff --git a/compiler/rustc_driver/src/lib.rs b/compiler/rustc_driver/src/lib.rs
index b71cdad718a..53ae913f94f 100644
--- a/compiler/rustc_driver/src/lib.rs
+++ b/compiler/rustc_driver/src/lib.rs
@@ -29,7 +29,7 @@ use rustc_log::stdout_isatty;
 use rustc_metadata::locator;
 use rustc_save_analysis as save;
 use rustc_save_analysis::DumpHandler;
-use rustc_session::config::{nightly_options, CG_OPTIONS, DB_OPTIONS};
+use rustc_session::config::{nightly_options, CG_OPTIONS, Z_OPTIONS};
 use rustc_session::config::{ErrorOutputType, Input, OutputType, PrintRequest, TrimmedDefPaths};
 use rustc_session::cstore::MetadataLoader;
 use rustc_session::getopts;
@@ -123,8 +123,7 @@ impl Callbacks for TimePassesCallbacks {
     fn config(&mut self, config: &mut interface::Config) {
         // If a --prints=... option has been given, we don't print the "total"
         // time because it will mess up the --prints output. See #64339.
-        self.time_passes = config.opts.prints.is_empty()
-            && (config.opts.debugging_opts.time_passes || config.opts.debugging_opts.time);
+        self.time_passes = config.opts.prints.is_empty() && config.opts.time_passes();
         config.opts.trimmed_def_paths = TrimmedDefPaths::GoodPath;
     }
 }
@@ -248,8 +247,8 @@ fn run_compiler(
                     let sopts = &compiler.session().opts;
                     if sopts.describe_lints {
                         let mut lint_store = rustc_lint::new_lint_store(
-                            sopts.debugging_opts.no_interleave_lints,
-                            compiler.session().unstable_options(),
+                            sopts.unstable_opts.no_interleave_lints,
+                            compiler.session().enable_internal_lints(),
                         );
                         let registered_lints =
                             if let Some(register_lints) = compiler.register_lints() {
@@ -342,7 +341,7 @@ fn run_compiler(
                 return early_exit();
             }
 
-            if sess.opts.debugging_opts.parse_only || sess.opts.debugging_opts.show_span.is_some() {
+            if sess.opts.unstable_opts.parse_only || sess.opts.unstable_opts.show_span.is_some() {
                 return early_exit();
             }
 
@@ -371,13 +370,13 @@ fn run_compiler(
 
             queries.global_ctxt()?;
 
-            if sess.opts.debugging_opts.no_analysis {
+            if sess.opts.unstable_opts.no_analysis {
                 return early_exit();
             }
 
             queries.global_ctxt()?.peek_mut().enter(|tcx| {
                 let result = tcx.analysis(());
-                if sess.opts.debugging_opts.save_analysis {
+                if sess.opts.unstable_opts.save_analysis {
                     let crate_name = queries.crate_name()?.peek().clone();
                     sess.time("save_analysis", || {
                         save::process_crate(
@@ -401,7 +400,7 @@ fn run_compiler(
 
             queries.ongoing_codegen()?;
 
-            if sess.opts.debugging_opts.print_type_sizes {
+            if sess.opts.unstable_opts.print_type_sizes {
                 sess.code_stats.print_type_sizes();
             }
 
@@ -414,14 +413,14 @@ fn run_compiler(
             linker.link()?
         }
 
-        if sess.opts.debugging_opts.perf_stats {
+        if sess.opts.unstable_opts.perf_stats {
             sess.print_perf_stats();
         }
 
-        if sess.opts.debugging_opts.print_fuel.is_some() {
+        if sess.opts.unstable_opts.print_fuel.is_some() {
             eprintln!(
                 "Fuel used by {}: {}",
-                sess.opts.debugging_opts.print_fuel.as_ref().unwrap(),
+                sess.opts.unstable_opts.print_fuel.as_ref().unwrap(),
                 sess.print_fuel.load(SeqCst)
             );
         }
@@ -576,7 +575,7 @@ fn show_content_with_pager(content: &str) {
 }
 
 pub fn try_process_rlink(sess: &Session, compiler: &interface::Compiler) -> Compilation {
-    if sess.opts.debugging_opts.link_only {
+    if sess.opts.unstable_opts.link_only {
         if let Input::File(file) = compiler.input() {
             // FIXME: #![crate_type] and #![crate_name] support not implemented yet
             sess.init_crate_types(collect_crate_types(sess, &[]));
@@ -606,7 +605,7 @@ pub fn list_metadata(
     metadata_loader: &dyn MetadataLoader,
     input: &Input,
 ) -> Compilation {
-    if sess.opts.debugging_opts.ls {
+    if sess.opts.unstable_opts.ls {
         match *input {
             Input::File(ref ifile) => {
                 let path = &(*ifile);
@@ -928,7 +927,7 @@ Available lint options:
 
 fn describe_debug_flags() {
     println!("\nAvailable options:\n");
-    print_flag_list("-Z", config::DB_OPTIONS);
+    print_flag_list("-Z", config::Z_OPTIONS);
 }
 
 fn describe_codegen_flags() {
@@ -1000,7 +999,7 @@ pub fn handle_options(args: &[String]) -> Option<getopts::Matches> {
             getopts::Fail::UnrecognizedOption(ref opt) => CG_OPTIONS
                 .iter()
                 .map(|&(name, ..)| ('C', name))
-                .chain(DB_OPTIONS.iter().map(|&(name, ..)| ('Z', name)))
+                .chain(Z_OPTIONS.iter().map(|&(name, ..)| ('Z', name)))
                 .find(|&(_, name)| *opt == name.replace('_', "-"))
                 .map(|(flag, _)| format!("{}. Did you mean `-{} {}`?", e, flag, opt)),
             _ => None,
diff --git a/compiler/rustc_error_codes/src/error_codes.rs b/compiler/rustc_error_codes/src/error_codes.rs
index d507293ccb0..854625579ee 100644
--- a/compiler/rustc_error_codes/src/error_codes.rs
+++ b/compiler/rustc_error_codes/src/error_codes.rs
@@ -492,6 +492,7 @@ E0785: include_str!("./error_codes/E0785.md"),
 E0786: include_str!("./error_codes/E0786.md"),
 E0787: include_str!("./error_codes/E0787.md"),
 E0788: include_str!("./error_codes/E0788.md"),
+E0790: include_str!("./error_codes/E0790.md"),
 ;
 //  E0006, // merged with E0005
 //  E0008, // cannot bind by-move into a pattern guard
@@ -558,7 +559,7 @@ E0788: include_str!("./error_codes/E0788.md"),
 //  E0273, // on_unimplemented #1
 //  E0274, // on_unimplemented #2
 //  E0278, // requirement is not satisfied
-    E0279, // requirement is not satisfied
+//  E0279,
     E0280, // requirement is not satisfied
 //  E0285, // overflow evaluation builtin bounds
 //  E0296, // replaced with a generic attribute input check
@@ -644,4 +645,5 @@ E0788: include_str!("./error_codes/E0788.md"),
 //  E0721, // `await` keyword
 //  E0723, // unstable feature in `const` context
 //  E0738, // Removed; errored on `#[track_caller] fn`s in `extern "Rust" { ... }`.
+    E0789, // rustc_allowed_through_unstable_modules without stability attribute
 }
diff --git a/compiler/rustc_error_codes/src/error_codes/E0118.md b/compiler/rustc_error_codes/src/error_codes/E0118.md
index 8033aa8384c..cfabae1a634 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0118.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0118.md
@@ -4,7 +4,7 @@ enum, union, or trait object.
 Erroneous code example:
 
 ```compile_fail,E0118
-impl fn(u8) { // error: no nominal type found for inherent implementation
+impl<T> T { // error: no nominal type found for inherent implementation
     fn get_state(&self) -> String {
         // ...
     }
@@ -20,8 +20,8 @@ trait LiveLongAndProsper {
     fn get_state(&self) -> String;
 }
 
-// and now you can implement it on fn(u8)
-impl LiveLongAndProsper for fn(u8) {
+// and now you can implement it on T
+impl<T> LiveLongAndProsper for T {
     fn get_state(&self) -> String {
         "He's dead, Jim!".to_owned()
     }
@@ -33,9 +33,9 @@ For example, `NewType` is a newtype over `Foo` in `struct NewType(Foo)`.
 Example:
 
 ```
-struct TypeWrapper(fn(u8));
+struct TypeWrapper<T>(T);
 
-impl TypeWrapper {
+impl<T> TypeWrapper<T> {
     fn get_state(&self) -> String {
         "Fascinating!".to_owned()
     }
diff --git a/compiler/rustc_error_codes/src/error_codes/E0283.md b/compiler/rustc_error_codes/src/error_codes/E0283.md
index 6885f9a486d..79d2c8204f9 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0283.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0283.md
@@ -3,48 +3,27 @@ An implementation cannot be chosen unambiguously because of lack of information.
 Erroneous code example:
 
 ```compile_fail,E0283
-trait Generator {
-    fn create() -> u32;
-}
-
-struct Impl;
-
-impl Generator for Impl {
-    fn create() -> u32 { 1 }
-}
-
-struct AnotherImpl;
+struct Foo;
 
-impl Generator for AnotherImpl {
-    fn create() -> u32 { 2 }
+impl Into<u32> for Foo {
+    fn into(self) -> u32 { 1 }
 }
 
-fn main() {
-    let cont: u32 = Generator::create();
-    // error, impossible to choose one of Generator trait implementation
-    // Should it be Impl or AnotherImpl, maybe something else?
-}
+let foo = Foo;
+let bar: u32 = foo.into() * 1u32;
 ```
 
 This error can be solved by adding type annotations that provide the missing
-information to the compiler. In this case, the solution is to use a concrete
-type:
+information to the compiler. In this case, the solution is to specify the
+trait's type parameter:
 
 ```
-trait Generator {
-    fn create() -> u32;
-}
-
-struct AnotherImpl;
+struct Foo;
 
-impl Generator for AnotherImpl {
-    fn create() -> u32 { 2 }
+impl Into<u32> for Foo {
+    fn into(self) -> u32 { 1 }
 }
 
-fn main() {
-    let gen1 = AnotherImpl::create();
-
-    // if there are multiple methods with same name (different traits)
-    let gen2 = <AnotherImpl as Generator>::create();
-}
+let foo = Foo;
+let bar: u32 = Into::<u32>::into(foo) * 1u32;
 ```
diff --git a/compiler/rustc_error_codes/src/error_codes/E0790.md b/compiler/rustc_error_codes/src/error_codes/E0790.md
new file mode 100644
index 00000000000..2aee9dfbdbd
--- /dev/null
+++ b/compiler/rustc_error_codes/src/error_codes/E0790.md
@@ -0,0 +1,47 @@
+You need to specify a specific implementation of the trait in order to call the
+method.
+
+Erroneous code example:
+
+```compile_fail,E0790
+trait Generator {
+    fn create() -> u32;
+}
+
+struct Impl;
+
+impl Generator for Impl {
+    fn create() -> u32 { 1 }
+}
+
+struct AnotherImpl;
+
+impl Generator for AnotherImpl {
+    fn create() -> u32 { 2 }
+}
+
+let cont: u32 = Generator::create();
+// error, impossible to choose one of Generator trait implementation
+// Should it be Impl or AnotherImpl, maybe something else?
+```
+
+This error can be solved by adding type annotations that provide the missing
+information to the compiler. In this case, the solution is to use a concrete
+type:
+
+```
+trait Generator {
+    fn create() -> u32;
+}
+
+struct AnotherImpl;
+
+impl Generator for AnotherImpl {
+    fn create() -> u32 { 2 }
+}
+
+let gen1 = AnotherImpl::create();
+
+// if there are multiple methods with same name (different traits)
+let gen2 = <AnotherImpl as Generator>::create();
+```
diff --git a/compiler/rustc_error_messages/locales/en-US/lint.ftl b/compiler/rustc_error_messages/locales/en-US/lint.ftl
index e7e07093c03..55e96e58e46 100644
--- a/compiler/rustc_error_messages/locales/en-US/lint.ftl
+++ b/compiler/rustc_error_messages/locales/en-US/lint.ftl
@@ -247,11 +247,6 @@ lint-atomic-ordering-invalid = `{$method}`'s failure ordering may not be `Releas
     .label = invalid failure ordering
     .help = consider using `Acquire` or `Relaxed` failure ordering instead
 
-lint-atomic-ordering-invalid-fail-success = `{$method}`'s success ordering must be at least as strong as its failure ordering
-    .fail-label = `{$fail_ordering}` failure ordering
-    .success-label = `{$success_ordering}` success ordering
-    .suggestion = consider using `{$success_suggestion}` success ordering instead
-
 lint-unused-op = unused {$op} that must be used
     .label = the {$op} produces a value
     .suggestion = use `let _ = ...` to ignore the resulting value
diff --git a/compiler/rustc_error_messages/locales/en-US/passes.ftl b/compiler/rustc_error_messages/locales/en-US/passes.ftl
new file mode 100644
index 00000000000..b17eb9c2d26
--- /dev/null
+++ b/compiler/rustc_error_messages/locales/en-US/passes.ftl
@@ -0,0 +1,264 @@
+-passes-previously-accepted =
+    this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+
+-passes-see-issue =
+    see issue #{$issue} <https://github.com/rust-lang/rust/issues/{$issue}> for more information
+
+passes-outer-crate-level-attr =
+    crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
+
+passes-inner-crate-level-attr =
+    crate-level attribute should be in the root module
+
+passes-ignored-attr-with-macro = `#[{$sym}]` is ignored on struct fields, match arms and macro defs
+    .warn = {-passes-previously-accepted}
+    .note = {-passes-see-issue(issue: "80564")}
+
+passes-ignored-attr = `#[{$sym}]` is ignored on struct fields and match arms
+    .warn = {-passes-previously-accepted}
+    .note = {-passes-see-issue(issue: "80564")}
+
+passes-inline-ignored-function-prototype = `#[inline]` is ignored on function prototypes
+
+passes-inline-ignored-constants = `#[inline]` is ignored on constants
+    .warn = {-passes-previously-accepted}
+    .note = {-passes-see-issue(issue: "65833")}
+
+passes-inline-not-fn-or-closure = attribute should be applied to function or closure
+    .label = not a function or closure
+
+passes-no-coverage-ignored-function-prototype = `#[no_coverage]` is ignored on function prototypes
+
+passes-no-coverage-propagate =
+    `#[no_coverage]` does not propagate into items and must be applied to the contained functions directly
+
+passes-no-coverage-fn-defn = `#[no_coverage]` may only be applied to function definitions
+
+passes-no-coverage-not-coverable = `#[no_coverage]` must be applied to coverable code
+    .label = not coverable code
+
+passes-should-be-applied-to-fn = attribute should be applied to a function definition
+    .label = not a function definition
+
+passes-naked-tracked-caller = cannot use `#[track_caller]` with `#[naked]`
+
+passes-should-be-applied-to-struct-enum = attribute should be applied to a struct or enum
+    .label = not a struct or enum
+
+passes-should-be-applied-to-trait = attribute should be applied to a trait
+    .label = not a trait
+
+passes-target-feature-on-statement = {passes-should-be-applied-to-fn}
+    .warn = {-passes-previously-accepted}
+    .label = {passes-should-be-applied-to-fn.label}
+
+passes-should-be-applied-to-static = attribute should be applied to a static
+    .label = not a static
+
+passes-doc-expect-str = doc {$attr_name} attribute expects a string: #[doc({$attr_name} = "a")]
+
+passes-doc-alias-empty = {$attr_str} attribute cannot have empty value
+
+passes-doc-alias-bad-char = {$char_} character isn't allowed in {$attr_str}
+
+passes-doc-alias-start-end = {$attr_str} cannot start or end with ' '
+
+passes-doc-alias-bad-location = {$attr_str} isn't allowed on {$location}
+
+passes-doc-alias-not-an-alias = {$attr_str} is the same as the item's name
+
+passes-doc-alias-duplicated = doc alias is duplicated
+    .label = first defined here
+
+passes-doc-alias-not-string-literal = `#[doc(alias("a"))]` expects string literals
+
+passes-doc-alias-malformed =
+    doc alias attribute expects a string `#[doc(alias = "a")]` or a list of strings `#[doc(alias("a", "b"))]`
+
+passes-doc-keyword-empty-mod = `#[doc(keyword = "...")]` should be used on empty modules
+
+passes-doc-keyword-not-mod = `#[doc(keyword = "...")]` should be used on modules
+
+passes-doc-keyword-invalid-ident = `{$doc_keyword}` is not a valid identifier
+
+passes-doc-fake-variadic-not-valid =
+    `#[doc(fake_variadic)]` must be used on the first of a set of tuple or fn pointer trait impls with varying arity
+
+passes-doc-keyword-only-impl = `#[doc(keyword = "...")]` should be used on impl blocks
+
+passes-doc-inline-conflict-first = this attribute...
+passes-doc-inline-conflict-second = ...conflicts with this attribute
+passes-doc-inline-conflict = conflicting doc inlining attributes
+    .help = remove one of the conflicting attributes
+
+passes-doc-inline-only-use = this attribute can only be applied to a `use` item
+    .label = only applicable on `use` items
+    .not-a-use-item-label = not a `use` item
+    .note = read <https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#inline-and-no_inline> for more information
+
+passes-doc-attr-not-crate-level =
+    `#![doc({$attr_name} = "...")]` isn't allowed as a crate-level attribute
+
+passes-attr-crate-level = this attribute can only be applied at the crate level
+    .suggestion = to apply to the crate, use an inner attribute
+    .help = to apply to the crate, use an inner attribute
+    .note = read <https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#at-the-crate-level> for more information
+
+passes-doc-test-unknown = unknown `doc(test)` attribute `{$path}`
+
+passes-doc-test-takes-list = `#[doc(test(...)]` takes a list of attributes
+
+passes-doc-primitive = `doc(primitive)` should never have been stable
+
+passes-doc-test-unknown-any = unknown `doc` attribute `{$path}`
+
+passes-doc-test-unknown-spotlight = unknown `doc` attribute `{$path}`
+    .note = `doc(spotlight)` was renamed to `doc(notable_trait)`
+    .suggestion = use `notable_trait` instead
+    .no-op-note = `doc(spotlight)` is now a no-op
+
+passes-doc-test-unknown-include = unknown `doc` attribute `{$path}`
+    .suggestion = use `doc = include_str!` instead
+
+passes-doc-invalid = invalid `doc` attribute
+
+passes-pass-by-value = `pass_by_value` attribute should be applied to a struct, enum or type alias
+    .label = is not a struct, enum or type alias
+
+passes-allow-incoherent-impl =
+    `rustc_allow_incoherent_impl` attribute should be applied to impl items.
+    .label = the only currently supported targets are inherent methods
+
+passes-has-incoherent-inherent-impl =
+    `rustc_has_incoherent_inherent_impls` attribute should be applied to types or traits.
+    .label = only adts, extern types and traits are supported
+
+passes-must-use-async =
+    `must_use` attribute on `async` functions applies to the anonymous `Future` returned by the function, not the value within
+    .label = this attribute does nothing, the `Future`s returned by async functions are already `must_use`
+
+passes-must-use-no-effect = `#[must_use]` has no effect when applied to {$article} {$target}
+
+passes-must-not-suspend = `must_not_suspend` attribute should be applied to a struct, enum, or trait
+    .label = is not a struct, enum, or trait
+
+passes-cold = {passes-should-be-applied-to-fn}
+    .warn = {-passes-previously-accepted}
+    .label = {passes-should-be-applied-to-fn.label}
+
+passes-link = attribute should be applied to an `extern` block with non-Rust ABI
+    .warn = {-passes-previously-accepted}
+    .label = not an `extern` block
+
+passes-link-name = attribute should be applied to a foreign function or static
+    .warn = {-passes-previously-accepted}
+    .label = not a foreign function or static
+    .help = try `#[link(name = "{$value}")]` instead
+
+passes-no-link = attribute should be applied to an `extern crate` item
+    .label = not an `extern crate` item
+
+passes-export-name = attribute should be applied to a free function, impl method or static
+    .label = not a free function, impl method or static
+
+passes-rustc-layout-scalar-valid-range-not-struct = attribute should be applied to a struct
+    .label = not a struct
+
+passes-rustc-layout-scalar-valid-range-arg = expected exactly one integer literal argument
+
+passes-rustc-legacy-const-generics-only = #[rustc_legacy_const_generics] functions must only have const generics
+    .label = non-const generic parameter
+
+passes-rustc-legacy-const-generics-index = #[rustc_legacy_const_generics] must have one index for each generic parameter
+    .label = generic parameters
+
+passes-rustc-legacy-const-generics-index-exceed = index exceeds number of arguments
+    .label = there {$arg_count ->
+        [one] is
+        *[other] are
+    } only {$arg_count} {$arg_count ->
+        [one] argument
+        *[other] arguments
+    }
+
+passes-rustc-legacy-const-generics-index-negative = arguments should be non-negative integers
+
+passes-rustc-dirty-clean = attribute requires -Z query-dep-graph to be enabled
+
+passes-link-section = attribute should be applied to a function or static
+    .warn = {-passes-previously-accepted}
+    .label = not a function or static
+
+passes-no-mangle-foreign = `#[no_mangle]` has no effect on a foreign {$foreign_item_kind}
+    .warn = {-passes-previously-accepted}
+    .label = foreign {$foreign_item_kind}
+    .note = symbol names in extern blocks are not mangled
+    .suggestion = remove this attribute
+
+passes-no-mangle = attribute should be applied to a free function, impl method or static
+    .warn = {-passes-previously-accepted}
+    .label = not a free function, impl method or static
+
+passes-repr-ident = meta item in `repr` must be an identifier
+
+passes-repr-conflicting = conflicting representation hints
+
+passes-used-static = attribute must be applied to a `static` variable
+
+passes-used-compiler-linker = `used(compiler)` and `used(linker)` can't be used together
+
+passes-allow-internal-unstable = attribute should be applied to a macro
+    .label = not a macro
+
+passes-debug-visualizer-placement = attribute should be applied to a module
+
+passes-debug-visualizer-invalid = invalid argument
+    .note-1 = expected: `natvis_file = "..."`
+    .note-2 = OR
+    .note-3 = expected: `gdb_script_file = "..."`
+
+passes-rustc-allow-const-fn-unstable = attribute should be applied to `const fn`
+    .label = not a `const fn`
+
+passes-rustc-std-internal-symbol = attribute should be applied to functions or statics
+    .label = not a function or static
+
+passes-const-trait = attribute should be applied to a trait
+
+passes-stability-promotable = attribute cannot be applied to an expression
+
+passes-deprecated = attribute is ignored here
+
+passes-macro-use = `#[{$name}]` only has an effect on `extern crate` and modules
+
+passes-macro-export = `#[macro_export]` only has an effect on macro definitions
+
+passes-plugin-registrar = `#[plugin_registrar]` only has an effect on functions
+
+passes-unused-empty-lints-note = attribute `{$name}` with an empty list has no effect
+
+passes-unused-no-lints-note = attribute `{$name}` without any lints has no effect
+
+passes-unused-default-method-body-const-note =
+    `default_method_body_is_const` has been replaced with `#[const_trait]` on traits
+
+passes-unused = unused attribute
+    .suggestion = remove this attribute
+
+passes-non-exported-macro-invalid-attrs = attribute should be applied to function or closure
+    .label = not a function or closure
+
+passes-unused-duplicate = unused attribute
+    .suggestion = remove this attribute
+    .note = attribute also specified here
+    .warn = {-passes-previously-accepted}
+
+passes-unused-multiple = multiple `{$name}` attributes
+    .suggestion = remove this attribute
+    .note = attribute also specified here
+
+passes-rustc-lint-opt-ty = `#[rustc_lint_opt_ty]` should be applied to a struct
+    .label = not a struct
+
+passes-rustc-lint-opt-deny-field-access = `#[rustc_lint_opt_deny_field_access]` should be applied to a field
+    .label = not a field
diff --git a/compiler/rustc_error_messages/locales/en-US/privacy.ftl b/compiler/rustc_error_messages/locales/en-US/privacy.ftl
index 2b0778f48ca..f8a750da93f 100644
--- a/compiler/rustc_error_messages/locales/en-US/privacy.ftl
+++ b/compiler/rustc_error_messages/locales/en-US/privacy.ftl
@@ -10,3 +10,12 @@ privacy-unnamed-item-is-private = {$kind} is private
 privacy-in-public-interface = {$vis_descr} {$kind} `{$descr}` in public interface
     .label = can't leak {$vis_descr} {$kind}
     .visibility-label = `{$descr}` declared as {$vis_descr}
+
+privacy-from-private-dep-in-public-interface =
+    {$kind} `{$descr}` from private dependency '{$krate}' in public interface
+
+private-in-public-lint =
+    {$vis_descr} {$kind} `{$descr}` in public interface (error {$kind ->
+        [trait] E0445
+        *[other] E0446
+    })
diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs
index d16171cb162..2ac5c1960cd 100644
--- a/compiler/rustc_error_messages/src/lib.rs
+++ b/compiler/rustc_error_messages/src/lib.rs
@@ -1,4 +1,4 @@
-#![feature(let_chains)]
+#![cfg_attr(bootstrap, feature(let_chains))]
 #![feature(once_cell)]
 #![feature(rustc_attrs)]
 #![feature(type_alias_impl_trait)]
@@ -37,6 +37,7 @@ fluent_messages! {
     expand => "../locales/en-US/expand.ftl",
     lint => "../locales/en-US/lint.ftl",
     parser => "../locales/en-US/parser.ftl",
+    passes => "../locales/en-US/passes.ftl",
     privacy => "../locales/en-US/privacy.ftl",
     typeck => "../locales/en-US/typeck.ftl",
 }
@@ -298,7 +299,7 @@ impl DiagnosticMessage {
     /// - If `self` is non-translatable then return `self`'s message.
     pub fn with_subdiagnostic_message(&self, sub: SubdiagnosticMessage) -> Self {
         let attr = match sub {
-            SubdiagnosticMessage::Str(s) => return DiagnosticMessage::Str(s.clone()),
+            SubdiagnosticMessage::Str(s) => return DiagnosticMessage::Str(s),
             SubdiagnosticMessage::FluentIdentifier(id) => {
                 return DiagnosticMessage::FluentIdentifier(id, None);
             }
diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs
index da321c45875..2a4f609a2d8 100644
--- a/compiler/rustc_errors/src/diagnostic.rs
+++ b/compiler/rustc_errors/src/diagnostic.rs
@@ -3,7 +3,7 @@ use crate::{
     CodeSuggestion, DiagnosticMessage, EmissionGuarantee, Level, LintDiagnosticBuilder, MultiSpan,
     SubdiagnosticMessage, Substitution, SubstitutionPart, SuggestionStyle,
 };
-use rustc_data_structures::stable_map::FxHashMap;
+use rustc_data_structures::fx::FxHashMap;
 use rustc_error_messages::FluentValue;
 use rustc_hir as hir;
 use rustc_lint_defs::{Applicability, LintExpectationId};
@@ -40,6 +40,35 @@ pub trait IntoDiagnosticArg {
     fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static>;
 }
 
+macro_rules! into_diagnostic_arg_using_display {
+    ($( $ty:ty ),+ $(,)?) => {
+        $(
+            impl IntoDiagnosticArg for $ty {
+                fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
+                    self.to_string().into_diagnostic_arg()
+                }
+            }
+        )+
+    }
+}
+
+into_diagnostic_arg_using_display!(
+    i8,
+    u8,
+    i16,
+    u16,
+    i32,
+    u32,
+    i64,
+    u64,
+    i128,
+    u128,
+    std::num::NonZeroU32,
+    hir::Target,
+    Edition,
+    Ident,
+);
+
 impl IntoDiagnosticArg for bool {
     fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
         if self {
@@ -50,81 +79,9 @@ impl IntoDiagnosticArg for bool {
     }
 }
 
-impl IntoDiagnosticArg for i8 {
-    fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
-        DiagnosticArgValue::Str(Cow::Owned(self.to_string()))
-    }
-}
-
-impl IntoDiagnosticArg for u8 {
-    fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
-        DiagnosticArgValue::Str(Cow::Owned(self.to_string()))
-    }
-}
-
-impl IntoDiagnosticArg for i16 {
-    fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
-        DiagnosticArgValue::Str(Cow::Owned(self.to_string()))
-    }
-}
-
-impl IntoDiagnosticArg for u16 {
-    fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
-        DiagnosticArgValue::Str(Cow::Owned(self.to_string()))
-    }
-}
-
-impl IntoDiagnosticArg for i32 {
-    fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
-        DiagnosticArgValue::Str(Cow::Owned(self.to_string()))
-    }
-}
-
-impl IntoDiagnosticArg for u32 {
-    fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
-        DiagnosticArgValue::Str(Cow::Owned(self.to_string()))
-    }
-}
-
-impl IntoDiagnosticArg for i64 {
-    fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
-        DiagnosticArgValue::Str(Cow::Owned(self.to_string()))
-    }
-}
-
-impl IntoDiagnosticArg for u64 {
-    fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
-        DiagnosticArgValue::Str(Cow::Owned(self.to_string()))
-    }
-}
-
-impl IntoDiagnosticArg for i128 {
-    fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
-        DiagnosticArgValue::Str(Cow::Owned(self.to_string()))
-    }
-}
-
-impl IntoDiagnosticArg for u128 {
-    fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
-        DiagnosticArgValue::Str(Cow::Owned(self.to_string()))
-    }
-}
-
-impl IntoDiagnosticArg for String {
+impl IntoDiagnosticArg for char {
     fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
-        DiagnosticArgValue::Str(Cow::Owned(self))
-    }
-}
-
-impl IntoDiagnosticArg for std::num::NonZeroU32 {
-    fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
-        DiagnosticArgValue::Str(Cow::Owned(self.to_string()))
-    }
-}
-
-impl IntoDiagnosticArg for Edition {
-    fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
-        DiagnosticArgValue::Str(Cow::Owned(self.to_string()))
+        DiagnosticArgValue::Str(Cow::Owned(format!("{:?}", self)))
     }
 }
 
@@ -134,15 +91,15 @@ impl IntoDiagnosticArg for Symbol {
     }
 }
 
-impl IntoDiagnosticArg for Ident {
+impl<'a> IntoDiagnosticArg for &'a str {
     fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
         self.to_string().into_diagnostic_arg()
     }
 }
 
-impl<'a> IntoDiagnosticArg for &'a str {
+impl IntoDiagnosticArg for String {
     fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
-        self.to_string().into_diagnostic_arg()
+        DiagnosticArgValue::Str(Cow::Owned(self))
     }
 }
 
@@ -433,18 +390,17 @@ impl Diagnostic {
         expected: DiagnosticStyledString,
         found: DiagnosticStyledString,
     ) -> &mut Self {
-        let mut msg: Vec<_> =
-            vec![("required when trying to coerce from type `".to_string(), Style::NoStyle)];
+        let mut msg: Vec<_> = vec![("required when trying to coerce from type `", Style::NoStyle)];
         msg.extend(expected.0.iter().map(|x| match *x {
-            StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle),
-            StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight),
+            StringPart::Normal(ref s) => (s.as_str(), Style::NoStyle),
+            StringPart::Highlighted(ref s) => (s.as_str(), Style::Highlight),
         }));
-        msg.push(("` to type '".to_string(), Style::NoStyle));
+        msg.push(("` to type '", Style::NoStyle));
         msg.extend(found.0.iter().map(|x| match *x {
-            StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle),
-            StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight),
+            StringPart::Normal(ref s) => (s.as_str(), Style::NoStyle),
+            StringPart::Highlighted(ref s) => (s.as_str(), Style::Highlight),
         }));
-        msg.push(("`".to_string(), Style::NoStyle));
+        msg.push(("`", Style::NoStyle));
 
         // For now, just attach these as notes
         self.highlighted_note(msg);
@@ -496,7 +452,7 @@ impl Diagnostic {
         self
     }
 
-    pub fn note_trait_signature(&mut self, name: String, signature: String) -> &mut Self {
+    pub fn note_trait_signature(&mut self, name: Symbol, signature: String) -> &mut Self {
         self.highlighted_note(vec![
             (format!("`{}` from trait: `", name), Style::NoStyle),
             (signature, Style::Highlight),
diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs
index 99ac6a3546e..9e68ee282e6 100644
--- a/compiler/rustc_errors/src/diagnostic_builder.rs
+++ b/compiler/rustc_errors/src/diagnostic_builder.rs
@@ -595,6 +595,7 @@ macro_rules! error_code {
 pub struct LintDiagnosticBuilder<'a, G: EmissionGuarantee>(DiagnosticBuilder<'a, G>);
 
 impl<'a, G: EmissionGuarantee> LintDiagnosticBuilder<'a, G> {
+    #[rustc_lint_diagnostics]
     /// Return the inner `DiagnosticBuilder`, first setting the primary message to `msg`.
     pub fn build(mut self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'a, G> {
         self.0.set_primary_message(msg);
diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs
index 85ea8eb3937..61d953cd6f1 100644
--- a/compiler/rustc_errors/src/emitter.rs
+++ b/compiler/rustc_errors/src/emitter.rs
@@ -399,11 +399,11 @@ pub trait Emitter {
     ) {
         // Check for spans in macros, before `fix_multispans_in_extern_macros`
         // has a chance to replace them.
-        let has_macro_spans = iter::once(&*span)
+        let has_macro_spans: Vec<_> = iter::once(&*span)
             .chain(children.iter().map(|child| &child.span))
             .flat_map(|span| span.primary_spans())
             .flat_map(|sp| sp.macro_backtrace())
-            .find_map(|expn_data| {
+            .filter_map(|expn_data| {
                 match expn_data.kind {
                     ExpnKind::Root => None,
 
@@ -413,7 +413,8 @@ pub trait Emitter {
 
                     ExpnKind::Macro(macro_kind, name) => Some((macro_kind, name)),
                 }
-            });
+            })
+            .collect();
 
         if !backtrace {
             self.fix_multispans_in_extern_macros(source_map, span, children);
@@ -422,11 +423,22 @@ pub trait Emitter {
         self.render_multispans_macro_backtrace(span, children, backtrace);
 
         if !backtrace {
-            if let Some((macro_kind, name)) = has_macro_spans {
-                let descr = macro_kind.descr();
+            if let Some((macro_kind, name)) = has_macro_spans.first() {
+                // Mark the actual macro this originates from
+                let and_then = if let Some((macro_kind, last_name)) = has_macro_spans.last()
+                    && last_name != name
+                {
+                    let descr = macro_kind.descr();
+                    format!(
+                        " which comes from the expansion of the {descr} `{last_name}`",
+                    )
+                } else {
+                    "".to_string()
+                };
 
+                let descr = macro_kind.descr();
                 let msg = format!(
-                    "this {level} originates in the {descr} `{name}` \
+                    "this {level} originates in the {descr} `{name}`{and_then} \
                     (in Nightly builds, run with -Z macro-backtrace for more info)",
                 );
 
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs
index e59a74e380a..b173ac0e916 100644
--- a/compiler/rustc_errors/src/lib.rs
+++ b/compiler/rustc_errors/src/lib.rs
@@ -6,6 +6,7 @@
 #![feature(drain_filter)]
 #![feature(backtrace)]
 #![feature(if_let_guard)]
+#![cfg_attr(bootstrap, feature(let_chains))]
 #![feature(let_else)]
 #![feature(never_type)]
 #![feature(adt_const_params)]
diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs
index bfe2d773788..6e093811fcf 100644
--- a/compiler/rustc_expand/src/base.rs
+++ b/compiler/rustc_expand/src/base.rs
@@ -8,11 +8,11 @@ use rustc_ast::tokenstream::TokenStream;
 use rustc_ast::visit::{AssocCtxt, Visitor};
 use rustc_ast::{self as ast, Attribute, HasAttrs, Item, NodeId, PatKind};
 use rustc_attr::{self as attr, Deprecation, Stability};
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
 use rustc_data_structures::sync::{self, Lrc};
 use rustc_errors::{Applicability, DiagnosticBuilder, ErrorGuaranteed, MultiSpan, PResult};
 use rustc_lint_defs::builtin::PROC_MACRO_BACK_COMPAT;
-use rustc_lint_defs::BuiltinLintDiagnostics;
+use rustc_lint_defs::{BufferedEarlyLint, BuiltinLintDiagnostics};
 use rustc_parse::{self, parser, MACRO_ARGUMENTS};
 use rustc_session::{parse::ParseSess, Limit, Session, SessionDiagnostic};
 use rustc_span::def_id::{CrateNum, DefId, LocalDefId};
@@ -985,9 +985,11 @@ pub struct ExtCtxt<'a> {
     /// Error recovery mode entered when expansion is stuck
     /// (or during eager expansion, but that's a hack).
     pub force_mode: bool,
-    pub expansions: FxHashMap<Span, Vec<String>>,
+    pub expansions: FxIndexMap<Span, Vec<String>>,
     /// Used for running pre-expansion lints on freshly loaded modules.
     pub(super) lint_store: LintStoreExpandDyn<'a>,
+    /// Used for storing lints generated during expansion, like `NAMED_ARGUMENTS_USED_POSITIONALLY`
+    pub buffered_early_lint: Vec<BufferedEarlyLint>,
     /// When we 'expand' an inert attribute, we leave it
     /// in the AST, but insert it here so that we know
     /// not to expand it again.
@@ -1018,8 +1020,9 @@ impl<'a> ExtCtxt<'a> {
                 is_trailing_mac: false,
             },
             force_mode: false,
-            expansions: FxHashMap::default(),
+            expansions: FxIndexMap::default(),
             expanded_inert_attrs: MarkedAttrs::new(),
+            buffered_early_lint: vec![],
         }
     }
 
diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs
index 74e9bbeeeaf..fa3e2a4a5b8 100644
--- a/compiler/rustc_expand/src/build.rs
+++ b/compiler/rustc_expand/src/build.rs
@@ -520,6 +520,7 @@ impl<'a> ExtCtxt<'a> {
         self.expr(
             span,
             ast::ExprKind::Closure(
+                ast::ClosureBinder::NotPresent,
                 ast::CaptureBy::Ref,
                 ast::Async::No,
                 ast::Movability::Movable,
diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs
index 3cada372570..ccc29adc015 100644
--- a/compiler/rustc_expand/src/config.rs
+++ b/compiler/rustc_expand/src/config.rs
@@ -129,7 +129,7 @@ fn get_features(
                         .span_suggestion(
                             mi.span(),
                             "expected just one word",
-                            format!("{}", ident.name),
+                            ident.name,
                             Applicability::MaybeIncorrect,
                         )
                         .emit();
@@ -171,7 +171,7 @@ fn get_features(
                 continue;
             }
 
-            if let Some(allowed) = sess.opts.debugging_opts.allow_features.as_ref() {
+            if let Some(allowed) = sess.opts.unstable_opts.allow_features.as_ref() {
                 if allowed.iter().all(|f| name.as_str() != f) {
                     struct_span_err!(
                         span_handler,
diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs
index 978f87b1d13..93eeca5b289 100644
--- a/compiler/rustc_expand/src/expand.rs
+++ b/compiler/rustc_expand/src/expand.rs
@@ -564,7 +564,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                 .resolver
                 .visit_ast_fragment_with_placeholders(self.cx.current_expansion.id, &fragment);
 
-            if self.cx.sess.opts.debugging_opts.incremental_relative_spans {
+            if self.cx.sess.opts.unstable_opts.incremental_relative_spans {
                 for (invoc, _) in invocations.iter_mut() {
                     let expn_id = invoc.expansion_data.id;
                     let parent_def = self.cx.resolver.invocation_parent(expn_id);
diff --git a/compiler/rustc_expand/src/lib.rs b/compiler/rustc_expand/src/lib.rs
index c18147592dc..20d01b6dc26 100644
--- a/compiler/rustc_expand/src/lib.rs
+++ b/compiler/rustc_expand/src/lib.rs
@@ -1,9 +1,8 @@
-#![allow(rustc::potential_query_instability)]
 #![feature(array_windows)]
 #![feature(associated_type_bounds)]
 #![feature(associated_type_defaults)]
 #![feature(if_let_guard)]
-#![feature(let_chains)]
+#![cfg_attr(bootstrap, feature(let_chains))]
 #![feature(let_else)]
 #![feature(macro_metavar_expr)]
 #![feature(proc_macro_diagnostic)]
diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs
index 3e9ddd6aec0..3d44c408d8f 100644
--- a/compiler/rustc_expand/src/mbe/macro_rules.rs
+++ b/compiler/rustc_expand/src/mbe/macro_rules.rs
@@ -13,7 +13,7 @@ use rustc_ast::tokenstream::{DelimSpan, TokenStream};
 use rustc_ast::{NodeId, DUMMY_NODE_ID};
 use rustc_ast_pretty::pprust;
 use rustc_attr::{self as attr, TransparencyError};
-use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
 use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed};
 use rustc_feature::Features;
 use rustc_lint_defs::builtin::{
@@ -198,7 +198,7 @@ fn macro_rules_dummy_expander<'cx>(
     DummyResult::any(span)
 }
 
-fn trace_macros_note(cx_expansions: &mut FxHashMap<Span, Vec<String>>, sp: Span, message: String) {
+fn trace_macros_note(cx_expansions: &mut FxIndexMap<Span, Vec<String>>, sp: Span, message: String) {
     let sp = sp.macro_backtrace().last().map_or(sp, |trace| trace.call_site);
     cx_expansions.entry(sp).or_default().push(message);
 }
diff --git a/compiler/rustc_expand/src/mbe/quoted.rs b/compiler/rustc_expand/src/mbe/quoted.rs
index d4b8563a036..707cb73f097 100644
--- a/compiler/rustc_expand/src/mbe/quoted.rs
+++ b/compiler/rustc_expand/src/mbe/quoted.rs
@@ -234,6 +234,8 @@ fn parse_tree(
                             sess,
                             &Token { kind: token::Dollar, span },
                         );
+                    } else {
+                        maybe_emit_macro_metavar_expr_feature(features, sess, span);
                     }
                     TokenTree::token(token::Dollar, span)
                 }
diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs
index fdd8dc93fc1..3037855ae28 100644
--- a/compiler/rustc_expand/src/mbe/transcribe.rs
+++ b/compiler/rustc_expand/src/mbe/transcribe.rs
@@ -512,7 +512,18 @@ fn out_of_bounds_err<'a>(
     span: Span,
     ty: &str,
 ) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
-    cx.struct_span_err(span, &format!("{ty} depth must be less than {max}"))
+    let msg = if max == 0 {
+        format!(
+            "meta-variable expression `{ty}` with depth parameter \
+             must be called inside of a macro repetition"
+        )
+    } else {
+        format!(
+            "depth parameter on meta-variable expression `{ty}` \
+             must be less than {max}"
+        )
+    };
+    cx.struct_span_err(span, &msg)
 }
 
 fn transcribe_metavar_expr<'a>(
diff --git a/compiler/rustc_expand/src/module.rs b/compiler/rustc_expand/src/module.rs
index 876faad33b6..0315d11634c 100644
--- a/compiler/rustc_expand/src/module.rs
+++ b/compiler/rustc_expand/src/module.rs
@@ -218,10 +218,9 @@ pub fn default_submod_path<'a>(
         ""
     };
 
-    let mod_name = ident.name.to_string();
-    let default_path_str = format!("{}{}.rs", relative_prefix, mod_name);
+    let default_path_str = format!("{}{}.rs", relative_prefix, ident.name);
     let secondary_path_str =
-        format!("{}{}{}mod.rs", relative_prefix, mod_name, path::MAIN_SEPARATOR);
+        format!("{}{}{}mod.rs", relative_prefix, ident.name, path::MAIN_SEPARATOR);
     let default_path = dir_path.join(&default_path_str);
     let secondary_path = dir_path.join(&secondary_path_str);
     let default_exists = sess.source_map().file_exists(&default_path);
diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs
index 411d6be9c44..176c77ca6ed 100644
--- a/compiler/rustc_expand/src/proc_macro_server.rs
+++ b/compiler/rustc_expand/src/proc_macro_server.rs
@@ -11,13 +11,14 @@ use rustc_parse::lexer::nfc_normalize;
 use rustc_parse::parse_stream_from_source_str;
 use rustc_session::parse::ParseSess;
 use rustc_span::def_id::CrateNum;
-use rustc_span::symbol::{self, kw, sym, Symbol};
+use rustc_span::symbol::{self, sym, Symbol};
 use rustc_span::{BytePos, FileName, Pos, SourceFile, Span};
 
-use pm::bridge::{server, DelimSpan, ExpnGlobals, Group, Punct, TokenTree};
+use pm::bridge::{
+    server, DelimSpan, ExpnGlobals, Group, Ident, LitKind, Literal, Punct, TokenTree,
+};
 use pm::{Delimiter, Level, LineColumn};
 use std::ops::Bound;
-use std::{ascii, panic};
 
 trait FromInternal<T> {
     fn from_internal(x: T) -> Self;
@@ -49,9 +50,40 @@ impl ToInternal<token::Delimiter> for Delimiter {
     }
 }
 
-impl FromInternal<(TokenStream, &mut Rustc<'_, '_>)>
-    for Vec<TokenTree<TokenStream, Span, Ident, Literal>>
-{
+impl FromInternal<token::LitKind> for LitKind {
+    fn from_internal(kind: token::LitKind) -> Self {
+        match kind {
+            token::Byte => LitKind::Byte,
+            token::Char => LitKind::Char,
+            token::Integer => LitKind::Integer,
+            token::Float => LitKind::Float,
+            token::Str => LitKind::Str,
+            token::StrRaw(n) => LitKind::StrRaw(n),
+            token::ByteStr => LitKind::ByteStr,
+            token::ByteStrRaw(n) => LitKind::ByteStrRaw(n),
+            token::Err => LitKind::Err,
+            token::Bool => unreachable!(),
+        }
+    }
+}
+
+impl ToInternal<token::LitKind> for LitKind {
+    fn to_internal(self) -> token::LitKind {
+        match self {
+            LitKind::Byte => token::Byte,
+            LitKind::Char => token::Char,
+            LitKind::Integer => token::Integer,
+            LitKind::Float => token::Float,
+            LitKind::Str => token::Str,
+            LitKind::StrRaw(n) => token::StrRaw(n),
+            LitKind::ByteStr => token::ByteStr,
+            LitKind::ByteStrRaw(n) => token::ByteStrRaw(n),
+            LitKind::Err => token::Err,
+        }
+    }
+}
+
+impl FromInternal<(TokenStream, &mut Rustc<'_, '_>)> for Vec<TokenTree<TokenStream, Span, Symbol>> {
     fn from_internal((stream, rustc): (TokenStream, &mut Rustc<'_, '_>)) -> Self {
         use rustc_ast::token::*;
 
@@ -135,16 +167,22 @@ impl FromInternal<(TokenStream, &mut Rustc<'_, '_>)>
                 Question => op("?"),
                 SingleQuote => op("'"),
 
-                Ident(name, false) if name == kw::DollarCrate => trees.push(TokenTree::Ident(Ident::dollar_crate(span))),
-                Ident(name, is_raw) => trees.push(TokenTree::Ident(Ident::new(rustc.sess(), name, is_raw, span))),
+                Ident(sym, is_raw) => trees.push(TokenTree::Ident(Ident { sym, is_raw, span })),
                 Lifetime(name) => {
                     let ident = symbol::Ident::new(name, span).without_first_quote();
                     trees.extend([
                         TokenTree::Punct(Punct { ch: b'\'', joint: true, span }),
-                        TokenTree::Ident(Ident::new(rustc.sess(), ident.name, false, span)),
+                        TokenTree::Ident(Ident { sym: ident.name, is_raw: false, span }),
                     ]);
                 }
-                Literal(lit) => trees.push(TokenTree::Literal(self::Literal { lit, span })),
+                Literal(token::Lit { kind, symbol, suffix }) => {
+                    trees.push(TokenTree::Literal(self::Literal {
+                        kind: FromInternal::from_internal(kind),
+                        symbol,
+                        suffix,
+                        span,
+                    }));
+                }
                 DocComment(_, attr_style, data) => {
                     let mut escaped = String::new();
                     for ch in data.as_str().chars() {
@@ -170,7 +208,7 @@ impl FromInternal<(TokenStream, &mut Rustc<'_, '_>)>
                 }
 
                 Interpolated(nt) if let NtIdent(ident, is_raw) = *nt => {
-                    trees.push(TokenTree::Ident(Ident::new(rustc.sess(), ident.name, is_raw, ident.span)))
+                    trees.push(TokenTree::Ident(Ident { sym: ident.name, is_raw, span: ident.span }))
                 }
 
                 Interpolated(nt) => {
@@ -200,11 +238,12 @@ impl FromInternal<(TokenStream, &mut Rustc<'_, '_>)>
     }
 }
 
-impl ToInternal<TokenStream> for TokenTree<TokenStream, Span, Ident, Literal> {
+impl ToInternal<TokenStream> for (TokenTree<TokenStream, Span, Symbol>, &mut Rustc<'_, '_>) {
     fn to_internal(self) -> TokenStream {
         use rustc_ast::token::*;
 
-        let (ch, joint, span) = match self {
+        let (tree, rustc) = self;
+        let (ch, joint, span) = match tree {
             TokenTree::Punct(Punct { ch, joint, span }) => (ch, joint, span),
             TokenTree::Group(Group { delimiter, stream, span: DelimSpan { open, close, .. } }) => {
                 return tokenstream::TokenTree::Delimited(
@@ -215,10 +254,13 @@ impl ToInternal<TokenStream> for TokenTree<TokenStream, Span, Ident, Literal> {
                 .into();
             }
             TokenTree::Ident(self::Ident { sym, is_raw, span }) => {
+                rustc.sess().symbol_gallery.insert(sym, span);
                 return tokenstream::TokenTree::token(Ident(sym, is_raw), span).into();
             }
             TokenTree::Literal(self::Literal {
-                lit: token::Lit { kind: token::Integer, symbol, suffix },
+                kind: self::LitKind::Integer,
+                symbol,
+                suffix,
                 span,
             }) if symbol.as_str().starts_with('-') => {
                 let minus = BinOp(BinOpToken::Minus);
@@ -229,7 +271,9 @@ impl ToInternal<TokenStream> for TokenTree<TokenStream, Span, Ident, Literal> {
                 return [a, b].into_iter().collect();
             }
             TokenTree::Literal(self::Literal {
-                lit: token::Lit { kind: token::Float, symbol, suffix },
+                kind: self::LitKind::Float,
+                symbol,
+                suffix,
                 span,
             }) if symbol.as_str().starts_with('-') => {
                 let minus = BinOp(BinOpToken::Minus);
@@ -239,8 +283,12 @@ impl ToInternal<TokenStream> for TokenTree<TokenStream, Span, Ident, Literal> {
                 let b = tokenstream::TokenTree::token(float, span);
                 return [a, b].into_iter().collect();
             }
-            TokenTree::Literal(self::Literal { lit, span }) => {
-                return tokenstream::TokenTree::token(Literal(lit), span).into();
+            TokenTree::Literal(self::Literal { kind, symbol, suffix, span }) => {
+                return tokenstream::TokenTree::token(
+                    TokenKind::lit(kind.to_internal(), symbol, suffix),
+                    span,
+                )
+                .into();
             }
         };
 
@@ -289,40 +337,6 @@ impl ToInternal<rustc_errors::Level> for Level {
 
 pub struct FreeFunctions;
 
-#[derive(Copy, Clone, PartialEq, Eq, Hash)]
-pub struct Ident {
-    sym: Symbol,
-    is_raw: bool,
-    span: Span,
-}
-
-impl Ident {
-    fn new(sess: &ParseSess, sym: Symbol, is_raw: bool, span: Span) -> Ident {
-        let sym = nfc_normalize(sym.as_str());
-        let string = sym.as_str();
-        if !rustc_lexer::is_ident(string) {
-            panic!("`{:?}` is not a valid identifier", string)
-        }
-        if is_raw && !sym.can_be_raw() {
-            panic!("`{}` cannot be a raw identifier", string);
-        }
-        sess.symbol_gallery.insert(sym, span);
-        Ident { sym, is_raw, span }
-    }
-
-    fn dollar_crate(span: Span) -> Ident {
-        // `$crate` is accepted as an ident only if it comes from the compiler.
-        Ident { sym: kw::DollarCrate, is_raw: false, span }
-    }
-}
-
-// FIXME(eddyb) `Literal` should not expose internal `Debug` impls.
-#[derive(Clone, Debug)]
-pub struct Literal {
-    lit: token::Lit,
-    span: Span,
-}
-
 pub(crate) struct Rustc<'a, 'b> {
     ecx: &'a mut ExtCtxt<'b>,
     def_site: Span,
@@ -348,21 +362,16 @@ impl<'a, 'b> Rustc<'a, 'b> {
     fn sess(&self) -> &ParseSess {
         self.ecx.parse_sess()
     }
-
-    fn lit(&mut self, kind: token::LitKind, symbol: Symbol, suffix: Option<Symbol>) -> Literal {
-        Literal { lit: token::Lit::new(kind, symbol, suffix), span: self.call_site }
-    }
 }
 
 impl server::Types for Rustc<'_, '_> {
     type FreeFunctions = FreeFunctions;
     type TokenStream = TokenStream;
-    type Ident = Ident;
-    type Literal = Literal;
     type SourceFile = Lrc<SourceFile>;
     type MultiSpan = Vec<Span>;
     type Diagnostic = Diagnostic;
     type Span = Span;
+    type Symbol = Symbol;
 }
 
 impl server::FreeFunctions for Rustc<'_, '_> {
@@ -376,6 +385,57 @@ impl server::FreeFunctions for Rustc<'_, '_> {
     fn track_path(&mut self, path: &str) {
         self.sess().file_depinfo.borrow_mut().insert(Symbol::intern(path));
     }
+
+    fn literal_from_str(&mut self, s: &str) -> Result<Literal<Self::Span, Self::Symbol>, ()> {
+        let name = FileName::proc_macro_source_code(s);
+        let mut parser = rustc_parse::new_parser_from_source_str(self.sess(), name, s.to_owned());
+
+        let first_span = parser.token.span.data();
+        let minus_present = parser.eat(&token::BinOp(token::Minus));
+
+        let lit_span = parser.token.span.data();
+        let token::Literal(mut lit) = parser.token.kind else {
+            return Err(());
+        };
+
+        // Check no comment or whitespace surrounding the (possibly negative)
+        // literal, or more tokens after it.
+        if (lit_span.hi.0 - first_span.lo.0) as usize != s.len() {
+            return Err(());
+        }
+
+        if minus_present {
+            // If minus is present, check no comment or whitespace in between it
+            // and the literal token.
+            if first_span.hi.0 != lit_span.lo.0 {
+                return Err(());
+            }
+
+            // Check literal is a kind we allow to be negated in a proc macro token.
+            match lit.kind {
+                token::LitKind::Bool
+                | token::LitKind::Byte
+                | token::LitKind::Char
+                | token::LitKind::Str
+                | token::LitKind::StrRaw(_)
+                | token::LitKind::ByteStr
+                | token::LitKind::ByteStrRaw(_)
+                | token::LitKind::Err => return Err(()),
+                token::LitKind::Integer | token::LitKind::Float => {}
+            }
+
+            // Synthesize a new symbol that includes the minus sign.
+            let symbol = Symbol::intern(&s[..1 + lit.symbol.as_str().len()]);
+            lit = token::Lit::new(lit.kind, symbol, lit.suffix);
+        }
+        let token::Lit { kind, symbol, suffix } = lit;
+        Ok(Literal {
+            kind: FromInternal::from_internal(kind),
+            symbol,
+            suffix,
+            span: self.call_site,
+        })
+    }
 }
 
 impl server::TokenStream for Rustc<'_, '_> {
@@ -453,22 +513,22 @@ impl server::TokenStream for Rustc<'_, '_> {
 
     fn from_token_tree(
         &mut self,
-        tree: TokenTree<Self::TokenStream, Self::Span, Self::Ident, Self::Literal>,
+        tree: TokenTree<Self::TokenStream, Self::Span, Self::Symbol>,
     ) -> Self::TokenStream {
-        tree.to_internal()
+        (tree, &mut *self).to_internal()
     }
 
     fn concat_trees(
         &mut self,
         base: Option<Self::TokenStream>,
-        trees: Vec<TokenTree<Self::TokenStream, Self::Span, Self::Ident, Self::Literal>>,
+        trees: Vec<TokenTree<Self::TokenStream, Self::Span, Self::Symbol>>,
     ) -> Self::TokenStream {
         let mut builder = tokenstream::TokenStreamBuilder::new();
         if let Some(base) = base {
             builder.push(base);
         }
         for tree in trees {
-            builder.push(tree.to_internal());
+            builder.push((tree, &mut *self).to_internal());
         }
         builder.build()
     }
@@ -491,178 +551,11 @@ impl server::TokenStream for Rustc<'_, '_> {
     fn into_trees(
         &mut self,
         stream: Self::TokenStream,
-    ) -> Vec<TokenTree<Self::TokenStream, Self::Span, Self::Ident, Self::Literal>> {
+    ) -> Vec<TokenTree<Self::TokenStream, Self::Span, Self::Symbol>> {
         FromInternal::from_internal((stream, self))
     }
 }
 
-impl server::Ident for Rustc<'_, '_> {
-    fn new(&mut self, string: &str, span: Self::Span, is_raw: bool) -> Self::Ident {
-        Ident::new(self.sess(), Symbol::intern(string), is_raw, span)
-    }
-
-    fn span(&mut self, ident: Self::Ident) -> Self::Span {
-        ident.span
-    }
-
-    fn with_span(&mut self, ident: Self::Ident, span: Self::Span) -> Self::Ident {
-        Ident { span, ..ident }
-    }
-}
-
-impl server::Literal for Rustc<'_, '_> {
-    fn from_str(&mut self, s: &str) -> Result<Self::Literal, ()> {
-        let name = FileName::proc_macro_source_code(s);
-        let mut parser = rustc_parse::new_parser_from_source_str(self.sess(), name, s.to_owned());
-
-        let first_span = parser.token.span.data();
-        let minus_present = parser.eat(&token::BinOp(token::Minus));
-
-        let lit_span = parser.token.span.data();
-        let token::Literal(mut lit) = parser.token.kind else {
-            return Err(());
-        };
-
-        // Check no comment or whitespace surrounding the (possibly negative)
-        // literal, or more tokens after it.
-        if (lit_span.hi.0 - first_span.lo.0) as usize != s.len() {
-            return Err(());
-        }
-
-        if minus_present {
-            // If minus is present, check no comment or whitespace in between it
-            // and the literal token.
-            if first_span.hi.0 != lit_span.lo.0 {
-                return Err(());
-            }
-
-            // Check literal is a kind we allow to be negated in a proc macro token.
-            match lit.kind {
-                token::LitKind::Bool
-                | token::LitKind::Byte
-                | token::LitKind::Char
-                | token::LitKind::Str
-                | token::LitKind::StrRaw(_)
-                | token::LitKind::ByteStr
-                | token::LitKind::ByteStrRaw(_)
-                | token::LitKind::Err => return Err(()),
-                token::LitKind::Integer | token::LitKind::Float => {}
-            }
-
-            // Synthesize a new symbol that includes the minus sign.
-            let symbol = Symbol::intern(&s[..1 + lit.symbol.as_str().len()]);
-            lit = token::Lit::new(lit.kind, symbol, lit.suffix);
-        }
-
-        Ok(Literal { lit, span: self.call_site })
-    }
-
-    fn to_string(&mut self, literal: &Self::Literal) -> String {
-        literal.lit.to_string()
-    }
-
-    fn debug_kind(&mut self, literal: &Self::Literal) -> String {
-        format!("{:?}", literal.lit.kind)
-    }
-
-    fn symbol(&mut self, literal: &Self::Literal) -> String {
-        literal.lit.symbol.to_string()
-    }
-
-    fn suffix(&mut self, literal: &Self::Literal) -> Option<String> {
-        literal.lit.suffix.as_ref().map(Symbol::to_string)
-    }
-
-    fn integer(&mut self, n: &str) -> Self::Literal {
-        self.lit(token::Integer, Symbol::intern(n), None)
-    }
-
-    fn typed_integer(&mut self, n: &str, kind: &str) -> Self::Literal {
-        self.lit(token::Integer, Symbol::intern(n), Some(Symbol::intern(kind)))
-    }
-
-    fn float(&mut self, n: &str) -> Self::Literal {
-        self.lit(token::Float, Symbol::intern(n), None)
-    }
-
-    fn f32(&mut self, n: &str) -> Self::Literal {
-        self.lit(token::Float, Symbol::intern(n), Some(sym::f32))
-    }
-
-    fn f64(&mut self, n: &str) -> Self::Literal {
-        self.lit(token::Float, Symbol::intern(n), Some(sym::f64))
-    }
-
-    fn string(&mut self, string: &str) -> Self::Literal {
-        let quoted = format!("{:?}", string);
-        assert!(quoted.starts_with('"') && quoted.ends_with('"'));
-        let symbol = &quoted[1..quoted.len() - 1];
-        self.lit(token::Str, Symbol::intern(symbol), None)
-    }
-
-    fn character(&mut self, ch: char) -> Self::Literal {
-        let quoted = format!("{:?}", ch);
-        assert!(quoted.starts_with('\'') && quoted.ends_with('\''));
-        let symbol = &quoted[1..quoted.len() - 1];
-        self.lit(token::Char, Symbol::intern(symbol), None)
-    }
-
-    fn byte_string(&mut self, bytes: &[u8]) -> Self::Literal {
-        let string = bytes
-            .iter()
-            .cloned()
-            .flat_map(ascii::escape_default)
-            .map(Into::<char>::into)
-            .collect::<String>();
-        self.lit(token::ByteStr, Symbol::intern(&string), None)
-    }
-
-    fn span(&mut self, literal: &Self::Literal) -> Self::Span {
-        literal.span
-    }
-
-    fn set_span(&mut self, literal: &mut Self::Literal, span: Self::Span) {
-        literal.span = span;
-    }
-
-    fn subspan(
-        &mut self,
-        literal: &Self::Literal,
-        start: Bound<usize>,
-        end: Bound<usize>,
-    ) -> Option<Self::Span> {
-        let span = literal.span;
-        let length = span.hi().to_usize() - span.lo().to_usize();
-
-        let start = match start {
-            Bound::Included(lo) => lo,
-            Bound::Excluded(lo) => lo.checked_add(1)?,
-            Bound::Unbounded => 0,
-        };
-
-        let end = match end {
-            Bound::Included(hi) => hi.checked_add(1)?,
-            Bound::Excluded(hi) => hi,
-            Bound::Unbounded => length,
-        };
-
-        // Bounds check the values, preventing addition overflow and OOB spans.
-        if start > u32::MAX as usize
-            || end > u32::MAX as usize
-            || (u32::MAX - start as u32) < span.lo().to_u32()
-            || (u32::MAX - end as u32) < span.lo().to_u32()
-            || start >= end
-            || end > length
-        {
-            return None;
-        }
-
-        let new_lo = span.lo() + BytePos::from_usize(start);
-        let new_hi = span.lo() + BytePos::from_usize(end);
-        Some(span.with_lo(new_lo).with_hi(new_hi))
-    }
-}
-
 impl server::SourceFile for Rustc<'_, '_> {
     fn eq(&mut self, file1: &Self::SourceFile, file2: &Self::SourceFile) -> bool {
         Lrc::ptr_eq(file1, file2)
@@ -767,6 +660,42 @@ impl server::Span for Rustc<'_, '_> {
         Some(first.to(second))
     }
 
+    fn subspan(
+        &mut self,
+        span: Self::Span,
+        start: Bound<usize>,
+        end: Bound<usize>,
+    ) -> Option<Self::Span> {
+        let length = span.hi().to_usize() - span.lo().to_usize();
+
+        let start = match start {
+            Bound::Included(lo) => lo,
+            Bound::Excluded(lo) => lo.checked_add(1)?,
+            Bound::Unbounded => 0,
+        };
+
+        let end = match end {
+            Bound::Included(hi) => hi.checked_add(1)?,
+            Bound::Excluded(hi) => hi,
+            Bound::Unbounded => length,
+        };
+
+        // Bounds check the values, preventing addition overflow and OOB spans.
+        if start > u32::MAX as usize
+            || end > u32::MAX as usize
+            || (u32::MAX - start as u32) < span.lo().to_u32()
+            || (u32::MAX - end as u32) < span.lo().to_u32()
+            || start >= end
+            || end > length
+        {
+            return None;
+        }
+
+        let new_lo = span.lo() + BytePos::from_usize(start);
+        let new_hi = span.lo() + BytePos::from_usize(end);
+        Some(span.with_lo(new_lo).with_hi(new_hi))
+    }
+
     fn resolved_at(&mut self, span: Self::Span, at: Self::Span) -> Self::Span {
         span.with_ctxt(at.ctxt())
     }
@@ -812,6 +741,13 @@ impl server::Span for Rustc<'_, '_> {
     }
 }
 
+impl server::Symbol for Rustc<'_, '_> {
+    fn normalize_and_validate_ident(&mut self, string: &str) -> Result<Self::Symbol, ()> {
+        let sym = nfc_normalize(string);
+        if rustc_lexer::is_ident(sym.as_str()) { Ok(sym) } else { Err(()) }
+    }
+}
+
 impl server::Server for Rustc<'_, '_> {
     fn globals(&mut self) -> ExpnGlobals<Self::Span> {
         ExpnGlobals {
@@ -820,4 +756,12 @@ impl server::Server for Rustc<'_, '_> {
             mixed_site: self.mixed_site,
         }
     }
+
+    fn intern_symbol(string: &str) -> Self::Symbol {
+        Symbol::intern(string)
+    }
+
+    fn with_symbol_string(symbol: &Self::Symbol, f: impl FnOnce(&str)) {
+        f(&symbol.as_str())
+    }
 }
diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs
index 099c40b215d..2d24101b2d5 100644
--- a/compiler/rustc_feature/src/accepted.rs
+++ b/compiler/rustc_feature/src/accepted.rs
@@ -186,6 +186,8 @@ declare_features! (
     /// Allows some increased flexibility in the name resolution rules,
     /// especially around globs and shadowing (RFC 1560).
     (accepted, item_like_imports, "1.15.0", Some(35120), None),
+    /// Allows `if/while p && let q = r && ...` chains.
+    (accepted, let_chains, "1.64.0", Some(53667), None),
     /// Allows `break {expr}` with a value inside `loop`s.
     (accepted, loop_break_value, "1.19.0", Some(37339), None),
     /// Allows use of `?` as the Kleene "at most one" operator in macros.
diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs
index b54f0ef361a..1018facebae 100644
--- a/compiler/rustc_feature/src/active.rs
+++ b/compiler/rustc_feature/src/active.rs
@@ -148,6 +148,8 @@ declare_features! (
     /// below (it has to be checked before expansion possibly makes
     /// macros disappear).
     (active, allow_internal_unstable, "1.0.0", None, None),
+    /// Allows using anonymous lifetimes in argument-position impl-trait.
+    (active, anonymous_lifetime_in_impl_trait, "1.63.0", None, None),
     /// Allows identifying the `compiler_builtins` crate.
     (active, compiler_builtins, "1.13.0", None, None),
     /// Outputs useful `assert!` messages
@@ -156,9 +158,6 @@ declare_features! (
     (active, intrinsics, "1.0.0", None, None),
     /// Allows using `#[lang = ".."]` attribute for linking items to special compiler logic.
     (active, lang_items, "1.0.0", None, None),
-    /// Allows `#[repr(no_niche)]` (an implementation detail of `rustc`,
-    /// it is not on path for eventual stabilization).
-    (active, no_niche, "1.42.0", None, None),
     /// Allows using `#[omit_gdb_pretty_printer_section]`.
     (active, omit_gdb_pretty_printer_section, "1.5.0", None, None),
     /// Allows using `#[prelude_import]` on glob `use` items.
@@ -331,6 +330,8 @@ declare_features! (
     (active, cfg_target_thread_local, "1.7.0", Some(29594), None),
     /// Allow conditional compilation depending on rust version
     (active, cfg_version, "1.45.0", Some(64796), None),
+    /// Allows `for<...>` on closures and generators.
+    (active, closure_lifetime_binder, "1.64.0", Some(97362), None),
     /// Allows `#[track_caller]` on closures and generators.
     (active, closure_track_caller, "1.57.0", Some(87417), None),
     /// Allows to use the `#[cmse_nonsecure_entry]` attribute.
@@ -423,8 +424,6 @@ declare_features! (
     (active, label_break_value, "1.28.0", Some(48594), None),
     // Allows setting the threshold for the `large_assignments` lint.
     (active, large_assignments, "1.52.0", Some(83518), None),
-    /// Allows `if/while p && let q = r && ...` chains.
-    (active, let_chains, "1.37.0", Some(53667), None),
     /// Allows `let...else` statements.
     (active, let_else, "1.56.0", Some(87335), None),
     /// Allows `#[link(..., cfg(..))]`.
@@ -528,13 +527,6 @@ declare_features! (
     (incomplete, unsized_locals, "1.30.0", Some(48055), None),
     /// Allows unsized tuple coercion.
     (active, unsized_tuple_coercion, "1.20.0", Some(42877), None),
-    /// Allows `union`s to implement `Drop`. Moreover, `union`s may now include fields
-    /// that don't implement `Copy` as long as they don't have any drop glue.
-    /// This is checked recursively. On encountering type variable where no progress can be made,
-    /// `T: Copy` is used as a substitute for "no drop glue".
-    ///
-    /// NOTE: A limited form of `union U { ... }` was accepted in 1.19.0.
-    (active, untagged_unions, "1.13.0", Some(55149), None),
     /// Allows using the `#[used(linker)]` (or `#[used(compiler)]`) attribute.
     (active, used_with_arg, "1.60.0", Some(93798), None),
     /// Allows `extern "wasm" fn`
diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs
index 6fcdfe44d8f..0e73d8fd7f6 100644
--- a/compiler/rustc_feature/src/builtin_attrs.rs
+++ b/compiler/rustc_feature/src/builtin_attrs.rs
@@ -512,6 +512,9 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
         allow_internal_unsafe, Normal, template!(Word), WarnFollowing,
         "allow_internal_unsafe side-steps the unsafe_code lint",
     ),
+    rustc_attr!(rustc_allowed_through_unstable_modules, Normal, template!(Word), WarnFollowing,
+    "rustc_allowed_through_unstable_modules special cases accidental stabilizations of stable items \
+    through unstable paths"),
 
     // ==========================================================================
     // Internal attributes: Type system related:
@@ -529,6 +532,9 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
 
     rustc_attr!(rustc_allocator, Normal, template!(Word), WarnFollowing, IMPL_DETAIL),
     rustc_attr!(rustc_allocator_nounwind, Normal, template!(Word), WarnFollowing, IMPL_DETAIL),
+    rustc_attr!(rustc_reallocator, Normal, template!(Word), WarnFollowing, IMPL_DETAIL),
+    rustc_attr!(rustc_deallocator, Normal, template!(Word), WarnFollowing, IMPL_DETAIL),
+    rustc_attr!(rustc_allocator_zeroed, Normal, template!(Word), WarnFollowing, IMPL_DETAIL),
     gated!(
         alloc_error_handler, Normal, template!(Word), WarnFollowing,
         experimental!(alloc_error_handler)
@@ -613,6 +619,12 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
     // Used by the `rustc::untranslatable_diagnostic` and `rustc::diagnostic_outside_of_impl` lints
     // to assist in changes to diagnostic APIs.
     rustc_attr!(rustc_lint_diagnostics, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE),
+    // Used by the `rustc::bad_opt_access` lint to identify `DebuggingOptions` and `CodegenOptions`
+    // types (as well as any others in future).
+    rustc_attr!(rustc_lint_opt_ty, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE),
+    // Used by the `rustc::bad_opt_access` lint on fields
+    // types (as well as any others in future).
+    rustc_attr!(rustc_lint_opt_deny_field_access, Normal, template!(List: "message"), WarnFollowing, INTERNAL_UNSTABLE),
 
     // ==========================================================================
     // Internal attributes, Const related:
diff --git a/compiler/rustc_feature/src/removed.rs b/compiler/rustc_feature/src/removed.rs
index 54626caaf53..2ddaf920109 100644
--- a/compiler/rustc_feature/src/removed.rs
+++ b/compiler/rustc_feature/src/removed.rs
@@ -180,6 +180,9 @@ declare_features! (
     /// Allows using items which are missing stability attributes
     (removed, unmarked_api, "1.0.0", None, None, None),
     (removed, unsafe_no_drop_flag, "1.0.0", None, None, None),
+    /// Allows `union` fields that don't implement `Copy` as long as they don't have any drop glue.
+    (removed, untagged_unions, "1.13.0", Some(55149), None,
+     Some("unions with `Copy` and `ManuallyDrop` fields are stable; there is no intent to stabilize more")),
     /// Allows `#[unwind(..)]`.
     ///
     /// Permits specifying whether a function should permit unwinding or abort on unwind.
diff --git a/compiler/rustc_hir/src/arena.rs b/compiler/rustc_hir/src/arena.rs
index a6d10f3adae..44335b7f42e 100644
--- a/compiler/rustc_hir/src/arena.rs
+++ b/compiler/rustc_hir/src/arena.rs
@@ -12,6 +12,7 @@ macro_rules! arena_types {
             [] asm_operand: (rustc_hir::InlineAsmOperand<'tcx>, rustc_span::Span),
             [] asm_template: rustc_ast::InlineAsmTemplatePiece,
             [] attribute: rustc_ast::Attribute,
+            [] closure: rustc_hir::Closure<'tcx>,
             [] block: rustc_hir::Block<'tcx>,
             [] bare_fn_ty: rustc_hir::BareFnTy<'tcx>,
             [] body: rustc_hir::Body<'tcx>,
diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs
index d0893cd09d8..be5b7eccbaf 100644
--- a/compiler/rustc_hir/src/def.rs
+++ b/compiler/rustc_hir/src/def.rs
@@ -713,7 +713,7 @@ impl<Id> Res<Id> {
 }
 
 /// Resolution for a lifetime appearing in a type.
-#[derive(Copy, Clone, Debug)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
 pub enum LifetimeRes {
     /// Successfully linked the lifetime to a generic parameter.
     Param {
@@ -738,13 +738,8 @@ pub enum LifetimeRes {
         binder: NodeId,
     },
     /// This variant is used for anonymous lifetimes that we did not resolve during
-    /// late resolution.  Shifting the work to the HIR lifetime resolver.
-    Anonymous {
-        /// Id of the introducing place. See `Param`.
-        binder: NodeId,
-        /// Whether this lifetime was spelled or elided.
-        elided: bool,
-    },
+    /// late resolution.  Those lifetimes will be inferred by typechecking.
+    Infer,
     /// Explicit `'static` lifetime.
     Static,
     /// Resolution failure.
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index 04f585df34c..f71400898e6 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -90,9 +90,6 @@ pub enum LifetimeName {
     /// User-given names or fresh (synthetic) names.
     Param(LocalDefId, ParamName),
 
-    /// User wrote nothing (e.g., the lifetime in `&u32`).
-    Implicit,
-
     /// Implicit lifetime in a context like `dyn Foo`. This is
     /// distinguished from implicit lifetimes elsewhere because the
     /// lifetime that they default to must appear elsewhere within the
@@ -110,8 +107,9 @@ pub enum LifetimeName {
     /// that was already reported.
     Error,
 
-    /// User wrote specifies `'_`.
-    Underscore,
+    /// User wrote an anonymous lifetime, either `'_` or nothing.
+    /// The semantics of this lifetime should be inferred by typechecking code.
+    Infer,
 
     /// User wrote `'static`.
     Static,
@@ -120,10 +118,8 @@ pub enum LifetimeName {
 impl LifetimeName {
     pub fn ident(&self) -> Ident {
         match *self {
-            LifetimeName::ImplicitObjectLifetimeDefault
-            | LifetimeName::Implicit
-            | LifetimeName::Error => Ident::empty(),
-            LifetimeName::Underscore => Ident::with_dummy_span(kw::UnderscoreLifetime),
+            LifetimeName::ImplicitObjectLifetimeDefault | LifetimeName::Error => Ident::empty(),
+            LifetimeName::Infer => Ident::with_dummy_span(kw::UnderscoreLifetime),
             LifetimeName::Static => Ident::with_dummy_span(kw::StaticLifetime),
             LifetimeName::Param(_, param_name) => param_name.ident(),
         }
@@ -132,8 +128,7 @@ impl LifetimeName {
     pub fn is_anonymous(&self) -> bool {
         match *self {
             LifetimeName::ImplicitObjectLifetimeDefault
-            | LifetimeName::Implicit
-            | LifetimeName::Underscore
+            | LifetimeName::Infer
             | LifetimeName::Param(_, ParamName::Fresh)
             | LifetimeName::Error => true,
             LifetimeName::Static | LifetimeName::Param(..) => false,
@@ -142,9 +137,7 @@ impl LifetimeName {
 
     pub fn is_elided(&self) -> bool {
         match self {
-            LifetimeName::ImplicitObjectLifetimeDefault
-            | LifetimeName::Implicit
-            | LifetimeName::Underscore => true,
+            LifetimeName::ImplicitObjectLifetimeDefault | LifetimeName::Infer => true,
 
             // It might seem surprising that `Fresh` counts as
             // *not* elided -- but this is because, as far as the code
@@ -922,6 +915,17 @@ pub struct Crate<'hir> {
     pub hir_hash: Fingerprint,
 }
 
+#[derive(Debug, HashStable_Generic)]
+pub struct Closure<'hir> {
+    pub binder: ClosureBinder,
+    pub capture_clause: CaptureBy,
+    pub bound_generic_params: &'hir [GenericParam<'hir>],
+    pub fn_decl: &'hir FnDecl<'hir>,
+    pub body: BodyId,
+    pub fn_decl_span: Span,
+    pub movability: Option<Movability>,
+}
+
 /// A block of statements `{ .. }`, which may have a label (in this case the
 /// `targeted_by_break` field will be `true`) and may be `unsafe` by means of
 /// the `rules` being anything but `DefaultBlock`.
@@ -943,6 +947,16 @@ pub struct Block<'hir> {
     pub targeted_by_break: bool,
 }
 
+impl<'hir> Block<'hir> {
+    pub fn innermost_block(&self) -> &Block<'hir> {
+        let mut block = self;
+        while let Some(Expr { kind: ExprKind::Block(inner_block, _), .. }) = block.expr {
+            block = inner_block;
+        }
+        block
+    }
+}
+
 #[derive(Debug, HashStable_Generic)]
 pub struct Pat<'hir> {
     #[stable_hasher(ignore)]
@@ -1316,6 +1330,8 @@ pub struct Local<'hir> {
     pub ty: Option<&'hir Ty<'hir>>,
     /// Initializer expression to set the value, if any.
     pub init: Option<&'hir Expr<'hir>>,
+    /// Else block for a `let...else` binding.
+    pub els: Option<&'hir Block<'hir>>,
     pub hir_id: HirId,
     pub span: Span,
     /// Can be `ForLoopDesugar` if the `let` statement is part of a `for` loop
@@ -1811,6 +1827,14 @@ impl Expr<'_> {
             _ => false,
         }
     }
+
+    pub fn method_ident(&self) -> Option<Ident> {
+        match self.kind {
+            ExprKind::MethodCall(receiver_method, ..) => Some(receiver_method.ident),
+            ExprKind::Unary(_, expr) | ExprKind::AddrOf(.., expr) => expr.method_ident(),
+            _ => None,
+        }
+    }
 }
 
 /// Checks if the specified expression is a built-in range literal.
@@ -1913,14 +1937,7 @@ pub enum ExprKind<'hir> {
     ///
     /// This may also be a generator literal or an `async block` as indicated by the
     /// `Option<Movability>`.
-    Closure {
-        capture_clause: CaptureBy,
-        bound_generic_params: &'hir [GenericParam<'hir>],
-        fn_decl: &'hir FnDecl<'hir>,
-        body: BodyId,
-        fn_decl_span: Span,
-        movability: Option<Movability>,
-    },
+    Closure(&'hir Closure<'hir>),
     /// A block (e.g., `'label: { ... }`).
     Block(&'hir Block<'hir>, Option<Label>),
 
@@ -2698,6 +2715,17 @@ impl FnRetTy<'_> {
     }
 }
 
+/// Represents `for<...>` binder before a closure
+#[derive(Copy, Clone, Debug, HashStable_Generic)]
+pub enum ClosureBinder {
+    /// Binder is not specified.
+    Default,
+    /// Binder is specified.
+    ///
+    /// Span points to the whole `for<...>`.
+    For { span: Span },
+}
+
 #[derive(Encodable, Debug, HashStable_Generic)]
 pub struct Mod<'hir> {
     pub spans: ModSpans,
@@ -3462,7 +3490,7 @@ impl<'hir> Node<'hir> {
 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
 mod size_asserts {
     rustc_data_structures::static_assert_size!(super::Block<'static>, 48);
-    rustc_data_structures::static_assert_size!(super::Expr<'static>, 64);
+    rustc_data_structures::static_assert_size!(super::Expr<'static>, 56);
     rustc_data_structures::static_assert_size!(super::Pat<'static>, 88);
     rustc_data_structures::static_assert_size!(super::QPath<'static>, 24);
     rustc_data_structures::static_assert_size!(super::Ty<'static>, 72);
diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs
index 531d9f14040..640974115b9 100644
--- a/compiler/rustc_hir/src/intravisit.rs
+++ b/compiler/rustc_hir/src/intravisit.rs
@@ -472,6 +472,9 @@ pub fn walk_local<'v, V: Visitor<'v>>(visitor: &mut V, local: &'v Local<'v>) {
     walk_list!(visitor, visit_expr, &local.init);
     visitor.visit_id(local.hir_id);
     visitor.visit_pat(&local.pat);
+    if let Some(els) = local.els {
+        visitor.visit_block(els);
+    }
     walk_list!(visitor, visit_ty, &local.ty);
 }
 
@@ -493,9 +496,8 @@ pub fn walk_lifetime<'v, V: Visitor<'v>>(visitor: &mut V, lifetime: &'v Lifetime
         | LifetimeName::Param(_, ParamName::Error)
         | LifetimeName::Static
         | LifetimeName::Error
-        | LifetimeName::Implicit
         | LifetimeName::ImplicitObjectLifetimeDefault
-        | LifetimeName::Underscore => {}
+        | LifetimeName::Infer => {}
     }
 }
 
@@ -925,7 +927,7 @@ pub fn walk_fn_kind<'v, V: Visitor<'v>>(visitor: &mut V, function_kind: FnKind<'
         FnKind::ItemFn(_, generics, ..) => {
             visitor.visit_generics(generics);
         }
-        FnKind::Method(..) | FnKind::Closure => {}
+        FnKind::Closure | FnKind::Method(..) => {}
     }
 }
 
@@ -1144,14 +1146,15 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>)
             visitor.visit_expr(subexpression);
             walk_list!(visitor, visit_arm, arms);
         }
-        ExprKind::Closure {
+        ExprKind::Closure(&Closure {
+            binder: _,
             bound_generic_params,
-            ref fn_decl,
+            fn_decl,
             body,
             capture_clause: _,
             fn_decl_span: _,
             movability: _,
-        } => {
+        }) => {
             walk_list!(visitor, visit_generic_param, bound_generic_params);
             visitor.visit_fn(FnKind::Closure, fn_decl, body, expression.span, expression.hir_id)
         }
diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs
index b0bfac8e1f5..13b3e954e1f 100644
--- a/compiler/rustc_hir/src/lang_items.rs
+++ b/compiler/rustc_hir/src/lang_items.rs
@@ -11,7 +11,7 @@ use crate::def_id::DefId;
 use crate::{MethodKind, Target};
 
 use rustc_ast as ast;
-use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::fx::FxIndexMap;
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 use rustc_macros::HashStable_Generic;
 use rustc_span::symbol::{kw, sym, Symbol};
@@ -134,8 +134,8 @@ macro_rules! language_item_table {
         }
 
         /// A mapping from the name of the lang item to its order and the form it must be of.
-        pub static ITEM_REFS: LazyLock<FxHashMap<Symbol, (usize, Target)>> = LazyLock::new(|| {
-            let mut item_refs = FxHashMap::default();
+        pub static ITEM_REFS: LazyLock<FxIndexMap<Symbol, (usize, Target)>> = LazyLock::new(|| {
+            let mut item_refs = FxIndexMap::default();
             $( item_refs.insert($module::$name, (LangItem::$variant as usize, $target)); )*
             item_refs
         });
diff --git a/compiler/rustc_hir/src/pat_util.rs b/compiler/rustc_hir/src/pat_util.rs
index b30076100bb..93112199b60 100644
--- a/compiler/rustc_hir/src/pat_util.rs
+++ b/compiler/rustc_hir/src/pat_util.rs
@@ -1,7 +1,7 @@
 use crate::def::{CtorOf, DefKind, Res};
 use crate::def_id::DefId;
 use crate::hir::{self, HirId, PatKind};
-use rustc_data_structures::stable_set::FxHashSet;
+use rustc_data_structures::fx::FxHashSet;
 use rustc_span::hygiene::DesugaringKind;
 use rustc_span::symbol::Ident;
 use rustc_span::Span;
diff --git a/compiler/rustc_hir/src/weak_lang_items.rs b/compiler/rustc_hir/src/weak_lang_items.rs
index dad22725511..b6a85c0472e 100644
--- a/compiler/rustc_hir/src/weak_lang_items.rs
+++ b/compiler/rustc_hir/src/weak_lang_items.rs
@@ -4,7 +4,7 @@ use crate::def_id::DefId;
 use crate::{lang_items, LangItem, LanguageItems};
 
 use rustc_ast as ast;
-use rustc_data_structures::stable_map::StableMap;
+use rustc_data_structures::fx::FxIndexMap;
 use rustc_span::symbol::{sym, Symbol};
 
 use std::sync::LazyLock;
@@ -12,8 +12,8 @@ use std::sync::LazyLock;
 macro_rules! weak_lang_items {
     ($($name:ident, $item:ident, $sym:ident;)*) => (
 
-pub static WEAK_ITEMS_REFS: LazyLock<StableMap<Symbol, LangItem>> = LazyLock::new(|| {
-    let mut map = StableMap::default();
+pub static WEAK_ITEMS_REFS: LazyLock<FxIndexMap<Symbol, LangItem>> = LazyLock::new(|| {
+    let mut map = FxIndexMap::default();
     $(map.insert(sym::$name, LangItem::$item);)*
     map
 });
diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs
index 50acb0270b0..e0179bd3ed1 100644
--- a/compiler/rustc_hir_pretty/src/lib.rs
+++ b/compiler/rustc_hir_pretty/src/lib.rs
@@ -6,6 +6,7 @@ use rustc_ast_pretty::pp::Breaks::{Consistent, Inconsistent};
 use rustc_ast_pretty::pp::{self, Breaks};
 use rustc_ast_pretty::pprust::{Comments, PrintState};
 use rustc_hir as hir;
+use rustc_hir::LifetimeParamKind;
 use rustc_hir::{GenericArg, GenericParam, GenericParamKind, Node, Term};
 use rustc_hir::{GenericBound, PatKind, RangeEnd, TraitBoundModifier};
 use rustc_span::source_map::SourceMap;
@@ -210,6 +211,10 @@ pub fn path_to_string(segment: &hir::Path<'_>) -> String {
     to_string(NO_ANN, |s| s.print_path(segment, false))
 }
 
+pub fn qpath_to_string(segment: &hir::QPath<'_>) -> String {
+    to_string(NO_ANN, |s| s.print_qpath(segment, false))
+}
+
 pub fn fn_to_string(
     decl: &hir::FnDecl<'_>,
     header: hir::FnHeader,
@@ -883,7 +888,12 @@ impl<'a> State<'a> {
         self.ann.post(self, AnnNode::SubItem(ii.hir_id()))
     }
 
-    pub fn print_local(&mut self, init: Option<&hir::Expr<'_>>, decl: impl Fn(&mut Self)) {
+    pub fn print_local(
+        &mut self,
+        init: Option<&hir::Expr<'_>>,
+        els: Option<&hir::Block<'_>>,
+        decl: impl Fn(&mut Self),
+    ) {
         self.space_if_not_bol();
         self.ibox(INDENT_UNIT);
         self.word_nbsp("let");
@@ -897,6 +907,13 @@ impl<'a> State<'a> {
             self.word_space("=");
             self.print_expr(init);
         }
+
+        if let Some(els) = els {
+            self.nbsp();
+            self.word_space("else");
+            self.print_block(els);
+        }
+
         self.end()
     }
 
@@ -904,7 +921,7 @@ impl<'a> State<'a> {
         self.maybe_print_comment(st.span.lo());
         match st.kind {
             hir::StmtKind::Local(loc) => {
-                self.print_local(loc.init, |this| this.print_local_decl(loc));
+                self.print_local(loc.init, loc.els, |this| this.print_local_decl(loc));
             }
             hir::StmtKind::Item(item) => self.ann.nested(self, Nested::Item(item)),
             hir::StmtKind::Expr(expr) => {
@@ -1404,7 +1421,7 @@ impl<'a> State<'a> {
 
                 // Print `let _t = $init;`:
                 let temp = Ident::from_str("_t");
-                self.print_local(Some(init), |this| this.print_ident(temp));
+                self.print_local(Some(init), None, |this| this.print_ident(temp));
                 self.word(";");
 
                 // Print `_t`:
@@ -1440,15 +1457,16 @@ impl<'a> State<'a> {
                 }
                 self.bclose(expr.span);
             }
-            hir::ExprKind::Closure {
+            hir::ExprKind::Closure(&hir::Closure {
+                binder,
                 capture_clause,
                 bound_generic_params,
                 fn_decl,
                 body,
                 fn_decl_span: _,
                 movability: _,
-            } => {
-                self.print_formal_generic_params(bound_generic_params);
+            }) => {
+                self.print_closure_binder(binder, bound_generic_params);
                 self.print_capture_clause(capture_clause);
 
                 self.print_closure_params(fn_decl, body);
@@ -2033,6 +2051,42 @@ impl<'a> State<'a> {
         }
     }
 
+    pub fn print_closure_binder(
+        &mut self,
+        binder: hir::ClosureBinder,
+        generic_params: &[GenericParam<'_>],
+    ) {
+        let generic_params = generic_params
+            .iter()
+            .filter(|p| {
+                matches!(
+                    p,
+                    GenericParam {
+                        kind: GenericParamKind::Lifetime { kind: LifetimeParamKind::Explicit },
+                        ..
+                    }
+                )
+            })
+            .collect::<Vec<_>>();
+
+        match binder {
+            hir::ClosureBinder::Default => {}
+            // we need to distinguish `|...| {}` from `for<> |...| {}` as `for<>` adds additional restrictions
+            hir::ClosureBinder::For { .. } if generic_params.is_empty() => self.word("for<>"),
+            hir::ClosureBinder::For { .. } => {
+                self.word("for");
+                self.word("<");
+
+                self.commasep(Inconsistent, &generic_params, |s, param| {
+                    s.print_generic_param(param)
+                });
+
+                self.word(">");
+                self.nbsp();
+            }
+        }
+    }
+
     pub fn print_bounds<'b>(
         &mut self,
         prefix: &'static str,
diff --git a/compiler/rustc_incremental/src/assert_dep_graph.rs b/compiler/rustc_incremental/src/assert_dep_graph.rs
index 93528b4514b..69e482ce854 100644
--- a/compiler/rustc_incremental/src/assert_dep_graph.rs
+++ b/compiler/rustc_incremental/src/assert_dep_graph.rs
@@ -55,11 +55,11 @@ use std::io::{BufWriter, Write};
 #[allow(missing_docs)]
 pub fn assert_dep_graph(tcx: TyCtxt<'_>) {
     tcx.dep_graph.with_ignore(|| {
-        if tcx.sess.opts.debugging_opts.dump_dep_graph {
+        if tcx.sess.opts.unstable_opts.dump_dep_graph {
             tcx.dep_graph.with_query(dump_graph);
         }
 
-        if !tcx.sess.opts.debugging_opts.query_dep_graph {
+        if !tcx.sess.opts.unstable_opts.query_dep_graph {
             return;
         }
 
@@ -81,7 +81,7 @@ pub fn assert_dep_graph(tcx: TyCtxt<'_>) {
 
         if !if_this_changed.is_empty() || !then_this_would_need.is_empty() {
             assert!(
-                tcx.sess.opts.debugging_opts.query_dep_graph,
+                tcx.sess.opts.unstable_opts.query_dep_graph,
                 "cannot use the `#[{}]` or `#[{}]` annotations \
                     without supplying `-Z query-dep-graph`",
                 sym::rustc_if_this_changed,
diff --git a/compiler/rustc_incremental/src/assert_module_sources.rs b/compiler/rustc_incremental/src/assert_module_sources.rs
index 61b1dd8cb01..89d419bc8e9 100644
--- a/compiler/rustc_incremental/src/assert_module_sources.rs
+++ b/compiler/rustc_incremental/src/assert_module_sources.rs
@@ -23,7 +23,7 @@
 //! was re-used.
 
 use rustc_ast as ast;
-use rustc_data_structures::stable_set::FxHashSet;
+use rustc_data_structures::fx::FxHashSet;
 use rustc_hir::def_id::LOCAL_CRATE;
 use rustc_middle::mir::mono::CodegenUnitNameBuilder;
 use rustc_middle::ty::TyCtxt;
@@ -76,7 +76,7 @@ impl<'tcx> AssertModuleSource<'tcx> {
             return;
         };
 
-        if !self.tcx.sess.opts.debugging_opts.query_dep_graph {
+        if !self.tcx.sess.opts.unstable_opts.query_dep_graph {
             self.tcx.sess.span_fatal(
                 attr.span,
                 "found CGU-reuse attribute but `-Zquery-dep-graph` was not specified",
diff --git a/compiler/rustc_incremental/src/persist/dirty_clean.rs b/compiler/rustc_incremental/src/persist/dirty_clean.rs
index 94097357f8c..35a278e6c92 100644
--- a/compiler/rustc_incremental/src/persist/dirty_clean.rs
+++ b/compiler/rustc_incremental/src/persist/dirty_clean.rs
@@ -134,7 +134,7 @@ struct Assertion {
 }
 
 pub fn check_dirty_clean_annotations(tcx: TyCtxt<'_>) {
-    if !tcx.sess.opts.debugging_opts.query_dep_graph {
+    if !tcx.sess.opts.unstable_opts.query_dep_graph {
         return;
     }
 
diff --git a/compiler/rustc_incremental/src/persist/fs.rs b/compiler/rustc_incremental/src/persist/fs.rs
index 9b328842056..25c1b2e1c43 100644
--- a/compiler/rustc_incremental/src/persist/fs.rs
+++ b/compiler/rustc_incremental/src/persist/fs.rs
@@ -447,7 +447,7 @@ fn copy_files(sess: &Session, target_dir: &Path, source_dir: &Path) -> Result<bo
         }
     }
 
-    if sess.opts.debugging_opts.incremental_info {
+    if sess.opts.unstable_opts.incremental_info {
         eprintln!(
             "[incremental] session directory: \
                   {} files hard-linked",
diff --git a/compiler/rustc_incremental/src/persist/load.rs b/compiler/rustc_incremental/src/persist/load.rs
index f59d8d596b9..1c5fd916902 100644
--- a/compiler/rustc_incremental/src/persist/load.rs
+++ b/compiler/rustc_incremental/src/persist/load.rs
@@ -141,7 +141,7 @@ pub fn load_dep_graph(sess: &Session) -> DepGraphFuture {
     // Calling `sess.incr_comp_session_dir()` will panic if `sess.opts.incremental.is_none()`.
     // Fortunately, we just checked that this isn't the case.
     let path = dep_graph_path(&sess);
-    let report_incremental_info = sess.opts.debugging_opts.incremental_info;
+    let report_incremental_info = sess.opts.unstable_opts.incremental_info;
     let expected_hash = sess.opts.dep_tracking_hash(false);
 
     let mut prev_work_products = FxHashMap::default();
@@ -163,7 +163,7 @@ pub fn load_dep_graph(sess: &Session) -> DepGraphFuture {
             for swp in work_products {
                 let all_files_exist = swp.work_product.saved_files.iter().all(|(_, path)| {
                     let exists = in_incr_comp_dir_sess(sess, path).exists();
-                    if !exists && sess.opts.debugging_opts.incremental_info {
+                    if !exists && sess.opts.unstable_opts.incremental_info {
                         eprintln!("incremental: could not find file for work product: {path}",);
                     }
                     exists
@@ -225,7 +225,7 @@ pub fn load_query_result_cache<'a, C: OnDiskCache<'a>>(sess: &'a Session) -> Opt
     let _prof_timer = sess.prof.generic_activity("incr_comp_load_query_result_cache");
 
     match load_data(
-        sess.opts.debugging_opts.incremental_info,
+        sess.opts.unstable_opts.incremental_info,
         &query_cache_path(sess),
         sess.is_nightly_build(),
     ) {
diff --git a/compiler/rustc_incremental/src/persist/save.rs b/compiler/rustc_incremental/src/persist/save.rs
index 4059b7cfc8e..71035031497 100644
--- a/compiler/rustc_incremental/src/persist/save.rs
+++ b/compiler/rustc_incremental/src/persist/save.rs
@@ -39,7 +39,7 @@ pub fn save_dep_graph(tcx: TyCtxt<'_>) {
         sess.time("assert_dep_graph", || crate::assert_dep_graph(tcx));
         sess.time("check_dirty_clean", || dirty_clean::check_dirty_clean_annotations(tcx));
 
-        if sess.opts.debugging_opts.incremental_info {
+        if sess.opts.unstable_opts.incremental_info {
             tcx.dep_graph.print_incremental_info()
         }
 
@@ -182,7 +182,7 @@ pub fn build_dep_graph(
         prev_graph,
         prev_work_products,
         encoder,
-        sess.opts.debugging_opts.query_dep_graph,
-        sess.opts.debugging_opts.incremental_info,
+        sess.opts.unstable_opts.query_dep_graph,
+        sess.opts.unstable_opts.incremental_info,
     ))
 }
diff --git a/compiler/rustc_incremental/src/persist/work_product.rs b/compiler/rustc_incremental/src/persist/work_product.rs
index 1b184eca964..2f1853c441e 100644
--- a/compiler/rustc_incremental/src/persist/work_product.rs
+++ b/compiler/rustc_incremental/src/persist/work_product.rs
@@ -3,7 +3,7 @@
 //! [work products]: WorkProduct
 
 use crate::persist::fs::*;
-use rustc_data_structures::stable_map::FxHashMap;
+use rustc_data_structures::fx::FxHashMap;
 use rustc_fs_util::link_or_copy;
 use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
 use rustc_session::Session;
diff --git a/compiler/rustc_index/src/vec.rs b/compiler/rustc_index/src/vec.rs
index 1a55519d7b1..30ff364210d 100644
--- a/compiler/rustc_index/src/vec.rs
+++ b/compiler/rustc_index/src/vec.rs
@@ -234,7 +234,9 @@ impl<I: Idx, T> IndexVec<I, T> {
         self.raw.get_mut(index.index())
     }
 
-    /// Returns mutable references to two distinct elements, a and b. Panics if a == b.
+    /// Returns mutable references to two distinct elements, `a` and `b`.
+    ///
+    /// Panics if `a == b`.
     #[inline]
     pub fn pick2_mut(&mut self, a: I, b: I) -> (&mut T, &mut T) {
         let (ai, bi) = (a.index(), b.index());
@@ -249,7 +251,9 @@ impl<I: Idx, T> IndexVec<I, T> {
         }
     }
 
-    /// Returns mutable references to three distinct elements or panics otherwise.
+    /// Returns mutable references to three distinct elements.
+    ///
+    /// Panics if the elements are not distinct.
     #[inline]
     pub fn pick3_mut(&mut self, a: I, b: I, c: I) -> (&mut T, &mut T, &mut T) {
         let (ai, bi, ci) = (a.index(), b.index(), c.index());
diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs
index ce2698ef44c..130214a653f 100644
--- a/compiler/rustc_infer/src/infer/at.rs
+++ b/compiler/rustc_infer/src/infer/at.rs
@@ -65,6 +65,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         Self {
             tcx: self.tcx,
             defining_use_anchor: self.defining_use_anchor,
+            considering_regions: self.considering_regions,
             in_progress_typeck_results: self.in_progress_typeck_results,
             inner: self.inner.clone(),
             skip_leak_check: self.skip_leak_check.clone(),
diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs
index 7120d5ad934..8dc20544f1b 100644
--- a/compiler/rustc_infer/src/infer/canonical/query_response.rs
+++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs
@@ -153,7 +153,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
             .opaque_type_storage
             .take_opaque_types()
             .into_iter()
-            .map(|(k, v)| (self.tcx.mk_opaque(k.def_id, k.substs), v.hidden_type.ty))
+            .map(|(k, v)| (self.tcx.mk_opaque(k.def_id.to_def_id(), k.substs), v.hidden_type.ty))
             .collect()
     }
 
@@ -714,10 +714,7 @@ impl<'tcx> TypeRelatingDelegate<'tcx> for QueryTypeRelatingDelegate<'_, 'tcx> {
     }
 
     fn const_equate(&mut self, _a: Const<'tcx>, _b: Const<'tcx>) {
-        span_bug!(
-            self.cause.span(self.infcx.tcx),
-            "generic_const_exprs: unreachable `const_equate`"
-        );
+        span_bug!(self.cause.span(), "generic_const_exprs: unreachable `const_equate`");
     }
 
     fn normalization() -> NormalizationStrategy {
diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index 05556f7d0f9..39faed0bf36 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -63,7 +63,7 @@ use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString, Mul
 use rustc_hir as hir;
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_hir::lang_items::LangItem;
-use rustc_hir::{Item, ItemKind, Node};
+use rustc_hir::Node;
 use rustc_middle::dep_graph::DepContext;
 use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_middle::ty::{
@@ -237,12 +237,14 @@ pub fn unexpected_hidden_region_diagnostic<'tcx>(
     span: Span,
     hidden_ty: Ty<'tcx>,
     hidden_region: ty::Region<'tcx>,
+    opaque_ty: ty::OpaqueTypeKey<'tcx>,
 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+    let opaque_ty = tcx.mk_opaque(opaque_ty.def_id.to_def_id(), opaque_ty.substs);
     let mut err = struct_span_err!(
         tcx.sess,
         span,
         E0700,
-        "hidden type for `impl Trait` captures lifetime that does not appear in bounds",
+        "hidden type for `{opaque_ty}` captures lifetime that does not appear in bounds",
     );
 
     // Explain the region we are capturing.
@@ -316,39 +318,12 @@ pub fn unexpected_hidden_region_diagnostic<'tcx>(
     err
 }
 
-/// Structurally compares two types, modulo any inference variables.
-///
-/// Returns `true` if two types are equal, or if one type is an inference variable compatible
-/// with the other type. A TyVar inference type is compatible with any type, and an IntVar or
-/// FloatVar inference type are compatible with themselves or their concrete types (Int and
-/// Float types, respectively). When comparing two ADTs, these rules apply recursively.
-pub fn same_type_modulo_infer<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
-    match (&a.kind(), &b.kind()) {
-        (&ty::Adt(did_a, substs_a), &ty::Adt(did_b, substs_b)) => {
-            if did_a != did_b {
-                return false;
-            }
-
-            substs_a.types().zip(substs_b.types()).all(|(a, b)| same_type_modulo_infer(a, b))
-        }
-        (&ty::Int(_), &ty::Infer(ty::InferTy::IntVar(_)))
-        | (&ty::Infer(ty::InferTy::IntVar(_)), &ty::Int(_) | &ty::Infer(ty::InferTy::IntVar(_)))
-        | (&ty::Float(_), &ty::Infer(ty::InferTy::FloatVar(_)))
-        | (
-            &ty::Infer(ty::InferTy::FloatVar(_)),
-            &ty::Float(_) | &ty::Infer(ty::InferTy::FloatVar(_)),
-        )
-        | (&ty::Infer(ty::InferTy::TyVar(_)), _)
-        | (_, &ty::Infer(ty::InferTy::TyVar(_))) => true,
-        (&ty::Ref(_, ty_a, mut_a), &ty::Ref(_, ty_b, mut_b)) => {
-            mut_a == mut_b && same_type_modulo_infer(*ty_a, *ty_b)
-        }
-        _ => a == b,
-    }
-}
-
 impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
-    pub fn report_region_errors(&self, errors: &[RegionResolutionError<'tcx>]) {
+    pub fn report_region_errors(
+        &self,
+        generic_param_scope: LocalDefId,
+        errors: &[RegionResolutionError<'tcx>],
+    ) {
         debug!("report_region_errors(): {} errors to start", errors.len());
 
         // try to pre-process the errors, which will group some of them
@@ -379,6 +354,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
 
                     RegionResolutionError::GenericBoundFailure(origin, param_ty, sub) => {
                         self.report_generic_bound_failure(
+                            generic_param_scope,
                             origin.span(),
                             Some(origin),
                             param_ty,
@@ -640,13 +616,16 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                 err.span_label(span, "expected due to this");
             }
             ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause {
-                semi_span,
+                arm_block_id,
+                arm_span,
+                arm_ty,
+                prior_arm_block_id,
+                prior_arm_span,
+                prior_arm_ty,
                 source,
                 ref prior_arms,
-                last_ty,
                 scrut_hir_id,
                 opt_suggest_box_span,
-                arm_span,
                 scrut_span,
                 ..
             }) => match source {
@@ -677,10 +656,10 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                     }
                 }
                 _ => {
-                    // `last_ty` can be `!`, `expected` will have better info when present.
+                    // `prior_arm_ty` can be `!`, `expected` will have better info when present.
                     let t = self.resolve_vars_if_possible(match exp_found {
                         Some(ty::error::ExpectedFound { expected, .. }) => expected,
-                        _ => last_ty,
+                        _ => prior_arm_ty,
                     });
                     let source_map = self.tcx.sess.source_map();
                     let mut any_multiline_arm = source_map.is_multiline(arm_span);
@@ -705,37 +684,15 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                     };
                     let msg = "`match` arms have incompatible types";
                     err.span_label(outer_error_span, msg);
-                    if let Some((sp, boxed)) = semi_span {
-                        if let (StatementAsExpression::NeedsBoxing, [.., prior_arm]) =
-                            (boxed, &prior_arms[..])
-                        {
-                            err.multipart_suggestion(
-                                "consider removing this semicolon and boxing the expressions",
-                                vec![
-                                    (prior_arm.shrink_to_lo(), "Box::new(".to_string()),
-                                    (prior_arm.shrink_to_hi(), ")".to_string()),
-                                    (arm_span.shrink_to_lo(), "Box::new(".to_string()),
-                                    (arm_span.shrink_to_hi(), ")".to_string()),
-                                    (sp, String::new()),
-                                ],
-                                Applicability::HasPlaceholders,
-                            );
-                        } else if matches!(boxed, StatementAsExpression::NeedsBoxing) {
-                            err.span_suggestion_short(
-                                sp,
-                                "consider removing this semicolon and boxing the expressions",
-                                "",
-                                Applicability::MachineApplicable,
-                            );
-                        } else {
-                            err.span_suggestion_short(
-                                sp,
-                                "consider removing this semicolon",
-                                "",
-                                Applicability::MachineApplicable,
-                            );
-                        }
-                    }
+                    self.suggest_remove_semi_or_return_binding(
+                        err,
+                        prior_arm_block_id,
+                        prior_arm_ty,
+                        prior_arm_span,
+                        arm_block_id,
+                        arm_ty,
+                        arm_span,
+                    );
                     if let Some(ret_sp) = opt_suggest_box_span {
                         // Get return type span and point to it.
                         self.suggest_boxing_for_return_impl_trait(
@@ -747,43 +704,33 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                 }
             },
             ObligationCauseCode::IfExpression(box IfExpressionCause {
-                then,
-                else_sp,
-                outer,
-                semicolon,
+                then_id,
+                else_id,
+                then_ty,
+                else_ty,
+                outer_span,
                 opt_suggest_box_span,
             }) => {
-                err.span_label(then, "expected because of this");
-                if let Some(sp) = outer {
+                let then_span = self.find_block_span_from_hir_id(then_id);
+                let else_span = self.find_block_span_from_hir_id(then_id);
+                err.span_label(then_span, "expected because of this");
+                if let Some(sp) = outer_span {
                     err.span_label(sp, "`if` and `else` have incompatible types");
                 }
-                if let Some((sp, boxed)) = semicolon {
-                    if matches!(boxed, StatementAsExpression::NeedsBoxing) {
-                        err.multipart_suggestion(
-                            "consider removing this semicolon and boxing the expression",
-                            vec![
-                                (then.shrink_to_lo(), "Box::new(".to_string()),
-                                (then.shrink_to_hi(), ")".to_string()),
-                                (else_sp.shrink_to_lo(), "Box::new(".to_string()),
-                                (else_sp.shrink_to_hi(), ")".to_string()),
-                                (sp, String::new()),
-                            ],
-                            Applicability::MachineApplicable,
-                        );
-                    } else {
-                        err.span_suggestion_short(
-                            sp,
-                            "consider removing this semicolon",
-                            "",
-                            Applicability::MachineApplicable,
-                        );
-                    }
-                }
+                self.suggest_remove_semi_or_return_binding(
+                    err,
+                    Some(then_id),
+                    then_ty,
+                    then_span,
+                    Some(else_id),
+                    else_ty,
+                    else_span,
+                );
                 if let Some(ret_sp) = opt_suggest_box_span {
                     self.suggest_boxing_for_return_impl_trait(
                         err,
                         ret_sp,
-                        [then, else_sp].into_iter(),
+                        [then_span, else_span].into_iter(),
                     );
                 }
             }
@@ -803,6 +750,56 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         }
     }
 
+    fn suggest_remove_semi_or_return_binding(
+        &self,
+        err: &mut Diagnostic,
+        first_id: Option<hir::HirId>,
+        first_ty: Ty<'tcx>,
+        first_span: Span,
+        second_id: Option<hir::HirId>,
+        second_ty: Ty<'tcx>,
+        second_span: Span,
+    ) {
+        let remove_semicolon =
+            [(first_id, second_ty), (second_id, first_ty)].into_iter().find_map(|(id, ty)| {
+                let hir::Node::Block(blk) = self.tcx.hir().get(id?) else { return None };
+                self.could_remove_semicolon(blk, ty)
+            });
+        match remove_semicolon {
+            Some((sp, StatementAsExpression::NeedsBoxing)) => {
+                err.multipart_suggestion(
+                    "consider removing this semicolon and boxing the expressions",
+                    vec![
+                        (first_span.shrink_to_lo(), "Box::new(".to_string()),
+                        (first_span.shrink_to_hi(), ")".to_string()),
+                        (second_span.shrink_to_lo(), "Box::new(".to_string()),
+                        (second_span.shrink_to_hi(), ")".to_string()),
+                        (sp, String::new()),
+                    ],
+                    Applicability::MachineApplicable,
+                );
+            }
+            Some((sp, StatementAsExpression::CorrectType)) => {
+                err.span_suggestion_short(
+                    sp,
+                    "consider removing this semicolon",
+                    "",
+                    Applicability::MachineApplicable,
+                );
+            }
+            None => {
+                for (id, ty) in [(first_id, second_ty), (second_id, first_ty)] {
+                    if let Some(id) = id
+                        && let hir::Node::Block(blk) = self.tcx.hir().get(id)
+                        && self.consider_returning_binding(blk, ty, err)
+                    {
+                        break;
+                    }
+                }
+            }
+        }
+    }
+
     fn suggest_boxing_for_return_impl_trait(
         &self,
         err: &mut Diagnostic,
@@ -1435,7 +1432,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         swap_secondary_and_primary: bool,
         force_label: bool,
     ) {
-        let span = cause.span(self.tcx);
+        let span = cause.span();
 
         // For some types of errors, expected-found does not make
         // sense, so just ignore the values we were given.
@@ -1718,15 +1715,14 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         };
         debug!("exp_found {:?} terr {:?} cause.code {:?}", exp_found, terr, cause.code());
         if let Some(exp_found) = exp_found {
-            let should_suggest_fixes = if let ObligationCauseCode::Pattern { root_ty, .. } =
-                cause.code()
-            {
-                // Skip if the root_ty of the pattern is not the same as the expected_ty.
-                // If these types aren't equal then we've probably peeled off a layer of arrays.
-                same_type_modulo_infer(self.resolve_vars_if_possible(*root_ty), exp_found.expected)
-            } else {
-                true
-            };
+            let should_suggest_fixes =
+                if let ObligationCauseCode::Pattern { root_ty, .. } = cause.code() {
+                    // Skip if the root_ty of the pattern is not the same as the expected_ty.
+                    // If these types aren't equal then we've probably peeled off a layer of arrays.
+                    self.same_type_modulo_infer(*root_ty, exp_found.expected)
+                } else {
+                    true
+                };
 
             if should_suggest_fixes {
                 self.suggest_tuple_pattern(cause, &exp_found, diag);
@@ -1781,7 +1777,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                     .filter_map(|variant| {
                         let sole_field = &variant.fields[0];
                         let sole_field_ty = sole_field.ty(self.tcx, substs);
-                        if same_type_modulo_infer(sole_field_ty, exp_found.found) {
+                        if self.same_type_modulo_infer(sole_field_ty, exp_found.found) {
                             let variant_path =
                                 with_no_trimmed_paths!(self.tcx.def_path_str(variant.def_id));
                             // FIXME #56861: DRYer prelude filtering
@@ -1889,7 +1885,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
             exp_span, exp_found.expected, exp_found.found,
         );
 
-        if let ObligationCauseCode::CompareImplMethodObligation { .. } = cause.code() {
+        if let ObligationCauseCode::CompareImplItemObligation { .. } = cause.code() {
             return;
         }
 
@@ -1897,12 +1893,15 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
             self.get_impl_future_output_ty(exp_found.expected).map(Binder::skip_binder),
             self.get_impl_future_output_ty(exp_found.found).map(Binder::skip_binder),
         ) {
-            (Some(exp), Some(found)) if same_type_modulo_infer(exp, found) => match cause.code() {
-                ObligationCauseCode::IfExpression(box IfExpressionCause { then, .. }) => {
+            (Some(exp), Some(found)) if self.same_type_modulo_infer(exp, found) => match cause
+                .code()
+            {
+                ObligationCauseCode::IfExpression(box IfExpressionCause { then_id, .. }) => {
+                    let then_span = self.find_block_span_from_hir_id(*then_id);
                     diag.multipart_suggestion(
                         "consider `await`ing on both `Future`s",
                         vec![
-                            (then.shrink_to_hi(), ".await".to_string()),
+                            (then_span.shrink_to_hi(), ".await".to_string()),
                             (exp_span.shrink_to_hi(), ".await".to_string()),
                         ],
                         Applicability::MaybeIncorrect,
@@ -1929,7 +1928,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                     diag.help("consider `await`ing on both `Future`s");
                 }
             },
-            (_, Some(ty)) if same_type_modulo_infer(exp_found.expected, ty) => {
+            (_, Some(ty)) if self.same_type_modulo_infer(exp_found.expected, ty) => {
                 diag.span_suggestion_verbose(
                     exp_span.shrink_to_hi(),
                     "consider `await`ing on the `Future`",
@@ -1937,11 +1936,20 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                     Applicability::MaybeIncorrect,
                 );
             }
-            (Some(ty), _) if same_type_modulo_infer(ty, exp_found.found) => match cause.code() {
-                ObligationCauseCode::Pattern { span: Some(span), .. }
-                | ObligationCauseCode::IfExpression(box IfExpressionCause { then: span, .. }) => {
+            (Some(ty), _) if self.same_type_modulo_infer(ty, exp_found.found) => match cause.code()
+            {
+                ObligationCauseCode::Pattern { span: Some(then_span), .. } => {
+                    diag.span_suggestion_verbose(
+                        then_span.shrink_to_hi(),
+                        "consider `await`ing on the `Future`",
+                        ".await",
+                        Applicability::MaybeIncorrect,
+                    );
+                }
+                ObligationCauseCode::IfExpression(box IfExpressionCause { then_id, .. }) => {
+                    let then_span = self.find_block_span_from_hir_id(*then_id);
                     diag.span_suggestion_verbose(
-                        span.shrink_to_hi(),
+                        then_span.shrink_to_hi(),
                         "consider `await`ing on the `Future`",
                         ".await",
                         Applicability::MaybeIncorrect,
@@ -1987,7 +1995,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                 .iter()
                 .filter(|field| field.vis.is_accessible_from(field.did, self.tcx))
                 .map(|field| (field.name, field.ty(self.tcx, expected_substs)))
-                .find(|(_, ty)| same_type_modulo_infer(*ty, exp_found.found))
+                .find(|(_, ty)| self.same_type_modulo_infer(*ty, exp_found.found))
             {
                 if let ObligationCauseCode::Pattern { span: Some(span), .. } = *cause.code() {
                     if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
@@ -2052,7 +2060,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                                         | (_, ty::Infer(_))
                                         | (ty::Param(_), _)
                                         | (ty::Infer(_), _) => {}
-                                        _ if same_type_modulo_infer(exp_ty, found_ty) => {}
+                                        _ if self.same_type_modulo_infer(exp_ty, found_ty) => {}
                                         _ => show_suggestion = false,
                                     };
                                 }
@@ -2085,7 +2093,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
 
         debug!("report_and_explain_type_error(trace={:?}, terr={:?})", trace, terr);
 
-        let span = trace.cause.span(self.tcx);
+        let span = trace.cause.span();
         let failure_code = trace.cause.as_failure_code(terr);
         let mut diag = match failure_code {
             FailureCode::Error0038(did) => {
@@ -2174,7 +2182,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
     ) {
         let [expected_tup_elem] = expected_fields[..] else { return };
 
-        if !same_type_modulo_infer(expected_tup_elem, found) {
+        if !self.same_type_modulo_infer(expected_tup_elem, found) {
             return;
         }
 
@@ -2269,56 +2277,30 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
 
     pub fn report_generic_bound_failure(
         &self,
+        generic_param_scope: LocalDefId,
         span: Span,
         origin: Option<SubregionOrigin<'tcx>>,
         bound_kind: GenericKind<'tcx>,
         sub: Region<'tcx>,
     ) {
-        let owner =
-            self.in_progress_typeck_results.map(|typeck_results| typeck_results.borrow().hir_owner);
-        self.construct_generic_bound_failure(span, origin, bound_kind, sub, owner).emit();
+        self.construct_generic_bound_failure(generic_param_scope, span, origin, bound_kind, sub)
+            .emit();
     }
 
     pub fn construct_generic_bound_failure(
         &self,
+        generic_param_scope: LocalDefId,
         span: Span,
         origin: Option<SubregionOrigin<'tcx>>,
         bound_kind: GenericKind<'tcx>,
         sub: Region<'tcx>,
-        owner: Option<LocalDefId>,
     ) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
-        let hir = self.tcx.hir();
         // Attempt to obtain the span of the parameter so we can
         // suggest adding an explicit lifetime bound to it.
-        let generics = owner.map(|owner| {
-            let hir_id = hir.local_def_id_to_hir_id(owner);
-            let parent_id = hir.get_parent_item(hir_id);
-            (
-                // Parent item could be a `mod`, so we check the HIR before calling:
-                if let Some(Node::Item(Item {
-                    kind: ItemKind::Trait(..) | ItemKind::Impl { .. },
-                    ..
-                })) = hir.find_by_def_id(parent_id)
-                {
-                    Some(self.tcx.generics_of(parent_id))
-                } else {
-                    None
-                },
-                self.tcx.generics_of(owner.to_def_id()),
-                hir.span(hir_id),
-            )
-        });
-
-        let span = match generics {
-            // This is to get around the trait identity obligation, that has a `DUMMY_SP` as signal
-            // for other diagnostics, so we need to recover it here.
-            Some((_, _, node)) if span.is_dummy() => node,
-            _ => span,
-        };
-
+        let generics = self.tcx.generics_of(generic_param_scope);
         // type_param_span is (span, has_bounds)
-        let type_param_span = match (generics, bound_kind) {
-            (Some((_, ref generics, _)), GenericKind::Param(ref param)) => {
+        let type_param_span = match bound_kind {
+            GenericKind::Param(ref param) => {
                 // Account for the case where `param` corresponds to `Self`,
                 // which doesn't have the expected type argument.
                 if !(generics.has_self && param.index == 0) {
@@ -2346,30 +2328,23 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
             }
             _ => None,
         };
-        let new_lt = generics
-            .as_ref()
-            .and_then(|(parent_g, g, _)| {
-                let mut possible = (b'a'..=b'z').map(|c| format!("'{}", c as char));
-                let mut lts_names = g
-                    .params
-                    .iter()
+
+        let new_lt = {
+            let mut possible = (b'a'..=b'z').map(|c| format!("'{}", c as char));
+            let lts_names =
+                iter::successors(Some(generics), |g| g.parent.map(|p| self.tcx.generics_of(p)))
+                    .flat_map(|g| &g.params)
                     .filter(|p| matches!(p.kind, ty::GenericParamDefKind::Lifetime))
                     .map(|p| p.name.as_str())
                     .collect::<Vec<_>>();
-                if let Some(g) = parent_g {
-                    lts_names.extend(
-                        g.params
-                            .iter()
-                            .filter(|p| matches!(p.kind, ty::GenericParamDefKind::Lifetime))
-                            .map(|p| p.name.as_str()),
-                    );
-                }
-                possible.find(|candidate| !lts_names.contains(&&candidate[..]))
-            })
-            .unwrap_or("'lt".to_string());
+            possible
+                .find(|candidate| !lts_names.contains(&&candidate[..]))
+                .unwrap_or("'lt".to_string())
+        };
+
         let add_lt_sugg = generics
-            .as_ref()
-            .and_then(|(_, g, _)| g.params.first())
+            .params
+            .first()
             .and_then(|param| param.def_id.as_local())
             .map(|def_id| (self.tcx.def_span(def_id).shrink_to_lo(), format!("{}, ", new_lt)));
 
@@ -2378,7 +2353,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
             GenericKind::Projection(ref p) => format!("the associated type `{}`", p),
         };
 
-        if let Some(SubregionOrigin::CompareImplMethodObligation {
+        if let Some(SubregionOrigin::CompareImplItemObligation {
             span,
             impl_item_def_id,
             trait_item_def_id,
@@ -2437,9 +2412,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         #[derive(Debug)]
         enum SubOrigin<'hir> {
             GAT(&'hir hir::Generics<'hir>),
-            Impl(&'hir hir::Generics<'hir>),
-            Trait(&'hir hir::Generics<'hir>),
-            Fn(&'hir hir::Generics<'hir>),
+            Impl,
+            Trait,
+            Fn,
             Unknown,
         }
         let sub_origin = 'origin: {
@@ -2454,34 +2429,30 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                                         kind: hir::ImplItemKind::TyAlias(..),
                                         generics,
                                         ..
-                                    }) => SubOrigin::GAT(generics),
-                                    Node::ImplItem(hir::ImplItem {
-                                        kind: hir::ImplItemKind::Fn(..),
-                                        generics,
-                                        ..
-                                    }) => SubOrigin::Fn(generics),
-                                    Node::TraitItem(hir::TraitItem {
+                                    })
+                                    | Node::TraitItem(hir::TraitItem {
                                         kind: hir::TraitItemKind::Type(..),
                                         generics,
                                         ..
                                     }) => SubOrigin::GAT(generics),
-                                    Node::TraitItem(hir::TraitItem {
-                                        kind: hir::TraitItemKind::Fn(..),
-                                        generics,
+                                    Node::ImplItem(hir::ImplItem {
+                                        kind: hir::ImplItemKind::Fn(..),
                                         ..
-                                    }) => SubOrigin::Fn(generics),
-                                    Node::Item(hir::Item {
-                                        kind: hir::ItemKind::Trait(_, _, generics, _, _),
+                                    })
+                                    | Node::TraitItem(hir::TraitItem {
+                                        kind: hir::TraitItemKind::Fn(..),
                                         ..
-                                    }) => SubOrigin::Trait(generics),
+                                    })
+                                    | Node::Item(hir::Item {
+                                        kind: hir::ItemKind::Fn(..), ..
+                                    }) => SubOrigin::Fn,
                                     Node::Item(hir::Item {
-                                        kind: hir::ItemKind::Impl(hir::Impl { generics, .. }),
+                                        kind: hir::ItemKind::Trait(..),
                                         ..
-                                    }) => SubOrigin::Impl(generics),
+                                    }) => SubOrigin::Trait,
                                     Node::Item(hir::Item {
-                                        kind: hir::ItemKind::Fn(_, generics, _),
-                                        ..
-                                    }) => SubOrigin::Fn(generics),
+                                        kind: hir::ItemKind::Impl(..), ..
+                                    }) => SubOrigin::Impl,
                                     _ => continue,
                                 };
                             }
@@ -2571,7 +2542,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                 );
                 if let Some(infer::RelateParamBound(_, t, _)) = origin {
                     let return_impl_trait =
-                        owner.and_then(|owner| self.tcx.return_type_impl_trait(owner)).is_some();
+                        self.tcx.return_type_impl_trait(generic_param_scope).is_some();
                     let t = self.resolve_vars_if_possible(t);
                     match t.kind() {
                         // We've got:
@@ -2675,6 +2646,76 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         span.is_desugaring(DesugaringKind::QuestionMark)
             && self.tcx.is_diagnostic_item(sym::From, trait_def_id)
     }
+
+    /// Structurally compares two types, modulo any inference variables.
+    ///
+    /// Returns `true` if two types are equal, or if one type is an inference variable compatible
+    /// with the other type. A TyVar inference type is compatible with any type, and an IntVar or
+    /// FloatVar inference type are compatible with themselves or their concrete types (Int and
+    /// Float types, respectively). When comparing two ADTs, these rules apply recursively.
+    pub fn same_type_modulo_infer(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
+        let (a, b) = self.resolve_vars_if_possible((a, b));
+        match (a.kind(), b.kind()) {
+            (&ty::Adt(def_a, substs_a), &ty::Adt(def_b, substs_b)) => {
+                if def_a != def_b {
+                    return false;
+                }
+
+                substs_a
+                    .types()
+                    .zip(substs_b.types())
+                    .all(|(a, b)| self.same_type_modulo_infer(a, b))
+            }
+            (&ty::FnDef(did_a, substs_a), &ty::FnDef(did_b, substs_b)) => {
+                if did_a != did_b {
+                    return false;
+                }
+
+                substs_a
+                    .types()
+                    .zip(substs_b.types())
+                    .all(|(a, b)| self.same_type_modulo_infer(a, b))
+            }
+            (&ty::Int(_) | &ty::Uint(_), &ty::Infer(ty::InferTy::IntVar(_)))
+            | (
+                &ty::Infer(ty::InferTy::IntVar(_)),
+                &ty::Int(_) | &ty::Uint(_) | &ty::Infer(ty::InferTy::IntVar(_)),
+            )
+            | (&ty::Float(_), &ty::Infer(ty::InferTy::FloatVar(_)))
+            | (
+                &ty::Infer(ty::InferTy::FloatVar(_)),
+                &ty::Float(_) | &ty::Infer(ty::InferTy::FloatVar(_)),
+            )
+            | (&ty::Infer(ty::InferTy::TyVar(_)), _)
+            | (_, &ty::Infer(ty::InferTy::TyVar(_))) => true,
+            (&ty::Ref(_, ty_a, mut_a), &ty::Ref(_, ty_b, mut_b)) => {
+                mut_a == mut_b && self.same_type_modulo_infer(ty_a, ty_b)
+            }
+            (&ty::RawPtr(a), &ty::RawPtr(b)) => {
+                a.mutbl == b.mutbl && self.same_type_modulo_infer(a.ty, b.ty)
+            }
+            (&ty::Slice(a), &ty::Slice(b)) => self.same_type_modulo_infer(a, b),
+            (&ty::Array(a_ty, a_ct), &ty::Array(b_ty, b_ct)) => {
+                self.same_type_modulo_infer(a_ty, b_ty) && a_ct == b_ct
+            }
+            (&ty::Tuple(a), &ty::Tuple(b)) => {
+                if a.len() != b.len() {
+                    return false;
+                }
+                std::iter::zip(a.iter(), b.iter()).all(|(a, b)| self.same_type_modulo_infer(a, b))
+            }
+            (&ty::FnPtr(a), &ty::FnPtr(b)) => {
+                let a = a.skip_binder().inputs_and_output;
+                let b = b.skip_binder().inputs_and_output;
+                if a.len() != b.len() {
+                    return false;
+                }
+                std::iter::zip(a.iter(), b.iter()).all(|(a, b)| self.same_type_modulo_infer(a, b))
+            }
+            // FIXME(compiler-errors): This needs to be generalized more
+            _ => a == b,
+        }
+    }
 }
 
 impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
@@ -2745,8 +2786,15 @@ impl<'tcx> ObligationCauseExt<'tcx> for ObligationCause<'tcx> {
         use self::FailureCode::*;
         use crate::traits::ObligationCauseCode::*;
         match self.code() {
-            CompareImplMethodObligation { .. } => Error0308("method not compatible with trait"),
-            CompareImplTypeObligation { .. } => Error0308("type not compatible with trait"),
+            CompareImplItemObligation { kind: ty::AssocKind::Fn, .. } => {
+                Error0308("method not compatible with trait")
+            }
+            CompareImplItemObligation { kind: ty::AssocKind::Type, .. } => {
+                Error0308("type not compatible with trait")
+            }
+            CompareImplItemObligation { kind: ty::AssocKind::Const, .. } => {
+                Error0308("const not compatible with trait")
+            }
             MatchExpressionArm(box MatchExpressionArmCause { source, .. }) => {
                 Error0308(match source {
                     hir::MatchSource::TryDesugar => "`?` operator has incompatible types",
@@ -2780,8 +2828,15 @@ impl<'tcx> ObligationCauseExt<'tcx> for ObligationCause<'tcx> {
     fn as_requirement_str(&self) -> &'static str {
         use crate::traits::ObligationCauseCode::*;
         match self.code() {
-            CompareImplMethodObligation { .. } => "method type is compatible with trait",
-            CompareImplTypeObligation { .. } => "associated type is compatible with trait",
+            CompareImplItemObligation { kind: ty::AssocKind::Fn, .. } => {
+                "method type is compatible with trait"
+            }
+            CompareImplItemObligation { kind: ty::AssocKind::Type, .. } => {
+                "associated type is compatible with trait"
+            }
+            CompareImplItemObligation { kind: ty::AssocKind::Const, .. } => {
+                "const is compatible with trait"
+            }
             ExprAssignable => "expression is assignable",
             IfExpression { .. } => "`if` and `else` have incompatible types",
             IfExpressionWithNoElse => "`if` missing an `else` returns `()`",
@@ -2826,3 +2881,237 @@ impl TyCategory {
         }
     }
 }
+
+impl<'tcx> InferCtxt<'_, 'tcx> {
+    /// Given a [`hir::Block`], get the span of its last expression or
+    /// statement, peeling off any inner blocks.
+    pub fn find_block_span(&self, block: &'tcx hir::Block<'tcx>) -> Span {
+        let block = block.innermost_block();
+        if let Some(expr) = &block.expr {
+            expr.span
+        } else if let Some(stmt) = block.stmts.last() {
+            // possibly incorrect trailing `;` in the else arm
+            stmt.span
+        } else {
+            // empty block; point at its entirety
+            block.span
+        }
+    }
+
+    /// Given a [`hir::HirId`] for a block, get the span of its last expression
+    /// or statement, peeling off any inner blocks.
+    pub fn find_block_span_from_hir_id(&self, hir_id: hir::HirId) -> Span {
+        match self.tcx.hir().get(hir_id) {
+            hir::Node::Block(blk) => self.find_block_span(blk),
+            // The parser was in a weird state if either of these happen, but
+            // it's better not to panic.
+            hir::Node::Expr(e) => e.span,
+            _ => rustc_span::DUMMY_SP,
+        }
+    }
+
+    /// Be helpful when the user wrote `{... expr; }` and taking the `;` off
+    /// is enough to fix the error.
+    pub fn could_remove_semicolon(
+        &self,
+        blk: &'tcx hir::Block<'tcx>,
+        expected_ty: Ty<'tcx>,
+    ) -> Option<(Span, StatementAsExpression)> {
+        let blk = blk.innermost_block();
+        // Do not suggest if we have a tail expr.
+        if blk.expr.is_some() {
+            return None;
+        }
+        let last_stmt = blk.stmts.last()?;
+        let hir::StmtKind::Semi(ref last_expr) = last_stmt.kind else {
+            return None;
+        };
+        let last_expr_ty = self.in_progress_typeck_results?.borrow().expr_ty_opt(*last_expr)?;
+        let needs_box = match (last_expr_ty.kind(), expected_ty.kind()) {
+            _ if last_expr_ty.references_error() => return None,
+            _ if self.same_type_modulo_infer(last_expr_ty, expected_ty) => {
+                StatementAsExpression::CorrectType
+            }
+            (ty::Opaque(last_def_id, _), ty::Opaque(exp_def_id, _))
+                if last_def_id == exp_def_id =>
+            {
+                StatementAsExpression::CorrectType
+            }
+            (ty::Opaque(last_def_id, last_bounds), ty::Opaque(exp_def_id, exp_bounds)) => {
+                debug!(
+                    "both opaque, likely future {:?} {:?} {:?} {:?}",
+                    last_def_id, last_bounds, exp_def_id, exp_bounds
+                );
+
+                let last_local_id = last_def_id.as_local()?;
+                let exp_local_id = exp_def_id.as_local()?;
+
+                match (
+                    &self.tcx.hir().expect_item(last_local_id).kind,
+                    &self.tcx.hir().expect_item(exp_local_id).kind,
+                ) {
+                    (
+                        hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: last_bounds, .. }),
+                        hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: exp_bounds, .. }),
+                    ) if iter::zip(*last_bounds, *exp_bounds).all(|(left, right)| {
+                        match (left, right) {
+                            (
+                                hir::GenericBound::Trait(tl, ml),
+                                hir::GenericBound::Trait(tr, mr),
+                            ) if tl.trait_ref.trait_def_id() == tr.trait_ref.trait_def_id()
+                                && ml == mr =>
+                            {
+                                true
+                            }
+                            (
+                                hir::GenericBound::LangItemTrait(langl, _, _, argsl),
+                                hir::GenericBound::LangItemTrait(langr, _, _, argsr),
+                            ) if langl == langr => {
+                                // FIXME: consider the bounds!
+                                debug!("{:?} {:?}", argsl, argsr);
+                                true
+                            }
+                            _ => false,
+                        }
+                    }) =>
+                    {
+                        StatementAsExpression::NeedsBoxing
+                    }
+                    _ => StatementAsExpression::CorrectType,
+                }
+            }
+            _ => return None,
+        };
+        let span = if last_stmt.span.from_expansion() {
+            let mac_call = rustc_span::source_map::original_sp(last_stmt.span, blk.span);
+            self.tcx.sess.source_map().mac_call_stmt_semi_span(mac_call)?
+        } else {
+            last_stmt.span.with_lo(last_stmt.span.hi() - BytePos(1))
+        };
+        Some((span, needs_box))
+    }
+
+    /// Suggest returning a local binding with a compatible type if the block
+    /// has no return expression.
+    pub fn consider_returning_binding(
+        &self,
+        blk: &'tcx hir::Block<'tcx>,
+        expected_ty: Ty<'tcx>,
+        err: &mut Diagnostic,
+    ) -> bool {
+        let blk = blk.innermost_block();
+        // Do not suggest if we have a tail expr.
+        if blk.expr.is_some() {
+            return false;
+        }
+        let mut shadowed = FxHashSet::default();
+        let mut candidate_idents = vec![];
+        let mut find_compatible_candidates = |pat: &hir::Pat<'_>| {
+            if let hir::PatKind::Binding(_, hir_id, ident, _) = &pat.kind
+                && let Some(pat_ty) = self
+                    .in_progress_typeck_results
+                    .and_then(|typeck_results| typeck_results.borrow().node_type_opt(*hir_id))
+            {
+                let pat_ty = self.resolve_vars_if_possible(pat_ty);
+                if self.same_type_modulo_infer(pat_ty, expected_ty)
+                    && !(pat_ty, expected_ty).references_error()
+                    && shadowed.insert(ident.name)
+                {
+                    candidate_idents.push((*ident, pat_ty));
+                }
+            }
+            true
+        };
+
+        let hir = self.tcx.hir();
+        for stmt in blk.stmts.iter().rev() {
+            let hir::StmtKind::Local(local) = &stmt.kind else { continue; };
+            local.pat.walk(&mut find_compatible_candidates);
+        }
+        match hir.find(hir.get_parent_node(blk.hir_id)) {
+            Some(hir::Node::Expr(hir::Expr { hir_id, .. })) => {
+                match hir.find(hir.get_parent_node(*hir_id)) {
+                    Some(hir::Node::Arm(hir::Arm { pat, .. })) => {
+                        pat.walk(&mut find_compatible_candidates);
+                    }
+                    Some(
+                        hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body), .. })
+                        | hir::Node::ImplItem(hir::ImplItem {
+                            kind: hir::ImplItemKind::Fn(_, body),
+                            ..
+                        })
+                        | hir::Node::TraitItem(hir::TraitItem {
+                            kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(body)),
+                            ..
+                        })
+                        | hir::Node::Expr(hir::Expr {
+                            kind: hir::ExprKind::Closure(hir::Closure { body, .. }),
+                            ..
+                        }),
+                    ) => {
+                        for param in hir.body(*body).params {
+                            param.pat.walk(&mut find_compatible_candidates);
+                        }
+                    }
+                    Some(hir::Node::Expr(hir::Expr {
+                        kind:
+                            hir::ExprKind::If(
+                                hir::Expr { kind: hir::ExprKind::Let(let_), .. },
+                                then_block,
+                                _,
+                            ),
+                        ..
+                    })) if then_block.hir_id == *hir_id => {
+                        let_.pat.walk(&mut find_compatible_candidates);
+                    }
+                    _ => {}
+                }
+            }
+            _ => {}
+        }
+
+        match &candidate_idents[..] {
+            [(ident, _ty)] => {
+                let sm = self.tcx.sess.source_map();
+                if let Some(stmt) = blk.stmts.last() {
+                    let stmt_span = sm.stmt_span(stmt.span, blk.span);
+                    let sugg = if sm.is_multiline(blk.span)
+                        && let Some(spacing) = sm.indentation_before(stmt_span)
+                    {
+                        format!("\n{spacing}{ident}")
+                    } else {
+                        format!(" {ident}")
+                    };
+                    err.span_suggestion_verbose(
+                        stmt_span.shrink_to_hi(),
+                        format!("consider returning the local binding `{ident}`"),
+                        sugg,
+                        Applicability::MaybeIncorrect,
+                    );
+                } else {
+                    let sugg = if sm.is_multiline(blk.span)
+                        && let Some(spacing) = sm.indentation_before(blk.span.shrink_to_lo())
+                    {
+                        format!("\n{spacing}    {ident}\n{spacing}")
+                    } else {
+                        format!(" {ident} ")
+                    };
+                    let left_span = sm.span_through_char(blk.span, '{').shrink_to_hi();
+                    err.span_suggestion_verbose(
+                        sm.span_extend_while(left_span, |c| c.is_whitespace()).unwrap_or(left_span),
+                        format!("consider returning the local binding `{ident}`"),
+                        sugg,
+                        Applicability::MaybeIncorrect,
+                    );
+                }
+                true
+            }
+            values if (1..3).contains(&values.len()) => {
+                let spans = values.iter().map(|(ident, _)| ident.span).collect::<Vec<_>>();
+                err.span_note(spans, "consider returning one of these bindings");
+                true
+            }
+            _ => false,
+        }
+    }
+}
diff --git a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
index 4d29fc46946..561d1354edd 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
@@ -1,4 +1,4 @@
-use crate::infer::type_variable::TypeVariableOriginKind;
+use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
 use crate::infer::InferCtxt;
 use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed};
 use rustc_hir as hir;
@@ -6,14 +6,14 @@ use rustc_hir::def::Res;
 use rustc_hir::def::{CtorOf, DefKind, Namespace};
 use rustc_hir::def_id::DefId;
 use rustc_hir::intravisit::{self, Visitor};
-use rustc_hir::{Body, Expr, ExprKind, FnRetTy, HirId, Local, LocalSource};
+use rustc_hir::{Body, Closure, Expr, ExprKind, FnRetTy, HirId, Local, LocalSource};
 use rustc_middle::hir::nested_filter;
-use rustc_middle::infer::unify_key::ConstVariableOriginKind;
+use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
 use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
 use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter, Print, Printer};
 use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst, SubstsRef};
 use rustc_middle::ty::{self, DefIdTree, InferConst};
-use rustc_middle::ty::{Ty, TyCtxt, TypeckResults};
+use rustc_middle::ty::{IsSuggestable, Ty, TyCtxt, TypeckResults};
 use rustc_span::symbol::{kw, Ident};
 use rustc_span::{BytePos, Span};
 use std::borrow::Cow;
@@ -138,7 +138,7 @@ fn fmt_printer<'a, 'tcx>(infcx: &'a InferCtxt<'_, 'tcx>, ns: Namespace) -> FmtPr
         if let TypeVariableOriginKind::TypeParameterDefinition(name, _) =
             infcx.inner.borrow_mut().type_variables().var_origin(ty_vid).kind
         {
-            Some(name.to_string())
+            Some(name)
         } else {
             None
         }
@@ -151,7 +151,7 @@ fn fmt_printer<'a, 'tcx>(infcx: &'a InferCtxt<'_, 'tcx>, ns: Namespace) -> FmtPr
         if let ConstVariableOriginKind::ConstParameterDefinition(name, _) =
             infcx.inner.borrow_mut().const_unification_table().probe_value(ct_vid).origin.kind
         {
-            return Some(name.to_string());
+            return Some(name);
         } else {
             None
         }
@@ -407,11 +407,40 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
 
                 err.span_label(span, cannot_infer_msg);
 
-                let printer = fmt_printer(self, Namespace::TypeNS);
-                let args = printer.comma_sep(generic_args.iter().copied()).unwrap().into_buffer();
+                let args = fmt_printer(self, Namespace::TypeNS)
+                    .comma_sep(generic_args.iter().copied().map(|arg| {
+                        if arg.is_suggestable(self.tcx, true) {
+                            return arg;
+                        }
+
+                        match arg.unpack() {
+                            GenericArgKind::Lifetime(_) => bug!("unexpected lifetime"),
+                            GenericArgKind::Type(_) => self
+                                .next_ty_var(TypeVariableOrigin {
+                                    span: rustc_span::DUMMY_SP,
+                                    kind: TypeVariableOriginKind::MiscVariable,
+                                })
+                                .into(),
+                            GenericArgKind::Const(arg) => self
+                                .next_const_var(
+                                    arg.ty(),
+                                    ConstVariableOrigin {
+                                        span: rustc_span::DUMMY_SP,
+                                        kind: ConstVariableOriginKind::MiscVariable,
+                                    },
+                                )
+                                .into(),
+                        }
+                    }))
+                    .unwrap()
+                    .into_buffer();
+
                 err.span_suggestion_verbose(
                     insert_span,
-                    &format!("consider specifying the generic argument{}", pluralize!(args.len()),),
+                    &format!(
+                        "consider specifying the generic argument{}",
+                        pluralize!(generic_args.len()),
+                    ),
                     format!("::<{}>", args),
                     Applicability::HasPlaceholders,
                 );
@@ -1051,7 +1080,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> {
 
         if let Some(node_ty) = self.opt_node_type(expr.hir_id) {
             if let (
-                &ExprKind::Closure { fn_decl, body, fn_decl_span, .. },
+                &ExprKind::Closure(&Closure { fn_decl, body, fn_decl_span, .. }),
                 ty::Closure(_, substs),
             ) = (&expr.kind, node_ty.kind())
             {
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs
index b744594ddb7..9a2ab3e3224 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs
@@ -228,7 +228,7 @@ pub fn suggest_adding_lifetime_params<'tcx>(
     if is_impl {
         sugg.push_str(" and update trait if needed");
     }
-    err.multipart_suggestion(sugg.as_str(), suggestions, Applicability::MaybeIncorrect);
+    err.multipart_suggestion(sugg, suggestions, Applicability::MaybeIncorrect);
 
     true
 }
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs
index e5ae835e813..c1b201da691 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs
@@ -100,23 +100,6 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> {
                 // the lifetime of the TyRptr
                 let hir_id = lifetime.hir_id;
                 match (self.tcx.named_region(hir_id), self.bound_region) {
-                    // Find the index of the anonymous region that was part of the
-                    // error. We will then search the function parameters for a bound
-                    // region at the right depth with the same index
-                    (
-                        Some(rl::Region::LateBoundAnon(debruijn_index, _, anon_index)),
-                        ty::BrAnon(br_index),
-                    ) => {
-                        debug!(
-                            "LateBoundAnon depth = {:?} anon_index = {:?} br_index={:?}",
-                            debruijn_index, anon_index, br_index
-                        );
-                        if debruijn_index == self.current_index && anon_index == br_index {
-                            self.found_type = Some(arg);
-                            return; // we can stop visiting now
-                        }
-                    }
-
                     // Find the index of the named region that was part of the
                     // error. We will then search the function parameters for a bound
                     // region at the right depth with the same index
@@ -151,8 +134,7 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> {
                             rl::Region::Static
                             | rl::Region::Free(_, _)
                             | rl::Region::EarlyBound(_, _)
-                            | rl::Region::LateBound(_, _, _)
-                            | rl::Region::LateBoundAnon(_, _, _),
+                            | rl::Region::LateBound(_, _, _),
                         )
                         | None,
                         _,
@@ -206,16 +188,6 @@ impl<'tcx> Visitor<'tcx> for TyPathVisitor<'tcx> {
     fn visit_lifetime(&mut self, lifetime: &hir::Lifetime) {
         match (self.tcx.named_region(lifetime.hir_id), self.bound_region) {
             // the lifetime of the TyPath!
-            (
-                Some(rl::Region::LateBoundAnon(debruijn_index, _, anon_index)),
-                ty::BrAnon(br_index),
-            ) => {
-                if debruijn_index == self.current_index && anon_index == br_index {
-                    self.found_it = true;
-                    return;
-                }
-            }
-
             (Some(rl::Region::EarlyBound(_, id)), ty::BrNamed(def_id, _)) => {
                 debug!("EarlyBound id={:?} def_id={:?}", id, def_id);
                 if id == def_id {
@@ -239,7 +211,6 @@ impl<'tcx> Visitor<'tcx> for TyPathVisitor<'tcx> {
                     rl::Region::Static
                     | rl::Region::EarlyBound(_, _)
                     | rl::Region::LateBound(_, _, _)
-                    | rl::Region::LateBoundAnon(_, _, _)
                     | rl::Region::Free(_, _),
                 )
                 | None,
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs
index 43d5c9fdf33..893ca3cf79d 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs
@@ -6,7 +6,7 @@ use crate::infer::error_reporting::note_and_explain_region;
 use crate::infer::lexical_region_resolve::RegionResolutionError;
 use crate::infer::{SubregionOrigin, TypeTrace};
 use crate::traits::ObligationCauseCode;
-use rustc_data_structures::stable_set::FxHashSet;
+use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::{Applicability, ErrorGuaranteed, MultiSpan};
 use rustc_hir as hir;
 use rustc_hir::intravisit::Visitor;
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs
index e2185d86133..998699158ff 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs
@@ -204,7 +204,7 @@ impl<'tcx> NiceRegionError<'_, 'tcx> {
         expected_substs: SubstsRef<'tcx>,
         actual_substs: SubstsRef<'tcx>,
     ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
-        let span = cause.span(self.tcx());
+        let span = cause.span();
         let msg = format!(
             "implementation of `{}` is not general enough",
             self.tcx().def_path_str(trait_def_id),
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs
index 02928c4aa57..246d27be71c 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs
@@ -4,7 +4,7 @@ use crate::infer::error_reporting::nice_region_error::NiceRegionError;
 use crate::infer::lexical_region_resolve::RegionResolutionError;
 use crate::infer::{SubregionOrigin, TypeTrace};
 use crate::traits::{ObligationCauseCode, UnifyReceiverContext};
-use rustc_data_structures::stable_set::FxHashSet;
+use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::{struct_span_err, Applicability, Diagnostic, ErrorGuaranteed, MultiSpan};
 use rustc_hir::def_id::DefId;
 use rustc_hir::intravisit::{walk_ty, Visitor};
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs
index 91bf9695dfc..da465a76429 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs
@@ -2,17 +2,17 @@
 
 use crate::infer::error_reporting::nice_region_error::NiceRegionError;
 use crate::infer::lexical_region_resolve::RegionResolutionError;
-use crate::infer::{SubregionOrigin, Subtype};
-use crate::traits::ObligationCauseCode::CompareImplMethodObligation;
+use crate::infer::Subtype;
+use crate::traits::ObligationCauseCode::CompareImplItemObligation;
 use rustc_errors::{ErrorGuaranteed, MultiSpan};
 use rustc_hir as hir;
 use rustc_hir::def::Res;
-use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_hir::def_id::DefId;
 use rustc_hir::intravisit::Visitor;
 use rustc_middle::hir::nested_filter;
 use rustc_middle::ty::print::RegionHighlightMode;
 use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor};
-use rustc_span::{Span, Symbol};
+use rustc_span::Span;
 
 use std::ops::ControlFlow;
 
@@ -22,38 +22,22 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
         let error = self.error.as_ref()?;
         debug!("try_report_impl_not_conforming_to_trait {:?}", error);
         if let RegionResolutionError::SubSupConflict(
-                _, var_origin, sub_origin, _sub, sup_origin, _sup, _,
+                _,
+                var_origin,
+                sub_origin,
+                _sub,
+                sup_origin,
+                _sup,
+                _,
             ) = error.clone()
-            && let (&Subtype(ref sup_trace), &Subtype(ref sub_trace)) = (&sup_origin, &sub_origin)
-            && let (
-                sub_expected_found @ Some((sub_expected, sub_found)),
-                sup_expected_found @ Some(_),
-                CompareImplMethodObligation { trait_item_def_id, .. },
-            ) = (sub_trace.values.ty(), sup_trace.values.ty(), sub_trace.cause.code())
+            && let (Subtype(sup_trace), Subtype(sub_trace)) = (&sup_origin, &sub_origin)
+            && let sub_expected_found @ Some((sub_expected, sub_found)) = sub_trace.values.ty()
+            && let sup_expected_found @ Some(_) = sup_trace.values.ty()
+            && let CompareImplItemObligation { trait_item_def_id, .. } = sub_trace.cause.code()
             && sup_expected_found == sub_expected_found
         {
-            let guar = self.emit_err(
-                var_origin.span(),
-                sub_expected,
-                sub_found,
-                *trait_item_def_id,
-            );
-            return Some(guar);
-        }
-        if let RegionResolutionError::ConcreteFailure(origin, _, _)
-            | RegionResolutionError::GenericBoundFailure(origin, _, _) = error.clone()
-            && let SubregionOrigin::CompareImplTypeObligation {
-                span,
-                impl_item_def_id,
-                trait_item_def_id,
-            } = origin
-        {
-            let guar = self.emit_associated_type_err(
-                span,
-                self.infcx.tcx.item_name(impl_item_def_id.to_def_id()),
-                impl_item_def_id,
-                trait_item_def_id,
-            );
+            let guar =
+                self.emit_err(var_origin.span(), sub_expected, sub_found, *trait_item_def_id);
             return Some(guar);
         }
         None
@@ -147,25 +131,6 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
         }
         err.emit()
     }
-
-    fn emit_associated_type_err(
-        &self,
-        span: Span,
-        item_name: Symbol,
-        impl_item_def_id: LocalDefId,
-        trait_item_def_id: DefId,
-    ) -> ErrorGuaranteed {
-        let impl_sp = self.tcx().def_span(impl_item_def_id);
-        let trait_sp = self.tcx().def_span(trait_item_def_id);
-        let mut err = self
-            .tcx()
-            .sess
-            .struct_span_err(span, &format!("`impl` associated type signature for `{}` doesn't match `trait` associated type signature", item_name));
-        err.span_label(impl_sp, "found");
-        err.span_label(trait_sp, "expected");
-
-        err.emit()
-    }
 }
 
 struct TypeParamSpanVisitor<'tcx> {
diff --git a/compiler/rustc_infer/src/infer/error_reporting/note.rs b/compiler/rustc_infer/src/infer/error_reporting/note.rs
index 67bbace39e3..c1940c5c082 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/note.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/note.rs
@@ -86,13 +86,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                     "...so that the declared lifetime parameter bounds are satisfied",
                 );
             }
-            infer::CompareImplMethodObligation { span, .. } => {
-                label_or_note(
-                    span,
-                    "...so that the definition in impl matches the definition from the trait",
-                );
-            }
-            infer::CompareImplTypeObligation { span, .. } => {
+            infer::CompareImplItemObligation { span, .. } => {
                 label_or_note(
                     span,
                     "...so that the definition in impl matches the definition from the trait",
@@ -329,15 +323,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                 );
                 err
             }
-            infer::CompareImplMethodObligation { span, impl_item_def_id, trait_item_def_id } => {
-                self.report_extra_impl_obligation(
-                    span,
-                    impl_item_def_id,
-                    trait_item_def_id,
-                    &format!("`{}: {}`", sup, sub),
-                )
-            }
-            infer::CompareImplTypeObligation { span, impl_item_def_id, trait_item_def_id } => self
+            infer::CompareImplItemObligation { span, impl_item_def_id, trait_item_def_id } => self
                 .report_extra_impl_obligation(
                     span,
                     impl_item_def_id,
@@ -357,7 +343,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                 let trait_predicates = self.tcx.explicit_predicates_of(trait_item_def_id);
                 let impl_predicates = self.tcx.explicit_predicates_of(impl_item_def_id);
 
-                let impl_predicates: rustc_data_structures::stable_set::FxHashSet<_> =
+                let impl_predicates: rustc_data_structures::fx::FxHashSet<_> =
                     impl_predicates.predicates.into_iter().map(|(pred, _)| pred).collect();
                 let clauses: Vec<_> = trait_predicates
                     .predicates
diff --git a/compiler/rustc_infer/src/infer/higher_ranked/mod.rs b/compiler/rustc_infer/src/infer/higher_ranked/mod.rs
index 12c44b4baaa..e543ae1fcda 100644
--- a/compiler/rustc_infer/src/infer/higher_ranked/mod.rs
+++ b/compiler/rustc_infer/src/infer/higher_ranked/mod.rs
@@ -122,7 +122,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         // subtyping errors that it would have caught will now be
         // caught later on, during region checking. However, we
         // continue to use it for a transition period.
-        if self.tcx.sess.opts.debugging_opts.no_leak_check || self.skip_leak_check.get() {
+        if self.tcx.sess.opts.unstable_opts.no_leak_check || self.skip_leak_check.get() {
             return Ok(());
         }
 
diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs
index 28f037cc61a..0ac6e8c541b 100644
--- a/compiler/rustc_infer/src/infer/mod.rs
+++ b/compiler/rustc_infer/src/infer/mod.rs
@@ -21,6 +21,7 @@ use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue};
 use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind, ToType};
 use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult};
 use rustc_middle::traits::select;
+use rustc_middle::ty::abstract_const::{AbstractConst, FailureKind};
 use rustc_middle::ty::error::{ExpectedFound, TypeError};
 use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable};
 use rustc_middle::ty::relate::RelateResult;
@@ -238,17 +239,36 @@ impl<'tcx> InferCtxtInner<'tcx> {
     }
 }
 
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum DefiningAnchor {
+    /// `DefId` of the item.
+    Bind(LocalDefId),
+    /// When opaque types are not resolved, we `Bubble` up, meaning
+    /// return the opaque/hidden type pair from query, for caller of query to handle it.
+    Bubble,
+    /// Used to catch type mismatch errors when handling opaque types.
+    Error,
+}
+
 pub struct InferCtxt<'a, 'tcx> {
     pub tcx: TyCtxt<'tcx>,
 
     /// The `DefId` of the item in whose context we are performing inference or typeck.
     /// It is used to check whether an opaque type use is a defining use.
     ///
-    /// If it is `None`, we can't resolve opaque types here and need to bubble up
+    /// If it is `DefiningAnchor::Bubble`, we can't resolve opaque types here and need to bubble up
     /// the obligation. This frequently happens for
     /// short lived InferCtxt within queries. The opaque type obligations are forwarded
     /// to the outside until the end up in an `InferCtxt` for typeck or borrowck.
-    pub defining_use_anchor: Option<LocalDefId>,
+    ///
+    /// It is default value is `DefiningAnchor::Error`, this way it is easier to catch errors that
+    /// might come up during inference or typeck.
+    pub defining_use_anchor: DefiningAnchor,
+
+    /// Whether this inference context should care about region obligations in
+    /// the root universe. Most notably, this is used during hir typeck as region
+    /// solving is left to borrowck instead.
+    pub considering_regions: bool,
 
     /// During type-checking/inference of a body, `in_progress_typeck_results`
     /// contains a reference to the typeck results being built up, which are
@@ -385,15 +405,7 @@ pub enum SubregionOrigin<'tcx> {
 
     /// Comparing the signature and requirements of an impl method against
     /// the containing trait.
-    CompareImplMethodObligation {
-        span: Span,
-        impl_item_def_id: LocalDefId,
-        trait_item_def_id: DefId,
-    },
-
-    /// Comparing the signature and requirements of an impl associated type
-    /// against the containing trait
-    CompareImplTypeObligation { span: Span, impl_item_def_id: LocalDefId, trait_item_def_id: DefId },
+    CompareImplItemObligation { span: Span, impl_item_def_id: LocalDefId, trait_item_def_id: DefId },
 
     /// Checking that the bounds of a trait's associated type hold for a given impl
     CheckAssociatedTypeBounds {
@@ -524,8 +536,9 @@ impl<'tcx> fmt::Display for FixupError<'tcx> {
 /// without using `Rc` or something similar.
 pub struct InferCtxtBuilder<'tcx> {
     tcx: TyCtxt<'tcx>,
+    defining_use_anchor: DefiningAnchor,
+    considering_regions: bool,
     fresh_typeck_results: Option<RefCell<ty::TypeckResults<'tcx>>>,
-    defining_use_anchor: Option<LocalDefId>,
 }
 
 pub trait TyCtxtInferExt<'tcx> {
@@ -534,7 +547,12 @@ pub trait TyCtxtInferExt<'tcx> {
 
 impl<'tcx> TyCtxtInferExt<'tcx> for TyCtxt<'tcx> {
     fn infer_ctxt(self) -> InferCtxtBuilder<'tcx> {
-        InferCtxtBuilder { tcx: self, defining_use_anchor: None, fresh_typeck_results: None }
+        InferCtxtBuilder {
+            tcx: self,
+            defining_use_anchor: DefiningAnchor::Error,
+            considering_regions: true,
+            fresh_typeck_results: None,
+        }
     }
 }
 
@@ -544,7 +562,7 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
     /// Will also change the scope for opaque type defining use checks to the given owner.
     pub fn with_fresh_in_progress_typeck_results(mut self, table_owner: LocalDefId) -> Self {
         self.fresh_typeck_results = Some(RefCell::new(ty::TypeckResults::new(table_owner)));
-        self.with_opaque_type_inference(table_owner)
+        self.with_opaque_type_inference(DefiningAnchor::Bind(table_owner))
     }
 
     /// Whenever the `InferCtxt` should be able to handle defining uses of opaque types,
@@ -553,8 +571,13 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
     /// It is only meant to be called in two places, for typeck
     /// (via `with_fresh_in_progress_typeck_results`) and for the inference context used
     /// in mir borrowck.
-    pub fn with_opaque_type_inference(mut self, defining_use_anchor: LocalDefId) -> Self {
-        self.defining_use_anchor = Some(defining_use_anchor);
+    pub fn with_opaque_type_inference(mut self, defining_use_anchor: DefiningAnchor) -> Self {
+        self.defining_use_anchor = defining_use_anchor;
+        self
+    }
+
+    pub fn ignoring_regions(mut self) -> Self {
+        self.considering_regions = false;
         self
     }
 
@@ -582,11 +605,17 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
     }
 
     pub fn enter<R>(&mut self, f: impl for<'a> FnOnce(InferCtxt<'a, 'tcx>) -> R) -> R {
-        let InferCtxtBuilder { tcx, defining_use_anchor, ref fresh_typeck_results } = *self;
+        let InferCtxtBuilder {
+            tcx,
+            defining_use_anchor,
+            considering_regions,
+            ref fresh_typeck_results,
+        } = *self;
         let in_progress_typeck_results = fresh_typeck_results.as_ref();
         f(InferCtxt {
             tcx,
             defining_use_anchor,
+            considering_regions,
             in_progress_typeck_results,
             inner: RefCell::new(InferCtxtInner::new()),
             lexical_region_resolutions: RefCell::new(None),
@@ -937,14 +966,14 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
     #[instrument(skip(self), level = "debug")]
     pub fn member_constraint(
         &self,
-        opaque_type_def_id: DefId,
+        key: ty::OpaqueTypeKey<'tcx>,
         definition_span: Span,
         hidden_ty: Ty<'tcx>,
         region: ty::Region<'tcx>,
         in_regions: &Lrc<Vec<ty::Region<'tcx>>>,
     ) {
         self.inner.borrow_mut().unwrap_region_constraints().member_constraint(
-            opaque_type_def_id,
+            key,
             definition_span,
             hidden_ty,
             region,
@@ -1024,16 +1053,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         &self,
         cause: &traits::ObligationCause<'tcx>,
         predicate: ty::PolyRegionOutlivesPredicate<'tcx>,
-    ) -> UnitResult<'tcx> {
-        self.commit_if_ok(|_snapshot| {
-            let ty::OutlivesPredicate(r_a, r_b) =
-                self.replace_bound_vars_with_placeholders(predicate);
-            let origin = SubregionOrigin::from_obligation_cause(cause, || {
-                RelateRegionParamBound(cause.span)
-            });
-            self.sub_regions(origin, r_b, r_a); // `b : a` ==> `a <= b`
-            Ok(())
-        })
+    ) {
+        let ty::OutlivesPredicate(r_a, r_b) = self.replace_bound_vars_with_placeholders(predicate);
+        let origin =
+            SubregionOrigin::from_obligation_cause(cause, || RelateRegionParamBound(cause.span));
+        self.sub_regions(origin, r_b, r_a); // `b : a` ==> `a <= b`
     }
 
     /// Number of type variables created so far.
@@ -1300,7 +1324,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
     /// result. After this, no more unification operations should be
     /// done -- or the compiler will panic -- but it is legal to use
     /// `resolve_vars_if_possible` as well as `fully_resolve`.
-    pub fn resolve_regions_and_report_errors(&self, outlives_env: &OutlivesEnvironment<'tcx>) {
+    pub fn resolve_regions_and_report_errors(
+        &self,
+        generic_param_scope: LocalDefId,
+        outlives_env: &OutlivesEnvironment<'tcx>,
+    ) {
         let errors = self.resolve_regions(outlives_env);
 
         if !self.is_tainted_by_errors() {
@@ -1309,7 +1337,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
             // this infcx was in use.  This is totally hokey but
             // otherwise we have a hard time separating legit region
             // errors from silly ones.
-            self.report_region_errors(&errors);
+            self.report_region_errors(generic_param_scope, &errors);
         }
     }
 
@@ -1647,18 +1675,31 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
     #[instrument(skip(self), level = "debug")]
     pub fn const_eval_resolve(
         &self,
-        param_env: ty::ParamEnv<'tcx>,
+        mut param_env: ty::ParamEnv<'tcx>,
         unevaluated: ty::Unevaluated<'tcx>,
         span: Option<Span>,
     ) -> EvalToValTreeResult<'tcx> {
-        let substs = self.resolve_vars_if_possible(unevaluated.substs);
+        let mut substs = self.resolve_vars_if_possible(unevaluated.substs);
         debug!(?substs);
 
         // Postpone the evaluation of constants whose substs depend on inference
         // variables
         if substs.has_infer_types_or_consts() {
-            debug!("substs have infer types or consts: {:?}", substs);
-            return Err(ErrorHandled::TooGeneric);
+            let ac = AbstractConst::new(self.tcx, unevaluated.shrink());
+            match ac {
+                Ok(None) => {
+                    substs = InternalSubsts::identity_for_item(self.tcx, unevaluated.def.did);
+                    param_env = self.tcx.param_env(unevaluated.def.did);
+                }
+                Ok(Some(ct)) => {
+                    if ct.unify_failure_kind(self.tcx) == FailureKind::Concrete {
+                        substs = replace_param_and_infer_substs_with_placeholder(self.tcx, substs);
+                    } else {
+                        return Err(ErrorHandled::TooGeneric);
+                    }
+                }
+                Err(guar) => return Err(ErrorHandled::Reported(guar)),
+            }
         }
 
         let param_env_erased = self.tcx.erase_regions(param_env);
@@ -1905,8 +1946,7 @@ impl<'tcx> SubregionOrigin<'tcx> {
             ReborrowUpvar(a, _) => a,
             DataBorrowed(_, a) => a,
             ReferenceOutlivesReferent(_, a) => a,
-            CompareImplMethodObligation { span, .. } => span,
-            CompareImplTypeObligation { span, .. } => span,
+            CompareImplItemObligation { span, .. } => span,
             CheckAssociatedTypeBounds { ref parent, .. } => parent.span(),
         }
     }
@@ -1920,19 +1960,11 @@ impl<'tcx> SubregionOrigin<'tcx> {
                 SubregionOrigin::ReferenceOutlivesReferent(ref_type, cause.span)
             }
 
-            traits::ObligationCauseCode::CompareImplMethodObligation {
-                impl_item_def_id,
-                trait_item_def_id,
-            } => SubregionOrigin::CompareImplMethodObligation {
-                span: cause.span,
-                impl_item_def_id,
-                trait_item_def_id,
-            },
-
-            traits::ObligationCauseCode::CompareImplTypeObligation {
+            traits::ObligationCauseCode::CompareImplItemObligation {
                 impl_item_def_id,
                 trait_item_def_id,
-            } => SubregionOrigin::CompareImplTypeObligation {
+                kind: _,
+            } => SubregionOrigin::CompareImplItemObligation {
                 span: cause.span,
                 impl_item_def_id,
                 trait_item_def_id,
@@ -1977,3 +2009,43 @@ impl<'tcx> fmt::Debug for RegionObligation<'tcx> {
         )
     }
 }
+
+/// Replaces substs that reference param or infer variables with suitable
+/// placeholders. This function is meant to remove these param and infer
+/// substs when they're not actually needed to evaluate a constant.
+fn replace_param_and_infer_substs_with_placeholder<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    substs: SubstsRef<'tcx>,
+) -> SubstsRef<'tcx> {
+    tcx.mk_substs(substs.iter().enumerate().map(|(idx, arg)| {
+        match arg.unpack() {
+            GenericArgKind::Type(_)
+                if arg.has_param_types_or_consts() || arg.has_infer_types_or_consts() =>
+            {
+                tcx.mk_ty(ty::Placeholder(ty::PlaceholderType {
+                    universe: ty::UniverseIndex::ROOT,
+                    name: ty::BoundVar::from_usize(idx),
+                }))
+                .into()
+            }
+            GenericArgKind::Const(ct)
+                if ct.has_infer_types_or_consts() || ct.has_param_types_or_consts() =>
+            {
+                let ty = ct.ty();
+                // If the type references param or infer, replace that too...
+                if ty.has_param_types_or_consts() || ty.has_infer_types_or_consts() {
+                    bug!("const `{ct}`'s type should not reference params or types");
+                }
+                tcx.mk_const(ty::ConstS {
+                    ty,
+                    kind: ty::ConstKind::Placeholder(ty::PlaceholderConst {
+                        universe: ty::UniverseIndex::ROOT,
+                        name: ty::BoundConst { ty, var: ty::BoundVar::from_usize(idx) },
+                    }),
+                })
+                .into()
+            }
+            _ => arg,
+        }
+    }))
+}
diff --git a/compiler/rustc_infer/src/infer/opaque_types.rs b/compiler/rustc_infer/src/infer/opaque_types.rs
index f11701bba6f..a1c7b70bd9c 100644
--- a/compiler/rustc_infer/src/infer/opaque_types.rs
+++ b/compiler/rustc_infer/src/infer/opaque_types.rs
@@ -1,4 +1,4 @@
-use crate::infer::{InferCtxt, InferOk};
+use crate::infer::{DefiningAnchor, InferCtxt, InferOk};
 use crate::traits;
 use hir::def_id::{DefId, LocalDefId};
 use hir::{HirId, OpaqueTyOrigin};
@@ -51,7 +51,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
             return InferOk { value: ty, obligations: vec![] };
         }
         let mut obligations = vec![];
-        let replace_opaque_type = |def_id| self.opaque_type_origin(def_id, span).is_some();
+        let replace_opaque_type = |def_id: DefId| {
+            def_id
+                .as_local()
+                .map_or(false, |def_id| self.opaque_type_origin(def_id, span).is_some())
+        };
         let value = ty.fold_with(&mut ty::fold::BottomUpFolder {
             tcx: self.tcx,
             lt_op: |lt| lt,
@@ -96,44 +100,47 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         let (a, b) = if a_is_expected { (a, b) } else { (b, a) };
         let process = |a: Ty<'tcx>, b: Ty<'tcx>| match *a.kind() {
             ty::Opaque(def_id, substs) if def_id.is_local() => {
-                let origin = if self.defining_use_anchor.is_some() {
-                    // Check that this is `impl Trait` type is
-                    // declared by `parent_def_id` -- i.e., one whose
-                    // value we are inferring.  At present, this is
-                    // always true during the first phase of
-                    // type-check, but not always true later on during
-                    // NLL. Once we support named opaque types more fully,
-                    // this same scenario will be able to arise during all phases.
-                    //
-                    // Here is an example using type alias `impl Trait`
-                    // that indicates the distinction we are checking for:
-                    //
-                    // ```rust
-                    // mod a {
-                    //   pub type Foo = impl Iterator;
-                    //   pub fn make_foo() -> Foo { .. }
-                    // }
-                    //
-                    // mod b {
-                    //   fn foo() -> a::Foo { a::make_foo() }
-                    // }
-                    // ```
-                    //
-                    // Here, the return type of `foo` references an
-                    // `Opaque` indeed, but not one whose value is
-                    // presently being inferred. You can get into a
-                    // similar situation with closure return types
-                    // today:
-                    //
-                    // ```rust
-                    // fn foo() -> impl Iterator { .. }
-                    // fn bar() {
-                    //     let x = || foo(); // returns the Opaque assoc with `foo`
-                    // }
-                    // ```
-                    self.opaque_type_origin(def_id, cause.span)?
-                } else {
-                    self.opaque_ty_origin_unchecked(def_id, cause.span)
+                let def_id = def_id.expect_local();
+                let origin = match self.defining_use_anchor {
+                    DefiningAnchor::Bind(_) => {
+                        // Check that this is `impl Trait` type is
+                        // declared by `parent_def_id` -- i.e., one whose
+                        // value we are inferring.  At present, this is
+                        // always true during the first phase of
+                        // type-check, but not always true later on during
+                        // NLL. Once we support named opaque types more fully,
+                        // this same scenario will be able to arise during all phases.
+                        //
+                        // Here is an example using type alias `impl Trait`
+                        // that indicates the distinction we are checking for:
+                        //
+                        // ```rust
+                        // mod a {
+                        //   pub type Foo = impl Iterator;
+                        //   pub fn make_foo() -> Foo { .. }
+                        // }
+                        //
+                        // mod b {
+                        //   fn foo() -> a::Foo { a::make_foo() }
+                        // }
+                        // ```
+                        //
+                        // Here, the return type of `foo` references an
+                        // `Opaque` indeed, but not one whose value is
+                        // presently being inferred. You can get into a
+                        // similar situation with closure return types
+                        // today:
+                        //
+                        // ```rust
+                        // fn foo() -> impl Iterator { .. }
+                        // fn bar() {
+                        //     let x = || foo(); // returns the Opaque assoc with `foo`
+                        // }
+                        // ```
+                        self.opaque_type_origin(def_id, cause.span)?
+                    }
+                    DefiningAnchor::Bubble => self.opaque_ty_origin_unchecked(def_id, cause.span),
+                    DefiningAnchor::Error => return None,
                 };
                 if let ty::Opaque(did2, _) = *b.kind() {
                     // We could accept this, but there are various ways to handle this situation, and we don't
@@ -141,7 +148,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                     // no one encounters it in practice.
                     // It does occur however in `fn fut() -> impl Future<Output = i32> { async { 42 } }`,
                     // where it is of no concern, so we only check for TAITs.
-                    if let Some(OpaqueTyOrigin::TyAlias) = self.opaque_type_origin(did2, cause.span)
+                    if let Some(OpaqueTyOrigin::TyAlias) =
+                        did2.as_local().and_then(|did2| self.opaque_type_origin(did2, cause.span))
                     {
                         self.tcx
                                 .sess
@@ -386,30 +394,24 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         );
 
         concrete_ty.visit_with(&mut ConstrainOpaqueTypeRegionVisitor {
-            op: |r| {
-                self.member_constraint(
-                    opaque_type_key.def_id,
-                    span,
-                    concrete_ty,
-                    r,
-                    &choice_regions,
-                )
-            },
+            op: |r| self.member_constraint(opaque_type_key, span, concrete_ty, r, &choice_regions),
         });
     }
 
     #[instrument(skip(self), level = "trace")]
-    pub fn opaque_type_origin(&self, opaque_def_id: DefId, span: Span) -> Option<OpaqueTyOrigin> {
-        let def_id = opaque_def_id.as_local()?;
+    pub fn opaque_type_origin(&self, def_id: LocalDefId, span: Span) -> Option<OpaqueTyOrigin> {
         let opaque_hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id);
-        let parent_def_id = self.defining_use_anchor?;
+        let parent_def_id = match self.defining_use_anchor {
+            DefiningAnchor::Bubble | DefiningAnchor::Error => return None,
+            DefiningAnchor::Bind(bind) => bind,
+        };
         let item_kind = &self.tcx.hir().expect_item(def_id).kind;
 
         let hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, ..  }) = item_kind else {
             span_bug!(
                 span,
                 "weird opaque type: {:#?}, {:#?}",
-                opaque_def_id,
+                def_id,
                 item_kind
             )
         };
@@ -428,12 +430,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
     }
 
     #[instrument(skip(self), level = "trace")]
-    fn opaque_ty_origin_unchecked(&self, opaque_def_id: DefId, span: Span) -> OpaqueTyOrigin {
-        let def_id = opaque_def_id.as_local().unwrap();
+    fn opaque_ty_origin_unchecked(&self, def_id: LocalDefId, span: Span) -> OpaqueTyOrigin {
         let origin = match self.tcx.hir().expect_item(def_id).kind {
             hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => origin,
             ref itemkind => {
-                span_bug!(span, "weird opaque type: {:?}, {:#?}", opaque_def_id, itemkind)
+                span_bug!(span, "weird opaque type: {:?}, {:#?}", def_id, itemkind)
             }
         };
         trace!(?origin);
@@ -557,7 +558,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
             obligations = self.at(&cause, param_env).eq(prev, hidden_ty)?.obligations;
         }
 
-        let item_bounds = tcx.bound_explicit_item_bounds(def_id);
+        let item_bounds = tcx.bound_explicit_item_bounds(def_id.to_def_id());
 
         for predicate in item_bounds.transpose_iter().map(|e| e.map_bound(|(p, _)| *p)) {
             debug!(?predicate);
@@ -579,7 +580,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                     }
                     // Replace all other mentions of the same opaque type with the hidden type,
                     // as the bounds must hold on the hidden type after all.
-                    ty::Opaque(def_id2, substs2) if def_id == def_id2 && substs == substs2 => {
+                    ty::Opaque(def_id2, substs2)
+                        if def_id.to_def_id() == def_id2 && substs == substs2 =>
+                    {
                         hidden_ty
                     }
                     _ => ty,
diff --git a/compiler/rustc_infer/src/infer/outlives/components.rs b/compiler/rustc_infer/src/infer/outlives/components.rs
index 7234660dbcd..b2d7f4a663a 100644
--- a/compiler/rustc_infer/src/infer/outlives/components.rs
+++ b/compiler/rustc_infer/src/infer/outlives/components.rs
@@ -190,7 +190,11 @@ fn compute_components<'tcx>(
         }
 }
 
-fn compute_components_recursive<'tcx>(
+/// Collect [Component]s for *all* the substs of `parent`.
+///
+/// This should not be used to get the components of `parent` itself.
+/// Use [push_outlives_components] instead.
+pub(super) fn compute_components_recursive<'tcx>(
     tcx: TyCtxt<'tcx>,
     parent: GenericArg<'tcx>,
     out: &mut SmallVec<[Component<'tcx>; 4]>,
diff --git a/compiler/rustc_infer/src/infer/outlives/env.rs b/compiler/rustc_infer/src/infer/outlives/env.rs
index b897de7315a..b2decd64f0f 100644
--- a/compiler/rustc_infer/src/infer/outlives/env.rs
+++ b/compiler/rustc_infer/src/infer/outlives/env.rs
@@ -1,6 +1,7 @@
 use crate::infer::free_regions::FreeRegionMap;
 use crate::infer::{GenericKind, InferCtxt};
 use crate::traits::query::OutlivesBound;
+use rustc_data_structures::fx::FxIndexSet;
 use rustc_middle::ty::{self, ReEarlyBound, ReFree, ReVar, Region};
 
 use super::explicit_outlives_bounds;
@@ -53,7 +54,8 @@ pub struct OutlivesEnvironment<'tcx> {
 /// "Region-bound pairs" tracks outlives relations that are known to
 /// be true, either because of explicit where-clauses like `T: 'a` or
 /// because of implied bounds.
-pub type RegionBoundPairs<'tcx> = Vec<(Region<'tcx>, GenericKind<'tcx>)>;
+pub type RegionBoundPairs<'tcx> =
+    FxIndexSet<ty::OutlivesPredicate<GenericKind<'tcx>, Region<'tcx>>>;
 
 impl<'a, 'tcx> OutlivesEnvironment<'tcx> {
     pub fn new(param_env: ty::ParamEnv<'tcx>) -> Self {
@@ -97,10 +99,12 @@ impl<'a, 'tcx> OutlivesEnvironment<'tcx> {
             debug!("add_outlives_bounds: outlives_bound={:?}", outlives_bound);
             match outlives_bound {
                 OutlivesBound::RegionSubParam(r_a, param_b) => {
-                    self.region_bound_pairs.push((r_a, GenericKind::Param(param_b)));
+                    self.region_bound_pairs
+                        .insert(ty::OutlivesPredicate(GenericKind::Param(param_b), r_a));
                 }
                 OutlivesBound::RegionSubProjection(r_a, projection_b) => {
-                    self.region_bound_pairs.push((r_a, GenericKind::Projection(projection_b)));
+                    self.region_bound_pairs
+                        .insert(ty::OutlivesPredicate(GenericKind::Projection(projection_b), r_a));
                 }
                 OutlivesBound::RegionSubRegion(r_a, r_b) => {
                     if let (ReEarlyBound(_) | ReFree(_), ReVar(vid_b)) = (r_a.kind(), r_b.kind()) {
diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs
index 59cf39abe64..a57971bfb69 100644
--- a/compiler/rustc_infer/src/infer/outlives/obligations.rs
+++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs
@@ -68,6 +68,7 @@ use crate::infer::{
 };
 use crate::traits::{ObligationCause, ObligationCauseCode};
 use rustc_data_structures::undo_log::UndoLogs;
+use rustc_hir::def_id::LocalDefId;
 use rustc_middle::ty::subst::GenericArgKind;
 use rustc_middle::ty::{self, Region, Ty, TyCtxt, TypeVisitable};
 use smallvec::smallvec;
@@ -163,6 +164,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
 
     pub fn check_region_obligations_and_report_errors(
         &self,
+        generic_param_scope: LocalDefId,
         outlives_env: &OutlivesEnvironment<'tcx>,
     ) {
         self.process_registered_region_obligations(
@@ -170,7 +172,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
             outlives_env.param_env,
         );
 
-        self.resolve_regions_and_report_errors(outlives_env)
+        self.resolve_regions_and_report_errors(generic_param_scope, outlives_env)
     }
 }
 
diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs
index 86b025dce5e..c7d7ef40d9d 100644
--- a/compiler/rustc_infer/src/infer/outlives/verify.rs
+++ b/compiler/rustc_infer/src/infer/outlives/verify.rs
@@ -1,11 +1,14 @@
+use crate::infer::outlives::components::{compute_components_recursive, Component};
 use crate::infer::outlives::env::RegionBoundPairs;
 use crate::infer::region_constraints::VerifyIfEq;
 use crate::infer::{GenericKind, VerifyBound};
 use rustc_data_structures::captures::Captures;
 use rustc_data_structures::sso::SsoHashSet;
 use rustc_hir::def_id::DefId;
-use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst};
-use rustc_middle::ty::{self, EarlyBinder, Ty, TyCtxt};
+use rustc_middle::ty::subst::{GenericArg, Subst};
+use rustc_middle::ty::{self, EarlyBinder, OutlivesPredicate, Ty, TyCtxt};
+
+use smallvec::smallvec;
 
 /// The `TypeOutlives` struct has the job of "lowering" a `T: 'a`
 /// obligation into a series of `'a: 'b` constraints and "verifys", as
@@ -47,43 +50,6 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
         }
     }
 
-    fn type_bound(
-        &self,
-        ty: Ty<'tcx>,
-        visited: &mut SsoHashSet<GenericArg<'tcx>>,
-    ) -> VerifyBound<'tcx> {
-        match *ty.kind() {
-            ty::Param(p) => self.param_bound(p),
-            ty::Projection(data) => self.projection_bound(data, visited),
-            ty::FnDef(_, substs) => {
-                // HACK(eddyb) ignore lifetimes found shallowly in `substs`.
-                // This is inconsistent with `ty::Adt` (including all substs),
-                // but consistent with previous (accidental) behavior.
-                // See https://github.com/rust-lang/rust/issues/70917
-                // for further background and discussion.
-                let mut bounds = substs
-                    .iter()
-                    .filter_map(|child| match child.unpack() {
-                        GenericArgKind::Type(ty) => Some(self.type_bound(ty, visited)),
-                        GenericArgKind::Lifetime(_) => None,
-                        GenericArgKind::Const(_) => Some(self.recursive_bound(child, visited)),
-                    })
-                    .filter(|bound| {
-                        // Remove bounds that must hold, since they are not interesting.
-                        !bound.must_hold()
-                    });
-
-                match (bounds.next(), bounds.next()) {
-                    (Some(first), None) => first,
-                    (first, second) => VerifyBound::AllBounds(
-                        first.into_iter().chain(second).chain(bounds).collect(),
-                    ),
-                }
-            }
-            _ => self.recursive_bound(ty.into(), visited),
-        }
-    }
-
     fn param_bound(&self, param_ty: ty::ParamTy) -> VerifyBound<'tcx> {
         debug!("param_bound(param_ty={:?})", param_ty);
 
@@ -188,27 +154,24 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
             .map(|r| VerifyBound::OutlivedBy(r));
 
         // see the extensive comment in projection_must_outlive
-        let ty = self.tcx.mk_projection(projection_ty.item_def_id, projection_ty.substs);
-        let recursive_bound = self.recursive_bound(ty.into(), visited);
+        let recursive_bound = {
+            let mut components = smallvec![];
+            let ty = self.tcx.mk_projection(projection_ty.item_def_id, projection_ty.substs);
+            compute_components_recursive(self.tcx, ty.into(), &mut components, visited);
+            self.bound_from_components(&components, visited)
+        };
 
         VerifyBound::AnyBound(env_bounds.chain(trait_bounds).collect()).or(recursive_bound)
     }
 
-    fn recursive_bound(
+    fn bound_from_components(
         &self,
-        parent: GenericArg<'tcx>,
+        components: &[Component<'tcx>],
         visited: &mut SsoHashSet<GenericArg<'tcx>>,
     ) -> VerifyBound<'tcx> {
-        let mut bounds = parent
-            .walk_shallow(visited)
-            .filter_map(|child| match child.unpack() {
-                GenericArgKind::Type(ty) => Some(self.type_bound(ty, visited)),
-                GenericArgKind::Lifetime(lt) => {
-                    // Ignore late-bound regions.
-                    if !lt.is_late_bound() { Some(VerifyBound::OutlivedBy(lt)) } else { None }
-                }
-                GenericArgKind::Const(_) => Some(self.recursive_bound(child, visited)),
-            })
+        let mut bounds = components
+            .iter()
+            .map(|component| self.bound_from_single_component(component, visited))
             .filter(|bound| {
                 // Remove bounds that must hold, since they are not interesting.
                 !bound.must_hold()
@@ -222,6 +185,32 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
         }
     }
 
+    fn bound_from_single_component(
+        &self,
+        component: &Component<'tcx>,
+        visited: &mut SsoHashSet<GenericArg<'tcx>>,
+    ) -> VerifyBound<'tcx> {
+        match *component {
+            Component::Region(lt) => VerifyBound::OutlivedBy(lt),
+            Component::Param(param_ty) => self.param_bound(param_ty),
+            Component::Projection(projection_ty) => self.projection_bound(projection_ty, visited),
+            Component::EscapingProjection(ref components) => {
+                self.bound_from_components(components, visited)
+            }
+            Component::UnresolvedInferenceVariable(v) => {
+                // ignore this, we presume it will yield an error
+                // later, since if a type variable is not resolved by
+                // this point it never will be
+                self.tcx.sess.delay_span_bug(
+                    rustc_span::DUMMY_SP,
+                    &format!("unresolved inference variable in outlives: {:?}", v),
+                );
+                // add a bound that never holds
+                VerifyBound::AnyBound(vec![])
+            }
+        }
+    }
+
     /// Searches the environment for where-clauses like `G: 'a` where
     /// `G` is either some type parameter `T` or a projection like
     /// `T::Item`. Returns a vector of the `'a` bounds it can find.
@@ -270,16 +259,17 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
         // The problem is that the type of `x` is `&'a A`. To be
         // well-formed, then, A must outlive `'a`, but we don't know that
         // this holds from first principles.
-        let from_region_bound_pairs = self.region_bound_pairs.iter().filter_map(|&(r, p)| {
-            debug!(
-                "declared_generic_bounds_from_env_for_erased_ty: region_bound_pair = {:?}",
-                (r, p)
-            );
-            let p_ty = p.to_ty(tcx);
-            let erased_p_ty = self.tcx.erase_regions(p_ty);
-            (erased_p_ty == erased_ty)
-                .then_some(ty::Binder::dummy(ty::OutlivesPredicate(p.to_ty(tcx), r)))
-        });
+        let from_region_bound_pairs =
+            self.region_bound_pairs.iter().filter_map(|&OutlivesPredicate(p, r)| {
+                debug!(
+                    "declared_generic_bounds_from_env_for_erased_ty: region_bound_pair = {:?}",
+                    (r, p)
+                );
+                let p_ty = p.to_ty(tcx);
+                let erased_p_ty = self.tcx.erase_regions(p_ty);
+                (erased_p_ty == erased_ty)
+                    .then_some(ty::Binder::dummy(ty::OutlivesPredicate(p.to_ty(tcx), r)))
+            });
 
         param_bounds
             .chain(from_region_bound_pairs)
diff --git a/compiler/rustc_infer/src/infer/region_constraints/mod.rs b/compiler/rustc_infer/src/infer/region_constraints/mod.rs
index c5747ecf702..0d4472a1cfd 100644
--- a/compiler/rustc_infer/src/infer/region_constraints/mod.rs
+++ b/compiler/rustc_infer/src/infer/region_constraints/mod.rs
@@ -12,7 +12,6 @@ use rustc_data_structures::intern::Interned;
 use rustc_data_structures::sync::Lrc;
 use rustc_data_structures::undo_log::UndoLogs;
 use rustc_data_structures::unify as ut;
-use rustc_hir::def_id::DefId;
 use rustc_index::vec::IndexVec;
 use rustc_middle::infer::unify_key::{RegionVidKey, UnifiedRegion};
 use rustc_middle::ty::ReStatic;
@@ -533,7 +532,7 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
 
     pub fn member_constraint(
         &mut self,
-        opaque_type_def_id: DefId,
+        key: ty::OpaqueTypeKey<'tcx>,
         definition_span: Span,
         hidden_ty: Ty<'tcx>,
         member_region: ty::Region<'tcx>,
@@ -546,7 +545,7 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
         }
 
         self.data.member_constraints.push(MemberConstraint {
-            opaque_type_def_id,
+            key,
             definition_span,
             hidden_ty,
             member_region,
diff --git a/compiler/rustc_infer/src/lib.rs b/compiler/rustc_infer/src/lib.rs
index 7769a68ba2a..490ba52503f 100644
--- a/compiler/rustc_infer/src/lib.rs
+++ b/compiler/rustc_infer/src/lib.rs
@@ -18,7 +18,7 @@
 #![feature(control_flow_enum)]
 #![feature(extend_one)]
 #![feature(label_break_value)]
-#![feature(let_chains)]
+#![cfg_attr(bootstrap, feature(let_chains))]
 #![feature(let_else)]
 #![feature(min_specialization)]
 #![feature(never_type)]
diff --git a/compiler/rustc_infer/src/traits/error_reporting/mod.rs b/compiler/rustc_infer/src/traits/error_reporting/mod.rs
index 7e42458fda3..95b6c4ce1f2 100644
--- a/compiler/rustc_infer/src/traits/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/traits/error_reporting/mod.rs
@@ -18,20 +18,19 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         trait_item_def_id: DefId,
         requirement: &dyn fmt::Display,
     ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
-        let msg = "impl has stricter requirements than trait";
-        let sp = self.tcx.sess.source_map().guess_head_span(error_span);
+        let mut err = struct_span_err!(
+            self.tcx.sess,
+            error_span,
+            E0276,
+            "impl has stricter requirements than trait"
+        );
 
-        let mut err = struct_span_err!(self.tcx.sess, sp, E0276, "{}", msg);
-
-        if trait_item_def_id.is_local() {
+        if let Some(span) = self.tcx.hir().span_if_local(trait_item_def_id) {
             let item_name = self.tcx.item_name(impl_item_def_id.to_def_id());
-            err.span_label(
-                self.tcx.def_span(trait_item_def_id),
-                format!("definition of `{}` from trait", item_name),
-            );
+            err.span_label(span, format!("definition of `{}` from trait", item_name));
         }
 
-        err.span_label(sp, format!("impl has extra requirement {}", requirement));
+        err.span_label(error_span, format!("impl has extra requirement {}", requirement));
 
         err
     }
@@ -48,7 +47,6 @@ pub fn report_object_safety_error<'tcx>(
         hir::Node::Item(item) => Some(item.ident.span),
         _ => None,
     });
-    let span = tcx.sess.source_map().guess_head_span(span);
     let mut err = struct_span_err!(
         tcx.sess,
         span,
diff --git a/compiler/rustc_infer/src/traits/util.rs b/compiler/rustc_infer/src/traits/util.rs
index 674c75fdee5..38ff9343537 100644
--- a/compiler/rustc_infer/src/traits/util.rs
+++ b/compiler/rustc_infer/src/traits/util.rs
@@ -145,7 +145,12 @@ impl<'tcx> Elaborator<'tcx> {
                 // Get predicates declared on the trait.
                 let predicates = tcx.super_predicates_of(data.def_id());
 
-                let obligations = predicates.predicates.iter().map(|&(pred, _)| {
+                let obligations = predicates.predicates.iter().map(|&(mut pred, _)| {
+                    // when parent predicate is non-const, elaborate it to non-const predicates.
+                    if data.constness == ty::BoundConstness::NotConst {
+                        pred = pred.without_const(tcx);
+                    }
+
                     predicate_obligation(
                         pred.subst_supertrait(tcx, &bound_predicate.rebind(data.trait_ref)),
                         obligation.param_env,
diff --git a/compiler/rustc_interface/Cargo.toml b/compiler/rustc_interface/Cargo.toml
index 9aacfba3c82..1ecbc876c8d 100644
--- a/compiler/rustc_interface/Cargo.toml
+++ b/compiler/rustc_interface/Cargo.toml
@@ -46,7 +46,6 @@ rustc_query_impl = { path = "../rustc_query_impl" }
 rustc_resolve = { path = "../rustc_resolve" }
 rustc_trait_selection = { path = "../rustc_trait_selection" }
 rustc_ty_utils = { path = "../rustc_ty_utils" }
-tempfile = "3.2"
 
 [target.'cfg(unix)'.dependencies]
 libc = "0.2"
diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs
index e7563933c88..94f81b66077 100644
--- a/compiler/rustc_interface/src/interface.rs
+++ b/compiler/rustc_interface/src/interface.rs
@@ -300,7 +300,7 @@ pub fn create_compiler_and_run<R>(config: Config, f: impl FnOnce(&Compiler) -> R
         );
     }
 
-    let temps_dir = sess.opts.debugging_opts.temps_dir.as_ref().map(|o| PathBuf::from(&o));
+    let temps_dir = sess.opts.unstable_opts.temps_dir.as_ref().map(|o| PathBuf::from(&o));
 
     let compiler = Compiler {
         sess,
@@ -329,11 +329,13 @@ pub fn create_compiler_and_run<R>(config: Config, f: impl FnOnce(&Compiler) -> R
     })
 }
 
+// JUSTIFICATION: before session exists, only config
+#[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))]
 pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Send) -> R {
     tracing::trace!("run_compiler");
     util::run_in_thread_pool_with_globals(
         config.opts.edition,
-        config.opts.debugging_opts.threads,
+        config.opts.unstable_opts.threads,
         || create_compiler_and_run(config, f),
     )
 }
diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs
index b7d1d6edfaa..8f083591786 100644
--- a/compiler/rustc_interface/src/passes.rs
+++ b/compiler/rustc_interface/src/passes.rs
@@ -5,18 +5,15 @@ use crate::util;
 use ast::CRATE_NODE_ID;
 use rustc_ast::{self as ast, visit};
 use rustc_borrowck as mir_borrowck;
-use rustc_codegen_ssa::back::link::emit_metadata;
 use rustc_codegen_ssa::traits::CodegenBackend;
 use rustc_data_structures::parallel;
 use rustc_data_structures::sync::{Lrc, OnceCell, WorkerLocal};
-use rustc_data_structures::temp_dir::MaybeTempDir;
 use rustc_errors::{Applicability, ErrorGuaranteed, MultiSpan, PResult};
 use rustc_expand::base::{ExtCtxt, LintStoreExpand, ResolverExpand};
-use rustc_hir::def_id::{StableCrateId, LOCAL_CRATE};
+use rustc_hir::def_id::StableCrateId;
 use rustc_hir::definitions::Definitions;
-use rustc_lint::{EarlyCheckNode, LintStore};
+use rustc_lint::{BufferedEarlyLint, EarlyCheckNode, LintStore};
 use rustc_metadata::creader::CStore;
-use rustc_metadata::{encode_metadata, EncodedMetadata};
 use rustc_middle::arena::Arena;
 use rustc_middle::dep_graph::DepGraph;
 use rustc_middle::ty::query::{ExternProviders, Providers};
@@ -29,14 +26,13 @@ use rustc_query_impl::{OnDiskCache, Queries as TcxQueries};
 use rustc_resolve::{Resolver, ResolverArenas};
 use rustc_session::config::{CrateType, Input, OutputFilenames, OutputType};
 use rustc_session::cstore::{CrateStoreDyn, MetadataLoader, MetadataLoaderDyn};
-use rustc_session::output::{filename_for_input, filename_for_metadata};
+use rustc_session::output::filename_for_input;
 use rustc_session::search_paths::PathKind;
 use rustc_session::{Limit, Session};
 use rustc_span::symbol::{sym, Symbol};
 use rustc_span::FileName;
 use rustc_trait_selection::traits;
 use rustc_typeck as typeck;
-use tempfile::Builder as TempFileBuilder;
 use tracing::{info, warn};
 
 use std::any::Any;
@@ -58,16 +54,16 @@ pub fn parse<'a>(sess: &'a Session, input: &Input) -> PResult<'a, ast::Crate> {
         }
     })?;
 
-    if sess.opts.debugging_opts.input_stats {
+    if sess.opts.unstable_opts.input_stats {
         eprintln!("Lines of code:             {}", sess.source_map().count_lines());
         eprintln!("Pre-expansion node count:  {}", count_nodes(&krate));
     }
 
-    if let Some(ref s) = sess.opts.debugging_opts.show_span {
+    if let Some(ref s) = sess.opts.unstable_opts.show_span {
         rustc_ast_passes::show_span::run(sess.diagnostic(), s, &krate);
     }
 
-    if sess.opts.debugging_opts.hir_stats {
+    if sess.opts.unstable_opts.hir_stats {
         hir_stats::print_ast_stats(&krate, "PRE EXPANSION AST STATS");
     }
 
@@ -181,7 +177,7 @@ pub fn register_plugins<'a>(
         rustc_builtin_macros::cmdline_attrs::inject(
             krate,
             &sess.parse_sess,
-            &sess.opts.debugging_opts.crate_attr,
+            &sess.opts.unstable_opts.crate_attr,
         )
     });
 
@@ -213,8 +209,8 @@ pub fn register_plugins<'a>(
     }
 
     let mut lint_store = rustc_lint::new_lint_store(
-        sess.opts.debugging_opts.no_interleave_lints,
-        sess.unstable_options(),
+        sess.opts.unstable_opts.no_interleave_lints,
+        sess.enable_internal_lints(),
     );
     register_lints(sess, &mut lint_store);
 
@@ -327,21 +323,24 @@ pub fn configure_and_expand(
         let cfg = rustc_expand::expand::ExpansionConfig {
             features: Some(features),
             recursion_limit,
-            trace_mac: sess.opts.debugging_opts.trace_macros,
+            trace_mac: sess.opts.unstable_opts.trace_macros,
             should_test: sess.opts.test,
-            span_debug: sess.opts.debugging_opts.span_debug,
-            proc_macro_backtrace: sess.opts.debugging_opts.proc_macro_backtrace,
+            span_debug: sess.opts.unstable_opts.span_debug,
+            proc_macro_backtrace: sess.opts.unstable_opts.proc_macro_backtrace,
             ..rustc_expand::expand::ExpansionConfig::default(crate_name.to_string())
         };
 
         let lint_store = LintStoreExpandImpl(lint_store);
         let mut ecx = ExtCtxt::new(sess, cfg, resolver, Some(&lint_store));
-
         // Expand macros now!
         let krate = sess.time("expand_crate", || ecx.monotonic_expander().expand_crate(krate));
 
         // The rest is error reporting
 
+        sess.parse_sess.buffered_lints.with_lock(|buffered_lints: &mut Vec<BufferedEarlyLint>| {
+            buffered_lints.append(&mut ecx.buffered_early_lint);
+        });
+
         sess.time("check_unused_macros", || {
             ecx.check_unused_macros();
         });
@@ -413,11 +412,11 @@ pub fn configure_and_expand(
 
     // Done with macro expansion!
 
-    if sess.opts.debugging_opts.input_stats {
+    if sess.opts.unstable_opts.input_stats {
         eprintln!("Post-expansion node count: {}", count_nodes(&krate));
     }
 
-    if sess.opts.debugging_opts.hir_stats {
+    if sess.opts.unstable_opts.hir_stats {
         hir_stats::print_ast_stats(&krate, "POST EXPANSION AST STATS");
     }
 
@@ -500,7 +499,7 @@ fn generated_output_paths(
                     out_filenames.push(p);
                 }
             }
-            OutputType::DepInfo if sess.opts.debugging_opts.dep_info_omit_d_target => {
+            OutputType::DepInfo if sess.opts.unstable_opts.dep_info_omit_d_target => {
                 // Don't add the dep-info output when omitting it from dep-info targets
             }
             _ => {
@@ -598,7 +597,7 @@ fn write_out_deps(
         files.extend(extra_tracked_files);
 
         if sess.binary_dep_depinfo() {
-            if let Some(ref backend) = sess.opts.debugging_opts.codegen_backend {
+            if let Some(ref backend) = sess.opts.unstable_opts.codegen_backend {
                 if backend.contains('.') {
                     // If the backend name contain a `.`, it is the path to an external dynamic
                     // library. If not, it is not a path.
@@ -928,7 +927,7 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> {
     sess.time("MIR_effect_checking", || {
         for def_id in tcx.hir().body_owners() {
             tcx.ensure().thir_check_unsafety(def_id);
-            if !tcx.sess.opts.debugging_opts.thir_unsafeck {
+            if !tcx.sess.opts.unstable_opts.thir_unsafeck {
                 rustc_mir_transform::check_unsafety::check_unsafety(tcx, def_id);
             }
             tcx.ensure().has_ffi_unwind_calls(def_id);
@@ -990,69 +989,6 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> {
     Ok(())
 }
 
-fn encode_and_write_metadata(
-    tcx: TyCtxt<'_>,
-    outputs: &OutputFilenames,
-) -> (EncodedMetadata, bool) {
-    #[derive(PartialEq, Eq, PartialOrd, Ord)]
-    enum MetadataKind {
-        None,
-        Uncompressed,
-        Compressed,
-    }
-
-    let metadata_kind = tcx
-        .sess
-        .crate_types()
-        .iter()
-        .map(|ty| match *ty {
-            CrateType::Executable | CrateType::Staticlib | CrateType::Cdylib => MetadataKind::None,
-
-            CrateType::Rlib => MetadataKind::Uncompressed,
-
-            CrateType::Dylib | CrateType::ProcMacro => MetadataKind::Compressed,
-        })
-        .max()
-        .unwrap_or(MetadataKind::None);
-
-    let metadata = match metadata_kind {
-        MetadataKind::None => EncodedMetadata::new(),
-        MetadataKind::Uncompressed | MetadataKind::Compressed => encode_metadata(tcx),
-    };
-
-    let _prof_timer = tcx.sess.prof.generic_activity("write_crate_metadata");
-
-    let need_metadata_file = tcx.sess.opts.output_types.contains_key(&OutputType::Metadata);
-    if need_metadata_file {
-        let crate_name = tcx.crate_name(LOCAL_CRATE);
-        let out_filename = filename_for_metadata(tcx.sess, crate_name.as_str(), outputs);
-        // To avoid races with another rustc process scanning the output directory,
-        // we need to write the file somewhere else and atomically move it to its
-        // final destination, with an `fs::rename` call. In order for the rename to
-        // always succeed, the temporary file needs to be on the same filesystem,
-        // which is why we create it inside the output directory specifically.
-        let metadata_tmpdir = TempFileBuilder::new()
-            .prefix("rmeta")
-            .tempdir_in(out_filename.parent().unwrap())
-            .unwrap_or_else(|err| tcx.sess.fatal(&format!("couldn't create a temp dir: {}", err)));
-        let metadata_tmpdir = MaybeTempDir::new(metadata_tmpdir, tcx.sess.opts.cg.save_temps);
-        let metadata_filename = emit_metadata(tcx.sess, metadata.raw_data(), &metadata_tmpdir);
-        if let Err(e) = util::non_durable_rename(&metadata_filename, &out_filename) {
-            tcx.sess.fatal(&format!("failed to write {}: {}", out_filename.display(), e));
-        }
-        if tcx.sess.opts.json_artifact_notifications {
-            tcx.sess
-                .parse_sess
-                .span_diagnostic
-                .emit_artifact_notification(&out_filename, "metadata");
-        }
-    }
-
-    let need_metadata_module = metadata_kind == MetadataKind::Compressed;
-
-    (metadata, need_metadata_module)
-}
-
 /// Runs the codegen backend, after which the AST and analysis can
 /// be discarded.
 pub fn start_codegen<'tcx>(
@@ -1062,7 +998,8 @@ pub fn start_codegen<'tcx>(
 ) -> Box<dyn Any> {
     info!("Pre-codegen\n{:?}", tcx.debug_stats());
 
-    let (metadata, need_metadata_module) = encode_and_write_metadata(tcx, outputs);
+    let (metadata, need_metadata_module) =
+        rustc_metadata::fs::encode_and_write_metadata(tcx, outputs);
 
     let codegen = tcx.sess.time("codegen_crate", move || {
         codegen_backend.codegen_crate(tcx, metadata, need_metadata_module)
diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs
index 8ffb1ad0539..73402ae0842 100644
--- a/compiler/rustc_interface/src/queries.rs
+++ b/compiler/rustc_interface/src/queries.rs
@@ -357,7 +357,7 @@ impl Linker {
             return Ok(());
         }
 
-        if sess.opts.debugging_opts.no_link {
+        if sess.opts.unstable_opts.no_link {
             let encoded = CodegenResults::serialize_rlink(&codegen_results);
             let rlink_file = self.prepare_outputs.with_extension(config::RLINK_EXT);
             std::fs::write(&rlink_file, encoded).map_err(|err| {
diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs
index 55827ff583a..57ce4933a3b 100644
--- a/compiler/rustc_interface/src/tests.rs
+++ b/compiler/rustc_interface/src/tests.rs
@@ -1,3 +1,4 @@
+#![cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))]
 use crate::interface::parse_cfgspecs;
 
 use rustc_data_structures::fx::FxHashSet;
@@ -629,14 +630,14 @@ fn test_top_level_options_tracked_no_crate() {
 }
 
 #[test]
-fn test_debugging_options_tracking_hash() {
+fn test_unstable_options_tracking_hash() {
     let reference = Options::default();
     let mut opts = Options::default();
 
     macro_rules! untracked {
         ($name: ident, $non_default_value: expr) => {
-            assert_ne!(opts.debugging_opts.$name, $non_default_value);
-            opts.debugging_opts.$name = $non_default_value;
+            assert_ne!(opts.unstable_opts.$name, $non_default_value);
+            opts.unstable_opts.$name = $non_default_value;
             assert_same_hash(&reference, &opts);
         };
     }
@@ -669,6 +670,7 @@ fn test_debugging_options_tracking_hash() {
     untracked!(ls, true);
     untracked!(macro_backtrace, true);
     untracked!(meta_stats, true);
+    untracked!(mir_pretty_relative_line_numbers, true);
     untracked!(nll_facts, true);
     untracked!(no_analysis, true);
     untracked!(no_interleave_lints, true);
@@ -705,8 +707,8 @@ fn test_debugging_options_tracking_hash() {
     macro_rules! tracked {
         ($name: ident, $non_default_value: expr) => {
             opts = reference.clone();
-            assert_ne!(opts.debugging_opts.$name, $non_default_value);
-            opts.debugging_opts.$name = $non_default_value;
+            assert_ne!(opts.unstable_opts.$name, $non_default_value);
+            opts.unstable_opts.$name = $non_default_value;
             assert_different_hash(&reference, &opts);
         };
     }
@@ -718,6 +720,7 @@ fn test_debugging_options_tracking_hash() {
     tracked!(asm_comments, true);
     tracked!(assume_incomplete_release, true);
     tracked!(binary_dep_depinfo, true);
+    tracked!(box_noalias, Some(false));
     tracked!(
         branch_protection,
         Some(BranchProtection {
@@ -732,8 +735,10 @@ fn test_debugging_options_tracking_hash() {
     tracked!(debug_macros, true);
     tracked!(dep_info_omit_d_target, true);
     tracked!(drop_tracking, true);
+    tracked!(export_executable_symbols, true);
     tracked!(dual_proc_macros, true);
     tracked!(dwarf_version, Some(5));
+    tracked!(emit_thin_lto, false);
     tracked!(fewer_names, Some(true));
     tracked!(force_unstable_if_unmarked, true);
     tracked!(fuel, Some(("abc".to_string(), 99)));
@@ -804,8 +809,8 @@ fn test_debugging_options_tracking_hash() {
     macro_rules! tracked_no_crate_hash {
         ($name: ident, $non_default_value: expr) => {
             opts = reference.clone();
-            assert_ne!(opts.debugging_opts.$name, $non_default_value);
-            opts.debugging_opts.$name = $non_default_value;
+            assert_ne!(opts.unstable_opts.$name, $non_default_value);
+            opts.unstable_opts.$name = $non_default_value;
             assert_non_crate_hash_different(&reference, &opts);
         };
     }
diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs
index 8796ad5a33c..4c64e679b95 100644
--- a/compiler/rustc_interface/src/util.rs
+++ b/compiler/rustc_interface/src/util.rs
@@ -48,7 +48,10 @@ pub fn add_configuration(
 ) {
     let tf = sym::target_feature;
 
-    let target_features = codegen_backend.target_features(sess);
+    let unstable_target_features = codegen_backend.target_features(sess, true);
+    sess.unstable_target_features.extend(unstable_target_features.iter().cloned());
+
+    let target_features = codegen_backend.target_features(sess, false);
     sess.target_features.extend(target_features.iter().cloned());
 
     cfg.extend(target_features.into_iter().map(|feat| (tf, Some(feat))));
@@ -76,7 +79,7 @@ pub fn create_session(
     } else {
         get_codegen_backend(
             &sopts.maybe_sysroot,
-            sopts.debugging_opts.codegen_backend.as_ref().map(|name| &name[..]),
+            sopts.unstable_opts.codegen_backend.as_ref().map(|name| &name[..]),
         )
     };
 
@@ -86,9 +89,9 @@ pub fn create_session(
     let bundle = match rustc_errors::fluent_bundle(
         sopts.maybe_sysroot.clone(),
         sysroot_candidates(),
-        sopts.debugging_opts.translate_lang.clone(),
-        sopts.debugging_opts.translate_additional_ftl.as_deref(),
-        sopts.debugging_opts.translate_directionality_markers,
+        sopts.unstable_opts.translate_lang.clone(),
+        sopts.unstable_opts.translate_additional_ftl.as_deref(),
+        sopts.unstable_opts.translate_directionality_markers,
     ) {
         Ok(bundle) => bundle,
         Err(e) => {
@@ -556,6 +559,8 @@ pub fn collect_crate_types(session: &Session, attrs: &[ast::Attribute]) -> Vec<C
     // Only check command line flags if present. If no types are specified by
     // command line, then reuse the empty `base` Vec to hold the types that
     // will be found in crate attributes.
+    // JUSTIFICATION: before wrapper fn is available
+    #[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))]
     let mut base = session.opts.crate_types.clone();
     if base.is_empty() {
         base.extend(attr_types);
@@ -647,24 +652,6 @@ pub fn build_output_filenames(
     }
 }
 
-#[cfg(not(target_os = "linux"))]
-pub fn non_durable_rename(src: &Path, dst: &Path) -> std::io::Result<()> {
-    std::fs::rename(src, dst)
-}
-
-/// This function attempts to bypass the auto_da_alloc heuristic implemented by some filesystems
-/// such as btrfs and ext4. When renaming over a file that already exists then they will "helpfully"
-/// write back the source file before committing the rename in case a developer forgot some of
-/// the fsyncs in the open/write/fsync(file)/rename/fsync(dir) dance for atomic file updates.
-///
-/// To avoid triggering this heuristic we delete the destination first, if it exists.
-/// The cost of an extra syscall is much lower than getting descheduled for the sync IO.
-#[cfg(target_os = "linux")]
-pub fn non_durable_rename(src: &Path, dst: &Path) -> std::io::Result<()> {
-    let _ = std::fs::remove_file(dst);
-    std::fs::rename(src, dst)
-}
-
 /// Returns a version string such as "1.46.0 (04488afe3 2020-08-24)"
 pub fn version_str() -> Option<&'static str> {
     option_env!("CFG_VERSION")
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index a0472f98d72..9e4dc702f07 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -2858,9 +2858,10 @@ impl ClashingExternDeclarations {
                             let a_poly_sig = a.fn_sig(tcx);
                             let b_poly_sig = b.fn_sig(tcx);
 
-                            // As we don't compare regions, skip_binder is fine.
-                            let a_sig = a_poly_sig.skip_binder();
-                            let b_sig = b_poly_sig.skip_binder();
+                            // We don't compare regions, but leaving bound regions around ICEs, so
+                            // we erase them.
+                            let a_sig = tcx.erase_late_bound_regions(a_poly_sig);
+                            let b_sig = tcx.erase_late_bound_regions(b_poly_sig);
 
                             (a_sig.abi, a_sig.unsafety, a_sig.c_variadic)
                                 == (b_sig.abi, b_sig.unsafety, b_sig.c_variadic)
diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs
index 5725c240320..04ac50f1d48 100644
--- a/compiler/rustc_lint/src/context.rs
+++ b/compiler/rustc_lint/src/context.rs
@@ -694,9 +694,8 @@ pub trait LintContext: Sized {
                     }
 
                     if let Some(span) = in_test_module {
-                        let def_span = self.sess().source_map().guess_head_span(span);
                         db.span_help(
-                            span.shrink_to_lo().to(def_span),
+                            self.sess().source_map().guess_head_span(span),
                             "consider adding a `#[cfg(test)]` to the containing module",
                         );
                     }
@@ -857,6 +856,19 @@ pub trait LintContext: Sized {
                         Applicability::MachineApplicable,
                     );
                 },
+                BuiltinLintDiagnostics::NamedArgumentUsedPositionally(positional_arg, named_arg, name) => {
+                    db.span_label(named_arg, "this named argument is only referred to by position in formatting string");
+                    if let Some(positional_arg) = positional_arg {
+                        let msg = format!("this formatting argument uses named argument `{}` by position", name);
+                        db.span_label(positional_arg, msg);
+                        db.span_suggestion_verbose(
+                            positional_arg,
+                            "use the named argument by name to avoid ambiguity",
+                            name,
+                            Applicability::MaybeIncorrect,
+                        );
+                    }
+                }
             }
             // Rewrap `db`, and pass control to the user.
             decorate(LintDiagnosticBuilder::new(db));
diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs
index 5de35dc0856..b0a7a87fda4 100644
--- a/compiler/rustc_lint/src/early.rs
+++ b/compiler/rustc_lint/src/early.rs
@@ -154,6 +154,7 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T>
                 self.check_id(closure_id);
             }
         }
+
         run_early_pass!(self, check_fn_post, fk, span, id);
     }
 
@@ -218,7 +219,7 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T>
         // Explicitly check for lints associated with 'closure_id', since
         // it does not have a corresponding AST node
         match e.kind {
-            ast::ExprKind::Closure(_, ast::Async::Yes { closure_id, .. }, ..)
+            ast::ExprKind::Closure(_, _, ast::Async::Yes { closure_id, .. }, ..)
             | ast::ExprKind::Async(_, closure_id, ..) => self.check_id(closure_id),
             _ => {}
         }
@@ -416,7 +417,7 @@ pub fn check_ast_node<'a>(
     let mut passes: Vec<_> = passes.iter().map(|p| (p)()).collect();
     let mut buffered = lint_buffer.unwrap_or_default();
 
-    if sess.opts.debugging_opts.no_interleave_lints {
+    if sess.opts.unstable_opts.no_interleave_lints {
         for (i, pass) in passes.iter_mut().enumerate() {
             buffered =
                 sess.prof.extra_verbose_generic_activity("run_lint", pass.name()).run(|| {
diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs
index 738f475983e..c26d7824758 100644
--- a/compiler/rustc_lint/src/internal.rs
+++ b/compiler/rustc_lint/src/internal.rs
@@ -51,20 +51,6 @@ fn typeck_results_of_method_fn<'tcx>(
     cx: &LateContext<'tcx>,
     expr: &Expr<'_>,
 ) -> Option<(Span, DefId, ty::subst::SubstsRef<'tcx>)> {
-    // FIXME(rustdoc): Lints which use this function use typecheck results which can cause
-    // `rustdoc` to error if there are resolution failures.
-    //
-    // As internal lints are currently always run if there are `unstable_options`, they are added
-    // to the lint store of rustdoc. Internal lints are also not used via the `lint_mod` query.
-    // Crate lints run outside of a query so rustdoc currently doesn't disable them.
-    //
-    // Instead of relying on this, either change crate lints to a query disabled by rustdoc, only
-    // run internal lints if the user is explicitly opting in or figure out a different way to
-    // avoid running lints for rustdoc.
-    if cx.tcx.sess.opts.actually_rustdoc {
-        return None;
-    }
-
     match expr.kind {
         ExprKind::MethodCall(segment, _, _)
             if let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) =>
@@ -446,3 +432,38 @@ impl LateLintPass<'_> for Diagnostics {
         }
     }
 }
+
+declare_tool_lint! {
+    pub rustc::BAD_OPT_ACCESS,
+    Deny,
+    "prevent using options by field access when there is a wrapper function",
+    report_in_external_macro: true
+}
+
+declare_lint_pass!(BadOptAccess => [ BAD_OPT_ACCESS ]);
+
+impl LateLintPass<'_> for BadOptAccess {
+    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
+        let ExprKind::Field(base, target) = expr.kind else { return };
+        let Some(adt_def) = cx.typeck_results().expr_ty(base).ty_adt_def() else { return };
+        // Skip types without `#[rustc_lint_opt_ty]` - only so that the rest of the lint can be
+        // avoided.
+        if !cx.tcx.has_attr(adt_def.did(), sym::rustc_lint_opt_ty) {
+            return;
+        }
+
+        for field in adt_def.all_fields() {
+            if field.name == target.name &&
+                let Some(attr) = cx.tcx.get_attr(field.did, sym::rustc_lint_opt_deny_field_access) &&
+                let Some(items) = attr.meta_item_list()  &&
+                let Some(item) = items.first()  &&
+                let Some(literal) = item.literal()  &&
+                let ast::LitKind::Str(val, _) = literal.kind
+            {
+                cx.struct_span_lint(BAD_OPT_ACCESS, expr.span, |lint| {
+                    lint.build(val.as_str()).emit(); }
+                );
+            }
+        }
+    }
+}
diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs
index 27f67207209..afb18451cf3 100644
--- a/compiler/rustc_lint/src/late.rs
+++ b/compiler/rustc_lint/src/late.rs
@@ -402,7 +402,7 @@ pub fn late_lint_mod<'tcx, T: LateLintPass<'tcx>>(
     module_def_id: LocalDefId,
     builtin_lints: T,
 ) {
-    if tcx.sess.opts.debugging_opts.no_interleave_lints {
+    if tcx.sess.opts.unstable_opts.no_interleave_lints {
         // These passes runs in late_lint_crate with -Z no_interleave_lints
         return;
     }
@@ -448,7 +448,7 @@ fn late_lint_pass_crate<'tcx, T: LateLintPass<'tcx>>(tcx: TyCtxt<'tcx>, pass: T)
 fn late_lint_crate<'tcx, T: LateLintPass<'tcx>>(tcx: TyCtxt<'tcx>, builtin_lints: T) {
     let mut passes = unerased_lint_store(tcx).late_passes.iter().map(|p| (p)()).collect::<Vec<_>>();
 
-    if !tcx.sess.opts.debugging_opts.no_interleave_lints {
+    if !tcx.sess.opts.unstable_opts.no_interleave_lints {
         if !passes.is_empty() {
             late_lint_pass_crate(tcx, LateLintPassObjects { lints: &mut passes[..] });
         }
diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs
index aaee0caa070..7b0702dad75 100644
--- a/compiler/rustc_lint/src/lib.rs
+++ b/compiler/rustc_lint/src/lib.rs
@@ -33,7 +33,7 @@
 #![feature(if_let_guard)]
 #![feature(iter_intersperse)]
 #![feature(iter_order_by)]
-#![feature(let_chains)]
+#![cfg_attr(bootstrap, feature(let_chains))]
 #![feature(let_else)]
 #![feature(never_type)]
 #![recursion_limit = "256"]
@@ -509,8 +509,14 @@ fn register_internals(store: &mut LintStore) {
     store.register_late_pass(|| Box::new(TyTyKind));
     store.register_lints(&Diagnostics::get_lints());
     store.register_late_pass(|| Box::new(Diagnostics));
+    store.register_lints(&BadOptAccess::get_lints());
+    store.register_late_pass(|| Box::new(BadOptAccess));
     store.register_lints(&PassByValue::get_lints());
     store.register_late_pass(|| Box::new(PassByValue));
+    // FIXME(davidtwco): deliberately do not include `UNTRANSLATABLE_DIAGNOSTIC` and
+    // `DIAGNOSTIC_OUTSIDE_OF_IMPL` here because `-Wrustc::internal` is provided to every crate and
+    // these lints will trigger all of the time - change this once migration to diagnostic structs
+    // and translation is completed
     store.register_group(
         false,
         "rustc::internal",
@@ -523,6 +529,7 @@ fn register_internals(store: &mut LintStore) {
             LintId::of(LINT_PASS_IMPL_WITHOUT_MACRO),
             LintId::of(USAGE_OF_QUALIFIED_TY),
             LintId::of(EXISTING_DOC_KEYWORD),
+            LintId::of(BAD_OPT_ACCESS),
         ],
     );
 }
diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs
index be4843c7ff1..5c07afeb7aa 100644
--- a/compiler/rustc_lint/src/types.rs
+++ b/compiler/rustc_lint/src/types.rs
@@ -703,9 +703,8 @@ fn ty_is_known_nonnull<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, mode: CItemKi
                 return true;
             }
 
-            // Types with a `#[repr(no_niche)]` attribute have their niche hidden.
-            // The attribute is used by the UnsafeCell for example (the only use so far).
-            if def.repr().hide_niche() {
+            // `UnsafeCell` has its niche hidden.
+            if def.is_unsafe_cell() {
                 return false;
             }
 
@@ -1435,10 +1434,6 @@ declare_lint! {
     /// - Passing `Ordering::Release` or `Ordering::AcqRel` as the failure
     ///   ordering for any of `AtomicType::compare_exchange`,
     ///   `AtomicType::compare_exchange_weak`, or `AtomicType::fetch_update`.
-    ///
-    /// - Passing in a pair of orderings to `AtomicType::compare_exchange`,
-    ///   `AtomicType::compare_exchange_weak`, or `AtomicType::fetch_update`
-    ///   where the failure ordering is stronger than the success ordering.
     INVALID_ATOMIC_ORDERING,
     Deny,
     "usage of invalid atomic ordering in atomic operations and memory fences"
@@ -1545,9 +1540,9 @@ impl InvalidAtomicOrdering {
         let Some((method, args)) = Self::inherent_atomic_method_call(cx, expr, &[sym::fetch_update, sym::compare_exchange, sym::compare_exchange_weak])
             else {return };
 
-        let (success_order_arg, fail_order_arg) = match method {
-            sym::fetch_update => (&args[1], &args[2]),
-            sym::compare_exchange | sym::compare_exchange_weak => (&args[3], &args[4]),
+        let fail_order_arg = match method {
+            sym::fetch_update => &args[2],
+            sym::compare_exchange | sym::compare_exchange_weak => &args[4],
             _ => return,
         };
 
@@ -1569,37 +1564,6 @@ impl InvalidAtomicOrdering {
                 InvalidAtomicOrderingDiag { method, fail_order_arg_span: fail_order_arg.span },
             );
         }
-
-        let Some(success_ordering) = Self::match_ordering(cx, success_order_arg) else { return };
-
-        if matches!(
-            (success_ordering, fail_ordering),
-            (sym::Relaxed | sym::Release, sym::Acquire)
-                | (sym::Relaxed | sym::Release | sym::Acquire | sym::AcqRel, sym::SeqCst)
-        ) {
-            let success_suggestion =
-                if success_ordering == sym::Release && fail_ordering == sym::Acquire {
-                    sym::AcqRel
-                } else {
-                    fail_ordering
-                };
-            cx.struct_span_lint(INVALID_ATOMIC_ORDERING, success_order_arg.span, |diag| {
-                diag.build(fluent::lint::atomic_ordering_invalid_fail_success)
-                    .set_arg("method", method)
-                    .set_arg("fail_ordering", fail_ordering)
-                    .set_arg("success_ordering", success_ordering)
-                    .set_arg("success_suggestion", success_suggestion)
-                    .span_label(fail_order_arg.span, fluent::lint::fail_label)
-                    .span_label(success_order_arg.span, fluent::lint::success_label)
-                    .span_suggestion_short(
-                        success_order_arg.span,
-                        fluent::lint::suggestion,
-                        format!("std::sync::atomic::Ordering::{success_suggestion}"),
-                        Applicability::MaybeIncorrect,
-                    )
-                    .emit();
-            });
-        }
     }
 }
 
diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs
index 9fc2249b290..39690851d1e 100644
--- a/compiler/rustc_lint_defs/src/builtin.rs
+++ b/compiler/rustc_lint_defs/src/builtin.rs
@@ -3132,6 +3132,60 @@ declare_lint! {
     "detects unexpected names and values in `#[cfg]` conditions",
 }
 
+declare_lint! {
+    /// The `repr_transparent_external_private_fields` lint
+    /// detects types marked `#[repr(transparent)]` that (transitively)
+    /// contain an external ZST type marked `#[non_exhaustive]` or containing
+    /// private fields
+    ///
+    /// ### Example
+    ///
+    /// ```rust,ignore (needs external crate)
+    /// #![deny(repr_transparent_external_private_fields)]
+    /// use foo::NonExhaustiveZst;
+    ///
+    /// #[repr(transparent)]
+    /// struct Bar(u32, ([u32; 0], NonExhaustiveZst));
+    /// ```
+    ///
+    /// This will produce:
+    ///
+    /// ```text
+    /// error: zero-sized fields in repr(transparent) cannot contain external non-exhaustive types
+    ///  --> src/main.rs:5:28
+    ///   |
+    /// 5 | struct Bar(u32, ([u32; 0], NonExhaustiveZst));
+    ///   |                            ^^^^^^^^^^^^^^^^
+    ///   |
+    /// note: the lint level is defined here
+    ///  --> src/main.rs:1:9
+    ///   |
+    /// 1 | #![deny(repr_transparent_external_private_fields)]
+    ///   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+    ///   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+    ///   = note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
+    ///   = note: this struct contains `NonExhaustiveZst`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future.
+    /// ```
+    ///
+    /// ### Explanation
+    ///
+    /// Previous, Rust accepted fields that contain external private zero-sized types,
+    /// even though it should not be a breaking change to add a non-zero-sized field to
+    /// that private type.
+    ///
+    /// This is a [future-incompatible] lint to transition this
+    /// to a hard error in the future. See [issue #78586] for more details.
+    ///
+    /// [issue #78586]: https://github.com/rust-lang/rust/issues/78586
+    /// [future-incompatible]: ../index.md#future-incompatible-lints
+    pub REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS,
+    Warn,
+    "tranparent type contains an external ZST that is marked #[non_exhaustive] or contains private fields",
+    @future_incompatible = FutureIncompatibleInfo {
+        reference: "issue #78586 <https://github.com/rust-lang/rust/issues/78586>",
+    };
+}
+
 declare_lint_pass! {
     /// Does nothing as a lint pass, but registers some `Lint`s
     /// that are used by other parts of the compiler.
@@ -3237,6 +3291,8 @@ declare_lint_pass! {
         DEPRECATED_WHERE_CLAUSE_LOCATION,
         TEST_UNSTABLE_LINT,
         FFI_UNWIND_CALLS,
+        REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS,
+        NAMED_ARGUMENTS_USED_POSITIONALLY,
     ]
 }
 
@@ -3941,3 +3997,33 @@ declare_lint! {
     "call to foreign functions or function pointers with FFI-unwind ABI",
     @feature_gate = sym::c_unwind;
 }
+
+declare_lint! {
+    /// The `named_arguments_used_positionally` lint detects cases where named arguments are only
+    /// used positionally in format strings. This usage is valid but potentially very confusing.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,compile_fail
+    /// #![deny(named_arguments_used_positionally)]
+    /// fn main() {
+    ///     let _x = 5;
+    ///     println!("{}", _x = 1); // Prints 1, will trigger lint
+    ///
+    ///     println!("{}", _x); // Prints 5, no lint emitted
+    ///     println!("{_x}", _x = _x); // Prints 5, no lint emitted
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Rust formatting strings can refer to named arguments by their position, but this usage is
+    /// potentially confusing. In particular, readers can incorrectly assume that the declaration
+    /// of named arguments is an assignment (which would produce the unit type).
+    /// For backwards compatibility, this is not a hard error.
+    pub NAMED_ARGUMENTS_USED_POSITIONALLY,
+    Warn,
+    "named arguments in format used positionally"
+}
diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs
index 48f441e69d6..4fd57ed8533 100644
--- a/compiler/rustc_lint_defs/src/lib.rs
+++ b/compiler/rustc_lint_defs/src/lib.rs
@@ -467,6 +467,7 @@ pub enum BuiltinLintDiagnostics {
         /// If true, the lifetime will be fully elided.
         use_span: Option<(Span, bool)>,
     },
+    NamedArgumentUsedPositionally(Option<Span>, Span, String),
 }
 
 /// Lints that are buffered up early on in the `Session` before the
diff --git a/compiler/rustc_llvm/build.rs b/compiler/rustc_llvm/build.rs
index 7729ec6bef4..62ef5804dce 100644
--- a/compiler/rustc_llvm/build.rs
+++ b/compiler/rustc_llvm/build.rs
@@ -4,6 +4,29 @@ use std::fmt::Display;
 use std::path::{Path, PathBuf};
 use std::process::{Command, Stdio};
 
+const OPTIONAL_COMPONENTS: &[&str] = &[
+    "x86",
+    "arm",
+    "aarch64",
+    "amdgpu",
+    "avr",
+    "m68k",
+    "mips",
+    "powerpc",
+    "systemz",
+    "jsbackend",
+    "webassembly",
+    "msp430",
+    "sparc",
+    "nvptx",
+    "hexagon",
+    "riscv",
+    "bpf",
+];
+
+const REQUIRED_COMPONENTS: &[&str] =
+    &["ipo", "bitreader", "bitwriter", "linker", "asmparser", "lto", "coverage", "instrumentation"];
+
 fn detect_llvm_link() -> (&'static str, &'static str) {
     // Force the link mode we want, preferring static by default, but
     // possibly overridden by `configure --enable-llvm-link-shared`.
@@ -76,6 +99,10 @@ fn output(cmd: &mut Command) -> String {
 }
 
 fn main() {
+    for component in REQUIRED_COMPONENTS.iter().chain(OPTIONAL_COMPONENTS.iter()) {
+        println!("cargo:rustc-check-cfg=values(llvm_component,\"{}\")", component);
+    }
+
     if tracked_env_var_os("RUST_CHECK").is_some() {
         // If we're just running `check`, there's no need for LLVM to be built.
         return;
@@ -131,42 +158,11 @@ fn main() {
     let host = env::var("HOST").expect("HOST was not set");
     let is_crossed = target != host;
 
-    let optional_components = &[
-        "x86",
-        "arm",
-        "aarch64",
-        "amdgpu",
-        "avr",
-        "m68k",
-        "mips",
-        "powerpc",
-        "systemz",
-        "jsbackend",
-        "webassembly",
-        "msp430",
-        "sparc",
-        "nvptx",
-        "hexagon",
-        "riscv",
-        "bpf",
-    ];
-
-    let required_components = &[
-        "ipo",
-        "bitreader",
-        "bitwriter",
-        "linker",
-        "asmparser",
-        "lto",
-        "coverage",
-        "instrumentation",
-    ];
-
     let components = output(Command::new(&llvm_config).arg("--components"));
     let mut components = components.split_whitespace().collect::<Vec<_>>();
-    components.retain(|c| optional_components.contains(c) || required_components.contains(c));
+    components.retain(|c| OPTIONAL_COMPONENTS.contains(c) || REQUIRED_COMPONENTS.contains(c));
 
-    for component in required_components {
+    for component in REQUIRED_COMPONENTS {
         if !components.contains(component) {
             panic!("require llvm component {} but wasn't found", component);
         }
diff --git a/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h b/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h
index a2b0e9b4d29..93f302b44e8 100644
--- a/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h
+++ b/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h
@@ -84,6 +84,11 @@ enum LLVMRustAttribute {
   StackProtect = 32,
   NoUndef = 33,
   SanitizeMemTag = 34,
+  NoCfCheck = 35,
+  ShadowCallStack = 36,
+  AllocSize = 37,
+  AllocatedPointer = 38,
+  AllocAlign = 39,
 };
 
 typedef struct OpaqueRustString *RustStringRef;
diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
index 0f4973ebf71..0a6bd49992d 100644
--- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
+++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
@@ -34,6 +34,7 @@
 #include "llvm/Transforms/Utils/AddDiscriminators.h"
 #include "llvm/Transforms/Utils/FunctionImportUtils.h"
 #include "llvm/LTO/LTO.h"
+#include "llvm/Bitcode/BitcodeWriterPass.h"
 #include "llvm-c/Transforms/PassManagerBuilder.h"
 
 #include "llvm/Transforms/Instrumentation.h"
@@ -89,23 +90,6 @@ extern "C" void LLVMTimeTraceProfilerFinish(const char* FileName) {
   timeTraceProfilerCleanup();
 }
 
-enum class LLVMRustPassKind {
-  Other,
-  Function,
-  Module,
-};
-
-static LLVMRustPassKind toRust(PassKind Kind) {
-  switch (Kind) {
-  case PT_Function:
-    return LLVMRustPassKind::Function;
-  case PT_Module:
-    return LLVMRustPassKind::Module;
-  default:
-    return LLVMRustPassKind::Other;
-  }
-}
-
 extern "C" LLVMPassRef LLVMRustFindAndCreatePass(const char *PassName) {
 #if LLVM_VERSION_LT(15, 0)
   StringRef SR(PassName);
@@ -171,12 +155,6 @@ extern "C" LLVMPassRef LLVMRustCreateHWAddressSanitizerPass(bool Recover) {
 #endif
 }
 
-extern "C" LLVMRustPassKind LLVMRustPassKind(LLVMPassRef RustPass) {
-  assert(RustPass);
-  Pass *Pass = unwrap(RustPass);
-  return toRust(Pass->getPassKind());
-}
-
 extern "C" void LLVMRustAddPass(LLVMPassManagerRef PMR, LLVMPassRef RustPass) {
 #if LLVM_VERSION_LT(15, 0)
   assert(RustPass);
@@ -1603,28 +1581,6 @@ LLVMRustPrepareThinLTOImport(const LLVMRustThinLTOData *Data, LLVMModuleRef M,
   return true;
 }
 
-extern "C" typedef void (*LLVMRustModuleNameCallback)(void*, // payload
-                                                      const char*, // importing module name
-                                                      const char*); // imported module name
-
-// Calls `module_name_callback` for each module import done by ThinLTO.
-// The callback is provided with regular null-terminated C strings.
-extern "C" void
-LLVMRustGetThinLTOModules(const LLVMRustThinLTOData *data,
-                                LLVMRustModuleNameCallback module_name_callback,
-                                void* callback_payload) {
-  for (const auto& importing_module : data->ImportLists) {
-    const std::string importing_module_id = importing_module.getKey().str();
-    const auto& imports = importing_module.getValue();
-    for (const auto& imported_module : imports) {
-      const std::string imported_module_id = imported_module.getKey().str();
-      module_name_callback(callback_payload,
-                           importing_module_id.c_str(),
-                           imported_module_id.c_str());
-    }
-  }
-}
-
 // This struct and various functions are sort of a hack right now, but the
 // problem is that we've got in-memory LLVM modules after we generate and
 // optimize all codegen-units for one compilation in rustc. To be compatible
@@ -1638,13 +1594,17 @@ struct LLVMRustThinLTOBuffer {
 };
 
 extern "C" LLVMRustThinLTOBuffer*
-LLVMRustThinLTOBufferCreate(LLVMModuleRef M) {
+LLVMRustThinLTOBufferCreate(LLVMModuleRef M, bool is_thin) {
   auto Ret = std::make_unique<LLVMRustThinLTOBuffer>();
   {
     raw_string_ostream OS(Ret->data);
     {
       legacy::PassManager PM;
-      PM.add(createWriteThinLTOBitcodePass(OS));
+      if (is_thin) {
+        PM.add(createWriteThinLTOBitcodePass(OS));
+      } else {
+        PM.add(createBitcodeWriterPass(OS));
+      }
       PM.run(*unwrap(M));
     }
   }
@@ -1715,7 +1675,7 @@ LLVMRustGetBitcodeSliceFromObjectData(const char *data,
 // Rewrite all `DICompileUnit` pointers to the `DICompileUnit` specified. See
 // the comment in `back/lto.rs` for why this exists.
 extern "C" void
-LLVMRustLTOGetDICompileUnit(LLVMModuleRef Mod,
+LLVMRustThinLTOGetDICompileUnit(LLVMModuleRef Mod,
                                 DICompileUnit **A,
                                 DICompileUnit **B) {
   Module *M = unwrap(Mod);
@@ -1733,7 +1693,7 @@ LLVMRustLTOGetDICompileUnit(LLVMModuleRef Mod,
 // Rewrite all `DICompileUnit` pointers to the `DICompileUnit` specified. See
 // the comment in `back/lto.rs` for why this exists.
 extern "C" void
-LLVMRustLTOPatchDICompileUnit(LLVMModuleRef Mod, DICompileUnit *Unit) {
+LLVMRustThinLTOPatchDICompileUnit(LLVMModuleRef Mod, DICompileUnit *Unit) {
   Module *M = unwrap(Mod);
 
   // If the original source module didn't have a `DICompileUnit` then try to
diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
index 8c5b4e2dc96..c333738ded4 100644
--- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
+++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
@@ -6,6 +6,7 @@
 #include "llvm/IR/GlobalVariable.h"
 #include "llvm/IR/Instructions.h"
 #include "llvm/IR/Intrinsics.h"
+#include "llvm/IR/IntrinsicsARM.h"
 #include "llvm/IR/Mangler.h"
 #include "llvm/Object/Archive.h"
 #include "llvm/Object/COFFImportFile.h"
@@ -88,10 +89,6 @@ extern "C" char *LLVMRustGetLastError(void) {
   return Ret;
 }
 
-extern "C" unsigned int LLVMRustGetInstructionCount(LLVMModuleRef M) {
-  return unwrap(M)->getInstructionCount();
-}
-
 extern "C" void LLVMRustSetLastError(const char *Err) {
   free((void *)LastError);
   LastError = strdup(Err);
@@ -176,6 +173,8 @@ static Attribute::AttrKind fromRust(LLVMRustAttribute Kind) {
     return Attribute::NoAlias;
   case NoCapture:
     return Attribute::NoCapture;
+  case NoCfCheck:
+    return Attribute::NoCfCheck;
   case NoInline:
     return Attribute::NoInline;
   case NonNull:
@@ -230,6 +229,16 @@ static Attribute::AttrKind fromRust(LLVMRustAttribute Kind) {
     return Attribute::NoUndef;
   case SanitizeMemTag:
     return Attribute::SanitizeMemTag;
+  case ShadowCallStack:
+    return Attribute::ShadowCallStack;
+  case AllocSize:
+    return Attribute::AllocSize;
+#if LLVM_VERSION_GE(15, 0)
+  case AllocatedPointer:
+    return Attribute::AllocatedPointer;
+  case AllocAlign:
+    return Attribute::AllocAlign;
+#endif
   }
   report_fatal_error("bad AttributeKind");
 }
@@ -292,6 +301,14 @@ extern "C" LLVMAttributeRef LLVMRustCreateStructRetAttr(LLVMContextRef C, LLVMTy
   return wrap(Attribute::getWithStructRetType(*unwrap(C), unwrap(Ty)));
 }
 
+extern "C" LLVMAttributeRef LLVMRustCreateElementTypeAttr(LLVMContextRef C, LLVMTypeRef Ty) {
+#if LLVM_VERSION_GE(15, 0)
+  return wrap(Attribute::get(*unwrap(C), Attribute::ElementType, unwrap(Ty)));
+#else
+  report_fatal_error("Should not be needed on LLVM < 15");
+#endif
+}
+
 extern "C" LLVMAttributeRef LLVMRustCreateUWTableAttr(LLVMContextRef C, bool Async) {
 #if LLVM_VERSION_LT(15, 0)
   return wrap(Attribute::get(*unwrap(C), Attribute::UWTable));
@@ -301,6 +318,67 @@ extern "C" LLVMAttributeRef LLVMRustCreateUWTableAttr(LLVMContextRef C, bool Asy
 #endif
 }
 
+extern "C" LLVMAttributeRef LLVMRustCreateAllocSizeAttr(LLVMContextRef C, uint32_t ElementSizeArg) {
+  return wrap(Attribute::getWithAllocSizeArgs(*unwrap(C), ElementSizeArg, None));
+}
+
+#if LLVM_VERSION_GE(15, 0)
+
+// These values **must** match ffi::AllocKindFlags.
+// It _happens_ to match the LLVM values of llvm::AllocFnKind,
+// but that's happenstance and we do explicit conversions before
+// passing them to LLVM.
+enum class LLVMRustAllocKindFlags : uint64_t {
+  Unknown = 0,
+  Alloc = 1,
+  Realloc = 1 << 1,
+  Free = 1 << 2,
+  Uninitialized = 1 << 3,
+  Zeroed = 1 << 4,
+  Aligned = 1 << 5,
+};
+
+static LLVMRustAllocKindFlags operator&(LLVMRustAllocKindFlags A, LLVMRustAllocKindFlags B) {
+  return static_cast<LLVMRustAllocKindFlags>(static_cast<uint64_t>(A) &
+                                      static_cast<uint64_t>(B));
+}
+
+static bool isSet(LLVMRustAllocKindFlags F) { return F != LLVMRustAllocKindFlags::Unknown; }
+
+static llvm::AllocFnKind allocKindFromRust(LLVMRustAllocKindFlags F) {
+  llvm::AllocFnKind AFK = llvm::AllocFnKind::Unknown;
+  if (isSet(F & LLVMRustAllocKindFlags::Alloc)) {
+    AFK |= llvm::AllocFnKind::Alloc;
+  }
+  if (isSet(F & LLVMRustAllocKindFlags::Realloc)) {
+    AFK |= llvm::AllocFnKind::Realloc;
+  }
+  if (isSet(F & LLVMRustAllocKindFlags::Free)) {
+    AFK |= llvm::AllocFnKind::Free;
+  }
+  if (isSet(F & LLVMRustAllocKindFlags::Uninitialized)) {
+    AFK |= llvm::AllocFnKind::Uninitialized;
+  }
+  if (isSet(F & LLVMRustAllocKindFlags::Zeroed)) {
+    AFK |= llvm::AllocFnKind::Zeroed;
+  }
+  if (isSet(F & LLVMRustAllocKindFlags::Aligned)) {
+    AFK |= llvm::AllocFnKind::Aligned;
+  }
+  return AFK;
+}
+#endif
+
+extern "C" LLVMAttributeRef LLVMRustCreateAllocKindAttr(LLVMContextRef C, uint64_t AllocKindArg) {
+#if LLVM_VERSION_GE(15, 0)
+  return wrap(Attribute::get(*unwrap(C), Attribute::AllocKind,
+      static_cast<uint64_t>(allocKindFromRust(static_cast<LLVMRustAllocKindFlags>(AllocKindArg)))));
+#else
+  report_fatal_error(
+      "allockind attributes are new in LLVM 15 and should not be used on older LLVMs");
+#endif
+}
+
 // Enable a fast-math flag
 //
 // https://llvm.org/docs/LangRef.html#fast-math-flags
@@ -411,8 +489,14 @@ LLVMRustInlineAsm(LLVMTypeRef Ty, char *AsmString, size_t AsmStringLen,
 
 extern "C" bool LLVMRustInlineAsmVerify(LLVMTypeRef Ty, char *Constraints,
                                         size_t ConstraintsLen) {
+#if LLVM_VERSION_LT(15, 0)
   return InlineAsm::Verify(unwrap<FunctionType>(Ty),
                            StringRef(Constraints, ConstraintsLen));
+#else
+  // llvm::Error converts to true if it is an error.
+  return !llvm::errorToBool(InlineAsm::verify(
+      unwrap<FunctionType>(Ty), StringRef(Constraints, ConstraintsLen)));
+#endif
 }
 
 extern "C" void LLVMRustAppendModuleInlineAsm(LLVMModuleRef M, const char *Asm,
@@ -1450,11 +1534,6 @@ extern "C" void LLVMRustSetComdat(LLVMModuleRef M, LLVMValueRef V,
   }
 }
 
-extern "C" void LLVMRustUnsetComdat(LLVMValueRef V) {
-  GlobalObject *GV = unwrap<GlobalObject>(V);
-  GV->setComdat(nullptr);
-}
-
 enum class LLVMRustLinkage {
   ExternalLinkage = 0,
   AvailableExternallyLinkage = 1,
@@ -1873,3 +1952,16 @@ extern "C" LLVMValueRef LLVMGetAggregateElement(LLVMValueRef C, unsigned Idx) {
     return wrap(unwrap<Constant>(C)->getAggregateElement(Idx));
 }
 #endif
+
+extern "C" int32_t LLVMRustGetElementTypeArgIndex(LLVMValueRef CallSite) {
+#if LLVM_VERSION_GE(15, 0)
+    auto *CB = unwrap<CallBase>(CallSite);
+    switch (CB->getIntrinsicID()) {
+        case Intrinsic::arm_ldrex:
+            return 0;
+        case Intrinsic::arm_strex:
+            return 1;
+    }
+#endif
+    return -1;
+}
diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic.rs b/compiler/rustc_macros/src/diagnostics/diagnostic.rs
index 027f377b0ac..6b5b8b59320 100644
--- a/compiler/rustc_macros/src/diagnostics/diagnostic.rs
+++ b/compiler/rustc_macros/src/diagnostics/diagnostic.rs
@@ -59,7 +59,7 @@ impl<'a> SessionDiagnosticDerive<'a> {
                         return DiagnosticDeriveError::ErrorHandled.to_compile_error();
                     }
                     (Some(DiagnosticDeriveKind::Lint), _) => {
-                        span_err(span, "only `#[error(..)]` and `#[warn(..)]` are supported")
+                        span_err(span, "only `#[error(..)]` and `#[warning(..)]` are supported")
                             .help("use the `#[error(...)]` attribute to create a error")
                             .emit();
                         return DiagnosticDeriveError::ErrorHandled.to_compile_error();
diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs
index 74ce1ab08c2..6c9561925fe 100644
--- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs
+++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs
@@ -8,12 +8,13 @@ use crate::diagnostics::utils::{
     report_error_if_not_applied_to_span, report_type_error, type_is_unit, type_matches_path,
     Applicability, FieldInfo, FieldInnerTy, HasFieldMap, SetOnce,
 };
-use proc_macro2::{Ident, TokenStream};
+use proc_macro2::{Ident, Span, TokenStream};
 use quote::{format_ident, quote};
 use std::collections::HashMap;
 use std::str::FromStr;
 use syn::{
-    parse_quote, spanned::Spanned, Attribute, Meta, MetaList, MetaNameValue, NestedMeta, Path, Type,
+    parse_quote, spanned::Spanned, Attribute, Field, Meta, MetaList, MetaNameValue, NestedMeta,
+    Path, Type,
 };
 use synstructure::{BindingInfo, Structure};
 
@@ -80,8 +81,8 @@ impl DiagnosticDeriveBuilder {
     }
 
     pub fn body<'s>(&mut self, structure: &mut Structure<'s>) -> (TokenStream, TokenStream) {
-        // Keep track of which fields are subdiagnostics or have no attributes.
-        let mut subdiagnostics_or_empty = std::collections::HashSet::new();
+        // Keep track of which fields need to be handled with a by-move binding.
+        let mut needs_moved = std::collections::HashSet::new();
 
         // Generates calls to `span_label` and similar functions based on the attributes
         // on fields. Code for suggestions uses formatting machinery and the value of
@@ -92,16 +93,11 @@ impl DiagnosticDeriveBuilder {
         let attrs = structure
             .clone()
             .filter(|field_binding| {
-                let attrs = &field_binding.ast().attrs;
-
-                (!attrs.is_empty()
-                    && attrs.iter().all(|attr| {
-                        "subdiagnostic" != attr.path.segments.last().unwrap().ident.to_string()
-                    }))
-                    || {
-                        subdiagnostics_or_empty.insert(field_binding.binding.clone());
-                        false
-                    }
+                let ast = &field_binding.ast();
+                !self.needs_move(ast) || {
+                    needs_moved.insert(field_binding.binding.clone());
+                    false
+                }
             })
             .each(|field_binding| self.generate_field_attrs_code(field_binding));
 
@@ -111,12 +107,41 @@ impl DiagnosticDeriveBuilder {
         // attributes or a `#[subdiagnostic]` attribute then it must be passed as an
         // argument to the diagnostic so that it can be referred to by Fluent messages.
         let args = structure
-            .filter(|field_binding| subdiagnostics_or_empty.contains(&field_binding.binding))
+            .filter(|field_binding| needs_moved.contains(&field_binding.binding))
             .each(|field_binding| self.generate_field_attrs_code(field_binding));
 
         (attrs, args)
     }
 
+    /// Returns `true` if `field` should generate a `set_arg` call rather than any other diagnostic
+    /// call (like `span_label`).
+    fn should_generate_set_arg(&self, field: &Field) -> bool {
+        field.attrs.is_empty()
+    }
+
+    /// Returns `true` if `field` needs to have code generated in the by-move branch of the
+    /// generated derive rather than the by-ref branch.
+    fn needs_move(&self, field: &Field) -> bool {
+        let generates_set_arg = self.should_generate_set_arg(field);
+        let is_multispan = type_matches_path(&field.ty, &["rustc_errors", "MultiSpan"]);
+        // FIXME(davidtwco): better support for one field needing to be in the by-move and
+        // by-ref branches.
+        let is_subdiagnostic = field
+            .attrs
+            .iter()
+            .map(|attr| attr.path.segments.last().unwrap().ident.to_string())
+            .any(|attr| attr == "subdiagnostic");
+
+        // `set_arg` calls take their argument by-move..
+        generates_set_arg
+            // If this is a `MultiSpan` field then it needs to be moved to be used by any
+            // attribute..
+            || is_multispan
+            // If this a `#[subdiagnostic]` then it needs to be moved as the other diagnostic is
+            // unlikely to be `Copy`..
+            || is_subdiagnostic
+    }
+
     /// Establishes state in the `DiagnosticDeriveBuilder` resulting from the struct
     /// attributes like `#[error(..)`, such as the diagnostic kind and slug. Generates
     /// diagnostic builder calls for setting error code and creating note/help messages.
@@ -131,7 +156,7 @@ impl DiagnosticDeriveBuilder {
         let name = name.as_str();
         let meta = attr.parse_meta()?;
 
-        let is_help_or_note = matches!(name, "help" | "note");
+        let is_help_note_or_warn = matches!(name, "help" | "note" | "warn_");
 
         let nested = match meta {
             // Most attributes are lists, like `#[error(..)]`/`#[warning(..)]` for most cases or
@@ -139,8 +164,12 @@ impl DiagnosticDeriveBuilder {
             Meta::List(MetaList { ref nested, .. }) => nested,
             // Subdiagnostics without spans can be applied to the type too, and these are just
             // paths: `#[help]` and `#[note]`
-            Meta::Path(_) if is_help_or_note => {
-                let fn_name = proc_macro2::Ident::new(name, attr.span());
+            Meta::Path(_) if is_help_note_or_warn => {
+                let fn_name = if name == "warn_" {
+                    Ident::new("warn", attr.span())
+                } else {
+                    Ident::new(name, attr.span())
+                };
                 return Ok(quote! { #diag.#fn_name(rustc_errors::fluent::_subdiag::#fn_name); });
             }
             _ => throw_invalid_attr!(attr, &meta),
@@ -152,9 +181,11 @@ impl DiagnosticDeriveBuilder {
             "error" => self.kind.set_once((DiagnosticDeriveKind::Error, span)),
             "warning" => self.kind.set_once((DiagnosticDeriveKind::Warn, span)),
             "lint" => self.kind.set_once((DiagnosticDeriveKind::Lint, span)),
-            "help" | "note" => (),
+            "help" | "note" | "warn_" => (),
             _ => throw_invalid_attr!(attr, &meta, |diag| {
-                diag.help("only `error`, `warning`, `help` and `note` are valid attributes")
+                diag.help(
+                    "only `error`, `warning`, `help`, `note` and `warn_` are valid attributes",
+                )
             }),
         }
 
@@ -163,14 +194,16 @@ impl DiagnosticDeriveBuilder {
         let mut nested_iter = nested.into_iter();
         if let Some(nested_attr) = nested_iter.next() {
             // Report an error if there are any other list items after the path.
-            if is_help_or_note && nested_iter.next().is_some() {
+            if is_help_note_or_warn && nested_iter.next().is_some() {
                 throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
-                    diag.help("`help` and `note` struct attributes can only have one argument")
+                    diag.help(
+                        "`help`, `note` and `warn_` struct attributes can only have one argument",
+                    )
                 });
             }
 
             match nested_attr {
-                NestedMeta::Meta(Meta::Path(path)) if is_help_or_note => {
+                NestedMeta::Meta(Meta::Path(path)) if is_help_note_or_warn => {
                     let fn_name = proc_macro2::Ident::new(name, attr.span());
                     return Ok(quote! { #diag.#fn_name(rustc_errors::fluent::#path); });
                 }
@@ -178,8 +211,8 @@ impl DiagnosticDeriveBuilder {
                     self.slug.set_once((path.clone(), span));
                 }
                 NestedMeta::Meta(meta @ Meta::NameValue(_))
-                    if !is_help_or_note
-                        && meta.path().segments.last().unwrap().ident.to_string() == "code" =>
+                    if !is_help_note_or_warn
+                        && meta.path().segments.last().unwrap().ident == "code" =>
                 {
                     // don't error for valid follow-up attributes
                 }
@@ -227,57 +260,55 @@ impl DiagnosticDeriveBuilder {
         let field = binding_info.ast();
         let field_binding = &binding_info.binding;
 
-        let inner_ty = FieldInnerTy::from_type(&field.ty);
-
-        // When generating `set_arg` or `add_subdiagnostic` calls, move data rather than
-        // borrow it to avoid requiring clones - this must therefore be the last use of
-        // each field (for example, any formatting machinery that might refer to a field
-        // should be generated already).
-        if field.attrs.is_empty() {
+        if self.should_generate_set_arg(&field) {
             let diag = &self.diag;
             let ident = field.ident.as_ref().unwrap();
-            quote! {
+            return quote! {
                 #diag.set_arg(
                     stringify!(#ident),
                     #field_binding
                 );
-            }
-        } else {
-            field
-                .attrs
-                .iter()
-                .map(move |attr| {
-                    let name = attr.path.segments.last().unwrap().ident.to_string();
-                    let (binding, needs_destructure) = match (name.as_str(), &inner_ty) {
-                        // `primary_span` can accept a `Vec<Span>` so don't destructure that.
-                        ("primary_span", FieldInnerTy::Vec(_)) => {
-                            (quote! { #field_binding.clone() }, false)
-                        }
-                        // `subdiagnostics` are not derefed because they are bound by value.
-                        ("subdiagnostic", _) => (quote! { #field_binding }, true),
-                        _ => (quote! { *#field_binding }, true),
-                    };
-
-                    let generated_code = self
-                        .generate_inner_field_code(
-                            attr,
-                            FieldInfo {
-                                binding: binding_info,
-                                ty: inner_ty.inner_type().unwrap_or(&field.ty),
-                                span: &field.span(),
-                            },
-                            binding,
-                        )
-                        .unwrap_or_else(|v| v.to_compile_error());
-
-                    if needs_destructure {
-                        inner_ty.with(field_binding, generated_code)
-                    } else {
-                        generated_code
-                    }
-                })
-                .collect()
+            };
         }
+
+        let needs_move = self.needs_move(&field);
+        let inner_ty = FieldInnerTy::from_type(&field.ty);
+
+        field
+            .attrs
+            .iter()
+            .map(move |attr| {
+                let name = attr.path.segments.last().unwrap().ident.to_string();
+                let needs_clone =
+                    name == "primary_span" && matches!(inner_ty, FieldInnerTy::Vec(_));
+                let (binding, needs_destructure) = if needs_clone {
+                    // `primary_span` can accept a `Vec<Span>` so don't destructure that.
+                    (quote! { #field_binding.clone() }, false)
+                } else if needs_move {
+                    (quote! { #field_binding }, true)
+                } else {
+                    (quote! { *#field_binding }, true)
+                };
+
+                let generated_code = self
+                    .generate_inner_field_code(
+                        attr,
+                        FieldInfo {
+                            binding: binding_info,
+                            ty: inner_ty.inner_type().unwrap_or(&field.ty),
+                            span: &field.span(),
+                        },
+                        binding,
+                    )
+                    .unwrap_or_else(|v| v.to_compile_error());
+
+                if needs_destructure {
+                    inner_ty.with(field_binding, generated_code)
+                } else {
+                    generated_code
+                }
+            })
+            .collect()
     }
 
     fn generate_inner_field_code(
@@ -324,10 +355,12 @@ impl DiagnosticDeriveBuilder {
                 report_error_if_not_applied_to_span(attr, &info)?;
                 Ok(self.add_spanned_subdiagnostic(binding, ident, parse_quote! { _subdiag::label }))
             }
-            "note" | "help" => {
-                let path = match name {
-                    "note" => parse_quote! { _subdiag::note },
-                    "help" => parse_quote! { _subdiag::help },
+            "note" | "help" | "warn_" => {
+                let warn_ident = Ident::new("warn", Span::call_site());
+                let (ident, path) = match name {
+                    "note" => (ident, parse_quote! { _subdiag::note }),
+                    "help" => (ident, parse_quote! { _subdiag::help }),
+                    "warn_" => (&warn_ident, parse_quote! { _subdiag::warn }),
                     _ => unreachable!(),
                 };
                 if type_matches_path(&info.ty, &["rustc_span", "Span"]) {
@@ -364,10 +397,10 @@ impl DiagnosticDeriveBuilder {
             "suggestion" | "suggestion_short" | "suggestion_hidden" | "suggestion_verbose" => {
                 return self.generate_inner_field_code_suggestion(attr, info);
             }
-            "label" | "help" | "note" => (),
+            "label" | "help" | "note" | "warn_" => (),
             _ => throw_invalid_attr!(attr, &meta, |diag| {
                 diag.help(
-                    "only `label`, `note`, `help` or `suggestion{,_short,_hidden,_verbose}` are \
+                    "only `label`, `help`, `note`, `warn` or `suggestion{,_short,_hidden,_verbose}` are \
                      valid field attributes",
                 )
             }),
@@ -396,7 +429,14 @@ impl DiagnosticDeriveBuilder {
                 Ok(self.add_spanned_subdiagnostic(binding, ident, msg))
             }
             "note" | "help" if type_is_unit(&info.ty) => Ok(self.add_subdiagnostic(ident, msg)),
-            "note" | "help" => report_type_error(attr, "`Span` or `()`")?,
+            // `warn_` must be special-cased because the attribute `warn` already has meaning and
+            // so isn't used, despite the diagnostic API being named `warn`.
+            "warn_" if type_matches_path(&info.ty, &["rustc_span", "Span"]) => Ok(self
+                .add_spanned_subdiagnostic(binding, &Ident::new("warn", Span::call_site()), msg)),
+            "warn_" if type_is_unit(&info.ty) => {
+                Ok(self.add_subdiagnostic(&Ident::new("warn", Span::call_site()), msg))
+            }
+            "note" | "help" | "warn_" => report_type_error(attr, "`Span` or `()`")?,
             _ => unreachable!(),
         }
     }
diff --git a/compiler/rustc_macros/src/diagnostics/fluent.rs b/compiler/rustc_macros/src/diagnostics/fluent.rs
index 2758fcd1310..562d5e9f4d2 100644
--- a/compiler/rustc_macros/src/diagnostics/fluent.rs
+++ b/compiler/rustc_macros/src/diagnostics/fluent.rs
@@ -194,8 +194,8 @@ pub(crate) fn fluent_messages(input: proc_macro::TokenStream) -> proc_macro::Tok
                 let snake_name = Ident::new(
                     // FIXME: should probably trim prefix, not replace all occurrences
                     &name
-                        .replace(&format!("{}-", res.ident).replace("_", "-"), "")
-                        .replace("-", "_"),
+                        .replace(&format!("{}-", res.ident).replace('_', "-"), "")
+                        .replace('-', "_"),
                     span,
                 );
                 constants.extend(quote! {
@@ -207,7 +207,7 @@ pub(crate) fn fluent_messages(input: proc_macro::TokenStream) -> proc_macro::Tok
                 });
 
                 for Attribute { id: Identifier { name: attr_name }, .. } in attributes {
-                    let snake_name = Ident::new(&attr_name.replace("-", "_"), span);
+                    let snake_name = Ident::new(&attr_name.replace('-', "_"), span);
                     if !previous_attrs.insert(snake_name.clone()) {
                         continue;
                     }
@@ -260,10 +260,12 @@ pub(crate) fn fluent_messages(input: proc_macro::TokenStream) -> proc_macro::Tok
             #generated
 
             pub mod _subdiag {
-                pub const note: crate::SubdiagnosticMessage =
-                    crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("note"));
                 pub const help: crate::SubdiagnosticMessage =
                     crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("help"));
+                pub const note: crate::SubdiagnosticMessage =
+                    crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("note"));
+                pub const warn: crate::SubdiagnosticMessage =
+                    crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("warn"));
                 pub const label: crate::SubdiagnosticMessage =
                     crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("label"));
                 pub const suggestion: crate::SubdiagnosticMessage =
diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs
index 2a5b6beba94..edf4dbed985 100644
--- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs
+++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs
@@ -37,6 +37,8 @@ enum SubdiagnosticKind {
     Note,
     /// `#[help(...)]`
     Help,
+    /// `#[warn_(...)]`
+    Warn,
     /// `#[suggestion{,_short,_hidden,_verbose}]`
     Suggestion(SubdiagnosticSuggestionKind),
 }
@@ -49,6 +51,7 @@ impl FromStr for SubdiagnosticKind {
             "label" => Ok(SubdiagnosticKind::Label),
             "note" => Ok(SubdiagnosticKind::Note),
             "help" => Ok(SubdiagnosticKind::Help),
+            "warn_" => Ok(SubdiagnosticKind::Warn),
             "suggestion" => Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Normal)),
             "suggestion_short" => {
                 Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Short))
@@ -70,6 +73,7 @@ impl quote::IdentFragment for SubdiagnosticKind {
             SubdiagnosticKind::Label => write!(f, "label"),
             SubdiagnosticKind::Note => write!(f, "note"),
             SubdiagnosticKind::Help => write!(f, "help"),
+            SubdiagnosticKind::Warn => write!(f, "warn"),
             SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Normal) => {
                 write!(f, "suggestion")
             }
diff --git a/compiler/rustc_macros/src/diagnostics/utils.rs b/compiler/rustc_macros/src/diagnostics/utils.rs
index 8977db4606c..002abb152f7 100644
--- a/compiler/rustc_macros/src/diagnostics/utils.rs
+++ b/compiler/rustc_macros/src/diagnostics/utils.rs
@@ -85,7 +85,13 @@ pub(crate) fn report_error_if_not_applied_to_span(
     attr: &Attribute,
     info: &FieldInfo<'_>,
 ) -> Result<(), DiagnosticDeriveError> {
-    report_error_if_not_applied_to_ty(attr, info, &["rustc_span", "Span"], "`Span`")
+    if !type_matches_path(&info.ty, &["rustc_span", "Span"])
+        && !type_matches_path(&info.ty, &["rustc_errors", "MultiSpan"])
+    {
+        report_type_error(attr, "`Span` or `MultiSpan`")?;
+    }
+
+    Ok(())
 }
 
 /// Inner type of a field and type of wrapper.
diff --git a/compiler/rustc_macros/src/lib.rs b/compiler/rustc_macros/src/lib.rs
index 168530c54b9..ab509b26f1c 100644
--- a/compiler/rustc_macros/src/lib.rs
+++ b/compiler/rustc_macros/src/lib.rs
@@ -130,8 +130,9 @@ decl_derive!(
         warning,
         error,
         lint,
-        note,
         help,
+        note,
+        warn_,
         // field attributes
         skip_arg,
         primary_span,
@@ -148,8 +149,9 @@ decl_derive!(
         warning,
         error,
         lint,
-        note,
         help,
+        note,
+        warn_,
         // field attributes
         skip_arg,
         primary_span,
@@ -166,6 +168,7 @@ decl_derive!(
         label,
         help,
         note,
+        warn_,
         suggestion,
         suggestion_short,
         suggestion_hidden,
diff --git a/compiler/rustc_metadata/Cargo.toml b/compiler/rustc_metadata/Cargo.toml
index 3ab4a8b7294..2c5db9d8b27 100644
--- a/compiler/rustc_metadata/Cargo.toml
+++ b/compiler/rustc_metadata/Cargo.toml
@@ -12,6 +12,7 @@ odht = { version = "0.3.1", features = ["nightly"] }
 snap = "1"
 tracing = "0.1"
 smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
+tempfile = "3.2"
 rustc_middle = { path = "../rustc_middle" }
 rustc_attr = { path = "../rustc_attr" }
 rustc_data_structures = { path = "../rustc_data_structures" }
diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs
index cb50c0fb738..708d0b1fd8a 100644
--- a/compiler/rustc_metadata/src/creader.rs
+++ b/compiler/rustc_metadata/src/creader.rs
@@ -456,7 +456,7 @@ impl<'a> CrateLoader<'a> {
         proc_macro_locator.is_proc_macro = true;
 
         // Load the proc macro crate for the target
-        let (locator, target_result) = if self.sess.opts.debugging_opts.dual_proc_macros {
+        let (locator, target_result) = if self.sess.opts.unstable_opts.dual_proc_macros {
             proc_macro_locator.reset();
             let result = match self.load(&mut proc_macro_locator)? {
                 Some(LoadResult::Previous(cnum)) => {
@@ -485,7 +485,7 @@ impl<'a> CrateLoader<'a> {
             return Ok(None);
         };
 
-        Ok(Some(if self.sess.opts.debugging_opts.dual_proc_macros {
+        Ok(Some(if self.sess.opts.unstable_opts.dual_proc_macros {
             let host_result = match host_result {
                 LoadResult::Previous(..) => {
                     panic!("host and target proc macros must be loaded in lock-step")
@@ -762,9 +762,9 @@ impl<'a> CrateLoader<'a> {
     }
 
     fn inject_profiler_runtime(&mut self, krate: &ast::Crate) {
-        if self.sess.opts.debugging_opts.no_profiler_runtime
+        if self.sess.opts.unstable_opts.no_profiler_runtime
             || !(self.sess.instrument_coverage()
-                || self.sess.opts.debugging_opts.profile
+                || self.sess.opts.unstable_opts.profile
                 || self.sess.opts.cg.profile_generate.enabled())
         {
             return;
@@ -772,7 +772,7 @@ impl<'a> CrateLoader<'a> {
 
         info!("loading profiler");
 
-        let name = Symbol::intern(&self.sess.opts.debugging_opts.profiler_runtime);
+        let name = Symbol::intern(&self.sess.opts.unstable_opts.profiler_runtime);
         if name == sym::profiler_builtins && self.sess.contains_name(&krate.attrs, sym::no_core) {
             self.sess.err(
                 "`profiler_builtins` crate (required by compiler options) \
diff --git a/compiler/rustc_metadata/src/dependency_format.rs b/compiler/rustc_metadata/src/dependency_format.rs
index 770d164894a..b765c34f8e3 100644
--- a/compiler/rustc_metadata/src/dependency_format.rs
+++ b/compiler/rustc_metadata/src/dependency_format.rs
@@ -419,7 +419,7 @@ fn verify_ok(tcx: TyCtxt<'_>, list: &[Linkage]) {
             }
 
             let found_drop_strategy = tcx.panic_in_drop_strategy(cnum);
-            if tcx.sess.opts.debugging_opts.panic_in_drop != found_drop_strategy {
+            if tcx.sess.opts.unstable_opts.panic_in_drop != found_drop_strategy {
                 sess.err(&format!(
                     "the crate `{}` is compiled with the \
                                panic-in-drop strategy `{}` which is \
@@ -427,7 +427,7 @@ fn verify_ok(tcx: TyCtxt<'_>, list: &[Linkage]) {
                                strategy of `{}`",
                     tcx.crate_name(cnum),
                     found_drop_strategy.desc(),
-                    tcx.sess.opts.debugging_opts.panic_in_drop.desc()
+                    tcx.sess.opts.unstable_opts.panic_in_drop.desc()
                 ));
             }
         }
diff --git a/compiler/rustc_metadata/src/fs.rs b/compiler/rustc_metadata/src/fs.rs
new file mode 100644
index 00000000000..e6072901aaa
--- /dev/null
+++ b/compiler/rustc_metadata/src/fs.rs
@@ -0,0 +1,137 @@
+use crate::{encode_metadata, EncodedMetadata};
+
+use rustc_data_structures::temp_dir::MaybeTempDir;
+use rustc_hir::def_id::LOCAL_CRATE;
+use rustc_middle::ty::TyCtxt;
+use rustc_session::config::{CrateType, OutputFilenames, OutputType};
+use rustc_session::output::filename_for_metadata;
+use rustc_session::Session;
+use tempfile::Builder as TempFileBuilder;
+
+use std::fs;
+use std::path::{Path, PathBuf};
+
+// FIXME(eddyb) maybe include the crate name in this?
+pub const METADATA_FILENAME: &str = "lib.rmeta";
+
+/// We use a temp directory here to avoid races between concurrent rustc processes,
+/// such as builds in the same directory using the same filename for metadata while
+/// building an `.rlib` (stomping over one another), or writing an `.rmeta` into a
+/// directory being searched for `extern crate` (observing an incomplete file).
+/// The returned path is the temporary file containing the complete metadata.
+pub fn emit_metadata(sess: &Session, metadata: &[u8], tmpdir: &MaybeTempDir) -> PathBuf {
+    let out_filename = tmpdir.as_ref().join(METADATA_FILENAME);
+    let result = fs::write(&out_filename, metadata);
+
+    if let Err(e) = result {
+        sess.fatal(&format!("failed to write {}: {}", out_filename.display(), e));
+    }
+
+    out_filename
+}
+
+pub fn encode_and_write_metadata(
+    tcx: TyCtxt<'_>,
+    outputs: &OutputFilenames,
+) -> (EncodedMetadata, bool) {
+    #[derive(PartialEq, Eq, PartialOrd, Ord)]
+    enum MetadataKind {
+        None,
+        Uncompressed,
+        Compressed,
+    }
+
+    let metadata_kind = tcx
+        .sess
+        .crate_types()
+        .iter()
+        .map(|ty| match *ty {
+            CrateType::Executable | CrateType::Staticlib | CrateType::Cdylib => MetadataKind::None,
+
+            CrateType::Rlib => MetadataKind::Uncompressed,
+
+            CrateType::Dylib | CrateType::ProcMacro => MetadataKind::Compressed,
+        })
+        .max()
+        .unwrap_or(MetadataKind::None);
+
+    let crate_name = tcx.crate_name(LOCAL_CRATE);
+    let out_filename = filename_for_metadata(tcx.sess, crate_name.as_str(), outputs);
+    // To avoid races with another rustc process scanning the output directory,
+    // we need to write the file somewhere else and atomically move it to its
+    // final destination, with an `fs::rename` call. In order for the rename to
+    // always succeed, the temporary file needs to be on the same filesystem,
+    // which is why we create it inside the output directory specifically.
+    let metadata_tmpdir = TempFileBuilder::new()
+        .prefix("rmeta")
+        .tempdir_in(out_filename.parent().unwrap_or_else(|| Path::new("")))
+        .unwrap_or_else(|err| tcx.sess.fatal(&format!("couldn't create a temp dir: {}", err)));
+    let metadata_tmpdir = MaybeTempDir::new(metadata_tmpdir, tcx.sess.opts.cg.save_temps);
+    let metadata_filename = metadata_tmpdir.as_ref().join(METADATA_FILENAME);
+
+    // Always create a file at `metadata_filename`, even if we have nothing to write to it.
+    // This simplifies the creation of the output `out_filename` when requested.
+    match metadata_kind {
+        MetadataKind::None => {
+            std::fs::File::create(&metadata_filename).unwrap_or_else(|e| {
+                tcx.sess.fatal(&format!(
+                    "failed to create the file {}: {}",
+                    metadata_filename.display(),
+                    e
+                ))
+            });
+        }
+        MetadataKind::Uncompressed | MetadataKind::Compressed => {
+            encode_metadata(tcx, &metadata_filename);
+        }
+    };
+
+    let _prof_timer = tcx.sess.prof.generic_activity("write_crate_metadata");
+
+    // If the user requests metadata as output, rename `metadata_filename`
+    // to the expected output `out_filename`.  The match above should ensure
+    // this file always exists.
+    let need_metadata_file = tcx.sess.opts.output_types.contains_key(&OutputType::Metadata);
+    let (metadata_filename, metadata_tmpdir) = if need_metadata_file {
+        if let Err(e) = non_durable_rename(&metadata_filename, &out_filename) {
+            tcx.sess.fatal(&format!("failed to write {}: {}", out_filename.display(), e));
+        }
+        if tcx.sess.opts.json_artifact_notifications {
+            tcx.sess
+                .parse_sess
+                .span_diagnostic
+                .emit_artifact_notification(&out_filename, "metadata");
+        }
+        (out_filename, None)
+    } else {
+        (metadata_filename, Some(metadata_tmpdir))
+    };
+
+    // Load metadata back to memory: codegen may need to include it in object files.
+    let metadata =
+        EncodedMetadata::from_path(metadata_filename, metadata_tmpdir).unwrap_or_else(|e| {
+            tcx.sess.fatal(&format!("failed to create encoded metadata from file: {}", e))
+        });
+
+    let need_metadata_module = metadata_kind == MetadataKind::Compressed;
+
+    (metadata, need_metadata_module)
+}
+
+#[cfg(not(target_os = "linux"))]
+pub fn non_durable_rename(src: &Path, dst: &Path) -> std::io::Result<()> {
+    std::fs::rename(src, dst)
+}
+
+/// This function attempts to bypass the auto_da_alloc heuristic implemented by some filesystems
+/// such as btrfs and ext4. When renaming over a file that already exists then they will "helpfully"
+/// write back the source file before committing the rename in case a developer forgot some of
+/// the fsyncs in the open/write/fsync(file)/rename/fsync(dir) dance for atomic file updates.
+///
+/// To avoid triggering this heuristic we delete the destination first, if it exists.
+/// The cost of an extra syscall is much lower than getting descheduled for the sync IO.
+#[cfg(target_os = "linux")]
+pub fn non_durable_rename(src: &Path, dst: &Path) -> std::io::Result<()> {
+    let _ = std::fs::remove_file(dst);
+    std::fs::rename(src, dst)
+}
diff --git a/compiler/rustc_metadata/src/lib.rs b/compiler/rustc_metadata/src/lib.rs
index 5ad16398695..e192378bcc3 100644
--- a/compiler/rustc_metadata/src/lib.rs
+++ b/compiler/rustc_metadata/src/lib.rs
@@ -4,7 +4,7 @@
 #![feature(generators)]
 #![feature(generic_associated_types)]
 #![feature(iter_from_generator)]
-#![feature(let_chains)]
+#![cfg_attr(bootstrap, feature(let_chains))]
 #![feature(let_else)]
 #![feature(once_cell)]
 #![feature(proc_macro_internals)]
@@ -34,6 +34,8 @@ mod native_libs;
 mod rmeta;
 
 pub mod creader;
+pub mod fs;
 pub mod locator;
 
+pub use fs::{emit_metadata, METADATA_FILENAME};
 pub use rmeta::{encode_metadata, EncodedMetadata, METADATA_HEADER};
diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs
index dbe53224e2a..2c1c84b0be2 100644
--- a/compiler/rustc_metadata/src/locator.rs
+++ b/compiler/rustc_metadata/src/locator.rs
@@ -67,7 +67,7 @@
 //!
 //! ## What criterion to select on?
 //!
-//! This a pretty tricky area of loading crates. Given a file, how do we know
+//! This is a pretty tricky area of loading crates. Given a file, how do we know
 //! whether it's the right crate? Currently, the rules look along these lines:
 //!
 //! 1. Does the filename match an rlib/dylib pattern? That is to say, does the
@@ -1179,7 +1179,7 @@ impl CrateError {
                             err.help("consider building the standard library from source with `cargo build -Zbuild-std`");
                         }
                     } else if crate_name
-                        == Symbol::intern(&sess.opts.debugging_opts.profiler_runtime)
+                        == Symbol::intern(&sess.opts.unstable_opts.profiler_runtime)
                     {
                         err.note("the compiler may have been built without the profiler runtime");
                     } else if crate_name.as_str().starts_with("rustc_") {
diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs
index f0ccf02c9fa..8fa703a7760 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder.rs
@@ -21,7 +21,6 @@ use rustc_index::vec::{Idx, IndexVec};
 use rustc_middle::metadata::ModChild;
 use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo};
 use rustc_middle::mir::interpret::{AllocDecodingSession, AllocDecodingState};
-use rustc_middle::thir;
 use rustc_middle::ty::codec::TyDecoder;
 use rustc_middle::ty::fast_reject::SimplifiedType;
 use rustc_middle::ty::GeneratorDiagnosticData;
@@ -42,7 +41,7 @@ use std::io;
 use std::iter::TrustedLen;
 use std::mem;
 use std::num::NonZeroUsize;
-use std::path::PathBuf;
+use std::path::Path;
 use tracing::debug;
 
 pub(super) use cstore_impl::provide;
@@ -638,7 +637,7 @@ impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for Span {
     }
 }
 
-impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for &'tcx [thir::abstract_const::Node<'tcx>] {
+impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for &'tcx [ty::abstract_const::Node<'tcx>] {
     fn decode(d: &mut DecodeContext<'a, 'tcx>) -> Self {
         ty::codec::RefDecodable::decode(d)
     }
@@ -952,6 +951,13 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
         tcx.arena.alloc_from_iter(self.root.lib_features.decode(self))
     }
 
+    /// Iterates over the stability implications in the given crate (when a `#[unstable]` attribute
+    /// has an `implied_by` meta item, then the mapping from the implied feature to the actual
+    /// feature is a stability implication).
+    fn get_stability_implications(self, tcx: TyCtxt<'tcx>) -> &'tcx [(Symbol, Symbol)] {
+        tcx.arena.alloc_from_iter(self.root.stability_implications.decode(self))
+    }
+
     /// Iterates over the language items in the given crate.
     fn get_lang_items(self, tcx: TyCtxt<'tcx>) -> &'tcx [(DefId, usize)] {
         tcx.arena.alloc_from_iter(
@@ -1468,30 +1474,30 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
     /// Proc macro crates don't currently export spans, so this function does not have
     /// to work for them.
     fn imported_source_files(self, sess: &Session) -> &'a [ImportedSourceFile] {
+        fn filter<'a>(sess: &Session, path: Option<&'a Path>) -> Option<&'a Path> {
+            path.filter(|_| {
+                // Only spend time on further checks if we have what to translate *to*.
+                sess.opts.real_rust_source_base_dir.is_some()
+                // Some tests need the translation to be always skipped.
+                && sess.opts.unstable_opts.translate_remapped_path_to_local_path
+            })
+            .filter(|virtual_dir| {
+                // Don't translate away `/rustc/$hash` if we're still remapping to it,
+                // since that means we're still building `std`/`rustc` that need it,
+                // and we don't want the real path to leak into codegen/debuginfo.
+                !sess.opts.remap_path_prefix.iter().any(|(_from, to)| to == virtual_dir)
+            })
+        }
+
         // Translate the virtual `/rustc/$hash` prefix back to a real directory
         // that should hold actual sources, where possible.
         //
         // NOTE: if you update this, you might need to also update bootstrap's code for generating
         // the `rust-src` component in `Src::run` in `src/bootstrap/dist.rs`.
         let virtual_rust_source_base_dir = [
-            option_env!("CFG_VIRTUAL_RUST_SOURCE_BASE_DIR").map(PathBuf::from),
-            sess.opts.debugging_opts.simulate_remapped_rust_src_base.clone(),
-        ]
-        .into_iter()
-        .filter(|_| {
-            // Only spend time on further checks if we have what to translate *to*.
-            sess.opts.real_rust_source_base_dir.is_some()
-                // Some tests need the translation to be always skipped.
-                && sess.opts.debugging_opts.translate_remapped_path_to_local_path
-        })
-        .flatten()
-        .filter(|virtual_dir| {
-            // Don't translate away `/rustc/$hash` if we're still remapping to it,
-            // since that means we're still building `std`/`rustc` that need it,
-            // and we don't want the real path to leak into codegen/debuginfo.
-            !sess.opts.remap_path_prefix.iter().any(|(_from, to)| to == virtual_dir)
-        })
-        .collect::<Vec<_>>();
+            filter(sess, option_env!("CFG_VIRTUAL_RUST_SOURCE_BASE_DIR").map(Path::new)),
+            filter(sess, sess.opts.unstable_opts.simulate_remapped_rust_src_base.as_deref()),
+        ];
 
         let try_to_translate_virtual_to_real = |name: &mut rustc_span::FileName| {
             debug!(
@@ -1500,7 +1506,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
                 name, virtual_rust_source_base_dir, sess.opts.real_rust_source_base_dir,
             );
 
-            for virtual_dir in &virtual_rust_source_base_dir {
+            for virtual_dir in virtual_rust_source_base_dir.iter().flatten() {
                 if let Some(real_dir) = &sess.opts.real_rust_source_base_dir {
                     if let rustc_span::FileName::Real(old_name) = name {
                         if let rustc_span::RealFileName::Remapped { local_path: _, virtual_name } =
@@ -1584,7 +1590,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
                     // `try_to_translate_virtual_to_real` don't have to worry about how the
                     // compiler is bootstrapped.
                     if let Some(virtual_dir) =
-                        &sess.opts.debugging_opts.simulate_remapped_rust_src_base
+                        &sess.opts.unstable_opts.simulate_remapped_rust_src_base
                     {
                         if let Some(real_dir) = &sess.opts.real_rust_source_base_dir {
                             if let rustc_span::FileName::Real(ref mut old_name) = name {
diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
index 565eec18ea9..6bf237b8ed5 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
@@ -291,6 +291,9 @@ provide! { <'tcx> tcx, def_id, other, cdata,
         tcx.arena.alloc_slice(&result)
     }
     defined_lib_features => { cdata.get_lib_features(tcx) }
+    stability_implications => {
+        cdata.get_stability_implications(tcx).iter().copied().collect()
+    }
     is_intrinsic => { cdata.get_is_intrinsic(def_id.index) }
     defined_lang_items => { cdata.get_lang_items(tcx) }
     diagnostic_items => { cdata.get_diagnostic_items() }
@@ -372,9 +375,13 @@ pub(in crate::rmeta) fn provide(providers: &mut Providers) {
             use std::collections::vec_deque::VecDeque;
 
             let mut visible_parent_map: DefIdMap<DefId> = Default::default();
-            // This is a secondary visible_parent_map, storing the DefId of parents that re-export
-            // the child as `_`. Since we prefer parents that don't do this, merge this map at the
-            // end, only if we're missing any keys from the former.
+            // This is a secondary visible_parent_map, storing the DefId of
+            // parents that re-export the child as `_` or module parents
+            // which are `#[doc(hidden)]`. Since we prefer paths that don't
+            // do this, merge this map at the end, only if we're missing
+            // keys from the former.
+            // This is a rudimentary check that does not catch all cases,
+            // just the easiest.
             let mut fallback_map: DefIdMap<DefId> = Default::default();
 
             // Issue 46112: We want the map to prefer the shortest
@@ -409,6 +416,11 @@ pub(in crate::rmeta) fn provide(providers: &mut Providers) {
                         return;
                     }
 
+                    if ty::util::is_doc_hidden(tcx, parent) {
+                        fallback_map.insert(def_id, parent);
+                        return;
+                    }
+
                     match visible_parent_map.entry(def_id) {
                         Entry::Occupied(mut entry) => {
                             // If `child` is defined in crate `cnum`, ensure
@@ -436,8 +448,9 @@ pub(in crate::rmeta) fn provide(providers: &mut Providers) {
                 }
             }
 
-            // Fill in any missing entries with the (less preferable) path ending in `::_`.
-            // We still use this path in a diagnostic that suggests importing `::*`.
+            // Fill in any missing entries with the less preferable path.
+            // If this path re-exports the child as `_`, we still use this
+            // path in a diagnostic that suggests importing `::*`.
             for (child, parent) in fallback_map {
                 visible_parent_map.entry(child).or_insert(parent);
             }
diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs
index caf5965c3a4..50d983754e8 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -4,8 +4,10 @@ use crate::rmeta::*;
 
 use rustc_data_structures::fingerprint::Fingerprint;
 use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
+use rustc_data_structures::memmap::{Mmap, MmapMut};
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 use rustc_data_structures::sync::{join, par_iter, Lrc, ParallelIterator};
+use rustc_data_structures::temp_dir::MaybeTempDir;
 use rustc_hir as hir;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::{
@@ -27,8 +29,7 @@ use rustc_middle::ty::codec::TyEncoder;
 use rustc_middle::ty::fast_reject::{self, SimplifiedType, TreatParams};
 use rustc_middle::ty::query::Providers;
 use rustc_middle::ty::{self, SymbolName, Ty, TyCtxt};
-use rustc_serialize::opaque::MemEncoder;
-use rustc_serialize::{Encodable, Encoder};
+use rustc_serialize::{opaque, Decodable, Decoder, Encodable, Encoder};
 use rustc_session::config::CrateType;
 use rustc_session::cstore::{ForeignModule, LinkagePreference, NativeLib};
 use rustc_span::hygiene::{ExpnIndex, HygieneEncodeContext, MacroKind};
@@ -39,12 +40,14 @@ use rustc_span::{
 use rustc_target::abi::VariantIdx;
 use std::borrow::Borrow;
 use std::hash::Hash;
+use std::io::{Read, Seek, Write};
 use std::iter;
 use std::num::NonZeroUsize;
+use std::path::{Path, PathBuf};
 use tracing::{debug, trace};
 
 pub(super) struct EncodeContext<'a, 'tcx> {
-    opaque: MemEncoder,
+    opaque: opaque::FileEncoder,
     tcx: TyCtxt<'tcx>,
     feat: &'tcx rustc_feature::Features,
 
@@ -535,6 +538,11 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
         let lib_features = self.encode_lib_features();
         let lib_feature_bytes = self.position() - i;
 
+        // Encode the stability implications.
+        i = self.position();
+        let stability_implications = self.encode_stability_implications();
+        let stability_implications_bytes = self.position() - i;
+
         // Encode the language items.
         i = self.position();
         let lang_items = self.encode_lang_items();
@@ -665,7 +673,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
             hash: tcx.crate_hash(LOCAL_CRATE),
             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.debugging_opts.panic_in_drop,
+            panic_in_drop_strategy: tcx.sess.opts.unstable_opts.panic_in_drop,
             edition: tcx.sess.edition(),
             has_global_allocator: tcx.has_global_allocator(LOCAL_CRATE),
             has_panic_handler: tcx.has_panic_handler(LOCAL_CRATE),
@@ -683,6 +691,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
             crate_deps,
             dylib_dependency_formats,
             lib_features,
+            stability_implications,
             lang_items,
             diagnostic_items,
             lang_items_missing,
@@ -707,6 +716,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
         let computed_total_bytes = preamble_bytes
             + dep_bytes
             + lib_feature_bytes
+            + stability_implications_bytes
             + lang_item_bytes
             + diagnostic_item_bytes
             + native_lib_bytes
@@ -729,12 +739,19 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
         assert_eq!(total_bytes, computed_total_bytes);
 
         if tcx.sess.meta_stats() {
+            self.opaque.flush();
+
+            // Rewind and re-read all the metadata to count the zero bytes we wrote.
+            let pos_before_rewind = self.opaque.file().stream_position().unwrap();
             let mut zero_bytes = 0;
-            for e in self.opaque.data.iter() {
-                if *e == 0 {
+            self.opaque.file().rewind().unwrap();
+            let file = std::io::BufReader::new(self.opaque.file());
+            for e in file.bytes() {
+                if e.unwrap() == 0 {
                     zero_bytes += 1;
                 }
             }
+            assert_eq!(self.opaque.file().stream_position().unwrap(), pos_before_rewind);
 
             let perc = |bytes| (bytes * 100) as f64 / total_bytes as f64;
             let p = |label, bytes| {
@@ -751,6 +768,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
             p("preamble", preamble_bytes);
             p("dep", dep_bytes);
             p("lib feature", lib_feature_bytes);
+            p("stability_implications", stability_implications_bytes);
             p("lang item", lang_item_bytes);
             p("diagnostic item", diagnostic_item_bytes);
             p("native lib", native_lib_bytes);
@@ -862,7 +880,7 @@ fn should_encode_mir(tcx: TyCtxt<'_>, def_id: LocalDefId) -> (bool, bool) {
         // Constructors
         DefKind::Ctor(_, _) => {
             let mir_opt_base = tcx.sess.opts.output_types.should_codegen()
-                || tcx.sess.opts.debugging_opts.always_encode_mir;
+                || tcx.sess.opts.unstable_opts.always_encode_mir;
             (true, mir_opt_base)
         }
         // Constants
@@ -880,7 +898,7 @@ fn should_encode_mir(tcx: TyCtxt<'_>, def_id: LocalDefId) -> (bool, bool) {
             // The function has a `const` modifier or is in a `#[const_trait]`.
             let is_const_fn = tcx.is_const_fn_raw(def_id.to_def_id())
                 || tcx.is_const_default_method(def_id.to_def_id());
-            let always_encode_mir = tcx.sess.opts.debugging_opts.always_encode_mir;
+            let always_encode_mir = tcx.sess.opts.unstable_opts.always_encode_mir;
             (is_const_fn, needs_inline || always_encode_mir)
         }
         // Closures can't be const fn.
@@ -889,7 +907,7 @@ fn should_encode_mir(tcx: TyCtxt<'_>, def_id: LocalDefId) -> (bool, bool) {
             let needs_inline = (generics.requires_monomorphization(tcx)
                 || tcx.codegen_fn_attrs(def_id).requests_inline())
                 && tcx.sess.opts.output_types.should_codegen();
-            let always_encode_mir = tcx.sess.opts.debugging_opts.always_encode_mir;
+            let always_encode_mir = tcx.sess.opts.unstable_opts.always_encode_mir;
             (false, needs_inline || always_encode_mir)
         }
         // Generators require optimized MIR to compute layout.
@@ -1360,7 +1378,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
 
         // The query lookup can take a measurable amount of time in crates with many items. Check if
         // the stability attributes are even enabled before using their queries.
-        if self.feat.staged_api || self.tcx.sess.opts.debugging_opts.force_unstable_if_unmarked {
+        if self.feat.staged_api || self.tcx.sess.opts.unstable_opts.force_unstable_if_unmarked {
             if let Some(stab) = self.tcx.lookup_stability(def_id) {
                 record!(self.tables.lookup_stability[def_id] <- stab)
             }
@@ -1372,7 +1390,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
 
         // The query lookup can take a measurable amount of time in crates with many items. Check if
         // the stability attributes are even enabled before using their queries.
-        if self.feat.staged_api || self.tcx.sess.opts.debugging_opts.force_unstable_if_unmarked {
+        if self.feat.staged_api || self.tcx.sess.opts.unstable_opts.force_unstable_if_unmarked {
             if let Some(stab) = self.tcx.lookup_const_stability(def_id) {
                 record!(self.tables.lookup_const_stability[def_id] <- stab)
             }
@@ -1767,6 +1785,13 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
         self.lazy_array(lib_features.to_vec())
     }
 
+    fn encode_stability_implications(&mut self) -> LazyArray<(Symbol, Symbol)> {
+        empty_proc_macro!(self);
+        let tcx = self.tcx;
+        let implications = tcx.stability_implications(LOCAL_CRATE);
+        self.lazy_array(implications.iter().map(|(k, v)| (*k, *v)))
+    }
+
     fn encode_diagnostic_items(&mut self) -> LazyArray<(Symbol, DefIndex)> {
         empty_proc_macro!(self);
         let tcx = self.tcx;
@@ -2133,24 +2158,58 @@ fn prefetch_mir(tcx: TyCtxt<'_>) {
 // will allow us to slice the metadata to the precise length that we just
 // generated regardless of trailing bytes that end up in it.
 
-#[derive(Encodable, Decodable)]
 pub struct EncodedMetadata {
-    raw_data: Vec<u8>,
+    // The declaration order matters because `mmap` should be dropped before `_temp_dir`.
+    mmap: Option<Mmap>,
+    // We need to carry MaybeTempDir to avoid deleting the temporary
+    // directory while accessing the Mmap.
+    _temp_dir: Option<MaybeTempDir>,
 }
 
 impl EncodedMetadata {
     #[inline]
-    pub fn new() -> EncodedMetadata {
-        EncodedMetadata { raw_data: Vec::new() }
+    pub fn from_path(path: PathBuf, temp_dir: Option<MaybeTempDir>) -> std::io::Result<Self> {
+        let file = std::fs::File::open(&path)?;
+        let file_metadata = file.metadata()?;
+        if file_metadata.len() == 0 {
+            return Ok(Self { mmap: None, _temp_dir: None });
+        }
+        let mmap = unsafe { Some(Mmap::map(file)?) };
+        Ok(Self { mmap, _temp_dir: temp_dir })
     }
 
     #[inline]
     pub fn raw_data(&self) -> &[u8] {
-        &self.raw_data
+        self.mmap.as_ref().map(|mmap| mmap.as_ref()).unwrap_or_default()
+    }
+}
+
+impl<S: Encoder> Encodable<S> for EncodedMetadata {
+    fn encode(&self, s: &mut S) {
+        let slice = self.raw_data();
+        slice.encode(s)
+    }
+}
+
+impl<D: Decoder> Decodable<D> for EncodedMetadata {
+    fn decode(d: &mut D) -> Self {
+        let len = d.read_usize();
+        let mmap = if len > 0 {
+            let mut mmap = MmapMut::map_anon(len).unwrap();
+            for _ in 0..len {
+                (&mut mmap[..]).write(&[d.read_u8()]).unwrap();
+            }
+            mmap.flush().unwrap();
+            Some(mmap.make_read_only().unwrap())
+        } else {
+            None
+        };
+
+        Self { mmap, _temp_dir: None }
     }
 }
 
-pub fn encode_metadata(tcx: TyCtxt<'_>) -> EncodedMetadata {
+pub fn encode_metadata(tcx: TyCtxt<'_>, path: &Path) {
     let _prof_timer = tcx.prof.verbose_generic_activity("generate_crate_metadata");
 
     // Since encoding metadata is not in a query, and nothing is cached,
@@ -2158,7 +2217,7 @@ pub fn encode_metadata(tcx: TyCtxt<'_>) -> EncodedMetadata {
     tcx.dep_graph.assert_ignored();
 
     join(
-        || encode_metadata_impl(tcx),
+        || encode_metadata_impl(tcx, path),
         || {
             if tcx.sess.threads() == 1 {
                 return;
@@ -2168,12 +2227,12 @@ pub fn encode_metadata(tcx: TyCtxt<'_>) -> EncodedMetadata {
             // It can be removed if it turns out to cause trouble or be detrimental to performance.
             join(|| prefetch_mir(tcx), || tcx.exported_symbols(LOCAL_CRATE));
         },
-    )
-    .0
+    );
 }
 
-fn encode_metadata_impl(tcx: TyCtxt<'_>) -> EncodedMetadata {
-    let mut encoder = MemEncoder::new();
+fn encode_metadata_impl(tcx: TyCtxt<'_>, path: &Path) {
+    let mut encoder = opaque::FileEncoder::new(path)
+        .unwrap_or_else(|err| tcx.sess.fatal(&format!("failed to create file encoder: {}", err)));
     encoder.emit_raw_bytes(METADATA_HEADER);
 
     // Will be filled with the root position after encoding everything.
@@ -2208,20 +2267,29 @@ fn encode_metadata_impl(tcx: TyCtxt<'_>) -> EncodedMetadata {
     // culminating in the `CrateRoot` which points to all of it.
     let root = ecx.encode_crate_root();
 
-    let mut result = ecx.opaque.finish();
+    ecx.opaque.flush();
+
+    let mut file = ecx.opaque.file();
+    // We will return to this position after writing the root position.
+    let pos_before_seek = file.stream_position().unwrap();
 
     // Encode the root position.
     let header = METADATA_HEADER.len();
+    file.seek(std::io::SeekFrom::Start(header as u64))
+        .unwrap_or_else(|err| tcx.sess.fatal(&format!("failed to seek the file: {}", err)));
     let pos = root.position.get();
-    result[header + 0] = (pos >> 24) as u8;
-    result[header + 1] = (pos >> 16) as u8;
-    result[header + 2] = (pos >> 8) as u8;
-    result[header + 3] = (pos >> 0) as u8;
+    file.write_all(&[(pos >> 24) as u8, (pos >> 16) as u8, (pos >> 8) as u8, (pos >> 0) as u8])
+        .unwrap_or_else(|err| tcx.sess.fatal(&format!("failed to write to the file: {}", err)));
 
-    // Record metadata size for self-profiling
-    tcx.prof.artifact_size("crate_metadata", "crate_metadata", result.len() as u64);
+    // Return to the position where we are before writing the root position.
+    file.seek(std::io::SeekFrom::Start(pos_before_seek)).unwrap();
 
-    EncodedMetadata { raw_data: result }
+    // Record metadata size for self-profiling
+    tcx.prof.artifact_size(
+        "crate_metadata",
+        "crate_metadata",
+        file.metadata().unwrap().len() as u64,
+    );
 }
 
 pub fn provide(providers: &mut Providers) {
@@ -2242,5 +2310,5 @@ pub fn provide(providers: &mut Providers) {
         },
 
         ..*providers
-    };
+    }
 }
diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs
index a50eb2a71cf..0f291f92647 100644
--- a/compiler/rustc_metadata/src/rmeta/mod.rs
+++ b/compiler/rustc_metadata/src/rmeta/mod.rs
@@ -17,12 +17,11 @@ use rustc_middle::metadata::ModChild;
 use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs;
 use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo};
 use rustc_middle::mir;
-use rustc_middle::thir;
 use rustc_middle::ty::fast_reject::SimplifiedType;
 use rustc_middle::ty::query::Providers;
 use rustc_middle::ty::{self, ReprOptions, Ty};
 use rustc_middle::ty::{GeneratorDiagnosticData, ParameterizedOverTcx, TyCtxt};
-use rustc_serialize::opaque::MemEncoder;
+use rustc_serialize::opaque::FileEncoder;
 use rustc_session::config::SymbolManglingVersion;
 use rustc_session::cstore::{CrateDepKind, ForeignModule, LinkagePreference, NativeLib};
 use rustc_span::edition::Edition;
@@ -227,6 +226,7 @@ pub(crate) struct CrateRoot {
     crate_deps: LazyArray<CrateDep>,
     dylib_dependency_formats: LazyArray<Option<LinkagePreference>>,
     lib_features: LazyArray<(Symbol, Option<Symbol>)>,
+    stability_implications: LazyArray<(Symbol, Symbol)>,
     lang_items: LazyArray<(DefIndex, usize)>,
     lang_items_missing: LazyArray<lang_items::LangItem>,
     diagnostic_items: LazyArray<(Symbol, DefIndex)>,
@@ -323,7 +323,7 @@ macro_rules! define_tables {
         }
 
         impl TableBuilders {
-            fn encode(&self, buf: &mut MemEncoder) -> LazyTables {
+            fn encode(&self, buf: &mut FileEncoder) -> LazyTables {
                 LazyTables {
                     $($name: self.$name.encode(buf)),+
                 }
@@ -361,7 +361,7 @@ define_tables! {
     mir_for_ctfe: Table<DefIndex, LazyValue<mir::Body<'static>>>,
     promoted_mir: Table<DefIndex, LazyValue<IndexVec<mir::Promoted, mir::Body<'static>>>>,
     // FIXME(compiler-errors): Why isn't this a LazyArray?
-    thir_abstract_const: Table<DefIndex, LazyValue<&'static [thir::abstract_const::Node<'static>]>>,
+    thir_abstract_const: Table<DefIndex, LazyValue<&'static [ty::abstract_const::Node<'static>]>>,
     impl_parent: Table<DefIndex, RawDefId>,
     impl_polarity: Table<DefIndex, ty::ImplPolarity>,
     constness: Table<DefIndex, hir::Constness>,
diff --git a/compiler/rustc_metadata/src/rmeta/table.rs b/compiler/rustc_metadata/src/rmeta/table.rs
index 5ab4269ae99..42759f0a652 100644
--- a/compiler/rustc_metadata/src/rmeta/table.rs
+++ b/compiler/rustc_metadata/src/rmeta/table.rs
@@ -4,8 +4,8 @@ use rustc_data_structures::fingerprint::Fingerprint;
 use rustc_hir::def::{CtorKind, CtorOf};
 use rustc_index::vec::Idx;
 use rustc_middle::ty::ParameterizedOverTcx;
-use rustc_serialize::opaque::MemEncoder;
-use rustc_serialize::Encoder;
+use rustc_serialize::opaque::FileEncoder;
+use rustc_serialize::Encoder as _;
 use rustc_span::hygiene::MacroKind;
 use std::convert::TryInto;
 use std::marker::PhantomData;
@@ -281,7 +281,7 @@ where
         Some(value).write_to_bytes(&mut self.blocks[i]);
     }
 
-    pub(crate) fn encode<const N: usize>(&self, buf: &mut MemEncoder) -> LazyTable<I, T>
+    pub(crate) fn encode<const N: usize>(&self, buf: &mut FileEncoder) -> LazyTable<I, T>
     where
         Option<T>: FixedSizeEncoding<ByteArray = [u8; N]>,
     {
diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs
index 661a9b1944c..b94de537dc8 100644
--- a/compiler/rustc_middle/src/arena.rs
+++ b/compiler/rustc_middle/src/arena.rs
@@ -55,6 +55,7 @@ macro_rules! arena_types {
             [] dtorck_constraint: rustc_middle::traits::query::DropckConstraint<'tcx>,
             [] candidate_step: rustc_middle::traits::query::CandidateStep<'tcx>,
             [] autoderef_bad_ty: rustc_middle::traits::query::MethodAutoderefBadTy<'tcx>,
+            [] query_region_constraints: rustc_middle::infer::canonical::QueryRegionConstraints<'tcx>,
             [] type_op_subtype:
                 rustc_middle::infer::canonical::Canonical<'tcx,
                     rustc_middle::infer::canonical::QueryResponse<'tcx, ()>
diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs
index 3e99ba5742a..0001e1aa53e 100644
--- a/compiler/rustc_middle/src/hir/map/mod.rs
+++ b/compiler/rustc_middle/src/hir/map/mod.rs
@@ -22,7 +22,7 @@ fn fn_decl<'hir>(node: Node<'hir>) -> Option<&'hir FnDecl<'hir>> {
         Node::Item(Item { kind: ItemKind::Fn(sig, _, _), .. })
         | Node::TraitItem(TraitItem { kind: TraitItemKind::Fn(sig, _), .. })
         | Node::ImplItem(ImplItem { kind: ImplItemKind::Fn(sig, _), .. }) => Some(&sig.decl),
-        Node::Expr(Expr { kind: ExprKind::Closure { fn_decl, .. }, .. })
+        Node::Expr(Expr { kind: ExprKind::Closure(Closure { fn_decl, .. }), .. })
         | Node::ForeignItem(ForeignItem { kind: ForeignItemKind::Fn(fn_decl, ..), .. }) => {
             Some(fn_decl)
         }
@@ -39,6 +39,7 @@ pub fn fn_sig<'hir>(node: Node<'hir>) -> Option<&'hir FnSig<'hir>> {
     }
 }
 
+#[inline]
 pub fn associated_body<'hir>(node: Node<'hir>) -> Option<BodyId> {
     match node {
         Node::Item(Item {
@@ -54,7 +55,7 @@ pub fn associated_body<'hir>(node: Node<'hir>) -> Option<BodyId> {
             kind: ImplItemKind::Const(_, body) | ImplItemKind::Fn(_, body),
             ..
         })
-        | Node::Expr(Expr { kind: ExprKind::Closure { body, .. }, .. }) => Some(*body),
+        | Node::Expr(Expr { kind: ExprKind::Closure(Closure { body, .. }), .. }) => Some(*body),
 
         Node::AnonConst(constant) => Some(constant.body),
 
@@ -279,8 +280,8 @@ impl<'hir> Map<'hir> {
             }
             Node::Field(_) => DefKind::Field,
             Node::Expr(expr) => match expr.kind {
-                ExprKind::Closure { movability: None, .. } => DefKind::Closure,
-                ExprKind::Closure { movability: Some(_), .. } => DefKind::Generator,
+                ExprKind::Closure(Closure { movability: None, .. }) => DefKind::Closure,
+                ExprKind::Closure(Closure { movability: Some(_), .. }) => DefKind::Generator,
                 _ => bug!("def_kind: unsupported node: {}", self.node_to_string(hir_id)),
             },
             Node::GenericParam(param) => match param.kind {
@@ -486,35 +487,11 @@ impl<'hir> Map<'hir> {
     /// crate. If you would prefer to iterate over the bodies
     /// themselves, you can do `self.hir().krate().body_ids.iter()`.
     pub fn body_owners(self) -> impl Iterator<Item = LocalDefId> + 'hir {
-        self.krate()
-            .owners
-            .iter_enumerated()
-            .flat_map(move |(owner, owner_info)| {
-                let bodies = &owner_info.as_owner()?.nodes.bodies;
-                Some(bodies.iter().map(move |&(local_id, _)| {
-                    let hir_id = HirId { owner, local_id };
-                    let body_id = BodyId { hir_id };
-                    self.body_owner_def_id(body_id)
-                }))
-            })
-            .flatten()
+        self.tcx.hir_crate_items(()).body_owners.iter().copied()
     }
 
     pub fn par_body_owners<F: Fn(LocalDefId) + Sync + Send>(self, f: F) {
-        use rustc_data_structures::sync::{par_iter, ParallelIterator};
-        #[cfg(parallel_compiler)]
-        use rustc_rayon::iter::IndexedParallelIterator;
-
-        par_iter(&self.krate().owners.raw).enumerate().for_each(|(owner, owner_info)| {
-            let owner = LocalDefId::new(owner);
-            if let MaybeOwner::Owner(owner_info) = owner_info {
-                par_iter(owner_info.nodes.bodies.range(..)).for_each(|(local_id, _)| {
-                    let hir_id = HirId { owner, local_id: *local_id };
-                    let body_id = BodyId { hir_id };
-                    f(self.body_owner_def_id(body_id))
-                })
-            }
-        });
+        par_for_each_in(&self.tcx.hir_crate_items(()).body_owners[..], |&def_id| f(def_id));
     }
 
     pub fn ty_param_owner(self, def_id: LocalDefId) -> LocalDefId {
@@ -1021,7 +998,9 @@ impl<'hir> Map<'hir> {
                 _ => named_span(item.span, item.ident, None),
             },
             Node::Ctor(_) => return self.opt_span(self.get_parent_node(hir_id)),
-            Node::Expr(Expr { kind: ExprKind::Closure { fn_decl_span, .. }, .. }) => *fn_decl_span,
+            Node::Expr(Expr { kind: ExprKind::Closure(Closure { fn_decl_span, .. }), .. }) => {
+                *fn_decl_span
+            }
             _ => self.span_with_body(hir_id),
         };
         Some(span)
@@ -1149,7 +1128,7 @@ pub(super) fn crate_hash(tcx: TyCtxt<'_>, crate_num: CrateNum) -> Svh {
         hir_body_hash.hash_stable(&mut hcx, &mut stable_hasher);
         upstream_crates.hash_stable(&mut hcx, &mut stable_hasher);
         source_file_names.hash_stable(&mut hcx, &mut stable_hasher);
-        if tcx.sess.opts.debugging_opts.incremental_relative_spans {
+        if tcx.sess.opts.unstable_opts.incremental_relative_spans {
             let definitions = tcx.definitions_untracked();
             let mut owner_spans: Vec<_> = krate
                 .owners
@@ -1281,85 +1260,48 @@ fn hir_id_to_string(map: Map<'_>, id: HirId) -> String {
 }
 
 pub(super) fn hir_module_items(tcx: TyCtxt<'_>, module_id: LocalDefId) -> ModuleItems {
-    let mut collector = ModuleCollector {
-        tcx,
-        submodules: Vec::default(),
-        items: Vec::default(),
-        trait_items: Vec::default(),
-        impl_items: Vec::default(),
-        foreign_items: Vec::default(),
-    };
+    let mut collector = ItemCollector::new(tcx, false);
 
     let (hir_mod, span, hir_id) = tcx.hir().get_module(module_id);
     collector.visit_mod(hir_mod, span, hir_id);
 
-    let ModuleCollector { submodules, items, trait_items, impl_items, foreign_items, .. } =
-        collector;
+    let ItemCollector {
+        submodules,
+        items,
+        trait_items,
+        impl_items,
+        foreign_items,
+        body_owners,
+        ..
+    } = collector;
     return ModuleItems {
         submodules: submodules.into_boxed_slice(),
         items: items.into_boxed_slice(),
         trait_items: trait_items.into_boxed_slice(),
         impl_items: impl_items.into_boxed_slice(),
         foreign_items: foreign_items.into_boxed_slice(),
+        body_owners: body_owners.into_boxed_slice(),
     };
-
-    struct ModuleCollector<'tcx> {
-        tcx: TyCtxt<'tcx>,
-        submodules: Vec<LocalDefId>,
-        items: Vec<ItemId>,
-        trait_items: Vec<TraitItemId>,
-        impl_items: Vec<ImplItemId>,
-        foreign_items: Vec<ForeignItemId>,
-    }
-
-    impl<'hir> Visitor<'hir> for ModuleCollector<'hir> {
-        type NestedFilter = nested_filter::All;
-
-        fn nested_visit_map(&mut self) -> Self::Map {
-            self.tcx.hir()
-        }
-
-        fn visit_item(&mut self, item: &'hir Item<'hir>) {
-            self.items.push(item.item_id());
-            if let ItemKind::Mod(..) = item.kind {
-                // If this declares another module, do not recurse inside it.
-                self.submodules.push(item.def_id);
-            } else {
-                intravisit::walk_item(self, item)
-            }
-        }
-
-        fn visit_trait_item(&mut self, item: &'hir TraitItem<'hir>) {
-            self.trait_items.push(item.trait_item_id());
-            intravisit::walk_trait_item(self, item)
-        }
-
-        fn visit_impl_item(&mut self, item: &'hir ImplItem<'hir>) {
-            self.impl_items.push(item.impl_item_id());
-            intravisit::walk_impl_item(self, item)
-        }
-
-        fn visit_foreign_item(&mut self, item: &'hir ForeignItem<'hir>) {
-            self.foreign_items.push(item.foreign_item_id());
-            intravisit::walk_foreign_item(self, item)
-        }
-    }
 }
 
 pub(crate) fn hir_crate_items(tcx: TyCtxt<'_>, _: ()) -> ModuleItems {
-    let mut collector = CrateCollector {
-        tcx,
-        submodules: Vec::default(),
-        items: Vec::default(),
-        trait_items: Vec::default(),
-        impl_items: Vec::default(),
-        foreign_items: Vec::default(),
-    };
+    let mut collector = ItemCollector::new(tcx, true);
 
+    // A "crate collector" and "module collector" start at a
+    // module item (the former starts at the crate root) but only
+    // the former needs to collect it. ItemCollector does not do this for us.
+    collector.submodules.push(CRATE_DEF_ID);
     tcx.hir().walk_toplevel_module(&mut collector);
 
-    let CrateCollector { submodules, items, trait_items, impl_items, foreign_items, .. } =
-        collector;
+    let ItemCollector {
+        submodules,
+        items,
+        trait_items,
+        impl_items,
+        foreign_items,
+        body_owners,
+        ..
+    } = collector;
 
     return ModuleItems {
         submodules: submodules.into_boxed_slice(),
@@ -1367,47 +1309,96 @@ pub(crate) fn hir_crate_items(tcx: TyCtxt<'_>, _: ()) -> ModuleItems {
         trait_items: trait_items.into_boxed_slice(),
         impl_items: impl_items.into_boxed_slice(),
         foreign_items: foreign_items.into_boxed_slice(),
+        body_owners: body_owners.into_boxed_slice(),
     };
+}
 
-    struct CrateCollector<'tcx> {
-        tcx: TyCtxt<'tcx>,
-        submodules: Vec<LocalDefId>,
-        items: Vec<ItemId>,
-        trait_items: Vec<TraitItemId>,
-        impl_items: Vec<ImplItemId>,
-        foreign_items: Vec<ForeignItemId>,
+struct ItemCollector<'tcx> {
+    // When true, it collects all items in the create,
+    // otherwise it collects items in some module.
+    crate_collector: bool,
+    tcx: TyCtxt<'tcx>,
+    submodules: Vec<LocalDefId>,
+    items: Vec<ItemId>,
+    trait_items: Vec<TraitItemId>,
+    impl_items: Vec<ImplItemId>,
+    foreign_items: Vec<ForeignItemId>,
+    body_owners: Vec<LocalDefId>,
+}
+
+impl<'tcx> ItemCollector<'tcx> {
+    fn new(tcx: TyCtxt<'tcx>, crate_collector: bool) -> ItemCollector<'tcx> {
+        ItemCollector {
+            crate_collector,
+            tcx,
+            submodules: Vec::default(),
+            items: Vec::default(),
+            trait_items: Vec::default(),
+            impl_items: Vec::default(),
+            foreign_items: Vec::default(),
+            body_owners: Vec::default(),
+        }
     }
+}
+
+impl<'hir> Visitor<'hir> for ItemCollector<'hir> {
+    type NestedFilter = nested_filter::All;
 
-    impl<'hir> Visitor<'hir> for CrateCollector<'hir> {
-        type NestedFilter = nested_filter::All;
+    fn nested_visit_map(&mut self) -> Self::Map {
+        self.tcx.hir()
+    }
 
-        fn nested_visit_map(&mut self) -> Self::Map {
-            self.tcx.hir()
+    fn visit_item(&mut self, item: &'hir Item<'hir>) {
+        if associated_body(Node::Item(item)).is_some() {
+            self.body_owners.push(item.def_id);
         }
 
-        fn visit_item(&mut self, item: &'hir Item<'hir>) {
-            self.items.push(item.item_id());
+        self.items.push(item.item_id());
+
+        // Items that are modules are handled here instead of in visit_mod.
+        if let ItemKind::Mod(module) = &item.kind {
+            self.submodules.push(item.def_id);
+            // A module collector does not recurse inside nested modules.
+            if self.crate_collector {
+                intravisit::walk_mod(self, module, item.hir_id());
+            }
+        } else {
             intravisit::walk_item(self, item)
         }
+    }
 
-        fn visit_mod(&mut self, m: &'hir Mod<'hir>, _s: Span, n: HirId) {
-            self.submodules.push(n.owner);
-            intravisit::walk_mod(self, m, n);
-        }
+    fn visit_foreign_item(&mut self, item: &'hir ForeignItem<'hir>) {
+        self.foreign_items.push(item.foreign_item_id());
+        intravisit::walk_foreign_item(self, item)
+    }
+
+    fn visit_anon_const(&mut self, c: &'hir AnonConst) {
+        self.body_owners.push(self.tcx.hir().local_def_id(c.hir_id));
+        intravisit::walk_anon_const(self, c)
+    }
 
-        fn visit_foreign_item(&mut self, item: &'hir ForeignItem<'hir>) {
-            self.foreign_items.push(item.foreign_item_id());
-            intravisit::walk_foreign_item(self, item)
+    fn visit_expr(&mut self, ex: &'hir Expr<'hir>) {
+        if matches!(ex.kind, ExprKind::Closure { .. }) {
+            self.body_owners.push(self.tcx.hir().local_def_id(ex.hir_id));
         }
+        intravisit::walk_expr(self, ex)
+    }
 
-        fn visit_trait_item(&mut self, item: &'hir TraitItem<'hir>) {
-            self.trait_items.push(item.trait_item_id());
-            intravisit::walk_trait_item(self, item)
+    fn visit_trait_item(&mut self, item: &'hir TraitItem<'hir>) {
+        if associated_body(Node::TraitItem(item)).is_some() {
+            self.body_owners.push(item.def_id);
         }
 
-        fn visit_impl_item(&mut self, item: &'hir ImplItem<'hir>) {
-            self.impl_items.push(item.impl_item_id());
-            intravisit::walk_impl_item(self, item)
+        self.trait_items.push(item.trait_item_id());
+        intravisit::walk_trait_item(self, item)
+    }
+
+    fn visit_impl_item(&mut self, item: &'hir ImplItem<'hir>) {
+        if associated_body(Node::ImplItem(item)).is_some() {
+            self.body_owners.push(item.def_id);
         }
+
+        self.impl_items.push(item.impl_item_id());
+        intravisit::walk_impl_item(self, item)
     }
 }
diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs
index 070a063c881..a605e234be9 100644
--- a/compiler/rustc_middle/src/hir/mod.rs
+++ b/compiler/rustc_middle/src/hir/mod.rs
@@ -44,6 +44,7 @@ pub struct ModuleItems {
     trait_items: Box<[TraitItemId]>,
     impl_items: Box<[ImplItemId]>,
     foreign_items: Box<[ForeignItemId]>,
+    body_owners: Box<[LocalDefId]>,
 }
 
 impl ModuleItems {
diff --git a/compiler/rustc_middle/src/infer/mod.rs b/compiler/rustc_middle/src/infer/mod.rs
index 55e00c4c0d8..38868c21049 100644
--- a/compiler/rustc_middle/src/infer/mod.rs
+++ b/compiler/rustc_middle/src/infer/mod.rs
@@ -2,9 +2,8 @@ pub mod canonical;
 pub mod unify_key;
 
 use crate::ty::Region;
-use crate::ty::Ty;
+use crate::ty::{OpaqueTypeKey, Ty};
 use rustc_data_structures::sync::Lrc;
-use rustc_hir::def_id::DefId;
 use rustc_span::Span;
 
 /// Requires that `region` must be equal to one of the regions in `choice_regions`.
@@ -15,8 +14,9 @@ use rustc_span::Span;
 /// ```
 #[derive(Debug, Clone, HashStable, TypeFoldable, TypeVisitable, Lift)]
 pub struct MemberConstraint<'tcx> {
-    /// The `DefId` of the opaque type causing this constraint: used for error reporting.
-    pub opaque_type_def_id: DefId,
+    /// The `DefId` and substs of the opaque type causing this constraint.
+    /// Used for error reporting.
+    pub key: OpaqueTypeKey<'tcx>,
 
     /// The span where the hidden type was instantiated.
     pub definition_span: Span,
diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs
index ef06c457bf4..45c6468bc24 100644
--- a/compiler/rustc_middle/src/lib.rs
+++ b/compiler/rustc_middle/src/lib.rs
@@ -40,7 +40,7 @@
 #![feature(extern_types)]
 #![feature(new_uninit)]
 #![feature(once_cell)]
-#![feature(let_chains)]
+#![cfg_attr(bootstrap, feature(let_chains))]
 #![feature(let_else)]
 #![feature(min_specialization)]
 #![feature(trusted_len)]
diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs
index 4b156de410d..2f45222de47 100644
--- a/compiler/rustc_middle/src/lint.rs
+++ b/compiler/rustc_middle/src/lint.rs
@@ -300,7 +300,7 @@ pub fn struct_lint_level<'s, 'd>(
 
         let has_future_breakage = future_incompatible.map_or(
             // Default allow lints trigger too often for testing.
-            sess.opts.debugging_opts.future_incompat_test && lint.default_level != Level::Allow,
+            sess.opts.unstable_opts.future_incompat_test && lint.default_level != Level::Allow,
             |incompat| {
                 matches!(incompat.reason, FutureIncompatibilityReason::FutureReleaseErrorReportNow)
             },
diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs
index 321fcd43797..45d33a1659f 100644
--- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs
+++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs
@@ -50,7 +50,7 @@ bitflags! {
         /// the hot path.
         const COLD                      = 1 << 0;
         /// `#[rustc_allocator]`: a hint to LLVM that the pointer returned from this
-        /// function is never null.
+        /// function is never null and the function has no side effects other than allocating.
         const ALLOCATOR                 = 1 << 1;
         /// An indicator that function will never unwind. Will become obsolete
         /// once C-unwind is fully stabilized.
@@ -91,6 +91,12 @@ bitflags! {
         const NO_COVERAGE               = 1 << 15;
         /// `#[used(linker)]`: indicates that LLVM nor the linker can eliminate this function.
         const USED_LINKER               = 1 << 16;
+        /// `#[rustc_deallocator]`: a hint to LLVM that the function only deallocates memory.
+        const DEALLOCATOR               = 1 << 17;
+        /// `#[rustc_reallocator]`: a hint to LLVM that the function only reallocates memory.
+        const REALLOCATOR               = 1 << 18;
+        /// `#[rustc_allocator_zeroed]`: a hint to LLVM that the function only allocates zeroed memory.
+        const ALLOCATOR_ZEROED          = 1 << 19;
     }
 }
 
diff --git a/compiler/rustc_middle/src/middle/limits.rs b/compiler/rustc_middle/src/middle/limits.rs
index 20dcb670cd6..acced0492ef 100644
--- a/compiler/rustc_middle/src/middle/limits.rs
+++ b/compiler/rustc_middle/src/middle/limits.rs
@@ -25,7 +25,7 @@ pub fn provide(providers: &mut ty::query::Providers) {
             tcx.hir().krate_attrs(),
             tcx.sess,
             sym::move_size_limit,
-            tcx.sess.opts.debugging_opts.move_size_limit.unwrap_or(0),
+            tcx.sess.opts.unstable_opts.move_size_limit.unwrap_or(0),
         ),
         type_length_limit: get_limit(
             tcx.hir().krate_attrs(),
diff --git a/compiler/rustc_middle/src/middle/mod.rs b/compiler/rustc_middle/src/middle/mod.rs
index fc35cafcc77..8dc68b1f5a8 100644
--- a/compiler/rustc_middle/src/middle/mod.rs
+++ b/compiler/rustc_middle/src/middle/mod.rs
@@ -3,14 +3,14 @@ pub mod dependency_format;
 pub mod exported_symbols;
 pub mod lang_items;
 pub mod lib_features {
-    use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-    use rustc_span::symbol::Symbol;
+    use rustc_data_structures::fx::FxHashMap;
+    use rustc_span::{symbol::Symbol, Span};
 
     #[derive(HashStable, Debug)]
     pub struct LibFeatures {
-        // A map from feature to stabilisation version.
-        pub stable: FxHashMap<Symbol, Symbol>,
-        pub unstable: FxHashSet<Symbol>,
+        /// A map from feature to stabilisation version.
+        pub stable: FxHashMap<Symbol, (Symbol, Span)>,
+        pub unstable: FxHashMap<Symbol, Span>,
     }
 
     impl LibFeatures {
@@ -18,8 +18,8 @@ pub mod lib_features {
             let mut all_features: Vec<_> = self
                 .stable
                 .iter()
-                .map(|(f, s)| (*f, Some(*s)))
-                .chain(self.unstable.iter().map(|f| (*f, None)))
+                .map(|(f, (s, _))| (*f, Some(*s)))
+                .chain(self.unstable.iter().map(|(f, _)| (*f, None)))
                 .collect();
             all_features.sort_unstable_by(|a, b| a.0.as_str().partial_cmp(b.0.as_str()).unwrap());
             all_features
diff --git a/compiler/rustc_middle/src/middle/resolve_lifetime.rs b/compiler/rustc_middle/src/middle/resolve_lifetime.rs
index c71ba7b1753..9b2f4456705 100644
--- a/compiler/rustc_middle/src/middle/resolve_lifetime.rs
+++ b/compiler/rustc_middle/src/middle/resolve_lifetime.rs
@@ -12,7 +12,6 @@ pub enum Region {
     Static,
     EarlyBound(/* index */ u32, /* lifetime decl */ DefId),
     LateBound(ty::DebruijnIndex, /* late-bound index */ u32, /* lifetime decl */ DefId),
-    LateBoundAnon(ty::DebruijnIndex, /* late-bound index */ u32, /* anon index */ u32),
     Free(DefId, /* lifetime decl */ DefId),
 }
 
diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs
index 802b7852bac..414912dd0f7 100644
--- a/compiler/rustc_middle/src/middle/stability.rs
+++ b/compiler/rustc_middle/src/middle/stability.rs
@@ -62,6 +62,19 @@ pub struct Index {
     pub stab_map: FxHashMap<LocalDefId, Stability>,
     pub const_stab_map: FxHashMap<LocalDefId, ConstStability>,
     pub depr_map: FxHashMap<LocalDefId, DeprecationEntry>,
+    /// Mapping from feature name to feature name based on the `implied_by` field of `#[unstable]`
+    /// attributes. If a `#[unstable(feature = "implier", implied_by = "impliee")]` attribute
+    /// exists, then this map will have a `impliee -> implier` entry.
+    ///
+    /// This mapping is necessary unless both the `#[stable]` and `#[unstable]` attributes should
+    /// specify their implications (both `implies` and `implied_by`). If only one of the two
+    /// attributes do (as in the current implementation, `implied_by` in `#[unstable]`), then this
+    /// mapping is necessary for diagnostics. When a "unnecessary feature attribute" error is
+    /// reported, only the `#[stable]` attribute information is available, so the map is necessary
+    /// to know that the feature implies another feature. If it were reversed, and the `#[stable]`
+    /// attribute had an `implies` meta item, then a map would be necessary when avoiding a "use of
+    /// unstable feature" error for a feature that was implied.
+    pub implications: FxHashMap<Symbol, Symbol>,
 }
 
 impl Index {
@@ -423,7 +436,9 @@ impl<'tcx> TyCtxt<'tcx> {
 
         match stability {
             Some(Stability {
-                level: attr::Unstable { reason, issue, is_soft }, feature, ..
+                level: attr::Unstable { reason, issue, is_soft, implied_by },
+                feature,
+                ..
             }) => {
                 if span.allows_unstable(feature) {
                     debug!("stability: skipping span={:?} since it is internal", span);
@@ -433,6 +448,13 @@ impl<'tcx> TyCtxt<'tcx> {
                     return EvalResult::Allow;
                 }
 
+                // If this item was previously part of a now-stabilized feature which is still
+                // active (i.e. the user hasn't removed the attribute for the stabilized feature
+                // yet) then allow use of this item.
+                if let Some(implied_by) = implied_by && self.features().active(implied_by) {
+                    return EvalResult::Allow;
+                }
+
                 // When we're compiling the compiler itself we may pull in
                 // crates from crates.io, but those crates may depend on other
                 // crates also pulled in from crates.io. We want to ideally be
@@ -443,7 +465,7 @@ impl<'tcx> TyCtxt<'tcx> {
                 // compiling a compiler crate), then let this missing feature
                 // annotation slide.
                 if feature == sym::rustc_private && issue == NonZeroU32::new(27812) {
-                    if self.sess.opts.debugging_opts.force_unstable_if_unmarked {
+                    if self.sess.opts.unstable_opts.force_unstable_if_unmarked {
                         return EvalResult::Allow;
                     }
                 }
@@ -453,7 +475,13 @@ impl<'tcx> TyCtxt<'tcx> {
                 }
 
                 let suggestion = suggestion_for_allocator_api(self, def_id, span, feature);
-                EvalResult::Deny { feature, reason, issue, suggestion, is_soft }
+                EvalResult::Deny {
+                    feature,
+                    reason: reason.to_opt_reason(),
+                    issue,
+                    suggestion,
+                    is_soft,
+                }
             }
             Some(_) => {
                 // Stable APIs are always ok to call and deprecated APIs are
@@ -471,13 +499,15 @@ impl<'tcx> TyCtxt<'tcx> {
     ///
     /// This function will also check if the item is deprecated.
     /// If so, and `id` is not `None`, a deprecated lint attached to `id` will be emitted.
+    ///
+    /// Returns `true` if item is allowed aka, stable or unstable under an enabled feature.
     pub fn check_stability(
         self,
         def_id: DefId,
         id: Option<HirId>,
         span: Span,
         method_span: Option<Span>,
-    ) {
+    ) -> bool {
         self.check_stability_allow_unstable(def_id, id, span, method_span, AllowUnstable::No)
     }
 
@@ -490,6 +520,8 @@ impl<'tcx> TyCtxt<'tcx> {
     /// If so, and `id` is not `None`, a deprecated lint attached to `id` will be emitted.
     ///
     /// Pass `AllowUnstable::Yes` to `allow_unstable` to force an unstable item to be allowed. Deprecation warnings will be emitted normally.
+    ///
+    /// Returns `true` if item is allowed aka, stable or unstable under an enabled feature.
     pub fn check_stability_allow_unstable(
         self,
         def_id: DefId,
@@ -497,7 +529,7 @@ impl<'tcx> TyCtxt<'tcx> {
         span: Span,
         method_span: Option<Span>,
         allow_unstable: AllowUnstable,
-    ) {
+    ) -> bool {
         self.check_optional_stability(
             def_id,
             id,
@@ -516,6 +548,8 @@ impl<'tcx> TyCtxt<'tcx> {
     /// missing stability attributes (not necessarily just emit a `bug!`). This is necessary
     /// for default generic parameters, which only have stability attributes if they were
     /// added after the type on which they're defined.
+    ///
+    /// Returns `true` if item is allowed aka, stable or unstable under an enabled feature.
     pub fn check_optional_stability(
         self,
         def_id: DefId,
@@ -524,13 +558,16 @@ impl<'tcx> TyCtxt<'tcx> {
         method_span: Option<Span>,
         allow_unstable: AllowUnstable,
         unmarked: impl FnOnce(Span, DefId),
-    ) {
+    ) -> bool {
         let soft_handler = |lint, span, msg: &_| {
             self.struct_span_lint_hir(lint, id.unwrap_or(hir::CRATE_HIR_ID), span, |lint| {
                 lint.build(msg).emit();
             })
         };
-        match self.eval_stability_allow_unstable(def_id, id, span, method_span, allow_unstable) {
+        let eval_result =
+            self.eval_stability_allow_unstable(def_id, id, span, method_span, allow_unstable);
+        let is_allowed = matches!(eval_result, EvalResult::Allow);
+        match eval_result {
             EvalResult::Allow => {}
             EvalResult::Deny { feature, reason, issue, suggestion, is_soft } => report_unstable(
                 self.sess,
@@ -544,6 +581,8 @@ impl<'tcx> TyCtxt<'tcx> {
             ),
             EvalResult::Unmarked => unmarked(span, def_id),
         }
+
+        is_allowed
     }
 
     pub fn lookup_deprecation(self, id: DefId) -> Option<Deprecation> {
diff --git a/compiler/rustc_middle/src/mir/generic_graph.rs b/compiler/rustc_middle/src/mir/generic_graph.rs
index a4d78911b27..f3621cd99d3 100644
--- a/compiler/rustc_middle/src/mir/generic_graph.rs
+++ b/compiler/rustc_middle/src/mir/generic_graph.rs
@@ -8,7 +8,7 @@ pub fn mir_fn_to_generic_graph<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Grap
     let def_id = body.source.def_id();
     let def_name = graphviz_safe_def_name(def_id);
     let graph_name = format!("Mir_{}", def_name);
-    let dark_mode = tcx.sess.opts.debugging_opts.graphviz_dark_mode;
+    let dark_mode = tcx.sess.opts.unstable_opts.graphviz_dark_mode;
 
     // Nodes
     let nodes: Vec<Node> = body
diff --git a/compiler/rustc_middle/src/mir/generic_graphviz.rs b/compiler/rustc_middle/src/mir/generic_graphviz.rs
index c907680bda1..11ac45943ac 100644
--- a/compiler/rustc_middle/src/mir/generic_graphviz.rs
+++ b/compiler/rustc_middle/src/mir/generic_graphviz.rs
@@ -56,11 +56,11 @@ impl<
         writeln!(w, "{} {}{} {{", kind, cluster, self.graphviz_name)?;
 
         // Global graph properties
-        let font = format!(r#"fontname="{}""#, tcx.sess.opts.debugging_opts.graphviz_font);
+        let font = format!(r#"fontname="{}""#, tcx.sess.opts.unstable_opts.graphviz_font);
         let mut graph_attrs = vec![&font[..]];
         let mut content_attrs = vec![&font[..]];
 
-        let dark_mode = tcx.sess.opts.debugging_opts.graphviz_dark_mode;
+        let dark_mode = tcx.sess.opts.unstable_opts.graphviz_dark_mode;
         if dark_mode {
             graph_attrs.push(r#"bgcolor="black""#);
             graph_attrs.push(r#"fontcolor="white""#);
diff --git a/compiler/rustc_middle/src/mir/graphviz.rs b/compiler/rustc_middle/src/mir/graphviz.rs
index 92c7a358c0a..5de56dad07a 100644
--- a/compiler/rustc_middle/src/mir/graphviz.rs
+++ b/compiler/rustc_middle/src/mir/graphviz.rs
@@ -57,11 +57,11 @@ where
     W: Write,
 {
     // Global graph properties
-    let font = format!(r#"fontname="{}""#, tcx.sess.opts.debugging_opts.graphviz_font);
+    let font = format!(r#"fontname="{}""#, tcx.sess.opts.unstable_opts.graphviz_font);
     let mut graph_attrs = vec![&font[..]];
     let mut content_attrs = vec![&font[..]];
 
-    let dark_mode = tcx.sess.opts.debugging_opts.graphviz_dark_mode;
+    let dark_mode = tcx.sess.opts.unstable_opts.graphviz_dark_mode;
     if dark_mode {
         graph_attrs.push(r#"bgcolor="black""#);
         graph_attrs.push(r#"fontcolor="white""#);
diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs
index ae333846f06..db7e0fb8a3b 100644
--- a/compiler/rustc_middle/src/mir/interpret/allocation.rs
+++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs
@@ -30,7 +30,7 @@ use crate::ty;
 // hashed. (see the `Hash` impl below for more details), so the impl is not derived.
 #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable)]
 #[derive(HashStable)]
-pub struct Allocation<Tag = AllocId, Extra = ()> {
+pub struct Allocation<Prov = AllocId, Extra = ()> {
     /// The actual bytes of the allocation.
     /// Note that the bytes of a pointer represent the offset of the pointer.
     bytes: Box<[u8]>,
@@ -38,7 +38,7 @@ pub struct Allocation<Tag = AllocId, Extra = ()> {
     /// Only the first byte of a pointer is inserted into the map; i.e.,
     /// every entry in this map applies to `pointer_size` consecutive bytes starting
     /// at the given offset.
-    relocations: Relocations<Tag>,
+    relocations: Relocations<Prov>,
     /// Denotes which part of this allocation is initialized.
     init_mask: InitMask,
     /// The alignment of the allocation to detect unaligned reads.
@@ -102,8 +102,8 @@ impl hash::Hash for Allocation {
 /// (`ConstAllocation`) are used quite a bit.
 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, HashStable)]
 #[rustc_pass_by_value]
-pub struct ConstAllocation<'tcx, Tag = AllocId, Extra = ()>(
-    pub Interned<'tcx, Allocation<Tag, Extra>>,
+pub struct ConstAllocation<'tcx, Prov = AllocId, Extra = ()>(
+    pub Interned<'tcx, Allocation<Prov, Extra>>,
 );
 
 impl<'tcx> fmt::Debug for ConstAllocation<'tcx> {
@@ -114,8 +114,8 @@ impl<'tcx> fmt::Debug for ConstAllocation<'tcx> {
     }
 }
 
-impl<'tcx, Tag, Extra> ConstAllocation<'tcx, Tag, Extra> {
-    pub fn inner(self) -> &'tcx Allocation<Tag, Extra> {
+impl<'tcx, Prov, Extra> ConstAllocation<'tcx, Prov, Extra> {
+    pub fn inner(self) -> &'tcx Allocation<Prov, Extra> {
         self.0.0
     }
 }
@@ -200,7 +200,7 @@ impl AllocRange {
 }
 
 // The constructors are all without extra; the extra gets added by a machine hook later.
-impl<Tag> Allocation<Tag> {
+impl<Prov> Allocation<Prov> {
     /// Creates an allocation initialized by the given bytes
     pub fn from_bytes<'a>(
         slice: impl Into<Cow<'a, [u8]>>,
@@ -225,6 +225,8 @@ impl<Tag> Allocation<Tag> {
 
     /// Try to create an Allocation of `size` bytes, failing if there is not enough memory
     /// available to the compiler to do so.
+    ///
+    /// If `panic_on_fail` is true, this will never return `Err`.
     pub fn uninit<'tcx>(size: Size, align: Align, panic_on_fail: bool) -> InterpResult<'tcx, Self> {
         let bytes = Box::<[u8]>::try_new_zeroed_slice(size.bytes_usize()).map_err(|_| {
             // This results in an error that can happen non-deterministically, since the memory
@@ -254,14 +256,15 @@ impl<Tag> Allocation<Tag> {
 }
 
 impl Allocation {
-    /// Convert Tag and add Extra fields
-    pub fn convert_tag_add_extra<Tag, Extra, Err>(
+    /// Adjust allocation from the ones in tcx to a custom Machine instance
+    /// with a different Provenance and Extra type.
+    pub fn adjust_from_tcx<Prov, Extra, Err>(
         self,
         cx: &impl HasDataLayout,
         extra: Extra,
-        mut tagger: impl FnMut(Pointer<AllocId>) -> Result<Pointer<Tag>, Err>,
-    ) -> Result<Allocation<Tag, Extra>, Err> {
-        // Compute new pointer tags, which also adjusts the bytes.
+        mut adjust_ptr: impl FnMut(Pointer<AllocId>) -> Result<Pointer<Prov>, Err>,
+    ) -> Result<Allocation<Prov, Extra>, Err> {
+        // Compute new pointer provenance, which also adjusts the bytes.
         let mut bytes = self.bytes;
         let mut new_relocations = Vec::with_capacity(self.relocations.0.len());
         let ptr_size = cx.data_layout().pointer_size.bytes_usize();
@@ -270,10 +273,10 @@ impl Allocation {
             let idx = offset.bytes_usize();
             let ptr_bytes = &mut bytes[idx..idx + ptr_size];
             let bits = read_target_uint(endian, ptr_bytes).unwrap();
-            let (ptr_tag, ptr_offset) =
-                tagger(Pointer::new(alloc_id, Size::from_bytes(bits)))?.into_parts();
+            let (ptr_prov, ptr_offset) =
+                adjust_ptr(Pointer::new(alloc_id, Size::from_bytes(bits)))?.into_parts();
             write_target_uint(endian, ptr_bytes, ptr_offset.bytes().into()).unwrap();
-            new_relocations.push((offset, ptr_tag));
+            new_relocations.push((offset, ptr_prov));
         }
         // Create allocation.
         Ok(Allocation {
@@ -288,7 +291,7 @@ impl Allocation {
 }
 
 /// Raw accessors. Provide access to otherwise private bytes.
-impl<Tag, Extra> Allocation<Tag, Extra> {
+impl<Prov, Extra> Allocation<Prov, Extra> {
     pub fn len(&self) -> usize {
         self.bytes.len()
     }
@@ -311,13 +314,13 @@ impl<Tag, Extra> Allocation<Tag, Extra> {
     }
 
     /// Returns the relocation list.
-    pub fn relocations(&self) -> &Relocations<Tag> {
+    pub fn relocations(&self) -> &Relocations<Prov> {
         &self.relocations
     }
 }
 
 /// Byte accessors.
-impl<Tag: Provenance, Extra> Allocation<Tag, Extra> {
+impl<Prov: Provenance, Extra> Allocation<Prov, Extra> {
     /// This is the entirely abstraction-violating way to just grab the raw bytes without
     /// caring about relocations. It just deduplicates some code between `read_scalar`
     /// and `get_bytes_internal`.
@@ -411,7 +414,7 @@ impl<Tag: Provenance, Extra> Allocation<Tag, Extra> {
 }
 
 /// Reading and writing.
-impl<Tag: Provenance, Extra> Allocation<Tag, Extra> {
+impl<Prov: Provenance, Extra> Allocation<Prov, Extra> {
     /// Validates that `ptr.offset` and `ptr.offset + size` do not point to the middle of a
     /// relocation. If `allow_uninit`/`allow_ptr` is `false`, also enforces that the memory in the
     /// given range contains no uninitialized bytes/relocations.
@@ -449,7 +452,7 @@ impl<Tag: Provenance, Extra> Allocation<Tag, Extra> {
         cx: &impl HasDataLayout,
         range: AllocRange,
         read_provenance: bool,
-    ) -> AllocResult<ScalarMaybeUninit<Tag>> {
+    ) -> AllocResult<ScalarMaybeUninit<Prov>> {
         if read_provenance {
             assert_eq!(range.size, cx.data_layout().pointer_size);
         }
@@ -473,7 +476,7 @@ impl<Tag: Provenance, Extra> Allocation<Tag, Extra> {
 
         // If we are *not* reading a pointer, and we can just ignore relocations,
         // then do exactly that.
-        if !read_provenance && Tag::OFFSET_IS_ADDR {
+        if !read_provenance && Prov::OFFSET_IS_ADDR {
             // We just strip provenance.
             let bytes = self.get_bytes_even_more_internal(range);
             let bits = read_target_uint(cx.data_layout().endian, bytes).unwrap();
@@ -504,7 +507,7 @@ impl<Tag: Provenance, Extra> Allocation<Tag, Extra> {
         &mut self,
         cx: &impl HasDataLayout,
         range: AllocRange,
-        val: ScalarMaybeUninit<Tag>,
+        val: ScalarMaybeUninit<Prov>,
     ) -> AllocResult {
         assert!(self.mutability == Mutability::Mut);
 
@@ -546,9 +549,9 @@ impl<Tag: Provenance, Extra> Allocation<Tag, Extra> {
 }
 
 /// Relocations.
-impl<Tag: Copy, Extra> Allocation<Tag, Extra> {
+impl<Prov: Copy, Extra> Allocation<Prov, Extra> {
     /// Returns all relocations overlapping with the given pointer-offset pair.
-    fn get_relocations(&self, cx: &impl HasDataLayout, range: AllocRange) -> &[(Size, Tag)] {
+    fn get_relocations(&self, cx: &impl HasDataLayout, range: AllocRange) -> &[(Size, Prov)] {
         // We have to go back `pointer_size - 1` bytes, as that one would still overlap with
         // the beginning of this range.
         let start = range.start.bytes().saturating_sub(cx.data_layout().pointer_size.bytes() - 1);
@@ -578,7 +581,7 @@ impl<Tag: Copy, Extra> Allocation<Tag, Extra> {
     /// immediately in that case.
     fn clear_relocations(&mut self, cx: &impl HasDataLayout, range: AllocRange) -> AllocResult
     where
-        Tag: Provenance,
+        Prov: Provenance,
     {
         // Find the start and end of the given range and its outermost relocations.
         let (first, last) = {
@@ -600,7 +603,7 @@ impl<Tag: Copy, Extra> Allocation<Tag, Extra> {
         // FIXME: Miri should preserve partial relocations; see
         // https://github.com/rust-lang/miri/issues/2181.
         if first < start {
-            if Tag::ERR_ON_PARTIAL_PTR_OVERWRITE {
+            if Prov::ERR_ON_PARTIAL_PTR_OVERWRITE {
                 return Err(AllocError::PartialPointerOverwrite(first));
             }
             warn!(
@@ -609,7 +612,7 @@ impl<Tag: Copy, Extra> Allocation<Tag, Extra> {
             self.init_mask.set_range(first, start, false);
         }
         if last > end {
-            if Tag::ERR_ON_PARTIAL_PTR_OVERWRITE {
+            if Prov::ERR_ON_PARTIAL_PTR_OVERWRITE {
                 return Err(AllocError::PartialPointerOverwrite(
                     last - cx.data_layout().pointer_size,
                 ));
@@ -640,22 +643,22 @@ impl<Tag: Copy, Extra> Allocation<Tag, Extra> {
 
 /// "Relocations" stores the provenance information of pointers stored in memory.
 #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TyEncodable, TyDecodable)]
-pub struct Relocations<Tag = AllocId>(SortedMap<Size, Tag>);
+pub struct Relocations<Prov = AllocId>(SortedMap<Size, Prov>);
 
-impl<Tag> Relocations<Tag> {
+impl<Prov> Relocations<Prov> {
     pub fn new() -> Self {
         Relocations(SortedMap::new())
     }
 
     // The caller must guarantee that the given relocations are already sorted
     // by address and contain no duplicates.
-    pub fn from_presorted(r: Vec<(Size, Tag)>) -> Self {
+    pub fn from_presorted(r: Vec<(Size, Prov)>) -> Self {
         Relocations(SortedMap::from_presorted_elements(r))
     }
 }
 
-impl<Tag> Deref for Relocations<Tag> {
-    type Target = SortedMap<Size, Tag>;
+impl<Prov> Deref for Relocations<Prov> {
+    type Target = SortedMap<Size, Prov>;
 
     fn deref(&self) -> &Self::Target {
         &self.0
@@ -665,18 +668,18 @@ impl<Tag> Deref for Relocations<Tag> {
 /// A partial, owned list of relocations to transfer into another allocation.
 ///
 /// Offsets are already adjusted to the destination allocation.
-pub struct AllocationRelocations<Tag> {
-    dest_relocations: Vec<(Size, Tag)>,
+pub struct AllocationRelocations<Prov> {
+    dest_relocations: Vec<(Size, Prov)>,
 }
 
-impl<Tag: Copy, Extra> Allocation<Tag, Extra> {
+impl<Prov: Copy, Extra> Allocation<Prov, Extra> {
     pub fn prepare_relocation_copy(
         &self,
         cx: &impl HasDataLayout,
         src: AllocRange,
         dest: Size,
         count: u64,
-    ) -> AllocationRelocations<Tag> {
+    ) -> AllocationRelocations<Prov> {
         let relocations = self.get_relocations(cx, src);
         if relocations.is_empty() {
             return AllocationRelocations { dest_relocations: Vec::new() };
@@ -686,7 +689,7 @@ impl<Tag: Copy, Extra> Allocation<Tag, Extra> {
         let mut new_relocations = Vec::with_capacity(relocations.len() * (count as usize));
 
         // If `count` is large, this is rather wasteful -- we are allocating a big array here, which
-        // is mostly filled with redundant information since it's just N copies of the same `Tag`s
+        // is mostly filled with redundant information since it's just N copies of the same `Prov`s
         // at slightly adjusted offsets. The reason we do this is so that in `mark_relocation_range`
         // we can use `insert_presorted`. That wouldn't work with an `Iterator` that just produces
         // the right sequence of relocations for all N copies.
@@ -711,7 +714,7 @@ impl<Tag: Copy, Extra> Allocation<Tag, Extra> {
     ///
     /// This is dangerous to use as it can violate internal `Allocation` invariants!
     /// It only exists to support an efficient implementation of `mem_copy_repeatedly`.
-    pub fn mark_relocation_range(&mut self, relocations: AllocationRelocations<Tag>) {
+    pub fn mark_relocation_range(&mut self, relocations: AllocationRelocations<Prov>) {
         self.relocations.0.insert_presorted(relocations.dest_relocations);
     }
 }
@@ -1176,7 +1179,7 @@ impl<'a> Iterator for InitChunkIter<'a> {
 }
 
 /// Uninitialized bytes.
-impl<Tag: Copy, Extra> Allocation<Tag, Extra> {
+impl<Prov: Copy, Extra> Allocation<Prov, Extra> {
     /// Checks whether the given range  is entirely initialized.
     ///
     /// Returns `Ok(())` if it's initialized. Otherwise returns the range of byte
@@ -1224,7 +1227,7 @@ impl InitMaskCompressed {
 }
 
 /// Transferring the initialization mask to other allocations.
-impl<Tag, Extra> Allocation<Tag, Extra> {
+impl<Prov, Extra> Allocation<Prov, Extra> {
     /// Creates a run-length encoding of the initialization mask; panics if range is empty.
     ///
     /// This is essentially a more space-efficient version of
diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs
index 795f23edb31..cecb55578d3 100644
--- a/compiler/rustc_middle/src/mir/interpret/error.rs
+++ b/compiler/rustc_middle/src/mir/interpret/error.rs
@@ -1,7 +1,7 @@
 use super::{AllocId, AllocRange, ConstAlloc, Pointer, Scalar};
 
 use crate::mir::interpret::ConstValue;
-use crate::ty::{layout, query::TyCtxtAt, tls, FnSig, Ty, ValTree};
+use crate::ty::{layout, query::TyCtxtAt, tls, Ty, ValTree};
 
 use rustc_data_structures::sync::Lock;
 use rustc_errors::{pluralize, struct_span_err, DiagnosticBuilder, ErrorGuaranteed};
@@ -219,7 +219,7 @@ pub struct ScalarSizeMismatch {
 }
 
 /// Error information for when the program caused Undefined Behavior.
-pub enum UndefinedBehaviorInfo<'tcx> {
+pub enum UndefinedBehaviorInfo {
     /// Free-form case. Only for errors that are never caught!
     Ub(String),
     /// Unreachable code was executed.
@@ -241,12 +241,6 @@ pub enum UndefinedBehaviorInfo<'tcx> {
     PointerArithOverflow,
     /// Invalid metadata in a wide pointer (using `str` to avoid allocations).
     InvalidMeta(&'static str),
-    /// Invalid drop function in vtable.
-    InvalidVtableDropFn(FnSig<'tcx>),
-    /// Invalid size in a vtable: too large.
-    InvalidVtableSize,
-    /// Invalid alignment in a vtable: too large, or not a power of 2.
-    InvalidVtableAlignment(String),
     /// Reading a C string that does not end within its allocation.
     UnterminatedCString(Pointer),
     /// Dereferencing a dangling pointer after it got freed.
@@ -271,6 +265,8 @@ pub enum UndefinedBehaviorInfo<'tcx> {
     WriteToReadOnly(AllocId),
     // Trying to access the data behind a function pointer.
     DerefFunctionPointer(AllocId),
+    // Trying to access the data behind a vtable pointer.
+    DerefVTablePointer(AllocId),
     /// The value validity check found a problem.
     /// Should only be thrown by `validity.rs` and always point out which part of the value
     /// is the problem.
@@ -288,6 +284,8 @@ pub enum UndefinedBehaviorInfo<'tcx> {
     InvalidTag(Scalar),
     /// Using a pointer-not-to-a-function as function pointer.
     InvalidFunctionPointer(Pointer),
+    /// Using a pointer-not-to-a-vtable as vtable pointer.
+    InvalidVTablePointer(Pointer),
     /// Using a string that is not valid UTF-8,
     InvalidStr(std::str::Utf8Error),
     /// Using uninitialized data where it is not allowed.
@@ -300,7 +298,7 @@ pub enum UndefinedBehaviorInfo<'tcx> {
     UninhabitedEnumVariantWritten,
 }
 
-impl fmt::Display for UndefinedBehaviorInfo<'_> {
+impl fmt::Display for UndefinedBehaviorInfo {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         use UndefinedBehaviorInfo::*;
         match self {
@@ -315,14 +313,6 @@ impl fmt::Display for UndefinedBehaviorInfo<'_> {
             RemainderOverflow => write!(f, "overflow in signed remainder (dividing MIN by -1)"),
             PointerArithOverflow => write!(f, "overflowing in-bounds pointer arithmetic"),
             InvalidMeta(msg) => write!(f, "invalid metadata in wide pointer: {msg}"),
-            InvalidVtableDropFn(sig) => write!(
-                f,
-                "invalid drop function signature: got {sig}, expected exactly one argument which must be a pointer type",
-            ),
-            InvalidVtableSize => {
-                write!(f, "invalid vtable: size is bigger than largest supported object")
-            }
-            InvalidVtableAlignment(msg) => write!(f, "invalid vtable: alignment {msg}"),
             UnterminatedCString(p) => write!(
                 f,
                 "reading a null-terminated string starting at {p:?} with no null found before end of allocation",
@@ -359,6 +349,7 @@ impl fmt::Display for UndefinedBehaviorInfo<'_> {
             ),
             WriteToReadOnly(a) => write!(f, "writing to {a:?} which is read-only"),
             DerefFunctionPointer(a) => write!(f, "accessing {a:?} which contains a function"),
+            DerefVTablePointer(a) => write!(f, "accessing {a:?} which contains a vtable"),
             ValidationFailure { path: None, msg } => {
                 write!(f, "constructing invalid value: {msg}")
             }
@@ -375,6 +366,9 @@ impl fmt::Display for UndefinedBehaviorInfo<'_> {
             InvalidFunctionPointer(p) => {
                 write!(f, "using {p:?} as function pointer but it does not point to a function")
             }
+            InvalidVTablePointer(p) => {
+                write!(f, "using {p:?} as vtable pointer but it does not point to a vtable")
+            }
             InvalidStr(err) => write!(f, "this string is not valid UTF-8: {err}"),
             InvalidUninitBytes(Some((alloc, info))) => write!(
                 f,
@@ -494,7 +488,7 @@ impl dyn MachineStopType {
 
 pub enum InterpError<'tcx> {
     /// The program caused undefined behavior.
-    UndefinedBehavior(UndefinedBehaviorInfo<'tcx>),
+    UndefinedBehavior(UndefinedBehaviorInfo),
     /// The program did something the interpreter does not support (some of these *might* be UB
     /// but the interpreter is not sure).
     Unsupported(UnsupportedOpInfo),
diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs
index 698024b2330..967f8ece16c 100644
--- a/compiler/rustc_middle/src/mir/interpret/mod.rs
+++ b/compiler/rustc_middle/src/mir/interpret/mod.rs
@@ -196,6 +196,7 @@ impl fmt::Debug for AllocId {
 enum AllocDiscriminant {
     Alloc,
     Fn,
+    VTable,
     Static,
 }
 
@@ -215,6 +216,12 @@ pub fn specialized_encode_alloc_id<'tcx, E: TyEncoder<I = TyCtxt<'tcx>>>(
             AllocDiscriminant::Fn.encode(encoder);
             fn_instance.encode(encoder);
         }
+        GlobalAlloc::VTable(ty, poly_trait_ref) => {
+            trace!("encoding {:?} with {ty:#?}, {poly_trait_ref:#?}", alloc_id);
+            AllocDiscriminant::VTable.encode(encoder);
+            ty.encode(encoder);
+            poly_trait_ref.encode(encoder);
+        }
         GlobalAlloc::Static(did) => {
             assert!(!tcx.is_thread_local_static(did));
             // References to statics doesn't need to know about their allocations,
@@ -305,7 +312,9 @@ impl<'s> AllocDecodingSession<'s> {
                                 State::InProgress(TinyList::new_single(self.session_id), alloc_id);
                             Some(alloc_id)
                         }
-                        AllocDiscriminant::Fn | AllocDiscriminant::Static => {
+                        AllocDiscriminant::Fn
+                        | AllocDiscriminant::Static
+                        | AllocDiscriminant::VTable => {
                             // Fns and statics cannot be cyclic, and their `AllocId`
                             // is determined later by interning.
                             *entry =
@@ -355,6 +364,16 @@ impl<'s> AllocDecodingSession<'s> {
                     let alloc_id = decoder.interner().create_fn_alloc(instance);
                     alloc_id
                 }
+                AllocDiscriminant::VTable => {
+                    assert!(alloc_id.is_none());
+                    trace!("creating vtable alloc ID");
+                    let ty = <Ty<'_> as Decodable<D>>::decode(decoder);
+                    let poly_trait_ref =
+                        <Option<ty::PolyExistentialTraitRef<'_>> as Decodable<D>>::decode(decoder);
+                    trace!("decoded vtable alloc instance: {ty:?}, {poly_trait_ref:?}");
+                    let alloc_id = decoder.interner().create_vtable_alloc(ty, poly_trait_ref);
+                    alloc_id
+                }
                 AllocDiscriminant::Static => {
                     assert!(alloc_id.is_none());
                     trace!("creating extern static alloc ID");
@@ -380,6 +399,8 @@ impl<'s> AllocDecodingSession<'s> {
 pub enum GlobalAlloc<'tcx> {
     /// The alloc ID is used as a function pointer.
     Function(Instance<'tcx>),
+    /// This alloc ID points to a symbolic (not-reified) vtable.
+    VTable(Ty<'tcx>, Option<ty::PolyExistentialTraitRef<'tcx>>),
     /// The alloc ID points to a "lazy" static variable that did not get computed (yet).
     /// This is also used to break the cycle in recursive statics.
     Static(DefId),
@@ -407,6 +428,16 @@ impl<'tcx> GlobalAlloc<'tcx> {
             _ => bug!("expected function, got {:?}", self),
         }
     }
+
+    /// Panics if the `GlobalAlloc` is not `GlobalAlloc::VTable`
+    #[track_caller]
+    #[inline]
+    pub fn unwrap_vtable(&self) -> (Ty<'tcx>, Option<ty::PolyExistentialTraitRef<'tcx>>) {
+        match *self {
+            GlobalAlloc::VTable(ty, poly_trait_ref) => (ty, poly_trait_ref),
+            _ => bug!("expected vtable, got {:?}", self),
+        }
+    }
 }
 
 pub(crate) struct AllocMap<'tcx> {
@@ -454,12 +485,12 @@ impl<'tcx> TyCtxt<'tcx> {
     }
 
     /// Reserves a new ID *if* this allocation has not been dedup-reserved before.
-    /// Should only be used for function pointers and statics, we don't want
-    /// to dedup IDs for "real" memory!
+    /// Should only be used for "symbolic" allocations (function pointers, vtables, statics), we
+    /// don't want to dedup IDs for "real" memory!
     fn reserve_and_set_dedup(self, alloc: GlobalAlloc<'tcx>) -> AllocId {
         let mut alloc_map = self.alloc_map.lock();
         match alloc {
-            GlobalAlloc::Function(..) | GlobalAlloc::Static(..) => {}
+            GlobalAlloc::Function(..) | GlobalAlloc::Static(..) | GlobalAlloc::VTable(..) => {}
             GlobalAlloc::Memory(..) => bug!("Trying to dedup-reserve memory with real data!"),
         }
         if let Some(&alloc_id) = alloc_map.dedup.get(&alloc) {
@@ -504,6 +535,15 @@ impl<'tcx> TyCtxt<'tcx> {
         }
     }
 
+    /// Generates an `AllocId` for a (symbolic, not-reified) vtable.  Will get deduplicated.
+    pub fn create_vtable_alloc(
+        self,
+        ty: Ty<'tcx>,
+        poly_trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>,
+    ) -> AllocId {
+        self.reserve_and_set_dedup(GlobalAlloc::VTable(ty, poly_trait_ref))
+    }
+
     /// Interns the `Allocation` and return a new `AllocId`, even if there's already an identical
     /// `Allocation` with a different `AllocId`.
     /// Statics with identical content will still point to the same `Allocation`, i.e.,
@@ -521,7 +561,7 @@ impl<'tcx> TyCtxt<'tcx> {
     /// This function exists to allow const eval to detect the difference between evaluation-
     /// local dangling pointers and allocations in constants/statics.
     #[inline]
-    pub fn get_global_alloc(self, id: AllocId) -> Option<GlobalAlloc<'tcx>> {
+    pub fn try_get_global_alloc(self, id: AllocId) -> Option<GlobalAlloc<'tcx>> {
         self.alloc_map.lock().alloc_map.get(&id).cloned()
     }
 
@@ -532,7 +572,7 @@ impl<'tcx> TyCtxt<'tcx> {
     /// ids), this function is frequently used throughout rustc, but should not be used within
     /// the miri engine.
     pub fn global_alloc(self, id: AllocId) -> GlobalAlloc<'tcx> {
-        match self.get_global_alloc(id) {
+        match self.try_get_global_alloc(id) {
             Some(alloc) => alloc,
             None => bug!("could not find allocation for {id:?}"),
         }
diff --git a/compiler/rustc_middle/src/mir/interpret/pointer.rs b/compiler/rustc_middle/src/mir/interpret/pointer.rs
index d4cdf45d186..384954cbbd5 100644
--- a/compiler/rustc_middle/src/mir/interpret/pointer.rs
+++ b/compiler/rustc_middle/src/mir/interpret/pointer.rs
@@ -159,34 +159,34 @@ impl Provenance for AllocId {
 /// Pointers are "tagged" with provenance information; typically the `AllocId` they belong to.
 #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, TyEncodable, TyDecodable, Hash)]
 #[derive(HashStable)]
-pub struct Pointer<Tag = AllocId> {
-    pub(super) offset: Size, // kept private to avoid accidental misinterpretation (meaning depends on `Tag` type)
-    pub provenance: Tag,
+pub struct Pointer<Prov = AllocId> {
+    pub(super) offset: Size, // kept private to avoid accidental misinterpretation (meaning depends on `Prov` type)
+    pub provenance: Prov,
 }
 
 static_assert_size!(Pointer, 16);
-// `Option<Tag>` pointers are also passed around quite a bit
+// `Option<Prov>` pointers are also passed around quite a bit
 // (but not stored in permanent machine state).
 static_assert_size!(Pointer<Option<AllocId>>, 16);
 
 // We want the `Debug` output to be readable as it is used by `derive(Debug)` for
 // all the Miri types.
-impl<Tag: Provenance> fmt::Debug for Pointer<Tag> {
+impl<Prov: Provenance> fmt::Debug for Pointer<Prov> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         Provenance::fmt(self, f)
     }
 }
 
-impl<Tag: Provenance> fmt::Debug for Pointer<Option<Tag>> {
+impl<Prov: Provenance> fmt::Debug for Pointer<Option<Prov>> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self.provenance {
-            Some(tag) => Provenance::fmt(&Pointer::new(tag, self.offset), f),
+            Some(prov) => Provenance::fmt(&Pointer::new(prov, self.offset), f),
             None => write!(f, "{:#x}[noalloc]", self.offset.bytes()),
         }
     }
 }
 
-impl<Tag: Provenance> fmt::Display for Pointer<Option<Tag>> {
+impl<Prov: Provenance> fmt::Display for Pointer<Option<Prov>> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         if self.provenance.is_none() && self.offset.bytes() == 0 {
             write!(f, "null pointer")
@@ -204,38 +204,38 @@ impl From<AllocId> for Pointer {
     }
 }
 
-impl<Tag> From<Pointer<Tag>> for Pointer<Option<Tag>> {
+impl<Prov> From<Pointer<Prov>> for Pointer<Option<Prov>> {
     #[inline(always)]
-    fn from(ptr: Pointer<Tag>) -> Self {
-        let (tag, offset) = ptr.into_parts();
-        Pointer::new(Some(tag), offset)
+    fn from(ptr: Pointer<Prov>) -> Self {
+        let (prov, offset) = ptr.into_parts();
+        Pointer::new(Some(prov), offset)
     }
 }
 
-impl<Tag> Pointer<Option<Tag>> {
-    /// Convert this pointer that *might* have a tag into a pointer that *definitely* has a tag, or
-    /// an absolute address.
+impl<Prov> Pointer<Option<Prov>> {
+    /// Convert this pointer that *might* have a provenance into a pointer that *definitely* has a
+    /// provenance, or an absolute address.
     ///
     /// This is rarely what you want; call `ptr_try_get_alloc_id` instead.
-    pub fn into_pointer_or_addr(self) -> Result<Pointer<Tag>, Size> {
+    pub fn into_pointer_or_addr(self) -> Result<Pointer<Prov>, Size> {
         match self.provenance {
-            Some(tag) => Ok(Pointer::new(tag, self.offset)),
+            Some(prov) => Ok(Pointer::new(prov, self.offset)),
             None => Err(self.offset),
         }
     }
 
     /// Returns the absolute address the pointer points to.
-    /// Only works if Tag::OFFSET_IS_ADDR is true!
+    /// Only works if Prov::OFFSET_IS_ADDR is true!
     pub fn addr(self) -> Size
     where
-        Tag: Provenance,
+        Prov: Provenance,
     {
-        assert!(Tag::OFFSET_IS_ADDR);
+        assert!(Prov::OFFSET_IS_ADDR);
         self.offset
     }
 }
 
-impl<Tag> Pointer<Option<Tag>> {
+impl<Prov> Pointer<Option<Prov>> {
     #[inline(always)]
     pub fn from_addr(addr: u64) -> Self {
         Pointer { provenance: None, offset: Size::from_bytes(addr) }
@@ -247,21 +247,21 @@ impl<Tag> Pointer<Option<Tag>> {
     }
 }
 
-impl<'tcx, Tag> Pointer<Tag> {
+impl<'tcx, Prov> Pointer<Prov> {
     #[inline(always)]
-    pub fn new(provenance: Tag, offset: Size) -> Self {
+    pub fn new(provenance: Prov, offset: Size) -> Self {
         Pointer { provenance, offset }
     }
 
-    /// Obtain the constituents of this pointer. Not that the meaning of the offset depends on the type `Tag`!
+    /// Obtain the constituents of this pointer. Not that the meaning of the offset depends on the type `Prov`!
     /// This function must only be used in the implementation of `Machine::ptr_get_alloc`,
     /// and when a `Pointer` is taken apart to be stored efficiently in an `Allocation`.
     #[inline(always)]
-    pub fn into_parts(self) -> (Tag, Size) {
+    pub fn into_parts(self) -> (Prov, Size) {
         (self.provenance, self.offset)
     }
 
-    pub fn map_provenance(self, f: impl FnOnce(Tag) -> Tag) -> Self {
+    pub fn map_provenance(self, f: impl FnOnce(Prov) -> Prov) -> Self {
         Pointer { provenance: f(self.provenance), ..self }
     }
 
diff --git a/compiler/rustc_middle/src/mir/interpret/value.rs b/compiler/rustc_middle/src/mir/interpret/value.rs
index 22bbe29c105..834c114ee1c 100644
--- a/compiler/rustc_middle/src/mir/interpret/value.rs
+++ b/compiler/rustc_middle/src/mir/interpret/value.rs
@@ -126,7 +126,7 @@ impl<'tcx> ConstValue<'tcx> {
 /// Do *not* match on a `Scalar`! Use the various `to_*` methods instead.
 #[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, TyEncodable, TyDecodable, Hash)]
 #[derive(HashStable)]
-pub enum Scalar<Tag = AllocId> {
+pub enum Scalar<Prov = AllocId> {
     /// The raw bytes of a simple value.
     Int(ScalarInt),
 
@@ -137,7 +137,7 @@ pub enum Scalar<Tag = AllocId> {
     /// We also store the size of the pointer, such that a `Scalar` always knows how big it is.
     /// The size is always the pointer size of the current target, but this is not information
     /// that we always have readily available.
-    Ptr(Pointer<Tag>, u8),
+    Ptr(Pointer<Prov>, u8),
 }
 
 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
@@ -145,7 +145,7 @@ static_assert_size!(Scalar, 24);
 
 // We want the `Debug` output to be readable as it is used by `derive(Debug)` for
 // all the Miri types.
-impl<Tag: Provenance> fmt::Debug for Scalar<Tag> {
+impl<Prov: Provenance> fmt::Debug for Scalar<Prov> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self {
             Scalar::Ptr(ptr, _size) => write!(f, "{:?}", ptr),
@@ -154,7 +154,7 @@ impl<Tag: Provenance> fmt::Debug for Scalar<Tag> {
     }
 }
 
-impl<Tag: Provenance> fmt::Display for Scalar<Tag> {
+impl<Prov: Provenance> fmt::Display for Scalar<Prov> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self {
             Scalar::Ptr(ptr, _size) => write!(f, "pointer to {:?}", ptr),
@@ -163,7 +163,7 @@ impl<Tag: Provenance> fmt::Display for Scalar<Tag> {
     }
 }
 
-impl<Tag: Provenance> fmt::LowerHex for Scalar<Tag> {
+impl<Prov: Provenance> fmt::LowerHex for Scalar<Prov> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self {
             Scalar::Ptr(ptr, _size) => write!(f, "pointer to {:?}", ptr),
@@ -172,37 +172,38 @@ impl<Tag: Provenance> fmt::LowerHex for Scalar<Tag> {
     }
 }
 
-impl<Tag> From<Single> for Scalar<Tag> {
+impl<Prov> From<Single> for Scalar<Prov> {
     #[inline(always)]
     fn from(f: Single) -> Self {
         Scalar::from_f32(f)
     }
 }
 
-impl<Tag> From<Double> for Scalar<Tag> {
+impl<Prov> From<Double> for Scalar<Prov> {
     #[inline(always)]
     fn from(f: Double) -> Self {
         Scalar::from_f64(f)
     }
 }
 
-impl<Tag> From<ScalarInt> for Scalar<Tag> {
+impl<Prov> From<ScalarInt> for Scalar<Prov> {
     #[inline(always)]
     fn from(ptr: ScalarInt) -> Self {
         Scalar::Int(ptr)
     }
 }
 
-impl<Tag> Scalar<Tag> {
+impl<Prov> Scalar<Prov> {
     #[inline(always)]
-    pub fn from_pointer(ptr: Pointer<Tag>, cx: &impl HasDataLayout) -> Self {
+    pub fn from_pointer(ptr: Pointer<Prov>, cx: &impl HasDataLayout) -> Self {
         Scalar::Ptr(ptr, u8::try_from(cx.pointer_size().bytes()).unwrap())
     }
 
-    /// Create a Scalar from a pointer with an `Option<_>` tag (where `None` represents a plain integer).
-    pub fn from_maybe_pointer(ptr: Pointer<Option<Tag>>, cx: &impl HasDataLayout) -> Self {
+    /// Create a Scalar from a pointer with an `Option<_>` provenance (where `None` represents a
+    /// plain integer / "invalid" pointer).
+    pub fn from_maybe_pointer(ptr: Pointer<Option<Prov>>, cx: &impl HasDataLayout) -> Self {
         match ptr.into_parts() {
-            (Some(tag), offset) => Scalar::from_pointer(Pointer::new(tag, offset), cx),
+            (Some(prov), offset) => Scalar::from_pointer(Pointer::new(prov, offset), cx),
             (None, offset) => {
                 Scalar::Int(ScalarInt::try_from_uint(offset.bytes(), cx.pointer_size()).unwrap())
             }
@@ -310,7 +311,7 @@ impl<Tag> Scalar<Tag> {
     pub fn to_bits_or_ptr_internal(
         self,
         target_size: Size,
-    ) -> Result<Result<u128, Pointer<Tag>>, ScalarSizeMismatch> {
+    ) -> Result<Result<u128, Pointer<Prov>>, ScalarSizeMismatch> {
         assert_ne!(target_size.bytes(), 0, "you should never look at the bits of a ZST");
         Ok(match self {
             Scalar::Int(int) => Ok(int.to_bits(target_size).map_err(|size| {
@@ -329,7 +330,20 @@ impl<Tag> Scalar<Tag> {
     }
 }
 
-impl<'tcx, Tag: Provenance> Scalar<Tag> {
+impl<'tcx, Prov: Provenance> Scalar<Prov> {
+    pub fn to_pointer(self, cx: &impl HasDataLayout) -> InterpResult<'tcx, Pointer<Option<Prov>>> {
+        match self
+            .to_bits_or_ptr_internal(cx.pointer_size())
+            .map_err(|s| err_ub!(ScalarSizeMismatch(s)))?
+        {
+            Err(ptr) => Ok(ptr.into()),
+            Ok(bits) => {
+                let addr = u64::try_from(bits).unwrap();
+                Ok(Pointer::from_addr(addr))
+            }
+        }
+    }
+
     /// Fundamental scalar-to-int (cast) operation. Many convenience wrappers exist below, that you
     /// likely want to use instead.
     ///
@@ -341,13 +355,13 @@ impl<'tcx, Tag: Provenance> Scalar<Tag> {
         match self {
             Scalar::Int(int) => Ok(int),
             Scalar::Ptr(ptr, sz) => {
-                if Tag::OFFSET_IS_ADDR {
+                if Prov::OFFSET_IS_ADDR {
                     Ok(ScalarInt::try_from_uint(ptr.offset.bytes(), Size::from_bytes(sz)).unwrap())
                 } else {
                     // We know `offset` is relative, since `OFFSET_IS_ADDR == false`.
-                    let (tag, offset) = ptr.into_parts();
+                    let (prov, offset) = ptr.into_parts();
                     // Because `OFFSET_IS_ADDR == false`, this unwrap can never fail.
-                    Err(Scalar::Ptr(Pointer::new(tag.get_alloc_id().unwrap(), offset), sz))
+                    Err(Scalar::Ptr(Pointer::new(prov.get_alloc_id().unwrap(), offset), sz))
                 }
             }
         }
@@ -489,24 +503,24 @@ impl<'tcx, Tag: Provenance> Scalar<Tag> {
 }
 
 #[derive(Clone, Copy, Eq, PartialEq, TyEncodable, TyDecodable, HashStable, Hash)]
-pub enum ScalarMaybeUninit<Tag = AllocId> {
-    Scalar(Scalar<Tag>),
+pub enum ScalarMaybeUninit<Prov = AllocId> {
+    Scalar(Scalar<Prov>),
     Uninit,
 }
 
 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
 static_assert_size!(ScalarMaybeUninit, 24);
 
-impl<Tag> From<Scalar<Tag>> for ScalarMaybeUninit<Tag> {
+impl<Prov> From<Scalar<Prov>> for ScalarMaybeUninit<Prov> {
     #[inline(always)]
-    fn from(s: Scalar<Tag>) -> Self {
+    fn from(s: Scalar<Prov>) -> Self {
         ScalarMaybeUninit::Scalar(s)
     }
 }
 
 // We want the `Debug` output to be readable as it is used by `derive(Debug)` for
 // all the Miri types.
-impl<Tag: Provenance> fmt::Debug for ScalarMaybeUninit<Tag> {
+impl<Prov: Provenance> fmt::Debug for ScalarMaybeUninit<Prov> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self {
             ScalarMaybeUninit::Uninit => write!(f, "<uninitialized>"),
@@ -515,7 +529,7 @@ impl<Tag: Provenance> fmt::Debug for ScalarMaybeUninit<Tag> {
     }
 }
 
-impl<Tag: Provenance> fmt::LowerHex for ScalarMaybeUninit<Tag> {
+impl<Prov: Provenance> fmt::LowerHex for ScalarMaybeUninit<Prov> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self {
             ScalarMaybeUninit::Uninit => write!(f, "uninitialized bytes"),
@@ -524,19 +538,19 @@ impl<Tag: Provenance> fmt::LowerHex for ScalarMaybeUninit<Tag> {
     }
 }
 
-impl<Tag> ScalarMaybeUninit<Tag> {
+impl<Prov> ScalarMaybeUninit<Prov> {
     #[inline]
-    pub fn from_pointer(ptr: Pointer<Tag>, cx: &impl HasDataLayout) -> Self {
+    pub fn from_pointer(ptr: Pointer<Prov>, cx: &impl HasDataLayout) -> Self {
         ScalarMaybeUninit::Scalar(Scalar::from_pointer(ptr, cx))
     }
 
     #[inline]
-    pub fn from_maybe_pointer(ptr: Pointer<Option<Tag>>, cx: &impl HasDataLayout) -> Self {
+    pub fn from_maybe_pointer(ptr: Pointer<Option<Prov>>, cx: &impl HasDataLayout) -> Self {
         ScalarMaybeUninit::Scalar(Scalar::from_maybe_pointer(ptr, cx))
     }
 
     #[inline]
-    pub fn check_init<'tcx>(self) -> InterpResult<'tcx, Scalar<Tag>> {
+    pub fn check_init<'tcx>(self) -> InterpResult<'tcx, Scalar<Prov>> {
         match self {
             ScalarMaybeUninit::Scalar(scalar) => Ok(scalar),
             ScalarMaybeUninit::Uninit => throw_ub!(InvalidUninitBytes(None)),
@@ -544,7 +558,12 @@ impl<Tag> ScalarMaybeUninit<Tag> {
     }
 }
 
-impl<'tcx, Tag: Provenance> ScalarMaybeUninit<Tag> {
+impl<'tcx, Prov: Provenance> ScalarMaybeUninit<Prov> {
+    #[inline(always)]
+    pub fn to_pointer(self, cx: &impl HasDataLayout) -> InterpResult<'tcx, Pointer<Option<Prov>>> {
+        self.check_init()?.to_pointer(cx)
+    }
+
     #[inline(always)]
     pub fn to_bool(self) -> InterpResult<'tcx, bool> {
         self.check_init()?.to_bool()
diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs
index 333a88ba520..f7311ebdabf 100644
--- a/compiler/rustc_middle/src/mir/mod.rs
+++ b/compiler/rustc_middle/src/mir/mod.rs
@@ -1461,6 +1461,14 @@ impl<'tcx> Place<'tcx> {
         self.projection.iter().any(|elem| elem.is_indirect())
     }
 
+    /// If MirPhase >= Derefered and if projection contains Deref,
+    /// It's guaranteed to be in the first place
+    pub fn has_deref(&self) -> bool {
+        // To make sure this is not accidently used in wrong mir phase
+        debug_assert!(!self.projection[1..].contains(&PlaceElem::Deref));
+        self.projection.first() == Some(&PlaceElem::Deref)
+    }
+
     /// Finds the innermost `Local` from this `Place`, *if* it is either a local itself or
     /// a single deref of a local.
     #[inline(always)]
@@ -1533,6 +1541,12 @@ impl<'tcx> PlaceRef<'tcx> {
         }
     }
 
+    /// If MirPhase >= Derefered and if projection contains Deref,
+    /// It's guaranteed to be in the first place
+    pub fn has_deref(&self) -> bool {
+        self.projection.first() == Some(&PlaceElem::Deref)
+    }
+
     /// If this place represents a local variable like `_X` with no
     /// projections, return `Some(_X)`.
     #[inline]
@@ -1662,6 +1676,22 @@ impl SourceScope {
             ClearCrossCrate::Clear => None,
         }
     }
+
+    /// The instance this source scope was inlined from, if any.
+    #[inline]
+    pub fn inlined_instance<'tcx>(
+        self,
+        source_scopes: &IndexVec<SourceScope, SourceScopeData<'tcx>>,
+    ) -> Option<ty::Instance<'tcx>> {
+        let scope_data = &source_scopes[self];
+        if let Some((inlined_instance, _)) = scope_data.inlined {
+            Some(inlined_instance)
+        } else if let Some(inlined_scope) = scope_data.inlined_parent_scope {
+            Some(source_scopes[inlined_scope].inlined.unwrap().0)
+        } else {
+            None
+        }
+    }
 }
 
 #[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
@@ -1884,7 +1914,7 @@ impl<'tcx> Debug for Rvalue<'tcx> {
 
                 // When printing regions, add trailing space if necessary.
                 let print_region = ty::tls::with(|tcx| {
-                    tcx.sess.verbose() || tcx.sess.opts.debugging_opts.identify_regions
+                    tcx.sess.verbose() || tcx.sess.opts.unstable_opts.identify_regions
                 });
                 let region = if print_region {
                     let mut region = region.to_string();
@@ -1954,7 +1984,7 @@ impl<'tcx> Debug for Rvalue<'tcx> {
 
                     AggregateKind::Closure(def_id, substs) => ty::tls::with(|tcx| {
                         if let Some(def_id) = def_id.as_local() {
-                            let name = if tcx.sess.opts.debugging_opts.span_free_formats {
+                            let name = if tcx.sess.opts.unstable_opts.span_free_formats {
                                 let substs = tcx.lift(substs).unwrap();
                                 format!(
                                     "[closure@{}]",
diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs
index 021f2782736..21ae121e1ce 100644
--- a/compiler/rustc_middle/src/mir/mono.rs
+++ b/compiler/rustc_middle/src/mir/mono.rs
@@ -90,7 +90,7 @@ impl<'tcx> MonoItem<'tcx> {
         let generate_cgu_internal_copies = tcx
             .sess
             .opts
-            .debugging_opts
+            .unstable_opts
             .inline_in_all_cgus
             .unwrap_or_else(|| tcx.sess.opts.optimize != OptLevel::No)
             && !tcx.sess.link_dead_code();
@@ -362,7 +362,7 @@ impl<'tcx> CodegenUnit<'tcx> {
                             // the codegen tests and can even make item order
                             // unstable.
                             InstanceDef::Item(def) => def.did.as_local().map(Idx::index),
-                            InstanceDef::VtableShim(..)
+                            InstanceDef::VTableShim(..)
                             | InstanceDef::ReifyShim(..)
                             | InstanceDef::Intrinsic(..)
                             | InstanceDef::FnPtrShim(..)
@@ -459,7 +459,7 @@ impl<'tcx> CodegenUnitNameBuilder<'tcx> {
     {
         let cgu_name = self.build_cgu_name_no_mangle(cnum, components, special_suffix);
 
-        if self.tcx.sess.opts.debugging_opts.human_readable_cgu_names {
+        if self.tcx.sess.opts.unstable_opts.human_readable_cgu_names {
             cgu_name
         } else {
             Symbol::intern(&CodegenUnit::mangle_name(cgu_name.as_str()))
diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs
index e2fa37ee7be..0ce41337b91 100644
--- a/compiler/rustc_middle/src/mir/pretty.rs
+++ b/compiler/rustc_middle/src/mir/pretty.rs
@@ -90,7 +90,7 @@ pub fn dump_mir<'tcx, F>(
 }
 
 pub fn dump_enabled<'tcx>(tcx: TyCtxt<'tcx>, pass_name: &str, def_id: DefId) -> bool {
-    let Some(ref filters) = tcx.sess.opts.debugging_opts.dump_mir else {
+    let Some(ref filters) = tcx.sess.opts.unstable_opts.dump_mir else {
         return false;
     };
     // see notes on #41697 below
@@ -141,7 +141,7 @@ fn dump_matched_mir_node<'tcx, F>(
         extra_data(PassWhere::AfterCFG, &mut file)?;
     };
 
-    if tcx.sess.opts.debugging_opts.dump_mir_graphviz {
+    if tcx.sess.opts.unstable_opts.dump_mir_graphviz {
         let _: io::Result<()> = try {
             let mut file =
                 create_dump_file(tcx, "dot", pass_num, pass_name, disambiguator, body.source)?;
@@ -149,7 +149,7 @@ fn dump_matched_mir_node<'tcx, F>(
         };
     }
 
-    if let Some(spanview) = tcx.sess.opts.debugging_opts.dump_mir_spanview {
+    if let Some(spanview) = tcx.sess.opts.unstable_opts.dump_mir_spanview {
         let _: io::Result<()> = try {
             let file_basename =
                 dump_file_basename(tcx, pass_num, pass_name, disambiguator, body.source);
@@ -175,7 +175,7 @@ fn dump_file_basename<'tcx>(
         None => String::new(),
     };
 
-    let pass_num = if tcx.sess.opts.debugging_opts.dump_mir_exclude_pass_number {
+    let pass_num = if tcx.sess.opts.unstable_opts.dump_mir_exclude_pass_number {
         String::new()
     } else {
         match pass_num {
@@ -214,7 +214,7 @@ fn dump_file_basename<'tcx>(
 /// graphviz data or other things.
 fn dump_path(tcx: TyCtxt<'_>, basename: &str, extension: &str) -> PathBuf {
     let mut file_path = PathBuf::new();
-    file_path.push(Path::new(&tcx.sess.opts.debugging_opts.dump_mir_dir));
+    file_path.push(Path::new(&tcx.sess.opts.unstable_opts.dump_mir_dir));
 
     let file_name = format!("{}.{}", basename, extension,);
 
@@ -360,7 +360,7 @@ where
             "{:A$} // {}{}",
             indented_body,
             if tcx.sess.verbose() { format!("{:?}: ", current_location) } else { String::new() },
-            comment(tcx, statement.source_info),
+            comment(tcx, statement.source_info, body.span),
             A = ALIGN,
         )?;
 
@@ -381,7 +381,7 @@ where
         "{:A$} // {}{}",
         indented_terminator,
         if tcx.sess.verbose() { format!("{:?}: ", current_location) } else { String::new() },
-        comment(tcx, data.terminator().source_info),
+        comment(tcx, data.terminator().source_info, body.span),
         A = ALIGN,
     )?;
 
@@ -518,8 +518,14 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> {
     }
 }
 
-fn comment(tcx: TyCtxt<'_>, SourceInfo { span, scope }: SourceInfo) -> String {
-    format!("scope {} at {}", scope.index(), tcx.sess.source_map().span_to_embeddable_string(span))
+fn comment(tcx: TyCtxt<'_>, SourceInfo { span, scope }: SourceInfo, function_span: Span) -> String {
+    let location = if tcx.sess.opts.unstable_opts.mir_pretty_relative_line_numbers {
+        tcx.sess.source_map().span_to_relative_line_string(span, function_span)
+    } else {
+        tcx.sess.source_map().span_to_embeddable_string(span)
+    };
+
+    format!("scope {} at {}", scope.index(), location,)
 }
 
 /// Prints local variables in a scope tree.
@@ -550,7 +556,7 @@ fn write_scope_tree(
             "{0:1$} // in {2}",
             indented_debug_info,
             ALIGN,
-            comment(tcx, var_debug_info.source_info),
+            comment(tcx, var_debug_info.source_info, body.span),
         )?;
     }
 
@@ -585,7 +591,7 @@ fn write_scope_tree(
             indented_decl,
             ALIGN,
             local_name,
-            comment(tcx, local_decl.source_info),
+            comment(tcx, local_decl.source_info, body.span),
         )?;
     }
 
@@ -720,11 +726,17 @@ pub fn write_allocations<'tcx>(
                 write!(w, "{}", display_allocation(tcx, alloc.inner()))
             };
         write!(w, "\n{id:?}")?;
-        match tcx.get_global_alloc(id) {
+        match tcx.try_get_global_alloc(id) {
             // This can't really happen unless there are bugs, but it doesn't cost us anything to
             // gracefully handle it and allow buggy rustc to be debugged via allocation printing.
             None => write!(w, " (deallocated)")?,
             Some(GlobalAlloc::Function(inst)) => write!(w, " (fn: {inst})")?,
+            Some(GlobalAlloc::VTable(ty, Some(trait_ref))) => {
+                write!(w, " (vtable: impl {trait_ref} for {ty})")?
+            }
+            Some(GlobalAlloc::VTable(ty, None)) => {
+                write!(w, " (vtable: impl <auto trait> for {ty})")?
+            }
             Some(GlobalAlloc::Static(did)) if !tcx.is_foreign_item(did) => {
                 match tcx.eval_static_initializer(did) {
                     Ok(alloc) => {
@@ -767,21 +779,21 @@ pub fn write_allocations<'tcx>(
 /// After the hex dump, an ascii dump follows, replacing all unprintable characters (control
 /// characters or characters whose value is larger than 127) with a `.`
 /// This also prints relocations adequately.
-pub fn display_allocation<'a, 'tcx, Tag, Extra>(
+pub fn display_allocation<'a, 'tcx, Prov, Extra>(
     tcx: TyCtxt<'tcx>,
-    alloc: &'a Allocation<Tag, Extra>,
-) -> RenderAllocation<'a, 'tcx, Tag, Extra> {
+    alloc: &'a Allocation<Prov, Extra>,
+) -> RenderAllocation<'a, 'tcx, Prov, Extra> {
     RenderAllocation { tcx, alloc }
 }
 
 #[doc(hidden)]
-pub struct RenderAllocation<'a, 'tcx, Tag, Extra> {
+pub struct RenderAllocation<'a, 'tcx, Prov, Extra> {
     tcx: TyCtxt<'tcx>,
-    alloc: &'a Allocation<Tag, Extra>,
+    alloc: &'a Allocation<Prov, Extra>,
 }
 
-impl<'a, 'tcx, Tag: Provenance, Extra> std::fmt::Display
-    for RenderAllocation<'a, 'tcx, Tag, Extra>
+impl<'a, 'tcx, Prov: Provenance, Extra> std::fmt::Display
+    for RenderAllocation<'a, 'tcx, Prov, Extra>
 {
     fn fmt(&self, w: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         let RenderAllocation { tcx, alloc } = *self;
@@ -825,9 +837,9 @@ fn write_allocation_newline(
 /// The `prefix` argument allows callers to add an arbitrary prefix before each line (even if there
 /// is only one line). Note that your prefix should contain a trailing space as the lines are
 /// printed directly after it.
-fn write_allocation_bytes<'tcx, Tag: Provenance, Extra>(
+fn write_allocation_bytes<'tcx, Prov: Provenance, Extra>(
     tcx: TyCtxt<'tcx>,
-    alloc: &Allocation<Tag, Extra>,
+    alloc: &Allocation<Prov, Extra>,
     w: &mut dyn std::fmt::Write,
     prefix: &str,
 ) -> std::fmt::Result {
@@ -861,7 +873,7 @@ fn write_allocation_bytes<'tcx, Tag: Provenance, Extra>(
         if i != line_start {
             write!(w, " ")?;
         }
-        if let Some(&tag) = alloc.relocations().get(&i) {
+        if let Some(&prov) = alloc.relocations().get(&i) {
             // Memory with a relocation must be defined
             assert!(alloc.init_mask().is_range_initialized(i, i + ptr_size).is_ok());
             let j = i.bytes_usize();
@@ -870,7 +882,7 @@ fn write_allocation_bytes<'tcx, Tag: Provenance, Extra>(
             let offset = read_target_uint(tcx.data_layout.endian, offset).unwrap();
             let offset = Size::from_bytes(offset);
             let relocation_width = |bytes| bytes * 3;
-            let ptr = Pointer::new(tag, offset);
+            let ptr = Pointer::new(prov, offset);
             let mut target = format!("{:?}", ptr);
             if target.len() > relocation_width(ptr_size.bytes_usize() - 1) {
                 // This is too long, try to save some space.
diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs
index 620f0380d53..dd9f8795f94 100644
--- a/compiler/rustc_middle/src/mir/query.rs
+++ b/compiler/rustc_middle/src/mir/query.rs
@@ -2,7 +2,7 @@
 
 use crate::mir::{Body, ConstantKind, Promoted};
 use crate::ty::{self, OpaqueHiddenType, Ty, TyCtxt};
-use rustc_data_structures::stable_map::FxHashMap;
+use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::vec_map::VecMap;
 use rustc_errors::ErrorGuaranteed;
 use rustc_hir as hir;
@@ -35,7 +35,6 @@ pub enum UnsafetyViolationDetails {
     UseOfMutableStatic,
     UseOfExternStatic,
     DerefOfRawPointer,
-    AssignToDroppingUnionField,
     AccessToUnionField,
     MutationOfLayoutConstrainedField,
     BorrowOfLayoutConstrainedField,
@@ -78,11 +77,6 @@ impl UnsafetyViolationDetails {
                 "raw pointers may be null, dangling or unaligned; they can violate aliasing rules \
                  and cause data races: all of these are undefined behavior",
             ),
-            AssignToDroppingUnionField => (
-                "assignment to union field that might need dropping",
-                "the previous content of the field will be dropped, which causes undefined \
-                 behavior if the field was not properly initialized",
-            ),
             AccessToUnionField => (
                 "access to union field",
                 "the field may not be properly initialized: using uninitialized data will cause \
@@ -241,7 +235,7 @@ pub struct BorrowCheckResult<'tcx> {
     /// All the opaque types that are restricted to concrete types
     /// by this function. Unlike the value in `TypeckResults`, this has
     /// unerased regions.
-    pub concrete_opaque_types: VecMap<DefId, OpaqueHiddenType<'tcx>>,
+    pub concrete_opaque_types: VecMap<LocalDefId, OpaqueHiddenType<'tcx>>,
     pub closure_requirements: Option<ClosureRegionRequirements<'tcx>>,
     pub used_mut_upvars: SmallVec<[Field; 8]>,
     pub tainted_by_errors: Option<ErrorGuaranteed>,
diff --git a/compiler/rustc_middle/src/mir/switch_sources.rs b/compiler/rustc_middle/src/mir/switch_sources.rs
index d1f3e6b6fe6..b91c0c25782 100644
--- a/compiler/rustc_middle/src/mir/switch_sources.rs
+++ b/compiler/rustc_middle/src/mir/switch_sources.rs
@@ -1,8 +1,8 @@
 //! Lazily compute the inverse of each `SwitchInt`'s switch targets. Modeled after
 //! `Predecessors`/`PredecessorCache`.
 
+use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
-use rustc_data_structures::stable_map::FxHashMap;
 use rustc_data_structures::sync::OnceCell;
 use rustc_index::vec::IndexVec;
 use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs
index 1cfda71baf8..89160876401 100644
--- a/compiler/rustc_middle/src/mir/visit.rs
+++ b/compiler/rustc_middle/src/mir/visit.rs
@@ -394,7 +394,7 @@ macro_rules! make_mir_visitor {
                         ty::InstanceDef::Item(_def_id) => {}
 
                         ty::InstanceDef::Intrinsic(_def_id) |
-                        ty::InstanceDef::VtableShim(_def_id) |
+                        ty::InstanceDef::VTableShim(_def_id) |
                         ty::InstanceDef::ReifyShim(_def_id) |
                         ty::InstanceDef::Virtual(_def_id, _) |
                         ty::InstanceDef::ClosureOnceShim { call_once: _def_id, track_caller: _ } |
@@ -1118,11 +1118,9 @@ macro_rules! visit_place_fns {
             context: PlaceContext,
             location: Location,
         ) {
-            // FIXME: Use PlaceRef::iter_projections, once that exists.
-            let mut cursor = place_ref.projection;
-            while let &[ref proj_base @ .., elem] = cursor {
-                cursor = proj_base;
-                self.visit_projection_elem(place_ref.local, cursor, elem, context, location);
+            for (base, elem) in place_ref.iter_projections().rev() {
+                let base_proj = base.projection;
+                self.visit_projection_elem(place_ref.local, base_proj, elem, context, location);
             }
         }
 
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index cbc45526e89..466a0fc25f7 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -351,7 +351,7 @@ rustc_queries! {
     /// Try to build an abstract representation of the given constant.
     query thir_abstract_const(
         key: DefId
-    ) -> Result<Option<&'tcx [thir::abstract_const::Node<'tcx>]>, ErrorGuaranteed> {
+    ) -> Result<Option<&'tcx [ty::abstract_const::Node<'tcx>]>, ErrorGuaranteed> {
         desc {
             |tcx| "building an abstract representation for {}", tcx.def_path_str(key),
         }
@@ -360,7 +360,7 @@ rustc_queries! {
     /// Try to build an abstract representation of the given constant.
     query thir_abstract_const_of_const_arg(
         key: (LocalDefId, DefId)
-    ) -> Result<Option<&'tcx [thir::abstract_const::Node<'tcx>]>, ErrorGuaranteed> {
+    ) -> Result<Option<&'tcx [ty::abstract_const::Node<'tcx>]>, ErrorGuaranteed> {
         desc {
             |tcx|
             "building an abstract representation for the const argument {}",
@@ -1634,11 +1634,15 @@ rustc_queries! {
         storage(ArenaCacheSelector<'tcx>)
         desc { "calculating the lib features map" }
     }
-    query defined_lib_features(_: CrateNum)
-        -> &'tcx [(Symbol, Option<Symbol>)] {
+    query defined_lib_features(_: CrateNum) -> &'tcx [(Symbol, Option<Symbol>)] {
         desc { "calculating the lib features defined in a crate" }
         separate_provide_extern
     }
+    query stability_implications(_: CrateNum) -> FxHashMap<Symbol, Symbol> {
+        storage(ArenaCacheSelector<'tcx>)
+        desc { "calculating the implications between `#[unstable]` features defined in a crate" }
+        separate_provide_extern
+    }
     /// Whether the function is an intrinsic
     query is_intrinsic(def_id: DefId) -> bool {
         desc { |tcx| "is_intrinsic({})", tcx.def_path_str(def_id) }
@@ -2053,4 +2057,12 @@ rustc_queries! {
         desc { |tcx| "looking up generator diagnostic data of `{}`", tcx.def_path_str(key) }
         separate_provide_extern
     }
+
+    query permits_uninit_init(key: TyAndLayout<'tcx>) -> bool {
+        desc { "checking to see if {:?} permits being left uninit", key.ty }
+    }
+
+    query permits_zero_init(key: TyAndLayout<'tcx>) -> bool {
+        desc { "checking to see if {:?} permits being left zeroed", key.ty }
+    }
 }
diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs
index 3fe6394ad7e..36db8d04918 100644
--- a/compiler/rustc_middle/src/thir.rs
+++ b/compiler/rustc_middle/src/thir.rs
@@ -30,7 +30,6 @@ use rustc_target::asm::InlineAsmRegOrRegClass;
 use std::fmt;
 use std::ops::Index;
 
-pub mod abstract_const;
 pub mod visit;
 
 newtype_index! {
@@ -182,6 +181,9 @@ pub enum StmtKind<'tcx> {
         /// `let pat: ty = <INIT>`
         initializer: Option<ExprId>,
 
+        /// `let pat: ty = <INIT> else { <ELSE> }
+        else_block: Option<Block>,
+
         /// The lint level for this `let` statement.
         lint_level: LintLevel,
     },
diff --git a/compiler/rustc_middle/src/thir/abstract_const.rs b/compiler/rustc_middle/src/thir/abstract_const.rs
deleted file mode 100644
index 527dbd1cd09..00000000000
--- a/compiler/rustc_middle/src/thir/abstract_const.rs
+++ /dev/null
@@ -1,61 +0,0 @@
-//! A subset of a mir body used for const evaluatability checking.
-use crate::mir;
-use crate::ty::{self, Ty, TyCtxt};
-use rustc_errors::ErrorGuaranteed;
-
-rustc_index::newtype_index! {
-    /// An index into an `AbstractConst`.
-    pub struct NodeId {
-        derive [HashStable]
-        DEBUG_FORMAT = "n{}",
-    }
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
-pub enum CastKind {
-    /// thir::ExprKind::As
-    As,
-    /// thir::ExprKind::Use
-    Use,
-}
-
-/// A node of an `AbstractConst`.
-#[derive(Debug, Clone, Copy, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
-pub enum Node<'tcx> {
-    Leaf(ty::Const<'tcx>),
-    Binop(mir::BinOp, NodeId, NodeId),
-    UnaryOp(mir::UnOp, NodeId),
-    FunctionCall(NodeId, &'tcx [NodeId]),
-    Cast(CastKind, NodeId, Ty<'tcx>),
-}
-
-#[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
-pub enum NotConstEvaluatable {
-    Error(ErrorGuaranteed),
-    MentionsInfer,
-    MentionsParam,
-}
-
-impl From<ErrorGuaranteed> for NotConstEvaluatable {
-    fn from(e: ErrorGuaranteed) -> NotConstEvaluatable {
-        NotConstEvaluatable::Error(e)
-    }
-}
-
-TrivialTypeTraversalAndLiftImpls! {
-    NotConstEvaluatable,
-}
-
-impl<'tcx> TyCtxt<'tcx> {
-    #[inline]
-    pub fn thir_abstract_const_opt_const_arg(
-        self,
-        def: ty::WithOptConstParam<rustc_hir::def_id::DefId>,
-    ) -> Result<Option<&'tcx [Node<'tcx>]>, ErrorGuaranteed> {
-        if let Some((did, param_did)) = def.as_const_arg() {
-            self.thir_abstract_const_of_const_arg((did, param_did))
-        } else {
-            self.thir_abstract_const(def.did)
-        }
-    }
-}
diff --git a/compiler/rustc_middle/src/thir/visit.rs b/compiler/rustc_middle/src/thir/visit.rs
index c8d09875c28..97249fdd175 100644
--- a/compiler/rustc_middle/src/thir/visit.rs
+++ b/compiler/rustc_middle/src/thir/visit.rs
@@ -167,11 +167,15 @@ pub fn walk_stmt<'a, 'tcx: 'a, V: Visitor<'a, 'tcx>>(visitor: &mut V, stmt: &Stm
             init_scope: _,
             ref pattern,
             lint_level: _,
+            else_block,
         } => {
             if let Some(init) = initializer {
                 visitor.visit_expr(&visitor.thir()[*init]);
             }
             visitor.visit_pat(pattern);
+            if let Some(block) = else_block {
+                visitor.visit_block(block)
+            }
         }
     }
 }
diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs
index fe7f72024d3..72b848c3ee2 100644
--- a/compiler/rustc_middle/src/traits/mod.rs
+++ b/compiler/rustc_middle/src/traits/mod.rs
@@ -10,9 +10,9 @@ mod structural_impls;
 pub mod util;
 
 use crate::infer::canonical::Canonical;
-use crate::thir::abstract_const::NotConstEvaluatable;
+use crate::ty::abstract_const::NotConstEvaluatable;
 use crate::ty::subst::SubstsRef;
-use crate::ty::{self, AdtKind, Ty, TyCtxt};
+use crate::ty::{self, AdtKind, Predicate, Ty, TyCtxt};
 
 use rustc_data_structures::sync::Lrc;
 use rustc_errors::{Applicability, Diagnostic};
@@ -139,13 +139,8 @@ impl<'tcx> ObligationCause<'tcx> {
         ObligationCause { span, body_id: hir::CRATE_HIR_ID, code: Default::default() }
     }
 
-    pub fn span(&self, tcx: TyCtxt<'tcx>) -> Span {
+    pub fn span(&self) -> Span {
         match *self.code() {
-            ObligationCauseCode::CompareImplMethodObligation { .. }
-            | ObligationCauseCode::MainFunctionType
-            | ObligationCauseCode::StartFunctionType => {
-                tcx.sess.source_map().guess_head_span(self.span)
-            }
             ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause {
                 arm_span,
                 ..
@@ -316,18 +311,10 @@ pub enum ObligationCauseCode<'tcx> {
     },
 
     /// Error derived when matching traits/impls; see ObligationCause for more details
-    CompareImplConstObligation,
-
-    /// Error derived when matching traits/impls; see ObligationCause for more details
-    CompareImplMethodObligation {
-        impl_item_def_id: LocalDefId,
-        trait_item_def_id: DefId,
-    },
-
-    /// Error derived when matching traits/impls; see ObligationCause for more details
-    CompareImplTypeObligation {
+    CompareImplItemObligation {
         impl_item_def_id: LocalDefId,
         trait_item_def_id: DefId,
+        kind: ty::AssocKind,
     },
 
     /// Checking that the bounds of a trait's associated type hold for a given impl
@@ -356,7 +343,7 @@ pub enum ObligationCauseCode<'tcx> {
     ConstPatternStructural,
 
     /// Computing common supertype in an if expression
-    IfExpression(Box<IfExpressionCause>),
+    IfExpression(Box<IfExpressionCause<'tcx>>),
 
     /// Computing common supertype of an if expression with no else counter-part
     IfExpressionWithNoElse,
@@ -419,6 +406,7 @@ pub enum ObligationCauseCode<'tcx> {
     BinOp {
         rhs_span: Option<Span>,
         is_lit: bool,
+        output_pred: Option<Predicate<'tcx>>,
     },
 }
 
@@ -492,22 +480,27 @@ impl<'tcx> ty::Lift<'tcx> for StatementAsExpression {
 
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Lift)]
 pub struct MatchExpressionArmCause<'tcx> {
+    pub arm_block_id: Option<hir::HirId>,
+    pub arm_ty: Ty<'tcx>,
     pub arm_span: Span,
+    pub prior_arm_block_id: Option<hir::HirId>,
+    pub prior_arm_ty: Ty<'tcx>,
+    pub prior_arm_span: Span,
     pub scrut_span: Span,
-    pub semi_span: Option<(Span, StatementAsExpression)>,
     pub source: hir::MatchSource,
     pub prior_arms: Vec<Span>,
-    pub last_ty: Ty<'tcx>,
     pub scrut_hir_id: hir::HirId,
     pub opt_suggest_box_span: Option<Span>,
 }
 
-#[derive(Clone, Debug, PartialEq, Eq, Hash)]
-pub struct IfExpressionCause {
-    pub then: Span,
-    pub else_sp: Span,
-    pub outer: Option<Span>,
-    pub semicolon: Option<(Span, StatementAsExpression)>,
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+#[derive(Lift, TypeFoldable, TypeVisitable)]
+pub struct IfExpressionCause<'tcx> {
+    pub then_id: hir::HirId,
+    pub else_id: hir::HirId,
+    pub then_ty: Ty<'tcx>,
+    pub else_ty: Ty<'tcx>,
+    pub outer_span: Option<Span>,
     pub opt_suggest_box_span: Option<Span>,
 }
 
@@ -546,17 +539,9 @@ pub enum SelectionError<'tcx> {
     ErrorReporting,
     /// Multiple applicable `impl`s where found. The `DefId`s correspond to
     /// all the `impl`s' Items.
-    Ambiguous(Vec<AmbiguousSelection>),
+    Ambiguous(Vec<DefId>),
 }
 
-#[derive(Copy, Clone, Debug)]
-pub enum AmbiguousSelection {
-    Impl(DefId),
-    ParamEnv(Span),
-}
-
-TrivialTypeTraversalAndLiftImpls! { AmbiguousSelection, }
-
 /// When performing resolution, it is typically the case that there
 /// can be one of three outcomes:
 ///
diff --git a/compiler/rustc_middle/src/traits/structural_impls.rs b/compiler/rustc_middle/src/traits/structural_impls.rs
index 8f1a1564fc8..7fbd57ac735 100644
--- a/compiler/rustc_middle/src/traits/structural_impls.rs
+++ b/compiler/rustc_middle/src/traits/structural_impls.rs
@@ -130,7 +130,6 @@ impl<N: fmt::Debug> fmt::Debug for traits::ImplSourceConstDestructData<N> {
 // Lift implementations
 
 TrivialTypeTraversalAndLiftImpls! {
-    super::IfExpressionCause,
     super::ImplSourceDiscriminantKindData,
     super::ImplSourcePointeeData,
 }
diff --git a/compiler/rustc_middle/src/traits/util.rs b/compiler/rustc_middle/src/traits/util.rs
index 8ce428c9799..d54b8c599d9 100644
--- a/compiler/rustc_middle/src/traits/util.rs
+++ b/compiler/rustc_middle/src/traits/util.rs
@@ -1,4 +1,4 @@
-use rustc_data_structures::stable_set::FxHashSet;
+use rustc_data_structures::fx::FxHashSet;
 
 use crate::ty::{PolyTraitRef, TyCtxt};
 
diff --git a/compiler/rustc_middle/src/ty/abstract_const.rs b/compiler/rustc_middle/src/ty/abstract_const.rs
new file mode 100644
index 00000000000..bed809930da
--- /dev/null
+++ b/compiler/rustc_middle/src/ty/abstract_const.rs
@@ -0,0 +1,194 @@
+//! A subset of a mir body used for const evaluatability checking.
+use crate::mir;
+use crate::ty::visit::TypeVisitable;
+use crate::ty::{self, subst::Subst, DelaySpanBugEmitted, EarlyBinder, SubstsRef, Ty, TyCtxt};
+use rustc_errors::ErrorGuaranteed;
+use rustc_hir::def_id::DefId;
+use std::cmp;
+use std::ops::ControlFlow;
+
+rustc_index::newtype_index! {
+    /// An index into an `AbstractConst`.
+    pub struct NodeId {
+        derive [HashStable]
+        DEBUG_FORMAT = "n{}",
+    }
+}
+
+/// A tree representing an anonymous constant.
+///
+/// This is only able to represent a subset of `MIR`,
+/// and should not leak any information about desugarings.
+#[derive(Debug, Clone, Copy)]
+pub struct AbstractConst<'tcx> {
+    // FIXME: Consider adding something like `IndexSlice`
+    // and use this here.
+    inner: &'tcx [Node<'tcx>],
+    substs: SubstsRef<'tcx>,
+}
+
+impl<'tcx> AbstractConst<'tcx> {
+    pub fn new(
+        tcx: TyCtxt<'tcx>,
+        uv: ty::Unevaluated<'tcx, ()>,
+    ) -> Result<Option<AbstractConst<'tcx>>, ErrorGuaranteed> {
+        let inner = tcx.thir_abstract_const_opt_const_arg(uv.def)?;
+        debug!("AbstractConst::new({:?}) = {:?}", uv, inner);
+        Ok(inner.map(|inner| AbstractConst { inner, substs: tcx.erase_regions(uv.substs) }))
+    }
+
+    pub fn from_const(
+        tcx: TyCtxt<'tcx>,
+        ct: ty::Const<'tcx>,
+    ) -> Result<Option<AbstractConst<'tcx>>, ErrorGuaranteed> {
+        match ct.kind() {
+            ty::ConstKind::Unevaluated(uv) => AbstractConst::new(tcx, uv.shrink()),
+            ty::ConstKind::Error(DelaySpanBugEmitted { reported, .. }) => Err(reported),
+            _ => Ok(None),
+        }
+    }
+
+    #[inline]
+    pub fn subtree(self, node: NodeId) -> AbstractConst<'tcx> {
+        AbstractConst { inner: &self.inner[..=node.index()], substs: self.substs }
+    }
+
+    #[inline]
+    pub fn root(self, tcx: TyCtxt<'tcx>) -> Node<'tcx> {
+        let node = self.inner.last().copied().unwrap();
+        match node {
+            Node::Leaf(leaf) => Node::Leaf(EarlyBinder(leaf).subst(tcx, self.substs)),
+            Node::Cast(kind, operand, ty) => {
+                Node::Cast(kind, operand, EarlyBinder(ty).subst(tcx, self.substs))
+            }
+            // Don't perform substitution on the following as they can't directly contain generic params
+            Node::Binop(_, _, _) | Node::UnaryOp(_, _) | Node::FunctionCall(_, _) => node,
+        }
+    }
+
+    pub fn unify_failure_kind(self, tcx: TyCtxt<'tcx>) -> FailureKind {
+        let mut failure_kind = FailureKind::Concrete;
+        walk_abstract_const::<!, _>(tcx, self, |node| {
+            match node.root(tcx) {
+                Node::Leaf(leaf) => {
+                    if leaf.has_infer_types_or_consts() {
+                        failure_kind = FailureKind::MentionsInfer;
+                    } else if leaf.has_param_types_or_consts() {
+                        failure_kind = cmp::min(failure_kind, FailureKind::MentionsParam);
+                    }
+                }
+                Node::Cast(_, _, ty) => {
+                    if ty.has_infer_types_or_consts() {
+                        failure_kind = FailureKind::MentionsInfer;
+                    } else if ty.has_param_types_or_consts() {
+                        failure_kind = cmp::min(failure_kind, FailureKind::MentionsParam);
+                    }
+                }
+                Node::Binop(_, _, _) | Node::UnaryOp(_, _) | Node::FunctionCall(_, _) => {}
+            }
+            ControlFlow::CONTINUE
+        });
+        failure_kind
+    }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
+pub enum CastKind {
+    /// thir::ExprKind::As
+    As,
+    /// thir::ExprKind::Use
+    Use,
+}
+
+/// A node of an `AbstractConst`.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
+pub enum Node<'tcx> {
+    Leaf(ty::Const<'tcx>),
+    Binop(mir::BinOp, NodeId, NodeId),
+    UnaryOp(mir::UnOp, NodeId),
+    FunctionCall(NodeId, &'tcx [NodeId]),
+    Cast(CastKind, NodeId, Ty<'tcx>),
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
+pub enum NotConstEvaluatable {
+    Error(ErrorGuaranteed),
+    MentionsInfer,
+    MentionsParam,
+}
+
+impl From<ErrorGuaranteed> for NotConstEvaluatable {
+    fn from(e: ErrorGuaranteed) -> NotConstEvaluatable {
+        NotConstEvaluatable::Error(e)
+    }
+}
+
+TrivialTypeTraversalAndLiftImpls! {
+    NotConstEvaluatable,
+}
+
+impl<'tcx> TyCtxt<'tcx> {
+    #[inline]
+    pub fn thir_abstract_const_opt_const_arg(
+        self,
+        def: ty::WithOptConstParam<DefId>,
+    ) -> Result<Option<&'tcx [Node<'tcx>]>, ErrorGuaranteed> {
+        if let Some((did, param_did)) = def.as_const_arg() {
+            self.thir_abstract_const_of_const_arg((did, param_did))
+        } else {
+            self.thir_abstract_const(def.did)
+        }
+    }
+}
+
+#[instrument(skip(tcx, f), level = "debug")]
+pub fn walk_abstract_const<'tcx, R, F>(
+    tcx: TyCtxt<'tcx>,
+    ct: AbstractConst<'tcx>,
+    mut f: F,
+) -> ControlFlow<R>
+where
+    F: FnMut(AbstractConst<'tcx>) -> ControlFlow<R>,
+{
+    #[instrument(skip(tcx, f), level = "debug")]
+    fn recurse<'tcx, R>(
+        tcx: TyCtxt<'tcx>,
+        ct: AbstractConst<'tcx>,
+        f: &mut dyn FnMut(AbstractConst<'tcx>) -> ControlFlow<R>,
+    ) -> ControlFlow<R> {
+        f(ct)?;
+        let root = ct.root(tcx);
+        debug!(?root);
+        match root {
+            Node::Leaf(_) => ControlFlow::CONTINUE,
+            Node::Binop(_, l, r) => {
+                recurse(tcx, ct.subtree(l), f)?;
+                recurse(tcx, ct.subtree(r), f)
+            }
+            Node::UnaryOp(_, v) => recurse(tcx, ct.subtree(v), f),
+            Node::FunctionCall(func, args) => {
+                recurse(tcx, ct.subtree(func), f)?;
+                args.iter().try_for_each(|&arg| recurse(tcx, ct.subtree(arg), f))
+            }
+            Node::Cast(_, operand, _) => recurse(tcx, ct.subtree(operand), f),
+        }
+    }
+
+    recurse(tcx, ct, &mut f)
+}
+
+// We were unable to unify the abstract constant with
+// a constant found in the caller bounds, there are
+// now three possible cases here.
+#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
+pub enum FailureKind {
+    /// The abstract const still references an inference
+    /// variable, in this case we return `TooGeneric`.
+    MentionsInfer,
+    /// The abstract const references a generic parameter,
+    /// this means that we emit an error here.
+    MentionsParam,
+    /// The substs are concrete enough that we can simply
+    /// try and evaluate the given constant.
+    Concrete,
+}
diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs
index 4cac7670735..809406aff18 100644
--- a/compiler/rustc_middle/src/ty/adt.rs
+++ b/compiler/rustc_middle/src/ty/adt.rs
@@ -52,6 +52,8 @@ bitflags! {
         /// Indicates whether the variant list of this ADT is `#[non_exhaustive]`.
         /// (i.e., this flag is never set unless this ADT is an enum).
         const IS_VARIANT_LIST_NON_EXHAUSTIVE = 1 << 8;
+        /// Indicates whether the type is `UnsafeCell`.
+        const IS_UNSAFE_CELL              = 1 << 9;
     }
 }
 
@@ -247,6 +249,9 @@ impl AdtDefData {
         if Some(did) == tcx.lang_items().manually_drop() {
             flags |= AdtFlags::IS_MANUALLY_DROP;
         }
+        if Some(did) == tcx.lang_items().unsafe_cell_type() {
+            flags |= AdtFlags::IS_UNSAFE_CELL;
+        }
 
         AdtDefData { did, variants, flags, repr }
     }
@@ -333,6 +338,12 @@ impl<'tcx> AdtDef<'tcx> {
         self.flags().contains(AdtFlags::IS_BOX)
     }
 
+    /// Returns `true` if this is UnsafeCell<T>.
+    #[inline]
+    pub fn is_unsafe_cell(self) -> bool {
+        self.flags().contains(AdtFlags::IS_UNSAFE_CELL)
+    }
+
     /// Returns `true` if this is `ManuallyDrop<T>`.
     #[inline]
     pub fn is_manually_drop(self) -> bool {
diff --git a/compiler/rustc_middle/src/ty/assoc.rs b/compiler/rustc_middle/src/ty/assoc.rs
index 2c93af50667..eb732148e3e 100644
--- a/compiler/rustc_middle/src/ty/assoc.rs
+++ b/compiler/rustc_middle/src/ty/assoc.rs
@@ -105,6 +105,16 @@ impl AssocKind {
     }
 }
 
+impl std::fmt::Display for AssocKind {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            AssocKind::Fn => write!(f, "method"),
+            AssocKind::Const => write!(f, "associated const"),
+            AssocKind::Type => write!(f, "associated type"),
+        }
+    }
+}
+
 /// A list of `ty::AssocItem`s in definition order that allows for efficient lookup by name.
 ///
 /// When doing lookup by name, we try to postpone hygienic comparison for as long as possible since
diff --git a/compiler/rustc_middle/src/ty/closure.rs b/compiler/rustc_middle/src/ty/closure.rs
index f5ce43f3afb..27b9d27b8bb 100644
--- a/compiler/rustc_middle/src/ty/closure.rs
+++ b/compiler/rustc_middle/src/ty/closure.rs
@@ -128,6 +128,14 @@ impl<'tcx> ClosureKind {
             None
         }
     }
+
+    pub fn to_def_id(&self, tcx: TyCtxt<'_>) -> DefId {
+        match self {
+            ClosureKind::Fn => tcx.lang_items().fn_once_trait().unwrap(),
+            ClosureKind::FnMut => tcx.lang_items().fn_mut_trait().unwrap(),
+            ClosureKind::FnOnce => tcx.lang_items().fn_trait().unwrap(),
+        }
+    }
 }
 
 /// A composite describing a `Place` that is captured by a closure.
@@ -174,7 +182,11 @@ impl<'tcx> CapturedPlace<'tcx> {
                         .unwrap();
                     }
                     ty => {
-                        bug!("Unexpected type {:?} for `Field` projection", ty)
+                        span_bug!(
+                            self.get_capture_kind_span(tcx),
+                            "Unexpected type {:?} for `Field` projection",
+                            ty
+                        )
                     }
                 },
 
diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs
index 9a363914dc3..51137c52659 100644
--- a/compiler/rustc_middle/src/ty/codec.rs
+++ b/compiler/rustc_middle/src/ty/codec.rs
@@ -12,7 +12,6 @@ use crate::mir::{
     self,
     interpret::{AllocId, ConstAllocation},
 };
-use crate::thir;
 use crate::traits;
 use crate::ty::subst::SubstsRef;
 use crate::ty::{self, AdtDef, Ty};
@@ -346,7 +345,7 @@ impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D>
 }
 
 impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D>
-    for [thir::abstract_const::Node<'tcx>]
+    for [ty::abstract_const::Node<'tcx>]
 {
     fn decode(decoder: &mut D) -> &'tcx Self {
         decoder.interner().arena.alloc_from_iter(
@@ -356,7 +355,7 @@ impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D>
 }
 
 impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D>
-    for [thir::abstract_const::NodeId]
+    for [ty::abstract_const::NodeId]
 {
     fn decode(decoder: &mut D) -> &'tcx Self {
         decoder.interner().arena.alloc_from_iter(
@@ -524,4 +523,5 @@ impl_binder_encode_decode! {
     ty::ExistentialPredicate<'tcx>,
     ty::TraitRef<'tcx>,
     Vec<ty::GeneratorInteriorTypeCause<'tcx>>,
+    ty::ExistentialTraitRef<'tcx>,
 }
diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs
index a4e7a12bba3..f8792edc017 100644
--- a/compiler/rustc_middle/src/ty/consts.rs
+++ b/compiler/rustc_middle/src/ty/consts.rs
@@ -203,7 +203,7 @@ impl<'tcx> Const<'tcx> {
     pub fn to_valtree(self) -> ty::ValTree<'tcx> {
         match self.kind() {
             ty::ConstKind::Value(valtree) => valtree,
-            _ => bug!("expected ConstKind::Value"),
+            _ => bug!("expected ConstKind::Value, got {:?}", self.kind()),
         }
     }
 
diff --git a/compiler/rustc_middle/src/ty/consts/valtree.rs b/compiler/rustc_middle/src/ty/consts/valtree.rs
index 973dc3dd4a1..93707bb18ce 100644
--- a/compiler/rustc_middle/src/ty/consts/valtree.rs
+++ b/compiler/rustc_middle/src/ty/consts/valtree.rs
@@ -80,33 +80,25 @@ impl<'tcx> ValTree<'tcx> {
     }
 
     /// Get the values inside the ValTree as a slice of bytes. This only works for
-    /// constants with types &str and &[u8].
+    /// constants with types &str, &[u8], or [u8; _].
     pub fn try_to_raw_bytes(self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option<&'tcx [u8]> {
         match ty.kind() {
             ty::Ref(_, inner_ty, _) => match inner_ty.kind() {
-                ty::Str => {
-                    let leafs = self
-                        .unwrap_branch()
-                        .into_iter()
-                        .map(|v| v.unwrap_leaf().try_to_u8().unwrap())
-                        .collect::<Vec<_>>();
-
-                    return Some(tcx.arena.alloc_from_iter(leafs.into_iter()));
-                }
-                ty::Slice(slice_ty) if *slice_ty == tcx.types.u8 => {
-                    let leafs = self
-                        .unwrap_branch()
-                        .into_iter()
-                        .map(|v| v.unwrap_leaf().try_to_u8().unwrap())
-                        .collect::<Vec<_>>();
-
-                    return Some(tcx.arena.alloc_from_iter(leafs.into_iter()));
-                }
-                _ => {}
+                // `&str` can be interpreted as raw bytes
+                ty::Str => {}
+                // `&[u8]` can be interpreted as raw bytes
+                ty::Slice(slice_ty) if *slice_ty == tcx.types.u8 => {}
+                // other `&_` can't be interpreted as raw bytes
+                _ => return None,
             },
-            _ => {}
+            // `[u8; N]` can be interpreted as raw bytes
+            ty::Array(array_ty, _) if *array_ty == tcx.types.u8 => {}
+            // Otherwise, type cannot be interpreted as raw bytes
+            _ => return None,
         }
 
-        None
+        Some(tcx.arena.alloc_from_iter(
+            self.unwrap_branch().into_iter().map(|v| v.unwrap_leaf().try_to_u8().unwrap()),
+        ))
     }
 }
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index a594dab2e20..0f98d19820e 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -542,7 +542,7 @@ pub struct TypeckResults<'tcx> {
     /// even if they are only set in dead code (which doesn't show up in MIR).
     /// For type-alias-impl-trait, this map is only used to prevent query cycles,
     /// so the hidden types are all `None`.
-    pub concrete_opaque_types: VecMap<DefId, Option<Ty<'tcx>>>,
+    pub concrete_opaque_types: VecMap<LocalDefId, Option<Ty<'tcx>>>,
 
     /// Tracks the minimum captures required for a closure;
     /// see `MinCaptureInformationMap` for more details.
diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs
index 25bc6dc6167..dd2f4321060 100644
--- a/compiler/rustc_middle/src/ty/diagnostics.rs
+++ b/compiler/rustc_middle/src/ty/diagnostics.rs
@@ -3,7 +3,7 @@
 use std::ops::ControlFlow;
 
 use crate::ty::{
-    visit::TypeVisitable, Const, ConstKind, DefIdTree, ExistentialPredicate, InferTy,
+    visit::TypeVisitable, Const, ConstKind, DefIdTree, ExistentialPredicate, InferConst, InferTy,
     PolyTraitPredicate, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor,
 };
 
@@ -82,15 +82,18 @@ pub trait IsSuggestable<'tcx> {
     /// meaningful rendered suggestions when pretty-printed. We leave some
     /// nonsense, such as region vars, since those render as `'_` and are
     /// usually okay to reinterpret as elided lifetimes.
-    fn is_suggestable(self, tcx: TyCtxt<'tcx>) -> bool;
+    ///
+    /// Only if `infer_suggestable` is true, we consider type and const
+    /// inference variables to be suggestable.
+    fn is_suggestable(self, tcx: TyCtxt<'tcx>, infer_suggestable: bool) -> bool;
 }
 
 impl<'tcx, T> IsSuggestable<'tcx> for T
 where
     T: TypeVisitable<'tcx>,
 {
-    fn is_suggestable(self, tcx: TyCtxt<'tcx>) -> bool {
-        self.visit_with(&mut IsSuggestableVisitor { tcx }).is_continue()
+    fn is_suggestable(self, tcx: TyCtxt<'tcx>, infer_suggestable: bool) -> bool {
+        self.visit_with(&mut IsSuggestableVisitor { tcx, infer_suggestable }).is_continue()
     }
 }
 
@@ -100,7 +103,7 @@ pub fn suggest_arbitrary_trait_bound<'tcx>(
     err: &mut Diagnostic,
     trait_pred: PolyTraitPredicate<'tcx>,
 ) -> bool {
-    if !trait_pred.is_suggestable(tcx) {
+    if !trait_pred.is_suggestable(tcx, false) {
         return false;
     }
 
@@ -419,6 +422,7 @@ impl<'v> hir::intravisit::Visitor<'v> for StaticLifetimeVisitor<'v> {
 
 pub struct IsSuggestableVisitor<'tcx> {
     tcx: TyCtxt<'tcx>,
+    infer_suggestable: bool,
 }
 
 impl<'tcx> TypeVisitor<'tcx> for IsSuggestableVisitor<'tcx> {
@@ -426,6 +430,8 @@ impl<'tcx> TypeVisitor<'tcx> for IsSuggestableVisitor<'tcx> {
 
     fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
         match t.kind() {
+            Infer(InferTy::TyVar(_)) if self.infer_suggestable => {}
+
             FnDef(..)
             | Closure(..)
             | Infer(..)
@@ -479,6 +485,8 @@ impl<'tcx> TypeVisitor<'tcx> for IsSuggestableVisitor<'tcx> {
 
     fn visit_const(&mut self, c: Const<'tcx>) -> ControlFlow<Self::BreakTy> {
         match c.kind() {
+            ConstKind::Infer(InferConst::Var(_)) if self.infer_suggestable => {}
+
             ConstKind::Infer(..)
             | ConstKind::Bound(..)
             | ConstKind::Placeholder(..)
diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs
index 49a518b101d..91246051316 100644
--- a/compiler/rustc_middle/src/ty/error.rs
+++ b/compiler/rustc_middle/src/ty/error.rs
@@ -660,12 +660,8 @@ impl<T> Trait<T> for X {
                     | hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. }),
             )
         );
-        let impl_comparison = matches!(
-            cause_code,
-            ObligationCauseCode::CompareImplMethodObligation { .. }
-                | ObligationCauseCode::CompareImplTypeObligation { .. }
-                | ObligationCauseCode::CompareImplConstObligation
-        );
+        let impl_comparison =
+            matches!(cause_code, ObligationCauseCode::CompareImplItemObligation { .. });
         let assoc = self.associated_item(proj_ty.item_def_id);
         if !callable_scope || impl_comparison {
             // We do not want to suggest calling functions when the reason of the
diff --git a/compiler/rustc_middle/src/ty/fold.rs b/compiler/rustc_middle/src/ty/fold.rs
index f8893ae29f5..878f31af00f 100644
--- a/compiler/rustc_middle/src/ty/fold.rs
+++ b/compiler/rustc_middle/src/ty/fold.rs
@@ -445,10 +445,13 @@ impl<'a, 'tcx> TypeFolder<'tcx> for BoundVarReplacer<'a, 'tcx> {
                 let ct = (self.fld_c)(bound_const, ct.ty());
                 ty::fold::shift_vars(self.tcx, ct, self.current_index.as_u32())
             }
-            _ if ct.has_vars_bound_at_or_above(self.current_index) => ct.super_fold_with(self),
-            _ => ct,
+            _ => ct.super_fold_with(self),
         }
     }
+
+    fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> {
+        if p.has_vars_bound_at_or_above(self.current_index) { p.super_fold_with(self) } else { p }
+    }
 }
 
 impl<'tcx> TyCtxt<'tcx> {
diff --git a/compiler/rustc_middle/src/ty/generics.rs b/compiler/rustc_middle/src/ty/generics.rs
index 84547dca453..add2df25884 100644
--- a/compiler/rustc_middle/src/ty/generics.rs
+++ b/compiler/rustc_middle/src/ty/generics.rs
@@ -85,10 +85,10 @@ impl GenericParamDef {
     ) -> Option<EarlyBinder<ty::GenericArg<'tcx>>> {
         match self.kind {
             GenericParamDefKind::Type { has_default, .. } if has_default => {
-                Some(EarlyBinder(tcx.type_of(self.def_id).into()))
+                Some(tcx.bound_type_of(self.def_id).map_bound(|t| t.into()))
             }
             GenericParamDefKind::Const { has_default } if has_default => {
-                Some(EarlyBinder(tcx.const_param_default(self.def_id).into()))
+                Some(tcx.bound_const_param_default(self.def_id).map_bound(|c| c.into()))
             }
             _ => None,
         }
diff --git a/compiler/rustc_middle/src/ty/impls_ty.rs b/compiler/rustc_middle/src/ty/impls_ty.rs
index 88397a2bb56..263d64a5777 100644
--- a/compiler/rustc_middle/src/ty/impls_ty.rs
+++ b/compiler/rustc_middle/src/ty/impls_ty.rs
@@ -147,15 +147,15 @@ impl<'a> HashStable<StableHashingContext<'a>> for mir::interpret::AllocId {
         ty::tls::with_opt(|tcx| {
             trace!("hashing {:?}", *self);
             let tcx = tcx.expect("can't hash AllocIds during hir lowering");
-            tcx.get_global_alloc(*self).hash_stable(hcx, hasher);
+            tcx.try_get_global_alloc(*self).hash_stable(hcx, hasher);
         });
     }
 }
 
 // `Relocations` with default type parameters is a sorted map.
-impl<'a, Tag> HashStable<StableHashingContext<'a>> for mir::interpret::Relocations<Tag>
+impl<'a, Prov> HashStable<StableHashingContext<'a>> for mir::interpret::Relocations<Prov>
 where
-    Tag: HashStable<StableHashingContext<'a>>,
+    Prov: HashStable<StableHashingContext<'a>>,
 {
     fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
         self.len().hash_stable(hcx, hasher);
diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs
index 391abdbe84c..33a46f809b0 100644
--- a/compiler/rustc_middle/src/ty/instance.rs
+++ b/compiler/rustc_middle/src/ty/instance.rs
@@ -49,7 +49,7 @@ pub enum InstanceDef<'tcx> {
     ///
     /// The generated shim will take `Self` via `*mut Self` - conceptually this is `&owned Self` -
     /// and dereference the argument to call the original function.
-    VtableShim(DefId),
+    VTableShim(DefId),
 
     /// `fn()` pointer where the function itself cannot be turned into a pointer.
     ///
@@ -145,7 +145,7 @@ impl<'tcx> InstanceDef<'tcx> {
     pub fn def_id(self) -> DefId {
         match self {
             InstanceDef::Item(def) => def.did,
-            InstanceDef::VtableShim(def_id)
+            InstanceDef::VTableShim(def_id)
             | InstanceDef::ReifyShim(def_id)
             | InstanceDef::FnPtrShim(def_id, _)
             | InstanceDef::Virtual(def_id, _)
@@ -161,7 +161,7 @@ impl<'tcx> InstanceDef<'tcx> {
         match self {
             ty::InstanceDef::Item(def) => Some(def.did),
             ty::InstanceDef::DropGlue(def_id, Some(_)) => Some(def_id),
-            InstanceDef::VtableShim(..)
+            InstanceDef::VTableShim(..)
             | InstanceDef::ReifyShim(..)
             | InstanceDef::FnPtrShim(..)
             | InstanceDef::Virtual(..)
@@ -176,7 +176,7 @@ impl<'tcx> InstanceDef<'tcx> {
     pub fn with_opt_param(self) -> ty::WithOptConstParam<DefId> {
         match self {
             InstanceDef::Item(def) => def,
-            InstanceDef::VtableShim(def_id)
+            InstanceDef::VTableShim(def_id)
             | InstanceDef::ReifyShim(def_id)
             | InstanceDef::FnPtrShim(def_id, _)
             | InstanceDef::Virtual(def_id, _)
@@ -273,7 +273,7 @@ impl<'tcx> InstanceDef<'tcx> {
             | InstanceDef::Intrinsic(..)
             | InstanceDef::ReifyShim(..)
             | InstanceDef::Virtual(..)
-            | InstanceDef::VtableShim(..) => true,
+            | InstanceDef::VTableShim(..) => true,
         }
     }
 }
@@ -290,7 +290,7 @@ impl<'tcx> fmt::Display for Instance<'tcx> {
 
         match self.def {
             InstanceDef::Item(_) => Ok(()),
-            InstanceDef::VtableShim(_) => write!(f, " - shim(vtable)"),
+            InstanceDef::VTableShim(_) => write!(f, " - shim(vtable)"),
             InstanceDef::ReifyShim(_) => write!(f, " - shim(reify)"),
             InstanceDef::Intrinsic(_) => write!(f, " - intrinsic"),
             InstanceDef::Virtual(_, num) => write!(f, " - virtual#{}", num),
@@ -434,7 +434,7 @@ impl<'tcx> Instance<'tcx> {
             && tcx.generics_of(def_id).has_self;
         if is_vtable_shim {
             debug!(" => associated item with unsizeable self: Self");
-            Some(Instance { def: InstanceDef::VtableShim(def_id), substs })
+            Some(Instance { def: InstanceDef::VTableShim(def_id), substs })
         } else {
             Instance::resolve(tcx, param_env, def_id, substs).ok().flatten().map(|mut resolved| {
                 match resolved.def {
@@ -605,7 +605,7 @@ impl<'tcx> Instance<'tcx> {
     /// identity parameters if they are determined to be unused in `instance.def`.
     pub fn polymorphize(self, tcx: TyCtxt<'tcx>) -> Self {
         debug!("polymorphize: running polymorphization analysis");
-        if !tcx.sess.opts.debugging_opts.polymorphize {
+        if !tcx.sess.opts.unstable_opts.polymorphize {
             return self;
         }
 
diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs
index f87b6e4212d..dde55dd9655 100644
--- a/compiler/rustc_middle/src/ty/layout.rs
+++ b/compiler/rustc_middle/src/ty/layout.rs
@@ -542,14 +542,12 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
             debug!("univariant offset: {:?} field: {:#?}", offset, field);
             offsets[i as usize] = offset;
 
-            if !repr.hide_niche() {
-                if let Some(mut niche) = field.largest_niche {
-                    let available = niche.available(dl);
-                    if available > largest_niche_available {
-                        largest_niche_available = available;
-                        niche.offset += offset;
-                        largest_niche = Some(niche);
-                    }
+            if let Some(mut niche) = field.largest_niche {
+                let available = niche.available(dl);
+                if available > largest_niche_available {
+                    largest_niche_available = available;
+                    niche.offset += offset;
+                    largest_niche = Some(niche);
                 }
             }
 
@@ -1078,6 +1076,29 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
 
                     let mut st = self.univariant_uninterned(ty, &variants[v], &def.repr(), kind)?;
                     st.variants = Variants::Single { index: v };
+
+                    if def.is_unsafe_cell() {
+                        let hide_niches = |scalar: &mut _| match scalar {
+                            Scalar::Initialized { value, valid_range } => {
+                                *valid_range = WrappingRange::full(value.size(dl))
+                            }
+                            // Already doesn't have any niches
+                            Scalar::Union { .. } => {}
+                        };
+                        match &mut st.abi {
+                            Abi::Uninhabited => {}
+                            Abi::Scalar(scalar) => hide_niches(scalar),
+                            Abi::ScalarPair(a, b) => {
+                                hide_niches(a);
+                                hide_niches(b);
+                            }
+                            Abi::Vector { element, count: _ } => hide_niches(element),
+                            Abi::Aggregate { sized: _ } => {}
+                        }
+                        st.largest_niche = None;
+                        return Ok(tcx.intern_layout(st));
+                    }
+
                     let (start, end) = self.tcx.layout_scalar_valid_range(def.did());
                     match st.abi {
                         Abi::Scalar(ref mut scalar) | Abi::ScalarPair(ref mut scalar, _) => {
@@ -1106,11 +1127,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
                             }
 
                             // Update `largest_niche` if we have introduced a larger niche.
-                            let niche = if def.repr().hide_niche() {
-                                None
-                            } else {
-                                Niche::from_scalar(dl, Size::ZERO, *scalar)
-                            };
+                            let niche = Niche::from_scalar(dl, Size::ZERO, *scalar);
                             if let Some(niche) = niche {
                                 match st.largest_niche {
                                     Some(largest_niche) => {
@@ -1897,7 +1914,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
     fn record_layout_for_printing(&self, layout: TyAndLayout<'tcx>) {
         // If we are running with `-Zprint-type-sizes`, maybe record layouts
         // for dumping later.
-        if self.tcx.sess.opts.debugging_opts.print_type_sizes {
+        if self.tcx.sess.opts.unstable_opts.print_type_sizes {
             self.record_layout_for_printing_outlined(layout)
         }
     }
@@ -1958,7 +1975,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
                         min_size = field_end;
                     }
                     FieldInfo {
-                        name: name.to_string(),
+                        name,
                         offset: offset.bytes(),
                         size: field_layout.size.bytes(),
                         align: field_layout.align.abi.bytes(),
@@ -1967,7 +1984,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
                 .collect();
 
             VariantInfo {
-                name: n.map(|n| n.to_string()),
+                name: n,
                 kind: if layout.is_unsized() { SizeKind::Min } else { SizeKind::Exact },
                 align: layout.align.abi.bytes(),
                 size: if min_size.bytes() == 0 { layout.size.bytes() } else { min_size.bytes() },
@@ -2601,14 +2618,14 @@ where
                     // Use conservative pointer kind if not optimizing. This saves us the
                     // Freeze/Unpin queries, and can save time in the codegen backend (noalias
                     // attributes in LLVM have compile-time cost even in unoptimized builds).
-                    PointerKind::Shared
+                    PointerKind::SharedMutable
                 } else {
                     match mt {
                         hir::Mutability::Not => {
                             if ty.is_freeze(tcx.at(DUMMY_SP), cx.param_env()) {
                                 PointerKind::Frozen
                             } else {
-                                PointerKind::Shared
+                                PointerKind::SharedMutable
                             }
                         }
                         hir::Mutability::Mut => {
@@ -2619,7 +2636,7 @@ where
                             if ty.is_unpin(tcx.at(DUMMY_SP), cx.param_env()) {
                                 PointerKind::UniqueBorrowed
                             } else {
-                                PointerKind::Shared
+                                PointerKind::UniqueBorrowedPinned
                             }
                         }
                     }
@@ -2754,7 +2771,7 @@ impl<'tcx> ty::Instance<'tcx> {
                     _ => unreachable!(),
                 };
 
-                if let ty::InstanceDef::VtableShim(..) = self.def {
+                if let ty::InstanceDef::VTableShim(..) = self.def {
                     // Modify `fn(self, ...)` to `fn(self: *mut Self, ...)`.
                     sig = sig.map_bound(|mut sig| {
                         let mut inputs_and_output = sig.inputs_and_output.to_vec();
@@ -2899,7 +2916,7 @@ pub fn fn_can_unwind<'tcx>(tcx: TyCtxt<'tcx>, fn_def_id: Option<DefId>, abi: Spe
         //
         // This is not part of `codegen_fn_attrs` as it can differ between crates
         // and therefore cannot be computed in core.
-        if tcx.sess.opts.debugging_opts.panic_in_drop == PanicStrategy::Abort {
+        if tcx.sess.opts.unstable_opts.panic_in_drop == PanicStrategy::Abort {
             if Some(did) == tcx.lang_items().drop_in_place_fn() {
                 return false;
             }
@@ -3238,10 +3255,13 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
 
                     // `Box` (`UniqueBorrowed`) are not necessarily dereferenceable
                     // for the entire duration of the function as they can be deallocated
-                    // at any time. Set their valid size to 0.
+                    // at any time. Same for shared mutable references. If LLVM had a
+                    // way to say "dereferenceable on entry" we could use it here.
                     attrs.pointee_size = match kind {
-                        PointerKind::UniqueOwned => Size::ZERO,
-                        _ => pointee.size,
+                        PointerKind::UniqueBorrowed
+                        | PointerKind::UniqueBorrowedPinned
+                        | PointerKind::Frozen => pointee.size,
+                        PointerKind::SharedMutable | PointerKind::UniqueOwned => Size::ZERO,
                     };
 
                     // `Box`, `&T`, and `&mut T` cannot be undef.
@@ -3249,7 +3269,12 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
                     // this attribute doesn't make it UB for the pointed-to data to be undef.
                     attrs.set(ArgAttribute::NoUndef);
 
-                    // `Box` pointer parameters never alias because ownership is transferred
+                    // The aliasing rules for `Box<T>` are still not decided, but currently we emit
+                    // `noalias` for it. This can be turned off using an unstable flag.
+                    // See https://github.com/rust-lang/unsafe-code-guidelines/issues/326
+                    let noalias_for_box =
+                        self.tcx().sess.opts.unstable_opts.box_noalias.unwrap_or(true);
+
                     // `&mut` pointer parameters never alias other parameters,
                     // or mutable global data
                     //
@@ -3263,8 +3288,10 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
                     // or not to actually emit the attribute. It can also be controlled with the
                     // `-Zmutable-noalias` debugging option.
                     let no_alias = match kind {
-                        PointerKind::Shared | PointerKind::UniqueBorrowed => false,
-                        PointerKind::UniqueOwned => true,
+                        PointerKind::SharedMutable
+                        | PointerKind::UniqueBorrowed
+                        | PointerKind::UniqueBorrowedPinned => false,
+                        PointerKind::UniqueOwned => noalias_for_box,
                         PointerKind::Frozen => !is_return,
                     };
                     if no_alias {
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index f15108fb750..31c523aaca9 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -92,6 +92,7 @@ pub use self::sty::{
 pub use self::trait_def::TraitDef;
 
 pub mod _match;
+pub mod abstract_const;
 pub mod adjustment;
 pub mod binding;
 pub mod cast;
@@ -574,6 +575,19 @@ impl<'tcx> Predicate<'tcx> {
 
         Some(tcx.mk_predicate(kind))
     }
+
+    pub fn without_const(mut self, tcx: TyCtxt<'tcx>) -> Self {
+        if let PredicateKind::Trait(TraitPredicate { trait_ref, constness, polarity }) = self.kind().skip_binder()
+            && constness != BoundConstness::NotConst
+        {
+            self = tcx.mk_predicate(self.kind().rebind(PredicateKind::Trait(TraitPredicate {
+                trait_ref,
+                constness: BoundConstness::NotConst,
+                polarity,
+            })));
+        }
+        self
+    }
 }
 
 impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for Predicate<'tcx> {
@@ -789,22 +803,15 @@ pub struct TraitPredicate<'tcx> {
 pub type PolyTraitPredicate<'tcx> = ty::Binder<'tcx, TraitPredicate<'tcx>>;
 
 impl<'tcx> TraitPredicate<'tcx> {
-    pub fn remap_constness(&mut self, tcx: TyCtxt<'tcx>, param_env: &mut ParamEnv<'tcx>) {
-        if std::intrinsics::unlikely(Some(self.trait_ref.def_id) == tcx.lang_items().drop_trait()) {
-            // remap without changing constness of this predicate.
-            // this is because `T: ~const Drop` has a different meaning to `T: Drop`
-            // FIXME(fee1-dead): remove this logic after beta bump
-            param_env.remap_constness_with(self.constness)
-        } else {
-            *param_env = param_env.with_constness(self.constness.and(param_env.constness()))
-        }
+    pub fn remap_constness(&mut self, param_env: &mut ParamEnv<'tcx>) {
+        *param_env = param_env.with_constness(self.constness.and(param_env.constness()))
     }
 
     /// Remap the constness of this predicate before emitting it for diagnostics.
     pub fn remap_constness_diag(&mut self, param_env: ParamEnv<'tcx>) {
         // this is different to `remap_constness` that callees want to print this predicate
         // in case of selection errors. `T: ~const Drop` bounds cannot end up here when the
-        // param_env is not const because we it is always satisfied in non-const contexts.
+        // param_env is not const because it is always satisfied in non-const contexts.
         if let hir::Constness::NotConst = param_env.constness() {
             self.constness = ty::BoundConstness::NotConst;
         }
@@ -822,6 +829,14 @@ impl<'tcx> TraitPredicate<'tcx> {
     pub fn is_const_if_const(self) -> bool {
         self.constness == BoundConstness::ConstIfConst
     }
+
+    pub fn is_constness_satisfied_by(self, constness: hir::Constness) -> bool {
+        match (self.constness, constness) {
+            (BoundConstness::NotConst, _)
+            | (BoundConstness::ConstIfConst, hir::Constness::Const) => true,
+            (BoundConstness::ConstIfConst, hir::Constness::NotConst) => false,
+        }
+    }
 }
 
 impl<'tcx> PolyTraitPredicate<'tcx> {
@@ -1032,6 +1047,24 @@ impl<'tcx> Predicate<'tcx> {
         }
     }
 
+    pub fn to_opt_poly_projection_pred(self) -> Option<PolyProjectionPredicate<'tcx>> {
+        let predicate = self.kind();
+        match predicate.skip_binder() {
+            PredicateKind::Projection(t) => Some(predicate.rebind(t)),
+            PredicateKind::Trait(..)
+            | PredicateKind::Subtype(..)
+            | PredicateKind::Coerce(..)
+            | PredicateKind::RegionOutlives(..)
+            | PredicateKind::WellFormed(..)
+            | PredicateKind::ObjectSafe(..)
+            | PredicateKind::ClosureKind(..)
+            | PredicateKind::TypeOutlives(..)
+            | PredicateKind::ConstEvaluatable(..)
+            | PredicateKind::ConstEquate(..)
+            | PredicateKind::TypeWellFormedFromEnv(..) => None,
+        }
+    }
+
     pub fn to_opt_type_outlives(self) -> Option<PolyTypeOutlivesPredicate<'tcx>> {
         let predicate = self.kind();
         match predicate.skip_binder() {
@@ -1089,8 +1122,7 @@ impl<'tcx> InstantiatedPredicates<'tcx> {
 #[derive(Copy, Clone, Debug, PartialEq, Eq, HashStable, TyEncodable, TyDecodable, Lift)]
 #[derive(TypeFoldable, TypeVisitable)]
 pub struct OpaqueTypeKey<'tcx> {
-    // FIXME(oli-obk): make this a LocalDefId
-    pub def_id: DefId,
+    pub def_id: LocalDefId,
     pub substs: SubstsRef<'tcx>,
 }
 
@@ -1720,11 +1752,9 @@ bitflags! {
         const IS_TRANSPARENT     = 1 << 2;
         // Internal only for now. If true, don't reorder fields.
         const IS_LINEAR          = 1 << 3;
-        // If true, don't expose any niche to type's context.
-        const HIDE_NICHE         = 1 << 4;
         // If true, the type's layout can be randomized using
         // the seed stored in `ReprOptions.layout_seed`
-        const RANDOMIZE_LAYOUT   = 1 << 5;
+        const RANDOMIZE_LAYOUT   = 1 << 4;
         // Any of these flags being set prevent field reordering optimisation.
         const IS_UNOPTIMISABLE   = ReprFlags::IS_C.bits
                                  | ReprFlags::IS_SIMD.bits
@@ -1763,7 +1793,7 @@ impl ReprOptions {
         // If the user defined a custom seed for layout randomization, xor the item's
         // path hash with the user defined seed, this will allowing determinism while
         // still allowing users to further randomize layout generation for e.g. fuzzing
-        if let Some(user_seed) = tcx.sess.opts.debugging_opts.layout_seed {
+        if let Some(user_seed) = tcx.sess.opts.unstable_opts.layout_seed {
             field_shuffle_seed ^= user_seed;
         }
 
@@ -1781,7 +1811,6 @@ impl ReprOptions {
                         ReprFlags::empty()
                     }
                     attr::ReprTransparent => ReprFlags::IS_TRANSPARENT,
-                    attr::ReprNoNiche => ReprFlags::HIDE_NICHE,
                     attr::ReprSimd => ReprFlags::IS_SIMD,
                     attr::ReprInt(i) => {
                         size = Some(i);
@@ -1797,7 +1826,7 @@ impl ReprOptions {
 
         // If `-Z randomize-layout` was enabled for the type definition then we can
         // consider performing layout randomization
-        if tcx.sess.opts.debugging_opts.randomize_layout {
+        if tcx.sess.opts.unstable_opts.randomize_layout {
             flags.insert(ReprFlags::RANDOMIZE_LAYOUT);
         }
 
@@ -1834,11 +1863,6 @@ impl ReprOptions {
         self.flags.contains(ReprFlags::IS_LINEAR)
     }
 
-    #[inline]
-    pub fn hide_niche(&self) -> bool {
-        self.flags.contains(ReprFlags::HIDE_NICHE)
-    }
-
     /// Returns the discriminant type, given these `repr` options.
     /// This must only be called on enums!
     pub fn discr_type(&self) -> attr::IntType {
@@ -2125,7 +2149,7 @@ impl<'tcx> TyCtxt<'tcx> {
                     }
                 }
             }
-            ty::InstanceDef::VtableShim(..)
+            ty::InstanceDef::VTableShim(..)
             | ty::InstanceDef::ReifyShim(..)
             | ty::InstanceDef::Intrinsic(..)
             | ty::InstanceDef::FnPtrShim(..)
diff --git a/compiler/rustc_middle/src/ty/parameterized.rs b/compiler/rustc_middle/src/ty/parameterized.rs
index 54ba9e84fdb..e189ee2fc4d 100644
--- a/compiler/rustc_middle/src/ty/parameterized.rs
+++ b/compiler/rustc_middle/src/ty/parameterized.rs
@@ -3,7 +3,7 @@ use rustc_index::vec::{Idx, IndexVec};
 
 use crate::middle::exported_symbols::ExportedSymbol;
 use crate::mir::Body;
-use crate::thir::abstract_const::Node;
+use crate::ty::abstract_const::Node;
 use crate::ty::{
     self, Const, FnSig, GeneratorDiagnosticData, GenericPredicates, Predicate, TraitRef, Ty,
 };
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs
index f721a175c98..7f2e81a71a9 100644
--- a/compiler/rustc_middle/src/ty/print/pretty.rs
+++ b/compiler/rustc_middle/src/ty/print/pretty.rs
@@ -297,7 +297,7 @@ pub trait PrettyPrinter<'tcx>:
         mut self,
         def_id: DefId,
     ) -> Result<(Self::Path, bool), Self::Error> {
-        if !self.tcx().sess.opts.debugging_opts.trim_diagnostic_paths
+        if !self.tcx().sess.opts.unstable_opts.trim_diagnostic_paths
             || matches!(self.tcx().sess.opts.trimmed_def_paths, TrimmedDefPaths::Never)
             || NO_TRIMMED_PATH.with(|flag| flag.get())
             || SHOULD_PREFIX_WITH_CRATE.with(|flag| flag.get())
@@ -712,7 +712,7 @@ pub trait PrettyPrinter<'tcx>:
                     p!(write("closure"));
                     // FIXME(eddyb) should use `def_span`.
                     if let Some(did) = did.as_local() {
-                        if self.tcx().sess.opts.debugging_opts.span_free_formats {
+                        if self.tcx().sess.opts.unstable_opts.span_free_formats {
                             p!("@", print_def_path(did.to_def_id(), substs));
                         } else {
                             let span = self.tcx().def_span(did);
@@ -1030,11 +1030,11 @@ pub trait PrettyPrinter<'tcx>:
         }
     }
 
-    fn ty_infer_name(&self, _: ty::TyVid) -> Option<String> {
+    fn ty_infer_name(&self, _: ty::TyVid) -> Option<Symbol> {
         None
     }
 
-    fn const_infer_name(&self, _: ty::ConstVid<'tcx>) -> Option<String> {
+    fn const_infer_name(&self, _: ty::ConstVid<'tcx>) -> Option<Symbol> {
         None
     }
 
@@ -1269,7 +1269,7 @@ pub trait PrettyPrinter<'tcx>:
                 if let ty::Array(elem, len) = inner.kind() {
                     if let ty::Uint(ty::UintTy::U8) = elem.kind() {
                         if let ty::ConstKind::Value(ty::ValTree::Leaf(int)) = len.kind() {
-                            match self.tcx().get_global_alloc(alloc_id) {
+                            match self.tcx().try_get_global_alloc(alloc_id) {
                                 Some(GlobalAlloc::Memory(alloc)) => {
                                     let len = int.assert_bits(self.tcx().data_layout.pointer_size);
                                     let range =
@@ -1282,11 +1282,12 @@ pub trait PrettyPrinter<'tcx>:
                                         p!("<too short allocation>")
                                     }
                                 }
-                                // FIXME: for statics and functions, we could in principle print more detail.
+                                // FIXME: for statics, vtables, and functions, we could in principle print more detail.
                                 Some(GlobalAlloc::Static(def_id)) => {
                                     p!(write("<static({:?})>", def_id))
                                 }
                                 Some(GlobalAlloc::Function(_)) => p!("<function>"),
+                                Some(GlobalAlloc::VTable(..)) => p!("<vtable>"),
                                 None => p!("<dangling pointer>"),
                             }
                             return Ok(self);
@@ -1297,7 +1298,8 @@ pub trait PrettyPrinter<'tcx>:
             ty::FnPtr(_) => {
                 // FIXME: We should probably have a helper method to share code with the "Byte strings"
                 // printing above (which also has to handle pointers to all sorts of things).
-                if let Some(GlobalAlloc::Function(instance)) = self.tcx().get_global_alloc(alloc_id)
+                if let Some(GlobalAlloc::Function(instance)) =
+                    self.tcx().try_get_global_alloc(alloc_id)
                 {
                     self = self.typed_value(
                         |this| this.print_value_path(instance.def_id(), instance.substs),
@@ -1377,9 +1379,9 @@ pub trait PrettyPrinter<'tcx>:
 
     /// This is overridden for MIR printing because we only want to hide alloc ids from users, not
     /// from MIR where it is actually useful.
-    fn pretty_print_const_pointer<Tag: Provenance>(
+    fn pretty_print_const_pointer<Prov: Provenance>(
         mut self,
-        _: Pointer<Tag>,
+        _: Pointer<Prov>,
         ty: Ty<'tcx>,
         print_ty: bool,
     ) -> Result<Self::Const, Self::Error> {
@@ -1450,7 +1452,7 @@ pub trait PrettyPrinter<'tcx>:
                 }
             },
             (ty::ValTree::Branch(_), ty::Array(t, _)) if *t == u8_type => {
-                let bytes = valtree.try_to_raw_bytes(self.tcx(), *t).unwrap_or_else(|| {
+                let bytes = valtree.try_to_raw_bytes(self.tcx(), ty).unwrap_or_else(|| {
                     bug!("expected to convert valtree to raw bytes for type {:?}", t)
                 });
                 p!("*");
@@ -1550,8 +1552,8 @@ pub struct FmtPrinterData<'a, 'tcx> {
 
     pub region_highlight_mode: RegionHighlightMode<'tcx>,
 
-    pub ty_infer_name_resolver: Option<Box<dyn Fn(ty::TyVid) -> Option<String> + 'a>>,
-    pub const_infer_name_resolver: Option<Box<dyn Fn(ty::ConstVid<'tcx>) -> Option<String> + 'a>>,
+    pub ty_infer_name_resolver: Option<Box<dyn Fn(ty::TyVid) -> Option<Symbol> + 'a>>,
+    pub const_infer_name_resolver: Option<Box<dyn Fn(ty::ConstVid<'tcx>) -> Option<Symbol> + 'a>>,
 }
 
 impl<'a, 'tcx> Deref for FmtPrinter<'a, 'tcx> {
@@ -1727,7 +1729,7 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> {
     }
 
     fn print_const(self, ct: ty::Const<'tcx>) -> Result<Self::Const, Self::Error> {
-        self.pretty_print_const(ct, true)
+        self.pretty_print_const(ct, false)
     }
 
     fn path_crate(mut self, cnum: CrateNum) -> Result<Self::Path, Self::Error> {
@@ -1841,11 +1843,11 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> {
 }
 
 impl<'tcx> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx> {
-    fn ty_infer_name(&self, id: ty::TyVid) -> Option<String> {
+    fn ty_infer_name(&self, id: ty::TyVid) -> Option<Symbol> {
         self.0.ty_infer_name_resolver.as_ref().and_then(|func| func(id))
     }
 
-    fn const_infer_name(&self, id: ty::ConstVid<'tcx>) -> Option<String> {
+    fn const_infer_name(&self, id: ty::ConstVid<'tcx>) -> Option<Symbol> {
         self.0.const_infer_name_resolver.as_ref().and_then(|func| func(id))
     }
 
@@ -1919,7 +1921,7 @@ impl<'tcx> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx> {
             return true;
         }
 
-        let identify_regions = self.tcx.sess.opts.debugging_opts.identify_regions;
+        let identify_regions = self.tcx.sess.opts.unstable_opts.identify_regions;
 
         match *region {
             ty::ReEarlyBound(ref data) => {
@@ -1952,9 +1954,9 @@ impl<'tcx> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx> {
         }
     }
 
-    fn pretty_print_const_pointer<Tag: Provenance>(
+    fn pretty_print_const_pointer<Prov: Provenance>(
         self,
-        p: Pointer<Tag>,
+        p: Pointer<Prov>,
         ty: Ty<'tcx>,
         print_ty: bool,
     ) -> Result<Self::Const, Self::Error> {
@@ -1992,7 +1994,7 @@ impl<'tcx> FmtPrinter<'_, 'tcx> {
             return Ok(self);
         }
 
-        let identify_regions = self.tcx.sess.opts.debugging_opts.identify_regions;
+        let identify_regions = self.tcx.sess.opts.unstable_opts.identify_regions;
 
         // These printouts are concise.  They do not contain all the information
         // the user might want to diagnose an error, but there is basically no way
@@ -2554,7 +2556,7 @@ define_print_and_forward_display! {
 
     ty::TraitPredicate<'tcx> {
         p!(print(self.trait_ref.self_ty()), ": ");
-        if let ty::BoundConstness::ConstIfConst = self.constness {
+        if let ty::BoundConstness::ConstIfConst = self.constness && cx.tcx().features().const_trait_impl {
             p!("~const ");
         }
         p!(print(self.trait_ref.print_only_trait_path()))
diff --git a/compiler/rustc_middle/src/ty/query.rs b/compiler/rustc_middle/src/ty/query.rs
index 3d662ed5de4..2452bcf6a61 100644
--- a/compiler/rustc_middle/src/ty/query.rs
+++ b/compiler/rustc_middle/src/ty/query.rs
@@ -28,6 +28,7 @@ use crate::traits::query::{
 use crate::traits::specialization_graph;
 use crate::traits::{self, ImplSource};
 use crate::ty::fast_reject::SimplifiedType;
+use crate::ty::layout::TyAndLayout;
 use crate::ty::subst::{GenericArg, SubstsRef};
 use crate::ty::util::AlwaysRequiresDrop;
 use crate::ty::GeneratorDiagnosticData;
diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs
index 391a0a20c96..7660a2f3af6 100644
--- a/compiler/rustc_middle/src/ty/structural_impls.rs
+++ b/compiler/rustc_middle/src/ty/structural_impls.rs
@@ -224,6 +224,7 @@ TrivialTypeTraversalAndLiftImpls! {
     // general `Region`.
     crate::ty::BoundRegionKind,
     crate::ty::AssocItem,
+    crate::ty::AssocKind,
     crate::ty::Placeholder<crate::ty::BoundRegionKind>,
     crate::ty::ClosureKind,
     crate::ty::FreeRegion,
@@ -624,7 +625,7 @@ impl<'a, 'tcx> Lift<'tcx> for ty::InstanceDef<'a> {
     fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted> {
         match self {
             ty::InstanceDef::Item(def_id) => Some(ty::InstanceDef::Item(def_id)),
-            ty::InstanceDef::VtableShim(def_id) => Some(ty::InstanceDef::VtableShim(def_id)),
+            ty::InstanceDef::VTableShim(def_id) => Some(ty::InstanceDef::VTableShim(def_id)),
             ty::InstanceDef::ReifyShim(def_id) => Some(ty::InstanceDef::ReifyShim(def_id)),
             ty::InstanceDef::Intrinsic(def_id) => Some(ty::InstanceDef::Intrinsic(def_id)),
             ty::InstanceDef::FnPtrShim(def_id, ty) => {
@@ -927,7 +928,7 @@ impl<'tcx> TypeFoldable<'tcx> for ty::instance::Instance<'tcx> {
             substs: self.substs.try_fold_with(folder)?,
             def: match self.def {
                 Item(def) => Item(def.try_fold_with(folder)?),
-                VtableShim(did) => VtableShim(did.try_fold_with(folder)?),
+                VTableShim(did) => VTableShim(did.try_fold_with(folder)?),
                 ReifyShim(did) => ReifyShim(did.try_fold_with(folder)?),
                 Intrinsic(did) => Intrinsic(did.try_fold_with(folder)?),
                 FnPtrShim(did, ty) => {
@@ -954,7 +955,7 @@ impl<'tcx> TypeVisitable<'tcx> for ty::instance::Instance<'tcx> {
         self.substs.visit_with(visitor)?;
         match self.def {
             Item(def) => def.visit_with(visitor),
-            VtableShim(did) | ReifyShim(did) | Intrinsic(did) | Virtual(did, _) => {
+            VTableShim(did) | ReifyShim(did) | Intrinsic(did) | Virtual(did, _) => {
                 did.visit_with(visitor)
             }
             FnPtrShim(did, ty) | CloneShim(did, ty) => {
@@ -1122,6 +1123,7 @@ impl<'tcx> TypeVisitable<'tcx> for ty::Predicate<'tcx> {
         visitor.visit_predicate(*self)
     }
 
+    #[inline]
     fn has_vars_bound_at_or_above(&self, binder: ty::DebruijnIndex) -> bool {
         self.outer_exclusive_binder() > binder
     }
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index 4b51daadabf..9f622ad6cd2 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -932,6 +932,10 @@ impl<T> EarlyBinder<T> {
         let value = f(self.0)?;
         Ok(EarlyBinder(value))
     }
+
+    pub fn rebind<U>(&self, value: U) -> EarlyBinder<U> {
+        EarlyBinder(value)
+    }
 }
 
 impl<T> EarlyBinder<Option<T>> {
@@ -1703,13 +1707,6 @@ impl<'tcx> Ty<'tcx> {
         }
     }
 
-    pub fn expect_opaque_type(self) -> ty::OpaqueTypeKey<'tcx> {
-        match *self.kind() {
-            Opaque(def_id, substs) => ty::OpaqueTypeKey { def_id, substs },
-            _ => bug!("`expect_opaque_type` called on non-opaque type: {}", self),
-        }
-    }
-
     pub fn simd_size_and_type(self, tcx: TyCtxt<'tcx>) -> (u64, Ty<'tcx>) {
         match self.kind() {
             Adt(def, substs) => {
diff --git a/compiler/rustc_middle/src/ty/subst.rs b/compiler/rustc_middle/src/ty/subst.rs
index 3a524d7b0f3..b4fa9837aed 100644
--- a/compiler/rustc_middle/src/ty/subst.rs
+++ b/compiler/rustc_middle/src/ty/subst.rs
@@ -11,7 +11,6 @@ use rustc_data_structures::intern::{Interned, WithStableHash};
 use rustc_hir::def_id::DefId;
 use rustc_macros::HashStable;
 use rustc_serialize::{self, Decodable, Encodable};
-use rustc_span::DUMMY_SP;
 use smallvec::SmallVec;
 
 use core::intrinsics;
@@ -525,6 +524,7 @@ struct SubstFolder<'a, 'tcx> {
 }
 
 impl<'a, 'tcx> TypeFolder<'tcx> for SubstFolder<'a, 'tcx> {
+    #[inline]
     fn tcx<'b>(&'b self) -> TyCtxt<'tcx> {
         self.tcx
     }
@@ -540,6 +540,16 @@ impl<'a, 'tcx> TypeFolder<'tcx> for SubstFolder<'a, 'tcx> {
     }
 
     fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
+        #[cold]
+        #[inline(never)]
+        fn region_param_out_of_range(data: ty::EarlyBoundRegion) -> ! {
+            bug!(
+                "Region parameter out of range when substituting in region {} (index={})",
+                data.name,
+                data.index
+            )
+        }
+
         // Note: This routine only handles regions that are bound on
         // type declarations and other outer declarations, not those
         // bound in *fn types*. Region substitution of the bound
@@ -550,14 +560,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for SubstFolder<'a, 'tcx> {
                 let rk = self.substs.get(data.index as usize).map(|k| k.unpack());
                 match rk {
                     Some(GenericArgKind::Lifetime(lt)) => self.shift_region_through_binders(lt),
-                    _ => {
-                        let msg = format!(
-                            "Region parameter out of range \
-                             when substituting in region {} (index={})",
-                            data.name, data.index
-                        );
-                        span_bug!(DUMMY_SP, "{}", msg);
-                    }
+                    _ => region_param_out_of_range(data),
                 }
             }
             _ => r,
@@ -595,67 +598,80 @@ impl<'a, 'tcx> SubstFolder<'a, 'tcx> {
         let opt_ty = self.substs.get(p.index as usize).map(|k| k.unpack());
         let ty = match opt_ty {
             Some(GenericArgKind::Type(ty)) => ty,
-            Some(kind) => {
-                span_bug!(
-                    DUMMY_SP,
-                    "expected type for `{:?}` ({:?}/{}) but found {:?} \
-                     when substituting, substs={:?}",
-                    p,
-                    source_ty,
-                    p.index,
-                    kind,
-                    self.substs,
-                );
-            }
-            None => {
-                span_bug!(
-                    DUMMY_SP,
-                    "type parameter `{:?}` ({:?}/{}) out of range \
-                     when substituting, substs={:?}",
-                    p,
-                    source_ty,
-                    p.index,
-                    self.substs,
-                );
-            }
+            Some(kind) => self.type_param_expected(p, source_ty, kind),
+            None => self.type_param_out_of_range(p, source_ty),
         };
 
         self.shift_vars_through_binders(ty)
     }
 
+    #[cold]
+    #[inline(never)]
+    fn type_param_expected(&self, p: ty::ParamTy, ty: Ty<'tcx>, kind: GenericArgKind<'tcx>) -> ! {
+        bug!(
+            "expected type for `{:?}` ({:?}/{}) but found {:?} when substituting, substs={:?}",
+            p,
+            ty,
+            p.index,
+            kind,
+            self.substs,
+        )
+    }
+
+    #[cold]
+    #[inline(never)]
+    fn type_param_out_of_range(&self, p: ty::ParamTy, ty: Ty<'tcx>) -> ! {
+        bug!(
+            "type parameter `{:?}` ({:?}/{}) out of range when substituting, substs={:?}",
+            p,
+            ty,
+            p.index,
+            self.substs,
+        )
+    }
+
     fn const_for_param(&self, p: ParamConst, source_ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
         // Look up the const in the substitutions. It really should be in there.
         let opt_ct = self.substs.get(p.index as usize).map(|k| k.unpack());
         let ct = match opt_ct {
             Some(GenericArgKind::Const(ct)) => ct,
-            Some(kind) => {
-                span_bug!(
-                    DUMMY_SP,
-                    "expected const for `{:?}` ({:?}/{}) but found {:?} \
-                     when substituting substs={:?}",
-                    p,
-                    source_ct,
-                    p.index,
-                    kind,
-                    self.substs,
-                );
-            }
-            None => {
-                span_bug!(
-                    DUMMY_SP,
-                    "const parameter `{:?}` ({:?}/{}) out of range \
-                     when substituting substs={:?}",
-                    p,
-                    source_ct,
-                    p.index,
-                    self.substs,
-                );
-            }
+            Some(kind) => self.const_param_expected(p, source_ct, kind),
+            None => self.const_param_out_of_range(p, source_ct),
         };
 
         self.shift_vars_through_binders(ct)
     }
 
+    #[cold]
+    #[inline(never)]
+    fn const_param_expected(
+        &self,
+        p: ty::ParamConst,
+        ct: ty::Const<'tcx>,
+        kind: GenericArgKind<'tcx>,
+    ) -> ! {
+        bug!(
+            "expected const for `{:?}` ({:?}/{}) but found {:?} when substituting substs={:?}",
+            p,
+            ct,
+            p.index,
+            kind,
+            self.substs,
+        )
+    }
+
+    #[cold]
+    #[inline(never)]
+    fn const_param_out_of_range(&self, p: ty::ParamConst, ct: ty::Const<'tcx>) -> ! {
+        bug!(
+            "const parameter `{:?}` ({:?}/{}) out of range when substituting substs={:?}",
+            p,
+            ct,
+            p.index,
+            self.substs,
+        )
+    }
+
     /// It is sometimes necessary to adjust the De Bruijn indices during substitution. This occurs
     /// when we are substituting a type with escaping bound vars into a context where we have
     /// passed through binders. That's quite a mouthful. Let's see an example:
diff --git a/compiler/rustc_middle/src/ty/trait_def.rs b/compiler/rustc_middle/src/ty/trait_def.rs
index 826c16dda4a..541dace5cc2 100644
--- a/compiler/rustc_middle/src/ty/trait_def.rs
+++ b/compiler/rustc_middle/src/ty/trait_def.rs
@@ -74,6 +74,10 @@ impl TraitImpls {
     pub fn blanket_impls(&self) -> &[DefId] {
         self.blanket_impls.as_slice()
     }
+
+    pub fn non_blanket_impls(&self) -> &FxIndexMap<SimplifiedType, Vec<DefId>> {
+        &self.non_blanket_impls
+    }
 }
 
 impl<'tcx> TraitDef {
diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs
index 52da6c3a8c0..4d2f69b23fa 100644
--- a/compiler/rustc_middle/src/ty/util.rs
+++ b/compiler/rustc_middle/src/ty/util.rs
@@ -676,6 +676,10 @@ impl<'tcx> TyCtxt<'tcx> {
     ) -> ty::EarlyBinder<&'tcx ty::List<ty::Predicate<'tcx>>> {
         ty::EarlyBinder(self.item_bounds(def_id))
     }
+
+    pub fn bound_const_param_default(self, def_id: DefId) -> ty::EarlyBinder<ty::Const<'tcx>> {
+        ty::EarlyBinder(self.const_param_default(def_id))
+    }
 }
 
 struct OpaqueTypeExpander<'tcx> {
diff --git a/compiler/rustc_mir_build/src/build/block.rs b/compiler/rustc_mir_build/src/build/block.rs
index a83328c0cab..cb8be51a085 100644
--- a/compiler/rustc_mir_build/src/build/block.rs
+++ b/compiler/rustc_mir_build/src/build/block.rs
@@ -99,6 +99,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                     ref pattern,
                     initializer,
                     lint_level,
+                    else_block,
                 } => {
                     let ignores_expr_result = matches!(*pattern.kind, PatKind::Wild);
                     this.block_context.push(BlockFrame::Statement { ignores_expr_result });
@@ -124,18 +125,30 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                                 |this| {
                                     let scope = (*init_scope, source_info);
                                     this.in_scope(scope, *lint_level, |this| {
-                                        this.declare_bindings(
-                                            visibility_scope,
-                                            remainder_span,
-                                            pattern,
-                                            ArmHasGuard(false),
-                                            Some((None, initializer_span)),
-                                        );
-                                        this.expr_into_pattern(block, pattern.clone(), init)
+                                        if let Some(else_block) = else_block {
+                                            this.ast_let_else(
+                                                block,
+                                                init,
+                                                initializer_span,
+                                                else_block,
+                                                visibility_scope,
+                                                remainder_span,
+                                                pattern,
+                                            )
+                                        } else {
+                                            this.declare_bindings(
+                                                visibility_scope,
+                                                remainder_span,
+                                                pattern,
+                                                ArmHasGuard(false),
+                                                Some((None, initializer_span)),
+                                            );
+                                            this.expr_into_pattern(block, pattern.clone(), init) // irrefutable pattern
+                                        }
                                     })
-                                }
+                                },
                             )
-                        );
+                        )
                     } else {
                         let scope = (*init_scope, source_info);
                         unpack!(this.in_scope(scope, *lint_level, |this| {
diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs
index 1628f1a4b85..7067a48b783 100644
--- a/compiler/rustc_mir_build/src/build/matches/mod.rs
+++ b/compiler/rustc_mir_build/src/build/matches/mod.rs
@@ -1615,7 +1615,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         // those N possible outcomes, create a (initially empty)
         // vector of candidates. Those are the candidates that still
         // apply if the test has that particular outcome.
-        debug!("match_candidates: test={:?} match_pair={:?}", test, match_pair);
+        debug!("test_candidates: test={:?} match_pair={:?}", test, match_pair);
         let mut target_candidates: Vec<Vec<&mut Candidate<'pat, 'tcx>>> = vec![];
         target_candidates.resize_with(test.targets(), Default::default);
 
@@ -1635,8 +1635,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         }
         // at least the first candidate ought to be tested
         assert!(total_candidate_count > candidates.len());
-        debug!("tested_candidates: {}", total_candidate_count - candidates.len());
-        debug!("untested_candidates: {}", candidates.len());
+        debug!("test_candidates: tested_candidates: {}", total_candidate_count - candidates.len());
+        debug!("test_candidates: untested_candidates: {}", candidates.len());
 
         // HACK(matthewjasper) This is a closure so that we can let the test
         // create its blocks before the rest of the match. This currently
@@ -2274,4 +2274,75 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         debug!("declare_binding: vars={:?}", locals);
         self.var_indices.insert(var_id, locals);
     }
+
+    pub(crate) fn ast_let_else(
+        &mut self,
+        mut block: BasicBlock,
+        init: &Expr<'tcx>,
+        initializer_span: Span,
+        else_block: &Block,
+        visibility_scope: Option<SourceScope>,
+        remainder_span: Span,
+        pattern: &Pat<'tcx>,
+    ) -> BlockAnd<()> {
+        let scrutinee = unpack!(block = self.lower_scrutinee(block, init, initializer_span));
+        let pat = Pat { ty: init.ty, span: else_block.span, kind: Box::new(PatKind::Wild) };
+        let mut wildcard = Candidate::new(scrutinee.clone(), &pat, false);
+        self.declare_bindings(
+            visibility_scope,
+            remainder_span,
+            pattern,
+            ArmHasGuard(false),
+            Some((None, initializer_span)),
+        );
+        let mut candidate = Candidate::new(scrutinee.clone(), pattern, false);
+        let fake_borrow_temps = self.lower_match_tree(
+            block,
+            initializer_span,
+            pattern.span,
+            false,
+            &mut [&mut candidate, &mut wildcard],
+        );
+        // This block is for the matching case
+        let matching = self.bind_pattern(
+            self.source_info(pattern.span),
+            candidate,
+            None,
+            &fake_borrow_temps,
+            initializer_span,
+            None,
+            None,
+            None,
+        );
+        // This block is for the failure case
+        let failure = self.bind_pattern(
+            self.source_info(else_block.span),
+            wildcard,
+            None,
+            &fake_borrow_temps,
+            initializer_span,
+            None,
+            None,
+            None,
+        );
+        // This place is not really used because this destination place
+        // should never be used to take values at the end of the failure
+        // block.
+        let dummy_place = Place { local: RETURN_PLACE, projection: ty::List::empty() };
+        let failure_block;
+        unpack!(
+            failure_block = self.ast_block(
+                dummy_place,
+                failure,
+                else_block,
+                self.source_info(else_block.span),
+            )
+        );
+        self.cfg.terminate(
+            failure_block,
+            self.source_info(else_block.span),
+            TerminatorKind::Unreachable,
+        );
+        matching.unit()
+    }
 }
diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs
index e6d42af9817..7ae26cccb38 100644
--- a/compiler/rustc_mir_build/src/build/mod.rs
+++ b/compiler/rustc_mir_build/src/build/mod.rs
@@ -68,9 +68,10 @@ fn mir_build(tcx: TyCtxt<'_>, def: ty::WithOptConstParam<LocalDefId>) -> Body<'_
 
     // Figure out what primary body this item has.
     let (body_id, return_ty_span, span_with_body) = match tcx.hir().get(id) {
-        Node::Expr(hir::Expr { kind: hir::ExprKind::Closure { fn_decl, body, .. }, .. }) => {
-            (*body, fn_decl.output.span(), None)
-        }
+        Node::Expr(hir::Expr {
+            kind: hir::ExprKind::Closure(hir::Closure { fn_decl, body, .. }),
+            ..
+        }) => (*body, fn_decl.output.span(), None),
         Node::Item(hir::Item {
             kind: hir::ItemKind::Fn(hir::FnSig { decl, .. }, _, body_id),
             span,
diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs
index 8585199faaf..1f0d0ce04aa 100644
--- a/compiler/rustc_mir_build/src/check_unsafety.rs
+++ b/compiler/rustc_mir_build/src/check_unsafety.rs
@@ -431,16 +431,9 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
                 let lhs = &self.thir[lhs];
                 if let ty::Adt(adt_def, _) = lhs.ty.kind() && adt_def.is_union() {
                     if let Some((assigned_ty, assignment_span)) = self.assignment_info {
-                        // To avoid semver hazard, we only consider `Copy` and `ManuallyDrop` non-dropping.
-                        if !(assigned_ty
-                            .ty_adt_def()
-                            .map_or(false, |adt| adt.is_manually_drop())
-                            || assigned_ty
-                                .is_copy_modulo_regions(self.tcx.at(expr.span), self.param_env))
-                        {
-                            self.requires_unsafe(assignment_span, AssignToDroppingUnionField);
-                        } else {
-                            // write to non-drop union field, safe
+                        if assigned_ty.needs_drop(self.tcx, self.param_env) {
+                            // This would be unsafe, but should be outright impossible since we reject such unions.
+                            self.tcx.sess.delay_span_bug(assignment_span, format!("union fields that need dropping should be impossible: {assigned_ty}"));
                         }
                     } else {
                         self.requires_unsafe(expr.span, AccessToUnionField);
@@ -537,7 +530,6 @@ enum UnsafeOpKind {
     UseOfMutableStatic,
     UseOfExternStatic,
     DerefOfRawPointer,
-    AssignToDroppingUnionField,
     AccessToUnionField,
     MutationOfLayoutConstrainedField,
     BorrowOfLayoutConstrainedField,
@@ -555,7 +547,6 @@ impl UnsafeOpKind {
             UseOfMutableStatic => "use of mutable static",
             UseOfExternStatic => "use of extern static",
             DerefOfRawPointer => "dereference of raw pointer",
-            AssignToDroppingUnionField => "assignment to union field that might need dropping",
             AccessToUnionField => "access to union field",
             MutationOfLayoutConstrainedField => "mutation of layout constrained field",
             BorrowOfLayoutConstrainedField => {
@@ -600,11 +591,6 @@ impl UnsafeOpKind {
                 "raw pointers may be null, dangling or unaligned; they can violate aliasing rules \
                  and cause data races: all of these are undefined behavior",
             ),
-            AssignToDroppingUnionField => (
-                Cow::Borrowed(self.simple_description()),
-                "the previous content of the field will be dropped, which causes undefined \
-                 behavior if the field was not properly initialized",
-            ),
             AccessToUnionField => (
                 Cow::Borrowed(self.simple_description()),
                 "the field may not be properly initialized: using uninitialized data will cause \
@@ -632,7 +618,7 @@ impl UnsafeOpKind {
 
 pub fn check_unsafety<'tcx>(tcx: TyCtxt<'tcx>, def: ty::WithOptConstParam<LocalDefId>) {
     // THIR unsafeck is gated under `-Z thir-unsafeck`
-    if !tcx.sess.opts.debugging_opts.thir_unsafeck {
+    if !tcx.sess.opts.unstable_opts.thir_unsafeck {
         return;
     }
 
diff --git a/compiler/rustc_mir_build/src/lib.rs b/compiler/rustc_mir_build/src/lib.rs
index 11cd2a9aa4d..a7d8431b6c7 100644
--- a/compiler/rustc_mir_build/src/lib.rs
+++ b/compiler/rustc_mir_build/src/lib.rs
@@ -5,7 +5,7 @@
 #![feature(box_patterns)]
 #![feature(control_flow_enum)]
 #![feature(if_let_guard)]
-#![feature(let_chains)]
+#![cfg_attr(bootstrap, feature(let_chains))]
 #![feature(let_else)]
 #![feature(min_specialization)]
 #![feature(once_cell)]
diff --git a/compiler/rustc_mir_build/src/thir/cx/block.rs b/compiler/rustc_mir_build/src/thir/cx/block.rs
index 59750d5d0b8..dccaa61ed89 100644
--- a/compiler/rustc_mir_build/src/thir/cx/block.rs
+++ b/compiler/rustc_mir_build/src/thir/cx/block.rs
@@ -74,6 +74,8 @@ impl<'tcx> Cx<'tcx> {
                             )),
                         };
 
+                        let else_block = local.els.map(|els| self.mirror_block(els));
+
                         let mut pattern = self.pattern_from_hir(local.pat);
                         debug!(?pattern);
 
@@ -110,6 +112,7 @@ impl<'tcx> Cx<'tcx> {
                                 },
                                 pattern,
                                 initializer: local.init.map(|init| self.mirror_expr(init)),
+                                else_block,
                                 lint_level: LintLevel::Explicit(local.hir_id),
                             },
                             opt_destruction_scope: opt_dxn_ext,
diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
index 75fd156ebfd..3435f127c72 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
@@ -21,7 +21,7 @@ use rustc_session::lint::builtin::{
 };
 use rustc_session::Session;
 use rustc_span::source_map::Spanned;
-use rustc_span::{BytePos, DesugaringKind, ExpnKind, Span};
+use rustc_span::{BytePos, Span};
 
 pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: DefId) {
     let body_id = match def_id.as_local() {
@@ -77,6 +77,10 @@ impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, '_, 'tcx> {
 
     fn visit_local(&mut self, loc: &'tcx hir::Local<'tcx>) {
         intravisit::walk_local(self, loc);
+        let els = loc.els;
+        if let Some(init) = loc.init && els.is_some() {
+            self.check_let(&loc.pat, init, loc.span);
+        }
 
         let (msg, sp) = match loc.source {
             hir::LocalSource::Normal => ("local binding", Some(loc.span)),
@@ -84,7 +88,9 @@ impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, '_, 'tcx> {
             hir::LocalSource::AwaitDesugar => ("`await` future binding", None),
             hir::LocalSource::AssignDesugar(_) => ("destructuring assignment binding", None),
         };
-        self.check_irrefutable(&loc.pat, msg, sp);
+        if els.is_none() {
+            self.check_irrefutable(&loc.pat, msg, sp);
+        }
     }
 
     fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
@@ -945,7 +951,7 @@ fn adt_defined_here<'p, 'tcx>(
         let mut span: MultiSpan =
             if spans.is_empty() { def_span.into() } else { spans.clone().into() };
 
-        span.push_span_label(def_span, String::new());
+        span.push_span_label(def_span, "");
         for pat in spans {
             span.push_span_label(pat, "not covered");
         }
@@ -1125,17 +1131,16 @@ fn let_source_parent(tcx: TyCtxt<'_>, parent: HirId, pat_id: Option<HirId>) -> L
         }) if Some(*hir_id) == pat_id => {
             return LetSource::IfLetGuard;
         }
-        hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Let(..), span, .. }) => {
-            let expn_data = span.ctxt().outer_expn_data();
-            if let ExpnKind::Desugaring(DesugaringKind::LetElse) = expn_data.kind {
-                return LetSource::LetElse(expn_data.call_site);
-            }
-        }
         _ => {}
     }
 
     let parent_parent = hir.get_parent_node(parent);
     let parent_parent_node = hir.get(parent_parent);
+    if let hir::Node::Stmt(hir::Stmt { kind: hir::StmtKind::Local(_), span, .. }) =
+        parent_parent_node
+    {
+        return LetSource::LetElse(*span);
+    }
 
     let parent_parent_parent = hir.get_parent_node(parent_parent);
     let parent_parent_parent_parent = hir.get_parent_node(parent_parent_parent);
diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
index e32e0b11ba4..d6dd0f01794 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
@@ -120,37 +120,35 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
     }
 
     fn search_for_structural_match_violation(&self, ty: Ty<'tcx>) -> Option<String> {
-        traits::search_for_structural_match_violation(self.span, self.tcx(), ty, true).map(
-            |non_sm_ty| {
-                with_no_trimmed_paths!(match non_sm_ty.kind {
-                    traits::NonStructuralMatchTyKind::Adt(adt) => self.adt_derive_msg(adt),
-                    traits::NonStructuralMatchTyKind::Dynamic => {
-                        "trait objects cannot be used in patterns".to_string()
-                    }
-                    traits::NonStructuralMatchTyKind::Opaque => {
-                        "opaque types cannot be used in patterns".to_string()
-                    }
-                    traits::NonStructuralMatchTyKind::Closure => {
-                        "closures cannot be used in patterns".to_string()
-                    }
-                    traits::NonStructuralMatchTyKind::Generator => {
-                        "generators cannot be used in patterns".to_string()
-                    }
-                    traits::NonStructuralMatchTyKind::Float => {
-                        "floating-point numbers cannot be used in patterns".to_string()
-                    }
-                    traits::NonStructuralMatchTyKind::Param => {
-                        bug!("use of a constant whose type is a parameter inside a pattern")
-                    }
-                    traits::NonStructuralMatchTyKind::Projection => {
-                        bug!("use of a constant whose type is a projection inside a pattern")
-                    }
-                    traits::NonStructuralMatchTyKind::Foreign => {
-                        bug!("use of a value of a foreign type inside a pattern")
-                    }
-                })
-            },
-        )
+        traits::search_for_structural_match_violation(self.span, self.tcx(), ty).map(|non_sm_ty| {
+            with_no_trimmed_paths!(match non_sm_ty.kind() {
+                ty::Adt(adt, _) => self.adt_derive_msg(*adt),
+                ty::Dynamic(..) => {
+                    "trait objects cannot be used in patterns".to_string()
+                }
+                ty::Opaque(..) => {
+                    "opaque types cannot be used in patterns".to_string()
+                }
+                ty::Closure(..) => {
+                    "closures cannot be used in patterns".to_string()
+                }
+                ty::Generator(..) | ty::GeneratorWitness(..) => {
+                    "generators cannot be used in patterns".to_string()
+                }
+                ty::Float(..) => {
+                    "floating-point numbers cannot be used in patterns".to_string()
+                }
+                ty::FnPtr(..) => {
+                    "function pointers cannot be used in patterns".to_string()
+                }
+                ty::RawPtr(..) => {
+                    "raw pointers cannot be used in patterns".to_string()
+                }
+                _ => {
+                    bug!("use of a value of `{non_sm_ty}` inside a pattern")
+                }
+            })
+        })
     }
 
     fn type_marked_structural(&self, ty: Ty<'tcx>) -> bool {
diff --git a/compiler/rustc_mir_dataflow/src/framework/engine.rs b/compiler/rustc_mir_dataflow/src/framework/engine.rs
index 180376d648a..f374658ceb6 100644
--- a/compiler/rustc_mir_dataflow/src/framework/engine.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/engine.rs
@@ -289,7 +289,7 @@ where
             io::BufWriter::new(fs::File::create(&path)?)
         }
 
-        None if tcx.sess.opts.debugging_opts.dump_mir_dataflow
+        None if tcx.sess.opts.unstable_opts.dump_mir_dataflow
             && dump_enabled(tcx, A::NAME, def_id) =>
         {
             create_dump_file(
@@ -314,8 +314,8 @@ where
 
     let graphviz = graphviz::Formatter::new(body, results, style);
     let mut render_opts =
-        vec![dot::RenderOption::Fontname(tcx.sess.opts.debugging_opts.graphviz_font.clone())];
-    if tcx.sess.opts.debugging_opts.graphviz_dark_mode {
+        vec![dot::RenderOption::Fontname(tcx.sess.opts.unstable_opts.graphviz_font.clone())];
+    if tcx.sess.opts.unstable_opts.graphviz_dark_mode {
         render_opts.push(dot::RenderOption::DarkTheme);
     }
     dot::render_opts(&graphviz, &mut buf, &render_opts)?;
diff --git a/compiler/rustc_mir_dataflow/src/impls/mod.rs b/compiler/rustc_mir_dataflow/src/impls/mod.rs
index af6a1bb1545..fd1e492779f 100644
--- a/compiler/rustc_mir_dataflow/src/impls/mod.rs
+++ b/compiler/rustc_mir_dataflow/src/impls/mod.rs
@@ -317,7 +317,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
             Self::update_bits(trans, path, s)
         });
 
-        if !self.tcx.sess.opts.debugging_opts.precise_enum_drop_elaboration {
+        if !self.tcx.sess.opts.unstable_opts.precise_enum_drop_elaboration {
             return;
         }
 
@@ -340,7 +340,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
             Self::update_bits(trans, path, s)
         });
 
-        if !self.tcx.sess.opts.debugging_opts.precise_enum_drop_elaboration {
+        if !self.tcx.sess.opts.unstable_opts.precise_enum_drop_elaboration {
             return;
         }
 
@@ -379,7 +379,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
         discr: &mir::Operand<'tcx>,
         edge_effects: &mut impl SwitchIntEdgeEffects<G>,
     ) {
-        if !self.tcx.sess.opts.debugging_opts.precise_enum_drop_elaboration {
+        if !self.tcx.sess.opts.unstable_opts.precise_enum_drop_elaboration {
             return;
         }
 
@@ -495,7 +495,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
         discr: &mir::Operand<'tcx>,
         edge_effects: &mut impl SwitchIntEdgeEffects<G>,
     ) {
-        if !self.tcx.sess.opts.debugging_opts.precise_enum_drop_elaboration {
+        if !self.tcx.sess.opts.unstable_opts.precise_enum_drop_elaboration {
             return;
         }
 
diff --git a/compiler/rustc_mir_dataflow/src/storage.rs b/compiler/rustc_mir_dataflow/src/storage.rs
index 566c9d2d505..c909648ea01 100644
--- a/compiler/rustc_mir_dataflow/src/storage.rs
+++ b/compiler/rustc_mir_dataflow/src/storage.rs
@@ -4,9 +4,6 @@ use rustc_middle::mir::{self, Local};
 /// The set of locals in a MIR body that do not have `StorageLive`/`StorageDead` annotations.
 ///
 /// These locals have fixed storage for the duration of the body.
-//
-// FIXME: Currently, we need to traverse the entire MIR to compute this. We should instead store it
-// as a field in the `LocalDecl` for each `Local`.
 pub fn always_storage_live_locals(body: &mir::Body<'_>) -> BitSet<Local> {
     let mut always_live_locals = BitSet::new_filled(body.local_decls.len());
 
diff --git a/compiler/rustc_mir_dataflow/src/un_derefer.rs b/compiler/rustc_mir_dataflow/src/un_derefer.rs
index 04f626b49e8..7e6e25cc603 100644
--- a/compiler/rustc_mir_dataflow/src/un_derefer.rs
+++ b/compiler/rustc_mir_dataflow/src/un_derefer.rs
@@ -1,4 +1,4 @@
-use rustc_data_structures::stable_map::FxHashMap;
+use rustc_data_structures::fx::FxHashMap;
 use rustc_middle::mir::*;
 use rustc_middle::ty::TyCtxt;
 
diff --git a/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs b/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs
index 2c8389e532d..2502e8b603c 100644
--- a/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs
+++ b/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs
@@ -75,7 +75,7 @@ impl<'tcx> MirPass<'tcx> for AbortUnwindingCalls {
                     layout::fn_can_unwind(tcx, fn_def_id, sig.abi())
                 }
                 TerminatorKind::Drop { .. } | TerminatorKind::DropAndReplace { .. } => {
-                    tcx.sess.opts.debugging_opts.panic_in_drop == PanicStrategy::Unwind
+                    tcx.sess.opts.unstable_opts.panic_in_drop == PanicStrategy::Unwind
                         && layout::fn_can_unwind(tcx, None, Abi::Rust)
                 }
                 TerminatorKind::Assert { .. } | TerminatorKind::FalseUnwind { .. } => {
diff --git a/compiler/rustc_mir_transform/src/add_retag.rs b/compiler/rustc_mir_transform/src/add_retag.rs
index 13ec1e7bb72..9c5896c4e4a 100644
--- a/compiler/rustc_mir_transform/src/add_retag.rs
+++ b/compiler/rustc_mir_transform/src/add_retag.rs
@@ -15,22 +15,9 @@ pub struct AddRetag;
 /// (Concurrent accesses by other threads are no problem as these are anyway non-atomic
 /// copies.  Data races are UB.)
 fn is_stable(place: PlaceRef<'_>) -> bool {
-    place.projection.iter().all(|elem| {
-        match elem {
-            // Which place this evaluates to can change with any memory write,
-            // so cannot assume this to be stable.
-            ProjectionElem::Deref => false,
-            // Array indices are interesting, but MIR building generates a *fresh*
-            // temporary for every array access, so the index cannot be changed as
-            // a side-effect.
-            ProjectionElem::Index { .. } |
-            // The rest is completely boring, they just offset by a constant.
-            ProjectionElem::Field { .. } |
-            ProjectionElem::ConstantIndex { .. } |
-            ProjectionElem::Subslice { .. } |
-            ProjectionElem::Downcast { .. } => true,
-        }
-    })
+    // Which place this evaluates to can change with any memory write,
+    // so cannot assume deref to be stable.
+    !place.has_deref()
 }
 
 /// Determine whether this type may contain a reference (or box), and thus needs retagging.
@@ -72,7 +59,7 @@ fn may_contain_reference<'tcx>(ty: Ty<'tcx>, depth: u32, tcx: TyCtxt<'tcx>) -> b
 
 impl<'tcx> MirPass<'tcx> for AddRetag {
     fn is_enabled(&self, sess: &rustc_session::Session) -> bool {
-        sess.opts.debugging_opts.mir_emit_retag
+        sess.opts.unstable_opts.mir_emit_retag
     }
 
     fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
@@ -91,11 +78,8 @@ impl<'tcx> MirPass<'tcx> for AddRetag {
         };
         let place_base_raw = |place: &Place<'tcx>| {
             // If this is a `Deref`, get the type of what we are deref'ing.
-            let deref_base =
-                place.projection.iter().rposition(|p| matches!(p, ProjectionElem::Deref));
-            if let Some(deref_base) = deref_base {
-                let base_proj = &place.projection[..deref_base];
-                let ty = Place::ty_from(place.local, base_proj, &*local_decls, tcx).ty;
+            if place.has_deref() {
+                let ty = &local_decls[place.local].ty;
                 ty.is_unsafe_ptr()
             } else {
                 // Not a deref, and thus not raw.
diff --git a/compiler/rustc_mir_transform/src/check_packed_ref.rs b/compiler/rustc_mir_transform/src/check_packed_ref.rs
index 2eb38941f1a..3b7ba3f9a67 100644
--- a/compiler/rustc_mir_transform/src/check_packed_ref.rs
+++ b/compiler/rustc_mir_transform/src/check_packed_ref.rs
@@ -36,13 +36,16 @@ fn unsafe_derive_on_repr_packed(tcx: TyCtxt<'_>, def_id: LocalDefId) {
     tcx.struct_span_lint_hir(UNALIGNED_REFERENCES, lint_hir_id, tcx.def_span(def_id), |lint| {
         // FIXME: when we make this a hard error, this should have its
         // own error code.
-        let message = if tcx.generics_of(def_id).own_requires_monomorphization() {
-            "`#[derive]` can't be used on a `#[repr(packed)]` struct with \
-             type or const parameters (error E0133)"
+        let extra = if tcx.generics_of(def_id).own_requires_monomorphization() {
+            "with type or const parameters"
         } else {
-            "`#[derive]` can't be used on a `#[repr(packed)]` struct that \
-             does not derive Copy (error E0133)"
+            "that does not derive `Copy`"
         };
+        let message = format!(
+            "`{}` can't be derived on this `#[repr(packed)]` struct {}",
+            tcx.item_name(tcx.trait_id_of_impl(def_id.to_def_id()).expect("derived trait name")),
+            extra
+        );
         lint.build(message).emit();
     });
 }
diff --git a/compiler/rustc_mir_transform/src/check_unsafety.rs b/compiler/rustc_mir_transform/src/check_unsafety.rs
index 1f73b7da815..a2ad96cfc16 100644
--- a/compiler/rustc_mir_transform/src/check_unsafety.rs
+++ b/compiler/rustc_mir_transform/src/check_unsafety.rs
@@ -219,22 +219,12 @@ impl<'tcx> Visitor<'tcx> for UnsafetyChecker<'_, 'tcx> {
                     // We have to check the actual type of the assignment, as that determines if the
                     // old value is being dropped.
                     let assigned_ty = place.ty(&self.body.local_decls, self.tcx).ty;
-                    // To avoid semver hazard, we only consider `Copy` and `ManuallyDrop` non-dropping.
-                    let manually_drop = assigned_ty
-                        .ty_adt_def()
-                        .map_or(false, |adt_def| adt_def.is_manually_drop());
-                    let nodrop = manually_drop
-                        || assigned_ty.is_copy_modulo_regions(
-                            self.tcx.at(self.source_info.span),
-                            self.param_env,
+                    if assigned_ty.needs_drop(self.tcx, self.param_env) {
+                        // This would be unsafe, but should be outright impossible since we reject such unions.
+                        self.tcx.sess.delay_span_bug(
+                            self.source_info.span,
+                            format!("union fields that need dropping should be impossible: {assigned_ty}")
                         );
-                    if !nodrop {
-                        self.require_unsafe(
-                            UnsafetyViolationKind::General,
-                            UnsafetyViolationDetails::AssignToDroppingUnionField,
-                        );
-                    } else {
-                        // write to non-drop union field, safe
                     }
                 } else {
                     self.require_unsafe(
diff --git a/compiler/rustc_mir_transform/src/const_debuginfo.rs b/compiler/rustc_mir_transform/src/const_debuginfo.rs
index 48aea61b191..6f0ae4f07ab 100644
--- a/compiler/rustc_mir_transform/src/const_debuginfo.rs
+++ b/compiler/rustc_mir_transform/src/const_debuginfo.rs
@@ -16,7 +16,7 @@ pub struct ConstDebugInfo;
 
 impl<'tcx> MirPass<'tcx> for ConstDebugInfo {
     fn is_enabled(&self, sess: &rustc_session::Session) -> bool {
-        sess.opts.debugging_opts.unsound_mir_opts && sess.mir_opt_level() > 0
+        sess.opts.unstable_opts.unsound_mir_opts && sess.mir_opt_level() > 0
     }
 
     fn run_pass(&self, _tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
diff --git a/compiler/rustc_mir_transform/src/const_prop.rs b/compiler/rustc_mir_transform/src/const_prop.rs
index acd9e605353..85ad6b8f2fe 100644
--- a/compiler/rustc_mir_transform/src/const_prop.rs
+++ b/compiler/rustc_mir_transform/src/const_prop.rs
@@ -155,18 +155,18 @@ impl<'tcx> MirPass<'tcx> for ConstProp {
     }
 }
 
-struct ConstPropMachine<'mir, 'tcx> {
+pub struct ConstPropMachine<'mir, 'tcx> {
     /// The virtual call stack.
     stack: Vec<Frame<'mir, 'tcx>>,
     /// `OnlyInsideOwnBlock` locals that were written in the current block get erased at the end.
-    written_only_inside_own_block_locals: FxHashSet<Local>,
+    pub written_only_inside_own_block_locals: FxHashSet<Local>,
     /// Locals that need to be cleared after every block terminates.
-    only_propagate_inside_block_locals: BitSet<Local>,
-    can_const_prop: IndexVec<Local, ConstPropMode>,
+    pub only_propagate_inside_block_locals: BitSet<Local>,
+    pub can_const_prop: IndexVec<Local, ConstPropMode>,
 }
 
 impl ConstPropMachine<'_, '_> {
-    fn new(
+    pub fn new(
         only_propagate_inside_block_locals: BitSet<Local>,
         can_const_prop: IndexVec<Local, ConstPropMode>,
     ) -> Self {
@@ -234,9 +234,9 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx>
     }
 
     fn access_local<'a>(
-        frame: &'a Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>,
+        frame: &'a Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>,
         local: Local,
-    ) -> InterpResult<'tcx, &'a interpret::Operand<Self::PointerTag>> {
+    ) -> InterpResult<'tcx, &'a interpret::Operand<Self::Provenance>> {
         let l = &frame.locals[local];
 
         if matches!(
@@ -255,7 +255,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx>
         ecx: &'a mut InterpCx<'mir, 'tcx, Self>,
         frame: usize,
         local: Local,
-    ) -> InterpResult<'tcx, &'a mut interpret::Operand<Self::PointerTag>> {
+    ) -> InterpResult<'tcx, &'a mut interpret::Operand<Self::Provenance>> {
         if ecx.machine.can_const_prop[local] == ConstPropMode::NoPropagation {
             throw_machine_stop_str!("tried to write to a local that is marked as not propagatable")
         }
@@ -274,7 +274,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx>
         _tcx: TyCtxt<'tcx>,
         _machine: &Self,
         _alloc_id: AllocId,
-        alloc: ConstAllocation<'tcx, Self::PointerTag, Self::AllocExtra>,
+        alloc: ConstAllocation<'tcx, Self::Provenance, Self::AllocExtra>,
         _static_def_id: Option<DefId>,
         is_write: bool,
     ) -> InterpResult<'tcx> {
@@ -309,14 +309,14 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx>
     #[inline(always)]
     fn stack<'a>(
         ecx: &'a InterpCx<'mir, 'tcx, Self>,
-    ) -> &'a [Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>] {
+    ) -> &'a [Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>] {
         &ecx.machine.stack
     }
 
     #[inline(always)]
     fn stack_mut<'a>(
         ecx: &'a mut InterpCx<'mir, 'tcx, Self>,
-    ) -> &'a mut Vec<Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>> {
+    ) -> &'a mut Vec<Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>> {
         &mut ecx.machine.stack
     }
 }
@@ -516,7 +516,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
         let l = self.use_ecx(|this| this.ecx.read_immediate(&this.ecx.eval_operand(left, None)?));
         // Check for exceeding shifts *even if* we cannot evaluate the LHS.
         if op == BinOp::Shr || op == BinOp::Shl {
-            let r = r?;
+            let r = r.clone()?;
             // We need the type of the LHS. We cannot use `place_layout` as that is the type
             // of the result, which for checked binops is not the same!
             let left_ty = left.ty(self.local_decls, self.tcx);
@@ -816,7 +816,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
 
 /// The mode that `ConstProp` is allowed to run in for a given `Local`.
 #[derive(Clone, Copy, Debug, PartialEq)]
-enum ConstPropMode {
+pub enum ConstPropMode {
     /// The `Local` can be propagated into and reads of this `Local` can also be propagated.
     FullConstProp,
     /// The `Local` can only be propagated into and from its own block.
@@ -828,7 +828,7 @@ enum ConstPropMode {
     NoPropagation,
 }
 
-struct CanConstProp {
+pub struct CanConstProp {
     can_const_prop: IndexVec<Local, ConstPropMode>,
     // False at the beginning. Once set, no more assignments are allowed to that local.
     found_assignment: BitSet<Local>,
@@ -838,7 +838,7 @@ struct CanConstProp {
 
 impl CanConstProp {
     /// Returns true if `local` can be propagated
-    fn check<'tcx>(
+    pub fn check<'tcx>(
         tcx: TyCtxt<'tcx>,
         param_env: ParamEnv<'tcx>,
         body: &Body<'tcx>,
diff --git a/compiler/rustc_mir_transform/src/const_prop_lint.rs b/compiler/rustc_mir_transform/src/const_prop_lint.rs
index 49db140c474..3ae6a88a140 100644
--- a/compiler/rustc_mir_transform/src/const_prop_lint.rs
+++ b/compiler/rustc_mir_transform/src/const_prop_lint.rs
@@ -1,19 +1,24 @@
 //! Propagates constants for early reporting of statically known
 //! assertion failures
 
-use std::cell::Cell;
-
-use rustc_ast::Mutability;
-use rustc_data_structures::fx::FxHashSet;
+use crate::const_prop::CanConstProp;
+use crate::const_prop::ConstPropMachine;
+use crate::const_prop::ConstPropMode;
+use crate::MirLint;
+use rustc_const_eval::const_eval::ConstEvalErr;
+use rustc_const_eval::interpret::{
+    self, InterpCx, InterpResult, LocalState, LocalValue, MemoryKind, OpTy, Scalar,
+    ScalarMaybeUninit, StackPopCleanup,
+};
 use rustc_hir::def::DefKind;
 use rustc_hir::HirId;
 use rustc_index::bit_set::BitSet;
 use rustc_index::vec::IndexVec;
-use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
+use rustc_middle::mir::visit::Visitor;
 use rustc_middle::mir::{
-    AssertKind, BasicBlock, BinOp, Body, Constant, ConstantKind, Local, LocalDecl, LocalKind,
-    Location, Operand, Place, Rvalue, SourceInfo, SourceScope, SourceScopeData, Statement,
-    StatementKind, Terminator, TerminatorKind, UnOp, RETURN_PLACE,
+    AssertKind, BinOp, Body, Constant, ConstantKind, Local, LocalDecl, Location, Operand, Place,
+    Rvalue, SourceInfo, SourceScope, SourceScopeData, Statement, StatementKind, Terminator,
+    TerminatorKind, UnOp, RETURN_PLACE,
 };
 use rustc_middle::ty::layout::{LayoutError, LayoutOf, LayoutOfHelpers, TyAndLayout};
 use rustc_middle::ty::subst::{InternalSubsts, Subst};
@@ -22,42 +27,15 @@ use rustc_middle::ty::{
     TypeVisitable,
 };
 use rustc_session::lint;
-use rustc_span::{def_id::DefId, Span};
+use rustc_span::Span;
 use rustc_target::abi::{HasDataLayout, Size, TargetDataLayout};
-use rustc_target::spec::abi::Abi as CallAbi;
 use rustc_trait_selection::traits;
-
-use crate::MirLint;
-use rustc_const_eval::const_eval::ConstEvalErr;
-use rustc_const_eval::interpret::{
-    self, compile_time_machine, AllocId, ConstAllocation, Frame, ImmTy, InterpCx, InterpResult,
-    LocalState, LocalValue, MemoryKind, OpTy, PlaceTy, Pointer, Scalar, ScalarMaybeUninit,
-    StackPopCleanup, StackPopUnwind,
-};
+use std::cell::Cell;
 
 /// The maximum number of bytes that we'll allocate space for a local or the return value.
 /// Needed for #66397, because otherwise we eval into large places and that can cause OOM or just
 /// Severely regress performance.
 const MAX_ALLOC_LIMIT: u64 = 1024;
-
-/// Macro for machine-specific `InterpError` without allocation.
-/// (These will never be shown to the user, but they help diagnose ICEs.)
-macro_rules! throw_machine_stop_str {
-    ($($tt:tt)*) => {{
-        // We make a new local type for it. The type itself does not carry any information,
-        // but its vtable (for the `MachineStopType` trait) does.
-        struct Zst;
-        // Printing this type shows the desired string.
-        impl std::fmt::Display for Zst {
-            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-                write!(f, $($tt)*)
-            }
-        }
-        impl rustc_middle::mir::interpret::MachineStopType for Zst {}
-        throw_machine_stop!(Zst)
-    }};
-}
-
 pub struct ConstProp;
 
 impl<'tcx> MirLint<'tcx> for ConstProp {
@@ -151,172 +129,6 @@ impl<'tcx> MirLint<'tcx> for ConstProp {
     }
 }
 
-struct ConstPropMachine<'mir, 'tcx> {
-    /// The virtual call stack.
-    stack: Vec<Frame<'mir, 'tcx>>,
-    /// `OnlyInsideOwnBlock` locals that were written in the current block get erased at the end.
-    written_only_inside_own_block_locals: FxHashSet<Local>,
-    /// Locals that need to be cleared after every block terminates.
-    only_propagate_inside_block_locals: BitSet<Local>,
-    can_const_prop: IndexVec<Local, ConstPropMode>,
-}
-
-impl ConstPropMachine<'_, '_> {
-    fn new(
-        only_propagate_inside_block_locals: BitSet<Local>,
-        can_const_prop: IndexVec<Local, ConstPropMode>,
-    ) -> Self {
-        Self {
-            stack: Vec::new(),
-            written_only_inside_own_block_locals: Default::default(),
-            only_propagate_inside_block_locals,
-            can_const_prop,
-        }
-    }
-}
-
-impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx> {
-    compile_time_machine!(<'mir, 'tcx>);
-    const PANIC_ON_ALLOC_FAIL: bool = true; // all allocations are small (see `MAX_ALLOC_LIMIT`)
-
-    type MemoryKind = !;
-
-    fn load_mir(
-        _ecx: &InterpCx<'mir, 'tcx, Self>,
-        _instance: ty::InstanceDef<'tcx>,
-    ) -> InterpResult<'tcx, &'tcx Body<'tcx>> {
-        throw_machine_stop_str!("calling functions isn't supported in ConstProp")
-    }
-
-    fn find_mir_or_eval_fn(
-        _ecx: &mut InterpCx<'mir, 'tcx, Self>,
-        _instance: ty::Instance<'tcx>,
-        _abi: CallAbi,
-        _args: &[OpTy<'tcx>],
-        _destination: &PlaceTy<'tcx>,
-        _target: Option<BasicBlock>,
-        _unwind: StackPopUnwind,
-    ) -> InterpResult<'tcx, Option<(&'mir Body<'tcx>, ty::Instance<'tcx>)>> {
-        Ok(None)
-    }
-
-    fn call_intrinsic(
-        _ecx: &mut InterpCx<'mir, 'tcx, Self>,
-        _instance: ty::Instance<'tcx>,
-        _args: &[OpTy<'tcx>],
-        _destination: &PlaceTy<'tcx>,
-        _target: Option<BasicBlock>,
-        _unwind: StackPopUnwind,
-    ) -> InterpResult<'tcx> {
-        throw_machine_stop_str!("calling intrinsics isn't supported in ConstProp")
-    }
-
-    fn assert_panic(
-        _ecx: &mut InterpCx<'mir, 'tcx, Self>,
-        _msg: &rustc_middle::mir::AssertMessage<'tcx>,
-        _unwind: Option<rustc_middle::mir::BasicBlock>,
-    ) -> InterpResult<'tcx> {
-        bug!("panics terminators are not evaluated in ConstProp")
-    }
-
-    fn binary_ptr_op(
-        _ecx: &InterpCx<'mir, 'tcx, Self>,
-        _bin_op: BinOp,
-        _left: &ImmTy<'tcx>,
-        _right: &ImmTy<'tcx>,
-    ) -> InterpResult<'tcx, (Scalar, bool, Ty<'tcx>)> {
-        // We can't do this because aliasing of memory can differ between const eval and llvm
-        throw_machine_stop_str!("pointer arithmetic or comparisons aren't supported in ConstProp")
-    }
-
-    fn access_local<'a>(
-        frame: &'a Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>,
-        local: Local,
-    ) -> InterpResult<'tcx, &'a interpret::Operand<Self::PointerTag>> {
-        let l = &frame.locals[local];
-
-        if matches!(
-            l.value,
-            LocalValue::Live(interpret::Operand::Immediate(interpret::Immediate::Uninit))
-        ) {
-            // For us "uninit" means "we don't know its value, might be initiailized or not".
-            // So stop here.
-            throw_machine_stop_str!("tried to access a local with unknown value")
-        }
-
-        l.access()
-    }
-
-    fn access_local_mut<'a>(
-        ecx: &'a mut InterpCx<'mir, 'tcx, Self>,
-        frame: usize,
-        local: Local,
-    ) -> InterpResult<'tcx, &'a mut interpret::Operand<Self::PointerTag>> {
-        if ecx.machine.can_const_prop[local] == ConstPropMode::NoPropagation {
-            throw_machine_stop_str!("tried to write to a local that is marked as not propagatable")
-        }
-        if frame == 0 && ecx.machine.only_propagate_inside_block_locals.contains(local) {
-            trace!(
-                "mutating local {:?} which is restricted to its block. \
-                Will remove it from const-prop after block is finished.",
-                local
-            );
-            ecx.machine.written_only_inside_own_block_locals.insert(local);
-        }
-        ecx.machine.stack[frame].locals[local].access_mut()
-    }
-
-    fn before_access_global(
-        _tcx: TyCtxt<'tcx>,
-        _machine: &Self,
-        _alloc_id: AllocId,
-        alloc: ConstAllocation<'tcx, Self::PointerTag, Self::AllocExtra>,
-        _static_def_id: Option<DefId>,
-        is_write: bool,
-    ) -> InterpResult<'tcx> {
-        if is_write {
-            throw_machine_stop_str!("can't write to global");
-        }
-        // If the static allocation is mutable, then we can't const prop it as its content
-        // might be different at runtime.
-        if alloc.inner().mutability == Mutability::Mut {
-            throw_machine_stop_str!("can't access mutable globals in ConstProp");
-        }
-
-        Ok(())
-    }
-
-    #[inline(always)]
-    fn expose_ptr(
-        _ecx: &mut InterpCx<'mir, 'tcx, Self>,
-        _ptr: Pointer<AllocId>,
-    ) -> InterpResult<'tcx> {
-        throw_machine_stop_str!("exposing pointers isn't supported in ConstProp")
-    }
-
-    #[inline(always)]
-    fn init_frame_extra(
-        _ecx: &mut InterpCx<'mir, 'tcx, Self>,
-        frame: Frame<'mir, 'tcx>,
-    ) -> InterpResult<'tcx, Frame<'mir, 'tcx>> {
-        Ok(frame)
-    }
-
-    #[inline(always)]
-    fn stack<'a>(
-        ecx: &'a InterpCx<'mir, 'tcx, Self>,
-    ) -> &'a [Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>] {
-        &ecx.machine.stack
-    }
-
-    #[inline(always)]
-    fn stack_mut<'a>(
-        ecx: &'a mut InterpCx<'mir, 'tcx, Self>,
-    ) -> &'a mut Vec<Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>> {
-        &mut ecx.machine.stack
-    }
-}
-
 /// Finds optimization opportunities on the MIR.
 struct ConstPropagator<'mir, 'tcx> {
     ecx: InterpCx<'mir, 'tcx, ConstPropMachine<'mir, 'tcx>>,
@@ -584,7 +396,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
         });
         // Check for exceeding shifts *even if* we cannot evaluate the LHS.
         if op == BinOp::Shr || op == BinOp::Shl {
-            let r = r?;
+            let r = r.clone()?;
             // We need the type of the LHS. We cannot use `place_layout` as that is the type
             // of the result, which for checked binops is not the same!
             let left_ty = left.ty(self.local_decls, self.tcx);
@@ -616,10 +428,10 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
             }
         }
 
-        if let (Some(l), Some(r)) = (&l, &r) {
+        if let (Some(l), Some(r)) = (l, r) {
             // The remaining operators are handled through `overflowing_binary_op`.
             if self.use_ecx(source_info, |this| {
-                let (_res, overflow, _ty) = this.ecx.overflowing_binary_op(op, l, r)?;
+                let (_res, overflow, _ty) = this.ecx.overflowing_binary_op(op, &l, &r)?;
                 Ok(overflow)
             })? {
                 self.report_assert_as_lint(
@@ -711,139 +523,6 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
     }
 }
 
-/// The mode that `ConstProp` is allowed to run in for a given `Local`.
-#[derive(Clone, Copy, Debug, PartialEq)]
-enum ConstPropMode {
-    /// The `Local` can be propagated into and reads of this `Local` can also be propagated.
-    FullConstProp,
-    /// The `Local` can only be propagated into and from its own block.
-    OnlyInsideOwnBlock,
-    /// The `Local` can be propagated into but reads cannot be propagated.
-    OnlyPropagateInto,
-    /// The `Local` cannot be part of propagation at all. Any statement
-    /// referencing it either for reading or writing will not get propagated.
-    NoPropagation,
-}
-
-struct CanConstProp {
-    can_const_prop: IndexVec<Local, ConstPropMode>,
-    // False at the beginning. Once set, no more assignments are allowed to that local.
-    found_assignment: BitSet<Local>,
-    // Cache of locals' information
-    local_kinds: IndexVec<Local, LocalKind>,
-}
-
-impl CanConstProp {
-    /// Returns true if `local` can be propagated
-    fn check<'tcx>(
-        tcx: TyCtxt<'tcx>,
-        param_env: ParamEnv<'tcx>,
-        body: &Body<'tcx>,
-    ) -> IndexVec<Local, ConstPropMode> {
-        let mut cpv = CanConstProp {
-            can_const_prop: IndexVec::from_elem(ConstPropMode::FullConstProp, &body.local_decls),
-            found_assignment: BitSet::new_empty(body.local_decls.len()),
-            local_kinds: IndexVec::from_fn_n(
-                |local| body.local_kind(local),
-                body.local_decls.len(),
-            ),
-        };
-        for (local, val) in cpv.can_const_prop.iter_enumerated_mut() {
-            let ty = body.local_decls[local].ty;
-            match tcx.layout_of(param_env.and(ty)) {
-                Ok(layout) if layout.size < Size::from_bytes(MAX_ALLOC_LIMIT) => {}
-                // Either the layout fails to compute, then we can't use this local anyway
-                // or the local is too large, then we don't want to.
-                _ => {
-                    *val = ConstPropMode::NoPropagation;
-                    continue;
-                }
-            }
-            // Cannot use args at all
-            // Cannot use locals because if x < y { y - x } else { x - y } would
-            //        lint for x != y
-            // FIXME(oli-obk): lint variables until they are used in a condition
-            // FIXME(oli-obk): lint if return value is constant
-            if cpv.local_kinds[local] == LocalKind::Arg {
-                *val = ConstPropMode::OnlyPropagateInto;
-                trace!(
-                    "local {:?} can't be const propagated because it's a function argument",
-                    local
-                );
-            } else if cpv.local_kinds[local] == LocalKind::Var {
-                *val = ConstPropMode::OnlyInsideOwnBlock;
-                trace!(
-                    "local {:?} will only be propagated inside its block, because it's a user variable",
-                    local
-                );
-            }
-        }
-        cpv.visit_body(&body);
-        cpv.can_const_prop
-    }
-}
-
-impl Visitor<'_> for CanConstProp {
-    fn visit_local(&mut self, local: Local, context: PlaceContext, _: Location) {
-        use rustc_middle::mir::visit::PlaceContext::*;
-        match context {
-            // Projections are fine, because `&mut foo.x` will be caught by
-            // `MutatingUseContext::Borrow` elsewhere.
-            MutatingUse(MutatingUseContext::Projection)
-            // These are just stores, where the storing is not propagatable, but there may be later
-            // mutations of the same local via `Store`
-            | MutatingUse(MutatingUseContext::Call)
-            | MutatingUse(MutatingUseContext::AsmOutput)
-            | MutatingUse(MutatingUseContext::Deinit)
-            // Actual store that can possibly even propagate a value
-            | MutatingUse(MutatingUseContext::SetDiscriminant)
-            | MutatingUse(MutatingUseContext::Store) => {
-                if !self.found_assignment.insert(local) {
-                    match &mut self.can_const_prop[local] {
-                        // If the local can only get propagated in its own block, then we don't have
-                        // to worry about multiple assignments, as we'll nuke the const state at the
-                        // end of the block anyway, and inside the block we overwrite previous
-                        // states as applicable.
-                        ConstPropMode::OnlyInsideOwnBlock => {}
-                        ConstPropMode::NoPropagation => {}
-                        ConstPropMode::OnlyPropagateInto => {}
-                        other @ ConstPropMode::FullConstProp => {
-                            trace!(
-                                "local {:?} can't be propagated because of multiple assignments. Previous state: {:?}",
-                                local, other,
-                            );
-                            *other = ConstPropMode::OnlyInsideOwnBlock;
-                        }
-                    }
-                }
-            }
-            // Reading constants is allowed an arbitrary number of times
-            NonMutatingUse(NonMutatingUseContext::Copy)
-            | NonMutatingUse(NonMutatingUseContext::Move)
-            | NonMutatingUse(NonMutatingUseContext::Inspect)
-            | NonMutatingUse(NonMutatingUseContext::Projection)
-            | NonUse(_) => {}
-
-            // These could be propagated with a smarter analysis or just some careful thinking about
-            // whether they'd be fine right now.
-            MutatingUse(MutatingUseContext::Yield)
-            | MutatingUse(MutatingUseContext::Drop)
-            | MutatingUse(MutatingUseContext::Retag)
-            // These can't ever be propagated under any scheme, as we can't reason about indirect
-            // mutation.
-            | NonMutatingUse(NonMutatingUseContext::SharedBorrow)
-            | NonMutatingUse(NonMutatingUseContext::ShallowBorrow)
-            | NonMutatingUse(NonMutatingUseContext::UniqueBorrow)
-            | NonMutatingUse(NonMutatingUseContext::AddressOf)
-            | MutatingUse(MutatingUseContext::Borrow)
-            | MutatingUse(MutatingUseContext::AddressOf) => {
-                trace!("local {:?} can't be propagaged because it's used: {:?}", local, context);
-                self.can_const_prop[local] = ConstPropMode::NoPropagation;
-            }
-        }
-    }
-}
-
 impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> {
     fn visit_body(&mut self, body: &Body<'tcx>) {
         for (bb, data) in body.basic_blocks().iter_enumerated() {
diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs
index 88ad5b7ef14..2619626a567 100644
--- a/compiler/rustc_mir_transform/src/coverage/mod.rs
+++ b/compiler/rustc_mir_transform/src/coverage/mod.rs
@@ -160,8 +160,8 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
         let mut debug_used_expressions = debug::UsedExpressions::new();
 
         let dump_mir = dump_enabled(tcx, self.pass_name, def_id);
-        let dump_graphviz = dump_mir && tcx.sess.opts.debugging_opts.dump_mir_graphviz;
-        let dump_spanview = dump_mir && tcx.sess.opts.debugging_opts.dump_mir_spanview.is_some();
+        let dump_graphviz = dump_mir && tcx.sess.opts.unstable_opts.dump_mir_graphviz;
+        let dump_spanview = dump_mir && tcx.sess.opts.unstable_opts.dump_mir_spanview.is_some();
 
         if dump_graphviz {
             graphviz_data.enable();
diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs
index 84c7aada5e5..33572068f5c 100644
--- a/compiler/rustc_mir_transform/src/dest_prop.rs
+++ b/compiler/rustc_mir_transform/src/dest_prop.rs
@@ -122,7 +122,7 @@ impl<'tcx> MirPass<'tcx> for DestinationPropagation {
         //
         // Only run at mir-opt-level=3 or higher for now (we don't fix up debuginfo and remove
         // storage statements at the moment).
-        sess.opts.debugging_opts.unsound_mir_opts && sess.mir_opt_level() >= 3
+        sess.opts.unstable_opts.unsound_mir_opts && sess.mir_opt_level() >= 3
     }
 
     fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
diff --git a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs
index 33f201cbd28..dba42f7aff0 100644
--- a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs
+++ b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs
@@ -95,7 +95,7 @@ pub struct EarlyOtherwiseBranch;
 
 impl<'tcx> MirPass<'tcx> for EarlyOtherwiseBranch {
     fn is_enabled(&self, sess: &rustc_session::Session) -> bool {
-        sess.mir_opt_level() >= 3 && sess.opts.debugging_opts.unsound_mir_opts
+        sess.mir_opt_level() >= 3 && sess.opts.unstable_opts.unsound_mir_opts
     }
 
     fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
diff --git a/compiler/rustc_mir_transform/src/generator.rs b/compiler/rustc_mir_transform/src/generator.rs
index 9fdea835967..91ecf387922 100644
--- a/compiler/rustc_mir_transform/src/generator.rs
+++ b/compiler/rustc_mir_transform/src/generator.rs
@@ -957,7 +957,7 @@ fn create_generator_drop_shim<'tcx>(
         tcx.mk_ptr(ty::TypeAndMut { ty: gen_ty, mutbl: hir::Mutability::Mut }),
         source_info,
     );
-    if tcx.sess.opts.debugging_opts.mir_emit_retag {
+    if tcx.sess.opts.unstable_opts.mir_emit_retag {
         // Alias tracking must know we changed the type
         body.basic_blocks_mut()[START_BLOCK].statements.insert(
             0,
@@ -1386,7 +1386,7 @@ impl<'tcx> MirPass<'tcx> for StateTransform {
 
         sanitize_witness(tcx, body, interior, upvars, &liveness_info.saved_locals);
 
-        if tcx.sess.opts.debugging_opts.validate_mir {
+        if tcx.sess.opts.unstable_opts.validate_mir {
             let mut vis = EnsureGeneratorFieldAssignmentsNeverAlias {
                 assigned_local: None,
                 saved_locals: &liveness_info.saved_locals,
diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs
index 8f049a182ee..1e46b0a0e81 100644
--- a/compiler/rustc_mir_transform/src/inline.rs
+++ b/compiler/rustc_mir_transform/src/inline.rs
@@ -40,7 +40,7 @@ struct CallSite<'tcx> {
 
 impl<'tcx> MirPass<'tcx> for Inline {
     fn is_enabled(&self, sess: &rustc_session::Session) -> bool {
-        if let Some(enabled) = sess.opts.debugging_opts.inline_mir {
+        if let Some(enabled) = sess.opts.unstable_opts.inline_mir {
             return enabled;
         }
 
@@ -246,7 +246,7 @@ impl<'tcx> Inliner<'tcx> {
             // not get any optimizations run on it. Any subsequent inlining may cause cycles, but we
             // do not need to catch this here, we can wait until the inliner decides to continue
             // inlining a second time.
-            InstanceDef::VtableShim(_)
+            InstanceDef::VTableShim(_)
             | InstanceDef::ReifyShim(_)
             | InstanceDef::FnPtrShim(..)
             | InstanceDef::ClosureOnceShim { .. }
@@ -395,9 +395,9 @@ impl<'tcx> Inliner<'tcx> {
         let tcx = self.tcx;
 
         let mut threshold = if callee_attrs.requests_inline() {
-            self.tcx.sess.opts.debugging_opts.inline_mir_hint_threshold.unwrap_or(100)
+            self.tcx.sess.opts.unstable_opts.inline_mir_hint_threshold.unwrap_or(100)
         } else {
-            self.tcx.sess.opts.debugging_opts.inline_mir_threshold.unwrap_or(50)
+            self.tcx.sess.opts.unstable_opts.inline_mir_threshold.unwrap_or(50)
         };
 
         // Give a bonus functions with a small number of blocks,
diff --git a/compiler/rustc_mir_transform/src/inline/cycle.rs b/compiler/rustc_mir_transform/src/inline/cycle.rs
index a3a35f95071..7810218fd67 100644
--- a/compiler/rustc_mir_transform/src/inline/cycle.rs
+++ b/compiler/rustc_mir_transform/src/inline/cycle.rs
@@ -79,7 +79,7 @@ pub(crate) fn mir_callgraph_reachable<'tcx>(
                 // These have MIR and if that MIR is inlined, substituted and then inlining is run
                 // again, a function item can end up getting inlined. Thus we'll be able to cause
                 // a cycle that way
-                InstanceDef::VtableShim(_)
+                InstanceDef::VTableShim(_)
                 | InstanceDef::ReifyShim(_)
                 | InstanceDef::FnPtrShim(..)
                 | InstanceDef::ClosureOnceShim { .. }
diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs
index 9e5c22308e7..40dc9fe9a05 100644
--- a/compiler/rustc_mir_transform/src/lib.rs
+++ b/compiler/rustc_mir_transform/src/lib.rs
@@ -1,6 +1,6 @@
 #![allow(rustc::potential_query_instability)]
 #![feature(box_patterns)]
-#![feature(let_chains)]
+#![cfg_attr(bootstrap, feature(let_chains))]
 #![feature(let_else)]
 #![feature(map_try_insert)]
 #![feature(min_specialization)]
@@ -217,7 +217,7 @@ fn mir_const<'tcx>(
     }
 
     // Unsafety check uses the raw mir, so make sure it is run.
-    if !tcx.sess.opts.debugging_opts.thir_unsafeck {
+    if !tcx.sess.opts.unstable_opts.thir_unsafeck {
         if let Some(param_did) = def.const_param_did {
             tcx.ensure().unsafety_check_result_for_const_arg((def.did, param_did));
         } else {
diff --git a/compiler/rustc_mir_transform/src/lower_slice_len.rs b/compiler/rustc_mir_transform/src/lower_slice_len.rs
index 47848cfa497..2f02d00ec9f 100644
--- a/compiler/rustc_mir_transform/src/lower_slice_len.rs
+++ b/compiler/rustc_mir_transform/src/lower_slice_len.rs
@@ -11,7 +11,7 @@ pub struct LowerSliceLenCalls;
 
 impl<'tcx> MirPass<'tcx> for LowerSliceLenCalls {
     fn is_enabled(&self, sess: &rustc_session::Session) -> bool {
-        sess.opts.mir_opt_level() > 0
+        sess.mir_opt_level() > 0
     }
 
     fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
diff --git a/compiler/rustc_mir_transform/src/pass_manager.rs b/compiler/rustc_mir_transform/src/pass_manager.rs
index 2380391d09a..e27d4ab1688 100644
--- a/compiler/rustc_mir_transform/src/pass_manager.rs
+++ b/compiler/rustc_mir_transform/src/pass_manager.rs
@@ -76,8 +76,8 @@ pub fn run_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, passes: &[&dyn
     let start_phase = body.phase;
     let mut cnt = 0;
 
-    let validate = tcx.sess.opts.debugging_opts.validate_mir;
-    let overridden_passes = &tcx.sess.opts.debugging_opts.mir_enable_passes;
+    let validate = tcx.sess.opts.unstable_opts.validate_mir;
+    let overridden_passes = &tcx.sess.opts.unstable_opts.mir_enable_passes;
     trace!(?overridden_passes);
 
     if validate {
diff --git a/compiler/rustc_mir_transform/src/remove_uninit_drops.rs b/compiler/rustc_mir_transform/src/remove_uninit_drops.rs
index 4c33f839f9b..96b715402e7 100644
--- a/compiler/rustc_mir_transform/src/remove_uninit_drops.rs
+++ b/compiler/rustc_mir_transform/src/remove_uninit_drops.rs
@@ -102,7 +102,7 @@ fn is_needs_drop_and_init<'tcx>(
     let field_needs_drop_and_init = |(f, f_ty, mpi)| {
         let child = move_path_children_matching(move_data, mpi, |x| x.is_field_to(f));
         let Some(mpi) = child else {
-            return f_ty.needs_drop(tcx, param_env);
+            return Ty::needs_drop(f_ty, tcx, param_env);
         };
 
         is_needs_drop_and_init(tcx, param_env, maybe_inits, move_data, f_ty, mpi)
diff --git a/compiler/rustc_mir_transform/src/reveal_all.rs b/compiler/rustc_mir_transform/src/reveal_all.rs
index 8ea550fa123..4919ad40098 100644
--- a/compiler/rustc_mir_transform/src/reveal_all.rs
+++ b/compiler/rustc_mir_transform/src/reveal_all.rs
@@ -9,7 +9,7 @@ pub struct RevealAll;
 
 impl<'tcx> MirPass<'tcx> for RevealAll {
     fn is_enabled(&self, sess: &rustc_session::Session) -> bool {
-        sess.opts.mir_opt_level() >= 3 || super::inline::Inline.is_enabled(sess)
+        sess.mir_opt_level() >= 3 || super::inline::Inline.is_enabled(sess)
     }
 
     fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs
index 3be1783ae33..3620e94bec7 100644
--- a/compiler/rustc_mir_transform/src/shim.rs
+++ b/compiler/rustc_mir_transform/src/shim.rs
@@ -32,7 +32,7 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<'
 
     let mut result = match instance {
         ty::InstanceDef::Item(..) => bug!("item {:?} passed to make_shim", instance),
-        ty::InstanceDef::VtableShim(def_id) => {
+        ty::InstanceDef::VTableShim(def_id) => {
             build_call_shim(tcx, instance, Some(Adjustment::Deref), CallKind::Direct(def_id))
         }
         ty::InstanceDef::FnPtrShim(def_id, ty) => {
@@ -113,7 +113,7 @@ enum Adjustment {
     /// We get passed `&[mut] self` and call the target with `*self`.
     ///
     /// This either copies `self` (if `Self: Copy`, eg. for function items), or moves out of it
-    /// (for `VtableShim`, which effectively is passed `&own Self`).
+    /// (for `VTableShim`, which effectively is passed `&own Self`).
     Deref,
 
     /// We get passed `self: Self` and call the target with `&mut self`.
@@ -176,7 +176,7 @@ fn build_drop_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, ty: Option<Ty<'tcx>>)
     if ty.is_some() {
         // The first argument (index 0), but add 1 for the return value.
         let dropee_ptr = Place::from(Local::new(1 + 0));
-        if tcx.sess.opts.debugging_opts.mir_emit_retag {
+        if tcx.sess.opts.unstable_opts.mir_emit_retag {
             // Function arguments should be retagged, and we make this one raw.
             body.basic_blocks_mut()[START_BLOCK].statements.insert(
                 0,
@@ -537,13 +537,12 @@ fn build_call_shim<'tcx>(
     };
 
     let def_id = instance.def_id();
-    let sig = tcx.fn_sig(def_id);
-    let mut sig = tcx.erase_late_bound_regions(sig);
+    let sig = tcx.bound_fn_sig(def_id);
+    let sig = sig.map_bound(|sig| tcx.erase_late_bound_regions(sig));
 
     assert_eq!(sig_substs.is_some(), !instance.has_polymorphic_mir_body());
-    if let Some(sig_substs) = sig_substs {
-        sig = EarlyBinder(sig).subst(tcx, sig_substs);
-    }
+    let mut sig =
+        if let Some(sig_substs) = sig_substs { sig.subst(tcx, sig_substs) } else { sig.0 };
 
     if let CallKind::Indirect(fnty) = call_kind {
         // `sig` determines our local decls, and thus the callee type in the `Call` terminator. This
@@ -570,7 +569,7 @@ fn build_call_shim<'tcx>(
 
     // FIXME(eddyb) avoid having this snippet both here and in
     // `Instance::fn_sig` (introduce `InstanceDef::fn_sig`?).
-    if let ty::InstanceDef::VtableShim(..) = instance {
+    if let ty::InstanceDef::VTableShim(..) = instance {
         // Modify fn(self, ...) to fn(self: *mut Self, ...)
         let mut inputs_and_output = sig.inputs_and_output.to_vec();
         let self_arg = &mut inputs_and_output[0];
diff --git a/compiler/rustc_mir_transform/src/simplify.rs b/compiler/rustc_mir_transform/src/simplify.rs
index 980af984362..180f4c7dcd6 100644
--- a/compiler/rustc_mir_transform/src/simplify.rs
+++ b/compiler/rustc_mir_transform/src/simplify.rs
@@ -28,6 +28,7 @@
 //! return.
 
 use crate::MirPass;
+use rustc_data_structures::fx::FxHashSet;
 use rustc_index::vec::{Idx, IndexVec};
 use rustc_middle::mir::coverage::*;
 use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor};
@@ -267,7 +268,8 @@ pub fn remove_dead_blocks<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
         return;
     }
 
-    let basic_blocks = body.basic_blocks_mut();
+    let basic_blocks = body.basic_blocks.as_mut();
+    let source_scopes = &body.source_scopes;
     let mut replacements: Vec<_> = (0..num_blocks).map(BasicBlock::new).collect();
     let mut used_blocks = 0;
     for alive_index in reachable.iter() {
@@ -282,7 +284,7 @@ pub fn remove_dead_blocks<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
     }
 
     if tcx.sess.instrument_coverage() {
-        save_unreachable_coverage(basic_blocks, used_blocks);
+        save_unreachable_coverage(basic_blocks, source_scopes, used_blocks);
     }
 
     basic_blocks.raw.truncate(used_blocks);
@@ -311,56 +313,72 @@ pub fn remove_dead_blocks<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
 /// `Unreachable` coverage statements. These are non-executable statements whose
 /// code regions are still recorded in the coverage map, representing regions
 /// with `0` executions.
+///
+/// If there are no live `Counter` `Coverage` statements remaining, we remove
+/// `Coverage` statements along with the dead blocks. Since at least one
+/// counter per function is required by LLVM (and necessary, to add the
+/// `function_hash` to the counter's call to the LLVM intrinsic
+/// `instrprof.increment()`).
+///
+/// The `generator::StateTransform` MIR pass and MIR inlining can create
+/// atypical conditions, where all live `Counter`s are dropped from the MIR.
+///
+/// With MIR inlining we can have coverage counters belonging to different
+/// instances in a single body, so the strategy described above is applied to
+/// coverage counters from each instance individually.
 fn save_unreachable_coverage(
     basic_blocks: &mut IndexVec<BasicBlock, BasicBlockData<'_>>,
+    source_scopes: &IndexVec<SourceScope, SourceScopeData<'_>>,
     first_dead_block: usize,
 ) {
-    let has_live_counters = basic_blocks.raw[0..first_dead_block].iter().any(|live_block| {
-        live_block.statements.iter().any(|statement| {
-            if let StatementKind::Coverage(coverage) = &statement.kind {
-                matches!(coverage.kind, CoverageKind::Counter { .. })
-            } else {
-                false
+    // Identify instances that still have some live coverage counters left.
+    let mut live = FxHashSet::default();
+    for basic_block in &basic_blocks.raw[0..first_dead_block] {
+        for statement in &basic_block.statements {
+            let StatementKind::Coverage(coverage) = &statement.kind else { continue };
+            let CoverageKind::Counter { .. } = coverage.kind else { continue };
+            let instance = statement.source_info.scope.inlined_instance(source_scopes);
+            live.insert(instance);
+        }
+    }
+
+    for block in &mut basic_blocks.raw[..first_dead_block] {
+        for statement in &mut block.statements {
+            let StatementKind::Coverage(_) = &statement.kind else { continue };
+            let instance = statement.source_info.scope.inlined_instance(source_scopes);
+            if !live.contains(&instance) {
+                statement.make_nop();
             }
-        })
-    });
-    if !has_live_counters {
-        // If there are no live `Counter` `Coverage` statements anymore, don't
-        // move dead coverage to the `START_BLOCK`. Just allow the dead
-        // `Coverage` statements to be dropped with the dead blocks.
-        //
-        // The `generator::StateTransform` MIR pass can create atypical
-        // conditions, where all live `Counter`s are dropped from the MIR.
-        //
-        // At least one Counter per function is required by LLVM (and necessary,
-        // to add the `function_hash` to the counter's call to the LLVM
-        // intrinsic `instrprof.increment()`).
+        }
+    }
+
+    if live.is_empty() {
         return;
     }
 
-    // Retain coverage info for dead blocks, so coverage reports will still
-    // report `0` executions for the uncovered code regions.
-    let mut dropped_coverage = Vec::new();
-    for dead_block in basic_blocks.raw[first_dead_block..].iter() {
-        for statement in dead_block.statements.iter() {
-            if let StatementKind::Coverage(coverage) = &statement.kind {
-                if let Some(code_region) = &coverage.code_region {
-                    dropped_coverage.push((statement.source_info, code_region.clone()));
-                }
+    // Retain coverage for instances that still have some live counters left.
+    let mut retained_coverage = Vec::new();
+    for dead_block in &basic_blocks.raw[first_dead_block..] {
+        for statement in &dead_block.statements {
+            let StatementKind::Coverage(coverage) = &statement.kind else { continue };
+            let Some(code_region) = &coverage.code_region else { continue };
+            let instance = statement.source_info.scope.inlined_instance(source_scopes);
+            if live.contains(&instance) {
+                retained_coverage.push((statement.source_info, code_region.clone()));
             }
         }
     }
 
     let start_block = &mut basic_blocks[START_BLOCK];
-    for (source_info, code_region) in dropped_coverage {
-        start_block.statements.push(Statement {
+    start_block.statements.extend(retained_coverage.into_iter().map(
+        |(source_info, code_region)| Statement {
             source_info,
             kind: StatementKind::Coverage(Box::new(Coverage {
                 kind: CoverageKind::Unreachable,
                 code_region: Some(code_region),
             })),
-        })
-    }
+        },
+    ));
 }
 
 pub struct SimplifyLocals;
diff --git a/compiler/rustc_mir_transform/src/simplify_try.rs b/compiler/rustc_mir_transform/src/simplify_try.rs
index fca9f7eeb24..d52f1261b23 100644
--- a/compiler/rustc_mir_transform/src/simplify_try.rs
+++ b/compiler/rustc_mir_transform/src/simplify_try.rs
@@ -378,7 +378,7 @@ fn optimization_applies<'tcx>(
 impl<'tcx> MirPass<'tcx> for SimplifyArmIdentity {
     fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
         // FIXME(77359): This optimization can result in unsoundness.
-        if !tcx.sess.opts.debugging_opts.unsound_mir_opts {
+        if !tcx.sess.opts.unstable_opts.unsound_mir_opts {
             return;
         }
 
@@ -551,7 +551,7 @@ impl<'tcx> MirPass<'tcx> for SimplifyBranchSame {
     fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
         // This optimization is disabled by default for now due to
         // soundness concerns; see issue #89485 and PR #89489.
-        if !tcx.sess.opts.debugging_opts.unsound_mir_opts {
+        if !tcx.sess.opts.unstable_opts.unsound_mir_opts {
             return;
         }
 
diff --git a/compiler/rustc_mir_transform/src/uninhabited_enum_branching.rs b/compiler/rustc_mir_transform/src/uninhabited_enum_branching.rs
index bd196f11879..30be64f5b2f 100644
--- a/compiler/rustc_mir_transform/src/uninhabited_enum_branching.rs
+++ b/compiler/rustc_mir_transform/src/uninhabited_enum_branching.rs
@@ -1,7 +1,7 @@
 //! A pass that eliminates branches on uninhabited enum variants.
 
 use crate::MirPass;
-use rustc_data_structures::stable_set::FxHashSet;
+use rustc_data_structures::fx::FxHashSet;
 use rustc_middle::mir::{
     BasicBlockData, Body, Local, Operand, Rvalue, StatementKind, SwitchTargets, Terminator,
     TerminatorKind,
diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs
index b3ac0f4cbea..68b65658c72 100644
--- a/compiler/rustc_monomorphize/src/collector.rs
+++ b/compiler/rustc_monomorphize/src/collector.rs
@@ -25,7 +25,7 @@
 //! codegen unit:
 //!
 //! - Constants
-//! - Vtables
+//! - VTables
 //! - Object Shims
 //!
 //!
@@ -180,7 +180,7 @@
 //! regardless of whether it is actually needed or not.
 
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_data_structures::sync::{par_iter, MTLock, MTRef, ParallelIterator};
+use rustc_data_structures::sync::{par_for_each_in, MTLock, MTRef};
 use rustc_hir as hir;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId};
@@ -346,7 +346,7 @@ pub fn collect_crate_mono_items(
         let inlining_map: MTRef<'_, _> = &mut inlining_map;
 
         tcx.sess.time("monomorphization_collector_graph_walk", || {
-            par_iter(roots).for_each(|root| {
+            par_for_each_in(roots, |root| {
                 let mut recursion_depths = DefIdMap::default();
                 collect_items_rec(
                     tcx,
@@ -992,7 +992,7 @@ fn visit_instance_use<'tcx>(
             }
         }
         ty::InstanceDef::DropGlue(_, Some(_))
-        | ty::InstanceDef::VtableShim(..)
+        | ty::InstanceDef::VTableShim(..)
         | ty::InstanceDef::ReifyShim(..)
         | ty::InstanceDef::ClosureOnceShim { .. }
         | ty::InstanceDef::Item(..)
@@ -1141,8 +1141,7 @@ fn create_fn_mono_item<'tcx>(
     debug!("create_fn_mono_item(instance={})", instance);
 
     let def_id = instance.def_id();
-    if tcx.sess.opts.debugging_opts.profile_closures && def_id.is_local() && tcx.is_closure(def_id)
-    {
+    if tcx.sess.opts.unstable_opts.profile_closures && def_id.is_local() && tcx.is_closure(def_id) {
         crate::util::dump_closure_profile(tcx, instance);
     }
 
@@ -1428,6 +1427,10 @@ fn collect_miri<'tcx>(tcx: TyCtxt<'tcx>, alloc_id: AllocId, output: &mut MonoIte
                 output.push(create_fn_mono_item(tcx, fn_instance, DUMMY_SP));
             }
         }
+        GlobalAlloc::VTable(ty, trait_ref) => {
+            let alloc_id = tcx.vtable_allocation((ty, trait_ref));
+            collect_miri(tcx, alloc_id, output)
+        }
     }
 }
 
diff --git a/compiler/rustc_monomorphize/src/partitioning/default.rs b/compiler/rustc_monomorphize/src/partitioning/default.rs
index 9190e5fe4eb..15276569c32 100644
--- a/compiler/rustc_monomorphize/src/partitioning/default.rs
+++ b/compiler/rustc_monomorphize/src/partitioning/default.rs
@@ -271,7 +271,7 @@ fn characteristic_def_id_of_mono_item<'tcx>(
         MonoItem::Fn(instance) => {
             let def_id = match instance.def {
                 ty::InstanceDef::Item(def) => def.did,
-                ty::InstanceDef::VtableShim(..)
+                ty::InstanceDef::VTableShim(..)
                 | ty::InstanceDef::ReifyShim(..)
                 | ty::InstanceDef::FnPtrShim(..)
                 | ty::InstanceDef::ClosureOnceShim { .. }
@@ -303,7 +303,7 @@ fn characteristic_def_id_of_mono_item<'tcx>(
 
                 // When polymorphization is enabled, methods which do not depend on their generic
                 // parameters, but the self-type of their impl block do will fail to normalize.
-                if !tcx.sess.opts.debugging_opts.polymorphize || !instance.needs_subst() {
+                if !tcx.sess.opts.unstable_opts.polymorphize || !instance.needs_subst() {
                     // This is a method within an impl, find out what the self-type is:
                     let impl_self_ty = tcx.subst_and_normalize_erasing_regions(
                         instance.substs,
@@ -425,7 +425,7 @@ fn mono_item_visibility<'tcx>(
         InstanceDef::DropGlue(def_id, Some(_)) => def_id,
 
         // These are all compiler glue and such, never exported, always hidden.
-        InstanceDef::VtableShim(..)
+        InstanceDef::VTableShim(..)
         | InstanceDef::ReifyShim(..)
         | InstanceDef::FnPtrShim(..)
         | InstanceDef::Virtual(..)
diff --git a/compiler/rustc_monomorphize/src/partitioning/merging.rs b/compiler/rustc_monomorphize/src/partitioning/merging.rs
index 09cadc907b1..02bb8dea0c0 100644
--- a/compiler/rustc_monomorphize/src/partitioning/merging.rs
+++ b/compiler/rustc_monomorphize/src/partitioning/merging.rs
@@ -83,7 +83,7 @@ pub fn merge_codegen_units<'tcx>(
 
         for cgu in codegen_units.iter_mut() {
             if let Some(new_cgu_name) = new_cgu_names.get(&cgu.name()) {
-                if cx.tcx.sess.opts.debugging_opts.human_readable_cgu_names {
+                if cx.tcx.sess.opts.unstable_opts.human_readable_cgu_names {
                     cgu.set_name(Symbol::intern(&new_cgu_name));
                 } else {
                     // If we don't require CGU names to be human-readable, we
diff --git a/compiler/rustc_monomorphize/src/partitioning/mod.rs b/compiler/rustc_monomorphize/src/partitioning/mod.rs
index c1992137575..ff2d3869328 100644
--- a/compiler/rustc_monomorphize/src/partitioning/mod.rs
+++ b/compiler/rustc_monomorphize/src/partitioning/mod.rs
@@ -98,6 +98,7 @@ mod merging;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::sync;
 use rustc_hir::def_id::DefIdSet;
+use rustc_middle::mir;
 use rustc_middle::mir::mono::MonoItem;
 use rustc_middle::mir::mono::{CodegenUnit, Linkage};
 use rustc_middle::ty::print::with_no_trimmed_paths;
@@ -141,7 +142,7 @@ trait Partitioner<'tcx> {
 }
 
 fn get_partitioner<'tcx>(tcx: TyCtxt<'tcx>) -> Box<dyn Partitioner<'tcx>> {
-    let strategy = match &tcx.sess.opts.debugging_opts.cgu_partitioning_strategy {
+    let strategy = match &tcx.sess.opts.unstable_opts.cgu_partitioning_strategy {
         None => "default",
         Some(s) => &s[..],
     };
@@ -345,7 +346,7 @@ fn collect_and_partition_mono_items<'tcx>(
     tcx: TyCtxt<'tcx>,
     (): (),
 ) -> (&'tcx DefIdSet, &'tcx [CodegenUnit<'tcx>]) {
-    let collection_mode = match tcx.sess.opts.debugging_opts.print_mono_items {
+    let collection_mode = match tcx.sess.opts.unstable_opts.print_mono_items {
         Some(ref s) => {
             let mode_string = s.to_lowercase();
             let mode_string = mode_string.trim();
@@ -413,7 +414,7 @@ fn collect_and_partition_mono_items<'tcx>(
         })
         .collect();
 
-    if tcx.sess.opts.debugging_opts.print_mono_items.is_some() {
+    if tcx.sess.opts.unstable_opts.print_mono_items.is_some() {
         let mut item_to_cgus: FxHashMap<_, Vec<_>> = Default::default();
 
         for cgu in codegen_units {
@@ -479,9 +480,14 @@ fn codegened_and_inlined_items<'tcx>(tcx: TyCtxt<'tcx>, (): ()) -> &'tcx DefIdSe
                 if !visited.insert(did) {
                     continue;
                 }
-                for scope in &tcx.instance_mir(instance.def).source_scopes {
-                    if let Some((ref inlined, _)) = scope.inlined {
-                        result.insert(inlined.def_id());
+                let body = tcx.instance_mir(instance.def);
+                for block in body.basic_blocks() {
+                    for statement in &block.statements {
+                        let mir::StatementKind::Coverage(_) = statement.kind else { continue };
+                        let scope = statement.source_info.scope;
+                        if let Some(inlined) = scope.inlined_instance(&body.source_scopes) {
+                            result.insert(inlined.def_id());
+                        }
                     }
                 }
             }
diff --git a/compiler/rustc_monomorphize/src/polymorphize.rs b/compiler/rustc_monomorphize/src/polymorphize.rs
index aaa924d7fa0..394843e510d 100644
--- a/compiler/rustc_monomorphize/src/polymorphize.rs
+++ b/compiler/rustc_monomorphize/src/polymorphize.rs
@@ -36,7 +36,7 @@ fn unused_generic_params<'tcx>(
     tcx: TyCtxt<'tcx>,
     instance: ty::InstanceDef<'tcx>,
 ) -> FiniteBitSet<u32> {
-    if !tcx.sess.opts.debugging_opts.polymorphize {
+    if !tcx.sess.opts.unstable_opts.polymorphize {
         // If polymorphization disabled, then all parameters are used.
         return FiniteBitSet::new_empty();
     }
diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs
index 113af328a91..261adbfc346 100644
--- a/compiler/rustc_parse/src/lib.rs
+++ b/compiler/rustc_parse/src/lib.rs
@@ -3,7 +3,7 @@
 #![feature(array_windows)]
 #![feature(box_patterns)]
 #![feature(if_let_guard)]
-#![feature(let_chains)]
+#![cfg_attr(bootstrap, feature(let_chains))]
 #![feature(let_else)]
 #![feature(never_type)]
 #![feature(rustc_attrs)]
@@ -117,12 +117,12 @@ pub fn new_parser_from_file<'a>(sess: &'a ParseSess, path: &Path, sp: Option<Spa
     source_file_to_parser(sess, file_to_source_file(sess, path, sp))
 }
 
-/// Given a `source_file` and config, returns a parser.
+/// Given a session and a `source_file`, returns a parser.
 fn source_file_to_parser(sess: &ParseSess, source_file: Lrc<SourceFile>) -> Parser<'_> {
     panictry_buffer!(&sess.span_diagnostic, maybe_source_file_to_parser(sess, source_file))
 }
 
-/// Given a `source_file` and config, return a parser. Returns any buffered errors from lexing the
+/// Given a session and a `source_file`, return a parser. Returns any buffered errors from lexing the
 /// initial token stream.
 fn maybe_source_file_to_parser(
     sess: &ParseSess,
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 2c43563b104..43b48613976 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -15,10 +15,10 @@ use rustc_ast::util::classify;
 use rustc_ast::util::literal::LitError;
 use rustc_ast::util::parser::{prec_let_scrutinee_needs_par, AssocOp, Fixity};
 use rustc_ast::visit::Visitor;
-use rustc_ast::StmtKind;
 use rustc_ast::{self as ast, AttrStyle, AttrVec, CaptureBy, ExprField, Lit, UnOp, DUMMY_NODE_ID};
 use rustc_ast::{AnonConst, BinOp, BinOpKind, FnDecl, FnRetTy, MacCall, Param, Ty, TyKind};
 use rustc_ast::{Arm, Async, BlockCheckMode, Expr, ExprKind, Label, Movability, RangeLimits};
+use rustc_ast::{ClosureBinder, StmtKind};
 use rustc_ast_pretty::pprust;
 use rustc_data_structures::thin_vec::ThinVec;
 use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, PResult};
@@ -1343,11 +1343,7 @@ impl<'a> Parser<'a> {
             self.parse_if_expr(attrs)
         } else if self.check_keyword(kw::For) {
             if self.choose_generics_over_qpath(1) {
-                // NOTE(Centril, eddyb): DO NOT REMOVE! Beyond providing parser recovery,
-                // this is an insurance policy in case we allow qpaths in (tuple-)struct patterns.
-                // When `for <Foo as Bar>::Proj in $expr $block` is wanted,
-                // you can disambiguate in favor of a pattern with `(...)`.
-                self.recover_quantified_closure_expr(attrs)
+                self.parse_closure_expr(attrs)
             } else {
                 assert!(self.eat_keyword(kw::For));
                 self.parse_for_expr(None, self.prev_token.span, attrs)
@@ -1393,7 +1389,9 @@ impl<'a> Parser<'a> {
             self.parse_yield_expr(attrs)
         } else if self.is_do_yeet() {
             self.parse_yeet_expr(attrs)
-        } else if self.eat_keyword(kw::Let) {
+        } else if self.check_keyword(kw::Let) {
+            self.manage_let_chains_context();
+            self.bump();
             self.parse_let_expr(attrs)
         } else if self.eat_keyword(kw::Underscore) {
             Ok(self.mk_expr(self.prev_token.span, ExprKind::Underscore, attrs))
@@ -2094,29 +2092,21 @@ impl<'a> Parser<'a> {
         Ok(self.mk_expr(blk.span, ExprKind::Block(blk, None), AttrVec::new()))
     }
 
-    /// Recover on an explicitly quantified closure expression, e.g., `for<'a> |x: &'a u8| *x + 1`.
-    fn recover_quantified_closure_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> {
+    /// Parses a closure expression (e.g., `move |args| expr`).
+    fn parse_closure_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> {
         let lo = self.token.span;
-        let _ = self.parse_late_bound_lifetime_defs()?;
-        let span_for = lo.to(self.prev_token.span);
-        let closure = self.parse_closure_expr(attrs)?;
 
-        self.struct_span_err(span_for, "cannot introduce explicit parameters for a closure")
-            .span_label(closure.span, "the parameters are attached to this closure")
-            .span_suggestion(
-                span_for,
-                "remove the parameters",
-                "",
-                Applicability::MachineApplicable,
-            )
-            .emit();
+        let binder = if self.check_keyword(kw::For) {
+            let lo = self.token.span;
+            let lifetime_defs = self.parse_late_bound_lifetime_defs()?;
+            let span = lo.to(self.prev_token.span);
 
-        Ok(self.mk_expr_err(lo.to(closure.span)))
-    }
+            self.sess.gated_spans.gate(sym::closure_lifetime_binder, span);
 
-    /// Parses a closure expression (e.g., `move |args| expr`).
-    fn parse_closure_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> {
-        let lo = self.token.span;
+            ClosureBinder::For { span, generic_params: P::from_vec(lifetime_defs) }
+        } else {
+            ClosureBinder::NotPresent
+        };
 
         let movability =
             if self.eat_keyword(kw::Static) { Movability::Static } else { Movability::Movable };
@@ -2160,7 +2150,15 @@ impl<'a> Parser<'a> {
 
         let closure = self.mk_expr(
             lo.to(body.span),
-            ExprKind::Closure(capture_clause, asyncness, movability, decl, body, lo.to(decl_hi)),
+            ExprKind::Closure(
+                binder,
+                capture_clause,
+                asyncness,
+                movability,
+                decl,
+                body,
+                lo.to(decl_hi),
+            ),
             attrs,
         );
 
@@ -2343,28 +2341,32 @@ impl<'a> Parser<'a> {
 
     /// Parses the condition of a `if` or `while` expression.
     fn parse_cond_expr(&mut self) -> PResult<'a, P<Expr>> {
-        let cond = self.with_let_management(true, |local_self| {
+        self.with_let_management(true, |local_self| {
             local_self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)
-        })?;
+        })
+    }
 
-        if let ExprKind::Let(..) = cond.kind {
-            // Remove the last feature gating of a `let` expression since it's stable.
-            self.sess.gated_spans.ungate_last(sym::let_chains, cond.span);
+    // Checks if `let` is in an invalid position like `let x = let y = 1;` or
+    // if the current `let` is in a let_chains context but nested in another
+    // expression like `if let Some(_) = _opt && [1, 2, 3][let _ = ()] = 1`.
+    //
+    // This method expects that the current token is `let`.
+    fn manage_let_chains_context(&mut self) {
+        debug_assert!(matches!(self.token.kind, TokenKind::Ident(kw::Let, _)));
+        let is_in_a_let_chains_context_but_nested_in_other_expr = self.let_expr_allowed
+            && !matches!(
+                self.prev_token.kind,
+                TokenKind::AndAnd | TokenKind::Ident(kw::If, _) | TokenKind::Ident(kw::While, _)
+            );
+        if !self.let_expr_allowed || is_in_a_let_chains_context_but_nested_in_other_expr {
+            self.struct_span_err(self.token.span, "expected expression, found `let` statement")
+                .emit();
         }
-
-        Ok(cond)
     }
 
     /// Parses a `let $pat = $expr` pseudo-expression.
     /// The `let` token has already been eaten.
     fn parse_let_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> {
-        if !self.let_expr_allowed {
-            self.struct_span_err(
-                self.prev_token.span,
-                "expected expression, found `let` statement",
-            )
-            .emit();
-        }
         let lo = self.prev_token.span;
         let pat = self.parse_pat_allow_top_alt(
             None,
@@ -2377,7 +2379,6 @@ impl<'a> Parser<'a> {
             this.parse_assoc_expr_with(1 + prec_let_scrutinee_needs_par(), None.into())
         })?;
         let span = lo.to(expr.span);
-        self.sess.gated_spans.gate(sym::let_chains, span);
         Ok(self.mk_expr(span, ExprKind::Let(pat, expr, span), attrs))
     }
 
@@ -2683,15 +2684,11 @@ impl<'a> Parser<'a> {
     pub(super) fn parse_arm(&mut self) -> PResult<'a, Arm> {
         // Used to check the `let_chains` and `if_let_guard` features mostly by scaning
         // `&&` tokens.
-        fn check_let_expr(expr: &Expr) -> (bool, bool) {
+        fn check_let_expr(expr: &Expr) -> bool {
             match expr.kind {
-                ExprKind::Binary(_, ref lhs, ref rhs) => {
-                    let lhs_rslt = check_let_expr(lhs);
-                    let rhs_rslt = check_let_expr(rhs);
-                    (lhs_rslt.0 || rhs_rslt.0, false)
-                }
-                ExprKind::Let(..) => (true, true),
-                _ => (false, true),
+                ExprKind::Binary(_, ref lhs, ref rhs) => check_let_expr(lhs) || check_let_expr(rhs),
+                ExprKind::Let(..) => true,
+                _ => false,
             }
         }
         let attrs = self.parse_outer_attributes()?;
@@ -2706,12 +2703,8 @@ impl<'a> Parser<'a> {
             let guard = if this.eat_keyword(kw::If) {
                 let if_span = this.prev_token.span;
                 let cond = this.with_let_management(true, |local_this| local_this.parse_expr())?;
-                let (has_let_expr, does_not_have_bin_op) = check_let_expr(&cond);
+                let has_let_expr = check_let_expr(&cond);
                 if has_let_expr {
-                    if does_not_have_bin_op {
-                        // Remove the last feature gating of a `let` expression since it's stable.
-                        this.sess.gated_spans.ungate_last(sym::let_chains, cond.span);
-                    }
                     let span = if_span.to(cond.span);
                     this.sess.gated_spans.gate(sym::if_let_guard, span);
                 }
@@ -3012,6 +3005,11 @@ impl<'a> Parser<'a> {
                 }
             };
 
+            let is_shorthand = parsed_field.as_ref().map_or(false, |f| f.is_shorthand);
+            // A shorthand field can be turned into a full field with `:`.
+            // We should point this out.
+            self.check_or_expected(!is_shorthand, TokenType::Token(token::Colon));
+
             match self.expect_one_of(&[token::Comma], &[token::CloseDelim(close_delim)]) {
                 Ok(_) => {
                     if let Some(f) = parsed_field.or(recovery_field) {
@@ -3032,6 +3030,19 @@ impl<'a> Parser<'a> {
                                 ",",
                                 Applicability::MachineApplicable,
                             );
+                        } else if is_shorthand
+                            && (AssocOp::from_token(&self.token).is_some()
+                                || matches!(&self.token.kind, token::OpenDelim(_))
+                                || self.token.kind == token::Dot)
+                        {
+                            // Looks like they tried to write a shorthand, complex expression.
+                            let ident = parsed_field.expect("is_shorthand implies Some").ident;
+                            e.span_suggestion(
+                                ident.span.shrink_to_lo(),
+                                "try naming a field",
+                                &format!("{ident}: "),
+                                Applicability::HasPlaceholders,
+                            );
                         }
                     }
                     if !recover {
diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index 87bc0d9762e..a14a7fc0610 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -1677,7 +1677,6 @@ impl<'a> Parser<'a> {
     }
 
     /// Is this a possibly malformed start of a `macro_rules! foo` item definition?
-
     fn is_macro_rules_item(&mut self) -> IsMacroRulesItem {
         if self.check_keyword(kw::MacroRules) {
             let macro_rules_span = self.token.span;
diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs
index f6fa19030ac..5deb17b8651 100644
--- a/compiler/rustc_parse_format/src/lib.rs
+++ b/compiler/rustc_parse_format/src/lib.rs
@@ -104,8 +104,8 @@ pub struct FormatSpec<'a> {
 pub enum Position<'a> {
     /// The argument is implied to be located at an index
     ArgumentImplicitlyIs(usize),
-    /// The argument is located at a specific index given in the format
-    ArgumentIs(usize),
+    /// The argument is located at a specific index given in the format,
+    ArgumentIs(usize, Option<InnerSpan>),
     /// The argument has a name.
     ArgumentNamed(&'a str, InnerSpan),
 }
@@ -113,7 +113,7 @@ pub enum Position<'a> {
 impl Position<'_> {
     pub fn index(&self) -> Option<usize> {
         match self {
-            ArgumentIs(i) | ArgumentImplicitlyIs(i) => Some(*i),
+            ArgumentIs(i, ..) | ArgumentImplicitlyIs(i) => Some(*i),
             _ => None,
         }
     }
@@ -502,8 +502,15 @@ impl<'a> Parser<'a> {
     /// Returns `Some(parsed_position)` if the position is not implicitly
     /// consuming a macro argument, `None` if it's the case.
     fn position(&mut self) -> Option<Position<'a>> {
+        let start_position = self.cur.peek().map(|item| item.0);
         if let Some(i) = self.integer() {
-            Some(ArgumentIs(i))
+            let inner_span = start_position.and_then(|start| {
+                self.cur
+                    .peek()
+                    .cloned()
+                    .and_then(|item| Some(self.to_span_index(start).to(self.to_span_index(item.0))))
+            });
+            Some(ArgumentIs(i, inner_span))
         } else {
             match self.cur.peek() {
                 Some(&(start, c)) if rustc_lexer::is_id_start(c) => {
@@ -572,9 +579,14 @@ impl<'a> Parser<'a> {
             // '0' flag and then an ill-formatted format string with just a '$'
             // and no count, but this is better if we instead interpret this as
             // no '0' flag and '0$' as the width instead.
-            if self.consume('$') {
+            if let Some(end) = self.consume_pos('$') {
                 spec.width = CountIsParam(0);
+
+                if let Some((pos, _)) = self.cur.peek().cloned() {
+                    spec.width_span = Some(self.to_span_index(pos - 2).to(self.to_span_index(pos)));
+                }
                 havewidth = true;
+                spec.width_span = Some(self.to_span_index(end - 1).to(self.to_span_index(end + 1)));
             } else {
                 spec.flags |= 1 << (FlagSignAwareZeroPad as u32);
             }
@@ -585,6 +597,7 @@ impl<'a> Parser<'a> {
             spec.width = w;
             spec.width_span = sp;
         }
+
         if let Some(start) = self.consume_pos('.') {
             if let Some(end) = self.consume_pos('*') {
                 // Resolve `CountIsNextParam`.
diff --git a/compiler/rustc_parse_format/src/tests.rs b/compiler/rustc_parse_format/src/tests.rs
index c9667922ee7..a98f816644b 100644
--- a/compiler/rustc_parse_format/src/tests.rs
+++ b/compiler/rustc_parse_format/src/tests.rs
@@ -62,18 +62,30 @@ fn format_nothing() {
 }
 #[test]
 fn format_position() {
-    same("{3}", &[NextArgument(Argument { position: ArgumentIs(3), format: fmtdflt() })]);
+    same(
+        "{3}",
+        &[NextArgument(Argument {
+            position: ArgumentIs(3, Some(InnerSpan { start: 2, end: 3 })),
+            format: fmtdflt(),
+        })],
+    );
 }
 #[test]
 fn format_position_nothing_else() {
-    same("{3:}", &[NextArgument(Argument { position: ArgumentIs(3), format: fmtdflt() })]);
+    same(
+        "{3:}",
+        &[NextArgument(Argument {
+            position: ArgumentIs(3, Some(InnerSpan { start: 2, end: 3 })),
+            format: fmtdflt(),
+        })],
+    );
 }
 #[test]
 fn format_type() {
     same(
         "{3:x}",
         &[NextArgument(Argument {
-            position: ArgumentIs(3),
+            position: ArgumentIs(3, Some(InnerSpan { start: 2, end: 3 })),
             format: FormatSpec {
                 fill: None,
                 align: AlignUnknown,
@@ -93,7 +105,7 @@ fn format_align_fill() {
     same(
         "{3:>}",
         &[NextArgument(Argument {
-            position: ArgumentIs(3),
+            position: ArgumentIs(3, Some(InnerSpan { start: 2, end: 3 })),
             format: FormatSpec {
                 fill: None,
                 align: AlignRight,
@@ -110,7 +122,7 @@ fn format_align_fill() {
     same(
         "{3:0<}",
         &[NextArgument(Argument {
-            position: ArgumentIs(3),
+            position: ArgumentIs(3, Some(InnerSpan { start: 2, end: 3 })),
             format: FormatSpec {
                 fill: Some('0'),
                 align: AlignLeft,
@@ -127,7 +139,7 @@ fn format_align_fill() {
     same(
         "{3:*<abcd}",
         &[NextArgument(Argument {
-            position: ArgumentIs(3),
+            position: ArgumentIs(3, Some(InnerSpan { start: 2, end: 3 })),
             format: FormatSpec {
                 fill: Some('*'),
                 align: AlignLeft,
@@ -179,6 +191,23 @@ fn format_counts() {
         })],
     );
     same(
+        "{1:0$.10x}",
+        &[NextArgument(Argument {
+            position: ArgumentIs(1, Some(InnerSpan { start: 2, end: 3 })),
+            format: FormatSpec {
+                fill: None,
+                align: AlignUnknown,
+                flags: 0,
+                precision: CountIs(10),
+                width: CountIsParam(0),
+                precision_span: None,
+                width_span: Some(InnerSpan::new(4, 6)),
+                ty: "x",
+                ty_span: None,
+            },
+        })],
+    );
+    same(
         "{:.*x}",
         &[NextArgument(Argument {
             position: ArgumentImplicitlyIs(1),
@@ -274,7 +303,7 @@ fn format_mixture() {
         &[
             String("abcd "),
             NextArgument(Argument {
-                position: ArgumentIs(3),
+                position: ArgumentIs(3, Some(InnerSpan { start: 7, end: 8 })),
                 format: FormatSpec {
                     fill: None,
                     align: AlignUnknown,
diff --git a/compiler/rustc_passes/Cargo.toml b/compiler/rustc_passes/Cargo.toml
index 676812db59a..faa9c493d88 100644
--- a/compiler/rustc_passes/Cargo.toml
+++ b/compiler/rustc_passes/Cargo.toml
@@ -15,6 +15,7 @@ rustc_hir = { path = "../rustc_hir" }
 rustc_index = { path = "../rustc_index" }
 rustc_session = { path = "../rustc_session" }
 rustc_target = { path = "../rustc_target" }
+rustc_macros = { path = "../rustc_macros" }
 rustc_ast = { path = "../rustc_ast" }
 rustc_serialize = { path = "../rustc_serialize" }
 rustc_span = { path = "../rustc_span" }
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index d0723c68a77..fde12b9eee6 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -4,9 +4,10 @@
 //! conflicts between multiple such attributes attached to the same
 //! item.
 
+use crate::errors;
 use rustc_ast::{ast, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem};
 use rustc_data_structures::fx::FxHashMap;
-use rustc_errors::{pluralize, struct_span_err, Applicability, MultiSpan};
+use rustc_errors::{fluent, struct_span_err, Applicability, MultiSpan};
 use rustc_expand::base::resolve_path;
 use rustc_feature::{AttributeDuplicates, AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP};
 use rustc_hir as hir;
@@ -120,6 +121,10 @@ impl CheckAttrVisitor<'_> {
                 sym::rustc_lint_diagnostics => {
                     self.check_rustc_lint_diagnostics(&attr, span, target)
                 }
+                sym::rustc_lint_opt_ty => self.check_rustc_lint_opt_ty(&attr, span, target),
+                sym::rustc_lint_opt_deny_field_access => {
+                    self.check_rustc_lint_opt_deny_field_access(&attr, span, target)
+                }
                 sym::rustc_clean
                 | sym::rustc_dirty
                 | sym::rustc_if_this_changed
@@ -139,6 +144,7 @@ impl CheckAttrVisitor<'_> {
                 | sym::rustc_const_stable
                 | sym::unstable
                 | sym::stable
+                | sym::rustc_allowed_through_unstable_modules
                 | sym::rustc_promotable => self.check_stability_promotable(&attr, span, target),
                 _ => true,
             };
@@ -174,16 +180,20 @@ impl CheckAttrVisitor<'_> {
                 if let Some(BuiltinAttribute { type_: AttributeType::CrateLevel, .. }) =
                     attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name))
                 {
-                    self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
-                        let msg = match attr.style {
-                            ast::AttrStyle::Outer => {
-                                "crate-level attribute should be an inner attribute: add an exclamation \
-                                 mark: `#![foo]`"
-                            }
-                            ast::AttrStyle::Inner => "crate-level attribute should be in the root module",
-                        };
-                        lint.build(msg).emit();
-                    });
+                    match attr.style {
+                        ast::AttrStyle::Outer => self.tcx.emit_spanned_lint(
+                            UNUSED_ATTRIBUTES,
+                            hir_id,
+                            attr.span,
+                            errors::OuterCrateLevelAttr,
+                        ),
+                        ast::AttrStyle::Inner => self.tcx.emit_spanned_lint(
+                            UNUSED_ATTRIBUTES,
+                            hir_id,
+                            attr.span,
+                            errors::InnerCrateLevelAttr,
+                        ),
+                    }
                 }
             }
 
@@ -208,37 +218,21 @@ impl CheckAttrVisitor<'_> {
     }
 
     fn inline_attr_str_error_with_macro_def(&self, hir_id: HirId, attr: &Attribute, sym: &str) {
-        self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
-            lint.build(&format!(
-                "`#[{sym}]` is ignored on struct fields, match arms and macro defs",
-            ))
-            .warn(
-                "this was previously accepted by the compiler but is \
-                 being phased out; it will become a hard error in \
-                 a future release!",
-            )
-            .note(
-                "see issue #80564 <https://github.com/rust-lang/rust/issues/80564> \
-                 for more information",
-            )
-            .emit();
-        });
+        self.tcx.emit_spanned_lint(
+            UNUSED_ATTRIBUTES,
+            hir_id,
+            attr.span,
+            errors::IgnoredAttrWithMacro { sym },
+        );
     }
 
     fn inline_attr_str_error_without_macro_def(&self, hir_id: HirId, attr: &Attribute, sym: &str) {
-        self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
-            lint.build(&format!("`#[{sym}]` is ignored on struct fields and match arms"))
-                .warn(
-                    "this was previously accepted by the compiler but is \
-                 being phased out; it will become a hard error in \
-                 a future release!",
-                )
-                .note(
-                    "see issue #80564 <https://github.com/rust-lang/rust/issues/80564> \
-                 for more information",
-                )
-                .emit();
-        });
+        self.tcx.emit_spanned_lint(
+            UNUSED_ATTRIBUTES,
+            hir_id,
+            attr.span,
+            errors::IgnoredAttr { sym },
+        );
     }
 
     /// Checks if an `#[inline]` is applied to a function or a closure. Returns `true` if valid.
@@ -248,9 +242,12 @@ impl CheckAttrVisitor<'_> {
             | Target::Closure
             | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true,
             Target::Method(MethodKind::Trait { body: false }) | Target::ForeignFn => {
-                self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
-                    lint.build("`#[inline]` is ignored on function prototypes").emit();
-                });
+                self.tcx.emit_spanned_lint(
+                    UNUSED_ATTRIBUTES,
+                    hir_id,
+                    attr.span,
+                    errors::IgnoredInlineAttrFnProto,
+                );
                 true
             }
             // FIXME(#65833): We permit associated consts to have an `#[inline]` attribute with
@@ -258,19 +255,12 @@ impl CheckAttrVisitor<'_> {
             // accidentally, to to be compatible with crates depending on them, we can't throw an
             // error here.
             Target::AssocConst => {
-                self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
-                    lint.build("`#[inline]` is ignored on constants")
-                        .warn(
-                            "this was previously accepted by the compiler but is \
-                             being phased out; it will become a hard error in \
-                             a future release!",
-                        )
-                        .note(
-                            "see issue #65833 <https://github.com/rust-lang/rust/issues/65833> \
-                             for more information",
-                        )
-                        .emit();
-                });
+                self.tcx.emit_spanned_lint(
+                    UNUSED_ATTRIBUTES,
+                    hir_id,
+                    attr.span,
+                    errors::IgnoredInlineAttrConstants,
+                );
                 true
             }
             // FIXME(#80564): Same for fields, arms, and macro defs
@@ -279,14 +269,10 @@ impl CheckAttrVisitor<'_> {
                 true
             }
             _ => {
-                struct_span_err!(
-                    self.tcx.sess,
-                    attr.span,
-                    E0518,
-                    "attribute should be applied to function or closure",
-                )
-                .span_label(span, "not a function or closure")
-                .emit();
+                self.tcx.sess.emit_err(errors::InlineNotFnOrClosure {
+                    attr_span: attr.span,
+                    defn_span: span,
+                });
                 false
             }
         }
@@ -308,36 +294,40 @@ impl CheckAttrVisitor<'_> {
 
             // function prototypes can't be covered
             Target::Method(MethodKind::Trait { body: false }) | Target::ForeignFn => {
-                self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
-                    lint.build("`#[no_coverage]` is ignored on function prototypes").emit();
-                });
+                self.tcx.emit_spanned_lint(
+                    UNUSED_ATTRIBUTES,
+                    hir_id,
+                    attr.span,
+                    errors::IgnoredNoCoverageFnProto,
+                );
                 true
             }
 
             Target::Mod | Target::ForeignMod | Target::Impl | Target::Trait => {
-                self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
-                    lint.build("`#[no_coverage]` does not propagate into items and must be applied to the contained functions directly").emit();
-                });
+                self.tcx.emit_spanned_lint(
+                    UNUSED_ATTRIBUTES,
+                    hir_id,
+                    attr.span,
+                    errors::IgnoredNoCoveragePropagate,
+                );
                 true
             }
 
             Target::Expression | Target::Statement | Target::Arm => {
-                self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
-                    lint.build("`#[no_coverage]` may only be applied to function definitions")
-                        .emit();
-                });
+                self.tcx.emit_spanned_lint(
+                    UNUSED_ATTRIBUTES,
+                    hir_id,
+                    attr.span,
+                    errors::IgnoredNoCoverageFnDefn,
+                );
                 true
             }
 
             _ => {
-                struct_span_err!(
-                    self.tcx.sess,
-                    attr.span,
-                    E0788,
-                    "`#[no_coverage]` must be applied to coverable code",
-                )
-                .span_label(span, "not coverable code")
-                .emit();
+                self.tcx.sess.emit_err(errors::IgnoredNoCoverageNotCoverable {
+                    attr_span: attr.span,
+                    defn_span: span,
+                });
                 false
             }
         }
@@ -388,14 +378,10 @@ impl CheckAttrVisitor<'_> {
                 true
             }
             _ => {
-                self.tcx
-                    .sess
-                    .struct_span_err(
-                        attr.span,
-                        "attribute should be applied to a function definition",
-                    )
-                    .span_label(span, "not a function definition")
-                    .emit();
+                self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToFn {
+                    attr_span: attr.span,
+                    defn_span: span,
+                });
                 false
             }
         }
@@ -407,14 +393,10 @@ impl CheckAttrVisitor<'_> {
             Target::Fn
             | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true,
             _ => {
-                self.tcx
-                    .sess
-                    .struct_span_err(
-                        attr.span,
-                        "attribute should be applied to a function definition",
-                    )
-                    .span_label(span, "not a function definition")
-                    .emit();
+                self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToFn {
+                    attr_span: attr.span,
+                    defn_span: span,
+                });
                 false
             }
         }
@@ -431,13 +413,7 @@ impl CheckAttrVisitor<'_> {
     ) -> bool {
         match target {
             _ if attrs.iter().any(|attr| attr.has_name(sym::naked)) => {
-                struct_span_err!(
-                    self.tcx.sess,
-                    attr_span,
-                    E0736,
-                    "cannot use `#[track_caller]` with `#[naked]`",
-                )
-                .emit();
+                self.tcx.sess.emit_err(errors::NakedTrackedCaller { attr_span });
                 false
             }
             Target::Fn | Target::Method(..) | Target::ForeignFn | Target::Closure => true,
@@ -452,14 +428,9 @@ impl CheckAttrVisitor<'_> {
                 true
             }
             _ => {
-                struct_span_err!(
-                    self.tcx.sess,
-                    attr_span,
-                    E0739,
-                    "attribute should be applied to function"
-                )
-                .span_label(span, "not a function")
-                .emit();
+                self.tcx
+                    .sess
+                    .emit_err(errors::TrackedCallerWrongLocation { attr_span, defn_span: span });
                 false
             }
         }
@@ -484,14 +455,10 @@ impl CheckAttrVisitor<'_> {
                 true
             }
             _ => {
-                struct_span_err!(
-                    self.tcx.sess,
-                    attr.span,
-                    E0701,
-                    "attribute can only be applied to a struct or enum"
-                )
-                .span_label(span, "not a struct or enum")
-                .emit();
+                self.tcx.sess.emit_err(errors::NonExhaustiveWrongLocation {
+                    attr_span: attr.span,
+                    defn_span: span,
+                });
                 false
             }
         }
@@ -510,11 +477,10 @@ impl CheckAttrVisitor<'_> {
                 true
             }
             _ => {
-                self.tcx
-                    .sess
-                    .struct_span_err(attr.span, "attribute can only be applied to a trait")
-                    .span_label(span, "not a trait")
-                    .emit();
+                self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToTrait {
+                    attr_span: attr.span,
+                    defn_span: span,
+                });
                 false
             }
         }
@@ -530,11 +496,10 @@ impl CheckAttrVisitor<'_> {
         match target {
             Target::Trait => true,
             _ => {
-                self.tcx
-                    .sess
-                    .struct_span_err(attr.span, "attribute can only be applied to a trait")
-                    .span_label(span, "not a trait")
-                    .emit();
+                self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToTrait {
+                    attr_span: attr.span,
+                    defn_span: span,
+                });
                 false
             }
         }
@@ -554,16 +519,12 @@ impl CheckAttrVisitor<'_> {
             // FIXME: #[target_feature] was previously erroneously allowed on statements and some
             // crates used this, so only emit a warning.
             Target::Statement => {
-                self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
-                    lint.build("attribute should be applied to a function")
-                        .warn(
-                            "this was previously accepted by the compiler but is \
-                             being phased out; it will become a hard error in \
-                             a future release!",
-                        )
-                        .span_label(span, "not a function")
-                        .emit();
-                });
+                self.tcx.emit_spanned_lint(
+                    UNUSED_ATTRIBUTES,
+                    hir_id,
+                    attr.span,
+                    errors::TargetFeatureOnStatement,
+                );
                 true
             }
             // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
@@ -575,11 +536,10 @@ impl CheckAttrVisitor<'_> {
                 true
             }
             _ => {
-                self.tcx
-                    .sess
-                    .struct_span_err(attr.span, "attribute should be applied to a function")
-                    .span_label(span, "not a function")
-                    .emit();
+                self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToFn {
+                    attr_span: attr.span,
+                    defn_span: span,
+                });
                 false
             }
         }
@@ -590,24 +550,17 @@ impl CheckAttrVisitor<'_> {
         match target {
             Target::ForeignStatic | Target::Static => true,
             _ => {
-                self.tcx
-                    .sess
-                    .struct_span_err(attr.span, "attribute should be applied to a static")
-                    .span_label(span, "not a static")
-                    .emit();
+                self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToStatic {
+                    attr_span: attr.span,
+                    defn_span: span,
+                });
                 false
             }
         }
     }
 
     fn doc_attr_str_error(&self, meta: &NestedMetaItem, attr_name: &str) {
-        self.tcx
-            .sess
-            .struct_span_err(
-                meta.span(),
-                &format!("doc {0} attribute expects a string: #[doc({0} = \"a\")]", attr_name),
-            )
-            .emit();
+        self.tcx.sess.emit_err(errors::DocExpectStr { attr_span: meta.span(), attr_name });
     }
 
     fn check_doc_alias_value(
@@ -620,22 +573,12 @@ impl CheckAttrVisitor<'_> {
         aliases: &mut FxHashMap<String, Span>,
     ) -> bool {
         let tcx = self.tcx;
-        let err_fn = move |span: Span, msg: &str| {
-            tcx.sess.span_err(
-                span,
-                &format!(
-                    "`#[doc(alias{})]` {}",
-                    if is_list { "(\"...\")" } else { " = \"...\"" },
-                    msg,
-                ),
-            );
-            false
-        };
+        let span = meta.name_value_literal_span().unwrap_or_else(|| meta.span());
+        let attr_str =
+            &format!("`#[doc(alias{})]`", if is_list { "(\"...\")" } else { " = \"...\"" });
         if doc_alias == kw::Empty {
-            return err_fn(
-                meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
-                "attribute cannot have empty value",
-            );
+            tcx.sess.emit_err(errors::DocAliasEmpty { span, attr_str });
+            return false;
         }
 
         let doc_alias_str = doc_alias.as_str();
@@ -643,23 +586,16 @@ impl CheckAttrVisitor<'_> {
             .chars()
             .find(|&c| c == '"' || c == '\'' || (c.is_whitespace() && c != ' '))
         {
-            self.tcx.sess.span_err(
-                meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
-                &format!(
-                    "{:?} character isn't allowed in `#[doc(alias{})]`",
-                    c,
-                    if is_list { "(\"...\")" } else { " = \"...\"" },
-                ),
-            );
+            tcx.sess.emit_err(errors::DocAliasBadChar { span, attr_str, char_: c });
             return false;
         }
         if doc_alias_str.starts_with(' ') || doc_alias_str.ends_with(' ') {
-            return err_fn(
-                meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
-                "cannot start or end with ' '",
-            );
+            tcx.sess.emit_err(errors::DocAliasStartEnd { span, attr_str });
+            return false;
         }
-        if let Some(err) = match target {
+
+        let span = meta.span();
+        if let Some(location) = match target {
             Target::Impl => Some("implementation block"),
             Target::ForeignMod => Some("extern block"),
             Target::AssocTy => {
@@ -685,19 +621,21 @@ impl CheckAttrVisitor<'_> {
             Target::Param => return false,
             _ => None,
         } {
-            return err_fn(meta.span(), &format!("isn't allowed on {}", err));
+            tcx.sess.emit_err(errors::DocAliasBadLocation { span, attr_str, location });
+            return false;
         }
         let item_name = self.tcx.hir().name(hir_id);
         if item_name == doc_alias {
-            return err_fn(meta.span(), "is the same as the item's name");
+            tcx.sess.emit_err(errors::DocAliasNotAnAlias { span, attr_str });
+            return false;
         }
-        let span = meta.span();
         if let Err(entry) = aliases.try_insert(doc_alias_str.to_owned(), span) {
-            self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, span, |lint| {
-                lint.build("doc alias is duplicated")
-                    .span_label(*entry.entry.get(), "first defined here")
-                    .emit();
-            });
+            self.tcx.emit_spanned_lint(
+                UNUSED_ATTRIBUTES,
+                hir_id,
+                span,
+                errors::DocAliasDuplicated { first_defn: *entry.entry.get() },
+            );
         }
         true
     }
@@ -722,22 +660,12 @@ impl CheckAttrVisitor<'_> {
                         _ => {
                             self.tcx
                                 .sess
-                                .struct_span_err(
-                                    v.span(),
-                                    "`#[doc(alias(\"a\"))]` expects string literals",
-                                )
-                                .emit();
+                                .emit_err(errors::DocAliasNotStringLiteral { span: v.span() });
                             errors += 1;
                         }
                     },
                     None => {
-                        self.tcx
-                            .sess
-                            .struct_span_err(
-                                v.span(),
-                                "`#[doc(alias(\"a\"))]` expects string literals",
-                            )
-                            .emit();
+                        self.tcx.sess.emit_err(errors::DocAliasNotStringLiteral { span: v.span() });
                         errors += 1;
                     }
                 }
@@ -746,14 +674,7 @@ impl CheckAttrVisitor<'_> {
         } else if let Some(doc_alias) = meta.value_str() {
             self.check_doc_alias_value(meta, doc_alias, hir_id, target, false, aliases)
         } else {
-            self.tcx
-                .sess
-                .struct_span_err(
-                    meta.span(),
-                    "doc alias attribute expects a string `#[doc(alias = \"a\")]` or a list of \
-                     strings `#[doc(alias(\"a\", \"b\"))]`",
-                )
-                .emit();
+            self.tcx.sess.emit_err(errors::DocAliasMalformed { span: meta.span() });
             false
         }
     }
@@ -770,65 +691,44 @@ impl CheckAttrVisitor<'_> {
         }) {
             Some(ItemKind::Mod(ref module)) => {
                 if !module.item_ids.is_empty() {
-                    self.tcx
-                        .sess
-                        .struct_span_err(
-                            meta.span(),
-                            "`#[doc(keyword = \"...\")]` can only be used on empty modules",
-                        )
-                        .emit();
+                    self.tcx.sess.emit_err(errors::DocKeywordEmptyMod { span: meta.span() });
                     return false;
                 }
             }
             _ => {
-                self.tcx
-                    .sess
-                    .struct_span_err(
-                        meta.span(),
-                        "`#[doc(keyword = \"...\")]` can only be used on modules",
-                    )
-                    .emit();
+                self.tcx.sess.emit_err(errors::DocKeywordNotMod { span: meta.span() });
                 return false;
             }
         }
         if !rustc_lexer::is_ident(doc_keyword.as_str()) {
-            self.tcx
-                .sess
-                .struct_span_err(
-                    meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
-                    &format!("`{doc_keyword}` is not a valid identifier"),
-                )
-                .emit();
+            self.tcx.sess.emit_err(errors::DocKeywordInvalidIdent {
+                span: meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
+                doc_keyword,
+            });
             return false;
         }
         true
     }
 
-    fn check_doc_tuple_variadic(&self, meta: &NestedMetaItem, hir_id: HirId) -> bool {
+    fn check_doc_fake_variadic(&self, meta: &NestedMetaItem, hir_id: HirId) -> bool {
         match self.tcx.hir().find(hir_id).and_then(|node| match node {
             hir::Node::Item(item) => Some(&item.kind),
             _ => None,
         }) {
             Some(ItemKind::Impl(ref i)) => {
-                if !matches!(&i.self_ty.kind, hir::TyKind::Tup([_])) {
-                    self.tcx
-                        .sess
-                        .struct_span_err(
-                            meta.span(),
-                            "`#[doc(tuple_variadic)]` must be used on the first of a set of tuple trait impls with varying arity",
-                        )
-                        .emit();
+                let is_valid = matches!(&i.self_ty.kind, hir::TyKind::Tup([_]))
+                    || if let hir::TyKind::BareFn(bare_fn_ty) = &i.self_ty.kind {
+                        bare_fn_ty.decl.inputs.len() == 1
+                    } else {
+                        false
+                    };
+                if !is_valid {
+                    self.tcx.sess.emit_err(errors::DocFakeVariadicNotValid { span: meta.span() });
                     return false;
                 }
             }
             _ => {
-                self.tcx
-                    .sess
-                    .struct_span_err(
-                        meta.span(),
-                        "`#[doc(keyword = \"...\")]` can only be used on impl blocks",
-                    )
-                    .emit();
+                self.tcx.sess.emit_err(errors::DocKeywordOnlyImpl { span: meta.span() });
                 return false;
             }
         }
@@ -857,13 +757,9 @@ impl CheckAttrVisitor<'_> {
             if let Some((prev_inline, prev_span)) = *specified_inline {
                 if do_inline != prev_inline {
                     let mut spans = MultiSpan::from_spans(vec![prev_span, meta.span()]);
-                    spans.push_span_label(prev_span, "this attribute...");
-                    spans.push_span_label(meta.span(), "...conflicts with this attribute");
-                    self.tcx
-                        .sess
-                        .struct_span_err(spans, "conflicting doc inlining attributes")
-                        .help("remove one of the conflicting attributes")
-                        .emit();
+                    spans.push_span_label(prev_span, fluent::passes::doc_inline_conflict_first);
+                    spans.push_span_label(meta.span(), fluent::passes::doc_inline_conflict_second);
+                    self.tcx.sess.emit_err(errors::DocKeywordConflict { spans });
                     return false;
                 }
                 true
@@ -872,23 +768,14 @@ impl CheckAttrVisitor<'_> {
                 true
             }
         } else {
-            self.tcx.struct_span_lint_hir(
+            self.tcx.emit_spanned_lint(
                 INVALID_DOC_ATTRIBUTES,
                 hir_id,
                 meta.span(),
-                |lint| {
-                    let mut err = lint.build(
-                        "this attribute can only be applied to a `use` item",
-                    );
-                    err.span_label(meta.span(), "only applicable on `use` items");
-                    if attr.style == AttrStyle::Outer {
-                        err.span_label(
-                            self.tcx.hir().span(hir_id),
-                            "not a `use` item",
-                        );
-                    }
-                    err.note("read https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#inline-and-no_inline for more information")
-                        .emit();
+                errors::DocInlineOnlyUse {
+                    attr_span: meta.span(),
+                    item_span: (attr.style == AttrStyle::Outer)
+                        .then(|| self.tcx.hir().span(hir_id)),
                 },
             );
             false
@@ -903,15 +790,7 @@ impl CheckAttrVisitor<'_> {
         attr_name: &str,
     ) -> bool {
         if CRATE_HIR_ID == hir_id {
-            self.tcx
-                .sess
-                .struct_span_err(
-                    meta.span(),
-                    &format!(
-                        "`#![doc({attr_name} = \"...\")]` isn't allowed as a crate-level attribute",
-                    ),
-                )
-                .emit();
+            self.tcx.sess.emit_err(errors::DocAttrNotCrateLevel { span: meta.span(), attr_name });
             return false;
         }
         true
@@ -925,36 +804,25 @@ impl CheckAttrVisitor<'_> {
         hir_id: HirId,
     ) -> bool {
         if hir_id != CRATE_HIR_ID {
-            self.tcx.struct_span_lint_hir(
-                INVALID_DOC_ATTRIBUTES,
-                hir_id,
-                meta.span(),
-                |lint| {
-                    let mut err = lint.build(
-                        "this attribute can only be applied at the crate level",
-                    );
-                    if attr.style == AttrStyle::Outer && self.tcx.hir().get_parent_item(hir_id) == CRATE_DEF_ID {
-                        if let Ok(mut src) =
-                            self.tcx.sess.source_map().span_to_snippet(attr.span)
-                        {
-                            src.insert(1, '!');
-                            err.span_suggestion_verbose(
-                                attr.span,
-                                "to apply to the crate, use an inner attribute",
-                                src,
-                                Applicability::MaybeIncorrect,
-                            );
-                        } else {
-                            err.span_help(
-                                attr.span,
-                                "to apply to the crate, use an inner attribute",
-                            );
-                        }
+            self.tcx.struct_span_lint_hir(INVALID_DOC_ATTRIBUTES, hir_id, meta.span(), |lint| {
+                let mut err = lint.build(fluent::passes::attr_crate_level);
+                if attr.style == AttrStyle::Outer
+                    && self.tcx.hir().get_parent_item(hir_id) == CRATE_DEF_ID
+                {
+                    if let Ok(mut src) = self.tcx.sess.source_map().span_to_snippet(attr.span) {
+                        src.insert(1, '!');
+                        err.span_suggestion_verbose(
+                            attr.span,
+                            fluent::passes::suggestion,
+                            src,
+                            Applicability::MaybeIncorrect,
+                        );
+                    } else {
+                        err.span_help(attr.span, fluent::passes::help);
                     }
-                    err.note("read https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#at-the-crate-level for more information")
-                        .emit();
-                },
-            );
+                }
+                err.note(fluent::passes::note).emit();
+            });
             return false;
         }
         true
@@ -969,18 +837,14 @@ impl CheckAttrVisitor<'_> {
                 match i_meta.name_or_empty() {
                     sym::attr | sym::no_crate_inject => {}
                     _ => {
-                        self.tcx.struct_span_lint_hir(
+                        self.tcx.emit_spanned_lint(
                             INVALID_DOC_ATTRIBUTES,
                             hir_id,
                             i_meta.span(),
-                            |lint| {
-                                lint.build(&format!(
-                                    "unknown `doc(test)` attribute `{}`",
-                                    rustc_ast_pretty::pprust::path_to_string(
-                                        &i_meta.meta_item().unwrap().path
-                                    ),
-                                ))
-                                .emit();
+                            errors::DocTestUnknown {
+                                path: rustc_ast_pretty::pprust::path_to_string(
+                                    &i_meta.meta_item().unwrap().path,
+                                ),
                             },
                         );
                         is_valid = false;
@@ -988,9 +852,12 @@ impl CheckAttrVisitor<'_> {
                 }
             }
         } else {
-            self.tcx.struct_span_lint_hir(INVALID_DOC_ATTRIBUTES, hir_id, meta.span(), |lint| {
-                lint.build("`#[doc(test(...)]` takes a list of attributes").emit();
-            });
+            self.tcx.emit_spanned_lint(
+                INVALID_DOC_ATTRIBUTES,
+                hir_id,
+                meta.span(),
+                errors::DocTestTakesList,
+            );
             is_valid = false;
         }
         is_valid
@@ -1030,9 +897,9 @@ impl CheckAttrVisitor<'_> {
                             is_valid = false
                         }
 
-                        sym::tuple_variadic
-                            if !self.check_attr_not_crate_level(meta, hir_id, "tuple_variadic")
-                                || !self.check_doc_tuple_variadic(meta, hir_id) =>
+                        sym::fake_variadic
+                            if !self.check_attr_not_crate_level(meta, hir_id, "fake_variadic")
+                                || !self.check_doc_fake_variadic(meta, hir_id) =>
                         {
                             is_valid = false
                         }
@@ -1082,7 +949,7 @@ impl CheckAttrVisitor<'_> {
                         | sym::notable_trait
                         | sym::passes
                         | sym::plugins
-                        | sym::tuple_variadic => {}
+                        | sym::fake_variadic => {}
 
                         sym::test => {
                             if !self.check_test_attr(meta, hir_id) {
@@ -1092,79 +959,66 @@ impl CheckAttrVisitor<'_> {
 
                         sym::primitive => {
                             if !self.tcx.features().rustdoc_internals {
-                                self.tcx.struct_span_lint_hir(
+                                self.tcx.emit_spanned_lint(
                                     INVALID_DOC_ATTRIBUTES,
                                     hir_id,
                                     i_meta.span,
-                                    |lint| {
-                                        let mut diag = lint.build(
-                                            "`doc(primitive)` should never have been stable",
-                                        );
-                                        diag.emit();
-                                    },
+                                    errors::DocPrimitive,
                                 );
                             }
                         }
 
                         _ => {
-                            self.tcx.struct_span_lint_hir(
-                                INVALID_DOC_ATTRIBUTES,
-                                hir_id,
-                                i_meta.span,
-                                |lint| {
-                                    let mut diag = lint.build(&format!(
-                                        "unknown `doc` attribute `{}`",
-                                        rustc_ast_pretty::pprust::path_to_string(&i_meta.path),
-                                    ));
-                                    if i_meta.has_name(sym::spotlight) {
-                                        diag.note(
-                                            "`doc(spotlight)` was renamed to `doc(notable_trait)`",
-                                        );
-                                        diag.span_suggestion_short(
-                                            i_meta.span,
-                                            "use `notable_trait` instead",
-                                            "notable_trait",
-                                            Applicability::MachineApplicable,
-                                        );
-                                        diag.note("`doc(spotlight)` is now a no-op");
+                            let path = rustc_ast_pretty::pprust::path_to_string(&i_meta.path);
+                            if i_meta.has_name(sym::spotlight) {
+                                self.tcx.emit_spanned_lint(
+                                    INVALID_DOC_ATTRIBUTES,
+                                    hir_id,
+                                    i_meta.span,
+                                    errors::DocTestUnknownSpotlight {
+                                        path,
+                                        span: i_meta.span
                                     }
-                                    if i_meta.has_name(sym::include) {
-                                        if let Some(value) = i_meta.value_str() {
-                                            // if there are multiple attributes, the suggestion would suggest deleting all of them, which is incorrect
-                                            let applicability = if list.len() == 1 {
-                                                Applicability::MachineApplicable
-                                            } else {
-                                                Applicability::MaybeIncorrect
-                                            };
-                                            let inner = if attr.style == AttrStyle::Inner {
-                                                "!"
-                                            } else {
-                                                ""
-                                            };
-                                            diag.span_suggestion(
-                                                attr.meta().unwrap().span,
-                                                "use `doc = include_str!` instead",
-                                                format!(
-                                                    "#{inner}[doc = include_str!(\"{value}\")]",
-                                                ),
-                                                applicability,
-                                            );
-                                        }
+                                );
+                            } else if i_meta.has_name(sym::include) &&
+                                    let Some(value) = i_meta.value_str() {
+                                let applicability = if list.len() == 1 {
+                                    Applicability::MachineApplicable
+                                } else {
+                                    Applicability::MaybeIncorrect
+                                };
+                                // If there are multiple attributes, the suggestion would suggest
+                                // deleting all of them, which is incorrect.
+                                self.tcx.emit_spanned_lint(
+                                    INVALID_DOC_ATTRIBUTES,
+                                    hir_id,
+                                    i_meta.span,
+                                    errors::DocTestUnknownInclude {
+                                        path,
+                                        value: value.to_string(),
+                                        inner: (attr.style == AttrStyle::Inner)
+                                            .then_some("!")
+                                            .unwrap_or(""),
+                                        sugg: (attr.meta().unwrap().span, applicability),
                                     }
-                                    diag.emit();
-                                },
-                            );
+                                );
+                            } else {
+                                self.tcx.emit_spanned_lint(
+                                    INVALID_DOC_ATTRIBUTES,
+                                    hir_id,
+                                    i_meta.span,
+                                    errors::DocTestUnknownAny { path }
+                                );
+                            }
                             is_valid = false;
                         }
                     }
                 } else {
-                    self.tcx.struct_span_lint_hir(
+                    self.tcx.emit_spanned_lint(
                         INVALID_DOC_ATTRIBUTES,
                         hir_id,
                         meta.span(),
-                        |lint| {
-                            lint.build("invalid `doc` attribute").emit();
-                        },
+                        errors::DocInvalid,
                     );
                     is_valid = false;
                 }
@@ -1179,14 +1033,7 @@ impl CheckAttrVisitor<'_> {
         match target {
             Target::Struct | Target::Enum | Target::TyAlias => true,
             _ => {
-                self.tcx
-                    .sess
-                    .struct_span_err(
-                        attr.span,
-                        "`pass_by_value` attribute should be applied to a struct, enum or type alias.",
-                    )
-                    .span_label(span, "is not a struct, enum or type alias")
-                    .emit();
+                self.tcx.sess.emit_err(errors::PassByValue { attr_span: attr.span, span });
                 false
             }
         }
@@ -1196,14 +1043,7 @@ impl CheckAttrVisitor<'_> {
         match target {
             Target::Method(MethodKind::Inherent) => true,
             _ => {
-                self.tcx
-                    .sess
-                    .struct_span_err(
-                        attr.span,
-                        "`rustc_allow_incoherent_impl` attribute should be applied to impl items.",
-                    )
-                    .span_label(span, "the only currently supported targets are inherent methods")
-                    .emit();
+                self.tcx.sess.emit_err(errors::AllowIncoherentImpl { attr_span: attr.span, span });
                 false
             }
         }
@@ -1222,12 +1062,7 @@ impl CheckAttrVisitor<'_> {
             _ => {
                 self.tcx
                     .sess
-                    .struct_span_err(
-                        attr.span,
-                        "`rustc_has_incoherent_inherent_impls` attribute should be applied to types or traits.",
-                    )
-                    .span_label(span, "only adts, extern types and traits are supported")
-                    .emit();
+                    .emit_err(errors::HasIncoherentInherentImpl { attr_span: attr.span, span });
                 false
             }
         }
@@ -1237,19 +1072,12 @@ impl CheckAttrVisitor<'_> {
     fn check_must_use(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) -> bool {
         let node = self.tcx.hir().get(hir_id);
         if let Some(kind) = node.fn_kind() && let rustc_hir::IsAsync::Async = kind.asyncness() {
-            self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
-                lint.build(
-                    "`must_use` attribute on `async` functions \
-                    applies to the anonymous `Future` returned by the \
-                    function, not the value within",
-                )
-                .span_label(
-                    span,
-                    "this attribute does nothing, the `Future`s \
-                    returned by async functions are already `must_use`",
-                )
-                .emit();
-            });
+            self.tcx.emit_spanned_lint(
+                UNUSED_ATTRIBUTES,
+                hir_id,
+                attr.span,
+                errors::MustUseAsync { span }
+            );
         }
 
         if !matches!(
@@ -1277,12 +1105,12 @@ impl CheckAttrVisitor<'_> {
                 _ => "a",
             };
 
-            self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
-                lint.build(&format!(
-                    "`#[must_use]` has no effect when applied to {article} {target}"
-                ))
-                .emit();
-            });
+            self.tcx.emit_spanned_lint(
+                UNUSED_ATTRIBUTES,
+                hir_id,
+                attr.span,
+                errors::MustUseNoEffect { article, target },
+            );
         }
 
         // For now, its always valid
@@ -1294,11 +1122,7 @@ impl CheckAttrVisitor<'_> {
         match target {
             Target::Struct | Target::Enum | Target::Union | Target::Trait => true,
             _ => {
-                self.tcx
-                    .sess
-                    .struct_span_err(attr.span, "`must_not_suspend` attribute should be applied to a struct, enum, or trait")
-                        .span_label(span, "is not a struct, enum, or trait")
-                        .emit();
+                self.tcx.sess.emit_err(errors::MustNotSuspend { attr_span: attr.span, span });
                 false
             }
         }
@@ -1318,16 +1142,12 @@ impl CheckAttrVisitor<'_> {
             _ => {
                 // FIXME: #[cold] was previously allowed on non-functions and some crates used
                 // this, so only emit a warning.
-                self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
-                    lint.build("attribute should be applied to a function")
-                        .warn(
-                            "this was previously accepted by the compiler but is \
-                             being phased out; it will become a hard error in \
-                             a future release!",
-                        )
-                        .span_label(span, "not a function")
-                        .emit();
-                });
+                self.tcx.emit_spanned_lint(
+                    UNUSED_ATTRIBUTES,
+                    hir_id,
+                    attr.span,
+                    errors::Cold { span },
+                );
             }
         }
     }
@@ -1342,19 +1162,12 @@ impl CheckAttrVisitor<'_> {
             return;
         }
 
-        self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
-            let mut diag =
-                lint.build("attribute should be applied to an `extern` block with non-Rust ABI");
-            diag.warn(
-                "this was previously accepted by the compiler but is \
-                 being phased out; it will become a hard error in \
-                 a future release!",
-            );
-            if target != Target::ForeignMod {
-                diag.span_label(span, "not an `extern` block");
-            }
-            diag.emit();
-        });
+        self.tcx.emit_spanned_lint(
+            UNUSED_ATTRIBUTES,
+            hir_id,
+            attr.span,
+            errors::Link { span: (target != Target::ForeignMod).then_some(span) },
+        );
     }
 
     /// Checks if `#[link_name]` is applied to an item other than a foreign function or static.
@@ -1371,30 +1184,22 @@ impl CheckAttrVisitor<'_> {
             _ => {
                 // FIXME: #[cold] was previously allowed on non-functions/statics and some crates
                 // used this, so only emit a warning.
-                self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
-                    let mut diag =
-                        lint.build("attribute should be applied to a foreign function or static");
-                    diag.warn(
-                        "this was previously accepted by the compiler but is \
-                         being phased out; it will become a hard error in \
-                         a future release!",
+                let attr_span = matches!(target, Target::ForeignMod).then_some(attr.span);
+                if let Some(s) = attr.value_str() {
+                    self.tcx.emit_spanned_lint(
+                        UNUSED_ATTRIBUTES,
+                        hir_id,
+                        attr.span,
+                        errors::LinkName { span, attr_span, value: s.as_str() },
                     );
-
-                    // See issue #47725
-                    if let Target::ForeignMod = target {
-                        if let Some(value) = attr.value_str() {
-                            diag.span_help(
-                                attr.span,
-                                &format!(r#"try `#[link(name = "{value}")]` instead"#),
-                            );
-                        } else {
-                            diag.span_help(attr.span, r#"try `#[link(name = "...")]` instead"#);
-                        }
-                    }
-
-                    diag.span_label(span, "not a foreign function or static");
-                    diag.emit();
-                });
+                } else {
+                    self.tcx.emit_spanned_lint(
+                        UNUSED_ATTRIBUTES,
+                        hir_id,
+                        attr.span,
+                        errors::LinkName { span, attr_span, value: "..." },
+                    );
+                };
             }
         }
     }
@@ -1412,14 +1217,7 @@ impl CheckAttrVisitor<'_> {
                 true
             }
             _ => {
-                self.tcx
-                    .sess
-                    .struct_span_err(
-                        attr.span,
-                        "attribute should be applied to an `extern crate` item",
-                    )
-                    .span_label(span, "not an `extern crate` item")
-                    .emit();
+                self.tcx.sess.emit_err(errors::NoLink { attr_span: attr.span, span });
                 false
             }
         }
@@ -1449,14 +1247,7 @@ impl CheckAttrVisitor<'_> {
                 true
             }
             _ => {
-                self.tcx
-                    .sess
-                    .struct_span_err(
-                        attr.span,
-                        "attribute should be applied to a free function, impl method or static",
-                    )
-                    .span_label(span, "not a free function, impl method or static")
-                    .emit();
+                self.tcx.sess.emit_err(errors::ExportName { attr_span: attr.span, span });
                 false
             }
         }
@@ -1469,11 +1260,10 @@ impl CheckAttrVisitor<'_> {
         target: Target,
     ) -> bool {
         if target != Target::Struct {
-            self.tcx
-                .sess
-                .struct_span_err(attr.span, "attribute should be applied to a struct")
-                .span_label(span, "not a struct")
-                .emit();
+            self.tcx.sess.emit_err(errors::RustcLayoutScalarValidRangeNotStruct {
+                attr_span: attr.span,
+                span,
+            });
             return false;
         }
 
@@ -1484,10 +1274,7 @@ impl CheckAttrVisitor<'_> {
         if matches!(&list[..], &[NestedMetaItem::Literal(Lit { kind: LitKind::Int(..), .. })]) {
             true
         } else {
-            self.tcx
-                .sess
-                .struct_span_err(attr.span, "expected exactly one integer literal argument")
-                .emit();
+            self.tcx.sess.emit_err(errors::RustcLayoutScalarValidRangeArg { attr_span: attr.span });
             false
         }
     }
@@ -1502,11 +1289,10 @@ impl CheckAttrVisitor<'_> {
     ) -> bool {
         let is_function = matches!(target, Target::Fn);
         if !is_function {
-            self.tcx
-                .sess
-                .struct_span_err(attr.span, "attribute should be applied to a function")
-                .span_label(span, "not a function")
-                .emit();
+            self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToFn {
+                attr_span: attr.span,
+                defn_span: span,
+            });
             return false;
         }
 
@@ -1526,29 +1312,20 @@ impl CheckAttrVisitor<'_> {
             match param.kind {
                 hir::GenericParamKind::Const { .. } => {}
                 _ => {
-                    self.tcx
-                        .sess
-                        .struct_span_err(
-                            attr.span,
-                            "#[rustc_legacy_const_generics] functions must \
-                             only have const generics",
-                        )
-                        .span_label(param.span, "non-const generic parameter")
-                        .emit();
+                    self.tcx.sess.emit_err(errors::RustcLegacyConstGenericsOnly {
+                        attr_span: attr.span,
+                        param_span: param.span,
+                    });
                     return false;
                 }
             }
         }
 
         if list.len() != generics.params.len() {
-            self.tcx
-                .sess
-                .struct_span_err(
-                    attr.span,
-                    "#[rustc_legacy_const_generics] must have one index for each generic parameter",
-                )
-                .span_label(generics.span, "generic parameters")
-                .emit();
+            self.tcx.sess.emit_err(errors::RustcLegacyConstGenericsIndex {
+                attr_span: attr.span,
+                generics_span: generics.span,
+            });
             return false;
         }
 
@@ -1558,19 +1335,10 @@ impl CheckAttrVisitor<'_> {
             if let Some(LitKind::Int(val, _)) = meta.literal().map(|lit| &lit.kind) {
                 if *val >= arg_count {
                     let span = meta.span();
-                    self.tcx
-                        .sess
-                        .struct_span_err(span, "index exceeds number of arguments")
-                        .span_label(
-                            span,
-                            format!(
-                                "there {} only {} argument{}",
-                                pluralize!("is", arg_count),
-                                arg_count,
-                                pluralize!(arg_count)
-                            ),
-                        )
-                        .emit();
+                    self.tcx.sess.emit_err(errors::RustcLegacyConstGenericsIndexExceed {
+                        span,
+                        arg_count: arg_count as usize,
+                    });
                     return false;
                 }
             } else {
@@ -1579,10 +1347,7 @@ impl CheckAttrVisitor<'_> {
         }
 
         if !invalid_args.is_empty() {
-            self.tcx
-                .sess
-                .struct_span_err(invalid_args, "arguments should be non-negative integers")
-                .emit();
+            self.tcx.sess.emit_err(errors::RustcLegacyConstGenericsIndexNegative { invalid_args });
             false
         } else {
             true
@@ -1594,11 +1359,10 @@ impl CheckAttrVisitor<'_> {
     fn check_applied_to_fn_or_method(&self, attr: &Attribute, span: Span, target: Target) -> bool {
         let is_function = matches!(target, Target::Fn | Target::Method(..));
         if !is_function {
-            self.tcx
-                .sess
-                .struct_span_err(attr.span, "attribute should be applied to a function")
-                .span_label(span, "not a function")
-                .emit();
+            self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToFn {
+                attr_span: attr.span,
+                defn_span: span,
+            });
             false
         } else {
             true
@@ -1622,16 +1386,42 @@ impl CheckAttrVisitor<'_> {
         self.check_applied_to_fn_or_method(attr, span, target)
     }
 
+    /// Checks that the `#[rustc_lint_opt_ty]` attribute is only applied to a struct.
+    fn check_rustc_lint_opt_ty(&self, attr: &Attribute, span: Span, target: Target) -> bool {
+        match target {
+            Target::Struct => true,
+            _ => {
+                self.tcx.sess.emit_err(errors::RustcLintOptTy { attr_span: attr.span, span });
+                false
+            }
+        }
+    }
+
+    /// Checks that the `#[rustc_lint_opt_deny_field_access]` attribute is only applied to a field.
+    fn check_rustc_lint_opt_deny_field_access(
+        &self,
+        attr: &Attribute,
+        span: Span,
+        target: Target,
+    ) -> bool {
+        match target {
+            Target::Field => true,
+            _ => {
+                self.tcx
+                    .sess
+                    .emit_err(errors::RustcLintOptDenyFieldAccess { attr_span: attr.span, span });
+                false
+            }
+        }
+    }
+
     /// Checks that the dep-graph debugging attributes are only present when the query-dep-graph
     /// option is passed to the compiler.
     fn check_rustc_dirty_clean(&self, attr: &Attribute) -> bool {
-        if self.tcx.sess.opts.debugging_opts.query_dep_graph {
+        if self.tcx.sess.opts.unstable_opts.query_dep_graph {
             true
         } else {
-            self.tcx
-                .sess
-                .struct_span_err(attr.span, "attribute requires -Z query-dep-graph to be enabled")
-                .emit();
+            self.tcx.sess.emit_err(errors::RustcDirtyClean { span: attr.span });
             false
         }
     }
@@ -1650,16 +1440,12 @@ impl CheckAttrVisitor<'_> {
             _ => {
                 // FIXME: #[link_section] was previously allowed on non-functions/statics and some
                 // crates used this, so only emit a warning.
-                self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
-                    lint.build("attribute should be applied to a function or static")
-                        .warn(
-                            "this was previously accepted by the compiler but is \
-                             being phased out; it will become a hard error in \
-                             a future release!",
-                        )
-                        .span_label(span, "not a function or static")
-                        .emit();
-                });
+                self.tcx.emit_spanned_lint(
+                    UNUSED_ATTRIBUTES,
+                    hir_id,
+                    attr.span,
+                    errors::LinkSection { span },
+                );
             }
         }
     }
@@ -1685,41 +1471,22 @@ impl CheckAttrVisitor<'_> {
                     Target::ForeignStatic => "static",
                     _ => unreachable!(),
                 };
-                self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
-                    lint.build(&format!(
-                        "`#[no_mangle]` has no effect on a foreign {foreign_item_kind}"
-                    ))
-                    .warn(
-                        "this was previously accepted by the compiler but is \
-                            being phased out; it will become a hard error in \
-                            a future release!",
-                    )
-                    .span_label(span, format!("foreign {foreign_item_kind}"))
-                    .note("symbol names in extern blocks are not mangled")
-                    .span_suggestion(
-                        attr.span,
-                        "remove this attribute",
-                        "",
-                        Applicability::MachineApplicable,
-                    )
-                    .emit();
-                });
+                self.tcx.emit_spanned_lint(
+                    UNUSED_ATTRIBUTES,
+                    hir_id,
+                    attr.span,
+                    errors::NoMangleForeign { span, attr_span: attr.span, foreign_item_kind },
+                );
             }
             _ => {
                 // FIXME: #[no_mangle] was previously allowed on non-functions/statics and some
                 // crates used this, so only emit a warning.
-                self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
-                    lint.build(
-                        "attribute should be applied to a free function, impl method or static",
-                    )
-                    .warn(
-                        "this was previously accepted by the compiler but is \
-                         being phased out; it will become a hard error in \
-                         a future release!",
-                    )
-                    .span_label(span, "not a free function, impl method or static")
-                    .emit();
-                });
+                self.tcx.emit_spanned_lint(
+                    UNUSED_ATTRIBUTES,
+                    hir_id,
+                    attr.span,
+                    errors::NoMangle { span },
+                );
             }
         }
     }
@@ -1752,13 +1519,7 @@ impl CheckAttrVisitor<'_> {
 
         for hint in &hints {
             if !hint.is_meta_item() {
-                struct_span_err!(
-                    self.tcx.sess,
-                    hint.span(),
-                    E0565,
-                    "meta item in `repr` must be an identifier"
-                )
-                .emit();
+                self.tcx.sess.emit_err(errors::ReprIdent { span: hint.span() });
                 continue;
             }
 
@@ -1808,21 +1569,6 @@ impl CheckAttrVisitor<'_> {
                         _ => ("a", "struct, enum, or union"),
                     }
                 }
-                sym::no_niche => {
-                    if !self.tcx.features().enabled(sym::no_niche) {
-                        feature_err(
-                            &self.tcx.sess.parse_sess,
-                            sym::no_niche,
-                            hint.span(),
-                            "the attribute `repr(no_niche)` is currently unstable",
-                        )
-                        .emit();
-                    }
-                    match target {
-                        Target::Struct | Target::Enum => continue,
-                        _ => ("a", "struct or enum"),
-                    }
-                }
                 sym::i8
                 | sym::u8
                 | sym::i16
@@ -1870,10 +1616,8 @@ impl CheckAttrVisitor<'_> {
         // This is not ideal, but tracking precisely which ones are at fault is a huge hassle.
         let hint_spans = hints.iter().map(|hint| hint.span());
 
-        // Error on repr(transparent, <anything else apart from no_niche>).
-        let non_no_niche = |hint: &&NestedMetaItem| hint.name_or_empty() != sym::no_niche;
-        let non_no_niche_count = hints.iter().filter(non_no_niche).count();
-        if is_transparent && non_no_niche_count > 1 {
+        // Error on repr(transparent, <anything else>).
+        if is_transparent && hints.len() > 1 {
             let hint_spans: Vec<_> = hint_spans.clone().collect();
             struct_span_err!(
                 self.tcx.sess,
@@ -1896,15 +1640,11 @@ impl CheckAttrVisitor<'_> {
                     return false;
                 }))
         {
-            self.tcx.struct_span_lint_hir(
+            self.tcx.emit_spanned_lint(
                 CONFLICTING_REPR_HINTS,
                 hir_id,
                 hint_spans.collect::<Vec<Span>>(),
-                |lint| {
-                    lint.build("conflicting representation hints")
-                        .code(rustc_errors::error_code!(E0566))
-                        .emit();
-                },
+                errors::ReprConflicting,
             );
         }
     }
@@ -1914,9 +1654,7 @@ impl CheckAttrVisitor<'_> {
         let mut used_compiler_span = None;
         for attr in attrs.iter().filter(|attr| attr.has_name(sym::used)) {
             if target != Target::Static {
-                self.tcx
-                    .sess
-                    .span_err(attr.span, "attribute must be applied to a `static` variable");
+                self.tcx.sess.emit_err(errors::UsedStatic { span: attr.span });
             }
             let inner = attr.meta_item_list();
             match inner.as_deref() {
@@ -1942,14 +1680,9 @@ impl CheckAttrVisitor<'_> {
             }
         }
         if let (Some(linker_span), Some(compiler_span)) = (used_linker_span, used_compiler_span) {
-            let spans = vec![linker_span, compiler_span];
             self.tcx
                 .sess
-                .struct_span_err(
-                    spans,
-                    "`used(compiler)` and `used(linker)` can't be used together",
-                )
-                .emit();
+                .emit_err(errors::UsedCompilerLinker { spans: vec![linker_span, compiler_span] });
         }
     }
 
@@ -1991,9 +1724,7 @@ impl CheckAttrVisitor<'_> {
             _ => {
                 self.tcx
                     .sess
-                    .struct_span_err(attr.span, "attribute should be applied to a macro")
-                    .span_label(span, "not a macro")
-                    .emit();
+                    .emit_err(errors::AllowInternalUnstable { attr_span: attr.span, span });
                 false
             }
         }
@@ -2004,29 +1735,26 @@ impl CheckAttrVisitor<'_> {
         match target {
             Target::Mod => {}
             _ => {
-                self.tcx
-                    .sess
-                    .struct_span_err(attr.span, "attribute should be applied to a module")
-                    .emit();
+                self.tcx.sess.emit_err(errors::DebugVisualizerPlacement { span: attr.span });
                 return false;
             }
         }
 
         let Some(hints) = attr.meta_item_list() else {
-            self.emit_debugger_visualizer_err(attr.span);
+            self.tcx.sess.emit_err(errors::DebugVisualizerInvalid { span: attr.span });
             return false;
         };
 
         let hint = match hints.len() {
             1 => &hints[0],
             _ => {
-                self.emit_debugger_visualizer_err(attr.span);
+                self.tcx.sess.emit_err(errors::DebugVisualizerInvalid { span: attr.span });
                 return false;
             }
         };
 
         let Some(meta_item) = hint.meta_item() else {
-            self.emit_debugger_visualizer_err(attr.span);
+            self.tcx.sess.emit_err(errors::DebugVisualizerInvalid { span: attr.span });
             return false;
         };
 
@@ -2034,7 +1762,7 @@ impl CheckAttrVisitor<'_> {
             (sym::natvis_file, Some(value)) => value,
             (sym::gdb_script_file, Some(value)) => value,
             (_, _) => {
-                self.emit_debugger_visualizer_err(meta_item.span);
+                self.tcx.sess.emit_err(errors::DebugVisualizerInvalid { span: meta_item.span });
                 return false;
             }
         };
@@ -2063,16 +1791,6 @@ impl CheckAttrVisitor<'_> {
         }
     }
 
-    fn emit_debugger_visualizer_err(&self, span: Span) {
-        self.tcx
-            .sess
-            .struct_span_err(span, "invalid argument")
-            .note(r#"expected: `natvis_file = "..."`"#)
-            .note(r#"OR"#)
-            .note(r#"expected: `gdb_script_file = "..."`"#)
-            .emit();
-    }
-
     /// Outputs an error for `#[allow_internal_unstable]` which can only be applied to macros.
     /// (Allows proc_macro functions)
     fn check_rustc_allow_const_fn_unstable(
@@ -2099,9 +1817,7 @@ impl CheckAttrVisitor<'_> {
             _ => {
                 self.tcx
                     .sess
-                    .struct_span_err(attr.span, "attribute should be applied to `const fn`")
-                    .span_label(span, "not a `const fn`")
-                    .emit();
+                    .emit_err(errors::RustcAllowConstFnUnstable { attr_span: attr.span, span });
                 false
             }
         }
@@ -2118,9 +1834,7 @@ impl CheckAttrVisitor<'_> {
             _ => {
                 self.tcx
                     .sess
-                    .struct_span_err(attr.span, "attribute should be applied functions or statics")
-                    .span_label(span, "not a function or static")
-                    .emit();
+                    .emit_err(errors::RustcStdInternalSymbol { attr_span: attr.span, span });
                 false
             }
         }
@@ -2131,10 +1845,7 @@ impl CheckAttrVisitor<'_> {
         match target {
             Target::Trait => true,
             _ => {
-                self.tcx
-                    .sess
-                    .struct_span_err(attr.span, "attribute should be applied to a trait")
-                    .emit();
+                self.tcx.sess.emit_err(errors::ConstTrait { attr_span: attr.span });
                 false
             }
         }
@@ -2143,10 +1854,7 @@ impl CheckAttrVisitor<'_> {
     fn check_stability_promotable(&self, attr: &Attribute, _span: Span, target: Target) -> bool {
         match target {
             Target::Expression => {
-                self.tcx
-                    .sess
-                    .struct_span_err(attr.span, "attribute cannot be applied to an expression")
-                    .emit();
+                self.tcx.sess.emit_err(errors::StabilityPromotable { attr_span: attr.span });
                 false
             }
             _ => true,
@@ -2156,9 +1864,12 @@ impl CheckAttrVisitor<'_> {
     fn check_deprecated(&self, hir_id: HirId, attr: &Attribute, _span: Span, target: Target) {
         match target {
             Target::Closure | Target::Expression | Target::Statement | Target::Arm => {
-                self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
-                    lint.build("attribute is ignored here").emit();
-                });
+                self.tcx.emit_spanned_lint(
+                    UNUSED_ATTRIBUTES,
+                    hir_id,
+                    attr.span,
+                    errors::Deprecated,
+                );
             }
             _ => {}
         }
@@ -2169,29 +1880,30 @@ impl CheckAttrVisitor<'_> {
         match target {
             Target::ExternCrate | Target::Mod => {}
             _ => {
-                self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
-                    lint.build(&format!(
-                        "`#[{name}]` only has an effect on `extern crate` and modules"
-                    ))
-                    .emit();
-                });
+                self.tcx.emit_spanned_lint(
+                    UNUSED_ATTRIBUTES,
+                    hir_id,
+                    attr.span,
+                    errors::MacroUse { name },
+                );
             }
         }
     }
 
     fn check_macro_export(&self, hir_id: HirId, attr: &Attribute, target: Target) {
         if target != Target::MacroDef {
-            self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
-                lint.build("`#[macro_export]` only has an effect on macro definitions").emit();
-            });
+            self.tcx.emit_spanned_lint(UNUSED_ATTRIBUTES, hir_id, attr.span, errors::MacroExport);
         }
     }
 
     fn check_plugin_registrar(&self, hir_id: HirId, attr: &Attribute, target: Target) {
         if target != Target::Fn {
-            self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
-                lint.build("`#[plugin_registrar]` only has an effect on functions").emit();
-            });
+            self.tcx.emit_spanned_lint(
+                UNUSED_ATTRIBUTES,
+                hir_id,
+                attr.span,
+                errors::PluginRegistrar,
+            );
         }
     }
 
@@ -2210,10 +1922,7 @@ impl CheckAttrVisitor<'_> {
                 | sym::target_feature
         ) && attr.meta_item_list().map_or(false, |list| list.is_empty())
         {
-            format!(
-                "attribute `{}` with an empty list has no effect",
-                attr.name_or_empty()
-            )
+            errors::UnusedNote::EmptyList { name: attr.name_or_empty() }
         } else if matches!(
                 attr.name_or_empty(),
                 sym::allow | sym::warn | sym::deny | sym::forbid | sym::expect
@@ -2223,27 +1932,19 @@ impl CheckAttrVisitor<'_> {
             && let MetaItemKind::NameValue(_) = &item.kind
             && item.path == sym::reason
         {
-            format!(
-                "attribute `{}` without any lints has no effect",
-                attr.name_or_empty()
-            )
+            errors::UnusedNote::NoLints { name: attr.name_or_empty() }
         } else if attr.name_or_empty() == sym::default_method_body_is_const {
-            format!("`default_method_body_is_const` has been replaced with `#[const_trait]` on traits")
+            errors::UnusedNote::DefaultMethodBodyConst
         } else {
             return;
         };
 
-        self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
-            lint.build("unused attribute")
-                .span_suggestion(
-                    attr.span,
-                    "remove this attribute",
-                    "",
-                    Applicability::MachineApplicable,
-                )
-                .note(&note)
-                .emit();
-        });
+        self.tcx.emit_spanned_lint(
+            UNUSED_ATTRIBUTES,
+            hir_id,
+            attr.span,
+            errors::Unused { attr_span: attr.span, note },
+        );
     }
 }
 
@@ -2414,14 +2115,7 @@ fn check_non_exported_macro_for_invalid_attrs(tcx: TyCtxt<'_>, item: &Item<'_>)
 
     for attr in attrs {
         if attr.has_name(sym::inline) {
-            struct_span_err!(
-                tcx.sess,
-                attr.span,
-                E0518,
-                "attribute should be applied to function or closure",
-            )
-            .span_label(attr.span, "not a function or closure")
-            .emit();
+            tcx.sess.emit_err(errors::NonExportedMacroInvalidAttrs { attr_span: attr.span });
         }
     }
 }
@@ -2461,23 +2155,20 @@ fn check_duplicates(
                     } else {
                         (attr.span, *entry.get())
                     };
-                    tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, this, |lint| {
-                        let mut db = lint.build("unused attribute");
-                        db.span_note(other, "attribute also specified here").span_suggestion(
+                    tcx.emit_spanned_lint(
+                        UNUSED_ATTRIBUTES,
+                        hir_id,
+                        this,
+                        errors::UnusedDuplicate {
                             this,
-                            "remove this attribute",
-                            "",
-                            Applicability::MachineApplicable,
-                        );
-                        if matches!(duplicates, FutureWarnFollowing | FutureWarnPreceding) {
-                            db.warn(
-                                "this was previously accepted by the compiler but is \
-                                 being phased out; it will become a hard error in \
-                                 a future release!",
-                            );
-                        }
-                        db.emit();
-                    });
+                            other,
+                            warning: matches!(
+                                duplicates,
+                                FutureWarnFollowing | FutureWarnPreceding
+                            )
+                            .then_some(()),
+                        },
+                    );
                 }
                 Entry::Vacant(entry) => {
                     entry.insert(attr.span);
@@ -2492,19 +2183,11 @@ fn check_duplicates(
                 } else {
                     (attr.span, *entry.get())
                 };
-                tcx.sess
-                    .struct_span_err(
-                        this,
-                        &format!("multiple `{}` attributes", attr.name_or_empty()),
-                    )
-                    .span_note(other, "attribute also specified here")
-                    .span_suggestion(
-                        this,
-                        "remove this attribute",
-                        "",
-                        Applicability::MachineApplicable,
-                    )
-                    .emit();
+                tcx.sess.emit_err(errors::UnusedMultiple {
+                    this,
+                    other,
+                    name: attr.name_or_empty(),
+                });
             }
             Entry::Vacant(entry) => {
                 entry.insert(attr.span);
diff --git a/compiler/rustc_passes/src/check_const.rs b/compiler/rustc_passes/src/check_const.rs
index 31c159c1f75..f1a81b65329 100644
--- a/compiler/rustc_passes/src/check_const.rs
+++ b/compiler/rustc_passes/src/check_const.rs
@@ -122,7 +122,7 @@ impl<'tcx> CheckConstVisitor<'tcx> {
             // `-Zunleash-the-miri-inside-of-you` only works for expressions that don't have a
             // corresponding feature gate. This encourages nightly users to use feature gates when
             // possible.
-            None if tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you => {
+            None if tcx.sess.opts.unstable_opts.unleash_the_miri_inside_of_you => {
                 tcx.sess.span_warn(span, "skipping const checks");
                 return;
             }
diff --git a/compiler/rustc_passes/src/entry.rs b/compiler/rustc_passes/src/entry.rs
index 1add91fc9c5..7381019a620 100644
--- a/compiler/rustc_passes/src/entry.rs
+++ b/compiler/rustc_passes/src/entry.rs
@@ -77,7 +77,7 @@ fn err_if_attr_found(ctxt: &EntryContext<'_>, attrs: &[Attribute], sym: Symbol)
             .sess
             .struct_span_err(
                 attr.span,
-                &format!("`{}` attribute can only be used on functions", sym.as_str()),
+                &format!("`{}` attribute can only be used on functions", sym),
             )
             .emit();
     }
diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs
new file mode 100644
index 00000000000..5feb0e2956b
--- /dev/null
+++ b/compiler/rustc_passes/src/errors.rs
@@ -0,0 +1,645 @@
+use rustc_errors::{Applicability, MultiSpan};
+use rustc_macros::{LintDiagnostic, SessionDiagnostic, SessionSubdiagnostic};
+use rustc_span::{Span, Symbol};
+
+#[derive(LintDiagnostic)]
+#[lint(passes::outer_crate_level_attr)]
+pub struct OuterCrateLevelAttr;
+
+#[derive(LintDiagnostic)]
+#[lint(passes::inner_crate_level_attr)]
+pub struct InnerCrateLevelAttr;
+
+#[derive(LintDiagnostic)]
+#[lint(passes::ignored_attr_with_macro)]
+pub struct IgnoredAttrWithMacro<'a> {
+    pub sym: &'a str,
+}
+
+#[derive(LintDiagnostic)]
+#[lint(passes::ignored_attr)]
+pub struct IgnoredAttr<'a> {
+    pub sym: &'a str,
+}
+
+#[derive(LintDiagnostic)]
+#[lint(passes::inline_ignored_function_prototype)]
+pub struct IgnoredInlineAttrFnProto;
+
+#[derive(LintDiagnostic)]
+#[lint(passes::inline_ignored_constants)]
+#[warn_]
+#[note]
+pub struct IgnoredInlineAttrConstants;
+
+#[derive(SessionDiagnostic)]
+#[error(passes::inline_not_fn_or_closure, code = "E0518")]
+pub struct InlineNotFnOrClosure {
+    #[primary_span]
+    pub attr_span: Span,
+    #[label]
+    pub defn_span: Span,
+}
+
+#[derive(LintDiagnostic)]
+#[lint(passes::no_coverage_ignored_function_prototype)]
+pub struct IgnoredNoCoverageFnProto;
+
+#[derive(LintDiagnostic)]
+#[lint(passes::no_coverage_propagate)]
+pub struct IgnoredNoCoveragePropagate;
+
+#[derive(LintDiagnostic)]
+#[lint(passes::no_coverage_fn_defn)]
+pub struct IgnoredNoCoverageFnDefn;
+
+#[derive(SessionDiagnostic)]
+#[error(passes::no_coverage_not_coverable, code = "E0788")]
+pub struct IgnoredNoCoverageNotCoverable {
+    #[primary_span]
+    pub attr_span: Span,
+    #[label]
+    pub defn_span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(passes::should_be_applied_to_fn)]
+pub struct AttrShouldBeAppliedToFn {
+    #[primary_span]
+    pub attr_span: Span,
+    #[label]
+    pub defn_span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(passes::naked_tracked_caller, code = "E0736")]
+pub struct NakedTrackedCaller {
+    #[primary_span]
+    pub attr_span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(passes::should_be_applied_to_fn, code = "E0739")]
+pub struct TrackedCallerWrongLocation {
+    #[primary_span]
+    pub attr_span: Span,
+    #[label]
+    pub defn_span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(passes::should_be_applied_to_struct_enum, code = "E0701")]
+pub struct NonExhaustiveWrongLocation {
+    #[primary_span]
+    pub attr_span: Span,
+    #[label]
+    pub defn_span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(passes::should_be_applied_to_trait)]
+pub struct AttrShouldBeAppliedToTrait {
+    #[primary_span]
+    pub attr_span: Span,
+    #[label]
+    pub defn_span: Span,
+}
+
+#[derive(LintDiagnostic)]
+#[lint(passes::target_feature_on_statement)]
+pub struct TargetFeatureOnStatement;
+
+#[derive(SessionDiagnostic)]
+#[error(passes::should_be_applied_to_static)]
+pub struct AttrShouldBeAppliedToStatic {
+    #[primary_span]
+    pub attr_span: Span,
+    #[label]
+    pub defn_span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(passes::doc_expect_str)]
+pub struct DocExpectStr<'a> {
+    #[primary_span]
+    pub attr_span: Span,
+    pub attr_name: &'a str,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(passes::doc_alias_empty)]
+pub struct DocAliasEmpty<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub attr_str: &'a str,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(passes::doc_alias_bad_char)]
+pub struct DocAliasBadChar<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub attr_str: &'a str,
+    pub char_: char,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(passes::doc_alias_start_end)]
+pub struct DocAliasStartEnd<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub attr_str: &'a str,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(passes::doc_alias_bad_location)]
+pub struct DocAliasBadLocation<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub attr_str: &'a str,
+    pub location: &'a str,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(passes::doc_alias_not_an_alias)]
+pub struct DocAliasNotAnAlias<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub attr_str: &'a str,
+}
+
+#[derive(LintDiagnostic)]
+#[lint(passes::doc_alias_duplicated)]
+pub struct DocAliasDuplicated {
+    #[label]
+    pub first_defn: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(passes::doc_alias_not_string_literal)]
+pub struct DocAliasNotStringLiteral {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(passes::doc_alias_malformed)]
+pub struct DocAliasMalformed {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(passes::doc_keyword_empty_mod)]
+pub struct DocKeywordEmptyMod {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(passes::doc_keyword_not_mod)]
+pub struct DocKeywordNotMod {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(passes::doc_keyword_invalid_ident)]
+pub struct DocKeywordInvalidIdent {
+    #[primary_span]
+    pub span: Span,
+    pub doc_keyword: Symbol,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(passes::doc_fake_variadic_not_valid)]
+pub struct DocFakeVariadicNotValid {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(passes::doc_keyword_only_impl)]
+pub struct DocKeywordOnlyImpl {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(passes::doc_inline_conflict)]
+#[help]
+pub struct DocKeywordConflict {
+    #[primary_span]
+    pub spans: MultiSpan,
+}
+
+#[derive(LintDiagnostic)]
+#[lint(passes::doc_inline_only_use)]
+#[note]
+pub struct DocInlineOnlyUse {
+    #[label]
+    pub attr_span: Span,
+    #[label(passes::not_a_use_item_label)]
+    pub item_span: Option<Span>,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(passes::doc_attr_not_crate_level)]
+pub struct DocAttrNotCrateLevel<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub attr_name: &'a str,
+}
+
+#[derive(LintDiagnostic)]
+#[lint(passes::doc_test_unknown)]
+pub struct DocTestUnknown {
+    pub path: String,
+}
+
+#[derive(LintDiagnostic)]
+#[lint(passes::doc_test_takes_list)]
+pub struct DocTestTakesList;
+
+#[derive(LintDiagnostic)]
+#[lint(passes::doc_primitive)]
+pub struct DocPrimitive;
+
+#[derive(LintDiagnostic)]
+#[lint(passes::doc_test_unknown_any)]
+pub struct DocTestUnknownAny {
+    pub path: String,
+}
+
+#[derive(LintDiagnostic)]
+#[lint(passes::doc_test_unknown_spotlight)]
+#[note]
+#[note(passes::no_op_note)]
+pub struct DocTestUnknownSpotlight {
+    pub path: String,
+    #[suggestion_short(applicability = "machine-applicable", code = "notable_trait")]
+    pub span: Span,
+}
+
+#[derive(LintDiagnostic)]
+#[lint(passes::doc_test_unknown_include)]
+pub struct DocTestUnknownInclude {
+    pub path: String,
+    pub value: String,
+    pub inner: &'static str,
+    #[suggestion(code = "#{inner}[doc = include_str!(\"{value}\")]")]
+    pub sugg: (Span, Applicability),
+}
+
+#[derive(LintDiagnostic)]
+#[lint(passes::doc_invalid)]
+pub struct DocInvalid;
+
+#[derive(SessionDiagnostic)]
+#[error(passes::pass_by_value)]
+pub struct PassByValue {
+    #[primary_span]
+    pub attr_span: Span,
+    #[label]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(passes::allow_incoherent_impl)]
+pub struct AllowIncoherentImpl {
+    #[primary_span]
+    pub attr_span: Span,
+    #[label]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(passes::has_incoherent_inherent_impl)]
+pub struct HasIncoherentInherentImpl {
+    #[primary_span]
+    pub attr_span: Span,
+    #[label]
+    pub span: Span,
+}
+
+#[derive(LintDiagnostic)]
+#[lint(passes::must_use_async)]
+pub struct MustUseAsync {
+    #[label]
+    pub span: Span,
+}
+
+#[derive(LintDiagnostic)]
+#[lint(passes::must_use_no_effect)]
+pub struct MustUseNoEffect {
+    pub article: &'static str,
+    pub target: rustc_hir::Target,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(passes::must_not_suspend)]
+pub struct MustNotSuspend {
+    #[primary_span]
+    pub attr_span: Span,
+    #[label]
+    pub span: Span,
+}
+
+#[derive(LintDiagnostic)]
+#[lint(passes::cold)]
+#[warn_]
+pub struct Cold {
+    #[label]
+    pub span: Span,
+}
+
+#[derive(LintDiagnostic)]
+#[lint(passes::link)]
+#[warn_]
+pub struct Link {
+    #[label]
+    pub span: Option<Span>,
+}
+
+#[derive(LintDiagnostic)]
+#[lint(passes::link_name)]
+#[warn_]
+pub struct LinkName<'a> {
+    #[help]
+    pub attr_span: Option<Span>,
+    #[label]
+    pub span: Span,
+    pub value: &'a str,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(passes::no_link)]
+pub struct NoLink {
+    #[primary_span]
+    pub attr_span: Span,
+    #[label]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(passes::export_name)]
+pub struct ExportName {
+    #[primary_span]
+    pub attr_span: Span,
+    #[label]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(passes::rustc_layout_scalar_valid_range_not_struct)]
+pub struct RustcLayoutScalarValidRangeNotStruct {
+    #[primary_span]
+    pub attr_span: Span,
+    #[label]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(passes::rustc_layout_scalar_valid_range_arg)]
+pub struct RustcLayoutScalarValidRangeArg {
+    #[primary_span]
+    pub attr_span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(passes::rustc_legacy_const_generics_only)]
+pub struct RustcLegacyConstGenericsOnly {
+    #[primary_span]
+    pub attr_span: Span,
+    #[label]
+    pub param_span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(passes::rustc_legacy_const_generics_index)]
+pub struct RustcLegacyConstGenericsIndex {
+    #[primary_span]
+    pub attr_span: Span,
+    #[label]
+    pub generics_span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(passes::rustc_legacy_const_generics_index_exceed)]
+pub struct RustcLegacyConstGenericsIndexExceed {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    pub arg_count: usize,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(passes::rustc_legacy_const_generics_index_negative)]
+pub struct RustcLegacyConstGenericsIndexNegative {
+    #[primary_span]
+    pub invalid_args: Vec<Span>,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(passes::rustc_dirty_clean)]
+pub struct RustcDirtyClean {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(LintDiagnostic)]
+#[lint(passes::link_section)]
+#[warn_]
+pub struct LinkSection {
+    #[label]
+    pub span: Span,
+}
+
+#[derive(LintDiagnostic)]
+#[lint(passes::no_mangle_foreign)]
+#[warn_]
+#[note]
+pub struct NoMangleForeign {
+    #[label]
+    pub span: Span,
+    #[suggestion(applicability = "machine-applicable")]
+    pub attr_span: Span,
+    pub foreign_item_kind: &'static str,
+}
+
+#[derive(LintDiagnostic)]
+#[lint(passes::no_mangle)]
+#[warn_]
+pub struct NoMangle {
+    #[label]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(passes::repr_ident, code = "E0565")]
+pub struct ReprIdent {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(LintDiagnostic)]
+#[lint(passes::repr_conflicting, code = "E0566")]
+pub struct ReprConflicting;
+
+#[derive(SessionDiagnostic)]
+#[error(passes::used_static)]
+pub struct UsedStatic {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(passes::used_compiler_linker)]
+pub struct UsedCompilerLinker {
+    #[primary_span]
+    pub spans: Vec<Span>,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(passes::allow_internal_unstable)]
+pub struct AllowInternalUnstable {
+    #[primary_span]
+    pub attr_span: Span,
+    #[label]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(passes::debug_visualizer_placement)]
+pub struct DebugVisualizerPlacement {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(passes::debug_visualizer_invalid)]
+#[note(passes::note_1)]
+#[note(passes::note_2)]
+#[note(passes::note_3)]
+pub struct DebugVisualizerInvalid {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(passes::rustc_allow_const_fn_unstable)]
+pub struct RustcAllowConstFnUnstable {
+    #[primary_span]
+    pub attr_span: Span,
+    #[label]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(passes::rustc_std_internal_symbol)]
+pub struct RustcStdInternalSymbol {
+    #[primary_span]
+    pub attr_span: Span,
+    #[label]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(passes::const_trait)]
+pub struct ConstTrait {
+    #[primary_span]
+    pub attr_span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(passes::stability_promotable)]
+pub struct StabilityPromotable {
+    #[primary_span]
+    pub attr_span: Span,
+}
+
+#[derive(LintDiagnostic)]
+#[lint(passes::deprecated)]
+pub struct Deprecated;
+
+#[derive(LintDiagnostic)]
+#[lint(passes::macro_use)]
+pub struct MacroUse {
+    pub name: Symbol,
+}
+
+#[derive(LintDiagnostic)]
+#[lint(passes::macro_export)]
+pub struct MacroExport;
+
+#[derive(LintDiagnostic)]
+#[lint(passes::plugin_registrar)]
+pub struct PluginRegistrar;
+
+#[derive(SessionSubdiagnostic)]
+pub enum UnusedNote {
+    #[note(passes::unused_empty_lints_note)]
+    EmptyList { name: Symbol },
+    #[note(passes::unused_no_lints_note)]
+    NoLints { name: Symbol },
+    #[note(passes::unused_default_method_body_const_note)]
+    DefaultMethodBodyConst,
+}
+
+#[derive(LintDiagnostic)]
+#[lint(passes::unused)]
+pub struct Unused {
+    #[suggestion(applicability = "machine-applicable")]
+    pub attr_span: Span,
+    #[subdiagnostic]
+    pub note: UnusedNote,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(passes::non_exported_macro_invalid_attrs, code = "E0518")]
+pub struct NonExportedMacroInvalidAttrs {
+    #[primary_span]
+    #[label]
+    pub attr_span: Span,
+}
+
+#[derive(LintDiagnostic)]
+#[lint(passes::unused_duplicate)]
+pub struct UnusedDuplicate {
+    #[primary_span]
+    #[suggestion(code = "", applicability = "machine-applicable")]
+    pub this: Span,
+    #[note]
+    pub other: Span,
+    #[warn_]
+    pub warning: Option<()>,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(passes::unused_multiple)]
+pub struct UnusedMultiple {
+    #[primary_span]
+    #[suggestion(code = "", applicability = "machine-applicable")]
+    pub this: Span,
+    #[note]
+    pub other: Span,
+    pub name: Symbol,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(passes::rustc_lint_opt_ty)]
+pub struct RustcLintOptTy {
+    #[primary_span]
+    pub attr_span: Span,
+    #[label]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(passes::rustc_lint_opt_deny_field_access)]
+pub struct RustcLintOptDenyFieldAccess {
+    #[primary_span]
+    pub attr_span: Span,
+    #[label]
+    pub span: Span,
+}
diff --git a/compiler/rustc_passes/src/hir_id_validator.rs b/compiler/rustc_passes/src/hir_id_validator.rs
index 9deb0042ff3..212ea9e57a3 100644
--- a/compiler/rustc_passes/src/hir_id_validator.rs
+++ b/compiler/rustc_passes/src/hir_id_validator.rs
@@ -11,7 +11,7 @@ use rustc_middle::ty::TyCtxt;
 pub fn check_crate(tcx: TyCtxt<'_>) {
     tcx.dep_graph.assert_ignored();
 
-    if tcx.sess.opts.debugging_opts.hir_stats {
+    if tcx.sess.opts.unstable_opts.hir_stats {
         crate::hir_stats::print_hir_stats(tcx);
     }
 
diff --git a/compiler/rustc_passes/src/lib.rs b/compiler/rustc_passes/src/lib.rs
index 497c0931c21..defa9d15296 100644
--- a/compiler/rustc_passes/src/lib.rs
+++ b/compiler/rustc_passes/src/lib.rs
@@ -7,8 +7,8 @@
 #![allow(rustc::potential_query_instability)]
 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
 #![feature(iter_intersperse)]
+#![cfg_attr(bootstrap, feature(let_chains))]
 #![feature(let_else)]
-#![feature(let_chains)]
 #![feature(map_try_insert)]
 #![feature(min_specialization)]
 #![feature(try_blocks)]
@@ -27,6 +27,7 @@ pub mod dead;
 mod debugger_visualizer;
 mod diagnostic_items;
 pub mod entry;
+mod errors;
 pub mod hir_id_validator;
 pub mod hir_stats;
 mod lang_items;
diff --git a/compiler/rustc_passes/src/lib_features.rs b/compiler/rustc_passes/src/lib_features.rs
index 26bfa4737a7..e05994f13e4 100644
--- a/compiler/rustc_passes/src/lib_features.rs
+++ b/compiler/rustc_passes/src/lib_features.rs
@@ -1,8 +1,8 @@
-// Detecting lib features (i.e., features that are not lang features).
-//
-// These are declared using stability attributes (e.g., `#[stable (..)]`
-// and `#[unstable (..)]`), but are not declared in one single location
-// (unlike lang features), which means we need to collect them instead.
+//! Detecting lib features (i.e., features that are not lang features).
+//!
+//! These are declared using stability attributes (e.g., `#[stable (..)]` and `#[unstable (..)]`),
+//! but are not declared in one single location (unlike lang features), which means we need to
+//! collect them instead.
 
 use rustc_ast::{Attribute, MetaItemKind};
 use rustc_errors::struct_span_err;
@@ -71,11 +71,11 @@ impl<'tcx> LibFeatureCollector<'tcx> {
 
     fn collect_feature(&mut self, feature: Symbol, since: Option<Symbol>, span: Span) {
         let already_in_stable = self.lib_features.stable.contains_key(&feature);
-        let already_in_unstable = self.lib_features.unstable.contains(&feature);
+        let already_in_unstable = self.lib_features.unstable.contains_key(&feature);
 
         match (since, already_in_stable, already_in_unstable) {
             (Some(since), _, false) => {
-                if let Some(prev_since) = self.lib_features.stable.get(&feature) {
+                if let Some((prev_since, _)) = self.lib_features.stable.get(&feature) {
                     if *prev_since != since {
                         self.span_feature_error(
                             span,
@@ -89,10 +89,10 @@ impl<'tcx> LibFeatureCollector<'tcx> {
                     }
                 }
 
-                self.lib_features.stable.insert(feature, since);
+                self.lib_features.stable.insert(feature, (since, span));
             }
             (None, false, _) => {
-                self.lib_features.unstable.insert(feature);
+                self.lib_features.unstable.insert(feature, span);
             }
             (Some(_), _, true) | (None, true, _) => {
                 self.span_feature_error(
diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs
index 0070c0699a4..eed3e1579a2 100644
--- a/compiler/rustc_passes/src/liveness.rs
+++ b/compiler/rustc_passes/src/liveness.rs
@@ -278,7 +278,7 @@ impl<'tcx> IrMaps<'tcx> {
                     pats.extend(inner_pat.iter());
                 }
                 Struct(_, fields, _) => {
-                    let (short, not_short): (Vec<&_>, Vec<&_>) =
+                    let (short, not_short): (Vec<_>, _) =
                         fields.iter().partition(|f| f.is_shorthand);
                     shorthand_field_ids.extend(short.iter().map(|f| f.pat.hir_id));
                     pats.extend(not_short.iter().map(|f| f.pat));
@@ -298,7 +298,7 @@ impl<'tcx> IrMaps<'tcx> {
             }
         }
 
-        return shorthand_field_ids;
+        shorthand_field_ids
     }
 
     fn add_from_pat(&mut self, pat: &hir::Pat<'tcx>) {
@@ -368,6 +368,9 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> {
 
     fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>) {
         self.add_from_pat(&local.pat);
+        if local.els.is_some() {
+            self.add_live_node_for_node(local.hir_id, ExprNode(local.span, local.hir_id));
+        }
         intravisit::walk_local(self, local);
     }
 
@@ -800,8 +803,40 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
                 // initialization, which is mildly more complex than checking
                 // once at the func header but otherwise equivalent.
 
-                let succ = self.propagate_through_opt_expr(local.init, succ);
-                self.define_bindings_in_pat(&local.pat, succ)
+                if let Some(els) = local.els {
+                    // Eventually, `let pat: ty = init else { els };` is mostly equivalent to
+                    // `let (bindings, ...) = match init { pat => (bindings, ...), _ => els };`
+                    // except that extended lifetime applies at the `init` location.
+                    //
+                    //       (e)
+                    //        |
+                    //        v
+                    //      (expr)
+                    //      /   \
+                    //     |     |
+                    //     v     v
+                    // bindings  els
+                    //     |
+                    //     v
+                    // ( succ )
+                    //
+                    if let Some(init) = local.init {
+                        let else_ln = self.propagate_through_block(els, succ);
+                        let ln = self.live_node(local.hir_id, local.span);
+                        self.init_from_succ(ln, succ);
+                        self.merge_from_succ(ln, else_ln);
+                        let succ = self.propagate_through_expr(init, ln);
+                        self.define_bindings_in_pat(&local.pat, succ)
+                    } else {
+                        span_bug!(
+                            stmt.span,
+                            "variable is uninitialized but an unexpected else branch is found"
+                        )
+                    }
+                } else {
+                    let succ = self.propagate_through_opt_expr(local.init, succ);
+                    self.define_bindings_in_pat(&local.pat, succ)
+                }
             }
             hir::StmtKind::Item(..) => succ,
             hir::StmtKind::Expr(ref expr) | hir::StmtKind::Semi(ref expr) => {
@@ -1121,7 +1156,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
         //     (rvalue)          ||       (rvalue)
         //         |             ||           |
         //         v             ||           v
-        // (write of place)     ||   (place components)
+        // (write of place)      ||   (place components)
         //         |             ||           |
         //         v             ||           v
         //      (succ)           ||        (succ)
diff --git a/compiler/rustc_passes/src/loops.rs b/compiler/rustc_passes/src/loops.rs
index 68b2a052391..cdda0e388dd 100644
--- a/compiler/rustc_passes/src/loops.rs
+++ b/compiler/rustc_passes/src/loops.rs
@@ -57,7 +57,13 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
             hir::ExprKind::Loop(ref b, _, source, _) => {
                 self.with_context(Loop(source), |v| v.visit_block(&b));
             }
-            hir::ExprKind::Closure { ref fn_decl, body, fn_decl_span, movability, .. } => {
+            hir::ExprKind::Closure(&hir::Closure {
+                ref fn_decl,
+                body,
+                fn_decl_span,
+                movability,
+                ..
+            }) => {
                 let cx = if let Some(Movability::Static) = movability {
                     AsyncClosure(fn_decl_span)
                 } else {
diff --git a/compiler/rustc_passes/src/reachable.rs b/compiler/rustc_passes/src/reachable.rs
index c830ab11e8e..f7e3fac6b2e 100644
--- a/compiler/rustc_passes/src/reachable.rs
+++ b/compiler/rustc_passes/src/reachable.rs
@@ -273,7 +273,10 @@ impl<'tcx> ReachableContext<'tcx> {
                 }
                 hir::ImplItemKind::TyAlias(_) => {}
             },
-            Node::Expr(&hir::Expr { kind: hir::ExprKind::Closure { body, .. }, .. }) => {
+            Node::Expr(&hir::Expr {
+                kind: hir::ExprKind::Closure(&hir::Closure { body, .. }),
+                ..
+            }) => {
                 self.visit_nested_body(body);
             }
             // Nothing to recurse on for these
diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs
index 12050dceb60..ca6a2ac3db3 100644
--- a/compiler/rustc_passes/src/stability.rs
+++ b/compiler/rustc_passes/src/stability.rs
@@ -1,9 +1,10 @@
 //! A pass that annotates every item and method with its stability level,
 //! propagating default levels lexically from parent to children ast nodes.
 
-use rustc_attr::{self as attr, ConstStability, Stability};
-use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
-use rustc_errors::struct_span_err;
+use attr::StabilityLevel;
+use rustc_attr::{self as attr, ConstStability, Stability, Unstable, UnstableReason};
+use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
+use rustc_errors::{struct_span_err, Applicability};
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID};
@@ -13,13 +14,12 @@ use rustc_hir::{FieldDef, Generics, HirId, Item, ItemKind, TraitRef, Ty, TyKind,
 use rustc_middle::hir::nested_filter;
 use rustc_middle::middle::privacy::AccessLevels;
 use rustc_middle::middle::stability::{AllowUnstable, DeprecationEntry, Index};
-use rustc_middle::ty::{self, query::Providers, TyCtxt};
+use rustc_middle::ty::{query::Providers, TyCtxt};
 use rustc_session::lint;
 use rustc_session::lint::builtin::{INEFFECTIVE_UNSTABLE_TRAIT_IMPL, USELESS_DEPRECATED};
-use rustc_session::parse::feature_err;
 use rustc_session::Session;
 use rustc_span::symbol::{sym, Symbol};
-use rustc_span::{Span, DUMMY_SP};
+use rustc_span::Span;
 use rustc_target::spec::abi::Abi;
 
 use std::cmp::Ordering;
@@ -29,13 +29,13 @@ use std::num::NonZeroU32;
 
 #[derive(PartialEq)]
 enum AnnotationKind {
-    // Annotation is required if not inherited from unstable parents
+    /// Annotation is required if not inherited from unstable parents.
     Required,
-    // Annotation is useless, reject it
+    /// Annotation is useless, reject it.
     Prohibited,
-    // Deprecation annotation is useless, reject it. (Stability attribute is still required.)
+    /// Deprecation annotation is useless, reject it. (Stability attribute is still required.)
     DeprecationProhibited,
-    // Annotation itself is useless, but it can be propagated to children
+    /// Annotation itself is useless, but it can be propagated to children.
     Container,
 }
 
@@ -83,7 +83,7 @@ impl InheritStability {
     }
 }
 
-// A private tree-walker for producing an Index.
+/// A private tree-walker for producing an `Index`.
 struct Annotator<'a, 'tcx> {
     tcx: TyCtxt<'tcx>,
     index: &'a mut Index,
@@ -94,9 +94,9 @@ struct Annotator<'a, 'tcx> {
 }
 
 impl<'a, 'tcx> Annotator<'a, 'tcx> {
-    // Determine the stability for a node based on its attributes and inherited
-    // stability. The stability is recorded in the index and used as the parent.
-    // If the node is a function, `fn_sig` is its signature
+    /// Determine the stability for a node based on its attributes and inherited stability. The
+    /// stability is recorded in the index and used as the parent. If the node is a function,
+    /// `fn_sig` is its signature.
     fn annotate<F>(
         &mut self,
         def_id: LocalDefId,
@@ -224,7 +224,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
 
             // Check if deprecated_since < stable_since. If it is,
             // this is *almost surely* an accident.
-            if let (&Some(dep_since), &attr::Stable { since: stab_since }) =
+            if let (&Some(dep_since), &attr::Stable { since: stab_since, .. }) =
                 (&depr.as_ref().and_then(|(d, _)| d.since), &stab.level)
             {
                 // Explicit version of iter::order::lt to handle parse errors properly
@@ -265,6 +265,10 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
                 }
             }
 
+            if let Stability { level: Unstable { implied_by: Some(implied_by), .. }, feature } = stab {
+                self.index.implications.insert(implied_by, feature);
+            }
+
             self.index.stab_map.insert(def_id, stab);
             stab
         });
@@ -610,6 +614,7 @@ fn stability_index(tcx: TyCtxt<'_>, (): ()) -> Index {
         stab_map: Default::default(),
         const_stab_map: Default::default(),
         depr_map: Default::default(),
+        implications: Default::default(),
     };
 
     {
@@ -628,15 +633,13 @@ fn stability_index(tcx: TyCtxt<'_>, (): ()) -> Index {
         // compiling `librustc_*` crates themselves so we can leverage crates.io
         // while maintaining the invariant that all sysroot crates are unstable
         // by default and are unable to be used.
-        if tcx.sess.opts.debugging_opts.force_unstable_if_unmarked {
-            let reason = "this crate is being loaded from the sysroot, an \
-                          unstable location; did you mean to load this crate \
-                          from crates.io via `Cargo.toml` instead?";
+        if tcx.sess.opts.unstable_opts.force_unstable_if_unmarked {
             let stability = Stability {
                 level: attr::StabilityLevel::Unstable {
-                    reason: Some(Symbol::intern(reason)),
+                    reason: UnstableReason::Default,
                     issue: NonZeroU32::new(27812),
                     is_soft: false,
+                    implied_by: None,
                 },
                 feature: sym::rustc_private,
             };
@@ -667,6 +670,7 @@ pub(crate) fn provide(providers: &mut Providers) {
     *providers = Providers {
         check_mod_unstable_api_usage,
         stability_index,
+        stability_implications: |tcx, _| tcx.stability().implications.clone(),
         lookup_stability: |tcx, id| tcx.stability().local_stability(id.expect_local()),
         lookup_const_stability: |tcx, id| tcx.stability().local_const_stability(id.expect_local()),
         lookup_deprecation_entry: |tcx, id| {
@@ -766,39 +770,6 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> {
                 }
             }
 
-            // There's no good place to insert stability check for non-Copy unions,
-            // so semi-randomly perform it here in stability.rs
-            hir::ItemKind::Union(..) if !self.tcx.features().untagged_unions => {
-                let ty = self.tcx.type_of(item.def_id);
-                let ty::Adt(adt_def, substs) = ty.kind() else { bug!() };
-
-                // Non-`Copy` fields are unstable, except for `ManuallyDrop`.
-                let param_env = self.tcx.param_env(item.def_id);
-                for field in &adt_def.non_enum_variant().fields {
-                    let field_ty = field.ty(self.tcx, substs);
-                    if !field_ty.ty_adt_def().map_or(false, |adt_def| adt_def.is_manually_drop())
-                        && !field_ty.is_copy_modulo_regions(self.tcx.at(DUMMY_SP), param_env)
-                    {
-                        if field_ty.needs_drop(self.tcx, param_env) {
-                            // Avoid duplicate error: This will error later anyway because fields
-                            // that need drop are not allowed.
-                            self.tcx.sess.delay_span_bug(
-                                item.span,
-                                "union should have been rejected due to potentially dropping field",
-                            );
-                        } else {
-                            feature_err(
-                                &self.tcx.sess.parse_sess,
-                                sym::untagged_unions,
-                                self.tcx.def_span(field.did),
-                                "unions with non-`Copy` fields other than `ManuallyDrop<T>` are unstable",
-                            )
-                            .emit();
-                        }
-                    }
-                }
-            }
-
             _ => (/* pass */),
         }
         intravisit::walk_item(self, item);
@@ -807,7 +778,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> {
     fn visit_path(&mut self, path: &'tcx hir::Path<'tcx>, id: hir::HirId) {
         if let Some(def_id) = path.res.opt_def_id() {
             let method_span = path.segments.last().map(|s| s.ident.span);
-            self.tcx.check_stability_allow_unstable(
+            let item_is_allowed = self.tcx.check_stability_allow_unstable(
                 def_id,
                 Some(id),
                 path.span,
@@ -817,8 +788,52 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> {
                 } else {
                     AllowUnstable::No
                 },
-            )
+            );
+
+            let is_allowed_through_unstable_modules = |def_id| {
+                self.tcx
+                    .lookup_stability(def_id)
+                    .map(|stab| match stab.level {
+                        StabilityLevel::Stable { allowed_through_unstable_modules, .. } => {
+                            allowed_through_unstable_modules
+                        }
+                        _ => false,
+                    })
+                    .unwrap_or(false)
+            };
+
+            if item_is_allowed && !is_allowed_through_unstable_modules(def_id) {
+                // Check parent modules stability as well if the item the path refers to is itself
+                // stable. We only emit warnings for unstable path segments if the item is stable
+                // or allowed because stability is often inherited, so the most common case is that
+                // both the segments and the item are unstable behind the same feature flag.
+                //
+                // We check here rather than in `visit_path_segment` to prevent visiting the last
+                // path segment twice
+                //
+                // We include special cases via #[rustc_allowed_through_unstable_modules] for items
+                // that were accidentally stabilized through unstable paths before this check was
+                // added, such as `core::intrinsics::transmute`
+                let parents = path.segments.iter().rev().skip(1);
+                for path_segment in parents {
+                    if let Some(def_id) = path_segment.res.as_ref().and_then(Res::opt_def_id) {
+                        // use `None` for id to prevent deprecation check
+                        self.tcx.check_stability_allow_unstable(
+                            def_id,
+                            None,
+                            path.span,
+                            None,
+                            if is_unstable_reexport(self.tcx, id) {
+                                AllowUnstable::Yes
+                            } else {
+                                AllowUnstable::No
+                            },
+                        );
+                    }
+                }
+            }
         }
+
         intravisit::walk_path(self, path)
     }
 }
@@ -884,7 +899,7 @@ impl<'tcx> Visitor<'tcx> for CheckTraitImplStable<'tcx> {
 /// libraries, identify activated features that don't exist and error about them.
 pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {
     let is_staged_api =
-        tcx.sess.opts.debugging_opts.force_unstable_if_unmarked || tcx.features().staged_api;
+        tcx.sess.opts.unstable_opts.force_unstable_if_unmarked || tcx.features().staged_api;
     if is_staged_api {
         let access_levels = &tcx.privacy_access_levels(());
         let mut missing = MissingStabilityAnnotations { tcx, access_levels };
@@ -934,32 +949,51 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {
     remaining_lib_features.remove(&sym::libc);
     remaining_lib_features.remove(&sym::test);
 
-    let check_features = |remaining_lib_features: &mut FxIndexMap<_, _>, defined_features: &[_]| {
-        for &(feature, since) in defined_features {
-            if let Some(since) = since {
-                if let Some(span) = remaining_lib_features.get(&feature) {
-                    // Warn if the user has enabled an already-stable lib feature.
-                    unnecessary_stable_feature_lint(tcx, *span, feature, since);
-                }
-            }
-            remaining_lib_features.remove(&feature);
-            if remaining_lib_features.is_empty() {
-                break;
-            }
-        }
-    };
-
     // We always collect the lib features declared in the current crate, even if there are
     // no unknown features, because the collection also does feature attribute validation.
-    let local_defined_features = tcx.lib_features(()).to_vec();
-    if !remaining_lib_features.is_empty() {
-        check_features(&mut remaining_lib_features, &local_defined_features);
+    let local_defined_features = tcx.lib_features(());
+    let mut all_lib_features: FxHashMap<_, _> =
+        local_defined_features.to_vec().iter().map(|el| *el).collect();
+    let mut implications = tcx.stability_implications(rustc_hir::def_id::LOCAL_CRATE).clone();
+    for &cnum in tcx.crates(()) {
+        implications.extend(tcx.stability_implications(cnum));
+        all_lib_features.extend(tcx.defined_lib_features(cnum).iter().map(|el| *el));
+    }
 
-        for &cnum in tcx.crates(()) {
+    // Check that every feature referenced by an `implied_by` exists (for features defined in the
+    // local crate).
+    for (implied_by, feature) in tcx.stability_implications(rustc_hir::def_id::LOCAL_CRATE) {
+        // Only `implied_by` needs to be checked, `feature` is guaranteed to exist.
+        if !all_lib_features.contains_key(implied_by) {
+            let span = local_defined_features
+                .stable
+                .get(feature)
+                .map(|(_, span)| span)
+                .or_else(|| local_defined_features.unstable.get(feature))
+                .expect("feature that implied another does not exist");
+            tcx.sess
+                .struct_span_err(
+                    *span,
+                    format!("feature `{implied_by}` implying `{feature}` does not exist"),
+                )
+                .emit();
+        }
+    }
+
+    if !remaining_lib_features.is_empty() {
+        for (feature, since) in all_lib_features.iter() {
+            if let Some(since) = since && let Some(span) = remaining_lib_features.get(&feature) {
+                // Warn if the user has enabled an already-stable lib feature.
+                if let Some(implies) = implications.get(&feature) {
+                    unnecessary_partially_stable_feature_lint(tcx, *span, *feature, *implies, *since);
+                } else {
+                    unnecessary_stable_feature_lint(tcx, *span, *feature, *since);
+                }
+            }
+            remaining_lib_features.remove(&feature);
             if remaining_lib_features.is_empty() {
                 break;
             }
-            check_features(&mut remaining_lib_features, tcx.defined_lib_features(cnum));
         }
     }
 
@@ -971,12 +1005,41 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {
     // don't lint about unused features. We should re-enable this one day!
 }
 
+fn unnecessary_partially_stable_feature_lint(
+    tcx: TyCtxt<'_>,
+    span: Span,
+    feature: Symbol,
+    implies: Symbol,
+    since: Symbol,
+) {
+    tcx.struct_span_lint_hir(lint::builtin::STABLE_FEATURES, hir::CRATE_HIR_ID, span, |lint| {
+        lint.build(&format!(
+            "the feature `{feature}` has been partially stabilized since {since} and is succeeded \
+             by the feature `{implies}`"
+        ))
+        .span_suggestion(
+            span,
+            &format!(
+                "if you are using features which are still unstable, change to using `{implies}`"
+            ),
+            implies,
+            Applicability::MaybeIncorrect,
+        )
+        .span_suggestion(
+            tcx.sess.source_map().span_extend_to_line(span),
+            "if you are using features which are now stable, remove this line",
+            "",
+            Applicability::MaybeIncorrect,
+        )
+        .emit();
+    });
+}
+
 fn unnecessary_stable_feature_lint(tcx: TyCtxt<'_>, span: Span, feature: Symbol, since: Symbol) {
     tcx.struct_span_lint_hir(lint::builtin::STABLE_FEATURES, hir::CRATE_HIR_ID, span, |lint| {
         lint.build(&format!(
-            "the feature `{}` has been stable since {} and no longer requires \
-                      an attribute to enable",
-            feature, since
+            "the feature `{feature}` has been stable since {since} and no longer requires an \
+             attribute to enable",
         ))
         .emit();
     });
diff --git a/compiler/rustc_passes/src/weak_lang_items.rs b/compiler/rustc_passes/src/weak_lang_items.rs
index 3291be05807..c48b4ecf87a 100644
--- a/compiler/rustc_passes/src/weak_lang_items.rs
+++ b/compiler/rustc_passes/src/weak_lang_items.rs
@@ -68,7 +68,7 @@ fn verify<'tcx>(tcx: TyCtxt<'tcx>, items: &lang_items::LanguageItems) {
         }
     }
 
-    for (name, item) in WEAK_ITEMS_REFS.clone().into_sorted_vector().into_iter() {
+    for (name, &item) in WEAK_ITEMS_REFS.iter() {
         if missing.contains(&item) && required(tcx, item) && items.require(item).is_err() {
             if item == LangItem::PanicImpl {
                 tcx.sess.err("`#[panic_handler]` function required, but not found");
diff --git a/compiler/rustc_privacy/src/errors.rs b/compiler/rustc_privacy/src/errors.rs
index 482721d373a..b0fac91f6eb 100644
--- a/compiler/rustc_privacy/src/errors.rs
+++ b/compiler/rustc_privacy/src/errors.rs
@@ -1,4 +1,4 @@
-use rustc_macros::{SessionDiagnostic, SessionSubdiagnostic};
+use rustc_macros::{LintDiagnostic, SessionDiagnostic, SessionSubdiagnostic};
 use rustc_span::{Span, Symbol};
 
 #[derive(SessionDiagnostic)]
@@ -73,3 +73,19 @@ pub struct InPublicInterface<'a> {
     #[label(privacy::visibility_label)]
     pub vis_span: Span,
 }
+
+#[derive(LintDiagnostic)]
+#[lint(privacy::from_private_dep_in_public_interface)]
+pub struct FromPrivateDependencyInPublicInterface<'a> {
+    pub kind: &'a str,
+    pub descr: String,
+    pub krate: Symbol,
+}
+
+#[derive(LintDiagnostic)]
+#[lint(privacy::private_in_public_lint)]
+pub struct PrivateInPublicLint<'a> {
+    pub vis_descr: &'static str,
+    pub kind: &'a str,
+    pub descr: String,
+}
diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs
index 5560d44aa0d..390d6f5a856 100644
--- a/compiler/rustc_privacy/src/lib.rs
+++ b/compiler/rustc_privacy/src/lib.rs
@@ -23,7 +23,7 @@ use rustc_middle::bug;
 use rustc_middle::hir::nested_filter;
 use rustc_middle::middle::privacy::{AccessLevel, AccessLevels};
 use rustc_middle::span_bug;
-use rustc_middle::thir::abstract_const::Node as ACNode;
+use rustc_middle::ty::abstract_const::{walk_abstract_const, AbstractConst, Node as ACNode};
 use rustc_middle::ty::query::Providers;
 use rustc_middle::ty::subst::InternalSubsts;
 use rustc_middle::ty::{self, Const, DefIdTree, GenericParamDefKind};
@@ -32,15 +32,14 @@ use rustc_session::lint;
 use rustc_span::hygiene::Transparency;
 use rustc_span::symbol::{kw, Ident};
 use rustc_span::Span;
-use rustc_trait_selection::traits::const_evaluatable::{self, AbstractConst};
 
 use std::marker::PhantomData;
 use std::ops::ControlFlow;
 use std::{cmp, fmt, mem};
 
 use errors::{
-    FieldIsPrivate, FieldIsPrivateLabel, InPublicInterface, InPublicInterfaceTraits, ItemIsPrivate,
-    UnnamedItemIsPrivate,
+    FieldIsPrivate, FieldIsPrivateLabel, FromPrivateDependencyInPublicInterface, InPublicInterface,
+    InPublicInterfaceTraits, ItemIsPrivate, PrivateInPublicLint, UnnamedItemIsPrivate,
 };
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -164,7 +163,7 @@ where
         tcx: TyCtxt<'tcx>,
         ct: AbstractConst<'tcx>,
     ) -> ControlFlow<V::BreakTy> {
-        const_evaluatable::walk_abstract_const(tcx, ct, |node| match node.root(tcx) {
+        walk_abstract_const(tcx, ct, |node| match node.root(tcx) {
             ACNode::Leaf(leaf) => self.visit_const(leaf),
             ACNode::Cast(_, _, ty) => self.visit_ty(ty),
             ACNode::Binop(..) | ACNode::UnaryOp(..) | ACNode::FunctionCall(_, _) => {
@@ -1717,19 +1716,14 @@ impl SearchInterfaceForPrivateItemsVisitor<'_> {
 
     fn check_def_id(&mut self, def_id: DefId, kind: &str, descr: &dyn fmt::Display) -> bool {
         if self.leaks_private_dep(def_id) {
-            self.tcx.struct_span_lint_hir(
+            self.tcx.emit_spanned_lint(
                 lint::builtin::EXPORTED_PRIVATE_DEPENDENCIES,
                 self.tcx.hir().local_def_id_to_hir_id(self.item_def_id),
                 self.tcx.def_span(self.item_def_id.to_def_id()),
-                |lint| {
-                    lint.build(&format!(
-                        "{} `{}` from private dependency '{}' in public \
-                                                interface",
-                        kind,
-                        descr,
-                        self.tcx.crate_name(def_id.krate)
-                    ))
-                    .emit();
+                FromPrivateDependencyInPublicInterface {
+                    kind,
+                    descr: descr.to_string(),
+                    krate: self.tcx.crate_name(def_id.krate),
                 },
             );
         }
@@ -1755,11 +1749,11 @@ impl SearchInterfaceForPrivateItemsVisitor<'_> {
                 }
             };
             let span = self.tcx.def_span(self.item_def_id.to_def_id());
+            let descr = descr.to_string();
             if self.has_old_errors
                 || self.in_assoc_ty
                 || self.tcx.resolutions(()).has_pub_restricted
             {
-                let descr = descr.to_string();
                 let vis_span =
                     self.tcx.sess.source_map().guess_head_span(self.tcx.def_span(def_id));
                 if kind == "trait" {
@@ -1780,19 +1774,11 @@ impl SearchInterfaceForPrivateItemsVisitor<'_> {
                     });
                 }
             } else {
-                let err_code = if kind == "trait" { "E0445" } else { "E0446" };
-                self.tcx.struct_span_lint_hir(
+                self.tcx.emit_spanned_lint(
                     lint::builtin::PRIVATE_IN_PUBLIC,
                     hir_id,
                     span,
-                    |lint| {
-                        lint.build(&format!(
-                            "{} (error {})",
-                            format!("{} {} `{}` in public interface", vis_descr, kind, descr),
-                            err_code
-                        ))
-                        .emit();
-                    },
+                    PrivateInPublicLint { vis_descr, kind, descr },
                 );
             }
         }
diff --git a/compiler/rustc_query_impl/src/keys.rs b/compiler/rustc_query_impl/src/keys.rs
index 6fbafeb1d32..54774314313 100644
--- a/compiler/rustc_query_impl/src/keys.rs
+++ b/compiler/rustc_query_impl/src/keys.rs
@@ -6,7 +6,7 @@ use rustc_middle::mir;
 use rustc_middle::traits;
 use rustc_middle::ty::fast_reject::SimplifiedType;
 use rustc_middle::ty::subst::{GenericArg, SubstsRef};
-use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_middle::ty::{self, layout::TyAndLayout, Ty, TyCtxt};
 use rustc_span::symbol::{Ident, Symbol};
 use rustc_span::{Span, DUMMY_SP};
 
@@ -385,6 +385,16 @@ impl<'tcx> Key for Ty<'tcx> {
     }
 }
 
+impl<'tcx> Key for TyAndLayout<'tcx> {
+    #[inline(always)]
+    fn query_crate_is_local(&self) -> bool {
+        true
+    }
+    fn default_span(&self, _: TyCtxt<'_>) -> Span {
+        DUMMY_SP
+    }
+}
+
 impl<'tcx> Key for (Ty<'tcx>, Ty<'tcx>) {
     #[inline(always)]
     fn query_crate_is_local(&self) -> bool {
diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs
index 7c1fdc4e306..eda61df7700 100644
--- a/compiler/rustc_query_impl/src/lib.rs
+++ b/compiler/rustc_query_impl/src/lib.rs
@@ -37,7 +37,7 @@ mod values;
 use self::values::Value;
 
 pub use rustc_query_system::query::QueryConfig;
-pub(crate) use rustc_query_system::query::{QueryDescription, QueryVtable};
+pub(crate) use rustc_query_system::query::{QueryDescription, QueryVTable};
 
 mod on_disk_cache;
 pub use on_disk_cache::OnDiskCache;
diff --git a/compiler/rustc_query_impl/src/on_disk_cache.rs b/compiler/rustc_query_impl/src/on_disk_cache.rs
index 4c25075327f..56fd90c9855 100644
--- a/compiler/rustc_query_impl/src/on_disk_cache.rs
+++ b/compiler/rustc_query_impl/src/on_disk_cache.rs
@@ -9,7 +9,6 @@ use rustc_index::vec::{Idx, IndexVec};
 use rustc_middle::dep_graph::{DepNodeIndex, SerializedDepNodeIndex};
 use rustc_middle::mir::interpret::{AllocDecodingSession, AllocDecodingState};
 use rustc_middle::mir::{self, interpret};
-use rustc_middle::thir;
 use rustc_middle::ty::codec::{RefDecodable, TyDecoder, TyEncoder};
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_query_system::dep_graph::DepContext;
@@ -766,7 +765,7 @@ impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>>
     }
 }
 
-impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> for &'tcx [thir::abstract_const::Node<'tcx>] {
+impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> for &'tcx [ty::abstract_const::Node<'tcx>] {
     fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self {
         RefDecodable::decode(d)
     }
diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs
index 333dc5aa668..eda4401c81d 100644
--- a/compiler/rustc_query_impl/src/plumbing.rs
+++ b/compiler/rustc_query_impl/src/plumbing.rs
@@ -340,11 +340,11 @@ macro_rules! define_queries {
 
             #[inline]
             fn make_vtable(tcx: QueryCtxt<'tcx>, key: &Self::Key) ->
-                QueryVtable<QueryCtxt<$tcx>, Self::Key, Self::Value>
+                QueryVTable<QueryCtxt<$tcx>, Self::Key, Self::Value>
             {
                 let compute = get_provider!([$($modifiers)*][tcx, $name, key]);
                 let cache_on_disk = Self::cache_on_disk(tcx.tcx, key);
-                QueryVtable {
+                QueryVTable {
                     anon: is_anon!([$($modifiers)*]),
                     eval_always: is_eval_always!([$($modifiers)*]),
                     dep_kind: dep_graph::DepKind::$name,
diff --git a/compiler/rustc_query_system/src/dep_graph/dep_node.rs b/compiler/rustc_query_system/src/dep_graph/dep_node.rs
index c6210095b60..162c274d8a2 100644
--- a/compiler/rustc_query_system/src/dep_graph/dep_node.rs
+++ b/compiler/rustc_query_system/src/dep_graph/dep_node.rs
@@ -79,8 +79,8 @@ impl<K: DepKind> DepNode<K> {
         #[cfg(debug_assertions)]
         {
             if !tcx.fingerprint_style(kind).reconstructible()
-                && (tcx.sess().opts.debugging_opts.incremental_info
-                    || tcx.sess().opts.debugging_opts.query_dep_graph)
+                && (tcx.sess().opts.unstable_opts.incremental_info
+                    || tcx.sess().opts.unstable_opts.query_dep_graph)
             {
                 tcx.dep_graph().register_dep_node_debug_str(dep_node, || arg.to_debug_str(tcx));
             }
diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs
index e7026096e7b..8ff56132749 100644
--- a/compiler/rustc_query_system/src/dep_graph/graph.rs
+++ b/compiler/rustc_query_system/src/dep_graph/graph.rs
@@ -347,7 +347,7 @@ impl<K: DepKind> DepGraph<K> {
         let current_fingerprint =
             hash_result.map(|f| dcx.with_stable_hashing_context(|mut hcx| f(&mut hcx, &result)));
 
-        let print_status = cfg!(debug_assertions) && dcx.sess().opts.debugging_opts.dep_tasks;
+        let print_status = cfg!(debug_assertions) && dcx.sess().opts.unstable_opts.dep_tasks;
 
         // Intern the new `DepNode`.
         let (dep_node_index, prev_and_color) = data.current.intern_node(
diff --git a/compiler/rustc_query_system/src/ich/hcx.rs b/compiler/rustc_query_system/src/ich/hcx.rs
index 843f6f9d703..217fac341ed 100644
--- a/compiler/rustc_query_system/src/ich/hcx.rs
+++ b/compiler/rustc_query_system/src/ich/hcx.rs
@@ -24,7 +24,7 @@ pub struct StableHashingContext<'a> {
     cstore: &'a dyn CrateStore,
     source_span: &'a IndexVec<LocalDefId, Span>,
     // The value of `-Z incremental-ignore-spans`.
-    // This field should only be used by `debug_opts_incremental_ignore_span`
+    // This field should only be used by `unstable_opts_incremental_ignore_span`
     incremental_ignore_spans: bool,
     pub(super) body_resolver: BodyResolver<'a>,
     // Very often, we are hashing something that does not need the
@@ -57,14 +57,14 @@ impl<'a> StableHashingContext<'a> {
         always_ignore_spans: bool,
     ) -> Self {
         let hash_spans_initial =
-            !always_ignore_spans && !sess.opts.debugging_opts.incremental_ignore_spans;
+            !always_ignore_spans && !sess.opts.unstable_opts.incremental_ignore_spans;
 
         StableHashingContext {
             body_resolver: BodyResolver::Forbidden,
             definitions,
             cstore,
             source_span,
-            incremental_ignore_spans: sess.opts.debugging_opts.incremental_ignore_spans,
+            incremental_ignore_spans: sess.opts.unstable_opts.incremental_ignore_spans,
             caching_source_map: None,
             raw_source_map: sess.source_map(),
             hashing_controls: HashingControls { hash_spans: hash_spans_initial },
@@ -186,7 +186,7 @@ impl<'a> rustc_span::HashStableContext for StableHashingContext<'a> {
     }
 
     #[inline]
-    fn debug_opts_incremental_ignore_spans(&self) -> bool {
+    fn unstable_opts_incremental_ignore_spans(&self) -> bool {
         self.incremental_ignore_spans
     }
 
diff --git a/compiler/rustc_query_system/src/query/config.rs b/compiler/rustc_query_system/src/query/config.rs
index 7ca668f8a1f..964914a1326 100644
--- a/compiler/rustc_query_system/src/query/config.rs
+++ b/compiler/rustc_query_system/src/query/config.rs
@@ -19,7 +19,7 @@ pub trait QueryConfig {
     type Stored: Clone;
 }
 
-pub struct QueryVtable<CTX: QueryContext, K, V> {
+pub struct QueryVTable<CTX: QueryContext, K, V> {
     pub anon: bool,
     pub dep_kind: CTX::DepKind,
     pub eval_always: bool,
@@ -31,7 +31,7 @@ pub struct QueryVtable<CTX: QueryContext, K, V> {
     pub try_load_from_disk: Option<fn(CTX, SerializedDepNodeIndex) -> Option<V>>,
 }
 
-impl<CTX: QueryContext, K, V> QueryVtable<CTX, K, V> {
+impl<CTX: QueryContext, K, V> QueryVTable<CTX, K, V> {
     pub(crate) fn to_dep_node(&self, tcx: CTX::DepContext, key: &K) -> DepNode<CTX::DepKind>
     where
         K: crate::dep_graph::DepNodeParams<CTX::DepContext>,
@@ -69,7 +69,7 @@ pub trait QueryDescription<CTX: QueryContext>: QueryConfig {
         CTX: 'a;
 
     // Don't use this method to compute query results, instead use the methods on TyCtxt
-    fn make_vtable(tcx: CTX, key: &Self::Key) -> QueryVtable<CTX, Self::Key, Self::Value>;
+    fn make_vtable(tcx: CTX, key: &Self::Key) -> QueryVTable<CTX, Self::Key, Self::Value>;
 
     fn cache_on_disk(tcx: CTX::DepContext, key: &Self::Key) -> bool;
 }
diff --git a/compiler/rustc_query_system/src/query/mod.rs b/compiler/rustc_query_system/src/query/mod.rs
index f698a853d1e..fb2258434f4 100644
--- a/compiler/rustc_query_system/src/query/mod.rs
+++ b/compiler/rustc_query_system/src/query/mod.rs
@@ -12,7 +12,7 @@ pub use self::caches::{
 };
 
 mod config;
-pub use self::config::{QueryConfig, QueryDescription, QueryVtable};
+pub use self::config::{QueryConfig, QueryDescription, QueryVTable};
 
 use crate::dep_graph::{DepNodeIndex, HasDepContext, SerializedDepNodeIndex};
 
diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs
index bbcd00be943..5e8ea07d00f 100644
--- a/compiler/rustc_query_system/src/query/plumbing.rs
+++ b/compiler/rustc_query_system/src/query/plumbing.rs
@@ -4,7 +4,7 @@
 
 use crate::dep_graph::{DepContext, DepNode, DepNodeIndex, DepNodeParams};
 use crate::query::caches::QueryCache;
-use crate::query::config::{QueryDescription, QueryVtable};
+use crate::query::config::{QueryDescription, QueryVTable};
 use crate::query::job::{report_cycle, QueryInfo, QueryJob, QueryJobId, QueryJobInfo};
 use crate::query::{QueryContext, QueryMap, QuerySideEffects, QueryStackFrame};
 use rustc_data_structures::fingerprint::Fingerprint;
@@ -331,7 +331,7 @@ fn try_execute_query<CTX, C>(
     span: Span,
     key: C::Key,
     dep_node: Option<DepNode<CTX::DepKind>>,
-    query: &QueryVtable<CTX, C::Key, C::Value>,
+    query: &QueryVTable<CTX, C::Key, C::Value>,
 ) -> (C::Stored, Option<DepNodeIndex>)
 where
     C: QueryCache,
@@ -368,7 +368,7 @@ fn execute_job<CTX, K, V>(
     tcx: CTX,
     key: K,
     mut dep_node_opt: Option<DepNode<CTX::DepKind>>,
-    query: &QueryVtable<CTX, K, V>,
+    query: &QueryVTable<CTX, K, V>,
     job_id: QueryJobId,
 ) -> (V, DepNodeIndex)
 where
@@ -437,7 +437,7 @@ fn try_load_from_disk_and_cache_in_memory<CTX, K, V>(
     tcx: CTX,
     key: &K,
     dep_node: &DepNode<CTX::DepKind>,
-    query: &QueryVtable<CTX, K, V>,
+    query: &QueryVTable<CTX, K, V>,
 ) -> Option<(V, DepNodeIndex)>
 where
     K: Clone,
@@ -467,7 +467,7 @@ where
 
         if let Some(result) = result {
             if std::intrinsics::unlikely(
-                tcx.dep_context().sess().opts.debugging_opts.query_dep_graph,
+                tcx.dep_context().sess().opts.unstable_opts.query_dep_graph,
             ) {
                 dep_graph.mark_debug_loaded_from_disk(*dep_node)
             }
@@ -486,7 +486,7 @@ where
             // give us some coverage of potential bugs though.
             let try_verify = prev_fingerprint.as_value().1 % 32 == 0;
             if std::intrinsics::unlikely(
-                try_verify || tcx.dep_context().sess().opts.debugging_opts.incremental_verify_ich,
+                try_verify || tcx.dep_context().sess().opts.unstable_opts.incremental_verify_ich,
             ) {
                 incremental_verify_ich(*tcx.dep_context(), &result, dep_node, query);
             }
@@ -530,7 +530,7 @@ fn incremental_verify_ich<CTX, K, V: Debug>(
     tcx: CTX::DepContext,
     result: &V,
     dep_node: &DepNode<CTX::DepKind>,
-    query: &QueryVtable<CTX, K, V>,
+    query: &QueryVTable<CTX, K, V>,
 ) where
     CTX: QueryContext,
 {
@@ -642,7 +642,7 @@ fn incremental_verify_ich_cold(sess: &Session, dep_node: DebugArg<'_>, result: D
 fn ensure_must_run<CTX, K, V>(
     tcx: CTX,
     key: &K,
-    query: &QueryVtable<CTX, K, V>,
+    query: &QueryVTable<CTX, K, V>,
 ) -> (bool, Option<DepNode<CTX::DepKind>>)
 where
     K: crate::dep_graph::DepNodeParams<CTX::DepContext>,
diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs
index 6631470f219..e955a1798b7 100644
--- a/compiler/rustc_resolve/src/build_reduced_graph.rs
+++ b/compiler/rustc_resolve/src/build_reduced_graph.rs
@@ -524,7 +524,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
                         let crate_root = self.r.resolve_crate_root(source.ident);
                         let crate_name = match crate_root.kind {
                             ModuleKind::Def(.., name) => name,
-                            ModuleKind::Block(..) => unreachable!(),
+                            ModuleKind::Block => unreachable!(),
                         };
                         // HACK(eddyb) unclear how good this is, but keeping `$crate`
                         // in `source` breaks `src/test/ui/imports/import-crate-var.rs`,
@@ -936,7 +936,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
         if self.block_needs_anonymous_module(block) {
             let module = self.r.new_module(
                 Some(parent),
-                ModuleKind::Block(block.id),
+                ModuleKind::Block,
                 expansion.to_expn_id(),
                 block.span,
                 parent.no_implicit_prelude,
diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs
index 52706fbb9e6..66641fb2cb2 100644
--- a/compiler/rustc_resolve/src/def_collector.rs
+++ b/compiler/rustc_resolve/src/def_collector.rs
@@ -259,7 +259,7 @@ impl<'a, 'b> visit::Visitor<'a> for DefCollector<'a, 'b> {
     fn visit_expr(&mut self, expr: &'a Expr) {
         let parent_def = match expr.kind {
             ExprKind::MacCall(..) => return self.visit_macro_invoc(expr.id),
-            ExprKind::Closure(_, asyncness, ..) => {
+            ExprKind::Closure(_, _, asyncness, ..) => {
                 // Async closures desugar to closures inside of closures, so
                 // we must create two defs.
                 let closure_def = self.create_def(expr.id, DefPathData::ClosureExpr, expr.span);
diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs
index 2851b08cd93..22a307a15ed 100644
--- a/compiler/rustc_resolve/src/diagnostics.rs
+++ b/compiler/rustc_resolve/src/diagnostics.rs
@@ -163,7 +163,7 @@ impl<'a> Resolver<'a> {
 
         let container = match parent.kind {
             ModuleKind::Def(kind, _, _) => kind.descr(parent.def_id()),
-            ModuleKind::Block(..) => "block",
+            ModuleKind::Block => "block",
         };
 
         let old_noun = match old_binding.is_import() {
@@ -508,7 +508,7 @@ impl<'a> Resolver<'a> {
                     E0401,
                     "can't use generic parameters from outer function",
                 );
-                err.span_label(span, "use of generic parameter from outer function".to_string());
+                err.span_label(span, "use of generic parameter from outer function");
 
                 let sm = self.session.source_map();
                 match outer_res {
@@ -565,8 +565,7 @@ impl<'a> Resolver<'a> {
                     } else if let Some(sp) = sm.generate_fn_name_span(span) {
                         err.span_label(
                             sp,
-                            "try adding a local generic parameter in this method instead"
-                                .to_string(),
+                            "try adding a local generic parameter in this method instead",
                         );
                     } else {
                         err.help("try using a local generic parameter instead");
@@ -990,7 +989,7 @@ impl<'a> Resolver<'a> {
                     E0735,
                     "generic parameters cannot use `Self` in their defaults"
                 );
-                err.span_label(span, "`Self` in generic parameter default".to_string());
+                err.span_label(span, "`Self` in generic parameter default");
                 err
             }
             ResolutionError::UnreachableLabel { name, definition_span, suggestion } => {
@@ -1627,7 +1626,7 @@ impl<'a> Resolver<'a> {
                     "{}{} `{}` defined here",
                     prefix,
                     suggestion.res.descr(),
-                    suggestion.candidate.as_str(),
+                    suggestion.candidate,
                 ),
             );
         }
@@ -2024,7 +2023,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
         span: Span,
         mut path: Vec<Segment>,
         parent_scope: &ParentScope<'b>,
-    ) -> Option<(Vec<Segment>, Vec<String>)> {
+    ) -> Option<(Vec<Segment>, Option<String>)> {
         debug!("make_path_suggestion: span={:?} path={:?}", span, path);
 
         match (path.get(0), path.get(1)) {
@@ -2059,12 +2058,12 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
         &mut self,
         mut path: Vec<Segment>,
         parent_scope: &ParentScope<'b>,
-    ) -> Option<(Vec<Segment>, Vec<String>)> {
+    ) -> Option<(Vec<Segment>, Option<String>)> {
         // Replace first ident with `self` and check if that is valid.
         path[0].ident.name = kw::SelfLower;
         let result = self.r.maybe_resolve_path(&path, None, parent_scope);
         debug!("make_missing_self_suggestion: path={:?} result={:?}", path, result);
-        if let PathResult::Module(..) = result { Some((path, Vec::new())) } else { None }
+        if let PathResult::Module(..) = result { Some((path, None)) } else { None }
     }
 
     /// Suggests a missing `crate::` if that resolves to an correct module.
@@ -2078,7 +2077,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
         &mut self,
         mut path: Vec<Segment>,
         parent_scope: &ParentScope<'b>,
-    ) -> Option<(Vec<Segment>, Vec<String>)> {
+    ) -> Option<(Vec<Segment>, Option<String>)> {
         // Replace first ident with `crate` and check if that is valid.
         path[0].ident.name = kw::Crate;
         let result = self.r.maybe_resolve_path(&path, None, parent_scope);
@@ -2086,12 +2085,12 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
         if let PathResult::Module(..) = result {
             Some((
                 path,
-                vec![
+                Some(
                     "`use` statements changed in Rust 2018; read more at \
                      <https://doc.rust-lang.org/edition-guide/rust-2018/module-system/path-\
                      clarity.html>"
                         .to_string(),
-                ],
+                ),
             ))
         } else {
             None
@@ -2109,12 +2108,12 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
         &mut self,
         mut path: Vec<Segment>,
         parent_scope: &ParentScope<'b>,
-    ) -> Option<(Vec<Segment>, Vec<String>)> {
+    ) -> Option<(Vec<Segment>, Option<String>)> {
         // Replace first ident with `crate` and check if that is valid.
         path[0].ident.name = kw::Super;
         let result = self.r.maybe_resolve_path(&path, None, parent_scope);
         debug!("make_missing_super_suggestion:  path={:?} result={:?}", path, result);
-        if let PathResult::Module(..) = result { Some((path, Vec::new())) } else { None }
+        if let PathResult::Module(..) = result { Some((path, None)) } else { None }
     }
 
     /// Suggests a missing external crate name if that resolves to an correct module.
@@ -2131,7 +2130,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
         &mut self,
         mut path: Vec<Segment>,
         parent_scope: &ParentScope<'b>,
-    ) -> Option<(Vec<Segment>, Vec<String>)> {
+    ) -> Option<(Vec<Segment>, Option<String>)> {
         if path[1].ident.span.rust_2015() {
             return None;
         }
@@ -2152,7 +2151,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
                 name, path, result
             );
             if let PathResult::Module(..) = result {
-                return Some((path, Vec::new()));
+                return Some((path, None));
             }
         }
 
@@ -2176,7 +2175,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
         import: &'b Import<'b>,
         module: ModuleOrUniformRoot<'b>,
         ident: Ident,
-    ) -> Option<(Option<Suggestion>, Vec<String>)> {
+    ) -> Option<(Option<Suggestion>, Option<String>)> {
         let ModuleOrUniformRoot::Module(mut crate_module) = module else {
             return None;
         };
@@ -2288,12 +2287,9 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
                 String::from("a macro with this name exists at the root of the crate"),
                 Applicability::MaybeIncorrect,
             ));
-            let note = vec![
-                "this could be because a macro annotated with `#[macro_export]` will be exported \
-                 at the root of the crate instead of the module where it is defined"
-                    .to_string(),
-            ];
-            Some((suggestion, note))
+            Some((suggestion, Some("this could be because a macro annotated with `#[macro_export]` will be exported \
+            at the root of the crate instead of the module where it is defined"
+               .to_string())))
         } else {
             None
         }
@@ -2603,12 +2599,12 @@ fn show_candidates(
                 .skip(1)
                 .all(|(_, descr, _, _)| descr == descr_first)
             {
-                descr_first.to_string()
+                descr_first
             } else {
-                "item".to_string()
+                "item"
             };
             let plural_descr =
-                if descr.ends_with("s") { format!("{}es", descr) } else { format!("{}s", descr) };
+                if descr.ends_with('s') { format!("{}es", descr) } else { format!("{}s", descr) };
 
             let mut msg = format!("{}these {} exist but are inaccessible", prefix, plural_descr);
             let mut has_colon = false;
diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs
index 0cc6d05d1d0..6e678288142 100644
--- a/compiler/rustc_resolve/src/ident.rs
+++ b/compiler/rustc_resolve/src/ident.rs
@@ -218,7 +218,7 @@ impl<'a> Resolver<'a> {
             return Some((self.expn_def_scope(ctxt.remove_mark()), None));
         }
 
-        if let ModuleKind::Block(..) = module.kind {
+        if let ModuleKind::Block = module.kind {
             return Some((module.parent.unwrap().nearest_item_scope(), None));
         }
 
@@ -333,7 +333,7 @@ impl<'a> Resolver<'a> {
             };
 
             match module.kind {
-                ModuleKind::Block(..) => {} // We can see through blocks
+                ModuleKind::Block => {} // We can see through blocks
                 _ => break,
             }
 
diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs
index e6060ad4665..b89273990d8 100644
--- a/compiler/rustc_resolve/src/imports.rs
+++ b/compiler/rustc_resolve/src/imports.rs
@@ -336,7 +336,7 @@ impl<'a> Resolver<'a> {
 struct UnresolvedImportError {
     span: Span,
     label: Option<String>,
-    note: Vec<String>,
+    note: Option<String>,
     suggestion: Option<Suggestion>,
 }
 
@@ -427,7 +427,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
                 let err = UnresolvedImportError {
                     span: import.span,
                     label: None,
-                    note: Vec::new(),
+                    note: None,
                     suggestion: None,
                 };
                 if path.contains("::") {
@@ -463,10 +463,8 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
 
         let mut diag = struct_span_err!(self.r.session, span, E0432, "{}", &msg);
 
-        if let Some((_, UnresolvedImportError { note, .. })) = errors.iter().last() {
-            for message in note {
-                diag.note(message);
-            }
+        if let Some((_, UnresolvedImportError { note: Some(note), .. })) = errors.iter().last() {
+            diag.note(note);
         }
 
         for (_, err) in errors.into_iter().take(MAX_LABEL_COUNT) {
@@ -644,7 +642,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
                         None => UnresolvedImportError {
                             span,
                             label: Some(label),
-                            note: Vec::new(),
+                            note: None,
                             suggestion,
                         },
                     };
@@ -686,7 +684,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
                         return Some(UnresolvedImportError {
                             span: import.span,
                             label: Some(String::from("cannot glob-import a module into itself")),
-                            note: Vec::new(),
+                            note: None,
                             suggestion: None,
                         });
                     }
@@ -830,7 +828,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
                 let (suggestion, note) =
                     match self.check_for_module_export_macro(import, module, ident) {
                         Some((suggestion, note)) => (suggestion.or(lev_suggestion), note),
-                        _ => (lev_suggestion, Vec::new()),
+                        _ => (lev_suggestion, None),
                     };
 
                 let label = match module {
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index 098b5a0c92e..ed65100ae77 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -21,6 +21,7 @@ use rustc_hir::def::Namespace::{self, *};
 use rustc_hir::def::{self, CtorKind, DefKind, LifetimeRes, PartialRes, PerNS};
 use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID};
 use rustc_hir::{PrimTy, TraitCandidate};
+use rustc_middle::middle::resolve_lifetime::Set1;
 use rustc_middle::ty::DefIdTree;
 use rustc_middle::{bug, span_bug};
 use rustc_session::lint;
@@ -43,6 +44,10 @@ type IdentMap<T> = FxHashMap<Ident, T>;
 /// Map from the name in a pattern to its binding mode.
 type BindingMap = IdentMap<BindingInfo>;
 
+use diagnostics::{
+    ElisionFnParameter, LifetimeElisionCandidate, MissingLifetime, MissingLifetimeKind,
+};
+
 #[derive(Copy, Clone, Debug)]
 struct BindingInfo {
     span: Span,
@@ -257,8 +262,11 @@ enum LifetimeRibKind {
     /// error on default object bounds (e.g., `Box<dyn Foo>`).
     AnonymousReportError,
 
-    /// Pass responsibility to `resolve_lifetime` code for all cases.
-    AnonymousPassThrough(NodeId, /* in_fn_return */ bool),
+    /// Replace all anonymous lifetimes by provided lifetime.
+    Elided(LifetimeRes),
+
+    /// Signal we cannot find which should be the anonymous lifetime.
+    ElisionFailure,
 }
 
 #[derive(Copy, Clone, Debug)]
@@ -268,6 +276,7 @@ enum LifetimeBinderKind {
     WhereBound,
     Item,
     Function,
+    Closure,
     ImplBlock,
 }
 
@@ -281,6 +290,7 @@ impl LifetimeBinderKind {
             Item => "item",
             ImplBlock => "impl block",
             Function => "function",
+            Closure => "closure",
         }
     }
 }
@@ -520,6 +530,10 @@ struct DiagnosticMetadata<'ast> {
 
     /// When processing impl trait
     currently_processing_impl_trait: Option<(TraitRef, Ty)>,
+
+    /// Accumulate the errors due to missed lifetime elision,
+    /// and report them all at once for each function.
+    current_elision_failures: Vec<MissingLifetime>,
 }
 
 struct LateResolutionVisitor<'a, 'b, 'ast> {
@@ -538,11 +552,18 @@ struct LateResolutionVisitor<'a, 'b, 'ast> {
     /// The current set of local scopes for lifetimes.
     lifetime_ribs: Vec<LifetimeRib>,
 
+    /// We are looking for lifetimes in an elision context.
+    /// The set contains all the resolutions that we encountered so far.
+    /// They will be used to determine the correct lifetime for the fn return type.
+    /// The `LifetimeElisionCandidate` is used for diagnostics, to suggest introducing named
+    /// lifetimes.
+    lifetime_elision_candidates: Option<FxIndexMap<LifetimeRes, LifetimeElisionCandidate>>,
+
     /// The trait that the current context can refer to.
     current_trait_ref: Option<(Module<'a>, TraitRef)>,
 
     /// Fields used to add information to diagnostic errors.
-    diagnostic_metadata: DiagnosticMetadata<'ast>,
+    diagnostic_metadata: Box<DiagnosticMetadata<'ast>>,
 
     /// State used to know whether to ignore resolution errors for function bodies.
     ///
@@ -578,7 +599,9 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
     fn visit_anon_const(&mut self, constant: &'ast AnonConst) {
         // We deal with repeat expressions explicitly in `resolve_expr`.
         self.with_lifetime_rib(LifetimeRibKind::AnonConst, |this| {
-            this.resolve_anon_const(constant, IsRepeatExpr::No);
+            this.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Static), |this| {
+                this.resolve_anon_const(constant, IsRepeatExpr::No);
+            })
         })
     }
     fn visit_expr(&mut self, expr: &'ast Expr) {
@@ -605,8 +628,10 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
             TyKind::Rptr(None, _) => {
                 // Elided lifetime in reference: we resolve as if there was some lifetime `'_` with
                 // NodeId `ty.id`.
+                // This span will be used in case of elision failure.
                 let span = self.r.session.source_map().next_point(ty.span.shrink_to_lo());
                 self.resolve_elided_lifetime(ty.id, span);
+                visit::walk_ty(self, ty);
             }
             TyKind::Path(ref qself, ref path) => {
                 self.diagnostic_metadata.current_type_path = Some(ty);
@@ -632,8 +657,8 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
                         },
                         |this| this.visit_path(&path, ty.id),
                     );
-                    self.diagnostic_metadata.current_type_path = prev_ty;
-                    return;
+                } else {
+                    visit::walk_ty(self, ty)
                 }
             }
             TyKind::ImplicitSelf => {
@@ -647,9 +672,16 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
                     )
                     .map_or(Res::Err, |d| d.res());
                 self.r.record_partial_res(ty.id, PartialRes::new(res));
+                visit::walk_ty(self, ty)
+            }
+            TyKind::ImplTrait(..) => {
+                let candidates = self.lifetime_elision_candidates.take();
+                visit::walk_ty(self, ty);
+                self.lifetime_elision_candidates = candidates;
             }
             TyKind::TraitObject(ref bounds, ..) => {
                 self.diagnostic_metadata.current_trait_object = Some(&bounds[..]);
+                visit::walk_ty(self, ty)
             }
             TyKind::BareFn(ref bare_fn) => {
                 let span = ty.span.shrink_to_lo().to(bare_fn.decl_span.shrink_to_lo());
@@ -668,20 +700,26 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
                                 binder: ty.id,
                                 report_in_path: false,
                             },
-                            |this| walk_list!(this, visit_param, &bare_fn.decl.inputs),
-                        );
-                        this.with_lifetime_rib(
-                            LifetimeRibKind::AnonymousPassThrough(ty.id, true),
-                            |this| this.visit_fn_ret_ty(&bare_fn.decl.output),
+                            |this| {
+                                this.resolve_fn_signature(
+                                    ty.id,
+                                    false,
+                                    // We don't need to deal with patterns in parameters, because
+                                    // they are not possible for foreign or bodiless functions.
+                                    bare_fn
+                                        .decl
+                                        .inputs
+                                        .iter()
+                                        .map(|Param { ty, .. }| (None, &**ty)),
+                                    &bare_fn.decl.output,
+                                )
+                            },
                         );
                     },
-                );
-                self.diagnostic_metadata.current_trait_object = prev;
-                return;
+                )
             }
-            _ => (),
+            _ => visit::walk_ty(self, ty),
         }
-        visit::walk_ty(self, ty);
         self.diagnostic_metadata.current_trait_object = prev;
         self.diagnostic_metadata.current_type_path = prev_ty;
     }
@@ -755,15 +793,19 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
             | FnKind::Fn(_, _, sig, _, generics, None) => {
                 self.visit_fn_header(&sig.header);
                 self.visit_generics(generics);
-                // We don't need to deal with patterns in parameters, because
-                // they are not possible for foreign or bodiless functions.
-                self.with_lifetime_rib(
-                    LifetimeRibKind::AnonymousPassThrough(fn_id, false),
-                    |this| walk_list!(this, visit_param, &sig.decl.inputs),
-                );
                 self.with_lifetime_rib(
-                    LifetimeRibKind::AnonymousPassThrough(fn_id, true),
-                    |this| this.visit_fn_ret_ty(&sig.decl.output),
+                    LifetimeRibKind::AnonymousCreateParameter {
+                        binder: fn_id,
+                        report_in_path: false,
+                    },
+                    |this| {
+                        this.resolve_fn_signature(
+                            fn_id,
+                            sig.decl.has_self(),
+                            sig.decl.inputs.iter().map(|Param { ty, .. }| (None, &**ty)),
+                            &sig.decl.output,
+                        )
+                    },
                 );
                 return;
             }
@@ -788,24 +830,22 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
                         let declaration = &sig.decl;
                         let async_node_id = sig.header.asyncness.opt_return_id();
 
-                        // Argument-position elided lifetimes must be transformed into fresh
-                        // generic parameters.  This is especially useful for `async fn`, where
-                        // these fresh generic parameters can be applied to the opaque `impl Trait`
-                        // return type.
-                        let rib = if async_node_id.is_some() {
-                            // Only emit a hard error for `async fn`, since this kind of
-                            // elision has always been allowed in regular `fn`s.
+                        this.with_lifetime_rib(
                             LifetimeRibKind::AnonymousCreateParameter {
                                 binder: fn_id,
-                                report_in_path: true,
-                            }
-                        } else {
-                            LifetimeRibKind::AnonymousPassThrough(fn_id, false)
-                        };
-                        this.with_lifetime_rib(
-                            rib,
-                            // Add each argument to the rib.
-                            |this| this.resolve_params(&declaration.inputs),
+                                report_in_path: async_node_id.is_some(),
+                            },
+                            |this| {
+                                this.resolve_fn_signature(
+                                    fn_id,
+                                    declaration.has_self(),
+                                    declaration
+                                        .inputs
+                                        .iter()
+                                        .map(|Param { pat, ty, .. }| (Some(&**pat), &**ty)),
+                                    &declaration.output,
+                                )
+                            },
                         );
 
                         // Construct the list of in-scope lifetime parameters for async lowering.
@@ -844,23 +884,13 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
                                 .insert(async_node_id, extra_lifetime_params);
                         }
 
-                        this.with_lifetime_rib(
-                            LifetimeRibKind::AnonymousPassThrough(
-                                // For async fn, the return type appears inside a custom
-                                // `impl Future` RPIT, so we override the binder's id.
-                                async_node_id.unwrap_or(fn_id),
-                                true,
-                            ),
-                            |this| visit::walk_fn_ret_ty(this, &declaration.output),
-                        );
-
                         if let Some(body) = body {
                             // Ignore errors in function bodies if this is rustdoc
                             // Be sure not to set this until the function signature has been resolved.
                             let previous_state = replace(&mut this.in_func_body, true);
                             // Resolve the function body, potentially inside the body of an async closure
                             this.with_lifetime_rib(
-                                LifetimeRibKind::AnonymousPassThrough(fn_id, false),
+                                LifetimeRibKind::Elided(LifetimeRes::Infer),
                                 |this| this.visit_block(body),
                             );
 
@@ -868,19 +898,30 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
                             this.in_func_body = previous_state;
                         }
                     }
-                    FnKind::Closure(declaration, body) => {
-                        // We do not have any explicit generic lifetime parameter.
-                        // FIXME(rfc3216): Change when implementing `for<>` bounds on closures.
+                    FnKind::Closure(binder, declaration, body) => {
+                        this.visit_closure_binder(binder);
+
                         this.with_lifetime_rib(
-                            LifetimeRibKind::AnonymousCreateParameter {
-                                binder: fn_id,
-                                report_in_path: false,
+                            match binder {
+                                // We do not have any explicit generic lifetime parameter.
+                                ClosureBinder::NotPresent => {
+                                    LifetimeRibKind::AnonymousCreateParameter {
+                                        binder: fn_id,
+                                        report_in_path: false,
+                                    }
+                                }
+                                ClosureBinder::For { .. } => LifetimeRibKind::AnonymousReportError,
                             },
                             // Add each argument to the rib.
                             |this| this.resolve_params(&declaration.inputs),
                         );
                         this.with_lifetime_rib(
-                            LifetimeRibKind::AnonymousPassThrough(fn_id, true),
+                            match binder {
+                                ClosureBinder::NotPresent => {
+                                    LifetimeRibKind::Elided(LifetimeRes::Infer)
+                                }
+                                ClosureBinder::For { .. } => LifetimeRibKind::AnonymousReportError,
+                            },
                             |this| visit::walk_fn_ret_ty(this, &declaration.output),
                         );
 
@@ -889,7 +930,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
                         let previous_state = replace(&mut this.in_func_body, true);
                         // Resolve the function body, potentially inside the body of an async closure
                         this.with_lifetime_rib(
-                            LifetimeRibKind::AnonymousPassThrough(fn_id, false),
+                            LifetimeRibKind::Elided(LifetimeRes::Infer),
                             |this| this.visit_expr(body),
                         );
 
@@ -915,6 +956,18 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
         }
     }
 
+    fn visit_closure_binder(&mut self, b: &'ast ClosureBinder) {
+        match b {
+            ClosureBinder::NotPresent => {}
+            ClosureBinder::For { generic_params, .. } => {
+                self.visit_generic_params(
+                    &generic_params,
+                    self.diagnostic_metadata.current_self_item.is_some(),
+                );
+            }
+        }
+    }
+
     fn visit_generic_arg(&mut self, arg: &'ast GenericArg) {
         debug!("visit_generic_arg({:?})", arg);
         let prev = replace(&mut self.diagnostic_metadata.currently_processing_generics, true);
@@ -1012,11 +1065,14 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
                                         binder,
                                         report_in_path: false,
                                     },
-                                    |this| walk_list!(this, visit_ty, &p_args.inputs),
-                                );
-                                self.with_lifetime_rib(
-                                    LifetimeRibKind::AnonymousPassThrough(binder, true),
-                                    |this| visit::walk_fn_ret_ty(this, &p_args.output),
+                                    |this| {
+                                        this.resolve_fn_signature(
+                                            binder,
+                                            false,
+                                            p_args.inputs.iter().map(|ty| (None, &**ty)),
+                                            &p_args.output,
+                                        )
+                                    },
                                 );
                                 break;
                             }
@@ -1026,9 +1082,10 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
                                 visit::walk_generic_args(self, path_span, args);
                                 break;
                             }
-                            LifetimeRibKind::AnonymousPassThrough(..)
-                            | LifetimeRibKind::AnonymousCreateParameter { .. }
+                            LifetimeRibKind::AnonymousCreateParameter { .. }
                             | LifetimeRibKind::AnonymousReportError
+                            | LifetimeRibKind::Elided(_)
+                            | LifetimeRibKind::ElisionFailure
                             | LifetimeRibKind::AnonConst
                             | LifetimeRibKind::ConstGeneric => {}
                         }
@@ -1133,8 +1190,9 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
             },
             label_ribs: Vec::new(),
             lifetime_ribs: Vec::new(),
+            lifetime_elision_candidates: None,
             current_trait_ref: None,
-            diagnostic_metadata: DiagnosticMetadata::default(),
+            diagnostic_metadata: Box::new(DiagnosticMetadata::default()),
             // errors at module scope should always be reported
             in_func_body: false,
             lifetime_uses: Default::default(),
@@ -1339,7 +1397,9 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
         work: impl FnOnce(&mut Self) -> T,
     ) -> T {
         self.lifetime_ribs.push(LifetimeRib::new(kind));
+        let outer_elision_candidates = self.lifetime_elision_candidates.take();
         let ret = work(self);
+        self.lifetime_elision_candidates = outer_elision_candidates;
         self.lifetime_ribs.pop();
         ret
     }
@@ -1349,7 +1409,11 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
         let ident = lifetime.ident;
 
         if ident.name == kw::StaticLifetime {
-            self.record_lifetime_res(lifetime.id, LifetimeRes::Static);
+            self.record_lifetime_res(
+                lifetime.id,
+                LifetimeRes::Static,
+                LifetimeElisionCandidate::Named,
+            );
             return;
         }
 
@@ -1362,7 +1426,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
             let rib = &self.lifetime_ribs[i];
             let normalized_ident = ident.normalize_to_macros_2_0();
             if let Some(&(_, res)) = rib.bindings.get(&normalized_ident) {
-                self.record_lifetime_res(lifetime.id, res);
+                self.record_lifetime_res(lifetime.id, res, LifetimeElisionCandidate::Named);
 
                 if let LifetimeRes::Param { param, .. } = res {
                     match self.lifetime_uses.entry(param) {
@@ -1376,15 +1440,19 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                                     // Do not suggest eliding a lifetime where an anonymous
                                     // lifetime would be illegal.
                                     LifetimeRibKind::Item
-                                    | LifetimeRibKind::AnonymousPassThrough(_, true)
-                                    | LifetimeRibKind::AnonymousReportError => {
-                                        Some(LifetimeUseSet::Many)
-                                    }
+                                    | LifetimeRibKind::AnonymousReportError
+                                    | LifetimeRibKind::ElisionFailure => Some(LifetimeUseSet::Many),
                                     // An anonymous lifetime is legal here, go ahead.
-                                    LifetimeRibKind::AnonymousPassThrough(_, false)
-                                    | LifetimeRibKind::AnonymousCreateParameter { .. } => {
+                                    LifetimeRibKind::AnonymousCreateParameter { .. } => {
                                         Some(LifetimeUseSet::One { use_span: ident.span, use_ctxt })
                                     }
+                                    // Only report if eliding the lifetime would have the same
+                                    // semantics.
+                                    LifetimeRibKind::Elided(r) => Some(if res == r {
+                                        LifetimeUseSet::One { use_span: ident.span, use_ctxt }
+                                    } else {
+                                        LifetimeUseSet::Many
+                                    }),
                                     LifetimeRibKind::Generics { .. }
                                     | LifetimeRibKind::ConstGeneric
                                     | LifetimeRibKind::AnonConst => None,
@@ -1406,12 +1474,20 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                 LifetimeRibKind::Item => break,
                 LifetimeRibKind::ConstGeneric => {
                     self.emit_non_static_lt_in_const_generic_error(lifetime);
-                    self.r.lifetimes_res_map.insert(lifetime.id, LifetimeRes::Error);
+                    self.record_lifetime_res(
+                        lifetime.id,
+                        LifetimeRes::Error,
+                        LifetimeElisionCandidate::Ignore,
+                    );
                     return;
                 }
                 LifetimeRibKind::AnonConst => {
                     self.maybe_emit_forbidden_non_static_lifetime_error(lifetime);
-                    self.r.lifetimes_res_map.insert(lifetime.id, LifetimeRes::Error);
+                    self.record_lifetime_res(
+                        lifetime.id,
+                        LifetimeRes::Error,
+                        LifetimeElisionCandidate::Ignore,
+                    );
                     return;
                 }
                 _ => {}
@@ -1429,19 +1505,31 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
         }
 
         self.emit_undeclared_lifetime_error(lifetime, outer_res);
-        self.record_lifetime_res(lifetime.id, LifetimeRes::Error);
+        self.record_lifetime_res(lifetime.id, LifetimeRes::Error, LifetimeElisionCandidate::Named);
     }
 
     #[tracing::instrument(level = "debug", skip(self))]
     fn resolve_anonymous_lifetime(&mut self, lifetime: &Lifetime, elided: bool) {
         debug_assert_eq!(lifetime.ident.name, kw::UnderscoreLifetime);
 
+        let missing_lifetime = MissingLifetime {
+            id: lifetime.id,
+            span: lifetime.ident.span,
+            kind: if elided {
+                MissingLifetimeKind::Ampersand
+            } else {
+                MissingLifetimeKind::Underscore
+            },
+            count: 1,
+        };
+        let elision_candidate = LifetimeElisionCandidate::Missing(missing_lifetime);
         for i in (0..self.lifetime_ribs.len()).rev() {
             let rib = &mut self.lifetime_ribs[i];
+            debug!(?rib.kind);
             match rib.kind {
                 LifetimeRibKind::AnonymousCreateParameter { binder, .. } => {
                     let res = self.create_fresh_lifetime(lifetime.id, lifetime.ident, binder);
-                    self.record_lifetime_res(lifetime.id, res);
+                    self.record_lifetime_res(lifetime.id, res, elision_candidate);
                     return;
                 }
                 LifetimeRibKind::AnonymousReportError => {
@@ -1463,14 +1551,16 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                     .span_label(lifetime.ident.span, note)
                     .emit();
 
-                    self.record_lifetime_res(lifetime.id, LifetimeRes::Error);
+                    self.record_lifetime_res(lifetime.id, LifetimeRes::Error, elision_candidate);
                     return;
                 }
-                LifetimeRibKind::AnonymousPassThrough(node_id, _) => {
-                    self.record_lifetime_res(
-                        lifetime.id,
-                        LifetimeRes::Anonymous { binder: node_id, elided },
-                    );
+                LifetimeRibKind::Elided(res) => {
+                    self.record_lifetime_res(lifetime.id, res, elision_candidate);
+                    return;
+                }
+                LifetimeRibKind::ElisionFailure => {
+                    self.diagnostic_metadata.current_elision_failures.push(missing_lifetime);
+                    self.record_lifetime_res(lifetime.id, LifetimeRes::Error, elision_candidate);
                     return;
                 }
                 LifetimeRibKind::Item => break,
@@ -1479,23 +1569,20 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                 | LifetimeRibKind::AnonConst => {}
             }
         }
-        // This resolution is wrong, it passes the work to HIR lifetime resolution.
-        // We cannot use `LifetimeRes::Error` because we do not emit a diagnostic.
-        self.record_lifetime_res(
-            lifetime.id,
-            LifetimeRes::Anonymous { binder: DUMMY_NODE_ID, elided },
-        );
+        self.record_lifetime_res(lifetime.id, LifetimeRes::Error, elision_candidate);
+        self.report_missing_lifetime_specifiers(vec![missing_lifetime], None);
     }
 
     #[tracing::instrument(level = "debug", skip(self))]
     fn resolve_elided_lifetime(&mut self, anchor_id: NodeId, span: Span) {
         let id = self.r.next_node_id();
+        let lt = Lifetime { id, ident: Ident::new(kw::UnderscoreLifetime, span) };
+
         self.record_lifetime_res(
             anchor_id,
             LifetimeRes::ElidedAnchor { start: id, end: NodeId::from_u32(id.as_u32() + 1) },
+            LifetimeElisionCandidate::Ignore,
         );
-
-        let lt = Lifetime { id, ident: Ident::new(kw::UnderscoreLifetime, span) };
         self.resolve_anonymous_lifetime(&lt, true);
     }
 
@@ -1564,6 +1651,9 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                 | PathSource::Struct
                 | PathSource::TupleStruct(..) => false,
             };
+            if !missing && !segment.has_generic_args {
+                continue;
+            }
 
             let elided_lifetime_span = if segment.has_generic_args {
                 // If there are brackets, but not generic arguments, then use the opening bracket
@@ -1580,43 +1670,31 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
             self.record_lifetime_res(
                 segment_id,
                 LifetimeRes::ElidedAnchor { start: node_ids.start, end: node_ids.end },
+                LifetimeElisionCandidate::Ignore,
             );
 
             if !missing {
                 // Do not create a parameter for patterns and expressions.
-                for rib in self.lifetime_ribs.iter().rev() {
-                    match rib.kind {
-                        LifetimeRibKind::AnonymousPassThrough(binder, _) => {
-                            let res = LifetimeRes::Anonymous { binder, elided: true };
-                            for id in node_ids {
-                                self.record_lifetime_res(id, res);
-                            }
-                            break;
-                        }
-                        // `LifetimeRes::Error`, which would usually be used in the case of
-                        // `ReportError`, is unsuitable here, as we don't emit an error yet.  Instead,
-                        // we simply resolve to an implicit lifetime, which will be checked later, at
-                        // which point a suitable error will be emitted.
-                        LifetimeRibKind::AnonymousReportError | LifetimeRibKind::Item => {
-                            // FIXME(cjgillot) This resolution is wrong, but this does not matter
-                            // since these cases are erroneous anyway.  Lifetime resolution should
-                            // emit a "missing lifetime specifier" diagnostic.
-                            let res =
-                                LifetimeRes::Anonymous { binder: DUMMY_NODE_ID, elided: true };
-                            for id in node_ids {
-                                self.record_lifetime_res(id, res);
-                            }
-                            break;
-                        }
-                        LifetimeRibKind::AnonymousCreateParameter { .. }
-                        | LifetimeRibKind::Generics { .. }
-                        | LifetimeRibKind::ConstGeneric
-                        | LifetimeRibKind::AnonConst => {}
-                    }
+                for id in node_ids {
+                    self.record_lifetime_res(
+                        id,
+                        LifetimeRes::Infer,
+                        LifetimeElisionCandidate::Named,
+                    );
                 }
                 continue;
             }
 
+            let missing_lifetime = MissingLifetime {
+                id: node_ids.start,
+                span: elided_lifetime_span,
+                kind: if segment.has_generic_args {
+                    MissingLifetimeKind::Comma
+                } else {
+                    MissingLifetimeKind::Brackets
+                },
+                count: expected_lifetimes,
+            };
             let mut should_lint = true;
             for rib in self.lifetime_ribs.iter().rev() {
                 match rib.kind {
@@ -1647,23 +1725,47 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                         should_lint = false;
 
                         for id in node_ids {
-                            self.record_lifetime_res(id, LifetimeRes::Error);
+                            self.record_lifetime_res(
+                                id,
+                                LifetimeRes::Error,
+                                LifetimeElisionCandidate::Named,
+                            );
                         }
                         break;
                     }
                     // Do not create a parameter for patterns and expressions.
                     LifetimeRibKind::AnonymousCreateParameter { binder, .. } => {
+                        // Group all suggestions into the first record.
+                        let mut candidate = LifetimeElisionCandidate::Missing(missing_lifetime);
                         for id in node_ids {
                             let res = self.create_fresh_lifetime(id, ident, binder);
-                            self.record_lifetime_res(id, res);
+                            self.record_lifetime_res(
+                                id,
+                                res,
+                                replace(&mut candidate, LifetimeElisionCandidate::Named),
+                            );
                         }
                         break;
                     }
-                    // `PassThrough` is the normal case.
-                    LifetimeRibKind::AnonymousPassThrough(binder, _) => {
-                        let res = LifetimeRes::Anonymous { binder, elided: true };
+                    LifetimeRibKind::Elided(res) => {
+                        let mut candidate = LifetimeElisionCandidate::Missing(missing_lifetime);
                         for id in node_ids {
-                            self.record_lifetime_res(id, res);
+                            self.record_lifetime_res(
+                                id,
+                                res,
+                                replace(&mut candidate, LifetimeElisionCandidate::Ignore),
+                            );
+                        }
+                        break;
+                    }
+                    LifetimeRibKind::ElisionFailure => {
+                        self.diagnostic_metadata.current_elision_failures.push(missing_lifetime);
+                        for id in node_ids {
+                            self.record_lifetime_res(
+                                id,
+                                LifetimeRes::Error,
+                                LifetimeElisionCandidate::Ignore,
+                            );
                         }
                         break;
                     }
@@ -1672,13 +1774,14 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                     // we simply resolve to an implicit lifetime, which will be checked later, at
                     // which point a suitable error will be emitted.
                     LifetimeRibKind::AnonymousReportError | LifetimeRibKind::Item => {
-                        // FIXME(cjgillot) This resolution is wrong, but this does not matter
-                        // since these cases are erroneous anyway.  Lifetime resolution should
-                        // emit a "missing lifetime specifier" diagnostic.
-                        let res = LifetimeRes::Anonymous { binder: DUMMY_NODE_ID, elided: true };
                         for id in node_ids {
-                            self.record_lifetime_res(id, res);
+                            self.record_lifetime_res(
+                                id,
+                                LifetimeRes::Error,
+                                LifetimeElisionCandidate::Ignore,
+                            );
                         }
+                        self.report_missing_lifetime_specifiers(vec![missing_lifetime], None);
                         break;
                     }
                     LifetimeRibKind::Generics { .. }
@@ -1705,13 +1808,214 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
     }
 
     #[tracing::instrument(level = "debug", skip(self))]
-    fn record_lifetime_res(&mut self, id: NodeId, res: LifetimeRes) {
+    fn record_lifetime_res(
+        &mut self,
+        id: NodeId,
+        res: LifetimeRes,
+        candidate: LifetimeElisionCandidate,
+    ) {
         if let Some(prev_res) = self.r.lifetimes_res_map.insert(id, res) {
             panic!(
                 "lifetime {:?} resolved multiple times ({:?} before, {:?} now)",
                 id, prev_res, res
             )
         }
+        match res {
+            LifetimeRes::Param { .. } | LifetimeRes::Fresh { .. } | LifetimeRes::Static => {
+                if let Some(ref mut candidates) = self.lifetime_elision_candidates {
+                    candidates.insert(res, candidate);
+                }
+            }
+            LifetimeRes::Infer | LifetimeRes::Error | LifetimeRes::ElidedAnchor { .. } => {}
+        }
+    }
+
+    #[tracing::instrument(level = "debug", skip(self))]
+    fn record_lifetime_param(&mut self, id: NodeId, res: LifetimeRes) {
+        if let Some(prev_res) = self.r.lifetimes_res_map.insert(id, res) {
+            panic!(
+                "lifetime parameter {:?} resolved multiple times ({:?} before, {:?} now)",
+                id, prev_res, res
+            )
+        }
+    }
+
+    /// Perform resolution of a function signature, accounting for lifetime elision.
+    #[tracing::instrument(level = "debug", skip(self, inputs))]
+    fn resolve_fn_signature(
+        &mut self,
+        fn_id: NodeId,
+        has_self: bool,
+        inputs: impl Iterator<Item = (Option<&'ast Pat>, &'ast Ty)> + Clone,
+        output_ty: &'ast FnRetTy,
+    ) {
+        // Add each argument to the rib.
+        let elision_lifetime = self.resolve_fn_params(has_self, inputs);
+        debug!(?elision_lifetime);
+
+        let outer_failures = take(&mut self.diagnostic_metadata.current_elision_failures);
+        let output_rib = if let Ok(res) = elision_lifetime.as_ref() {
+            LifetimeRibKind::Elided(*res)
+        } else {
+            LifetimeRibKind::ElisionFailure
+        };
+        self.with_lifetime_rib(output_rib, |this| visit::walk_fn_ret_ty(this, &output_ty));
+        let elision_failures =
+            replace(&mut self.diagnostic_metadata.current_elision_failures, outer_failures);
+        if !elision_failures.is_empty() {
+            let Err(failure_info) = elision_lifetime else { bug!() };
+            self.report_missing_lifetime_specifiers(elision_failures, Some(failure_info));
+        }
+    }
+
+    /// Resolve inside function parameters and parameter types.
+    /// Returns the lifetime for elision in fn return type,
+    /// or diagnostic information in case of elision failure.
+    fn resolve_fn_params(
+        &mut self,
+        has_self: bool,
+        inputs: impl Iterator<Item = (Option<&'ast Pat>, &'ast Ty)>,
+    ) -> Result<LifetimeRes, (Vec<MissingLifetime>, Vec<ElisionFnParameter>)> {
+        let outer_candidates =
+            replace(&mut self.lifetime_elision_candidates, Some(Default::default()));
+
+        let mut elision_lifetime = None;
+        let mut lifetime_count = 0;
+        let mut parameter_info = Vec::new();
+
+        let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())];
+        for (index, (pat, ty)) in inputs.enumerate() {
+            debug!(?pat, ?ty);
+            if let Some(pat) = pat {
+                self.resolve_pattern(pat, PatternSource::FnParam, &mut bindings);
+            }
+            self.visit_ty(ty);
+
+            if let Some(ref candidates) = self.lifetime_elision_candidates {
+                let new_count = candidates.len();
+                let local_count = new_count - lifetime_count;
+                if local_count != 0 {
+                    parameter_info.push(ElisionFnParameter {
+                        index,
+                        ident: if let Some(pat) = pat && let PatKind::Ident(_, ident, _) = pat.kind {
+                            Some(ident)
+                        } else {
+                            None
+                        },
+                        lifetime_count: local_count,
+                        span: ty.span,
+                    });
+                }
+                lifetime_count = new_count;
+            }
+
+            // Handle `self` specially.
+            if index == 0 && has_self {
+                let self_lifetime = self.find_lifetime_for_self(ty);
+                if let Set1::One(lifetime) = self_lifetime {
+                    elision_lifetime = Some(lifetime);
+                    self.lifetime_elision_candidates = None;
+                } else {
+                    self.lifetime_elision_candidates = Some(Default::default());
+                    lifetime_count = 0;
+                }
+            }
+            debug!("(resolving function / closure) recorded parameter");
+        }
+
+        let all_candidates = replace(&mut self.lifetime_elision_candidates, outer_candidates);
+        debug!(?all_candidates);
+
+        if let Some(res) = elision_lifetime {
+            return Ok(res);
+        }
+
+        // We do not have a `self` candidate, look at the full list.
+        let all_candidates = all_candidates.unwrap();
+        if all_candidates.len() == 1 {
+            Ok(*all_candidates.first().unwrap().0)
+        } else {
+            let all_candidates = all_candidates
+                .into_iter()
+                .filter_map(|(_, candidate)| match candidate {
+                    LifetimeElisionCandidate::Ignore | LifetimeElisionCandidate::Named => None,
+                    LifetimeElisionCandidate::Missing(missing) => Some(missing),
+                })
+                .collect();
+            Err((all_candidates, parameter_info))
+        }
+    }
+
+    /// List all the lifetimes that appear in the provided type.
+    fn find_lifetime_for_self(&self, ty: &'ast Ty) -> Set1<LifetimeRes> {
+        struct SelfVisitor<'r, 'a> {
+            r: &'r Resolver<'a>,
+            impl_self: Option<Res>,
+            lifetime: Set1<LifetimeRes>,
+        }
+
+        impl SelfVisitor<'_, '_> {
+            // Look for `self: &'a Self` - also desugared from `&'a self`,
+            // and if that matches, use it for elision and return early.
+            fn is_self_ty(&self, ty: &Ty) -> bool {
+                match ty.kind {
+                    TyKind::ImplicitSelf => true,
+                    TyKind::Path(None, _) => {
+                        let path_res = self.r.partial_res_map[&ty.id].base_res();
+                        if let Res::SelfTy { .. } = path_res {
+                            return true;
+                        }
+                        Some(path_res) == self.impl_self
+                    }
+                    _ => false,
+                }
+            }
+        }
+
+        impl<'a> Visitor<'a> for SelfVisitor<'_, '_> {
+            fn visit_ty(&mut self, ty: &'a Ty) {
+                trace!("SelfVisitor considering ty={:?}", ty);
+                if let TyKind::Rptr(lt, ref mt) = ty.kind && self.is_self_ty(&mt.ty) {
+                    let lt_id = if let Some(lt) = lt {
+                        lt.id
+                    } else {
+                        let res = self.r.lifetimes_res_map[&ty.id];
+                        let LifetimeRes::ElidedAnchor { start, .. } = res else { bug!() };
+                        start
+                    };
+                    let lt_res = self.r.lifetimes_res_map[&lt_id];
+                    trace!("SelfVisitor inserting res={:?}", lt_res);
+                    self.lifetime.insert(lt_res);
+                }
+                visit::walk_ty(self, ty)
+            }
+        }
+
+        let impl_self = self
+            .diagnostic_metadata
+            .current_self_type
+            .as_ref()
+            .and_then(|ty| {
+                if let TyKind::Path(None, _) = ty.kind {
+                    self.r.partial_res_map.get(&ty.id)
+                } else {
+                    None
+                }
+            })
+            .map(|res| res.base_res())
+            .filter(|res| {
+                // Permit the types that unambiguously always
+                // result in the same type constructor being used
+                // (it can't differ between `Self` and `self`).
+                matches!(
+                    res,
+                    Res::Def(DefKind::Struct | DefKind::Union | DefKind::Enum, _,) | Res::PrimTy(_)
+                )
+            });
+        let mut visitor = SelfVisitor { r: self.r, impl_self, lifetime: Set1::Empty };
+        visitor.visit_ty(ty);
+        trace!("SelfVisitor found={:?}", visitor.lifetime);
+        visitor.lifetime
     }
 
     /// Searches the current set of local scopes for labels. Returns the `NodeId` of the resolved
@@ -1936,22 +2240,26 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
 
             ItemKind::Static(ref ty, _, ref expr) | ItemKind::Const(_, ref ty, ref expr) => {
                 self.with_item_rib(|this| {
-                    this.visit_ty(ty);
-                    if let Some(expr) = expr {
-                        let constant_item_kind = match item.kind {
-                            ItemKind::Const(..) => ConstantItemKind::Const,
-                            ItemKind::Static(..) => ConstantItemKind::Static,
-                            _ => unreachable!(),
-                        };
-                        // We already forbid generic params because of the above item rib,
-                        // so it doesn't matter whether this is a trivial constant.
-                        this.with_constant_rib(
-                            IsRepeatExpr::No,
-                            HasGenericParams::Yes,
-                            Some((item.ident, constant_item_kind)),
-                            |this| this.visit_expr(expr),
-                        );
-                    }
+                    this.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Static), |this| {
+                        this.visit_ty(ty);
+                    });
+                    this.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Infer), |this| {
+                        if let Some(expr) = expr {
+                            let constant_item_kind = match item.kind {
+                                ItemKind::Const(..) => ConstantItemKind::Const,
+                                ItemKind::Static(..) => ConstantItemKind::Static,
+                                _ => unreachable!(),
+                            };
+                            // We already forbid generic params because of the above item rib,
+                            // so it doesn't matter whether this is a trivial constant.
+                            this.with_constant_rib(
+                                IsRepeatExpr::No,
+                                HasGenericParams::Yes,
+                                Some((item.ident, constant_item_kind)),
+                                |this| this.visit_expr(expr),
+                            );
+                        }
+                    });
                 });
             }
 
@@ -2022,7 +2330,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
             {
                 diagnostics::signal_lifetime_shadowing(self.r.session, original, param.ident);
                 // Record lifetime res, so lowering knows there is something fishy.
-                self.record_lifetime_res(param.id, LifetimeRes::Error);
+                self.record_lifetime_param(param.id, LifetimeRes::Error);
                 continue;
             }
 
@@ -2033,7 +2341,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                     self.report_error(param.ident.span, err);
                     if let GenericParamKind::Lifetime = param.kind {
                         // Record lifetime res, so lowering knows there is something fishy.
-                        self.record_lifetime_res(param.id, LifetimeRes::Error);
+                        self.record_lifetime_param(param.id, LifetimeRes::Error);
                         continue;
                     }
                 }
@@ -2052,7 +2360,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                 .span_label(param.ident.span, "`'_` is a reserved lifetime name")
                 .emit();
                 // Record lifetime res, so lowering knows there is something fishy.
-                self.record_lifetime_res(param.id, LifetimeRes::Error);
+                self.record_lifetime_param(param.id, LifetimeRes::Error);
                 continue;
             }
 
@@ -2067,7 +2375,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                 .span_label(param.ident.span, "'static is a reserved lifetime name")
                 .emit();
                 // Record lifetime res, so lowering knows there is something fishy.
-                self.record_lifetime_res(param.id, LifetimeRes::Error);
+                self.record_lifetime_param(param.id, LifetimeRes::Error);
                 continue;
             }
 
@@ -2079,7 +2387,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                 GenericParamKind::Const { .. } => (&mut function_value_rib, DefKind::ConstParam),
                 GenericParamKind::Lifetime => {
                     let res = LifetimeRes::Param { param: def_id, binder };
-                    self.record_lifetime_res(param.id, res);
+                    self.record_lifetime_param(param.id, res);
                     function_lifetime_rib.bindings.insert(ident, (param.id, res));
                     continue;
                 }
@@ -2088,7 +2396,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
             let res = match kind {
                 ItemRibKind(..) | AssocItemRibKind => Res::Def(def_kind, def_id.to_def_id()),
                 NormalRibKind => Res::Err,
-                _ => bug!("Unexpected rib kind {:?}", kind),
+                _ => span_bug!(param.ident.span, "Unexpected rib kind {:?}", kind),
             };
             self.r.record_partial_res(param.id, PartialRes::new(res));
             rib.bindings.insert(ident, res);
@@ -2102,7 +2410,14 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
 
         self.ribs[TypeNS].pop();
         self.ribs[ValueNS].pop();
-        self.lifetime_ribs.pop();
+        let function_lifetime_rib = self.lifetime_ribs.pop().unwrap();
+
+        // Do not account for the parameters we just bound for function lifetime elision.
+        if let Some(ref mut candidates) = self.lifetime_elision_candidates {
+            for (_, res) in function_lifetime_rib.bindings.values() {
+                candidates.remove(res);
+            }
+        }
 
         if let LifetimeBinderKind::BareFnType
         | LifetimeBinderKind::WhereBound
@@ -2200,20 +2515,26 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                         //
                         // Type parameters can already be used and as associated consts are
                         // not used as part of the type system, this is far less surprising.
-                        self.with_constant_rib(
-                            IsRepeatExpr::No,
-                            HasGenericParams::Yes,
-                            None,
-                            |this| this.visit_expr(expr),
+                        self.with_lifetime_rib(
+                            LifetimeRibKind::Elided(LifetimeRes::Infer),
+                            |this| {
+                                this.with_constant_rib(
+                                    IsRepeatExpr::No,
+                                    HasGenericParams::Yes,
+                                    None,
+                                    |this| this.visit_expr(expr),
+                                )
+                            },
                         );
                     }
                 }
                 AssocItemKind::Fn(box Fn { generics, .. }) => {
                     walk_assoc_item(self, generics, LifetimeBinderKind::Function, item);
                 }
-                AssocItemKind::TyAlias(box TyAlias { generics, .. }) => {
-                    walk_assoc_item(self, generics, LifetimeBinderKind::Item, item);
-                }
+                AssocItemKind::TyAlias(box TyAlias { generics, .. }) => self
+                    .with_lifetime_rib(LifetimeRibKind::AnonymousReportError, |this| {
+                        walk_assoc_item(this, generics, LifetimeBinderKind::Item, item)
+                    }),
                 AssocItemKind::MacCall(_) => {
                     panic!("unexpanded macro in resolve!")
                 }
@@ -2284,7 +2605,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
             LifetimeRibKind::Generics {
                 span: generics.span,
                 binder: item_id,
-                kind: LifetimeBinderKind::ImplBlock
+                kind: LifetimeBinderKind::ImplBlock,
             },
             |this| {
                 // Dummy self type for better errors if `Self` is used in the trait path.
@@ -2304,7 +2625,11 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
 
                                     // Register the trait definitions from here.
                                     if let Some(trait_id) = trait_id {
-                                        this.r.trait_impls.entry(trait_id).or_default().push(item_def_id);
+                                        this.r
+                                            .trait_impls
+                                            .entry(trait_id)
+                                            .or_default()
+                                            .push(item_def_id);
                                     }
 
                                     let item_def_id = item_def_id.to_def_id();
@@ -2323,21 +2648,17 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                                         this.visit_generics(generics);
 
                                         // Resolve the items within the impl.
-                                        this.with_lifetime_rib(LifetimeRibKind::AnonymousPassThrough(item_id,false),
-                                            |this| {
-                                                this.with_current_self_type(self_type, |this| {
-                                                    this.with_self_rib_ns(ValueNS, Res::SelfCtor(item_def_id), |this| {
-                                                        debug!("resolve_implementation with_self_rib_ns(ValueNS, ...)");
-                                                        for item in impl_items {
-                                                            this.resolve_impl_item(&**item);
-                                                        }
-                                                    });
-                                                });
-                                            },
-                                        );
+                                        this.with_current_self_type(self_type, |this| {
+                                            this.with_self_rib_ns(ValueNS, Res::SelfCtor(item_def_id), |this| {
+                                                debug!("resolve_implementation with_self_rib_ns(ValueNS, ...)");
+                                                for item in impl_items {
+                                                    this.resolve_impl_item(&**item);
+                                                }
+                                            });
+                                        });
                                     });
                                 },
-                            );
+                            )
                         },
                     );
                 });
@@ -2368,8 +2689,13 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                     //
                     // Type parameters can already be used and as associated consts are
                     // not used as part of the type system, this is far less surprising.
-                    self.with_constant_rib(IsRepeatExpr::No, HasGenericParams::Yes, None, |this| {
-                        this.visit_expr(expr)
+                    self.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Infer), |this| {
+                        this.with_constant_rib(
+                            IsRepeatExpr::No,
+                            HasGenericParams::Yes,
+                            None,
+                            |this| this.visit_expr(expr),
+                        )
                     });
                 }
             }
@@ -2412,18 +2738,20 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                         kind: LifetimeBinderKind::Item,
                     },
                     |this| {
-                        // If this is a trait impl, ensure the type
-                        // exists in trait
-                        this.check_trait_item(
-                            item.id,
-                            item.ident,
-                            &item.kind,
-                            TypeNS,
-                            item.span,
-                            |i, s, c| TypeNotMemberOfTrait(i, s, c),
-                        );
+                        this.with_lifetime_rib(LifetimeRibKind::AnonymousReportError, |this| {
+                            // If this is a trait impl, ensure the type
+                            // exists in trait
+                            this.check_trait_item(
+                                item.id,
+                                item.ident,
+                                &item.kind,
+                                TypeNS,
+                                item.span,
+                                |i, s, c| TypeNotMemberOfTrait(i, s, c),
+                            );
 
-                        visit::walk_assoc_item(this, item, AssocCtxt::Impl)
+                            visit::walk_assoc_item(this, item, AssocCtxt::Impl)
+                        });
                     },
                 );
             }
@@ -3519,7 +3847,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
             // `async |x| ...` gets desugared to `|x| future_from_generator(|| ...)`, so we need to
             // resolve the arguments within the proper scopes so that usages of them inside the
             // closure are detected as upvars rather than normal closure arg usages.
-            ExprKind::Closure(_, Async::Yes { .. }, _, ref fn_decl, ref body, _span) => {
+            ExprKind::Closure(_, _, Async::Yes { .. }, _, ref fn_decl, ref body, _span) => {
                 self.with_rib(ValueNS, NormalRibKind, |this| {
                     this.with_label_rib(ClosureOrAsyncRibKind, |this| {
                         // Resolve arguments:
@@ -3539,6 +3867,18 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                 });
             }
             // For closures, ClosureOrAsyncRibKind is added in visit_fn
+            ExprKind::Closure(ClosureBinder::For { ref generic_params, span }, ..) => {
+                self.with_generic_param_rib(
+                    &generic_params,
+                    NormalRibKind,
+                    LifetimeRibKind::Generics {
+                        binder: expr.id,
+                        kind: LifetimeBinderKind::Closure,
+                        span,
+                    },
+                    |this| visit::walk_expr(this, expr),
+                );
+            }
             ExprKind::Closure(..) => visit::walk_expr(self, expr),
             ExprKind::Async(..) => {
                 self.with_label_rib(ClosureOrAsyncRibKind, |this| visit::walk_expr(this, expr));
@@ -3546,7 +3886,9 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
             ExprKind::Repeat(ref elem, ref ct) => {
                 self.visit_expr(elem);
                 self.with_lifetime_rib(LifetimeRibKind::AnonConst, |this| {
-                    this.resolve_anon_const(ct, IsRepeatExpr::Yes)
+                    this.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Static), |this| {
+                        this.resolve_anon_const(ct, IsRepeatExpr::Yes)
+                    })
                 });
             }
             ExprKind::ConstBlock(ref ct) => {
diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs
index 677d7036b2f..6b49c6b1ac6 100644
--- a/compiler/rustc_resolve/src/late/diagnostics.rs
+++ b/compiler/rustc_resolve/src/late/diagnostics.rs
@@ -1,7 +1,6 @@
 use crate::diagnostics::{ImportSuggestion, LabelSuggestion, TypoSuggestion};
-use crate::late::lifetimes::{ElisionFailureInfo, LifetimeContext};
 use crate::late::{AliasPossibility, LateResolutionVisitor, RibKind};
-use crate::late::{LifetimeBinderKind, LifetimeRibKind, LifetimeUseSet};
+use crate::late::{LifetimeBinderKind, LifetimeRes, LifetimeRibKind, LifetimeUseSet};
 use crate::path_names_to_string;
 use crate::{Module, ModuleKind, ModuleOrUniformRoot};
 use crate::{PathResult, PathSource, Segment};
@@ -9,10 +8,10 @@ use crate::{PathResult, PathSource, Segment};
 use rustc_ast::visit::{FnCtxt, FnKind, LifetimeCtxt};
 use rustc_ast::{
     self as ast, AssocItemKind, Expr, ExprKind, GenericParam, GenericParamKind, Item, ItemKind,
-    NodeId, Path, Ty, TyKind,
+    NodeId, Path, Ty, TyKind, DUMMY_NODE_ID,
 };
 use rustc_ast_pretty::pprust::path_segment_to_string;
-use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
+use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::{
     pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
     MultiSpan,
@@ -20,7 +19,7 @@ use rustc_errors::{
 use rustc_hir as hir;
 use rustc_hir::def::Namespace::{self, *};
 use rustc_hir::def::{self, CtorKind, CtorOf, DefKind};
-use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID, LOCAL_CRATE};
+use rustc_hir::def_id::{DefId, CRATE_DEF_ID, LOCAL_CRATE};
 use rustc_hir::PrimTy;
 use rustc_session::lint;
 use rustc_session::parse::feature_err;
@@ -29,7 +28,7 @@ use rustc_span::edition::Edition;
 use rustc_span::hygiene::MacroKind;
 use rustc_span::lev_distance::find_best_match_for_name;
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
-use rustc_span::{BytePos, Span, DUMMY_SP};
+use rustc_span::{BytePos, Span};
 
 use std::iter;
 use std::ops::Deref;
@@ -59,41 +58,6 @@ impl AssocSuggestion {
     }
 }
 
-pub(crate) enum MissingLifetimeSpot<'tcx> {
-    Generics(&'tcx hir::Generics<'tcx>),
-    HigherRanked { span: Span, span_type: ForLifetimeSpanType },
-    Static,
-}
-
-pub(crate) enum ForLifetimeSpanType {
-    BoundEmpty,
-    BoundTail,
-    TypeEmpty,
-    TypeTail,
-}
-
-impl ForLifetimeSpanType {
-    pub(crate) fn descr(&self) -> &'static str {
-        match self {
-            Self::BoundEmpty | Self::BoundTail => "bound",
-            Self::TypeEmpty | Self::TypeTail => "type",
-        }
-    }
-
-    pub(crate) fn suggestion(&self, sugg: &str) -> String {
-        match self {
-            Self::BoundEmpty | Self::TypeEmpty => format!("for<{}> ", sugg),
-            Self::BoundTail | Self::TypeTail => format!(", {}", sugg),
-        }
-    }
-}
-
-impl<'tcx> Into<MissingLifetimeSpot<'tcx>> for &&'tcx hir::Generics<'tcx> {
-    fn into(self) -> MissingLifetimeSpot<'tcx> {
-        MissingLifetimeSpot::Generics(self)
-    }
-}
-
 fn is_self_type(path: &[Segment], namespace: Namespace) -> bool {
     namespace == TypeNS && path.len() == 1 && path[0].ident.name == kw::SelfUpper
 }
@@ -118,6 +82,56 @@ fn import_candidate_to_enum_paths(suggestion: &ImportSuggestion) -> (String, Str
     (variant_path_string, enum_path_string)
 }
 
+/// Description of an elided lifetime.
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
+pub(super) struct MissingLifetime {
+    /// Used to overwrite the resolution with the suggestion, to avoid cascasing errors.
+    pub id: NodeId,
+    /// Where to suggest adding the lifetime.
+    pub span: Span,
+    /// How the lifetime was introduced, to have the correct space and comma.
+    pub kind: MissingLifetimeKind,
+    /// Number of elided lifetimes, used for elision in path.
+    pub count: usize,
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
+pub(super) enum MissingLifetimeKind {
+    /// An explicit `'_`.
+    Underscore,
+    /// An elided lifetime `&' ty`.
+    Ampersand,
+    /// An elided lifetime in brackets with written brackets.
+    Comma,
+    /// An elided lifetime with elided brackets.
+    Brackets,
+}
+
+/// Description of the lifetimes appearing in a function parameter.
+/// This is used to provide a literal explanation to the elision failure.
+#[derive(Clone, Debug)]
+pub(super) struct ElisionFnParameter {
+    /// The index of the argument in the original definition.
+    pub index: usize,
+    /// The name of the argument if it's a simple ident.
+    pub ident: Option<Ident>,
+    /// The number of lifetimes in the parameter.
+    pub lifetime_count: usize,
+    /// The span of the parameter.
+    pub span: Span,
+}
+
+/// Description of lifetimes that appear as candidates for elision.
+/// This is used to suggest introducing an explicit lifetime.
+#[derive(Debug)]
+pub(super) enum LifetimeElisionCandidate {
+    /// This is not a real lifetime.
+    Ignore,
+    /// There is a named lifetime, we won't suggest anything.
+    Named,
+    Missing(MissingLifetime),
+}
+
 impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
     fn def_span(&self, def_id: DefId) -> Option<Span> {
         match def_id.krate {
@@ -1430,7 +1444,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
                     // Items from this module
                     self.r.add_module_candidates(module, &mut names, &filter_fn);
 
-                    if let ModuleKind::Block(..) = module.kind {
+                    if let ModuleKind::Block = module.kind {
                         // We can see through blocks
                     } else {
                         // Items from the prelude
@@ -1999,18 +2013,35 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
             err.span_label(lifetime_ref.ident.span, "undeclared lifetime");
             err
         };
-        let mut suggest_note = true;
+        self.suggest_introducing_lifetime(
+            &mut err,
+            Some(lifetime_ref.ident.name.as_str()),
+            |err, _, span, message, suggestion| {
+                err.span_suggestion(span, message, suggestion, Applicability::MaybeIncorrect);
+                true
+            },
+        );
+        err.emit();
+    }
 
+    fn suggest_introducing_lifetime(
+        &self,
+        err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>,
+        name: Option<&str>,
+        suggest: impl Fn(&mut DiagnosticBuilder<'_, ErrorGuaranteed>, bool, Span, &str, String) -> bool,
+    ) {
+        let mut suggest_note = true;
         for rib in self.lifetime_ribs.iter().rev() {
+            let mut should_continue = true;
             match rib.kind {
                 LifetimeRibKind::Generics { binder: _, span, kind } => {
-                    if !span.can_be_used_for_suggestions() && suggest_note {
+                    if !span.can_be_used_for_suggestions() && suggest_note && let Some(name) = name {
                         suggest_note = false; // Avoid displaying the same help multiple times.
                         err.span_label(
                             span,
                             &format!(
                                 "lifetime `{}` is missing in item created through this procedural macro",
-                                lifetime_ref.ident,
+                                name,
                             ),
                         );
                         continue;
@@ -2026,46 +2057,42 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
                         let sugg = format!(
                             "{}<{}>{}",
                             if higher_ranked { "for" } else { "" },
-                            lifetime_ref.ident,
+                            name.unwrap_or("'a"),
                             if higher_ranked { " " } else { "" },
                         );
                         (span, sugg)
                     } else {
                         let span =
                             self.r.session.source_map().span_through_char(span, '<').shrink_to_hi();
-                        let sugg = format!("{}, ", lifetime_ref.ident);
+                        let sugg = format!("{}, ", name.unwrap_or("'a"));
                         (span, sugg)
                     };
                     if higher_ranked {
-                        err.span_suggestion(
-                            span,
-                            &format!(
-                                "consider making the {} lifetime-generic with a new `{}` lifetime",
-                                kind.descr(),
-                                lifetime_ref
-                            ),
-                            sugg,
-                            Applicability::MaybeIncorrect,
+                        let message = format!(
+                            "consider making the {} lifetime-generic with a new `{}` lifetime",
+                            kind.descr(),
+                            name.unwrap_or("'a"),
                         );
+                        should_continue = suggest(err, true, span, &message, sugg);
                         err.note_once(
                             "for more information on higher-ranked polymorphism, visit \
                              https://doc.rust-lang.org/nomicon/hrtb.html",
                         );
+                    } else if let Some(name) = name {
+                        let message = format!("consider introducing lifetime `{}` here", name);
+                        should_continue = suggest(err, false, span, &message, sugg);
                     } else {
-                        err.span_suggestion(
-                            span,
-                            &format!("consider introducing lifetime `{}` here", lifetime_ref.ident),
-                            sugg,
-                            Applicability::MaybeIncorrect,
-                        );
+                        let message = format!("consider introducing a named lifetime parameter");
+                        should_continue = suggest(err, false, span, &message, sugg);
                     }
                 }
                 LifetimeRibKind::Item => break,
                 _ => {}
             }
+            if !should_continue {
+                break;
+            }
         }
-
-        err.emit();
     }
 
     pub(crate) fn emit_non_static_lt_in_const_generic_error(&self, lifetime_ref: &ast::Lifetime) {
@@ -2101,547 +2128,209 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
             .emit();
         }
     }
-}
 
-/// Report lifetime/lifetime shadowing as an error.
-pub fn signal_lifetime_shadowing(sess: &Session, orig: Ident, shadower: Ident) {
-    let mut err = struct_span_err!(
-        sess,
-        shadower.span,
-        E0496,
-        "lifetime name `{}` shadows a lifetime name that is already in scope",
-        orig.name,
-    );
-    err.span_label(orig.span, "first declared here");
-    err.span_label(shadower.span, format!("lifetime `{}` already in scope", orig.name));
-    err.emit();
-}
-
-/// Shadowing involving a label is only a warning for historical reasons.
-//FIXME: make this a proper lint.
-pub fn signal_label_shadowing(sess: &Session, orig: Span, shadower: Ident) {
-    let name = shadower.name;
-    let shadower = shadower.span;
-    let mut err = sess.struct_span_warn(
-        shadower,
-        &format!("label name `{}` shadows a label name that is already in scope", name),
-    );
-    err.span_label(orig, "first declared here");
-    err.span_label(shadower, format!("label `{}` already in scope", name));
-    err.emit();
-}
-
-impl<'tcx> LifetimeContext<'_, 'tcx> {
     pub(crate) fn report_missing_lifetime_specifiers(
-        &self,
-        spans: Vec<Span>,
-        count: usize,
-    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
-        struct_span_err!(
-            self.tcx.sess,
+        &mut self,
+        lifetime_refs: Vec<MissingLifetime>,
+        function_param_lifetimes: Option<(Vec<MissingLifetime>, Vec<ElisionFnParameter>)>,
+    ) -> ErrorGuaranteed {
+        let num_lifetimes: usize = lifetime_refs.iter().map(|lt| lt.count).sum();
+        let spans: Vec<_> = lifetime_refs.iter().map(|lt| lt.span).collect();
+
+        let mut err = struct_span_err!(
+            self.r.session,
             spans,
             E0106,
             "missing lifetime specifier{}",
-            pluralize!(count)
-        )
+            pluralize!(num_lifetimes)
+        );
+        self.add_missing_lifetime_specifiers_label(
+            &mut err,
+            lifetime_refs,
+            function_param_lifetimes,
+        );
+        err.emit()
     }
 
-    /// Returns whether to add `'static` lifetime to the suggested lifetime list.
-    pub(crate) fn report_elision_failure(
-        &self,
-        diag: &mut Diagnostic,
-        params: &[ElisionFailureInfo],
-    ) -> bool {
-        let mut m = String::new();
-        let len = params.len();
+    pub(crate) fn add_missing_lifetime_specifiers_label(
+        &mut self,
+        err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>,
+        lifetime_refs: Vec<MissingLifetime>,
+        function_param_lifetimes: Option<(Vec<MissingLifetime>, Vec<ElisionFnParameter>)>,
+    ) {
+        for &lt in &lifetime_refs {
+            err.span_label(
+                lt.span,
+                format!(
+                    "expected {} lifetime parameter{}",
+                    if lt.count == 1 { "named".to_string() } else { lt.count.to_string() },
+                    pluralize!(lt.count),
+                ),
+            );
+        }
 
-        let elided_params: Vec<_> =
-            params.iter().cloned().filter(|info| info.lifetime_count > 0).collect();
+        let mut in_scope_lifetimes: Vec<_> = self
+            .lifetime_ribs
+            .iter()
+            .rev()
+            .take_while(|rib| !matches!(rib.kind, LifetimeRibKind::Item))
+            .flat_map(|rib| rib.bindings.iter())
+            .map(|(&ident, &res)| (ident, res))
+            .filter(|(ident, _)| ident.name != kw::UnderscoreLifetime)
+            .collect();
+        debug!(?in_scope_lifetimes);
 
-        let elided_len = elided_params.len();
+        debug!(?function_param_lifetimes);
+        if let Some((param_lifetimes, params)) = &function_param_lifetimes {
+            let elided_len = param_lifetimes.len();
+            let num_params = params.len();
 
-        for (i, info) in elided_params.into_iter().enumerate() {
-            let ElisionFailureInfo { parent, index, lifetime_count: n, have_bound_regions, span } =
-                info;
+            let mut m = String::new();
 
-            diag.span_label(span, "");
-            let help_name = if let Some(ident) =
-                parent.and_then(|body| self.tcx.hir().body(body).params[index].pat.simple_ident())
-            {
-                format!("`{}`", ident)
-            } else {
-                format!("argument {}", index + 1)
-            };
+            for (i, info) in params.iter().enumerate() {
+                let ElisionFnParameter { ident, index, lifetime_count, span } = *info;
+                debug_assert_ne!(lifetime_count, 0);
 
-            m.push_str(
-                &(if n == 1 {
-                    help_name
+                err.span_label(span, "");
+
+                if i != 0 {
+                    if i + 1 < num_params {
+                        m.push_str(", ");
+                    } else if num_params == 2 {
+                        m.push_str(" or ");
+                    } else {
+                        m.push_str(", or ");
+                    }
+                }
+
+                let help_name = if let Some(ident) = ident {
+                    format!("`{}`", ident)
                 } else {
-                    format!(
-                        "one of {}'s {} {}lifetimes",
-                        help_name,
-                        n,
-                        if have_bound_regions { "free " } else { "" }
-                    )
-                })[..],
-            );
+                    format!("argument {}", index + 1)
+                };
 
-            if elided_len == 2 && i == 0 {
-                m.push_str(" or ");
-            } else if i + 2 == elided_len {
-                m.push_str(", or ");
-            } else if i != elided_len - 1 {
-                m.push_str(", ");
+                if lifetime_count == 1 {
+                    m.push_str(&help_name[..])
+                } else {
+                    m.push_str(&format!("one of {}'s {} lifetimes", help_name, lifetime_count)[..])
+                }
             }
-        }
 
-        if len == 0 {
-            diag.help(
-                "this function's return type contains a borrowed value, \
+            if num_params == 0 {
+                err.help(
+                    "this function's return type contains a borrowed value, \
                  but there is no value for it to be borrowed from",
-            );
-            true
-        } else if elided_len == 0 {
-            diag.help(
-                "this function's return type contains a borrowed value with \
+                );
+                if in_scope_lifetimes.is_empty() {
+                    in_scope_lifetimes = vec![(
+                        Ident::with_dummy_span(kw::StaticLifetime),
+                        (DUMMY_NODE_ID, LifetimeRes::Static),
+                    )];
+                }
+            } else if elided_len == 0 {
+                err.help(
+                    "this function's return type contains a borrowed value with \
                  an elided lifetime, but the lifetime cannot be derived from \
                  the arguments",
-            );
-            true
-        } else if elided_len == 1 {
-            diag.help(&format!(
-                "this function's return type contains a borrowed value, \
+                );
+                if in_scope_lifetimes.is_empty() {
+                    in_scope_lifetimes = vec![(
+                        Ident::with_dummy_span(kw::StaticLifetime),
+                        (DUMMY_NODE_ID, LifetimeRes::Static),
+                    )];
+                }
+            } else if num_params == 1 {
+                err.help(&format!(
+                    "this function's return type contains a borrowed value, \
                  but the signature does not say which {} it is borrowed from",
-                m
-            ));
-            false
-        } else {
-            diag.help(&format!(
-                "this function's return type contains a borrowed value, \
+                    m
+                ));
+            } else {
+                err.help(&format!(
+                    "this function's return type contains a borrowed value, \
                  but the signature does not say whether it is borrowed from {}",
-                m
-            ));
-            false
+                    m
+                ));
+            }
         }
-    }
 
-    pub(crate) fn is_trait_ref_fn_scope(
-        &mut self,
-        trait_ref: &'tcx hir::PolyTraitRef<'tcx>,
-    ) -> bool {
-        if let def::Res::Def(_, did) = trait_ref.trait_ref.path.res {
-            if [
-                self.tcx.lang_items().fn_once_trait(),
-                self.tcx.lang_items().fn_trait(),
-                self.tcx.lang_items().fn_mut_trait(),
-            ]
-            .contains(&Some(did))
-            {
-                let (span, span_type) = if let Some(bound) =
-                    trait_ref.bound_generic_params.iter().rfind(|param| {
-                        matches!(
-                            param.kind,
-                            hir::GenericParamKind::Lifetime {
-                                kind: hir::LifetimeParamKind::Explicit
-                            }
-                        )
-                    }) {
-                    (bound.span.shrink_to_hi(), ForLifetimeSpanType::BoundTail)
-                } else {
-                    (trait_ref.span.shrink_to_lo(), ForLifetimeSpanType::BoundEmpty)
-                };
-                self.missing_named_lifetime_spots
-                    .push(MissingLifetimeSpot::HigherRanked { span, span_type });
-                return true;
-            }
+        let existing_name = match &in_scope_lifetimes[..] {
+            [] => Symbol::intern("'a"),
+            [(existing, _)] => existing.name,
+            _ => Symbol::intern("'lifetime"),
         };
-        false
-    }
-
-    pub(crate) fn add_missing_lifetime_specifiers_label(
-        &self,
-        err: &mut Diagnostic,
-        mut spans_with_counts: Vec<(Span, usize)>,
-        in_scope_lifetimes: FxIndexSet<LocalDefId>,
-        params: Option<&[ElisionFailureInfo]>,
-    ) {
-        let (mut lifetime_names, lifetime_spans): (FxHashSet<_>, Vec<_>) = in_scope_lifetimes
-            .iter()
-            .filter_map(|def_id| {
-                let name = self.tcx.item_name(def_id.to_def_id());
-                let span = self.tcx.def_ident_span(def_id.to_def_id())?;
-                Some((name, span))
-            })
-            .filter(|&(n, _)| n != kw::UnderscoreLifetime)
-            .unzip();
 
-        if let Some(params) = params {
-            // If there's no lifetime available, suggest `'static`.
-            if self.report_elision_failure(err, params) && lifetime_names.is_empty() {
-                lifetime_names.insert(kw::StaticLifetime);
+        let mut spans_suggs: Vec<_> = Vec::new();
+        let build_sugg = |lt: MissingLifetime| match lt.kind {
+            MissingLifetimeKind::Underscore => {
+                debug_assert_eq!(lt.count, 1);
+                (lt.span, existing_name.to_string())
             }
-        }
-        let params = params.unwrap_or(&[]);
-
-        let snippets: Vec<Option<String>> = spans_with_counts
-            .iter()
-            .map(|(span, _)| self.tcx.sess.source_map().span_to_snippet(*span).ok())
-            .collect();
-
-        // Empty generics are marked with a span of "<", but since from now on
-        // that information is in the snippets it can be removed from the spans.
-        for ((span, _), snippet) in spans_with_counts.iter_mut().zip(&snippets) {
-            if snippet.as_deref() == Some("<") {
-                *span = span.shrink_to_hi();
+            MissingLifetimeKind::Ampersand => {
+                debug_assert_eq!(lt.count, 1);
+                (lt.span.shrink_to_hi(), format!("{} ", existing_name))
             }
+            MissingLifetimeKind::Comma => {
+                let sugg: String = std::iter::repeat([existing_name.as_str(), ", "])
+                    .take(lt.count)
+                    .flatten()
+                    .collect();
+                (lt.span.shrink_to_hi(), sugg)
+            }
+            MissingLifetimeKind::Brackets => {
+                let sugg: String = std::iter::once("<")
+                    .chain(
+                        std::iter::repeat(existing_name.as_str()).take(lt.count).intersperse(", "),
+                    )
+                    .chain([">"])
+                    .collect();
+                (lt.span.shrink_to_hi(), sugg)
+            }
+        };
+        for &lt in &lifetime_refs {
+            spans_suggs.push(build_sugg(lt));
         }
-
-        for &(span, count) in &spans_with_counts {
-            err.span_label(
-                span,
-                format!(
-                    "expected {} lifetime parameter{}",
-                    if count == 1 { "named".to_string() } else { count.to_string() },
-                    pluralize!(count),
-                ),
-            );
-        }
-
-        let suggest_existing =
-            |err: &mut Diagnostic,
-             name: &str,
-             formatters: Vec<Option<Box<dyn Fn(&str) -> String>>>| {
-                if let Some(MissingLifetimeSpot::HigherRanked { span: for_span, span_type }) =
-                    self.missing_named_lifetime_spots.iter().rev().next()
-                {
-                    // When we have `struct S<'a>(&'a dyn Fn(&X) -> &X);` we want to not only suggest
-                    // using `'a`, but also introduce the concept of HRLTs by suggesting
-                    // `struct S<'a>(&'a dyn for<'b> Fn(&X) -> &'b X);`. (#72404)
-                    let mut introduce_suggestion = vec![];
-
-                    let a_to_z_repeat_n = |n| {
-                        (b'a'..=b'z').map(move |c| {
-                            let mut s = '\''.to_string();
-                            s.extend(std::iter::repeat(char::from(c)).take(n));
-                            s
-                        })
-                    };
-
-                    // If all single char lifetime names are present, we wrap around and double the chars.
-                    let lt_name = (1..)
-                        .flat_map(a_to_z_repeat_n)
-                        .find(|lt| !lifetime_names.contains(&Symbol::intern(&lt)))
-                        .unwrap();
-                    let msg = format!(
-                        "consider making the {} lifetime-generic with a new `{}` lifetime",
-                        span_type.descr(),
-                        lt_name,
-                    );
-                    err.note(
-                        "for more information on higher-ranked polymorphism, visit \
-                    https://doc.rust-lang.org/nomicon/hrtb.html",
-                    );
-                    let for_sugg = span_type.suggestion(&lt_name);
-                    for param in params {
-                        if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(param.span)
-                        {
-                            if snippet.starts_with('&') && !snippet.starts_with("&'") {
-                                introduce_suggestion
-                                    .push((param.span, format!("&{} {}", lt_name, &snippet[1..])));
-                            } else if let Some(stripped) = snippet.strip_prefix("&'_ ") {
-                                introduce_suggestion
-                                    .push((param.span, format!("&{} {}", lt_name, stripped)));
-                            }
-                        }
+        debug!(?spans_suggs);
+        match in_scope_lifetimes.len() {
+            0 => {
+                if let Some((param_lifetimes, _)) = function_param_lifetimes {
+                    for lt in param_lifetimes {
+                        spans_suggs.push(build_sugg(lt))
                     }
-                    introduce_suggestion.push((*for_span, for_sugg));
-                    for ((span, _), formatter) in spans_with_counts.iter().zip(formatters.iter()) {
-                        if let Some(formatter) = formatter {
-                            introduce_suggestion.push((*span, formatter(&lt_name)));
-                        }
-                    }
-                    err.multipart_suggestion_verbose(
-                        &msg,
-                        introduce_suggestion,
-                        Applicability::MaybeIncorrect,
-                    );
                 }
-
-                let spans_suggs: Vec<_> = formatters
-                    .into_iter()
-                    .zip(spans_with_counts.iter())
-                    .filter_map(|(formatter, (span, _))| {
-                        if let Some(formatter) = formatter {
-                            Some((*span, formatter(name)))
-                        } else {
-                            None
-                        }
-                    })
-                    .collect();
-                if spans_suggs.is_empty() {
-                    // If all the spans come from macros, we cannot extract snippets and then
-                    // `formatters` only contains None and `spans_suggs` is empty.
-                    return;
-                }
-                err.multipart_suggestion_verbose(
-                    &format!(
-                        "consider using the `{}` lifetime",
-                        lifetime_names.iter().next().unwrap()
-                    ),
-                    spans_suggs,
-                    Applicability::MaybeIncorrect,
-                );
-            };
-        let suggest_new = |err: &mut Diagnostic, suggs: Vec<Option<String>>| {
-            for missing in self.missing_named_lifetime_spots.iter().rev() {
-                let mut introduce_suggestion = vec![];
-                let msg;
-                let should_break;
-                introduce_suggestion.push(match missing {
-                    MissingLifetimeSpot::Generics(generics) => {
-                        if generics.span == DUMMY_SP {
-                            // Account for malformed generics in the HIR. This shouldn't happen,
-                            // but if we make a mistake elsewhere, mainly by keeping something in
-                            // `missing_named_lifetime_spots` that we shouldn't, like associated
-                            // `const`s or making a mistake in the AST lowering we would provide
-                            // nonsensical suggestions. Guard against that by skipping these.
-                            // (#74264)
-                            continue;
-                        }
-                        msg = "consider introducing a named lifetime parameter".to_string();
-                        should_break = true;
-                        if let Some(param) = generics.params.iter().find(|p| {
-                            !matches!(
-                                p.kind,
-                                hir::GenericParamKind::Type { synthetic: true, .. }
-                                    | hir::GenericParamKind::Lifetime {
-                                        kind: hir::LifetimeParamKind::Elided
-                                    }
-                            )
-                        }) {
-                            (param.span.shrink_to_lo(), "'a, ".to_string())
-                        } else {
-                            (generics.span, "<'a>".to_string())
-                        }
-                    }
-                    MissingLifetimeSpot::HigherRanked { span, span_type } => {
-                        msg = format!(
-                            "consider making the {} lifetime-generic with a new `'a` lifetime",
-                            span_type.descr(),
-                        );
-                        should_break = false;
-                        err.note(
-                            "for more information on higher-ranked polymorphism, visit \
-                            https://doc.rust-lang.org/nomicon/hrtb.html",
-                        );
-                        (*span, span_type.suggestion("'a"))
-                    }
-                    MissingLifetimeSpot::Static => {
-                        let mut spans_suggs = Vec::new();
-                        for ((span, count), snippet) in
-                            spans_with_counts.iter().copied().zip(snippets.iter())
-                        {
-                            let (span, sugg) = match snippet.as_deref() {
-                                Some("&") => (span.shrink_to_hi(), "'static ".to_owned()),
-                                Some("'_") => (span, "'static".to_owned()),
-                                Some(snippet) if !snippet.ends_with('>') => {
-                                    if snippet == "" {
-                                        (
-                                            span,
-                                            std::iter::repeat("'static")
-                                                .take(count)
-                                                .collect::<Vec<_>>()
-                                                .join(", "),
-                                        )
-                                    } else if snippet == "<" || snippet == "(" {
-                                        (
-                                            span.shrink_to_hi(),
-                                            std::iter::repeat("'static")
-                                                .take(count)
-                                                .collect::<Vec<_>>()
-                                                .join(", "),
-                                        )
-                                    } else {
-                                        (
-                                            span.shrink_to_hi(),
-                                            format!(
-                                                "<{}>",
-                                                std::iter::repeat("'static")
-                                                    .take(count)
-                                                    .collect::<Vec<_>>()
-                                                    .join(", "),
-                                            ),
-                                        )
-                                    }
-                                }
-                                _ => continue,
-                            };
-                            spans_suggs.push((span, sugg.to_string()));
-                        }
+                self.suggest_introducing_lifetime(
+                    err,
+                    None,
+                    |err, higher_ranked, span, message, intro_sugg| {
                         err.multipart_suggestion_verbose(
-                            "consider using the `'static` lifetime",
-                            spans_suggs,
+                            message,
+                            std::iter::once((span, intro_sugg))
+                                .chain(spans_suggs.clone())
+                                .collect(),
                             Applicability::MaybeIncorrect,
                         );
-                        continue;
-                    }
-                });
-
-                struct Lifetime(Span, String);
-                impl Lifetime {
-                    fn is_unnamed(&self) -> bool {
-                        self.1.starts_with('&') && !self.1.starts_with("&'")
-                    }
-                    fn is_underscore(&self) -> bool {
-                        self.1.starts_with("&'_ ")
-                    }
-                    fn is_named(&self) -> bool {
-                        self.1.starts_with("&'")
-                    }
-                    fn suggestion(&self, sugg: String) -> Option<(Span, String)> {
-                        Some(
-                            match (
-                                self.is_unnamed(),
-                                self.is_underscore(),
-                                self.is_named(),
-                                sugg.starts_with('&'),
-                            ) {
-                                (true, _, _, false) => (self.span_unnamed_borrow(), sugg),
-                                (true, _, _, true) => {
-                                    (self.span_unnamed_borrow(), sugg[1..].to_string())
-                                }
-                                (_, true, _, false) => {
-                                    (self.span_underscore_borrow(), sugg.trim().to_string())
-                                }
-                                (_, true, _, true) => {
-                                    (self.span_underscore_borrow(), sugg[1..].trim().to_string())
-                                }
-                                (_, _, true, false) => {
-                                    (self.span_named_borrow(), sugg.trim().to_string())
-                                }
-                                (_, _, true, true) => {
-                                    (self.span_named_borrow(), sugg[1..].trim().to_string())
-                                }
-                                _ => return None,
-                            },
-                        )
-                    }
-                    fn span_unnamed_borrow(&self) -> Span {
-                        let lo = self.0.lo() + BytePos(1);
-                        self.0.with_lo(lo).with_hi(lo)
-                    }
-                    fn span_named_borrow(&self) -> Span {
-                        let lo = self.0.lo() + BytePos(1);
-                        self.0.with_lo(lo)
-                    }
-                    fn span_underscore_borrow(&self) -> Span {
-                        let lo = self.0.lo() + BytePos(1);
-                        let hi = lo + BytePos(2);
-                        self.0.with_lo(lo).with_hi(hi)
-                    }
-                }
-
-                for param in params {
-                    if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(param.span) {
-                        if let Some((span, sugg)) =
-                            Lifetime(param.span, snippet).suggestion("'a ".to_string())
-                        {
-                            introduce_suggestion.push((span, sugg));
-                        }
-                    }
-                }
-                for (span, sugg) in spans_with_counts.iter().copied().zip(suggs.iter()).filter_map(
-                    |((span, _), sugg)| match &sugg {
-                        Some(sugg) => Some((span, sugg.to_string())),
-                        _ => None,
+                        higher_ranked
                     },
-                ) {
-                    let (span, sugg) = self
-                        .tcx
-                        .sess
-                        .source_map()
-                        .span_to_snippet(span)
-                        .ok()
-                        .and_then(|snippet| Lifetime(span, snippet).suggestion(sugg.clone()))
-                        .unwrap_or((span, sugg));
-                    introduce_suggestion.push((span, sugg.to_string()));
-                }
+                );
+            }
+            1 => {
                 err.multipart_suggestion_verbose(
-                    &msg,
-                    introduce_suggestion,
+                    &format!("consider using the `{}` lifetime", existing_name),
+                    spans_suggs,
                     Applicability::MaybeIncorrect,
                 );
-                if should_break {
-                    break;
-                }
-            }
-        };
 
-        let lifetime_names: Vec<_> = lifetime_names.iter().collect();
-        match &lifetime_names[..] {
-            [name] => {
-                let mut suggs: Vec<Option<Box<dyn Fn(&str) -> String>>> = Vec::new();
-                for (snippet, (_, count)) in snippets.iter().zip(spans_with_counts.iter().copied())
-                {
-                    suggs.push(match snippet.as_deref() {
-                        Some("&") => Some(Box::new(|name| format!("&{} ", name))),
-                        Some("'_") => Some(Box::new(|n| n.to_string())),
-                        Some("") => Some(Box::new(move |n| format!("{}, ", n).repeat(count))),
-                        Some("<") => Some(Box::new(move |n| {
-                            std::iter::repeat(n).take(count).collect::<Vec<_>>().join(", ")
-                        })),
-                        Some(snippet) if !snippet.ends_with('>') => Some(Box::new(move |name| {
-                            format!(
-                                "{}<{}>",
-                                snippet,
-                                std::iter::repeat(name.to_string())
-                                    .take(count)
-                                    .collect::<Vec<_>>()
-                                    .join(", ")
-                            )
-                        })),
-                        _ => None,
-                    });
+                // Record as using the suggested resolution.
+                let (_, (_, res)) = in_scope_lifetimes[0];
+                for &lt in &lifetime_refs {
+                    self.r.lifetimes_res_map.insert(lt.id, res);
                 }
-                suggest_existing(err, name.as_str(), suggs);
             }
-            [] => {
-                let mut suggs = Vec::new();
-                for (snippet, (_, count)) in
-                    snippets.iter().cloned().zip(spans_with_counts.iter().copied())
-                {
-                    suggs.push(match snippet.as_deref() {
-                        Some("&") => Some("&'a ".to_string()),
-                        Some("'_") => Some("'a".to_string()),
-                        Some("") => {
-                            Some(std::iter::repeat("'a, ").take(count).collect::<Vec<_>>().join(""))
-                        }
-                        Some("<") => {
-                            Some(std::iter::repeat("'a").take(count).collect::<Vec<_>>().join(", "))
-                        }
-                        Some(snippet) => Some(format!(
-                            "{}<{}>",
-                            snippet,
-                            std::iter::repeat("'a").take(count).collect::<Vec<_>>().join(", "),
-                        )),
-                        None => None,
-                    });
-                }
-                suggest_new(err, suggs);
-            }
-            lts if lts.len() > 1 => {
+            _ => {
+                let lifetime_spans: Vec<_> =
+                    in_scope_lifetimes.iter().map(|(ident, _)| ident.span).collect();
                 err.span_note(lifetime_spans, "these named lifetimes are available to use");
 
-                let mut spans_suggs: Vec<_> = Vec::new();
-                for ((span, _), snippet) in spans_with_counts.iter().copied().zip(snippets.iter()) {
-                    match snippet.as_deref() {
-                        Some("") => spans_suggs.push((span, "'lifetime, ".to_string())),
-                        Some("&") => spans_suggs
-                            .push((span.with_lo(span.lo() + BytePos(1)), "'lifetime ".to_string())),
-                        _ => {}
-                    }
-                }
-
                 if spans_suggs.len() > 0 {
                     // This happens when we have `Foo<T>` where we point at the space before `T`,
                     // but this can be confusing so we give a suggestion with placeholders.
@@ -2652,7 +2341,34 @@ impl<'tcx> LifetimeContext<'_, 'tcx> {
                     );
                 }
             }
-            _ => unreachable!(),
         }
     }
 }
+
+/// Report lifetime/lifetime shadowing as an error.
+pub fn signal_lifetime_shadowing(sess: &Session, orig: Ident, shadower: Ident) {
+    let mut err = struct_span_err!(
+        sess,
+        shadower.span,
+        E0496,
+        "lifetime name `{}` shadows a lifetime name that is already in scope",
+        orig.name,
+    );
+    err.span_label(orig.span, "first declared here");
+    err.span_label(shadower.span, format!("lifetime `{}` already in scope", orig.name));
+    err.emit();
+}
+
+/// Shadowing involving a label is only a warning for historical reasons.
+//FIXME: make this a proper lint.
+pub fn signal_label_shadowing(sess: &Session, orig: Span, shadower: Ident) {
+    let name = shadower.name;
+    let shadower = shadower.span;
+    let mut err = sess.struct_span_warn(
+        shadower,
+        &format!("label name `{}` shadows a label name that is already in scope", name),
+    );
+    err.span_label(orig, "first declared here");
+    err.span_label(shadower, format!("label `{}` already in scope", name));
+    err.emit();
+}
diff --git a/compiler/rustc_resolve/src/late/lifetimes.rs b/compiler/rustc_resolve/src/late/lifetimes.rs
index 557dbecfabe..94460e33d8b 100644
--- a/compiler/rustc_resolve/src/late/lifetimes.rs
+++ b/compiler/rustc_resolve/src/late/lifetimes.rs
@@ -1,12 +1,11 @@
-// ignore-tidy-filelength
-//! Name resolution for lifetimes.
+//! Resolution of early vs late bound lifetimes.
 //!
-//! Name resolution for lifetimes follows *much* simpler rules than the
-//! full resolve. For example, lifetime names are never exported or
-//! used between functions, and they operate in a purely top-down
-//! way. Therefore, we break lifetime name resolution into a separate pass.
+//! Name resolution for lifetimes is performed on the AST and embedded into HIR.  From this
+//! information, typechecking needs to transform the lifetime parameters into bound lifetimes.
+//! Lifetimes can be early-bound or late-bound.  Construction of typechecking terms needs to visit
+//! the types in HIR to identify late-bound lifetimes and assign their Debruijn indices.  This file
+//! is also responsible for assigning their semantics to implicit lifetimes in trait objects.
 
-use crate::late::diagnostics::{ForLifetimeSpanType, MissingLifetimeSpot};
 use rustc_ast::walk_list;
 use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
 use rustc_errors::struct_span_err;
@@ -14,18 +13,16 @@ use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{DefIdMap, LocalDefId};
 use rustc_hir::intravisit::{self, Visitor};
-use rustc_hir::{GenericArg, GenericParam, LifetimeName, Node};
-use rustc_hir::{GenericParamKind, HirIdMap};
+use rustc_hir::{GenericArg, GenericParam, GenericParamKind, HirIdMap, LifetimeName, Node};
+use rustc_middle::bug;
 use rustc_middle::hir::map::Map;
 use rustc_middle::hir::nested_filter;
 use rustc_middle::middle::resolve_lifetime::*;
 use rustc_middle::ty::{self, GenericParamDefKind, TyCtxt};
-use rustc_middle::{bug, span_bug};
 use rustc_span::def_id::DefId;
-use rustc_span::symbol::{kw, sym, Ident};
+use rustc_span::symbol::{sym, Ident};
 use rustc_span::Span;
 use std::borrow::Cow;
-use std::cell::Cell;
 use std::fmt;
 use std::mem::take;
 
@@ -34,8 +31,6 @@ trait RegionExt {
 
     fn late(index: u32, hir_map: Map<'_>, param: &GenericParam<'_>) -> (LocalDefId, Region);
 
-    fn late_anon(named_late_bound_vars: u32, index: &Cell<u32>) -> Region;
-
     fn id(&self) -> Option<DefId>;
 
     fn shifted(self, amount: u32) -> Region;
@@ -66,16 +61,9 @@ impl RegionExt for Region {
         (def_id, Region::LateBound(depth, idx, def_id.to_def_id()))
     }
 
-    fn late_anon(named_late_bound_vars: u32, index: &Cell<u32>) -> Region {
-        let i = index.get();
-        index.set(i + 1);
-        let depth = ty::INNERMOST;
-        Region::LateBoundAnon(depth, named_late_bound_vars + i, i)
-    }
-
     fn id(&self) -> Option<DefId> {
         match *self {
-            Region::Static | Region::LateBoundAnon(..) => None,
+            Region::Static => None,
 
             Region::EarlyBound(_, id) | Region::LateBound(_, _, id) | Region::Free(_, id) => {
                 Some(id)
@@ -88,9 +76,6 @@ impl RegionExt for Region {
             Region::LateBound(debruijn, idx, id) => {
                 Region::LateBound(debruijn.shifted_in(amount), idx, id)
             }
-            Region::LateBoundAnon(debruijn, index, anon_index) => {
-                Region::LateBoundAnon(debruijn.shifted_in(amount), index, anon_index)
-            }
             _ => self,
         }
     }
@@ -100,9 +85,6 @@ impl RegionExt for Region {
             Region::LateBound(debruijn, index, id) => {
                 Region::LateBound(debruijn.shifted_out_to_binder(binder), index, id)
             }
-            Region::LateBoundAnon(debruijn, index, anon_index) => {
-                Region::LateBoundAnon(debruijn.shifted_out_to_binder(binder), index, anon_index)
-            }
             _ => self,
         }
     }
@@ -152,10 +134,6 @@ pub(crate) struct LifetimeContext<'a, 'tcx> {
 
     /// Cache for cross-crate per-definition object lifetime defaults.
     xcrate_object_lifetime_defaults: DefIdMap<Vec<ObjectLifetimeDefault>>,
-
-    /// When encountering an undefined named lifetime, we will suggest introducing it in these
-    /// places.
-    pub(crate) missing_named_lifetime_spots: Vec<MissingLifetimeSpot<'tcx>>,
 }
 
 #[derive(Debug)]
@@ -198,10 +176,6 @@ enum Scope<'a> {
 
         s: ScopeRef<'a>,
 
-        /// In some cases not allowing late bounds allows us to avoid ICEs.
-        /// This is almost ways set to true.
-        allow_late_bound: bool,
-
         /// If this binder comes from a where clause, specify how it was created.
         /// This is used to diagnose inaccessible lifetimes in APIT:
         /// ```ignore (illustrative)
@@ -220,9 +194,8 @@ enum Scope<'a> {
     },
 
     /// A scope which either determines unspecified lifetimes or errors
-    /// on them (e.g., due to ambiguity). For more details, see `Elide`.
+    /// on them (e.g., due to ambiguity).
     Elision {
-        elide: Elide,
         s: ScopeRef<'a>,
     },
 
@@ -278,7 +251,6 @@ impl<'a> fmt::Debug for TruncatedScopeDebug<'a> {
                 opaque_type_parent,
                 scope_type,
                 hir_id,
-                allow_late_bound,
                 where_bound_origin,
                 s: _,
             } => f
@@ -288,16 +260,13 @@ impl<'a> fmt::Debug for TruncatedScopeDebug<'a> {
                 .field("opaque_type_parent", opaque_type_parent)
                 .field("scope_type", scope_type)
                 .field("hir_id", hir_id)
-                .field("allow_late_bound", allow_late_bound)
                 .field("where_bound_origin", where_bound_origin)
                 .field("s", &"..")
                 .finish(),
             Scope::Body { id, s: _ } => {
                 f.debug_struct("Body").field("id", id).field("s", &"..").finish()
             }
-            Scope::Elision { elide, s: _ } => {
-                f.debug_struct("Elision").field("elide", elide).field("s", &"..").finish()
-            }
+            Scope::Elision { s: _ } => f.debug_struct("Elision").field("s", &"..").finish(),
             Scope::ObjectLifetimeDefault { lifetime, s: _ } => f
                 .debug_struct("ObjectLifetimeDefault")
                 .field("lifetime", lifetime)
@@ -314,32 +283,6 @@ impl<'a> fmt::Debug for TruncatedScopeDebug<'a> {
     }
 }
 
-#[derive(Clone, Debug)]
-enum Elide {
-    /// Use a fresh anonymous late-bound lifetime each time, by
-    /// incrementing the counter to generate sequential indices. All
-    /// anonymous lifetimes must start *after* named bound vars.
-    FreshLateAnon(u32, Cell<u32>),
-    /// Always use this one lifetime.
-    Exact(Region),
-    /// Less or more than one lifetime were found, error on unspecified.
-    Error(Vec<ElisionFailureInfo>),
-    /// Forbid lifetime elision inside of a larger scope where it would be
-    /// permitted. For example, in let position impl trait.
-    Forbid,
-}
-
-#[derive(Clone, Debug)]
-pub(crate) struct ElisionFailureInfo {
-    /// Where we can find the argument pattern.
-    pub(crate) parent: Option<hir::BodyId>,
-    /// The index of the argument in the original definition.
-    pub(crate) index: usize,
-    pub(crate) lifetime_count: usize,
-    pub(crate) have_bound_regions: bool,
-    pub(crate) span: Span,
-}
-
 type ScopeRef<'a> = &'a Scope<'a>;
 
 const ROOT_SCOPE: ScopeRef<'static> = &Scope::Root;
@@ -421,7 +364,6 @@ fn do_resolve(
         scope: ROOT_SCOPE,
         trait_definition_only,
         xcrate_object_lifetime_defaults: Default::default(),
-        missing_named_lifetime_spots: vec![],
     };
     visitor.visit_item(item);
 
@@ -503,9 +445,6 @@ fn late_region_as_bound_region<'tcx>(tcx: TyCtxt<'tcx>, region: &Region) -> ty::
             let name = tcx.hir().name(tcx.hir().local_def_id_to_hir_id(def_id.expect_local()));
             ty::BoundVariableKind::Region(ty::BrNamed(*def_id, name))
         }
-        Region::LateBoundAnon(_, _, anon_idx) => {
-            ty::BoundVariableKind::Region(ty::BrAnon(*anon_idx))
-        }
         _ => bug!("{:?} is not a late region", region),
     }
 }
@@ -571,7 +510,54 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
     }
 
     fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) {
-        if let hir::ExprKind::Closure { bound_generic_params, .. } = e.kind {
+        if let hir::ExprKind::Closure(hir::Closure {
+            binder, bound_generic_params, fn_decl, ..
+        }) = e.kind
+        {
+            if let &hir::ClosureBinder::For { span: for_sp, .. } = binder {
+                fn span_of_infer(ty: &hir::Ty<'_>) -> Option<Span> {
+                    struct V(Option<Span>);
+
+                    impl<'v> Visitor<'v> for V {
+                        fn visit_ty(&mut self, t: &'v hir::Ty<'v>) {
+                            match t.kind {
+                                _ if self.0.is_some() => (),
+                                hir::TyKind::Infer => {
+                                    self.0 = Some(t.span);
+                                }
+                                _ => intravisit::walk_ty(self, t),
+                            }
+                        }
+                    }
+
+                    let mut v = V(None);
+                    v.visit_ty(ty);
+                    v.0
+                }
+
+                let infer_in_rt_sp = match fn_decl.output {
+                    hir::FnRetTy::DefaultReturn(sp) => Some(sp),
+                    hir::FnRetTy::Return(ty) => span_of_infer(ty),
+                };
+
+                let infer_spans = fn_decl
+                    .inputs
+                    .into_iter()
+                    .filter_map(span_of_infer)
+                    .chain(infer_in_rt_sp)
+                    .collect::<Vec<_>>();
+
+                if !infer_spans.is_empty() {
+                    self.tcx.sess
+                        .struct_span_err(
+                            infer_spans,
+                            "implicit types in closure signatures are forbidden when `for<...>` is present",
+                        )
+                        .span_label(for_sp, "`for<...>` is here")
+                        .emit();
+                }
+            }
+
             let next_early_index = self.next_early_index();
             let (lifetimes, binders): (FxIndexMap<LocalDefId, Region>, Vec<_>) =
                 bound_generic_params
@@ -584,6 +570,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                         (pair, r)
                     })
                     .unzip();
+
             self.map.late_bound_vars.insert(e.hir_id, binders);
             let scope = Scope::Binder {
                 hir_id: e.hir_id,
@@ -592,9 +579,9 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                 next_early_index,
                 opaque_type_parent: false,
                 scope_type: BinderScopeType::Normal,
-                allow_late_bound: true,
                 where_bound_origin: None,
             };
+
             self.with(scope, |this| {
                 // a closure has no bounds, so everything
                 // contained within is scoped within its binder.
@@ -616,11 +603,9 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
         }
         match item.kind {
             hir::ItemKind::Fn(_, ref generics, _) => {
-                self.missing_named_lifetime_spots.push(generics.into());
                 self.visit_early_late(None, item.hir_id(), generics, |this| {
                     intravisit::walk_item(this, item);
                 });
-                self.missing_named_lifetime_spots.pop();
             }
 
             hir::ItemKind::ExternCrate(_)
@@ -634,8 +619,9 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
             }
             hir::ItemKind::Static(..) | hir::ItemKind::Const(..) => {
                 // No lifetime parameters, but implied 'static.
-                let scope = Scope::Elision { elide: Elide::Exact(Region::Static), s: ROOT_SCOPE };
-                self.with(scope, |this| intravisit::walk_item(this, item));
+                self.with(Scope::Elision { s: self.scope }, |this| {
+                    intravisit::walk_item(this, item)
+                });
             }
             hir::ItemKind::OpaqueTy(hir::OpaqueTy { .. }) => {
                 // Opaque types are visited when we visit the
@@ -683,8 +669,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
             | hir::ItemKind::Trait(_, _, ref generics, ..)
             | hir::ItemKind::TraitAlias(ref generics, ..)
             | hir::ItemKind::Impl(hir::Impl { ref generics, .. }) => {
-                self.missing_named_lifetime_spots.push(generics.into());
-
                 // These kinds of items have only early-bound lifetime parameters.
                 let mut index = if sub_items_have_self_param(&item.kind) {
                     1 // Self comes before lifetimes
@@ -713,7 +697,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                     opaque_type_parent: true,
                     scope_type: BinderScopeType::Normal,
                     s: ROOT_SCOPE,
-                    allow_late_bound: false,
                     where_bound_origin: None,
                 };
                 self.with(scope, |this| {
@@ -722,7 +705,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                         intravisit::walk_item(this, item);
                     });
                 });
-                self.missing_named_lifetime_spots.pop();
             }
         }
     }
@@ -748,20 +730,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
         match ty.kind {
             hir::TyKind::BareFn(ref c) => {
                 let next_early_index = self.next_early_index();
-                let lifetime_span: Option<Span> =
-                    c.generic_params.iter().rev().find_map(|param| match param.kind {
-                        GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Explicit } => {
-                            Some(param.span)
-                        }
-                        _ => None,
-                    });
-                let (span, span_type) = if let Some(span) = lifetime_span {
-                    (span.shrink_to_hi(), ForLifetimeSpanType::TypeTail)
-                } else {
-                    (ty.span.shrink_to_lo(), ForLifetimeSpanType::TypeEmpty)
-                };
-                self.missing_named_lifetime_spots
-                    .push(MissingLifetimeSpot::HigherRanked { span, span_type });
                 let (lifetimes, binders): (FxIndexMap<LocalDefId, Region>, Vec<_>) = c
                     .generic_params
                     .iter()
@@ -781,7 +749,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                     next_early_index,
                     opaque_type_parent: false,
                     scope_type: BinderScopeType::Normal,
-                    allow_late_bound: true,
                     where_bound_origin: None,
                 };
                 self.with(scope, |this| {
@@ -789,7 +756,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                     // contained within is scoped within its binder.
                     intravisit::walk_ty(this, ty);
                 });
-                self.missing_named_lifetime_spots.pop();
             }
             hir::TyKind::TraitObject(bounds, ref lifetime, _) => {
                 debug!(?bounds, ?lifetime, "TraitObject");
@@ -800,11 +766,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                     }
                 });
                 match lifetime.name {
-                    LifetimeName::Implicit => {
-                        // For types like `dyn Foo`, we should
-                        // generate a special form of elided.
-                        span_bug!(ty.span, "object-lifetime-default expected, not implicit",);
-                    }
                     LifetimeName::ImplicitObjectLifetimeDefault => {
                         // If the user does not write *anything*, we
                         // use the object lifetime defaulting
@@ -812,13 +773,12 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                         // `Box<dyn Debug + 'static>`.
                         self.resolve_object_lifetime_default(lifetime)
                     }
-                    LifetimeName::Underscore => {
+                    LifetimeName::Infer => {
                         // If the user writes `'_`, we use the *ordinary* elision
                         // rules. So the `'_` in e.g., `Box<dyn Debug + '_>` will be
                         // resolved the same as the `'_` in `&'_ Foo`.
                         //
                         // cc #48468
-                        self.resolve_elided_lifetimes(&[lifetime])
                     }
                     LifetimeName::Param(..) | LifetimeName::Static => {
                         // If the user wrote an explicit name, use that.
@@ -853,7 +813,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                         // position impl Trait
                         let scope = Scope::TraitRefBoundary { s: self.scope };
                         self.with(scope, |this| {
-                            let scope = Scope::Elision { elide: Elide::Forbid, s: this.scope };
+                            let scope = Scope::Elision { s: this.scope };
                             this.with(scope, |this| {
                                 intravisit::walk_item(this, opaque_ty);
                             })
@@ -929,24 +889,14 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                 let mut index = self.next_early_index_for_opaque_type();
                 debug!(?index);
 
-                let mut elision = None;
                 let mut lifetimes = FxIndexMap::default();
                 let mut non_lifetime_count = 0;
+                debug!(?generics.params);
                 for param in generics.params {
                     match param.kind {
                         GenericParamKind::Lifetime { .. } => {
                             let (def_id, reg) = Region::early(self.tcx.hir(), &mut index, &param);
-                            if let hir::ParamName::Plain(Ident {
-                                name: kw::UnderscoreLifetime,
-                                ..
-                            }) = param.name
-                            {
-                                // Pick the elided lifetime "definition" if one exists
-                                // and use it to make an elision scope.
-                                elision = Some(reg);
-                            } else {
-                                lifetimes.insert(def_id, reg);
-                            }
+                            lifetimes.insert(def_id, reg);
                         }
                         GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => {
                             non_lifetime_count += 1;
@@ -956,51 +906,24 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                 let next_early_index = index + non_lifetime_count;
                 self.map.late_bound_vars.insert(ty.hir_id, vec![]);
 
-                if let Some(elision_region) = elision {
-                    let scope =
-                        Scope::Elision { elide: Elide::Exact(elision_region), s: self.scope };
-                    self.with(scope, |this| {
-                        let scope = Scope::Binder {
-                            hir_id: ty.hir_id,
-                            lifetimes,
-                            next_early_index,
-                            s: this.scope,
-                            opaque_type_parent: false,
-                            scope_type: BinderScopeType::Normal,
-                            allow_late_bound: false,
-                            where_bound_origin: None,
-                        };
-                        this.with(scope, |this| {
-                            this.visit_generics(generics);
-                            let scope = Scope::TraitRefBoundary { s: this.scope };
-                            this.with(scope, |this| {
-                                for bound in bounds {
-                                    this.visit_param_bound(bound);
-                                }
-                            })
-                        });
-                    });
-                } else {
-                    let scope = Scope::Binder {
-                        hir_id: ty.hir_id,
-                        lifetimes,
-                        next_early_index,
-                        s: self.scope,
-                        opaque_type_parent: false,
-                        scope_type: BinderScopeType::Normal,
-                        allow_late_bound: false,
-                        where_bound_origin: None,
-                    };
-                    self.with(scope, |this| {
-                        let scope = Scope::TraitRefBoundary { s: this.scope };
-                        this.with(scope, |this| {
-                            this.visit_generics(generics);
-                            for bound in bounds {
-                                this.visit_param_bound(bound);
-                            }
-                        })
-                    });
-                }
+                let scope = Scope::Binder {
+                    hir_id: ty.hir_id,
+                    lifetimes,
+                    next_early_index,
+                    s: self.scope,
+                    opaque_type_parent: false,
+                    scope_type: BinderScopeType::Normal,
+                    where_bound_origin: None,
+                };
+                self.with(scope, |this| {
+                    let scope = Scope::TraitRefBoundary { s: this.scope };
+                    this.with(scope, |this| {
+                        this.visit_generics(generics);
+                        for bound in bounds {
+                            this.visit_param_bound(bound);
+                        }
+                    })
+                });
             }
             _ => intravisit::walk_ty(self, ty),
         }
@@ -1010,7 +933,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
         use self::hir::TraitItemKind::*;
         match trait_item.kind {
             Fn(_, _) => {
-                self.missing_named_lifetime_spots.push((&trait_item.generics).into());
                 let tcx = self.tcx;
                 self.visit_early_late(
                     Some(tcx.hir().get_parent_item(trait_item.hir_id())),
@@ -1018,10 +940,8 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                     &trait_item.generics,
                     |this| intravisit::walk_trait_item(this, trait_item),
                 );
-                self.missing_named_lifetime_spots.pop();
             }
             Type(bounds, ref ty) => {
-                self.missing_named_lifetime_spots.push((&trait_item.generics).into());
                 let generics = &trait_item.generics;
                 let mut index = self.next_early_index();
                 debug!("visit_ty: index = {}", index);
@@ -1047,7 +967,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                     s: self.scope,
                     opaque_type_parent: true,
                     scope_type: BinderScopeType::Normal,
-                    allow_late_bound: false,
                     where_bound_origin: None,
                 };
                 self.with(scope, |this| {
@@ -1062,14 +981,11 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                         }
                     })
                 });
-                self.missing_named_lifetime_spots.pop();
             }
             Const(_, _) => {
                 // Only methods and types support generics.
                 assert!(trait_item.generics.params.is_empty());
-                self.missing_named_lifetime_spots.push(MissingLifetimeSpot::Static);
                 intravisit::walk_trait_item(self, trait_item);
-                self.missing_named_lifetime_spots.pop();
             }
         }
     }
@@ -1078,7 +994,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
         use self::hir::ImplItemKind::*;
         match impl_item.kind {
             Fn(..) => {
-                self.missing_named_lifetime_spots.push((&impl_item.generics).into());
                 let tcx = self.tcx;
                 self.visit_early_late(
                     Some(tcx.hir().get_parent_item(impl_item.hir_id())),
@@ -1086,11 +1001,9 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                     &impl_item.generics,
                     |this| intravisit::walk_impl_item(this, impl_item),
                 );
-                self.missing_named_lifetime_spots.pop();
             }
             TyAlias(ref ty) => {
                 let generics = &impl_item.generics;
-                self.missing_named_lifetime_spots.push(generics.into());
                 let mut index = self.next_early_index();
                 let mut non_lifetime_count = 0;
                 debug!("visit_ty: index = {}", index);
@@ -1115,7 +1028,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                     s: self.scope,
                     opaque_type_parent: true,
                     scope_type: BinderScopeType::Normal,
-                    allow_late_bound: true,
                     where_bound_origin: None,
                 };
                 self.with(scope, |this| {
@@ -1125,14 +1037,11 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                         this.visit_ty(ty);
                     })
                 });
-                self.missing_named_lifetime_spots.pop();
             }
             Const(_, _) => {
                 // Only methods and types support generics.
                 assert!(impl_item.generics.params.is_empty());
-                self.missing_named_lifetime_spots.push(MissingLifetimeSpot::Static);
                 intravisit::walk_impl_item(self, impl_item);
-                self.missing_named_lifetime_spots.pop();
             }
         }
     }
@@ -1140,15 +1049,14 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
     #[tracing::instrument(level = "debug", skip(self))]
     fn visit_lifetime(&mut self, lifetime_ref: &'tcx hir::Lifetime) {
         match lifetime_ref.name {
-            hir::LifetimeName::ImplicitObjectLifetimeDefault
-            | hir::LifetimeName::Implicit
-            | hir::LifetimeName::Underscore => self.resolve_elided_lifetimes(&[lifetime_ref]),
             hir::LifetimeName::Static => self.insert_lifetime(lifetime_ref, Region::Static),
             hir::LifetimeName::Param(param_def_id, _) => {
                 self.resolve_lifetime_ref(param_def_id, lifetime_ref)
             }
             // If we've already reported an error, just ignore `lifetime_ref`.
             hir::LifetimeName::Error => {}
+            // Those will be resolved by typechecking.
+            hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Infer => {}
         }
     }
 
@@ -1161,12 +1069,21 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
         }
     }
 
-    fn visit_fn_decl(&mut self, fd: &'tcx hir::FnDecl<'tcx>) {
+    fn visit_fn(
+        &mut self,
+        fk: intravisit::FnKind<'tcx>,
+        fd: &'tcx hir::FnDecl<'tcx>,
+        body_id: hir::BodyId,
+        _: Span,
+        _: hir::HirId,
+    ) {
         let output = match fd.output {
             hir::FnRetTy::DefaultReturn(_) => None,
             hir::FnRetTy::Return(ref ty) => Some(&**ty),
         };
-        self.visit_fn_like_elision(&fd.inputs, output);
+        self.visit_fn_like_elision(&fd.inputs, output, matches!(fk, intravisit::FnKind::Closure));
+        intravisit::walk_fn_kind(self, fk);
+        self.visit_nested_body(body_id)
     }
 
     fn visit_generics(&mut self, generics: &'tcx hir::Generics<'tcx>) {
@@ -1224,7 +1141,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                             next_early_index,
                             opaque_type_parent: false,
                             scope_type: BinderScopeType::Normal,
-                            allow_late_bound: true,
                             where_bound_origin: Some(origin),
                         };
                         this.with(scope, |this| {
@@ -1297,7 +1213,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                     next_early_index: self.next_early_index(),
                     opaque_type_parent: false,
                     scope_type,
-                    allow_late_bound: true,
                     where_bound_origin: None,
                 };
                 self.with(scope, |this| {
@@ -1315,8 +1230,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
     ) {
         debug!("visit_poly_trait_ref(trait_ref={:?})", trait_ref);
 
-        let should_pop_missing_lt = self.is_trait_ref_fn_scope(trait_ref);
-
         let next_early_index = self.next_early_index();
         let (mut binders, scope_type) = self.poly_trait_ref_binder_info();
 
@@ -1350,17 +1263,12 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
             next_early_index,
             opaque_type_parent: false,
             scope_type,
-            allow_late_bound: true,
             where_bound_origin: None,
         };
         self.with(scope, |this| {
             walk_list!(this, visit_generic_param, trait_ref.bound_generic_params);
             this.visit_trait_ref(&trait_ref.trait_ref);
         });
-
-        if should_pop_missing_lt {
-            self.missing_named_lifetime_spots.pop();
-        }
     }
 }
 
@@ -1506,14 +1414,12 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
     {
         let LifetimeContext { tcx, map, .. } = self;
         let xcrate_object_lifetime_defaults = take(&mut self.xcrate_object_lifetime_defaults);
-        let missing_named_lifetime_spots = take(&mut self.missing_named_lifetime_spots);
         let mut this = LifetimeContext {
             tcx: *tcx,
             map,
             scope: &wrap_scope,
             trait_definition_only: self.trait_definition_only,
             xcrate_object_lifetime_defaults,
-            missing_named_lifetime_spots,
         };
         let span = tracing::debug_span!("scope", scope = ?TruncatedScopeDebug(&this.scope));
         {
@@ -1521,7 +1427,6 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
             f(&mut this);
         }
         self.xcrate_object_lifetime_defaults = this.xcrate_object_lifetime_defaults;
-        self.missing_named_lifetime_spots = this.missing_named_lifetime_spots;
     }
 
     /// Visits self by adding a scope and handling recursive walk over the contents with `walk`.
@@ -1611,7 +1516,6 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
             s: self.scope,
             opaque_type_parent: true,
             scope_type: BinderScopeType::Normal,
-            allow_late_bound: true,
             where_bound_origin: None,
         };
         self.with(scope, walk);
@@ -1677,7 +1581,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
                     break None;
                 }
 
-                Scope::Binder { ref lifetimes, scope_type, s, .. } => {
+                Scope::Binder { ref lifetimes, scope_type, s, where_bound_origin, .. } => {
                     if let Some(&def) = lifetimes.get(&region_def_id) {
                         break Some(def.shifted(late_depth));
                     }
@@ -1685,6 +1589,21 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
                         BinderScopeType::Normal => late_depth += 1,
                         BinderScopeType::Concatenating => {}
                     }
+                    // Fresh lifetimes in APIT used to be allowed in async fns and forbidden in
+                    // regular fns.
+                    if let Some(hir::PredicateOrigin::ImplTrait) = where_bound_origin
+                        && let hir::LifetimeName::Param(_, hir::ParamName::Fresh) = lifetime_ref.name
+                        && let hir::IsAsync::NotAsync = self.tcx.asyncness(lifetime_ref.hir_id.owner)
+                        && !self.tcx.features().anonymous_lifetime_in_impl_trait
+                    {
+                        rustc_session::parse::feature_err(
+                            &self.tcx.sess.parse_sess,
+                            sym::anonymous_lifetime_in_impl_trait,
+                            lifetime_ref.span,
+                            "anonymous lifetimes in `impl Trait` are unstable",
+                        ).emit();
+                        return;
+                    }
                     scope = s;
                 }
 
@@ -1772,30 +1691,18 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
         );
 
         if generic_args.parenthesized {
-            self.visit_fn_like_elision(generic_args.inputs(), Some(generic_args.bindings[0].ty()));
+            self.visit_fn_like_elision(
+                generic_args.inputs(),
+                Some(generic_args.bindings[0].ty()),
+                false,
+            );
             return;
         }
 
-        let mut elide_lifetimes = true;
-        let lifetimes: Vec<_> = generic_args
-            .args
-            .iter()
-            .filter_map(|arg| match arg {
-                hir::GenericArg::Lifetime(lt) => {
-                    if !lt.is_elided() {
-                        elide_lifetimes = false;
-                    }
-                    Some(lt)
-                }
-                _ => None,
-            })
-            .collect();
-        // We short-circuit here if all are elided in order to pluralize
-        // possible errors
-        if elide_lifetimes {
-            self.resolve_elided_lifetimes(&lifetimes);
-        } else {
-            lifetimes.iter().for_each(|lt| self.visit_lifetime(lt));
+        for arg in generic_args.args {
+            if let hir::GenericArg::Lifetime(lt) = arg {
+                self.visit_lifetime(lt);
+            }
         }
 
         // Figure out if this is a type/trait segment,
@@ -2051,436 +1958,19 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
         &mut self,
         inputs: &'tcx [hir::Ty<'tcx>],
         output: Option<&'tcx hir::Ty<'tcx>>,
+        in_closure: bool,
     ) {
-        debug!("visit_fn_like_elision: enter");
-        let mut scope = &*self.scope;
-        let hir_id = loop {
-            match scope {
-                Scope::Binder { hir_id, allow_late_bound: true, .. } => {
-                    break *hir_id;
-                }
-                Scope::ObjectLifetimeDefault { ref s, .. }
-                | Scope::Elision { ref s, .. }
-                | Scope::Supertrait { ref s, .. }
-                | Scope::TraitRefBoundary { ref s, .. } => {
-                    scope = *s;
-                }
-                Scope::Root
-                | Scope::Body { .. }
-                | Scope::Binder { allow_late_bound: false, .. } => {
-                    // See issues #83907 and #83693. Just bail out from looking inside.
-                    // See the issue #95023 for not allowing late bound
-                    self.tcx.sess.delay_span_bug(
-                        rustc_span::DUMMY_SP,
-                        "In fn_like_elision without appropriate scope above",
-                    );
-                    return;
-                }
-            }
-        };
-        // While not strictly necessary, we gather anon lifetimes *before* actually
-        // visiting the argument types.
-        let mut gather = GatherAnonLifetimes { anon_count: 0 };
-        for input in inputs {
-            gather.visit_ty(input);
-        }
-        trace!(?gather.anon_count);
-        let late_bound_vars = self.map.late_bound_vars.entry(hir_id).or_default();
-        let named_late_bound_vars = late_bound_vars.len() as u32;
-        late_bound_vars.extend(
-            (0..gather.anon_count).map(|var| ty::BoundVariableKind::Region(ty::BrAnon(var))),
-        );
-        let arg_scope = Scope::Elision {
-            elide: Elide::FreshLateAnon(named_late_bound_vars, Cell::new(0)),
-            s: self.scope,
-        };
-        self.with(arg_scope, |this| {
+        self.with(Scope::Elision { s: self.scope }, |this| {
             for input in inputs {
                 this.visit_ty(input);
             }
-        });
-
-        let Some(output) = output else { return };
-
-        debug!("determine output");
-
-        // Figure out if there's a body we can get argument names from,
-        // and whether there's a `self` argument (treated specially).
-        let mut assoc_item_kind = None;
-        let mut impl_self = None;
-        let parent = self.tcx.hir().get_parent_node(output.hir_id);
-        let body = match self.tcx.hir().get(parent) {
-            // `fn` definitions and methods.
-            Node::Item(&hir::Item { kind: hir::ItemKind::Fn(.., body), .. }) => Some(body),
-
-            Node::TraitItem(&hir::TraitItem { kind: hir::TraitItemKind::Fn(_, ref m), .. }) => {
-                if let hir::ItemKind::Trait(.., ref trait_items) =
-                    self.tcx.hir().expect_item(self.tcx.hir().get_parent_item(parent)).kind
-                {
-                    assoc_item_kind =
-                        trait_items.iter().find(|ti| ti.id.hir_id() == parent).map(|ti| ti.kind);
-                }
-                match *m {
-                    hir::TraitFn::Required(_) => None,
-                    hir::TraitFn::Provided(body) => Some(body),
-                }
-            }
-
-            Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(_, body), .. }) => {
-                if let hir::ItemKind::Impl(hir::Impl { ref self_ty, ref items, .. }) =
-                    self.tcx.hir().expect_item(self.tcx.hir().get_parent_item(parent)).kind
-                {
-                    impl_self = Some(self_ty);
-                    assoc_item_kind =
-                        items.iter().find(|ii| ii.id.hir_id() == parent).map(|ii| ii.kind);
-                }
-                Some(body)
-            }
-
-            // Foreign functions, `fn(...) -> R` and `Trait(...) -> R` (both types and bounds).
-            Node::ForeignItem(_) | Node::Ty(_) | Node::TraitRef(_) => None,
-
-            Node::TypeBinding(_) if let Node::TraitRef(_) = self.tcx.hir().get(self.tcx.hir().get_parent_node(parent)) => None,
-
-            // Everything else (only closures?) doesn't
-            // actually enjoy elision in return types.
-            _ => {
-                self.visit_ty(output);
-                return;
-            }
-        };
-
-        let has_self = match assoc_item_kind {
-            Some(hir::AssocItemKind::Fn { has_self }) => has_self,
-            _ => false,
-        };
-
-        // In accordance with the rules for lifetime elision, we can determine
-        // what region to use for elision in the output type in two ways.
-        // First (determined here), if `self` is by-reference, then the
-        // implied output region is the region of the self parameter.
-        if has_self {
-            struct SelfVisitor<'a> {
-                map: &'a NamedRegionMap,
-                impl_self: Option<&'a hir::TyKind<'a>>,
-                lifetime: Set1<Region>,
-            }
-
-            impl SelfVisitor<'_> {
-                // Look for `self: &'a Self` - also desugared from `&'a self`,
-                // and if that matches, use it for elision and return early.
-                fn is_self_ty(&self, res: Res) -> bool {
-                    if let Res::SelfTy { .. } = res {
-                        return true;
-                    }
-
-                    // Can't always rely on literal (or implied) `Self` due
-                    // to the way elision rules were originally specified.
-                    if let Some(&hir::TyKind::Path(hir::QPath::Resolved(None, ref path))) =
-                        self.impl_self
-                    {
-                        match path.res {
-                            // Permit the types that unambiguously always
-                            // result in the same type constructor being used
-                            // (it can't differ between `Self` and `self`).
-                            Res::Def(DefKind::Struct | DefKind::Union | DefKind::Enum, _)
-                            | Res::PrimTy(_) => return res == path.res,
-                            _ => {}
-                        }
-                    }
-
-                    false
-                }
-            }
-
-            impl<'a> Visitor<'a> for SelfVisitor<'a> {
-                fn visit_ty(&mut self, ty: &'a hir::Ty<'a>) {
-                    if let hir::TyKind::Rptr(lifetime_ref, ref mt) = ty.kind {
-                        if let hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) = mt.ty.kind
-                        {
-                            if self.is_self_ty(path.res) {
-                                if let Some(lifetime) = self.map.defs.get(&lifetime_ref.hir_id) {
-                                    self.lifetime.insert(*lifetime);
-                                }
-                            }
-                        }
-                    }
-                    intravisit::walk_ty(self, ty)
-                }
-            }
-
-            let mut visitor = SelfVisitor {
-                map: self.map,
-                impl_self: impl_self.map(|ty| &ty.kind),
-                lifetime: Set1::Empty,
-            };
-            visitor.visit_ty(&inputs[0]);
-            if let Set1::One(lifetime) = visitor.lifetime {
-                let scope = Scope::Elision { elide: Elide::Exact(lifetime), s: self.scope };
-                self.with(scope, |this| this.visit_ty(output));
-                return;
-            }
-        }
-
-        // Second, if there was exactly one lifetime (either a substitution or a
-        // reference) in the arguments, then any anonymous regions in the output
-        // have that lifetime.
-        let mut possible_implied_output_region = None;
-        let mut lifetime_count = 0;
-        let arg_lifetimes = inputs
-            .iter()
-            .enumerate()
-            .skip(has_self as usize)
-            .map(|(i, input)| {
-                let mut gather = GatherLifetimes {
-                    map: self.map,
-                    outer_index: ty::INNERMOST,
-                    have_bound_regions: false,
-                    lifetimes: Default::default(),
-                };
-                gather.visit_ty(input);
-
-                lifetime_count += gather.lifetimes.len();
-
-                if lifetime_count == 1 && gather.lifetimes.len() == 1 {
-                    // there's a chance that the unique lifetime of this
-                    // iteration will be the appropriate lifetime for output
-                    // parameters, so lets store it.
-                    possible_implied_output_region = gather.lifetimes.iter().cloned().next();
-                }
-
-                ElisionFailureInfo {
-                    parent: body,
-                    index: i,
-                    lifetime_count: gather.lifetimes.len(),
-                    have_bound_regions: gather.have_bound_regions,
-                    span: input.span,
-                }
-            })
-            .collect();
-
-        let elide = if lifetime_count == 1 {
-            Elide::Exact(possible_implied_output_region.unwrap())
-        } else {
-            Elide::Error(arg_lifetimes)
-        };
-
-        debug!(?elide);
-
-        let scope = Scope::Elision { elide, s: self.scope };
-        self.with(scope, |this| this.visit_ty(output));
-
-        struct GatherLifetimes<'a> {
-            map: &'a NamedRegionMap,
-            outer_index: ty::DebruijnIndex,
-            have_bound_regions: bool,
-            lifetimes: FxHashSet<Region>,
-        }
-
-        impl<'v, 'a> Visitor<'v> for GatherLifetimes<'a> {
-            fn visit_ty(&mut self, ty: &hir::Ty<'_>) {
-                if let hir::TyKind::BareFn(_) = ty.kind {
-                    self.outer_index.shift_in(1);
-                }
-                match ty.kind {
-                    hir::TyKind::TraitObject(bounds, ref lifetime, _) => {
-                        for bound in bounds {
-                            self.visit_poly_trait_ref(bound, hir::TraitBoundModifier::None);
-                        }
-
-                        // Stay on the safe side and don't include the object
-                        // lifetime default (which may not end up being used).
-                        if !lifetime.is_elided() {
-                            self.visit_lifetime(lifetime);
-                        }
-                    }
-                    _ => {
-                        intravisit::walk_ty(self, ty);
-                    }
-                }
-                if let hir::TyKind::BareFn(_) = ty.kind {
-                    self.outer_index.shift_out(1);
-                }
-            }
-
-            fn visit_generic_param(&mut self, param: &hir::GenericParam<'_>) {
-                if let hir::GenericParamKind::Lifetime { .. } = param.kind {
-                    // FIXME(eddyb) Do we want this? It only makes a difference
-                    // if this `for<'a>` lifetime parameter is never used.
-                    self.have_bound_regions = true;
-                }
-
-                intravisit::walk_generic_param(self, param);
-            }
-
-            fn visit_poly_trait_ref(
-                &mut self,
-                trait_ref: &hir::PolyTraitRef<'_>,
-                modifier: hir::TraitBoundModifier,
-            ) {
-                self.outer_index.shift_in(1);
-                intravisit::walk_poly_trait_ref(self, trait_ref, modifier);
-                self.outer_index.shift_out(1);
-            }
-
-            fn visit_param_bound(&mut self, bound: &hir::GenericBound<'_>) {
-                if let hir::GenericBound::LangItemTrait { .. } = bound {
-                    self.outer_index.shift_in(1);
-                    intravisit::walk_param_bound(self, bound);
-                    self.outer_index.shift_out(1);
-                } else {
-                    intravisit::walk_param_bound(self, bound);
-                }
+            if !in_closure && let Some(output) = output {
+                this.visit_ty(output);
             }
-
-            fn visit_lifetime(&mut self, lifetime_ref: &hir::Lifetime) {
-                if let Some(&lifetime) = self.map.defs.get(&lifetime_ref.hir_id) {
-                    match lifetime {
-                        Region::LateBound(debruijn, _, _)
-                        | Region::LateBoundAnon(debruijn, _, _)
-                            if debruijn < self.outer_index =>
-                        {
-                            self.have_bound_regions = true;
-                        }
-                        _ => {
-                            // FIXME(jackh726): nested trait refs?
-                            self.lifetimes.insert(lifetime.shifted_out_to_binder(self.outer_index));
-                        }
-                    }
-                }
-            }
-        }
-
-        struct GatherAnonLifetimes {
-            anon_count: u32,
-        }
-        impl<'v> Visitor<'v> for GatherAnonLifetimes {
-            #[instrument(skip(self), level = "trace")]
-            fn visit_ty(&mut self, ty: &hir::Ty<'_>) {
-                // If we enter a `BareFn`, then we enter a *new* binding scope
-                if let hir::TyKind::BareFn(_) = ty.kind {
-                    return;
-                }
-                intravisit::walk_ty(self, ty);
-            }
-
-            fn visit_generic_args(
-                &mut self,
-                path_span: Span,
-                generic_args: &'v hir::GenericArgs<'v>,
-            ) {
-                // parenthesized args enter a new elision scope
-                if generic_args.parenthesized {
-                    return;
-                }
-                intravisit::walk_generic_args(self, path_span, generic_args)
-            }
-
-            #[instrument(skip(self), level = "trace")]
-            fn visit_lifetime(&mut self, lifetime_ref: &hir::Lifetime) {
-                if lifetime_ref.is_elided() {
-                    self.anon_count += 1;
-                }
-            }
-        }
-    }
-
-    fn resolve_elided_lifetimes(&mut self, lifetime_refs: &[&'tcx hir::Lifetime]) {
-        debug!("resolve_elided_lifetimes(lifetime_refs={:?})", lifetime_refs);
-
-        if lifetime_refs.is_empty() {
-            return;
+        });
+        if in_closure && let Some(output) = output {
+            self.visit_ty(output);
         }
-
-        let mut late_depth = 0;
-        let mut scope = self.scope;
-        let mut in_scope_lifetimes = FxIndexSet::default();
-        let error = loop {
-            match *scope {
-                // Do not assign any resolution, it will be inferred.
-                Scope::Body { .. } => return,
-
-                Scope::Root => break None,
-
-                Scope::Binder { s, ref lifetimes, scope_type, .. } => {
-                    // collect named lifetimes for suggestions
-                    in_scope_lifetimes.extend(lifetimes.keys().copied());
-                    match scope_type {
-                        BinderScopeType::Normal => late_depth += 1,
-                        BinderScopeType::Concatenating => {}
-                    }
-                    scope = s;
-                }
-
-                Scope::Elision {
-                    elide: Elide::FreshLateAnon(named_late_bound_vars, ref counter),
-                    ..
-                } => {
-                    for lifetime_ref in lifetime_refs {
-                        let lifetime =
-                            Region::late_anon(named_late_bound_vars, counter).shifted(late_depth);
-
-                        self.insert_lifetime(lifetime_ref, lifetime);
-                    }
-                    return;
-                }
-
-                Scope::Elision { elide: Elide::Exact(l), .. } => {
-                    let lifetime = l.shifted(late_depth);
-                    for lifetime_ref in lifetime_refs {
-                        self.insert_lifetime(lifetime_ref, lifetime);
-                    }
-                    return;
-                }
-
-                Scope::Elision { elide: Elide::Error(ref e), ref s, .. } => {
-                    let mut scope = s;
-                    loop {
-                        match scope {
-                            Scope::Binder { ref lifetimes, s, .. } => {
-                                // Collect named lifetimes for suggestions.
-                                in_scope_lifetimes.extend(lifetimes.keys().copied());
-                                scope = s;
-                            }
-                            Scope::ObjectLifetimeDefault { ref s, .. }
-                            | Scope::Elision { ref s, .. }
-                            | Scope::TraitRefBoundary { ref s, .. } => {
-                                scope = s;
-                            }
-                            _ => break,
-                        }
-                    }
-                    break Some(&e[..]);
-                }
-
-                Scope::Elision { elide: Elide::Forbid, .. } => break None,
-
-                Scope::ObjectLifetimeDefault { s, .. }
-                | Scope::Supertrait { s, .. }
-                | Scope::TraitRefBoundary { s, .. } => {
-                    scope = s;
-                }
-            }
-        };
-
-        let mut spans: Vec<_> = lifetime_refs.iter().map(|lt| lt.span).collect();
-        spans.sort();
-        let mut spans_dedup = spans.clone();
-        spans_dedup.dedup();
-        let spans_with_counts: Vec<_> = spans_dedup
-            .into_iter()
-            .map(|sp| (sp, spans.iter().filter(|nsp| *nsp == &sp).count()))
-            .collect();
-
-        let mut err = self.report_missing_lifetime_specifiers(spans.clone(), lifetime_refs.len());
-
-        self.add_missing_lifetime_specifiers_label(
-            &mut err,
-            spans_with_counts,
-            in_scope_lifetimes,
-            error,
-        );
-        err.emit();
     }
 
     fn resolve_object_lifetime_default(&mut self, lifetime_ref: &'tcx hir::Lifetime) {
diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs
index 8968179c92e..31d10008efb 100644
--- a/compiler/rustc_resolve/src/lib.rs
+++ b/compiler/rustc_resolve/src/lib.rs
@@ -10,7 +10,8 @@
 #![feature(box_patterns)]
 #![feature(drain_filter)]
 #![feature(if_let_guard)]
-#![feature(let_chains)]
+#![cfg_attr(bootstrap, feature(let_chains))]
+#![feature(iter_intersperse)]
 #![feature(let_else)]
 #![feature(never_type)]
 #![recursion_limit = "256"]
@@ -436,7 +437,7 @@ enum ModuleKind {
     ///     f(); // Resolves to (1)
     /// }
     /// ```
-    Block(NodeId),
+    Block,
     /// Any module with a name.
     ///
     /// This could be:
@@ -453,7 +454,7 @@ impl ModuleKind {
     /// Get name of the module.
     pub fn name(&self) -> Option<Symbol> {
         match self {
-            ModuleKind::Block(..) => None,
+            ModuleKind::Block => None,
             ModuleKind::Def(.., name) => Some(*name),
         }
     }
@@ -529,7 +530,7 @@ impl<'a> ModuleData<'a> {
     ) -> Self {
         let is_foreign = match kind {
             ModuleKind::Def(_, def_id, _) => !def_id.is_local(),
-            ModuleKind::Block(_) => false,
+            ModuleKind::Block => false,
         };
         ModuleData {
             parent,
diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs
index 4210560f531..070fb9c721b 100644
--- a/compiler/rustc_resolve/src/macros.rs
+++ b/compiler/rustc_resolve/src/macros.rs
@@ -325,7 +325,7 @@ impl<'a> ResolverExpand for Resolver<'a> {
                 UNUSED_MACROS,
                 node_id,
                 ident.span,
-                &format!("unused macro definition: `{}`", ident.as_str()),
+                &format!("unused macro definition: `{}`", ident.name),
             );
         }
         for (&(def_id, arm_i), &(ident, rule_span)) in self.unused_macro_rules.iter() {
@@ -341,7 +341,7 @@ impl<'a> ResolverExpand for Resolver<'a> {
                 &format!(
                     "{} rule of macro `{}` is never used",
                     crate::diagnostics::ordinalize(arm_i + 1),
-                    ident.as_str()
+                    ident.name
                 ),
             );
         }
@@ -796,16 +796,23 @@ impl<'a> Resolver<'a> {
     ) {
         let span = path.span;
         if let Some(stability) = &ext.stability {
-            if let StabilityLevel::Unstable { reason, issue, is_soft } = stability.level {
+            if let StabilityLevel::Unstable { reason, issue, is_soft, implied_by } = stability.level
+            {
                 let feature = stability.feature;
-                if !self.active_features.contains(&feature) && !span.allows_unstable(feature) {
+
+                let is_allowed = |feature| {
+                    self.active_features.contains(&feature) || span.allows_unstable(feature)
+                };
+                let allowed_by_implication =
+                    implied_by.map(|feature| is_allowed(feature)).unwrap_or(false);
+                if !is_allowed(feature) && !allowed_by_implication {
                     let lint_buffer = &mut self.lint_buffer;
                     let soft_handler =
                         |lint, span, msg: &_| lint_buffer.buffer_lint(lint, node_id, span, msg);
                     stability::report_unstable(
                         self.session,
                         feature,
-                        reason,
+                        reason.to_opt_reason(),
                         issue,
                         None,
                         is_soft,
diff --git a/compiler/rustc_save_analysis/src/dump_visitor.rs b/compiler/rustc_save_analysis/src/dump_visitor.rs
index 6eb2f2d929d..e2e0e1f5b30 100644
--- a/compiler/rustc_save_analysis/src/dump_visitor.rs
+++ b/compiler/rustc_save_analysis/src/dump_visitor.rs
@@ -82,14 +82,7 @@ impl<'tcx> DumpVisitor<'tcx> {
     pub fn new(save_ctxt: SaveContext<'tcx>) -> DumpVisitor<'tcx> {
         let span_utils = SpanUtils::new(&save_ctxt.tcx.sess);
         let dumper = Dumper::new(save_ctxt.config.clone());
-        DumpVisitor {
-            tcx: save_ctxt.tcx,
-            save_ctxt,
-            dumper,
-            span: span_utils,
-            // mac_defs: FxHashSet::default(),
-            // macro_calls: FxHashSet::default(),
-        }
+        DumpVisitor { tcx: save_ctxt.tcx, save_ctxt, dumper, span: span_utils }
     }
 
     pub fn analysis(&self) -> &rls_data::Analysis {
@@ -1360,7 +1353,7 @@ impl<'tcx> Visitor<'tcx> for DumpVisitor<'tcx> {
                     }
                 }
             }
-            hir::ExprKind::Closure { ref fn_decl, body, .. } => {
+            hir::ExprKind::Closure(&hir::Closure { ref fn_decl, body, .. }) => {
                 let id = format!("${}", ex.hir_id);
 
                 // walk arg and return types
@@ -1425,9 +1418,10 @@ impl<'tcx> Visitor<'tcx> for DumpVisitor<'tcx> {
         self.process_macro_use(l.span);
         self.process_var_decl(&l.pat);
 
-        // Just walk the initializer and type (don't want to walk the pattern again).
+        // Just walk the initializer, the else branch and type (don't want to walk the pattern again).
         walk_list!(self, visit_ty, &l.ty);
         walk_list!(self, visit_expr, &l.init);
+        walk_list!(self, visit_block, l.els);
     }
 
     fn visit_foreign_item(&mut self, item: &'tcx hir::ForeignItem<'tcx>) {
diff --git a/compiler/rustc_serialize/Cargo.toml b/compiler/rustc_serialize/Cargo.toml
index 5056163b7fe..dbc5c15195c 100644
--- a/compiler/rustc_serialize/Cargo.toml
+++ b/compiler/rustc_serialize/Cargo.toml
@@ -4,7 +4,7 @@ version = "0.0.0"
 edition = "2021"
 
 [dependencies]
-indexmap = "1.8.0"
+indexmap = "1.9.1"
 smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
 
 [dev-dependencies]
diff --git a/compiler/rustc_serialize/src/opaque.rs b/compiler/rustc_serialize/src/opaque.rs
index 366efe9cfa5..5c17ef6ace2 100644
--- a/compiler/rustc_serialize/src/opaque.rs
+++ b/compiler/rustc_serialize/src/opaque.rs
@@ -297,6 +297,10 @@ impl FileEncoder {
         }
     }
 
+    pub fn file(&self) -> &File {
+        &self.file
+    }
+
     #[inline]
     fn capacity(&self) -> usize {
         self.buf.len()
diff --git a/compiler/rustc_session/src/code_stats.rs b/compiler/rustc_session/src/code_stats.rs
index cbb03ffd7a0..eede4d16ea3 100644
--- a/compiler/rustc_session/src/code_stats.rs
+++ b/compiler/rustc_session/src/code_stats.rs
@@ -1,11 +1,12 @@
 use rustc_data_structures::fx::FxHashSet;
 use rustc_data_structures::sync::Lock;
+use rustc_span::Symbol;
 use rustc_target::abi::{Align, Size};
 use std::cmp::{self, Ordering};
 
 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
 pub struct VariantInfo {
-    pub name: Option<String>,
+    pub name: Option<Symbol>,
     pub kind: SizeKind,
     pub size: u64,
     pub align: u64,
@@ -20,7 +21,7 @@ pub enum SizeKind {
 
 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
 pub struct FieldInfo {
-    pub name: String,
+    pub name: Symbol,
     pub offset: u64,
     pub size: u64,
     pub align: u64,
@@ -119,7 +120,7 @@ impl CodeStats {
                 let VariantInfo { ref name, kind: _, align: _, size, ref fields } = *variant_info;
                 let indent = if !struct_like {
                     let name = match name.as_ref() {
-                        Some(name) => name.to_owned(),
+                        Some(name) => name.to_string(),
                         None => i.to_string(),
                     };
                     println!(
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index b7da0f22942..fe9ef604541 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -722,7 +722,7 @@ impl Default for Options {
             target_triple: TargetTriple::from_triple(host_triple()),
             test: false,
             incremental: None,
-            debugging_opts: Default::default(),
+            unstable_opts: Default::default(),
             prints: Vec::new(),
             cg: Default::default(),
             error_format: ErrorOutputType::default(),
@@ -752,8 +752,8 @@ impl Options {
     /// Returns `true` if there is a reason to build the dep graph.
     pub fn build_dep_graph(&self) -> bool {
         self.incremental.is_some()
-            || self.debugging_opts.dump_dep_graph
-            || self.debugging_opts.query_dep_graph
+            || self.unstable_opts.dump_dep_graph
+            || self.unstable_opts.query_dep_graph
     }
 
     pub fn file_path_mapping(&self) -> FilePathMapping {
@@ -762,13 +762,13 @@ impl Options {
 
     /// Returns `true` if there will be an output file generated.
     pub fn will_create_output_file(&self) -> bool {
-        !self.debugging_opts.parse_only && // The file is just being parsed
-            !self.debugging_opts.ls // The file is just being queried
+        !self.unstable_opts.parse_only && // The file is just being parsed
+            !self.unstable_opts.ls // The file is just being queried
     }
 
     #[inline]
     pub fn share_generics(&self) -> bool {
-        match self.debugging_opts.share_generics {
+        match self.unstable_opts.share_generics {
             Some(setting) => setting,
             None => match self.optimize {
                 OptLevel::No | OptLevel::Less | OptLevel::Size | OptLevel::SizeMin => true,
@@ -782,7 +782,7 @@ impl Options {
     }
 }
 
-impl DebuggingOptions {
+impl UnstableOptions {
     pub fn diagnostic_handler_flags(&self, can_emit_warnings: bool) -> HandlerFlags {
         HandlerFlags {
             can_emit_warnings,
@@ -940,7 +940,7 @@ fn default_configuration(sess: &Session) -> CrateConfig {
     let panic_strategy = sess.panic_strategy();
     ret.insert((sym::panic, Some(panic_strategy.desc_symbol())));
 
-    for s in sess.opts.debugging_opts.sanitizer {
+    for s in sess.opts.unstable_opts.sanitizer {
         let symbol = Symbol::intern(&s.to_string());
         ret.insert((sym::sanitize, Some(symbol)));
     }
@@ -948,6 +948,8 @@ fn default_configuration(sess: &Session) -> CrateConfig {
     if sess.opts.debug_assertions {
         ret.insert((sym::debug_assertions, None));
     }
+    // JUSTIFICATION: before wrapper fn is available
+    #[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))]
     if sess.opts.crate_types.contains(&CrateType::ProcMacro) {
         ret.insert((sym::proc_macro, None));
     }
@@ -1403,6 +1405,7 @@ pub fn rustc_short_optgroups() -> Vec<RustcOptGroup> {
 /// long-term interface for rustc.
 pub fn rustc_optgroups() -> Vec<RustcOptGroup> {
     let mut opts = rustc_short_optgroups();
+    // FIXME: none of these descriptions are actually used
     opts.extend(vec![
         opt::multi_s(
             "",
@@ -1411,7 +1414,7 @@ pub fn rustc_optgroups() -> Vec<RustcOptGroup> {
             "NAME[=PATH]",
         ),
         opt::opt_s("", "sysroot", "Override the system root", "PATH"),
-        opt::multi("Z", "", "Set internal debugging options", "FLAG"),
+        opt::multi("Z", "", "Set unstable / perma-unstable options", "FLAG"),
         opt::opt_s(
             "",
             "error-format",
@@ -1659,12 +1662,12 @@ pub fn parse_crate_edition(matches: &getopts::Matches) -> Edition {
     edition
 }
 
-fn check_debug_option_stability(
-    debugging_opts: &DebuggingOptions,
+fn check_error_format_stability(
+    unstable_opts: &UnstableOptions,
     error_format: ErrorOutputType,
     json_rendered: HumanReadableErrorType,
 ) {
-    if !debugging_opts.unstable_options {
+    if !unstable_opts.unstable_options {
         if let ErrorOutputType::Json { pretty: true, json_rendered } = error_format {
             early_error(
                 ErrorOutputType::Json { pretty: false, json_rendered },
@@ -1683,12 +1686,12 @@ fn check_debug_option_stability(
 }
 
 fn parse_output_types(
-    debugging_opts: &DebuggingOptions,
+    unstable_opts: &UnstableOptions,
     matches: &getopts::Matches,
     error_format: ErrorOutputType,
 ) -> OutputTypes {
     let mut output_types = BTreeMap::new();
-    if !debugging_opts.parse_only {
+    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('=') {
@@ -1762,19 +1765,19 @@ fn should_override_cgus_and_disable_thinlto(
     (disable_thinlto, codegen_units)
 }
 
-fn check_thread_count(debugging_opts: &DebuggingOptions, error_format: ErrorOutputType) {
-    if debugging_opts.threads == 0 {
+fn check_thread_count(unstable_opts: &UnstableOptions, error_format: ErrorOutputType) {
+    if unstable_opts.threads == 0 {
         early_error(error_format, "value for threads must be a positive non-zero integer");
     }
 
-    if debugging_opts.threads > 1 && debugging_opts.fuel.is_some() {
+    if unstable_opts.threads > 1 && unstable_opts.fuel.is_some() {
         early_error(error_format, "optimization fuel is incompatible with multiple threads");
     }
 }
 
 fn collect_print_requests(
     cg: &mut CodegenOptions,
-    dopts: &mut DebuggingOptions,
+    unstable_opts: &mut UnstableOptions,
     matches: &getopts::Matches,
     error_format: ErrorOutputType,
 ) -> Vec<PrintRequest> {
@@ -1803,7 +1806,7 @@ fn collect_print_requests(
         "native-static-libs" => PrintRequest::NativeStaticLibs,
         "stack-protector-strategies" => PrintRequest::StackProtectorStrategies,
         "target-spec-json" => {
-            if dopts.unstable_options {
+            if unstable_opts.unstable_options {
                 PrintRequest::TargetSpec
             } else {
                 early_error(
@@ -2069,10 +2072,10 @@ fn parse_libs(matches: &getopts::Matches, error_format: ErrorOutputType) -> Vec<
 
 pub fn parse_externs(
     matches: &getopts::Matches,
-    debugging_opts: &DebuggingOptions,
+    unstable_opts: &UnstableOptions,
     error_format: ErrorOutputType,
 ) -> Externs {
-    let is_unstable_enabled = debugging_opts.unstable_options;
+    let is_unstable_enabled = unstable_opts.unstable_options;
     let mut externs: BTreeMap<String, ExternEntry> = BTreeMap::new();
     for arg in matches.opt_strs("extern") {
         let (name, path) = match arg.split_once('=') {
@@ -2171,7 +2174,7 @@ pub fn parse_externs(
 
 fn parse_remap_path_prefix(
     matches: &getopts::Matches,
-    debugging_opts: &DebuggingOptions,
+    unstable_opts: &UnstableOptions,
     error_format: ErrorOutputType,
 ) -> Vec<(PathBuf, PathBuf)> {
     let mut mapping: Vec<(PathBuf, PathBuf)> = matches
@@ -2185,7 +2188,7 @@ fn parse_remap_path_prefix(
             Some((from, to)) => (PathBuf::from(from), PathBuf::from(to)),
         })
         .collect();
-    match &debugging_opts.remap_cwd_prefix {
+    match &unstable_opts.remap_cwd_prefix {
         Some(to) => match std::env::current_dir() {
             Ok(cwd) => mapping.push((cwd, to.clone())),
             Err(_) => (),
@@ -2195,6 +2198,8 @@ fn parse_remap_path_prefix(
     mapping
 }
 
+// JUSTIFICATION: before wrapper fn is available
+#[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))]
 pub fn build_session_options(matches: &getopts::Matches) -> Options {
     let color = parse_color(matches);
 
@@ -2217,12 +2222,12 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
     let crate_types = parse_crate_types_from_list(unparsed_crate_types)
         .unwrap_or_else(|e| early_error(error_format, &e));
 
-    let mut debugging_opts = DebuggingOptions::build(matches, error_format);
+    let mut unstable_opts = UnstableOptions::build(matches, error_format);
     let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(matches, error_format);
 
-    check_debug_option_stability(&debugging_opts, error_format, json_rendered);
+    check_error_format_stability(&unstable_opts, error_format, json_rendered);
 
-    if !debugging_opts.unstable_options && json_unused_externs.is_enabled() {
+    if !unstable_opts.unstable_options && json_unused_externs.is_enabled() {
         early_error(
             error_format,
             "the `-Z unstable-options` flag must also be passed to enable \
@@ -2230,7 +2235,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
         );
     }
 
-    let output_types = parse_output_types(&debugging_opts, matches, error_format);
+    let output_types = parse_output_types(&unstable_opts, matches, error_format);
 
     let mut cg = CodegenOptions::build(matches, error_format);
     let (disable_thinlto, mut codegen_units) = should_override_cgus_and_disable_thinlto(
@@ -2240,20 +2245,19 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
         cg.codegen_units,
     );
 
-    check_thread_count(&debugging_opts, error_format);
+    check_thread_count(&unstable_opts, error_format);
 
     let incremental = cg.incremental.as_ref().map(PathBuf::from);
 
-    let assert_incr_state =
-        parse_assert_incr_state(&debugging_opts.assert_incr_state, error_format);
+    let assert_incr_state = parse_assert_incr_state(&unstable_opts.assert_incr_state, error_format);
 
-    if debugging_opts.profile && incremental.is_some() {
+    if unstable_opts.profile && incremental.is_some() {
         early_error(
             error_format,
             "can't instrument with gcov profiling when compiling incrementally",
         );
     }
-    if debugging_opts.profile {
+    if unstable_opts.profile {
         match codegen_units {
             Some(1) => {}
             None => codegen_units = Some(1),
@@ -2271,7 +2275,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
         );
     }
 
-    if debugging_opts.profile_sample_use.is_some()
+    if unstable_opts.profile_sample_use.is_some()
         && (cg.profile_generate.enabled() || cg.profile_use.is_some())
     {
         early_error(
@@ -2282,7 +2286,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
 
     // Handle both `-Z symbol-mangling-version` and `-C symbol-mangling-version`; the latter takes
     // precedence.
-    match (cg.symbol_mangling_version, debugging_opts.symbol_mangling_version) {
+    match (cg.symbol_mangling_version, unstable_opts.symbol_mangling_version) {
         (Some(smv_c), Some(smv_z)) if smv_c != smv_z => {
             early_error(
                 error_format,
@@ -2291,7 +2295,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
             );
         }
         (Some(SymbolManglingVersion::V0), _) => {}
-        (Some(_), _) if !debugging_opts.unstable_options => {
+        (Some(_), _) if !unstable_opts.unstable_options => {
             early_error(
                 error_format,
                 "`-C symbol-mangling-version=legacy` requires `-Z unstable-options`",
@@ -2310,7 +2314,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
 
     // Handle both `-Z instrument-coverage` and `-C instrument-coverage`; the latter takes
     // precedence.
-    match (cg.instrument_coverage, debugging_opts.instrument_coverage) {
+    match (cg.instrument_coverage, unstable_opts.instrument_coverage) {
         (Some(ic_c), Some(ic_z)) if ic_c != ic_z => {
             early_error(
                 error_format,
@@ -2319,7 +2323,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
             );
         }
         (Some(InstrumentCoverage::Off | InstrumentCoverage::All), _) => {}
-        (Some(_), _) if !debugging_opts.unstable_options => {
+        (Some(_), _) if !unstable_opts.unstable_options => {
             early_error(
                 error_format,
                 "`-C instrument-coverage=except-*` requires `-Z unstable-options`",
@@ -2363,7 +2367,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
     }
 
     if let Ok(graphviz_font) = std::env::var("RUSTC_GRAPHVIZ_FONT") {
-        debugging_opts.graphviz_font = graphviz_font;
+        unstable_opts.graphviz_font = graphviz_font;
     }
 
     if !cg.embed_bitcode {
@@ -2386,7 +2390,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
         );
     }
 
-    let prints = collect_print_requests(&mut cg, &mut debugging_opts, matches, error_format);
+    let prints = collect_print_requests(&mut cg, &mut unstable_opts, matches, error_format);
 
     let cg = cg;
 
@@ -2412,15 +2416,15 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
         early_warn(error_format, "-C remark requires \"-C debuginfo=n\" to show source locations");
     }
 
-    let externs = parse_externs(matches, &debugging_opts, error_format);
+    let externs = parse_externs(matches, &unstable_opts, error_format);
 
     let crate_name = matches.opt_str("crate-name");
 
-    let remap_path_prefix = parse_remap_path_prefix(matches, &debugging_opts, error_format);
+    let remap_path_prefix = parse_remap_path_prefix(matches, &unstable_opts, error_format);
 
-    let pretty = parse_pretty(&debugging_opts, error_format);
+    let pretty = parse_pretty(&unstable_opts, error_format);
 
-    if !debugging_opts.unstable_options
+    if !unstable_opts.unstable_options
         && !target_triple.triple().contains("apple")
         && cg.split_debuginfo.is_some()
     {
@@ -2481,7 +2485,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
         target_triple,
         test,
         incremental,
-        debugging_opts,
+        unstable_opts,
         prints,
         cg,
         error_format,
@@ -2506,10 +2510,10 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
     }
 }
 
-fn parse_pretty(debugging_opts: &DebuggingOptions, efmt: ErrorOutputType) -> Option<PpMode> {
+fn parse_pretty(unstable_opts: &UnstableOptions, efmt: ErrorOutputType) -> Option<PpMode> {
     use PpMode::*;
 
-    let first = match debugging_opts.unpretty.as_deref()? {
+    let first = match unstable_opts.unpretty.as_deref()? {
         "normal" => Source(PpSourceMode::Normal),
         "identified" => Source(PpSourceMode::Identified),
         "expanded" => Source(PpSourceMode::Expanded),
@@ -2707,6 +2711,14 @@ impl PpMode {
             | MirCFG => true,
         }
     }
+    pub fn needs_hir(&self) -> bool {
+        use PpMode::*;
+        match *self {
+            Source(_) | AstTree(_) => false,
+
+            Hir(_) | HirTree | ThirTree | Mir | MirCFG => true,
+        }
+    }
 
     pub fn needs_analysis(&self) -> bool {
         use PpMode::*;
diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs
index 7353c1ca0e2..44cf5048642 100644
--- a/compiler/rustc_session/src/lib.rs
+++ b/compiler/rustc_session/src/lib.rs
@@ -1,5 +1,5 @@
 #![feature(if_let_guard)]
-#![feature(let_chains)]
+#![cfg_attr(bootstrap, feature(let_chains))]
 #![feature(let_else)]
 #![feature(min_specialization)]
 #![feature(never_type)]
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index 8f1057b793f..ef314115043 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -102,28 +102,6 @@ macro_rules! top_level_options {
     );
 }
 
-impl Options {
-    pub fn mir_opt_level(&self) -> usize {
-        self.debugging_opts
-            .mir_opt_level
-            .unwrap_or_else(|| if self.optimize != OptLevel::No { 2 } else { 1 })
-    }
-
-    pub fn instrument_coverage(&self) -> bool {
-        self.cg.instrument_coverage.unwrap_or(InstrumentCoverage::Off) != InstrumentCoverage::Off
-    }
-
-    pub fn instrument_coverage_except_unused_generics(&self) -> bool {
-        self.cg.instrument_coverage.unwrap_or(InstrumentCoverage::Off)
-            == InstrumentCoverage::ExceptUnusedGenerics
-    }
-
-    pub fn instrument_coverage_except_unused_functions(&self) -> bool {
-        self.cg.instrument_coverage.unwrap_or(InstrumentCoverage::Off)
-            == InstrumentCoverage::ExceptUnusedFunctions
-    }
-}
-
 top_level_options!(
     /// The top-level command-line options struct.
     ///
@@ -149,9 +127,11 @@ top_level_options!(
     /// `CodegenOptions`, think about how it influences incremental compilation. If in
     /// doubt, specify `[TRACKED]`, which is always "correct" but might lead to
     /// unnecessary re-compilation.
+    #[cfg_attr(not(bootstrap), rustc_lint_opt_ty)]
     pub struct Options {
         /// The crate config requested for the session, which may be combined
         /// with additional crate configurations during the compile process.
+        #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::crate_types` instead of this field"))]
         crate_types: Vec<CrateType> [TRACKED],
         optimize: OptLevel [TRACKED],
         /// Include the `debug_assertions` flag in dependency tracking, since it
@@ -177,7 +157,7 @@ top_level_options!(
         incremental: Option<PathBuf> [UNTRACKED],
         assert_incr_state: Option<IncrementalStateAssertion> [UNTRACKED],
 
-        debugging_opts: DebuggingOptions [SUBSTRUCT],
+        unstable_opts: UnstableOptions [SUBSTRUCT],
         prints: Vec<PrintRequest> [UNTRACKED],
         cg: CodegenOptions [SUBSTRUCT],
         externs: Externs [UNTRACKED],
@@ -198,7 +178,9 @@ top_level_options!(
         /// what rustc was invoked with, but massaged a bit to agree with
         /// commands like `--emit llvm-ir` which they're often incompatible with
         /// if we otherwise use the defaults of rustc.
+        #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::codegen_units` instead of this field"))]
         cli_forced_codegen_units: Option<usize> [UNTRACKED],
+        #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::lto` instead of this field"))]
         cli_forced_thinlto_off: bool [UNTRACKED],
 
         /// Remap source path prefixes in all output (messages, object files, debug, etc.).
@@ -249,11 +231,12 @@ macro_rules! options {
      ),* ,) =>
 (
     #[derive(Clone)]
-    pub struct $struct_name { $(pub $opt: $t),* }
+    #[cfg_attr(not(bootstrap), rustc_lint_opt_ty)]
+    pub struct $struct_name { $( $( #[$attr] )* pub $opt: $t),* }
 
     impl Default for $struct_name {
         fn default() -> $struct_name {
-            $struct_name { $( $( #[$attr] )* $opt: $init),* }
+            $struct_name { $($opt: $init),* }
         }
     }
 
@@ -297,6 +280,22 @@ macro_rules! options {
 
 ) }
 
+impl Options {
+    // JUSTIFICATION: defn of the suggested wrapper fn
+    #[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))]
+    pub fn time_passes(&self) -> bool {
+        self.unstable_opts.time_passes || self.unstable_opts.time
+    }
+}
+
+impl CodegenOptions {
+    // JUSTIFICATION: defn of the suggested wrapper fn
+    #[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))]
+    pub fn instrument_coverage(&self) -> InstrumentCoverage {
+        self.instrument_coverage.unwrap_or(InstrumentCoverage::Off)
+    }
+}
+
 // Sometimes different options need to build a common structure.
 // That structure can be kept in one of the options' fields, the others become dummy.
 macro_rules! redirect_field {
@@ -377,7 +376,7 @@ mod desc {
     pub const parse_opt_panic_strategy: &str = parse_panic_strategy;
     pub const parse_oom_strategy: &str = "either `panic` or `abort`";
     pub const parse_relro_level: &str = "one of: `full`, `partial`, or `off`";
-    pub const parse_sanitizers: &str = "comma separated list of sanitizers: `address`, `cfi`, `hwaddress`, `leak`, `memory`, `memtag`, or `thread`";
+    pub const parse_sanitizers: &str = "comma separated list of sanitizers: `address`, `cfi`, `hwaddress`, `leak`, `memory`, `memtag`, `shadow-call-stack`, or `thread`";
     pub const parse_sanitizer_memory_track_origins: &str = "0, 1, or 2";
     pub const parse_cfguard: &str =
         "either a boolean (`yes`, `no`, `on`, `off`, etc), `checks`, or `nochecks`";
@@ -535,7 +534,7 @@ mod parse {
     ) -> bool {
         match v {
             Some(s) => {
-                for s in s.split(",") {
+                for s in s.split(',') {
                     let Some(pass_name) = s.strip_prefix(&['+', '-'][..]) else { return false };
                     slot.push((pass_name.to_string(), &s[..1] == "+"));
                 }
@@ -683,6 +682,7 @@ mod parse {
                     "leak" => SanitizerSet::LEAK,
                     "memory" => SanitizerSet::MEMORY,
                     "memtag" => SanitizerSet::MEMTAG,
+                    "shadow-call-stack" => SanitizerSet::SHADOWCALLSTACK,
                     "thread" => SanitizerSet::THREAD,
                     "hwaddress" => SanitizerSet::HWADDRESS,
                     _ => return false,
@@ -1075,6 +1075,7 @@ options! {
 
     ar: String = (String::new(), parse_string, [UNTRACKED],
         "this option is deprecated and does nothing"),
+    #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::code_model` instead of this field"))]
     code_model: Option<CodeModel> = (None, parse_code_model, [TRACKED],
         "choose the code model to use (`rustc --print code-models` for details)"),
     codegen_units: Option<usize> = (None, parse_opt_number, [UNTRACKED],
@@ -1094,12 +1095,14 @@ options! {
         "extra data to put in each output filename"),
     force_frame_pointers: Option<bool> = (None, parse_opt_bool, [TRACKED],
         "force use of the frame pointers"),
+    #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::must_emit_unwind_tables` instead of this field"))]
     force_unwind_tables: Option<bool> = (None, parse_opt_bool, [TRACKED],
         "force use of unwind tables"),
     incremental: Option<String> = (None, parse_opt_string, [UNTRACKED],
         "enable incremental compilation"),
     inline_threshold: Option<u32> = (None, parse_opt_number, [TRACKED],
         "set the threshold for inlining a function"),
+    #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::instrument_coverage` instead of this field"))]
     instrument_coverage: Option<InstrumentCoverage> = (None, parse_instrument_coverage, [TRACKED],
         "instrument the generated code to support LLVM source-based code coverage \
         reports (note, the compiler build config must include `profiler = true`); \
@@ -1112,6 +1115,7 @@ options! {
         "a single extra argument to append to the linker invocation (can be used several times)"),
     link_args: Vec<String> = (Vec::new(), parse_list, [UNTRACKED],
         "extra arguments to append to the linker invocation (space separated)"),
+    #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::link_dead_code` instead of this field"))]
     link_dead_code: Option<bool> = (None, parse_opt_bool, [TRACKED],
         "keep dead code at link time (useful for code coverage) (default: no)"),
     link_self_contained: Option<bool> = (None, parse_opt_bool, [UNTRACKED],
@@ -1126,6 +1130,7 @@ options! {
         "generate build artifacts that are compatible with linker-based LTO"),
     llvm_args: Vec<String> = (Vec::new(), parse_list, [TRACKED],
         "a list of arguments to pass to LLVM (space separated)"),
+    #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::lto` instead of this field"))]
     lto: LtoCli = (LtoCli::Unspecified, parse_lto, [TRACKED],
         "perform LLVM link-time optimizations"),
     metadata: Vec<String> = (Vec::new(), parse_list, [TRACKED],
@@ -1142,8 +1147,10 @@ options! {
         "disable LLVM's SLP vectorization pass"),
     opt_level: String = ("0".to_string(), parse_string, [TRACKED],
         "optimization level (0-3, s, or z; default: 0)"),
+    #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::overflow_checks` instead of this field"))]
     overflow_checks: Option<bool> = (None, parse_opt_bool, [TRACKED],
         "use overflow checks for integer arithmetic"),
+    #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::panic_strategy` instead of this field"))]
     panic: Option<PanicStrategy> = (None, parse_opt_panic_strategy, [TRACKED],
         "panic strategy to compile crate with"),
     passes: Vec<String> = (Vec::new(), parse_list, [TRACKED],
@@ -1155,6 +1162,7 @@ options! {
         "compile the program with profiling instrumentation"),
     profile_use: Option<PathBuf> = (None, parse_opt_pathbuf, [TRACKED],
         "use the given `.profdata` file for profile-guided optimization"),
+    #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::relocation_model` instead of this field"))]
     relocation_model: Option<RelocModel> = (None, parse_relocation_model, [TRACKED],
         "control generation of position-independent code (PIC) \
         (`rustc --print relocation-models` for details)"),
@@ -1166,6 +1174,7 @@ options! {
         "save all temporary output files during compilation (default: no)"),
     soft_float: bool = (false, parse_bool, [TRACKED],
         "use soft float ABI (*eabihf targets only) (default: no)"),
+    #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::split_debuginfo` instead of this field"))]
     split_debuginfo: Option<SplitDebuginfo> = (None, parse_split_debuginfo, [TRACKED],
         "how to handle split-debuginfo, a platform-specific option"),
     strip: Strip = (Strip::None, parse_strip, [UNTRACKED],
@@ -1187,12 +1196,13 @@ options! {
 }
 
 options! {
-    DebuggingOptions, DB_OPTIONS, dbopts, "Z", "debugging",
+    UnstableOptions, Z_OPTIONS, dbopts, "Z", "unstable",
 
     // This list is in alphabetical order.
     //
     // If you add a new option, please update:
     // - compiler/rustc_interface/src/tests.rs
+    // - src/doc/unstable-book/src/compiler-flags
 
     allow_features: Option<Vec<String>> = (None, parse_opt_comma_list, [TRACKED],
         "only allow the listed language features to be enabled in code (space separated)"),
@@ -1200,14 +1210,18 @@ options! {
         "encode MIR of all functions into the crate metadata (default: no)"),
     assume_incomplete_release: bool = (false, parse_bool, [TRACKED],
         "make cfg(version) treat the current version as incomplete (default: no)"),
+    #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::asm_comments` instead of this field"))]
     asm_comments: bool = (false, parse_bool, [TRACKED],
         "generate comments into the assembly (may change behavior) (default: no)"),
     assert_incr_state: Option<String> = (None, parse_opt_string, [UNTRACKED],
         "assert that the incremental cache is in given state: \
          either `loaded` or `not-loaded`."),
+    #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::binary_dep_depinfo` instead of this field"))]
     binary_dep_depinfo: bool = (false, parse_bool, [TRACKED],
         "include artifacts (sysroot, crate dependencies) used during compilation in dep-info \
         (default: no)"),
+    box_noalias: Option<bool> = (None, parse_opt_bool, [TRACKED],
+        "emit noalias metadata for box (default: yes)"),
     branch_protection: Option<BranchProtection> = (None, parse_branch_protection, [TRACKED],
         "set options for branch target identification and pointer authentication on AArch64"),
     cf_protection: CFProtection = (CFProtection::None, parse_cfprotection, [TRACKED],
@@ -1276,6 +1290,11 @@ options! {
         "version of DWARF debug information to emit (default: 2 or 4, depending on platform)"),
     emit_stack_sizes: bool = (false, parse_bool, [UNTRACKED],
         "emit a section containing stack size metadata (default: no)"),
+    emit_thin_lto: bool = (true, parse_bool, [TRACKED],
+        "emit the bc module with thin LTO info (default: yes)"),
+    export_executable_symbols: bool = (false, parse_bool, [TRACKED],
+        "export symbols from executables, as if they were dynamic libraries"),
+    #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::fewer_names` instead of this field"))]
     fewer_names: Option<bool> = (None, parse_opt_bool, [TRACKED],
         "reduce memory use by retaining fewer names within compilation artifacts (LLVM-IR) \
         (default: no)"),
@@ -1318,6 +1337,7 @@ options! {
         "control whether `#[inline]` functions are in all CGUs"),
     input_stats: bool = (false, parse_bool, [UNTRACKED],
         "gather statistics about the input (default: no)"),
+    #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::instrument_coverage` instead of this field"))]
     instrument_coverage: Option<InstrumentCoverage> = (None, parse_instrument_coverage, [TRACKED],
         "instrument the generated code to support LLVM source-based code coverage \
         reports (note, the compiler build config must include `profiler = true`); \
@@ -1326,6 +1346,7 @@ options! {
         `=except-unused-generics`
         `=except-unused-functions`
         `=off` (default)"),
+    #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::instrument_mcount` instead of this field"))]
     instrument_mcount: bool = (false, parse_bool, [TRACKED],
         "insert function instrument code for mcount-based tracing (default: no)"),
     keep_hygiene_data: bool = (false, parse_bool, [UNTRACKED],
@@ -1348,6 +1369,7 @@ options! {
     merge_functions: Option<MergeFunctions> = (None, parse_merge_functions, [TRACKED],
         "control the operation of the MergeFunctions LLVM pass, taking \
         the same values as the target option of the same name"),
+    #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::meta_stats` instead of this field"))]
     meta_stats: bool = (false, parse_bool, [UNTRACKED],
         "gather metadata statistics (default: no)"),
     mir_emit_retag: bool = (false, parse_bool, [TRACKED],
@@ -1357,6 +1379,9 @@ options! {
         "use like `-Zmir-enable-passes=+DestProp,-InstCombine`. Forces the specified passes to be \
         enabled, overriding all other checks. Passes that are not specified are enabled or \
         disabled by other flags as usual."),
+    mir_pretty_relative_line_numbers: bool = (false, parse_bool, [UNTRACKED],
+        "use line numbers relative to the function in mir pretty printing"),
+    #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::mir_opt_level` instead of this field"))]
     mir_opt_level: Option<usize> = (None, parse_opt_number, [TRACKED],
         "MIR optimization level (0-4; default: 1 in non optimized builds and 2 in optimized builds)"),
     move_size_limit: Option<usize> = (None, parse_opt_number, [TRACKED],
@@ -1423,6 +1448,7 @@ options! {
         See #77382 and #74551."),
     print_fuel: Option<String> = (None, parse_opt_string, [TRACKED],
         "make rustc print the total optimization fuel used by a crate"),
+    #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::print_llvm_passes` instead of this field"))]
     print_llvm_passes: bool = (false, parse_bool, [UNTRACKED],
         "print the LLVM optimization passes being run (default: no)"),
     print_mono_items: Option<String> = (None, parse_opt_string, [UNTRACKED],
@@ -1497,6 +1523,7 @@ options! {
         "exclude spans when debug-printing compiler state (default: no)"),
     src_hash_algorithm: Option<SourceFileHashAlgorithm> = (None, parse_src_file_hash, [TRACKED],
         "hash algorithm of source files in debug info (`md5`, `sha1`, or `sha256`)"),
+    #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::stack_protector` instead of this field"))]
     stack_protector: StackProtector = (StackProtector::None, parse_stack_protector, [TRACKED],
         "control stack smash protection strategy (`rustc --print stack-protector-strategies` for details)"),
     strict_init_checks: bool = (false, parse_bool, [TRACKED],
@@ -1517,6 +1544,7 @@ options! {
     symbol_mangling_version: Option<SymbolManglingVersion> = (None,
         parse_symbol_mangling_version, [TRACKED],
         "which mangling version to use for symbol names ('legacy' (default) or 'v0')"),
+    #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::teach` instead of this field"))]
     teach: bool = (false, parse_bool, [TRACKED],
         "show extended diagnostic help (default: no)"),
     temps_dir: Option<String> = (None, parse_opt_string, [UNTRACKED],
@@ -1532,6 +1560,7 @@ options! {
         "emit directionality isolation markers in translated diagnostics"),
     tune_cpu: Option<String> = (None, parse_opt_string, [TRACKED],
         "select processor to schedule for (`rustc --print target-cpus` for details)"),
+    #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::lto` instead of this field"))]
     thinlto: Option<bool> = (None, parse_opt_bool, [TRACKED],
         "enable ThinLTO when possible"),
     thir_unsafeck: bool = (false, parse_bool, [TRACKED],
@@ -1540,14 +1569,19 @@ options! {
     /// a sequential compiler for now. This'll likely be adjusted
     /// in the future. Note that -Zthreads=0 is the way to get
     /// the num_cpus behavior.
+    #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::threads` instead of this field"))]
     threads: usize = (1, parse_threads, [UNTRACKED],
         "use a thread pool with N threads"),
+    #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::time_passes` instead of this field"))]
     time: bool = (false, parse_bool, [UNTRACKED],
         "measure time of rustc processes (default: no)"),
+    #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::time_llvm_passes` instead of this field"))]
     time_llvm_passes: bool = (false, parse_bool, [UNTRACKED],
         "measure time of each LLVM pass (default: no)"),
+    #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::time_passes` instead of this field"))]
     time_passes: bool = (false, parse_bool, [UNTRACKED],
         "measure time of each rustc pass (default: no)"),
+    #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::tls_model` instead of this field"))]
     tls_model: Option<TlsModel> = (None, parse_tls_model, [TRACKED],
         "choose the TLS model to use (`rustc --print tls-models` for details)"),
     trace_macros: bool = (false, parse_bool, [UNTRACKED],
@@ -1580,14 +1614,19 @@ options! {
         `mir` (the MIR), or `mir-cfg` (graphviz formatted MIR)"),
     unsound_mir_opts: bool = (false, parse_bool, [TRACKED],
         "enable unsound and buggy MIR optimizations (default: no)"),
+    /// This name is kind of confusing: Most unstable options enable something themselves, while
+    /// this just allows "normal" options to be feature-gated.
+    #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::unstable_options` instead of this field"))]
     unstable_options: bool = (false, parse_bool, [UNTRACKED],
         "adds unstable command line options to rustc interface (default: no)"),
     use_ctors_section: Option<bool> = (None, parse_opt_bool, [TRACKED],
         "use legacy .ctors section for initializers rather than .init_array"),
     validate_mir: bool = (false, parse_bool, [UNTRACKED],
         "validate MIR after each transformation"),
+    #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::verbose` instead of this field"))]
     verbose: bool = (false, parse_bool, [UNTRACKED],
         "in general, enable more debug printouts (default: no)"),
+    #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::verify_llvm_ir` instead of this field"))]
     verify_llvm_ir: bool = (false, parse_bool, [TRACKED],
         "verify LLVM IR (default: no)"),
     virtual_function_elimination: bool = (false, parse_bool, [TRACKED],
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index 1cccef2f64f..9669287b3f3 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -1,7 +1,7 @@
 use crate::cgu_reuse_tracker::CguReuseTracker;
 use crate::code_stats::CodeStats;
 pub use crate::code_stats::{DataTypeKind, FieldInfo, SizeKind, VariantInfo};
-use crate::config::{self, CrateType, OutputType, SwitchWithOptPath};
+use crate::config::{self, CrateType, InstrumentCoverage, OptLevel, OutputType, SwitchWithOptPath};
 use crate::parse::{add_feature_diagnostics, ParseSess};
 use crate::search_paths::{PathKind, SearchPath};
 use crate::{filesearch, lint};
@@ -194,6 +194,9 @@ pub struct Session {
 
     /// Set of enabled features for the current target.
     pub target_features: FxHashSet<Symbol>,
+
+    /// Set of enabled features for the current target, including unstable ones.
+    pub unstable_target_features: FxHashSet<Symbol>,
 }
 
 pub struct PerfStats {
@@ -541,10 +544,10 @@ impl Session {
     /// warnings or errors are emitted. If no messages are emitted ("good path"), then
     /// it's likely a bug.
     pub fn delay_good_path_bug(&self, msg: impl Into<DiagnosticMessage>) {
-        if self.opts.debugging_opts.print_type_sizes
-            || self.opts.debugging_opts.query_dep_graph
-            || self.opts.debugging_opts.dump_mir.is_some()
-            || self.opts.debugging_opts.unpretty.is_some()
+        if self.opts.unstable_opts.print_type_sizes
+            || self.opts.unstable_opts.query_dep_graph
+            || self.opts.unstable_opts.dump_mir.is_some()
+            || self.opts.unstable_opts.unpretty.is_some()
             || self.opts.output_types.contains_key(&OutputType::Mir)
             || std::env::var_os("RUSTC_LOG").is_some()
         {
@@ -580,35 +583,28 @@ impl Session {
     pub fn source_map(&self) -> &SourceMap {
         self.parse_sess.source_map()
     }
-    pub fn verbose(&self) -> bool {
-        self.opts.debugging_opts.verbose
-    }
+
     pub fn time_passes(&self) -> bool {
-        self.opts.debugging_opts.time_passes || self.opts.debugging_opts.time
-    }
-    pub fn instrument_mcount(&self) -> bool {
-        self.opts.debugging_opts.instrument_mcount
-    }
-    pub fn time_llvm_passes(&self) -> bool {
-        self.opts.debugging_opts.time_llvm_passes
+        self.opts.time_passes()
     }
-    pub fn meta_stats(&self) -> bool {
-        self.opts.debugging_opts.meta_stats
-    }
-    pub fn asm_comments(&self) -> bool {
-        self.opts.debugging_opts.asm_comments
-    }
-    pub fn verify_llvm_ir(&self) -> bool {
-        self.opts.debugging_opts.verify_llvm_ir || option_env!("RUSTC_VERIFY_LLVM_IR").is_some()
+
+    /// Returns `true` if internal lints should be added to the lint store - i.e. if
+    /// `-Zunstable-options` is provided and this isn't rustdoc (internal lints can trigger errors
+    /// to be emitted under rustdoc).
+    pub fn enable_internal_lints(&self) -> bool {
+        self.unstable_options() && !self.opts.actually_rustdoc
     }
-    pub fn print_llvm_passes(&self) -> bool {
-        self.opts.debugging_opts.print_llvm_passes
+
+    pub fn instrument_coverage(&self) -> bool {
+        self.opts.cg.instrument_coverage() != InstrumentCoverage::Off
     }
-    pub fn binary_dep_depinfo(&self) -> bool {
-        self.opts.debugging_opts.binary_dep_depinfo
+
+    pub fn instrument_coverage_except_unused_generics(&self) -> bool {
+        self.opts.cg.instrument_coverage() == InstrumentCoverage::ExceptUnusedGenerics
     }
-    pub fn mir_opt_level(&self) -> usize {
-        self.opts.mir_opt_level()
+
+    pub fn instrument_coverage_except_unused_functions(&self) -> bool {
+        self.opts.cg.instrument_coverage() == InstrumentCoverage::ExceptUnusedFunctions
     }
 
     /// Gets the features enabled for the current compilation session.
@@ -626,102 +622,8 @@ impl Session {
         }
     }
 
-    /// Calculates the flavor of LTO to use for this compilation.
-    pub fn lto(&self) -> config::Lto {
-        // If our target has codegen requirements ignore the command line
-        if self.target.requires_lto {
-            return config::Lto::Fat;
-        }
-
-        // If the user specified something, return that. If they only said `-C
-        // lto` and we've for whatever reason forced off ThinLTO via the CLI,
-        // then ensure we can't use a ThinLTO.
-        match self.opts.cg.lto {
-            config::LtoCli::Unspecified => {
-                // The compiler was invoked without the `-Clto` flag. Fall
-                // through to the default handling
-            }
-            config::LtoCli::No => {
-                // The user explicitly opted out of any kind of LTO
-                return config::Lto::No;
-            }
-            config::LtoCli::Yes | config::LtoCli::Fat | config::LtoCli::NoParam => {
-                // All of these mean fat LTO
-                return config::Lto::Fat;
-            }
-            config::LtoCli::Thin => {
-                return if self.opts.cli_forced_thinlto_off {
-                    config::Lto::Fat
-                } else {
-                    config::Lto::Thin
-                };
-            }
-        }
-
-        // Ok at this point the target doesn't require anything and the user
-        // hasn't asked for anything. Our next decision is whether or not
-        // we enable "auto" ThinLTO where we use multiple codegen units and
-        // then do ThinLTO over those codegen units. The logic below will
-        // either return `No` or `ThinLocal`.
-
-        // If processing command line options determined that we're incompatible
-        // with ThinLTO (e.g., `-C lto --emit llvm-ir`) then return that option.
-        if self.opts.cli_forced_thinlto_off {
-            return config::Lto::No;
-        }
-
-        // If `-Z thinlto` specified process that, but note that this is mostly
-        // a deprecated option now that `-C lto=thin` exists.
-        if let Some(enabled) = self.opts.debugging_opts.thinlto {
-            if enabled {
-                return config::Lto::ThinLocal;
-            } else {
-                return config::Lto::No;
-            }
-        }
-
-        // If there's only one codegen unit and LTO isn't enabled then there's
-        // no need for ThinLTO so just return false.
-        if self.codegen_units() == 1 {
-            return config::Lto::No;
-        }
-
-        // Now we're in "defaults" territory. By default we enable ThinLTO for
-        // optimized compiles (anything greater than O0).
-        match self.opts.optimize {
-            config::OptLevel::No => config::Lto::No,
-            _ => config::Lto::ThinLocal,
-        }
-    }
-
-    /// Returns the panic strategy for this compile session. If the user explicitly selected one
-    /// using '-C panic', use that, otherwise use the panic strategy defined by the target.
-    pub fn panic_strategy(&self) -> PanicStrategy {
-        self.opts.cg.panic.unwrap_or(self.target.panic_strategy)
-    }
-    pub fn fewer_names(&self) -> bool {
-        if let Some(fewer_names) = self.opts.debugging_opts.fewer_names {
-            fewer_names
-        } else {
-            let more_names = self.opts.output_types.contains_key(&OutputType::LlvmAssembly)
-                || self.opts.output_types.contains_key(&OutputType::Bitcode)
-                // AddressSanitizer and MemorySanitizer use alloca name when reporting an issue.
-                || self.opts.debugging_opts.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::MEMORY);
-            !more_names
-        }
-    }
-
-    pub fn unstable_options(&self) -> bool {
-        self.opts.debugging_opts.unstable_options
-    }
-    pub fn is_nightly_build(&self) -> bool {
-        self.opts.unstable_features.is_nightly_build()
-    }
     pub fn is_sanitizer_cfi_enabled(&self) -> bool {
-        self.opts.debugging_opts.sanitizer.contains(SanitizerSet::CFI)
-    }
-    pub fn overflow_checks(&self) -> bool {
-        self.opts.cg.overflow_checks.unwrap_or(self.opts.debug_assertions)
+        self.opts.unstable_opts.sanitizer.contains(SanitizerSet::CFI)
     }
 
     /// Check whether this compile session and crate type use static crt.
@@ -735,6 +637,8 @@ impl Session {
         let found_negative = requested_features.clone().any(|r| r == "-crt-static");
         let found_positive = requested_features.clone().any(|r| r == "+crt-static");
 
+        // JUSTIFICATION: necessary use of crate_types directly (see FIXME below)
+        #[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))]
         if found_positive || found_negative {
             found_positive
         } else if crate_type == Some(CrateType::ProcMacro)
@@ -749,69 +653,18 @@ impl Session {
         }
     }
 
-    pub fn relocation_model(&self) -> RelocModel {
-        self.opts.cg.relocation_model.unwrap_or(self.target.relocation_model)
-    }
-
-    pub fn code_model(&self) -> Option<CodeModel> {
-        self.opts.cg.code_model.or(self.target.code_model)
-    }
-
-    pub fn tls_model(&self) -> TlsModel {
-        self.opts.debugging_opts.tls_model.unwrap_or(self.target.tls_model)
-    }
-
     pub fn is_wasi_reactor(&self) -> bool {
         self.target.options.os == "wasi"
             && matches!(
-                self.opts.debugging_opts.wasi_exec_model,
+                self.opts.unstable_opts.wasi_exec_model,
                 Some(config::WasiExecModel::Reactor)
             )
     }
 
-    pub fn split_debuginfo(&self) -> SplitDebuginfo {
-        self.opts.cg.split_debuginfo.unwrap_or(self.target.split_debuginfo)
-    }
-
-    pub fn stack_protector(&self) -> StackProtector {
-        if self.target.options.supports_stack_protector {
-            self.opts.debugging_opts.stack_protector
-        } else {
-            StackProtector::None
-        }
-    }
-
     pub fn target_can_use_split_dwarf(&self) -> bool {
         !self.target.is_like_windows && !self.target.is_like_osx
     }
 
-    pub fn must_emit_unwind_tables(&self) -> bool {
-        // This is used to control the emission of the `uwtable` attribute on
-        // LLVM functions.
-        //
-        // Unwind tables are needed when compiling with `-C panic=unwind`, but
-        // LLVM won't omit unwind tables unless the function is also marked as
-        // `nounwind`, so users are allowed to disable `uwtable` emission.
-        // Historically rustc always emits `uwtable` attributes by default, so
-        // even they can be disabled, they're still emitted by default.
-        //
-        // On some targets (including windows), however, exceptions include
-        // other events such as illegal instructions, segfaults, etc. This means
-        // that on Windows we end up still needing unwind tables even if the `-C
-        // panic=abort` flag is passed.
-        //
-        // You can also find more info on why Windows needs unwind tables in:
-        //      https://bugzilla.mozilla.org/show_bug.cgi?id=1302078
-        //
-        // If a target requires unwind tables, then they must be emitted.
-        // Otherwise, we can defer to the `-C force-unwind-tables=<yes/no>`
-        // value, if it is provided, or disable them, if not.
-        self.target.requires_uwtable
-            || self.opts.cg.force_unwind_tables.unwrap_or(
-                self.panic_strategy() == PanicStrategy::Unwind || self.target.default_uwtable,
-            )
-    }
-
     pub fn generate_proc_macro_decls_symbol(&self, stable_crate_id: StableCrateId) -> String {
         format!("__rustc_proc_macro_decls_{:08x}__", stable_crate_id.to_u64())
     }
@@ -930,7 +783,7 @@ impl Session {
     /// This expends fuel if applicable, and records fuel if applicable.
     pub fn consider_optimizing<T: Fn() -> String>(&self, crate_name: &str, msg: T) -> bool {
         let mut ret = true;
-        if let Some((ref c, _)) = self.opts.debugging_opts.fuel {
+        if let Some((ref c, _)) = self.opts.unstable_opts.fuel {
             if c == crate_name {
                 assert_eq!(self.threads(), 1);
                 let mut fuel = self.optimization_fuel.lock();
@@ -948,7 +801,7 @@ impl Session {
                 }
             }
         }
-        if let Some(ref c) = self.opts.debugging_opts.print_fuel {
+        if let Some(ref c) = self.opts.unstable_opts.print_fuel {
             if c == crate_name {
                 assert_eq!(self.threads(), 1);
                 self.print_fuel.fetch_add(1, SeqCst);
@@ -957,10 +810,284 @@ impl Session {
         ret
     }
 
+    pub fn rust_2015(&self) -> bool {
+        self.edition() == Edition::Edition2015
+    }
+
+    /// Are we allowed to use features from the Rust 2018 edition?
+    pub fn rust_2018(&self) -> bool {
+        self.edition() >= Edition::Edition2018
+    }
+
+    /// Are we allowed to use features from the Rust 2021 edition?
+    pub fn rust_2021(&self) -> bool {
+        self.edition() >= Edition::Edition2021
+    }
+
+    /// Are we allowed to use features from the Rust 2024 edition?
+    pub fn rust_2024(&self) -> bool {
+        self.edition() >= Edition::Edition2024
+    }
+
+    /// Returns `true` if we cannot skip the PLT for shared library calls.
+    pub fn needs_plt(&self) -> bool {
+        // Check if the current target usually needs PLT to be enabled.
+        // The user can use the command line flag to override it.
+        let needs_plt = self.target.needs_plt;
+
+        let dbg_opts = &self.opts.unstable_opts;
+
+        let relro_level = dbg_opts.relro_level.unwrap_or(self.target.relro_level);
+
+        // Only enable this optimization by default if full relro is also enabled.
+        // In this case, lazy binding was already unavailable, so nothing is lost.
+        // This also ensures `-Wl,-z,now` is supported by the linker.
+        let full_relro = RelroLevel::Full == relro_level;
+
+        // If user didn't explicitly forced us to use / skip the PLT,
+        // then try to skip it where possible.
+        dbg_opts.plt.unwrap_or(needs_plt || !full_relro)
+    }
+
+    /// Checks if LLVM lifetime markers should be emitted.
+    pub fn emit_lifetime_markers(&self) -> bool {
+        self.opts.optimize != config::OptLevel::No
+        // AddressSanitizer uses lifetimes to detect use after scope bugs.
+        // MemorySanitizer uses lifetimes to detect use of uninitialized stack variables.
+        // HWAddressSanitizer will use lifetimes to detect use after scope bugs in the future.
+        || self.opts.unstable_opts.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::MEMORY | SanitizerSet::HWADDRESS)
+    }
+
+    pub fn is_proc_macro_attr(&self, attr: &Attribute) -> bool {
+        [sym::proc_macro, sym::proc_macro_attribute, sym::proc_macro_derive]
+            .iter()
+            .any(|kind| attr.has_name(*kind))
+    }
+
+    pub fn contains_name(&self, attrs: &[Attribute], name: Symbol) -> bool {
+        attrs.iter().any(|item| item.has_name(name))
+    }
+
+    pub fn find_by_name<'a>(
+        &'a self,
+        attrs: &'a [Attribute],
+        name: Symbol,
+    ) -> Option<&'a Attribute> {
+        attrs.iter().find(|attr| attr.has_name(name))
+    }
+
+    pub fn filter_by_name<'a>(
+        &'a self,
+        attrs: &'a [Attribute],
+        name: Symbol,
+    ) -> impl Iterator<Item = &'a Attribute> {
+        attrs.iter().filter(move |attr| attr.has_name(name))
+    }
+
+    pub fn first_attr_value_str_by_name(
+        &self,
+        attrs: &[Attribute],
+        name: Symbol,
+    ) -> Option<Symbol> {
+        attrs.iter().find(|at| at.has_name(name)).and_then(|at| at.value_str())
+    }
+}
+
+// JUSTIFICATION: defn of the suggested wrapper fns
+#[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))]
+impl Session {
+    pub fn verbose(&self) -> bool {
+        self.opts.unstable_opts.verbose
+    }
+
+    pub fn instrument_mcount(&self) -> bool {
+        self.opts.unstable_opts.instrument_mcount
+    }
+
+    pub fn time_llvm_passes(&self) -> bool {
+        self.opts.unstable_opts.time_llvm_passes
+    }
+
+    pub fn meta_stats(&self) -> bool {
+        self.opts.unstable_opts.meta_stats
+    }
+
+    pub fn asm_comments(&self) -> bool {
+        self.opts.unstable_opts.asm_comments
+    }
+
+    pub fn verify_llvm_ir(&self) -> bool {
+        self.opts.unstable_opts.verify_llvm_ir || option_env!("RUSTC_VERIFY_LLVM_IR").is_some()
+    }
+
+    pub fn print_llvm_passes(&self) -> bool {
+        self.opts.unstable_opts.print_llvm_passes
+    }
+
+    pub fn binary_dep_depinfo(&self) -> bool {
+        self.opts.unstable_opts.binary_dep_depinfo
+    }
+
+    pub fn mir_opt_level(&self) -> usize {
+        self.opts
+            .unstable_opts
+            .mir_opt_level
+            .unwrap_or_else(|| if self.opts.optimize != OptLevel::No { 2 } else { 1 })
+    }
+
+    /// Calculates the flavor of LTO to use for this compilation.
+    pub fn lto(&self) -> config::Lto {
+        // If our target has codegen requirements ignore the command line
+        if self.target.requires_lto {
+            return config::Lto::Fat;
+        }
+
+        // If the user specified something, return that. If they only said `-C
+        // lto` and we've for whatever reason forced off ThinLTO via the CLI,
+        // then ensure we can't use a ThinLTO.
+        match self.opts.cg.lto {
+            config::LtoCli::Unspecified => {
+                // The compiler was invoked without the `-Clto` flag. Fall
+                // through to the default handling
+            }
+            config::LtoCli::No => {
+                // The user explicitly opted out of any kind of LTO
+                return config::Lto::No;
+            }
+            config::LtoCli::Yes | config::LtoCli::Fat | config::LtoCli::NoParam => {
+                // All of these mean fat LTO
+                return config::Lto::Fat;
+            }
+            config::LtoCli::Thin => {
+                return if self.opts.cli_forced_thinlto_off {
+                    config::Lto::Fat
+                } else {
+                    config::Lto::Thin
+                };
+            }
+        }
+
+        // Ok at this point the target doesn't require anything and the user
+        // hasn't asked for anything. Our next decision is whether or not
+        // we enable "auto" ThinLTO where we use multiple codegen units and
+        // then do ThinLTO over those codegen units. The logic below will
+        // either return `No` or `ThinLocal`.
+
+        // If processing command line options determined that we're incompatible
+        // with ThinLTO (e.g., `-C lto --emit llvm-ir`) then return that option.
+        if self.opts.cli_forced_thinlto_off {
+            return config::Lto::No;
+        }
+
+        // If `-Z thinlto` specified process that, but note that this is mostly
+        // a deprecated option now that `-C lto=thin` exists.
+        if let Some(enabled) = self.opts.unstable_opts.thinlto {
+            if enabled {
+                return config::Lto::ThinLocal;
+            } else {
+                return config::Lto::No;
+            }
+        }
+
+        // If there's only one codegen unit and LTO isn't enabled then there's
+        // no need for ThinLTO so just return false.
+        if self.codegen_units() == 1 {
+            return config::Lto::No;
+        }
+
+        // Now we're in "defaults" territory. By default we enable ThinLTO for
+        // optimized compiles (anything greater than O0).
+        match self.opts.optimize {
+            config::OptLevel::No => config::Lto::No,
+            _ => config::Lto::ThinLocal,
+        }
+    }
+
+    /// Returns the panic strategy for this compile session. If the user explicitly selected one
+    /// using '-C panic', use that, otherwise use the panic strategy defined by the target.
+    pub fn panic_strategy(&self) -> PanicStrategy {
+        self.opts.cg.panic.unwrap_or(self.target.panic_strategy)
+    }
+
+    pub fn fewer_names(&self) -> bool {
+        if let Some(fewer_names) = self.opts.unstable_opts.fewer_names {
+            fewer_names
+        } else {
+            let more_names = self.opts.output_types.contains_key(&OutputType::LlvmAssembly)
+                || self.opts.output_types.contains_key(&OutputType::Bitcode)
+                // AddressSanitizer and MemorySanitizer use alloca name when reporting an issue.
+                || self.opts.unstable_opts.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::MEMORY);
+            !more_names
+        }
+    }
+
+    pub fn unstable_options(&self) -> bool {
+        self.opts.unstable_opts.unstable_options
+    }
+
+    pub fn is_nightly_build(&self) -> bool {
+        self.opts.unstable_features.is_nightly_build()
+    }
+
+    pub fn overflow_checks(&self) -> bool {
+        self.opts.cg.overflow_checks.unwrap_or(self.opts.debug_assertions)
+    }
+
+    pub fn relocation_model(&self) -> RelocModel {
+        self.opts.cg.relocation_model.unwrap_or(self.target.relocation_model)
+    }
+
+    pub fn code_model(&self) -> Option<CodeModel> {
+        self.opts.cg.code_model.or(self.target.code_model)
+    }
+
+    pub fn tls_model(&self) -> TlsModel {
+        self.opts.unstable_opts.tls_model.unwrap_or(self.target.tls_model)
+    }
+
+    pub fn split_debuginfo(&self) -> SplitDebuginfo {
+        self.opts.cg.split_debuginfo.unwrap_or(self.target.split_debuginfo)
+    }
+
+    pub fn stack_protector(&self) -> StackProtector {
+        if self.target.options.supports_stack_protector {
+            self.opts.unstable_opts.stack_protector
+        } else {
+            StackProtector::None
+        }
+    }
+
+    pub fn must_emit_unwind_tables(&self) -> bool {
+        // This is used to control the emission of the `uwtable` attribute on
+        // LLVM functions.
+        //
+        // Unwind tables are needed when compiling with `-C panic=unwind`, but
+        // LLVM won't omit unwind tables unless the function is also marked as
+        // `nounwind`, so users are allowed to disable `uwtable` emission.
+        // Historically rustc always emits `uwtable` attributes by default, so
+        // even they can be disabled, they're still emitted by default.
+        //
+        // On some targets (including windows), however, exceptions include
+        // other events such as illegal instructions, segfaults, etc. This means
+        // that on Windows we end up still needing unwind tables even if the `-C
+        // panic=abort` flag is passed.
+        //
+        // You can also find more info on why Windows needs unwind tables in:
+        //      https://bugzilla.mozilla.org/show_bug.cgi?id=1302078
+        //
+        // If a target requires unwind tables, then they must be emitted.
+        // Otherwise, we can defer to the `-C force-unwind-tables=<yes/no>`
+        // value, if it is provided, or disable them, if not.
+        self.target.requires_uwtable
+            || self.opts.cg.force_unwind_tables.unwrap_or(
+                self.panic_strategy() == PanicStrategy::Unwind || self.target.default_uwtable,
+            )
+    }
+
     /// Returns the number of query threads that should be used for this
     /// compilation
     pub fn threads(&self) -> usize {
-        self.opts.debugging_opts.threads
+        self.opts.unstable_opts.threads
     }
 
     /// Returns the number of codegen units that should be used for this
@@ -1034,112 +1161,20 @@ impl Session {
     }
 
     pub fn teach(&self, code: &DiagnosticId) -> bool {
-        self.opts.debugging_opts.teach && self.diagnostic().must_teach(code)
-    }
-
-    pub fn rust_2015(&self) -> bool {
-        self.opts.edition == Edition::Edition2015
-    }
-
-    /// Are we allowed to use features from the Rust 2018 edition?
-    pub fn rust_2018(&self) -> bool {
-        self.opts.edition >= Edition::Edition2018
-    }
-
-    /// Are we allowed to use features from the Rust 2021 edition?
-    pub fn rust_2021(&self) -> bool {
-        self.opts.edition >= Edition::Edition2021
-    }
-
-    /// Are we allowed to use features from the Rust 2024 edition?
-    pub fn rust_2024(&self) -> bool {
-        self.opts.edition >= Edition::Edition2024
+        self.opts.unstable_opts.teach && self.diagnostic().must_teach(code)
     }
 
     pub fn edition(&self) -> Edition {
         self.opts.edition
     }
 
-    /// Returns `true` if we cannot skip the PLT for shared library calls.
-    pub fn needs_plt(&self) -> bool {
-        // Check if the current target usually needs PLT to be enabled.
-        // The user can use the command line flag to override it.
-        let needs_plt = self.target.needs_plt;
-
-        let dbg_opts = &self.opts.debugging_opts;
-
-        let relro_level = dbg_opts.relro_level.unwrap_or(self.target.relro_level);
-
-        // Only enable this optimization by default if full relro is also enabled.
-        // In this case, lazy binding was already unavailable, so nothing is lost.
-        // This also ensures `-Wl,-z,now` is supported by the linker.
-        let full_relro = RelroLevel::Full == relro_level;
-
-        // If user didn't explicitly forced us to use / skip the PLT,
-        // then try to skip it where possible.
-        dbg_opts.plt.unwrap_or(needs_plt || !full_relro)
-    }
-
-    /// Checks if LLVM lifetime markers should be emitted.
-    pub fn emit_lifetime_markers(&self) -> bool {
-        self.opts.optimize != config::OptLevel::No
-        // AddressSanitizer uses lifetimes to detect use after scope bugs.
-        // MemorySanitizer uses lifetimes to detect use of uninitialized stack variables.
-        // HWAddressSanitizer will use lifetimes to detect use after scope bugs in the future.
-        || self.opts.debugging_opts.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::MEMORY | SanitizerSet::HWADDRESS)
-    }
-
     pub fn link_dead_code(&self) -> bool {
         self.opts.cg.link_dead_code.unwrap_or(false)
     }
-
-    pub fn instrument_coverage(&self) -> bool {
-        self.opts.instrument_coverage()
-    }
-
-    pub fn instrument_coverage_except_unused_generics(&self) -> bool {
-        self.opts.instrument_coverage_except_unused_generics()
-    }
-
-    pub fn instrument_coverage_except_unused_functions(&self) -> bool {
-        self.opts.instrument_coverage_except_unused_functions()
-    }
-
-    pub fn is_proc_macro_attr(&self, attr: &Attribute) -> bool {
-        [sym::proc_macro, sym::proc_macro_attribute, sym::proc_macro_derive]
-            .iter()
-            .any(|kind| attr.has_name(*kind))
-    }
-
-    pub fn contains_name(&self, attrs: &[Attribute], name: Symbol) -> bool {
-        attrs.iter().any(|item| item.has_name(name))
-    }
-
-    pub fn find_by_name<'a>(
-        &'a self,
-        attrs: &'a [Attribute],
-        name: Symbol,
-    ) -> Option<&'a Attribute> {
-        attrs.iter().find(|attr| attr.has_name(name))
-    }
-
-    pub fn filter_by_name<'a>(
-        &'a self,
-        attrs: &'a [Attribute],
-        name: Symbol,
-    ) -> impl Iterator<Item = &'a Attribute> {
-        attrs.iter().filter(move |attr| attr.has_name(name))
-    }
-
-    pub fn first_attr_value_str_by_name(
-        &self,
-        attrs: &[Attribute],
-        name: Symbol,
-    ) -> Option<Symbol> {
-        attrs.iter().find(|at| at.has_name(name)).and_then(|at| at.value_str())
-    }
 }
 
+// JUSTIFICATION: part of session construction
+#[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))]
 fn default_emitter(
     sopts: &config::Options,
     registry: rustc_errors::registry::Registry,
@@ -1148,7 +1183,7 @@ fn default_emitter(
     fallback_bundle: LazyFallbackBundle,
     emitter_dest: Option<Box<dyn Write + Send>>,
 ) -> Box<dyn Emitter + sync::Send> {
-    let macro_backtrace = sopts.debugging_opts.macro_backtrace;
+    let macro_backtrace = sopts.unstable_opts.macro_backtrace;
     match (sopts.error_format, emitter_dest) {
         (config::ErrorOutputType::HumanReadable(kind), dst) => {
             let (short, color_config) = kind.unzip();
@@ -1161,7 +1196,7 @@ fn default_emitter(
                     short,
                     macro_backtrace,
                 );
-                Box::new(emitter.ui_testing(sopts.debugging_opts.ui_testing))
+                Box::new(emitter.ui_testing(sopts.unstable_opts.ui_testing))
             } else {
                 let emitter = match dst {
                     None => EmitterWriter::stderr(
@@ -1170,7 +1205,7 @@ fn default_emitter(
                         bundle,
                         fallback_bundle,
                         short,
-                        sopts.debugging_opts.teach,
+                        sopts.unstable_opts.teach,
                         sopts.diagnostic_width,
                         macro_backtrace,
                     ),
@@ -1186,7 +1221,7 @@ fn default_emitter(
                         macro_backtrace,
                     ),
                 };
-                Box::new(emitter.ui_testing(sopts.debugging_opts.ui_testing))
+                Box::new(emitter.ui_testing(sopts.unstable_opts.ui_testing))
             }
         }
         (config::ErrorOutputType::Json { pretty, json_rendered }, None) => Box::new(
@@ -1200,7 +1235,7 @@ fn default_emitter(
                 sopts.diagnostic_width,
                 macro_backtrace,
             )
-            .ui_testing(sopts.debugging_opts.ui_testing),
+            .ui_testing(sopts.unstable_opts.ui_testing),
         ),
         (config::ErrorOutputType::Json { pretty, json_rendered }, Some(dst)) => Box::new(
             JsonEmitter::new(
@@ -1214,7 +1249,7 @@ fn default_emitter(
                 sopts.diagnostic_width,
                 macro_backtrace,
             )
-            .ui_testing(sopts.debugging_opts.ui_testing),
+            .ui_testing(sopts.unstable_opts.ui_testing),
         ),
     }
 }
@@ -1224,6 +1259,8 @@ pub enum DiagnosticOutput {
     Raw(Box<dyn Write + Send>),
 }
 
+// JUSTIFICATION: literally session construction
+#[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))]
 pub fn build_session(
     sopts: config::Options,
     local_crate_source_file: Option<PathBuf>,
@@ -1267,7 +1304,7 @@ pub fn build_session(
     }
 
     let loader = file_loader.unwrap_or_else(|| Box::new(RealFileLoader));
-    let hash_kind = sopts.debugging_opts.src_hash_algorithm.unwrap_or_else(|| {
+    let hash_kind = sopts.unstable_opts.src_hash_algorithm.unwrap_or_else(|| {
         if target_cfg.is_like_msvc {
             SourceFileHashAlgorithm::Sha1
         } else {
@@ -1282,17 +1319,17 @@ pub fn build_session(
 
     let fallback_bundle = fallback_fluent_bundle(
         rustc_errors::DEFAULT_LOCALE_RESOURCES,
-        sopts.debugging_opts.translate_directionality_markers,
+        sopts.unstable_opts.translate_directionality_markers,
     );
     let emitter =
         default_emitter(&sopts, registry, source_map.clone(), bundle, fallback_bundle, write_dest);
 
     let span_diagnostic = rustc_errors::Handler::with_emitter_and_flags(
         emitter,
-        sopts.debugging_opts.diagnostic_handler_flags(can_emit_warnings),
+        sopts.unstable_opts.diagnostic_handler_flags(can_emit_warnings),
     );
 
-    let self_profiler = if let SwitchWithOptPath::Enabled(ref d) = sopts.debugging_opts.self_profile
+    let self_profiler = if let SwitchWithOptPath::Enabled(ref d) = sopts.unstable_opts.self_profile
     {
         let directory =
             if let Some(ref directory) = d { directory } else { std::path::Path::new(".") };
@@ -1300,8 +1337,8 @@ pub fn build_session(
         let profiler = SelfProfiler::new(
             directory,
             sopts.crate_name.as_deref(),
-            sopts.debugging_opts.self_profile_events.as_ref().map(|xs| &xs[..]),
-            &sopts.debugging_opts.self_profile_counter,
+            sopts.unstable_opts.self_profile_events.as_ref().map(|xs| &xs[..]),
+            &sopts.unstable_opts.self_profile_counter,
         );
         match profiler {
             Ok(profiler) => Some(Arc::new(profiler)),
@@ -1315,7 +1352,7 @@ pub fn build_session(
     };
 
     let mut parse_sess = ParseSess::with_span_handler(span_diagnostic, source_map);
-    parse_sess.assume_incomplete_release = sopts.debugging_opts.assume_incomplete_release;
+    parse_sess.assume_incomplete_release = sopts.unstable_opts.assume_incomplete_release;
 
     let host_triple = config::host_triple();
     let target_triple = sopts.target_triple.triple();
@@ -1334,22 +1371,19 @@ pub fn build_session(
         local_crate_source_file.map(|path| file_path_mapping.map_prefix(path).0);
 
     let optimization_fuel = Lock::new(OptimizationFuel {
-        remaining: sopts.debugging_opts.fuel.as_ref().map_or(0, |i| i.1),
+        remaining: sopts.unstable_opts.fuel.as_ref().map_or(0, |i| i.1),
         out_of_fuel: false,
     });
     let print_fuel = AtomicU64::new(0);
 
-    let cgu_reuse_tracker = if sopts.debugging_opts.query_dep_graph {
+    let cgu_reuse_tracker = if sopts.unstable_opts.query_dep_graph {
         CguReuseTracker::new()
     } else {
         CguReuseTracker::new_disabled()
     };
 
-    let prof = SelfProfilerRef::new(
-        self_profiler,
-        sopts.debugging_opts.time_passes || sopts.debugging_opts.time,
-        sopts.debugging_opts.time_passes,
-    );
+    let prof =
+        SelfProfilerRef::new(self_profiler, sopts.time_passes(), sopts.unstable_opts.time_passes);
 
     let ctfe_backtrace = Lock::new(match env::var("RUSTC_CTFE_BACKTRACE") {
         Ok(ref val) if val == "immediate" => CtfeBacktrace::Immediate,
@@ -1390,6 +1424,7 @@ pub fn build_session(
         miri_unleashed_features: Lock::new(Default::default()),
         asm_arch,
         target_features: FxHashSet::default(),
+        unstable_target_features: FxHashSet::default(),
     };
 
     validate_commandline_args_with_session_available(&sess);
@@ -1397,8 +1432,12 @@ pub fn build_session(
     sess
 }
 
-// If it is useful to have a Session available already for validating a
-// commandline argument, you can do so here.
+/// Validate command line arguments with a `Session`.
+///
+/// If it is useful to have a Session available already for validating a commandline argument, you
+/// can do so here.
+// JUSTIFICATION: needs to access args to validate them
+#[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))]
 fn validate_commandline_args_with_session_available(sess: &Session) {
     // Since we don't know if code in an rlib will be linked to statically or
     // dynamically downstream, rustc generates `__imp_` symbols that help linkers
@@ -1429,7 +1468,7 @@ fn validate_commandline_args_with_session_available(sess: &Session) {
     }
 
     // Do the same for sample profile data.
-    if let Some(ref path) = sess.opts.debugging_opts.profile_sample_use {
+    if let Some(ref path) = sess.opts.unstable_opts.profile_sample_use {
         if !path.exists() {
             sess.err(&format!(
                 "File `{}` passed to `-C profile-sample-use` does not exist.",
@@ -1450,7 +1489,7 @@ fn validate_commandline_args_with_session_available(sess: &Session) {
 
     // Sanitizers can only be used on platforms that we know have working sanitizer codegen.
     let supported_sanitizers = sess.target.options.supported_sanitizers;
-    let unsupported_sanitizers = sess.opts.debugging_opts.sanitizer - supported_sanitizers;
+    let unsupported_sanitizers = sess.opts.unstable_opts.sanitizer - supported_sanitizers;
     match unsupported_sanitizers.into_iter().count() {
         0 => {}
         1 => {
@@ -1467,13 +1506,13 @@ fn validate_commandline_args_with_session_available(sess: &Session) {
         }
     }
     // Cannot mix and match sanitizers.
-    let mut sanitizer_iter = sess.opts.debugging_opts.sanitizer.into_iter();
+    let mut sanitizer_iter = sess.opts.unstable_opts.sanitizer.into_iter();
     if let (Some(first), Some(second)) = (sanitizer_iter.next(), sanitizer_iter.next()) {
         sess.err(&format!("`-Zsanitizer={first}` is incompatible with `-Zsanitizer={second}`"));
     }
 
     // Cannot enable crt-static with sanitizers on Linux
-    if sess.crt_static(None) && !sess.opts.debugging_opts.sanitizer.is_empty() {
+    if sess.crt_static(None) && !sess.opts.unstable_opts.sanitizer.is_empty() {
         sess.err(
             "sanitizer is incompatible with statically linked libc, \
                                 disable it using `-C target-feature=-crt-static`",
@@ -1485,21 +1524,21 @@ fn validate_commandline_args_with_session_available(sess: &Session) {
         if sess.is_sanitizer_cfi_enabled() {
             sess.err("`-Zsanitizer=cfi` requires `-Clto`");
         }
-        if sess.opts.debugging_opts.virtual_function_elimination {
+        if sess.opts.unstable_opts.virtual_function_elimination {
             sess.err("`-Zvirtual-function-elimination` requires `-Clto`");
         }
     }
 
-    if sess.opts.debugging_opts.stack_protector != StackProtector::None {
+    if sess.opts.unstable_opts.stack_protector != StackProtector::None {
         if !sess.target.options.supports_stack_protector {
             sess.warn(&format!(
                 "`-Z stack-protector={}` is not supported for target {} and will be ignored",
-                sess.opts.debugging_opts.stack_protector, sess.opts.target_triple
+                sess.opts.unstable_opts.stack_protector, sess.opts.target_triple
             ))
         }
     }
 
-    if let Some(dwarf_version) = sess.opts.debugging_opts.dwarf_version {
+    if let Some(dwarf_version) = sess.opts.unstable_opts.dwarf_version {
         if dwarf_version > 5 {
             sess.err(&format!("requested DWARF version {} is greater than 5", dwarf_version));
         }
diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs
index 3df4dfb74b3..e169d3c7cfb 100644
--- a/compiler/rustc_span/src/hygiene.rs
+++ b/compiler/rustc_span/src/hygiene.rs
@@ -108,7 +108,7 @@ fn assert_default_hashing_controls<CTX: HashStableContext>(ctx: &CTX, msg: &str)
         // which will cause us to require that this method always be called with `Span` hashing
         // enabled.
         HashingControls { hash_spans }
-            if hash_spans == !ctx.debug_opts_incremental_ignore_spans() => {}
+            if hash_spans == !ctx.unstable_opts_incremental_ignore_spans() => {}
         other => panic!("Attempted hashing of {msg} with non-default HashingControls: {:?}", other),
     }
 }
@@ -644,7 +644,10 @@ pub fn debug_hygiene_data(verbose: bool) -> String {
                 let expn_data = expn_data.as_ref().expect("no expansion data for an expansion ID");
                 debug_expn_data((&id.to_expn_id(), expn_data))
             });
+
             // Sort the hash map for more reproducible output.
+            // Because of this, it is fine to rely on the unstable iteration order of the map.
+            #[allow(rustc::potential_query_instability)]
             let mut foreign_expn_data: Vec<_> = data.foreign_expn_data.iter().collect();
             foreign_expn_data.sort_by_key(|(id, _)| (id.krate, id.local_id));
             foreign_expn_data.into_iter().for_each(debug_expn_data);
@@ -1141,7 +1144,6 @@ pub enum DesugaringKind {
     Async,
     Await,
     ForLoop,
-    LetElse,
     WhileLoop,
 }
 
@@ -1157,7 +1159,6 @@ impl DesugaringKind {
             DesugaringKind::YeetExpr => "`do yeet` expression",
             DesugaringKind::OpaqueTy => "`impl Trait`",
             DesugaringKind::ForLoop => "`for` loop",
-            DesugaringKind::LetElse => "`let...else`",
             DesugaringKind::WhileLoop => "`while` loop",
         }
     }
@@ -1210,6 +1211,7 @@ impl HygieneEncodeContext {
             // It's fine to iterate over a HashMap, because the serialization
             // of the table that we insert data into doesn't depend on insertion
             // order
+            #[allow(rustc::potential_query_instability)]
             for_all_ctxts_in(latest_ctxts.into_iter(), |index, ctxt, data| {
                 if self.serialized_ctxts.lock().insert(ctxt) {
                     encode_ctxt(encoder, index, data);
@@ -1218,6 +1220,8 @@ impl HygieneEncodeContext {
 
             let latest_expns = { std::mem::take(&mut *self.latest_expns.lock()) };
 
+            // Same as above, this is fine as we are inserting into a order-independent hashset
+            #[allow(rustc::potential_query_instability)]
             for_all_expns_in(latest_expns.into_iter(), |expn, data, hash| {
                 if self.serialized_expns.lock().insert(expn) {
                     encode_expn(encoder, expn, data, hash);
diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs
index a1f34287a5f..cf306928151 100644
--- a/compiler/rustc_span/src/lib.rs
+++ b/compiler/rustc_span/src/lib.rs
@@ -20,7 +20,6 @@
 #![feature(negative_impls)]
 #![feature(min_specialization)]
 #![feature(rustc_attrs)]
-#![allow(rustc::potential_query_instability)]
 
 #[macro_use]
 extern crate rustc_macros;
@@ -2029,9 +2028,9 @@ impl InnerSpan {
 pub trait HashStableContext {
     fn def_path_hash(&self, def_id: DefId) -> DefPathHash;
     fn hash_spans(&self) -> bool;
-    /// Accesses `sess.opts.debugging_opts.incremental_ignore_spans` since
+    /// Accesses `sess.opts.unstable_opts.incremental_ignore_spans` since
     /// we don't have easy access to a `Session`
-    fn debug_opts_incremental_ignore_spans(&self) -> bool;
+    fn unstable_opts_incremental_ignore_spans(&self) -> bool;
     fn def_span(&self, def_id: LocalDefId) -> Span;
     fn span_data_to_lines_and_cols(
         &mut self,
diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs
index afbb88e9233..28381157d50 100644
--- a/compiler/rustc_span/src/source_map.rs
+++ b/compiler/rustc_span/src/source_map.rs
@@ -463,6 +463,33 @@ impl SourceMap {
         self.span_to_string(sp, FileNameDisplayPreference::Remapped)
     }
 
+    /// Format the span location suitable for pretty printing anotations with relative line numbers
+    pub fn span_to_relative_line_string(&self, sp: Span, relative_to: Span) -> String {
+        if self.files.borrow().source_files.is_empty() || sp.is_dummy() || relative_to.is_dummy() {
+            return "no-location".to_string();
+        }
+
+        let lo = self.lookup_char_pos(sp.lo());
+        let hi = self.lookup_char_pos(sp.hi());
+        let offset = self.lookup_char_pos(relative_to.lo());
+
+        if lo.file.name != offset.file.name {
+            return self.span_to_embeddable_string(sp);
+        }
+
+        let lo_line = lo.line.saturating_sub(offset.line);
+        let hi_line = hi.line.saturating_sub(offset.line);
+
+        format!(
+            "{}:+{}:{}: +{}:{}",
+            lo.file.name.display(FileNameDisplayPreference::Remapped),
+            lo_line,
+            lo.col.to_usize() + 1,
+            hi_line,
+            hi.col.to_usize() + 1,
+        )
+    }
+
     /// Format the span location to be printed in diagnostics. Must not be emitted
     /// to build artifacts as this may leak local file paths. Use span_to_embeddable_string
     /// for string suitable for embedding.
@@ -586,17 +613,6 @@ impl SourceMap {
         }
     }
 
-    /// Returns whether or not this span points into a file
-    /// in the current crate. This may be `false` for spans
-    /// produced by a macro expansion, or for spans associated
-    /// with the definition of an item in a foreign crate
-    pub fn is_local_span(&self, sp: Span) -> bool {
-        let local_begin = self.lookup_byte_offset(sp.lo());
-        let local_end = self.lookup_byte_offset(sp.hi());
-        // This might be a weird span that covers multiple files
-        local_begin.sf.src.is_some() && local_end.sf.src.is_some()
-    }
-
     pub fn is_span_accessible(&self, sp: Span) -> bool {
         self.span_to_source(sp, |src, start_index, end_index| {
             Ok(src.get(start_index..end_index).is_some())
@@ -718,6 +734,11 @@ impl SourceMap {
         sp
     }
 
+    /// Extends the given `Span` to contain the entire line it is on.
+    pub fn span_extend_to_line(&self, sp: Span) -> Span {
+        self.span_extend_to_prev_char(self.span_extend_to_next_char(sp, '\n', true), '\n', true)
+    }
+
     /// Given a `Span`, tries to get a shorter span ending before the first occurrence of `char`
     /// `c`.
     pub fn span_until_char(&self, sp: Span, c: char) -> Span {
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 9b6967621f1..060e7a7b90a 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -223,6 +223,16 @@ symbols! {
         LintPass,
         Mutex,
         N,
+        NonZeroI128,
+        NonZeroI16,
+        NonZeroI32,
+        NonZeroI64,
+        NonZeroI8,
+        NonZeroU128,
+        NonZeroU16,
+        NonZeroU32,
+        NonZeroU64,
+        NonZeroU8,
         None,
         Ok,
         Option,
@@ -257,6 +267,7 @@ symbols! {
         Result,
         Return,
         Right,
+        Rust,
         RustcDecodable,
         RustcEncodable,
         Send,
@@ -341,6 +352,7 @@ symbols! {
         always,
         and,
         and_then,
+        anonymous_lifetime_in_impl_trait,
         any,
         append_const_msg,
         arbitrary_enum_discriminant,
@@ -459,6 +471,7 @@ symbols! {
         clone_closures,
         clone_from,
         closure,
+        closure_lifetime_binder,
         closure_to_fn_coercion,
         closure_track_caller,
         cmp,
@@ -683,6 +696,7 @@ symbols! {
         fabsf32,
         fabsf64,
         fadd_fast,
+        fake_variadic,
         fdiv_fast,
         feature,
         fence,
@@ -786,6 +800,7 @@ symbols! {
         impl_lint_pass,
         impl_macros,
         impl_trait_in_bindings,
+        implied_by,
         import,
         import_shadowing,
         imported_main,
@@ -980,7 +995,6 @@ symbols! {
         no_link,
         no_main,
         no_mangle,
-        no_niche,
         no_sanitize,
         no_stack_check,
         no_start,
@@ -1153,7 +1167,6 @@ symbols! {
         repr128,
         repr_align,
         repr_align_enum,
-        repr_no_niche,
         repr_packed,
         repr_simd,
         repr_transparent,
@@ -1189,8 +1202,10 @@ symbols! {
         rustc,
         rustc_allocator,
         rustc_allocator_nounwind,
+        rustc_allocator_zeroed,
         rustc_allow_const_fn_unstable,
         rustc_allow_incoherent_impl,
+        rustc_allowed_through_unstable_modules,
         rustc_attrs,
         rustc_box,
         rustc_builtin_macro,
@@ -1200,6 +1215,7 @@ symbols! {
         rustc_const_stable,
         rustc_const_unstable,
         rustc_conversion_suggestion,
+        rustc_deallocator,
         rustc_def_path,
         rustc_diagnostic_item,
         rustc_diagnostic_macros,
@@ -1222,6 +1238,8 @@ symbols! {
         rustc_layout_scalar_valid_range_start,
         rustc_legacy_const_generics,
         rustc_lint_diagnostics,
+        rustc_lint_opt_deny_field_access,
+        rustc_lint_opt_ty,
         rustc_lint_query_instability,
         rustc_macro_transparency,
         rustc_main,
@@ -1244,6 +1262,7 @@ symbols! {
         rustc_private,
         rustc_proc_macro_decls,
         rustc_promotable,
+        rustc_reallocator,
         rustc_regions,
         rustc_reservation_impl,
         rustc_serialize,
@@ -1270,6 +1289,7 @@ symbols! {
         self_in_typedefs,
         self_struct_ctor,
         semitransparent,
+        shadow_call_stack,
         shl,
         shl_assign,
         should_panic,
@@ -1434,6 +1454,8 @@ symbols! {
         thumb2,
         thumb_mode: "thumb-mode",
         tmm_reg,
+        to_string,
+        to_vec,
         todo_macro,
         tool_attributes,
         tool_lints,
@@ -1457,7 +1479,6 @@ symbols! {
         tuple,
         tuple_from_req,
         tuple_indexing,
-        tuple_variadic,
         two_phase,
         ty,
         type_alias_enum_variants,
@@ -1512,6 +1533,9 @@ symbols! {
         unsized_locals,
         unsized_tuple_coercion,
         unstable,
+        unstable_location_reason_default: "this crate is being loaded from the sysroot, an \
+                          unstable location; did you mean to load this crate \
+                          from crates.io via `Cargo.toml` instead?",
         untagged_unions,
         unused_imports,
         unused_qualifications,
@@ -1550,6 +1574,8 @@ symbols! {
         volatile_store,
         vreg,
         vreg_low16,
+        vtable_align,
+        vtable_size,
         warn,
         wasm_abi,
         wasm_import_module,
diff --git a/compiler/rustc_symbol_mangling/Cargo.toml b/compiler/rustc_symbol_mangling/Cargo.toml
index d5befa10e23..b104a40c231 100644
--- a/compiler/rustc_symbol_mangling/Cargo.toml
+++ b/compiler/rustc_symbol_mangling/Cargo.toml
@@ -7,6 +7,7 @@ edition = "2021"
 doctest = false
 
 [dependencies]
+bitflags = "1.2.1"
 tracing = "0.1"
 punycode = "0.4.0"
 rustc-demangle = "0.1.21"
diff --git a/compiler/rustc_symbol_mangling/src/legacy.rs b/compiler/rustc_symbol_mangling/src/legacy.rs
index e3045c9321d..9241fd82c74 100644
--- a/compiler/rustc_symbol_mangling/src/legacy.rs
+++ b/compiler/rustc_symbol_mangling/src/legacy.rs
@@ -67,7 +67,7 @@ pub(super) fn mangle<'tcx>(
         )
         .unwrap();
 
-    if let ty::InstanceDef::VtableShim(..) = instance.def {
+    if let ty::InstanceDef::VTableShim(..) = instance.def {
         let _ = printer.write_str("{{vtable-shim}}");
     }
 
@@ -129,7 +129,7 @@ fn get_symbol_hash<'tcx>(
                 }
 
                 // We want to avoid accidental collision between different types of instances.
-                // Especially, `VtableShim`s and `ReifyShim`s may overlap with their original
+                // Especially, `VTableShim`s and `ReifyShim`s may overlap with their original
                 // instances without this.
                 discriminant(&instance.def).hash_stable(hcx, &mut hasher);
             });
diff --git a/compiler/rustc_symbol_mangling/src/lib.rs b/compiler/rustc_symbol_mangling/src/lib.rs
index bed0e81e66e..5fc992023ca 100644
--- a/compiler/rustc_symbol_mangling/src/lib.rs
+++ b/compiler/rustc_symbol_mangling/src/lib.rs
@@ -102,9 +102,8 @@ use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs;
 use rustc_middle::mir::mono::{InstantiationMode, MonoItem};
 use rustc_middle::ty::query::Providers;
 use rustc_middle::ty::subst::SubstsRef;
-use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
+use rustc_middle::ty::{self, Instance, TyCtxt};
 use rustc_session::config::SymbolManglingVersion;
-use rustc_target::abi::call::FnAbi;
 
 use tracing::debug;
 
@@ -112,6 +111,7 @@ mod legacy;
 mod v0;
 
 pub mod test;
+pub mod typeid;
 
 /// This function computes the symbol name for the given `instance` and the
 /// given instantiating crate. That is, if you know that instance X is
@@ -150,11 +150,6 @@ fn symbol_name_provider<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> ty
     ty::SymbolName::new(tcx, &symbol_name)
 }
 
-/// This function computes the typeid for the given function ABI.
-pub fn typeid_for_fnabi<'tcx>(tcx: TyCtxt<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> String {
-    v0::mangle_typeid_for_fnabi(tcx, fn_abi)
-}
-
 pub fn typeid_for_trait_ref<'tcx>(
     tcx: TyCtxt<'tcx>,
     trait_ref: ty::PolyExistentialTraitRef<'tcx>,
diff --git a/compiler/rustc_symbol_mangling/src/typeid.rs b/compiler/rustc_symbol_mangling/src/typeid.rs
new file mode 100644
index 00000000000..9228bea43f9
--- /dev/null
+++ b/compiler/rustc_symbol_mangling/src/typeid.rs
@@ -0,0 +1,18 @@
+// For more information about type metadata and type metadata identifiers for cross-language LLVM
+// CFI support, see Type metadata in the design document in the tracking issue #89653.
+
+use rustc_middle::ty::{FnSig, Ty, TyCtxt};
+use rustc_target::abi::call::FnAbi;
+
+mod typeid_itanium_cxx_abi;
+use typeid_itanium_cxx_abi::TypeIdOptions;
+
+/// Returns a type metadata identifier for the specified FnAbi.
+pub fn typeid_for_fnabi<'tcx>(tcx: TyCtxt<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> String {
+    typeid_itanium_cxx_abi::typeid_for_fnabi(tcx, fn_abi, TypeIdOptions::NO_OPTIONS)
+}
+
+/// Returns a type metadata identifier for the specified FnSig.
+pub fn typeid_for_fnsig<'tcx>(tcx: TyCtxt<'tcx>, fn_sig: &FnSig<'tcx>) -> String {
+    typeid_itanium_cxx_abi::typeid_for_fnsig(tcx, fn_sig, TypeIdOptions::NO_OPTIONS)
+}
diff --git a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs
new file mode 100644
index 00000000000..a09b52fbfdf
--- /dev/null
+++ b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs
@@ -0,0 +1,929 @@
+// For more information about type metadata and type metadata identifiers for cross-language LLVM
+// CFI support, see Type metadata in the design document in the tracking issue #89653.
+
+// FIXME(rcvalle): Identify C char and integer type uses and encode them with their respective
+// builtin type encodings as specified by the Itanium C++ ABI for extern function types with the "C"
+// calling convention to use this encoding for cross-language LLVM CFI.
+
+use bitflags::bitflags;
+use core::fmt::Display;
+use rustc_data_structures::base_n;
+use rustc_data_structures::fx::FxHashMap;
+use rustc_hir as hir;
+use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef};
+use rustc_middle::ty::{
+    self, Binder, Const, ExistentialPredicate, FloatTy, FnSig, IntTy, List, Region, RegionKind,
+    Term, Ty, TyCtxt, UintTy,
+};
+use rustc_span::def_id::DefId;
+use rustc_span::symbol::sym;
+use rustc_target::abi::call::{Conv, FnAbi};
+use rustc_target::spec::abi::Abi;
+use std::fmt::Write as _;
+
+/// Type and extended type qualifiers.
+#[derive(Eq, Hash, PartialEq)]
+enum TyQ {
+    None,
+    Const,
+    Mut,
+}
+
+/// Substitution dictionary key.
+#[derive(Eq, Hash, PartialEq)]
+enum DictKey<'tcx> {
+    Ty(Ty<'tcx>, TyQ),
+    Region(Region<'tcx>),
+    Const(Const<'tcx>),
+    Predicate(ExistentialPredicate<'tcx>),
+}
+
+bitflags! {
+    /// Options for typeid_for_fnabi and typeid_for_fnsig.
+    pub struct TypeIdOptions: u32 {
+        const NO_OPTIONS = 0;
+        const GENERALIZE_POINTERS = 1;
+        const GENERALIZE_REPR_C = 2;
+    }
+}
+
+/// Options for encode_ty.
+type EncodeTyOptions = TypeIdOptions;
+
+/// Options for transform_ty.
+type TransformTyOptions = TypeIdOptions;
+
+/// Converts a number to a disambiguator (see
+/// <https://rust-lang.github.io/rfcs/2603-rust-symbol-name-mangling-v0.html>).
+fn to_disambiguator(num: u64) -> String {
+    if let Some(num) = num.checked_sub(1) {
+        format!("s{}_", base_n::encode(num as u128, 62))
+    } else {
+        "s_".to_string()
+    }
+}
+
+/// Converts a number to a sequence number (see
+/// <https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangle.seq-id>).
+fn to_seq_id(num: usize) -> String {
+    if let Some(num) = num.checked_sub(1) {
+        base_n::encode(num as u128, 36).to_uppercase()
+    } else {
+        "".to_string()
+    }
+}
+
+/// Substitutes a component if found in the substitution dictionary (see
+/// <https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-compression>).
+fn compress<'tcx>(
+    dict: &mut FxHashMap<DictKey<'tcx>, usize>,
+    key: DictKey<'tcx>,
+    comp: &mut String,
+) {
+    match dict.get(&key) {
+        Some(num) => {
+            comp.clear();
+            let _ = write!(comp, "S{}_", to_seq_id(*num));
+        }
+        None => {
+            dict.insert(key, dict.len());
+        }
+    }
+}
+
+// FIXME(rcvalle): Move to compiler/rustc_middle/src/ty/sty.rs after C types work is done, possibly
+// along with other is_c_type methods.
+/// Returns whether a `ty::Ty` is `c_void`.
+fn is_c_void_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool {
+    match ty.kind() {
+        ty::Adt(adt_def, ..) => {
+            let def_id = adt_def.0.did;
+            let crate_name = tcx.crate_name(def_id.krate);
+            if tcx.item_name(def_id).as_str() == "c_void"
+                && (crate_name == sym::core || crate_name == sym::std || crate_name == sym::libc)
+            {
+                true
+            } else {
+                false
+            }
+        }
+        _ => false,
+    }
+}
+
+/// Encodes a const using the Itanium C++ ABI as a literal argument (see
+/// <https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling.literal>).
+fn encode_const<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    c: Const<'tcx>,
+    dict: &mut FxHashMap<DictKey<'tcx>, usize>,
+    options: EncodeTyOptions,
+) -> String {
+    // L<element-type>[n]<element-value>E as literal argument
+    let mut s = String::from('L');
+
+    // Element type
+    s.push_str(&encode_ty(tcx, c.ty(), dict, options));
+
+    // The only allowed types of const parameters are bool, u8, u16, u32, u64, u128, usize i8, i16,
+    // i32, i64, i128, isize, and char. The bool value false is encoded as 0 and true as 1.
+    fn push_signed_value<T: Display + PartialOrd>(s: &mut String, value: T, zero: T) {
+        if value < zero {
+            s.push('n')
+        };
+        let _ = write!(s, "{}", value);
+    }
+
+    fn push_unsigned_value<T: Display>(s: &mut String, value: T) {
+        let _ = write!(s, "{}", value);
+    }
+
+    if let Some(scalar_int) = c.kind().try_to_scalar_int() {
+        let signed = c.ty().is_signed();
+        match scalar_int.size().bits() {
+            8 if signed => push_signed_value(&mut s, scalar_int.try_to_i8().unwrap(), 0),
+            16 if signed => push_signed_value(&mut s, scalar_int.try_to_i16().unwrap(), 0),
+            32 if signed => push_signed_value(&mut s, scalar_int.try_to_i32().unwrap(), 0),
+            64 if signed => push_signed_value(&mut s, scalar_int.try_to_i64().unwrap(), 0),
+            128 if signed => push_signed_value(&mut s, scalar_int.try_to_i128().unwrap(), 0),
+            8 => push_unsigned_value(&mut s, scalar_int.try_to_u8().unwrap()),
+            16 => push_unsigned_value(&mut s, scalar_int.try_to_u16().unwrap()),
+            32 => push_unsigned_value(&mut s, scalar_int.try_to_u32().unwrap()),
+            64 => push_unsigned_value(&mut s, scalar_int.try_to_u64().unwrap()),
+            128 => push_unsigned_value(&mut s, scalar_int.try_to_u128().unwrap()),
+            _ => {
+                bug!("encode_const: unexpected size `{:?}`", scalar_int.size().bits());
+            }
+        };
+    } else {
+        bug!("encode_const: unexpected type `{:?}`", c.ty());
+    }
+
+    // Close the "L..E" pair
+    s.push('E');
+
+    compress(dict, DictKey::Const(c), &mut s);
+
+    s
+}
+
+/// Encodes a FnSig using the Itanium C++ ABI with vendor extended type qualifiers and types for
+/// Rust types that are not used at the FFI boundary.
+fn encode_fnsig<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    fn_sig: &FnSig<'tcx>,
+    dict: &mut FxHashMap<DictKey<'tcx>, usize>,
+    options: TypeIdOptions,
+) -> String {
+    // Function types are delimited by an "F..E" pair
+    let mut s = String::from("F");
+
+    let mut encode_ty_options = EncodeTyOptions::from_bits(options.bits())
+        .unwrap_or_else(|| bug!("encode_fnsig: invalid option(s) `{:?}`", options.bits()));
+    match fn_sig.abi {
+        Abi::C { .. } => {
+            encode_ty_options.insert(EncodeTyOptions::GENERALIZE_REPR_C);
+        }
+        _ => {
+            encode_ty_options.remove(EncodeTyOptions::GENERALIZE_REPR_C);
+        }
+    }
+
+    // Encode the return type
+    let transform_ty_options = TransformTyOptions::from_bits(options.bits())
+        .unwrap_or_else(|| bug!("encode_fnsig: invalid option(s) `{:?}`", options.bits()));
+    let ty = transform_ty(tcx, fn_sig.output(), transform_ty_options);
+    s.push_str(&encode_ty(tcx, ty, dict, encode_ty_options));
+
+    // Encode the parameter types
+    let tys = fn_sig.inputs();
+    if !tys.is_empty() {
+        for ty in tys {
+            let ty = transform_ty(tcx, *ty, transform_ty_options);
+            s.push_str(&encode_ty(tcx, ty, dict, encode_ty_options));
+        }
+
+        if fn_sig.c_variadic {
+            s.push('z');
+        }
+    } else {
+        if fn_sig.c_variadic {
+            s.push('z');
+        } else {
+            // Empty parameter lists, whether declared as () or conventionally as (void), are
+            // encoded with a void parameter specifier "v".
+            s.push('v')
+        }
+    }
+
+    // Close the "F..E" pair
+    s.push('E');
+
+    s
+}
+
+/// Encodes a predicate using the Itanium C++ ABI with vendor extended type qualifiers and types for
+/// Rust types that are not used at the FFI boundary.
+fn encode_predicate<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    predicate: Binder<'tcx, ExistentialPredicate<'tcx>>,
+    dict: &mut FxHashMap<DictKey<'tcx>, usize>,
+    options: EncodeTyOptions,
+) -> String {
+    // u<length><name>[I<element-type1..element-typeN>E], where <element-type> is <subst>, as vendor
+    // extended type.
+    let mut s = String::new();
+    match predicate.as_ref().skip_binder() {
+        ty::ExistentialPredicate::Trait(trait_ref) => {
+            let name = encode_ty_name(tcx, trait_ref.def_id);
+            let _ = write!(s, "u{}{}", name.len(), &name);
+            s.push_str(&encode_substs(tcx, trait_ref.substs, dict, options));
+        }
+        ty::ExistentialPredicate::Projection(projection) => {
+            let name = encode_ty_name(tcx, projection.item_def_id);
+            let _ = write!(s, "u{}{}", name.len(), &name);
+            s.push_str(&encode_substs(tcx, projection.substs, dict, options));
+            match projection.term {
+                Term::Ty(ty) => {
+                    s.push_str(&encode_ty(tcx, ty, dict, options));
+                }
+                Term::Const(c) => {
+                    s.push_str(&encode_const(tcx, c, dict, options));
+                }
+            }
+        }
+        ty::ExistentialPredicate::AutoTrait(def_id) => {
+            let name = encode_ty_name(tcx, *def_id);
+            let _ = write!(s, "u{}{}", name.len(), &name);
+        }
+    };
+    compress(dict, DictKey::Predicate(*predicate.as_ref().skip_binder()), &mut s);
+    s
+}
+
+/// Encodes predicates using the Itanium C++ ABI with vendor extended type qualifiers and types for
+/// Rust types that are not used at the FFI boundary.
+fn encode_predicates<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    predicates: &List<Binder<'tcx, ExistentialPredicate<'tcx>>>,
+    dict: &mut FxHashMap<DictKey<'tcx>, usize>,
+    options: EncodeTyOptions,
+) -> String {
+    // <predicate1[..predicateN]>E as part of vendor extended type
+    let mut s = String::new();
+    let predicates: Vec<Binder<'tcx, ExistentialPredicate<'tcx>>> =
+        predicates.iter().map(|predicate| predicate).collect();
+    for predicate in predicates {
+        s.push_str(&encode_predicate(tcx, predicate, dict, options));
+    }
+    s
+}
+
+/// Encodes a region using the Itanium C++ ABI as a vendor extended type.
+fn encode_region<'tcx>(
+    _tcx: TyCtxt<'tcx>,
+    region: Region<'tcx>,
+    dict: &mut FxHashMap<DictKey<'tcx>, usize>,
+    _options: EncodeTyOptions,
+) -> String {
+    // u6region[I[<region-disambiguator>][<region-index>]E] as vendor extended type
+    let mut s = String::new();
+    match region.kind() {
+        RegionKind::ReLateBound(debruijn, r) => {
+            s.push_str("u6regionI");
+            // Debruijn index, which identifies the binder, as region disambiguator
+            let num = debruijn.index() as u64;
+            if num > 0 {
+                s.push_str(&to_disambiguator(num));
+            }
+            // Index within the binder
+            let _ = write!(s, "{}", r.var.index() as u64);
+            s.push('E');
+            compress(dict, DictKey::Region(region), &mut s);
+        }
+        RegionKind::ReErased => {
+            s.push_str("u6region");
+            compress(dict, DictKey::Region(region), &mut s);
+        }
+        RegionKind::ReEarlyBound(..)
+        | RegionKind::ReFree(..)
+        | RegionKind::ReStatic
+        | RegionKind::ReVar(..)
+        | RegionKind::RePlaceholder(..)
+        | RegionKind::ReEmpty(..) => {
+            bug!("encode_region: unexpected `{:?}`", region.kind());
+        }
+    }
+    s
+}
+
+/// Encodes substs using the Itanium C++ ABI with vendor extended type qualifiers and types for Rust
+/// types that are not used at the FFI boundary.
+fn encode_substs<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    substs: SubstsRef<'tcx>,
+    dict: &mut FxHashMap<DictKey<'tcx>, usize>,
+    options: EncodeTyOptions,
+) -> String {
+    // [I<subst1..substN>E] as part of vendor extended type
+    let mut s = String::new();
+    let substs: Vec<GenericArg<'_>> = substs.iter().map(|subst| subst).collect();
+    if !substs.is_empty() {
+        s.push('I');
+        for subst in substs {
+            match subst.unpack() {
+                GenericArgKind::Lifetime(region) => {
+                    s.push_str(&encode_region(tcx, region, dict, options));
+                }
+                GenericArgKind::Type(ty) => {
+                    s.push_str(&encode_ty(tcx, ty, dict, options));
+                }
+                GenericArgKind::Const(c) => {
+                    s.push_str(&encode_const(tcx, c, dict, options));
+                }
+            }
+        }
+        s.push('E');
+    }
+    s
+}
+
+/// Encodes a ty:Ty name, including its crate and path disambiguators and names.
+fn encode_ty_name<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> String {
+    // Encode <name> for use in u<length><name>[I<element-type1..element-typeN>E], where
+    // <element-type> is <subst>, using v0's <path> without v0's extended form of paths:
+    //
+    // N<namespace-tagN>..N<namespace-tag1>
+    // C<crate-disambiguator><crate-name>
+    // <path-disambiguator1><path-name1>..<path-disambiguatorN><path-nameN>
+    //
+    // With additional tags for DefPathData::Impl and DefPathData::ForeignMod. For instance:
+    //
+    //     pub type Type1 = impl Send;
+    //     let _: Type1 = <Struct1<i32>>::foo;
+    //     fn foo1(_: Type1) { }
+    //
+    //     pub type Type2 = impl Send;
+    //     let _: Type2 = <Trait1<i32>>::foo;
+    //     fn foo2(_: Type2) { }
+    //
+    //     pub type Type3 = impl Send;
+    //     let _: Type3 = <i32 as Trait1<i32>>::foo;
+    //     fn foo3(_: Type3) { }
+    //
+    //     pub type Type4 = impl Send;
+    //     let _: Type4 = <Struct1<i32> as Trait1<i32>>::foo;
+    //     fn foo3(_: Type4) { }
+    //
+    // Are encoded as:
+    //
+    //     _ZTSFvu29NvNIC1234_5crate8{{impl}}3fooIu3i32EE
+    //     _ZTSFvu27NvNtC1234_5crate6Trait13fooIu3dynIu21NtC1234_5crate6Trait1Iu3i32Eu6regionES_EE
+    //     _ZTSFvu27NvNtC1234_5crate6Trait13fooIu3i32S_EE
+    //     _ZTSFvu27NvNtC1234_5crate6Trait13fooIu22NtC1234_5crate7Struct1Iu3i32ES_EE
+    //
+    // The reason for not using v0's extended form of paths is to use a consistent and simpler
+    // encoding, as the reasoning for using it isn't relevand for type metadata identifiers (i.e.,
+    // keep symbol names close to how methods are represented in error messages). See
+    // https://rust-lang.github.io/rfcs/2603-rust-symbol-name-mangling-v0.html#methods.
+    let mut s = String::new();
+
+    // Start and namespace tags
+    let mut def_path = tcx.def_path(def_id);
+    def_path.data.reverse();
+    for disambiguated_data in &def_path.data {
+        s.push('N');
+        s.push_str(match disambiguated_data.data {
+            hir::definitions::DefPathData::Impl => "I", // Not specified in v0's <namespace>
+            hir::definitions::DefPathData::ForeignMod => "F", // Not specified in v0's <namespace>
+            hir::definitions::DefPathData::TypeNs(..) => "t",
+            hir::definitions::DefPathData::ValueNs(..) => "v",
+            hir::definitions::DefPathData::ClosureExpr => "C",
+            hir::definitions::DefPathData::Ctor => "c",
+            hir::definitions::DefPathData::AnonConst => "k",
+            hir::definitions::DefPathData::ImplTrait => "i",
+            hir::definitions::DefPathData::CrateRoot
+            | hir::definitions::DefPathData::Use
+            | hir::definitions::DefPathData::GlobalAsm
+            | hir::definitions::DefPathData::MacroNs(..)
+            | hir::definitions::DefPathData::LifetimeNs(..) => {
+                bug!("encode_ty_name: unexpected `{:?}`", disambiguated_data.data);
+            }
+        });
+    }
+
+    // Crate disambiguator and name
+    s.push('C');
+    s.push_str(&to_disambiguator(tcx.stable_crate_id(def_path.krate).to_u64()));
+    let crate_name = tcx.crate_name(def_path.krate).to_string();
+    let _ = write!(s, "{}{}", crate_name.len(), &crate_name);
+
+    // Disambiguators and names
+    def_path.data.reverse();
+    for disambiguated_data in &def_path.data {
+        let num = disambiguated_data.disambiguator as u64;
+        if num > 0 {
+            s.push_str(&to_disambiguator(num));
+        }
+
+        let name = disambiguated_data.data.to_string();
+        let _ = write!(s, "{}", name.len());
+
+        // Prepend a '_' if name starts with a digit or '_'
+        if let Some(first) = name.as_bytes().get(0) {
+            if first.is_ascii_digit() || *first == b'_' {
+                s.push('_');
+            }
+        } else {
+            bug!("encode_ty_name: invalid name `{:?}`", name);
+        }
+
+        s.push_str(&name);
+    }
+
+    s
+}
+
+/// Encodes a ty:Ty using the Itanium C++ ABI with vendor extended type qualifiers and types for
+/// Rust types that are not used at the FFI boundary.
+fn encode_ty<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    ty: Ty<'tcx>,
+    dict: &mut FxHashMap<DictKey<'tcx>, usize>,
+    options: EncodeTyOptions,
+) -> String {
+    let mut typeid = String::new();
+
+    match ty.kind() {
+        // Primitive types
+        ty::Bool => {
+            typeid.push('b');
+        }
+
+        ty::Int(..) | ty::Uint(..) | ty::Float(..) => {
+            // u<length><type-name> as vendor extended type
+            let mut s = String::from(match ty.kind() {
+                ty::Int(IntTy::I8) => "u2i8",
+                ty::Int(IntTy::I16) => "u3i16",
+                ty::Int(IntTy::I32) => "u3i32",
+                ty::Int(IntTy::I64) => "u3i64",
+                ty::Int(IntTy::I128) => "u4i128",
+                ty::Int(IntTy::Isize) => "u5isize",
+                ty::Uint(UintTy::U8) => "u2u8",
+                ty::Uint(UintTy::U16) => "u3u16",
+                ty::Uint(UintTy::U32) => "u3u32",
+                ty::Uint(UintTy::U64) => "u3u64",
+                ty::Uint(UintTy::U128) => "u4u128",
+                ty::Uint(UintTy::Usize) => "u5usize",
+                ty::Float(FloatTy::F32) => "u3f32",
+                ty::Float(FloatTy::F64) => "u3f64",
+                _ => "",
+            });
+            compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
+            typeid.push_str(&s);
+        }
+
+        ty::Char => {
+            // u4char as vendor extended type
+            let mut s = String::from("u4char");
+            compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
+            typeid.push_str(&s);
+        }
+
+        ty::Str => {
+            // u3str as vendor extended type
+            let mut s = String::from("u3str");
+            compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
+            typeid.push_str(&s);
+        }
+
+        ty::Never => {
+            // u5never as vendor extended type
+            let mut s = String::from("u5never");
+            compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
+            typeid.push_str(&s);
+        }
+
+        // Compound types
+        // () in Rust is equivalent to void return type in C
+        _ if ty.is_unit() => {
+            typeid.push('v');
+        }
+
+        // Sequence types
+        ty::Tuple(tys) => {
+            // u5tupleI<element-type1..element-typeN>E as vendor extended type
+            let mut s = String::from("u5tupleI");
+            for ty in tys.iter() {
+                s.push_str(&encode_ty(tcx, ty, dict, options));
+            }
+            s.push('E');
+            compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
+            typeid.push_str(&s);
+        }
+
+        ty::Array(ty0, len) => {
+            // A<array-length><element-type>
+            let mut s = String::from("A");
+            let _ = write!(s, "{}", &len.kind().try_to_scalar().unwrap().to_u64().unwrap());
+            s.push_str(&encode_ty(tcx, *ty0, dict, options));
+            compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
+            typeid.push_str(&s);
+        }
+
+        ty::Slice(ty0) => {
+            // u5sliceI<element-type>E as vendor extended type
+            let mut s = String::from("u5sliceI");
+            s.push_str(&encode_ty(tcx, *ty0, dict, options));
+            s.push('E');
+            compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
+            typeid.push_str(&s);
+        }
+
+        // User-defined types
+        ty::Adt(adt_def, substs) => {
+            let mut s = String::new();
+            let def_id = adt_def.0.did;
+            if options.contains(EncodeTyOptions::GENERALIZE_REPR_C) && adt_def.repr().c() {
+                // For for cross-language CFI support, the encoding must be compatible at the FFI
+                // boundary. For instance:
+                //
+                //     struct type1 {};
+                //     void foo(struct type1* bar) {}
+                //
+                // Is encoded as:
+                //
+                //     _ZTSFvP5type1E
+                //
+                // So, encode any repr(C) user-defined type for extern function types with the "C"
+                // calling convention (or extern types [i.e., ty::Foreign]) as <length><name>, where
+                // <name> is <unscoped-name>.
+                let name = tcx.item_name(def_id).to_string();
+                let _ = write!(s, "{}{}", name.len(), &name);
+                compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
+            } else {
+                // u<length><name>[I<element-type1..element-typeN>E], where <element-type> is
+                // <subst>, as vendor extended type.
+                let name = encode_ty_name(tcx, def_id);
+                let _ = write!(s, "u{}{}", name.len(), &name);
+                s.push_str(&encode_substs(tcx, substs, dict, options));
+                compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
+            }
+            typeid.push_str(&s);
+        }
+
+        ty::Foreign(def_id) => {
+            // <length><name>, where <name> is <unscoped-name>
+            let mut s = String::new();
+            let name = tcx.item_name(*def_id).to_string();
+            let _ = write!(s, "{}{}", name.len(), &name);
+            compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
+            typeid.push_str(&s);
+        }
+
+        // Function types
+        ty::FnDef(def_id, substs)
+        | ty::Closure(def_id, substs)
+        | ty::Generator(def_id, substs, ..) => {
+            // u<length><name>[I<element-type1..element-typeN>E], where <element-type> is <subst>,
+            // as vendor extended type.
+            let mut s = String::new();
+            let name = encode_ty_name(tcx, *def_id);
+            let _ = write!(s, "u{}{}", name.len(), &name);
+            s.push_str(&encode_substs(tcx, substs, dict, options));
+            compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
+            typeid.push_str(&s);
+        }
+
+        // Pointer types
+        ty::Ref(region, ty0, ..) => {
+            // [U3mut]u3refI<element-type>E as vendor extended type qualifier and type
+            let mut s = String::new();
+            s.push_str("u3refI");
+            s.push_str(&encode_ty(tcx, *ty0, dict, options));
+            s.push('E');
+            compress(dict, DictKey::Ty(tcx.mk_imm_ref(*region, *ty0), TyQ::None), &mut s);
+            if ty.is_mutable_ptr() {
+                s = format!("{}{}", "U3mut", &s);
+                compress(dict, DictKey::Ty(ty, TyQ::Mut), &mut s);
+            }
+            typeid.push_str(&s);
+        }
+
+        ty::RawPtr(tm) => {
+            // P[K]<element-type>
+            let mut s = String::new();
+            s.push_str(&encode_ty(tcx, tm.ty, dict, options));
+            if !ty.is_mutable_ptr() {
+                s = format!("{}{}", "K", &s);
+                compress(dict, DictKey::Ty(tm.ty, TyQ::Const), &mut s);
+            };
+            s = format!("{}{}", "P", &s);
+            compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
+            typeid.push_str(&s);
+        }
+
+        ty::FnPtr(fn_sig) => {
+            // PF<return-type><parameter-type1..parameter-typeN>E
+            let mut s = String::from("P");
+            s.push_str(&encode_fnsig(tcx, &fn_sig.skip_binder(), dict, TypeIdOptions::NO_OPTIONS));
+            compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
+            typeid.push_str(&s);
+        }
+
+        // Trait types
+        ty::Dynamic(predicates, region) => {
+            // u3dynI<element-type1[..element-typeN]>E, where <element-type> is <predicate>, as
+            // vendor extended type.
+            let mut s = String::from("u3dynI");
+            s.push_str(&encode_predicates(tcx, predicates, dict, options));
+            s.push_str(&encode_region(tcx, *region, dict, options));
+            s.push('E');
+            compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
+            typeid.push_str(&s);
+        }
+
+        // Unexpected types
+        ty::Bound(..)
+        | ty::Error(..)
+        | ty::GeneratorWitness(..)
+        | ty::Infer(..)
+        | ty::Opaque(..)
+        | ty::Param(..)
+        | ty::Placeholder(..)
+        | ty::Projection(..) => {
+            bug!("encode_ty: unexpected `{:?}`", ty.kind());
+        }
+    };
+
+    typeid
+}
+
+// Transforms a ty:Ty for being encoded and used in the substitution dictionary. It transforms all
+// c_void types into unit types unconditionally, and generalizes all pointers if
+// TransformTyOptions::GENERALIZE_POINTERS option is set.
+fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptions) -> Ty<'tcx> {
+    let mut ty = ty;
+
+    match ty.kind() {
+        ty::Bool
+        | ty::Int(..)
+        | ty::Uint(..)
+        | ty::Float(..)
+        | ty::Char
+        | ty::Str
+        | ty::Never
+        | ty::Foreign(..)
+        | ty::Dynamic(..) => {}
+
+        _ if ty.is_unit() => {}
+
+        ty::Tuple(tys) => {
+            ty = tcx.mk_tup(tys.iter().map(|ty| transform_ty(tcx, ty, options)));
+        }
+
+        ty::Array(ty0, len) => {
+            let len = len.kind().try_to_scalar().unwrap().to_u64().unwrap();
+            ty = tcx.mk_array(transform_ty(tcx, *ty0, options), len);
+        }
+
+        ty::Slice(ty0) => {
+            ty = tcx.mk_slice(transform_ty(tcx, *ty0, options));
+        }
+
+        ty::Adt(adt_def, substs) => {
+            if is_c_void_ty(tcx, ty) {
+                ty = tcx.mk_unit();
+            } else if options.contains(TransformTyOptions::GENERALIZE_REPR_C) && adt_def.repr().c()
+            {
+                ty = tcx.mk_adt(*adt_def, ty::List::empty());
+            } else if adt_def.repr().transparent() && adt_def.is_struct() {
+                let variant = adt_def.non_enum_variant();
+                let param_env = tcx.param_env(variant.def_id);
+                let field = variant.fields.iter().find(|field| {
+                    let ty = tcx.type_of(field.did);
+                    let is_zst =
+                        tcx.layout_of(param_env.and(ty)).map_or(false, |layout| layout.is_zst());
+                    !is_zst
+                });
+                if field.is_none() {
+                    // Transform repr(transparent) types without non-ZST field into ()
+                    ty = tcx.mk_unit();
+                } else {
+                    let ty0 = tcx.type_of(field.unwrap().did);
+                    // Generalize any repr(transparent) user-defined type that is either a pointer
+                    // or reference, and either references itself or any other type that contains or
+                    // references itself, to avoid a reference cycle.
+                    if ty0.is_any_ptr() && ty0.contains(ty) {
+                        ty = transform_ty(
+                            tcx,
+                            ty0,
+                            options | TransformTyOptions::GENERALIZE_POINTERS,
+                        );
+                    } else {
+                        ty = transform_ty(tcx, ty0, options);
+                    }
+                }
+            } else {
+                ty = tcx.mk_adt(*adt_def, transform_substs(tcx, substs, options));
+            }
+        }
+
+        ty::FnDef(def_id, substs) => {
+            ty = tcx.mk_fn_def(*def_id, transform_substs(tcx, substs, options));
+        }
+
+        ty::Closure(def_id, substs) => {
+            ty = tcx.mk_closure(*def_id, transform_substs(tcx, substs, options));
+        }
+
+        ty::Generator(def_id, substs, movability) => {
+            ty = tcx.mk_generator(*def_id, transform_substs(tcx, substs, options), *movability);
+        }
+
+        ty::Ref(region, ty0, ..) => {
+            if options.contains(TransformTyOptions::GENERALIZE_POINTERS) {
+                if ty.is_mutable_ptr() {
+                    ty = tcx.mk_mut_ref(tcx.lifetimes.re_static, tcx.mk_unit());
+                } else {
+                    ty = tcx.mk_imm_ref(tcx.lifetimes.re_static, tcx.mk_unit());
+                }
+            } else {
+                if ty.is_mutable_ptr() {
+                    ty = tcx.mk_mut_ref(*region, transform_ty(tcx, *ty0, options));
+                } else {
+                    ty = tcx.mk_imm_ref(*region, transform_ty(tcx, *ty0, options));
+                }
+            }
+        }
+
+        ty::RawPtr(tm) => {
+            if options.contains(TransformTyOptions::GENERALIZE_POINTERS) {
+                if ty.is_mutable_ptr() {
+                    ty = tcx.mk_mut_ptr(tcx.mk_unit());
+                } else {
+                    ty = tcx.mk_imm_ptr(tcx.mk_unit());
+                }
+            } else {
+                if ty.is_mutable_ptr() {
+                    ty = tcx.mk_mut_ptr(transform_ty(tcx, tm.ty, options));
+                } else {
+                    ty = tcx.mk_imm_ptr(transform_ty(tcx, tm.ty, options));
+                }
+            }
+        }
+
+        ty::FnPtr(fn_sig) => {
+            if options.contains(TransformTyOptions::GENERALIZE_POINTERS) {
+                ty = tcx.mk_imm_ptr(tcx.mk_unit());
+            } else {
+                let parameters: Vec<Ty<'tcx>> = fn_sig
+                    .skip_binder()
+                    .inputs()
+                    .iter()
+                    .map(|ty| transform_ty(tcx, *ty, options))
+                    .collect();
+                let output = transform_ty(tcx, fn_sig.skip_binder().output(), options);
+                ty = tcx.mk_fn_ptr(ty::Binder::bind_with_vars(
+                    tcx.mk_fn_sig(
+                        parameters.iter(),
+                        &output,
+                        fn_sig.c_variadic(),
+                        fn_sig.unsafety(),
+                        fn_sig.abi(),
+                    ),
+                    fn_sig.bound_vars(),
+                ));
+            }
+        }
+
+        ty::Bound(..)
+        | ty::Error(..)
+        | ty::GeneratorWitness(..)
+        | ty::Infer(..)
+        | ty::Opaque(..)
+        | ty::Param(..)
+        | ty::Placeholder(..)
+        | ty::Projection(..) => {
+            bug!("transform_ty: unexpected `{:?}`", ty.kind());
+        }
+    }
+
+    ty
+}
+
+/// Transforms substs for being encoded and used in the substitution dictionary.
+fn transform_substs<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    substs: SubstsRef<'tcx>,
+    options: TransformTyOptions,
+) -> SubstsRef<'tcx> {
+    let substs: Vec<GenericArg<'tcx>> = substs
+        .iter()
+        .map(|subst| {
+            if let GenericArgKind::Type(ty) = subst.unpack() {
+                if is_c_void_ty(tcx, ty) {
+                    tcx.mk_unit().into()
+                } else {
+                    transform_ty(tcx, ty, options).into()
+                }
+            } else {
+                subst
+            }
+        })
+        .collect();
+    tcx.mk_substs(substs.iter())
+}
+
+/// Returns a type metadata identifier for the specified FnAbi using the Itanium C++ ABI with vendor
+/// extended type qualifiers and types for Rust types that are not used at the FFI boundary.
+pub fn typeid_for_fnabi<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
+    options: TypeIdOptions,
+) -> String {
+    // A name is mangled by prefixing "_Z" to an encoding of its name, and in the case of functions
+    // its type.
+    let mut typeid = String::from("_Z");
+
+    // Clang uses the Itanium C++ ABI's virtual tables and RTTI typeinfo structure name as type
+    // metadata identifiers for function pointers. The typeinfo name encoding is a two-character
+    // code (i.e., 'TS') prefixed to the type encoding for the function.
+    typeid.push_str("TS");
+
+    // Function types are delimited by an "F..E" pair
+    typeid.push('F');
+
+    // A dictionary of substitution candidates used for compression (see
+    // https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-compression).
+    let mut dict: FxHashMap<DictKey<'tcx>, usize> = FxHashMap::default();
+
+    let mut encode_ty_options = EncodeTyOptions::from_bits(options.bits())
+        .unwrap_or_else(|| bug!("typeid_for_fnabi: invalid option(s) `{:?}`", options.bits()));
+    match fn_abi.conv {
+        Conv::C => {
+            encode_ty_options.insert(EncodeTyOptions::GENERALIZE_REPR_C);
+        }
+        _ => {
+            encode_ty_options.remove(EncodeTyOptions::GENERALIZE_REPR_C);
+        }
+    }
+
+    // Encode the return type
+    let transform_ty_options = TransformTyOptions::from_bits(options.bits())
+        .unwrap_or_else(|| bug!("typeid_for_fnabi: invalid option(s) `{:?}`", options.bits()));
+    let ty = transform_ty(tcx, fn_abi.ret.layout.ty, transform_ty_options);
+    typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options));
+
+    // Encode the parameter types
+    if !fn_abi.c_variadic {
+        if !fn_abi.args.is_empty() {
+            for arg in fn_abi.args.iter() {
+                let ty = transform_ty(tcx, arg.layout.ty, transform_ty_options);
+                typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options));
+            }
+        } else {
+            // Empty parameter lists, whether declared as () or conventionally as (void), are
+            // encoded with a void parameter specifier "v".
+            typeid.push('v');
+        }
+    } else {
+        for n in 0..fn_abi.fixed_count {
+            let ty = transform_ty(tcx, fn_abi.args[n].layout.ty, transform_ty_options);
+            typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options));
+        }
+
+        typeid.push('z');
+    }
+
+    // Close the "F..E" pair
+    typeid.push('E');
+
+    typeid
+}
+
+/// Returns a type metadata identifier for the specified FnSig using the Itanium C++ ABI with vendor
+/// extended type qualifiers and types for Rust types that are not used at the FFI boundary.
+pub fn typeid_for_fnsig<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    fn_sig: &FnSig<'tcx>,
+    options: TypeIdOptions,
+) -> String {
+    // A name is mangled by prefixing "_Z" to an encoding of its name, and in the case of functions
+    // its type.
+    let mut typeid = String::from("_Z");
+
+    // Clang uses the Itanium C++ ABI's virtual tables and RTTI typeinfo structure name as type
+    // metadata identifiers for function pointers. The typeinfo name encoding is a two-character
+    // code (i.e., 'TS') prefixed to the type encoding for the function.
+    typeid.push_str("TS");
+
+    // A dictionary of substitution candidates used for compression (see
+    // https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-compression).
+    let mut dict: FxHashMap<DictKey<'tcx>, usize> = FxHashMap::default();
+
+    // Encode the function signature
+    typeid.push_str(&encode_fnsig(tcx, fn_sig, &mut dict, options));
+
+    typeid
+}
diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs
index 13229a3995c..71fa5a44887 100644
--- a/compiler/rustc_symbol_mangling/src/v0.rs
+++ b/compiler/rustc_symbol_mangling/src/v0.rs
@@ -12,7 +12,6 @@ use rustc_middle::ty::{
     self, EarlyBinder, FloatTy, Instance, IntTy, Ty, TyCtxt, TypeVisitable, UintTy,
 };
 use rustc_span::symbol::kw;
-use rustc_target::abi::call::FnAbi;
 use rustc_target::abi::Integer;
 use rustc_target::spec::abi::Abi;
 
@@ -42,7 +41,7 @@ pub(super) fn mangle<'tcx>(
 
     // Append `::{shim:...#0}` to shims that can coexist with a non-shim instance.
     let shim_kind = match instance.def {
-        ty::InstanceDef::VtableShim(_) => Some("vtable"),
+        ty::InstanceDef::VTableShim(_) => Some("vtable"),
         ty::InstanceDef::ReifyShim(_) => Some("reify"),
 
         _ => None,
@@ -59,41 +58,6 @@ pub(super) fn mangle<'tcx>(
     std::mem::take(&mut cx.out)
 }
 
-pub(super) fn mangle_typeid_for_fnabi<'tcx>(
-    _tcx: TyCtxt<'tcx>,
-    fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
-) -> String {
-    // LLVM uses type metadata to allow IR modules to aggregate pointers by their types.[1] This
-    // type metadata is used by LLVM Control Flow Integrity to test whether a given pointer is
-    // associated with a type identifier (i.e., test type membership).
-    //
-    // Clang uses the Itanium C++ ABI's[2] virtual tables and RTTI typeinfo structure name[3] as
-    // type metadata identifiers for function pointers. The typeinfo name encoding is a
-    // two-character code (i.e., “TS”) prefixed to the type encoding for the function.
-    //
-    // For cross-language LLVM CFI support, a compatible encoding must be used by either
-    //
-    //  a. Using a superset of types that encompasses types used by Clang (i.e., Itanium C++ ABI's
-    //     type encodings[4]), or at least types used at the FFI boundary.
-    //  b. Reducing the types to the least common denominator between types used by Clang (or at
-    //     least types used at the FFI boundary) and Rust compilers (if even possible).
-    //  c. Creating a new ABI for cross-language CFI and using it for Clang and Rust compilers (and
-    //     possibly other compilers).
-    //
-    // Option (b) may weaken the protection for Rust-compiled only code, so it should be provided
-    // as an alternative to a Rust-specific encoding for when mixing Rust and C and C++ -compiled
-    // code. Option (c) would require changes to Clang to use the new ABI.
-    //
-    // [1] https://llvm.org/docs/TypeMetadata.html
-    // [2] https://itanium-cxx-abi.github.io/cxx-abi/abi.html
-    // [3] https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-special-vtables
-    // [4] https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-type
-    //
-    // FIXME(rcvalle): See comment above.
-    let arg_count = fn_abi.args.len() + fn_abi.ret.is_indirect() as usize;
-    format!("typeid{}", arg_count)
-}
-
 pub(super) fn mangle_typeid_for_trait_ref<'tcx>(
     tcx: TyCtxt<'tcx>,
     trait_ref: ty::PolyExistentialTraitRef<'tcx>,
diff --git a/compiler/rustc_target/src/abi/mod.rs b/compiler/rustc_target/src/abi/mod.rs
index d1eafd6ac5f..b35502d9ee4 100644
--- a/compiler/rustc_target/src/abi/mod.rs
+++ b/compiler/rustc_target/src/abi/mod.rs
@@ -1350,15 +1350,19 @@ impl<'a, Ty> Deref for TyAndLayout<'a, Ty> {
 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
 pub enum PointerKind {
     /// Most general case, we know no restrictions to tell LLVM.
-    Shared,
+    SharedMutable,
 
-    /// `&T` where `T` contains no `UnsafeCell`, is `noalias` and `readonly`.
+    /// `&T` where `T` contains no `UnsafeCell`, is `dereferenceable`, `noalias` and `readonly`.
     Frozen,
 
-    /// `&mut T` which is `noalias` but not `readonly`.
+    /// `&mut T` which is `dereferenceable` and `noalias` but not `readonly`.
     UniqueBorrowed,
 
-    /// `Box<T>`, unlike `UniqueBorrowed`, it also has `noalias` on returns.
+    /// `&mut !Unpin`, which is `dereferenceable` but neither `noalias` nor `readonly`.
+    UniqueBorrowedPinned,
+
+    /// `Box<T>`, which is `noalias` (even on return types, unlike the above) but neither `readonly`
+    /// nor `dereferenceable`.
     UniqueOwned,
 }
 
@@ -1372,7 +1376,7 @@ pub struct PointeeInfo {
 
 /// Used in `might_permit_raw_init` to indicate the kind of initialisation
 /// that is checked to be valid
-#[derive(Copy, Clone, Debug)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
 pub enum InitKind {
     Zero,
     Uninit,
@@ -1487,14 +1491,18 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
     ///
     /// `init_kind` indicates if the memory is zero-initialized or left uninitialized.
     ///
-    /// `strict` is an opt-in debugging flag added in #97323 that enables more checks.
+    /// This code is intentionally conservative, and will not detect
+    /// * zero init of an enum whose 0 variant does not allow zero initialization
+    /// * making uninitialized types who have a full valid range (ints, floats, raw pointers)
+    /// * Any form of invalid value being made inside an array (unless the value is uninhabited)
     ///
-    /// This is conservative: in doubt, it will answer `true`.
+    /// A strict form of these checks that uses const evaluation exists in
+    /// `rustc_const_eval::might_permit_raw_init`, and a tracking issue for making these checks
+    /// stricter is <https://github.com/rust-lang/rust/issues/66151>.
     ///
-    /// FIXME: Once we removed all the conservatism, we could alternatively
-    /// create an all-0/all-undef constant and run the const value validator to see if
-    /// this is a valid value for the given type.
-    pub fn might_permit_raw_init<C>(self, cx: &C, init_kind: InitKind, strict: bool) -> bool
+    /// FIXME: Once all the conservatism is removed from here, and the checks are ran by default,
+    /// we can use the const evaluation checks always instead.
+    pub fn might_permit_raw_init<C>(self, cx: &C, init_kind: InitKind) -> bool
     where
         Self: Copy,
         Ty: TyAbiInterface<'a, C>,
@@ -1507,13 +1515,8 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
                     s.valid_range(cx).contains(0)
                 }
                 InitKind::Uninit => {
-                    if strict {
-                        // The type must be allowed to be uninit (which means "is a union").
-                        s.is_uninit_valid()
-                    } else {
-                        // The range must include all values.
-                        s.is_always_valid(cx)
-                    }
+                    // The range must include all values.
+                    s.is_always_valid(cx)
                 }
             }
         };
@@ -1534,19 +1537,12 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
         // If we have not found an error yet, we need to recursively descend into fields.
         match &self.fields {
             FieldsShape::Primitive | FieldsShape::Union { .. } => {}
-            FieldsShape::Array { count, .. } => {
+            FieldsShape::Array { .. } => {
                 // FIXME(#66151): For now, we are conservative and do not check arrays by default.
-                if strict
-                    && *count > 0
-                    && !self.field(cx, 0).might_permit_raw_init(cx, init_kind, strict)
-                {
-                    // Found non empty array with a type that is unhappy about this kind of initialization
-                    return false;
-                }
             }
             FieldsShape::Arbitrary { offsets, .. } => {
                 for idx in 0..offsets.len() {
-                    if !self.field(cx, idx).might_permit_raw_init(cx, init_kind, strict) {
+                    if !self.field(cx, idx).might_permit_raw_init(cx, init_kind) {
                         // We found a field that is unhappy with this kind of initialization.
                         return false;
                     }
diff --git a/compiler/rustc_target/src/asm/aarch64.rs b/compiler/rustc_target/src/asm/aarch64.rs
index 25842049413..62a0f9fb034 100644
--- a/compiler/rustc_target/src/asm/aarch64.rs
+++ b/compiler/rustc_target/src/asm/aarch64.rs
@@ -1,6 +1,6 @@
 use super::{InlineAsmArch, InlineAsmType};
 use crate::spec::{RelocModel, Target};
-use rustc_data_structures::stable_set::FxHashSet;
+use rustc_data_structures::fx::FxHashSet;
 use rustc_macros::HashStable_Generic;
 use rustc_span::Symbol;
 use std::fmt;
diff --git a/compiler/rustc_target/src/asm/arm.rs b/compiler/rustc_target/src/asm/arm.rs
index aaa632333db..0db3eb6fcac 100644
--- a/compiler/rustc_target/src/asm/arm.rs
+++ b/compiler/rustc_target/src/asm/arm.rs
@@ -1,6 +1,6 @@
 use super::{InlineAsmArch, InlineAsmType};
 use crate::spec::{RelocModel, Target};
-use rustc_data_structures::stable_set::FxHashSet;
+use rustc_data_structures::fx::FxHashSet;
 use rustc_macros::HashStable_Generic;
 use rustc_span::{sym, Symbol};
 use std::fmt;
diff --git a/compiler/rustc_target/src/asm/riscv.rs b/compiler/rustc_target/src/asm/riscv.rs
index 987bf970529..e41bdc9a58c 100644
--- a/compiler/rustc_target/src/asm/riscv.rs
+++ b/compiler/rustc_target/src/asm/riscv.rs
@@ -1,6 +1,6 @@
 use super::{InlineAsmArch, InlineAsmType};
 use crate::spec::{RelocModel, Target};
-use rustc_data_structures::stable_set::FxHashSet;
+use rustc_data_structures::fx::FxHashSet;
 use rustc_macros::HashStable_Generic;
 use rustc_span::{sym, Symbol};
 use std::fmt;
diff --git a/compiler/rustc_target/src/asm/x86.rs b/compiler/rustc_target/src/asm/x86.rs
index e35035fd25a..238c365093f 100644
--- a/compiler/rustc_target/src/asm/x86.rs
+++ b/compiler/rustc_target/src/asm/x86.rs
@@ -1,6 +1,6 @@
 use super::{InlineAsmArch, InlineAsmType};
 use crate::spec::{RelocModel, Target};
-use rustc_data_structures::stable_set::FxHashSet;
+use rustc_data_structures::fx::FxHashSet;
 use rustc_macros::HashStable_Generic;
 use rustc_span::Symbol;
 use std::fmt;
diff --git a/compiler/rustc_target/src/spec/aarch64_linux_android.rs b/compiler/rustc_target/src/spec/aarch64_linux_android.rs
index 5e31859aaef..c85f7f62a42 100644
--- a/compiler/rustc_target/src/spec/aarch64_linux_android.rs
+++ b/compiler/rustc_target/src/spec/aarch64_linux_android.rs
@@ -17,6 +17,7 @@ pub fn target() -> Target {
             supported_sanitizers: SanitizerSet::CFI
                 | SanitizerSet::HWADDRESS
                 | SanitizerSet::MEMTAG
+                | SanitizerSet::SHADOWCALLSTACK
                 | SanitizerSet::ADDRESS,
             ..super::android_base::opts()
         },
diff --git a/compiler/rustc_target/src/spec/aarch64_nintendo_switch_freestanding.rs b/compiler/rustc_target/src/spec/aarch64_nintendo_switch_freestanding.rs
new file mode 100644
index 00000000000..1b7161fbb85
--- /dev/null
+++ b/compiler/rustc_target/src/spec/aarch64_nintendo_switch_freestanding.rs
@@ -0,0 +1,26 @@
+use super::{LinkerFlavor, LldFlavor, PanicStrategy, RelroLevel, Target, TargetOptions};
+
+const LINKER_SCRIPT: &str = include_str!("./aarch64_nintendo_switch_freestanding_linker_script.ld");
+
+/// A base target for Nintendo Switch devices using a pure LLVM toolchain.
+pub fn target() -> Target {
+    Target {
+        llvm_target: "aarch64-unknown-none".into(),
+        pointer_width: 64,
+        data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".into(),
+        arch: "aarch64".into(),
+        options: TargetOptions {
+            linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
+            linker: Some("rust-lld".into()),
+            link_script: Some(LINKER_SCRIPT.into()),
+            os: "horizon".into(),
+            max_atomic_width: Some(128),
+            panic_strategy: PanicStrategy::Abort,
+            position_independent_executables: true,
+            dynamic_linking: true,
+            executables: true,
+            relro_level: RelroLevel::Off,
+            ..Default::default()
+        },
+    }
+}
diff --git a/compiler/rustc_target/src/spec/aarch64_nintendo_switch_freestanding_linker_script.ld b/compiler/rustc_target/src/spec/aarch64_nintendo_switch_freestanding_linker_script.ld
new file mode 100644
index 00000000000..f3441e65937
--- /dev/null
+++ b/compiler/rustc_target/src/spec/aarch64_nintendo_switch_freestanding_linker_script.ld
@@ -0,0 +1,78 @@
+OUTPUT_FORMAT(elf64-littleaarch64)
+OUTPUT_ARCH(aarch64)
+ENTRY(_start)
+
+PHDRS
+{
+  text PT_LOAD FLAGS(5);
+  rodata PT_LOAD FLAGS(4);
+  data PT_LOAD FLAGS(6);
+  bss PT_LOAD FLAGS(6);
+  dynamic PT_DYNAMIC;
+}
+
+SECTIONS
+{
+  . = 0;
+
+  .text : ALIGN(0x1000) {
+    HIDDEN(__text_start = .);
+    KEEP(*(.text.jmp))
+
+    . = 0x80;
+
+    *(.text .text.*)
+    *(.plt .plt.*)
+  }
+
+  /* Read-only sections */
+
+  . = ALIGN(0x1000);
+
+  .module_name : { *(.module_name) } :rodata
+
+  .rodata : { *(.rodata .rodata.*) } :rodata
+  .hash : { *(.hash) }
+  .dynsym : { *(.dynsym .dynsym.*) }
+  .dynstr : { *(.dynstr .dynstr.*) }
+  .rela.dyn : { *(.rela.dyn) }
+
+  .eh_frame : {
+    HIDDEN(__eh_frame_start = .);
+    *(.eh_frame .eh_frame.*)
+    HIDDEN(__eh_frame_end = .);
+  }
+
+  .eh_frame_hdr : {
+    HIDDEN(__eh_frame_hdr_start = .);
+    *(.eh_frame_hdr .eh_frame_hdr.*)
+    HIDDEN(__eh_frame_hdr_end = .);
+  }
+
+  /* Read-write sections */
+
+   . = ALIGN(0x1000);
+
+  .data : {
+    *(.data .data.*)
+    *(.got .got.*)
+    *(.got.plt .got.plt.*)
+  } :data
+
+  .dynamic : {
+    HIDDEN(__dynamic_start = .);
+    *(.dynamic)
+  }
+
+  /* BSS section */
+
+  . = ALIGN(0x1000);
+
+  .bss : {
+    HIDDEN(__bss_start = .);
+    *(.bss .bss.*)
+    *(COMMON)
+    . = ALIGN(8);
+    HIDDEN(__bss_end = .);
+  } :bss
+}
diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_none.rs b/compiler/rustc_target/src/spec/aarch64_unknown_none.rs
index 2c7834c225b..d3fd7051a12 100644
--- a/compiler/rustc_target/src/spec/aarch64_unknown_none.rs
+++ b/compiler/rustc_target/src/spec/aarch64_unknown_none.rs
@@ -13,7 +13,6 @@ pub fn target() -> Target {
         linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
         linker: Some("rust-lld".into()),
         features: "+strict-align,+neon,+fp-armv8".into(),
-        executables: true,
         relocation_model: RelocModel::Static,
         disable_redzone: true,
         max_atomic_width: Some(128),
diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_none_softfloat.rs b/compiler/rustc_target/src/spec/aarch64_unknown_none_softfloat.rs
index 1b6525a7c69..6316abe1ba9 100644
--- a/compiler/rustc_target/src/spec/aarch64_unknown_none_softfloat.rs
+++ b/compiler/rustc_target/src/spec/aarch64_unknown_none_softfloat.rs
@@ -14,7 +14,6 @@ pub fn target() -> Target {
         linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
         linker: Some("rust-lld".into()),
         features: "+strict-align,-neon,-fp-armv8".into(),
-        executables: true,
         relocation_model: RelocModel::Static,
         disable_redzone: true,
         max_atomic_width: Some(128),
diff --git a/compiler/rustc_target/src/spec/apple_base.rs b/compiler/rustc_target/src/spec/apple_base.rs
index 713dc9a1f0e..9bfae46ef32 100644
--- a/compiler/rustc_target/src/spec/apple_base.rs
+++ b/compiler/rustc_target/src/spec/apple_base.rs
@@ -25,7 +25,6 @@ pub fn opts(os: &'static str) -> TargetOptions {
         function_sections: false,
         dynamic_linking: true,
         linker_is_gnu: false,
-        executables: true,
         families: cvs!["unix"],
         is_like_osx: true,
         default_dwarf_version: 2,
diff --git a/compiler/rustc_target/src/spec/apple_sdk_base.rs b/compiler/rustc_target/src/spec/apple_sdk_base.rs
index ecb6cbd9f8a..0328ea98c48 100644
--- a/compiler/rustc_target/src/spec/apple_sdk_base.rs
+++ b/compiler/rustc_target/src/spec/apple_sdk_base.rs
@@ -54,7 +54,6 @@ pub fn opts(os: &'static str, arch: Arch) -> TargetOptions {
         abi: target_abi(arch).into(),
         cpu: target_cpu(arch).into(),
         dynamic_linking: false,
-        executables: true,
         link_env_remove: link_env_remove(arch),
         has_thread_local: false,
         ..super::apple_base::opts(os)
diff --git a/compiler/rustc_target/src/spec/armebv7r_none_eabi.rs b/compiler/rustc_target/src/spec/armebv7r_none_eabi.rs
index 0cb18f17310..511693abe98 100644
--- a/compiler/rustc_target/src/spec/armebv7r_none_eabi.rs
+++ b/compiler/rustc_target/src/spec/armebv7r_none_eabi.rs
@@ -14,7 +14,6 @@ pub fn target() -> Target {
             abi: "eabi".into(),
             endian: Endian::Big,
             linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
-            executables: true,
             linker: Some("rust-lld".into()),
             relocation_model: RelocModel::Static,
             panic_strategy: PanicStrategy::Abort,
diff --git a/compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs b/compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs
index a5b7c12cc7b..5df4a0a1583 100644
--- a/compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs
+++ b/compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs
@@ -14,7 +14,6 @@ pub fn target() -> Target {
             abi: "eabihf".into(),
             endian: Endian::Big,
             linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
-            executables: true,
             linker: Some("rust-lld".into()),
             relocation_model: RelocModel::Static,
             panic_strategy: PanicStrategy::Abort,
diff --git a/compiler/rustc_target/src/spec/armv6k_nintendo_3ds.rs b/compiler/rustc_target/src/spec/armv6k_nintendo_3ds.rs
index 8c2a9bcfde6..1bba3939397 100644
--- a/compiler/rustc_target/src/spec/armv6k_nintendo_3ds.rs
+++ b/compiler/rustc_target/src/spec/armv6k_nintendo_3ds.rs
@@ -23,7 +23,6 @@ pub fn target() -> Target {
             abi: "eabihf".into(),
             linker_flavor: LinkerFlavor::Gcc,
             cpu: "mpcore".into(),
-            executables: true,
             families: cvs!["unix"],
             linker: Some("arm-none-eabi-gcc".into()),
             relocation_model: RelocModel::Static,
diff --git a/compiler/rustc_target/src/spec/armv7a_none_eabi.rs b/compiler/rustc_target/src/spec/armv7a_none_eabi.rs
index ff649434312..cb5cbe15836 100644
--- a/compiler/rustc_target/src/spec/armv7a_none_eabi.rs
+++ b/compiler/rustc_target/src/spec/armv7a_none_eabi.rs
@@ -22,7 +22,6 @@ pub fn target() -> Target {
         linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
         linker: Some("rust-lld".into()),
         features: "+v7,+thumb2,+soft-float,-neon,+strict-align".into(),
-        executables: true,
         relocation_model: RelocModel::Static,
         disable_redzone: true,
         max_atomic_width: Some(64),
diff --git a/compiler/rustc_target/src/spec/armv7a_none_eabihf.rs b/compiler/rustc_target/src/spec/armv7a_none_eabihf.rs
index c0321d0bef4..fb5dd2e7574 100644
--- a/compiler/rustc_target/src/spec/armv7a_none_eabihf.rs
+++ b/compiler/rustc_target/src/spec/armv7a_none_eabihf.rs
@@ -13,7 +13,6 @@ pub fn target() -> Target {
         linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
         linker: Some("rust-lld".into()),
         features: "+v7,+vfp3,-d32,+thumb2,-neon,+strict-align".into(),
-        executables: true,
         relocation_model: RelocModel::Static,
         disable_redzone: true,
         max_atomic_width: Some(64),
diff --git a/compiler/rustc_target/src/spec/armv7r_none_eabi.rs b/compiler/rustc_target/src/spec/armv7r_none_eabi.rs
index 2c3f79cc58b..5f1da09b317 100644
--- a/compiler/rustc_target/src/spec/armv7r_none_eabi.rs
+++ b/compiler/rustc_target/src/spec/armv7r_none_eabi.rs
@@ -13,7 +13,6 @@ pub fn target() -> Target {
         options: TargetOptions {
             abi: "eabi".into(),
             linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
-            executables: true,
             linker: Some("rust-lld".into()),
             relocation_model: RelocModel::Static,
             panic_strategy: PanicStrategy::Abort,
diff --git a/compiler/rustc_target/src/spec/armv7r_none_eabihf.rs b/compiler/rustc_target/src/spec/armv7r_none_eabihf.rs
index 5c82e768483..0038ed0df8b 100644
--- a/compiler/rustc_target/src/spec/armv7r_none_eabihf.rs
+++ b/compiler/rustc_target/src/spec/armv7r_none_eabihf.rs
@@ -13,7 +13,6 @@ pub fn target() -> Target {
         options: TargetOptions {
             abi: "eabihf".into(),
             linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
-            executables: true,
             linker: Some("rust-lld".into()),
             relocation_model: RelocModel::Static,
             panic_strategy: PanicStrategy::Abort,
diff --git a/compiler/rustc_target/src/spec/avr_gnu_base.rs b/compiler/rustc_target/src/spec/avr_gnu_base.rs
index 4fd6c06394d..1d441e558dd 100644
--- a/compiler/rustc_target/src/spec/avr_gnu_base.rs
+++ b/compiler/rustc_target/src/spec/avr_gnu_base.rs
@@ -16,7 +16,6 @@ pub fn target(target_cpu: &'static str, mmcu: &'static str) -> Target {
             exe_suffix: ".elf".into(),
 
             linker: Some("avr-gcc".into()),
-            executables: true,
             eh_frame_header: false,
             pre_link_args: TargetOptions::link_args(LinkerFlavor::Gcc, &[mmcu]),
             late_link_args: TargetOptions::link_args(LinkerFlavor::Gcc, &["-lgcc"]),
diff --git a/compiler/rustc_target/src/spec/bpf_base.rs b/compiler/rustc_target/src/spec/bpf_base.rs
index 4fa6e12f5ba..3c4da6f883d 100644
--- a/compiler/rustc_target/src/spec/bpf_base.rs
+++ b/compiler/rustc_target/src/spec/bpf_base.rs
@@ -7,7 +7,6 @@ pub fn opts(endian: Endian) -> TargetOptions {
         endian,
         linker_flavor: LinkerFlavor::BpfLinker,
         atomic_cas: false,
-        executables: true,
         dynamic_linking: true,
         no_builtins: true,
         panic_strategy: PanicStrategy::Abort,
diff --git a/compiler/rustc_target/src/spec/dragonfly_base.rs b/compiler/rustc_target/src/spec/dragonfly_base.rs
index c1e469746cb..de2be781796 100644
--- a/compiler/rustc_target/src/spec/dragonfly_base.rs
+++ b/compiler/rustc_target/src/spec/dragonfly_base.rs
@@ -4,7 +4,6 @@ pub fn opts() -> TargetOptions {
     TargetOptions {
         os: "dragonfly".into(),
         dynamic_linking: true,
-        executables: true,
         families: cvs!["unix"],
         has_rpath: true,
         position_independent_executables: true,
diff --git a/compiler/rustc_target/src/spec/freebsd_base.rs b/compiler/rustc_target/src/spec/freebsd_base.rs
index 36312d26e37..8c141aaaec3 100644
--- a/compiler/rustc_target/src/spec/freebsd_base.rs
+++ b/compiler/rustc_target/src/spec/freebsd_base.rs
@@ -4,7 +4,6 @@ pub fn opts() -> TargetOptions {
     TargetOptions {
         os: "freebsd".into(),
         dynamic_linking: true,
-        executables: true,
         families: cvs!["unix"],
         has_rpath: true,
         position_independent_executables: true,
diff --git a/compiler/rustc_target/src/spec/fuchsia_base.rs b/compiler/rustc_target/src/spec/fuchsia_base.rs
index b02b70f76ee..df1e3275f73 100644
--- a/compiler/rustc_target/src/spec/fuchsia_base.rs
+++ b/compiler/rustc_target/src/spec/fuchsia_base.rs
@@ -23,7 +23,6 @@ pub fn opts() -> TargetOptions {
         linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
         linker: Some("rust-lld".into()),
         dynamic_linking: true,
-        executables: true,
         families: cvs!["unix"],
         pre_link_args,
         pre_link_objects: crt_objects::new(&[
diff --git a/compiler/rustc_target/src/spec/haiku_base.rs b/compiler/rustc_target/src/spec/haiku_base.rs
index 61c05a2bdb6..8ab874410aa 100644
--- a/compiler/rustc_target/src/spec/haiku_base.rs
+++ b/compiler/rustc_target/src/spec/haiku_base.rs
@@ -4,7 +4,6 @@ pub fn opts() -> TargetOptions {
     TargetOptions {
         os: "haiku".into(),
         dynamic_linking: true,
-        executables: true,
         families: cvs!["unix"],
         relro_level: RelroLevel::Full,
         ..Default::default()
diff --git a/compiler/rustc_target/src/spec/hermit_base.rs b/compiler/rustc_target/src/spec/hermit_base.rs
index e43153177f0..562ccef7eba 100644
--- a/compiler/rustc_target/src/spec/hermit_base.rs
+++ b/compiler/rustc_target/src/spec/hermit_base.rs
@@ -10,7 +10,6 @@ pub fn opts() -> TargetOptions {
         os: "hermit".into(),
         linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
         linker: Some("rust-lld".into()),
-        executables: true,
         has_thread_local: true,
         pre_link_args,
         panic_strategy: PanicStrategy::Abort,
diff --git a/compiler/rustc_target/src/spec/hexagon_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/hexagon_unknown_linux_musl.rs
index 80cf09517cc..cc2c78c69fe 100644
--- a/compiler/rustc_target/src/spec/hexagon_unknown_linux_musl.rs
+++ b/compiler/rustc_target/src/spec/hexagon_unknown_linux_musl.rs
@@ -11,7 +11,6 @@ pub fn target() -> Target {
     base.has_rpath = true;
     base.linker_is_gnu = false;
     base.dynamic_linking = true;
-    base.executables = true;
 
     base.c_enum_min_bits = 8;
 
diff --git a/compiler/rustc_target/src/spec/illumos_base.rs b/compiler/rustc_target/src/spec/illumos_base.rs
index b0e1b109be1..77e000474b8 100644
--- a/compiler/rustc_target/src/spec/illumos_base.rs
+++ b/compiler/rustc_target/src/spec/illumos_base.rs
@@ -27,7 +27,6 @@ pub fn opts() -> TargetOptions {
     TargetOptions {
         os: "illumos".into(),
         dynamic_linking: true,
-        executables: true,
         has_rpath: true,
         families: cvs!["unix"],
         is_like_solaris: true,
diff --git a/compiler/rustc_target/src/spec/l4re_base.rs b/compiler/rustc_target/src/spec/l4re_base.rs
index 7a051532f82..a08756861e5 100644
--- a/compiler/rustc_target/src/spec/l4re_base.rs
+++ b/compiler/rustc_target/src/spec/l4re_base.rs
@@ -5,7 +5,6 @@ pub fn opts() -> TargetOptions {
         os: "l4re".into(),
         env: "uclibc".into(),
         linker_flavor: LinkerFlavor::L4Bender,
-        executables: true,
         panic_strategy: PanicStrategy::Abort,
         linker: Some("l4-bender".into()),
         linker_is_gnu: false,
diff --git a/compiler/rustc_target/src/spec/linux_base.rs b/compiler/rustc_target/src/spec/linux_base.rs
index 0f79ada0d93..f4fce3b4050 100644
--- a/compiler/rustc_target/src/spec/linux_base.rs
+++ b/compiler/rustc_target/src/spec/linux_base.rs
@@ -4,7 +4,6 @@ pub fn opts() -> TargetOptions {
     TargetOptions {
         os: "linux".into(),
         dynamic_linking: true,
-        executables: true,
         families: cvs!["unix"],
         has_rpath: true,
         position_independent_executables: true,
diff --git a/compiler/rustc_target/src/spec/mipsel_sony_psp.rs b/compiler/rustc_target/src/spec/mipsel_sony_psp.rs
index e3522de6de0..cfc8ec21c2a 100644
--- a/compiler/rustc_target/src/spec/mipsel_sony_psp.rs
+++ b/compiler/rustc_target/src/spec/mipsel_sony_psp.rs
@@ -18,7 +18,6 @@ pub fn target() -> Target {
             vendor: "sony".into(),
             linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
             cpu: "mips2".into(),
-            executables: true,
             linker: Some("rust-lld".into()),
             relocation_model: RelocModel::Static,
 
diff --git a/compiler/rustc_target/src/spec/mipsel_unknown_none.rs b/compiler/rustc_target/src/spec/mipsel_unknown_none.rs
index 736af15cf44..fe2aa2de871 100644
--- a/compiler/rustc_target/src/spec/mipsel_unknown_none.rs
+++ b/compiler/rustc_target/src/spec/mipsel_unknown_none.rs
@@ -17,7 +17,6 @@ pub fn target() -> Target {
             cpu: "mips32r2".into(),
             features: "+mips32r2,+soft-float,+noabicalls".into(),
             max_atomic_width: Some(32),
-            executables: true,
             linker: Some("rust-lld".into()),
             panic_strategy: PanicStrategy::Abort,
             relocation_model: RelocModel::Static,
diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs
index 48ccb10f214..f7abeafd38f 100644
--- a/compiler/rustc_target/src/spec/mod.rs
+++ b/compiler/rustc_target/src/spec/mod.rs
@@ -618,6 +618,7 @@ bitflags::bitflags! {
         const HWADDRESS = 1 << 4;
         const CFI     = 1 << 5;
         const MEMTAG  = 1 << 6;
+        const SHADOWCALLSTACK = 1 << 7;
     }
 }
 
@@ -632,6 +633,7 @@ impl SanitizerSet {
             SanitizerSet::LEAK => "leak",
             SanitizerSet::MEMORY => "memory",
             SanitizerSet::MEMTAG => "memtag",
+            SanitizerSet::SHADOWCALLSTACK => "shadow-call-stack",
             SanitizerSet::THREAD => "thread",
             SanitizerSet::HWADDRESS => "hwaddress",
             _ => return None,
@@ -666,6 +668,7 @@ impl IntoIterator for SanitizerSet {
             SanitizerSet::LEAK,
             SanitizerSet::MEMORY,
             SanitizerSet::MEMTAG,
+            SanitizerSet::SHADOWCALLSTACK,
             SanitizerSet::THREAD,
             SanitizerSet::HWADDRESS,
         ]
@@ -1035,6 +1038,8 @@ supported_targets! {
 
     ("armv6k-nintendo-3ds", armv6k_nintendo_3ds),
 
+    ("aarch64-nintendo-switch-freestanding", aarch64_nintendo_switch_freestanding),
+
     ("armv7-unknown-linux-uclibceabi", armv7_unknown_linux_uclibceabi),
     ("armv7-unknown-linux-uclibceabihf", armv7_unknown_linux_uclibceabihf),
 
@@ -1212,8 +1217,7 @@ pub struct TargetOptions {
     pub dynamic_linking: bool,
     /// If dynamic linking is available, whether only cdylibs are supported.
     pub only_cdylib: bool,
-    /// Whether executables are available on this target. iOS, for example, only allows static
-    /// libraries. Defaults to false.
+    /// Whether executables are available on this target. Defaults to true.
     pub executables: bool,
     /// Relocation model to use in object file. Corresponds to `llc
     /// -relocation-model=$relocation_model`. Defaults to `Pic`.
@@ -1520,7 +1524,7 @@ impl Default for TargetOptions {
             features: "".into(),
             dynamic_linking: false,
             only_cdylib: false,
-            executables: false,
+            executables: true,
             relocation_model: RelocModel::Pic,
             code_model: None,
             tls_model: TlsModel::GeneralDynamic,
@@ -1959,6 +1963,7 @@ impl Target {
                                 Some("leak") => SanitizerSet::LEAK,
                                 Some("memory") => SanitizerSet::MEMORY,
                                 Some("memtag") => SanitizerSet::MEMTAG,
+                                Some("shadow-call-stack") => SanitizerSet::SHADOWCALLSTACK,
                                 Some("thread") => SanitizerSet::THREAD,
                                 Some("hwaddress") => SanitizerSet::HWADDRESS,
                                 Some(s) => return Err(format!("unknown sanitizer {}", s)),
diff --git a/compiler/rustc_target/src/spec/msp430_none_elf.rs b/compiler/rustc_target/src/spec/msp430_none_elf.rs
index cedacb60f31..6b09386ae3e 100644
--- a/compiler/rustc_target/src/spec/msp430_none_elf.rs
+++ b/compiler/rustc_target/src/spec/msp430_none_elf.rs
@@ -9,7 +9,6 @@ pub fn target() -> Target {
 
         options: TargetOptions {
             c_int_width: "16".into(),
-            executables: true,
 
             // The LLVM backend currently can't generate object files. To
             // workaround this LLVM generates assembly files which then we feed
diff --git a/compiler/rustc_target/src/spec/msvc_base.rs b/compiler/rustc_target/src/spec/msvc_base.rs
index c4df4b546e3..edb30b72bf6 100644
--- a/compiler/rustc_target/src/spec/msvc_base.rs
+++ b/compiler/rustc_target/src/spec/msvc_base.rs
@@ -7,7 +7,6 @@ pub fn opts() -> TargetOptions {
 
     TargetOptions {
         linker_flavor: LinkerFlavor::Msvc,
-        executables: true,
         is_like_windows: true,
         is_like_msvc: true,
         lld_flavor: LldFlavor::Link,
diff --git a/compiler/rustc_target/src/spec/netbsd_base.rs b/compiler/rustc_target/src/spec/netbsd_base.rs
index 40ef04ba043..be94ea23465 100644
--- a/compiler/rustc_target/src/spec/netbsd_base.rs
+++ b/compiler/rustc_target/src/spec/netbsd_base.rs
@@ -4,7 +4,6 @@ pub fn opts() -> TargetOptions {
     TargetOptions {
         os: "netbsd".into(),
         dynamic_linking: true,
-        executables: true,
         families: cvs!["unix"],
         no_default_libraries: false,
         has_rpath: true,
diff --git a/compiler/rustc_target/src/spec/nvptx64_nvidia_cuda.rs b/compiler/rustc_target/src/spec/nvptx64_nvidia_cuda.rs
index 9d94ed8aa48..1c5b68001b9 100644
--- a/compiler/rustc_target/src/spec/nvptx64_nvidia_cuda.rs
+++ b/compiler/rustc_target/src/spec/nvptx64_nvidia_cuda.rs
@@ -26,7 +26,6 @@ pub fn target() -> Target {
 
             // Needed to use `dylib` and `bin` crate types and the linker.
             dynamic_linking: true,
-            executables: true,
 
             // Avoid using dylib because it contain metadata not supported
             // by LLVM NVPTX backend.
diff --git a/compiler/rustc_target/src/spec/openbsd_base.rs b/compiler/rustc_target/src/spec/openbsd_base.rs
index 51cecdd47ea..e7db14e05a4 100644
--- a/compiler/rustc_target/src/spec/openbsd_base.rs
+++ b/compiler/rustc_target/src/spec/openbsd_base.rs
@@ -4,7 +4,6 @@ pub fn opts() -> TargetOptions {
     TargetOptions {
         os: "openbsd".into(),
         dynamic_linking: true,
-        executables: true,
         families: cvs!["unix"],
         has_rpath: true,
         abi_return_struct_as_int: true,
diff --git a/compiler/rustc_target/src/spec/redox_base.rs b/compiler/rustc_target/src/spec/redox_base.rs
index 1878cc3fc11..468fe478549 100644
--- a/compiler/rustc_target/src/spec/redox_base.rs
+++ b/compiler/rustc_target/src/spec/redox_base.rs
@@ -5,7 +5,6 @@ pub fn opts() -> TargetOptions {
         os: "redox".into(),
         env: "relibc".into(),
         dynamic_linking: true,
-        executables: true,
         families: cvs!["unix"],
         has_rpath: true,
         position_independent_executables: true,
diff --git a/compiler/rustc_target/src/spec/riscv32i_unknown_none_elf.rs b/compiler/rustc_target/src/spec/riscv32i_unknown_none_elf.rs
index 7124e2df9b3..232139db6ca 100644
--- a/compiler/rustc_target/src/spec/riscv32i_unknown_none_elf.rs
+++ b/compiler/rustc_target/src/spec/riscv32i_unknown_none_elf.rs
@@ -14,7 +14,6 @@ pub fn target() -> Target {
             cpu: "generic-rv32".into(),
             max_atomic_width: Some(0),
             atomic_cas: false,
-            executables: true,
             panic_strategy: PanicStrategy::Abort,
             relocation_model: RelocModel::Static,
             emit_debug_gdb_scripts: false,
diff --git a/compiler/rustc_target/src/spec/riscv32im_unknown_none_elf.rs b/compiler/rustc_target/src/spec/riscv32im_unknown_none_elf.rs
index 508982eed68..3e5d2887f43 100644
--- a/compiler/rustc_target/src/spec/riscv32im_unknown_none_elf.rs
+++ b/compiler/rustc_target/src/spec/riscv32im_unknown_none_elf.rs
@@ -15,7 +15,6 @@ pub fn target() -> Target {
             max_atomic_width: Some(0),
             atomic_cas: false,
             features: "+m".into(),
-            executables: true,
             panic_strategy: PanicStrategy::Abort,
             relocation_model: RelocModel::Static,
             emit_debug_gdb_scripts: false,
diff --git a/compiler/rustc_target/src/spec/riscv32imac_unknown_none_elf.rs b/compiler/rustc_target/src/spec/riscv32imac_unknown_none_elf.rs
index f2bd6249f0a..99317b9f118 100644
--- a/compiler/rustc_target/src/spec/riscv32imac_unknown_none_elf.rs
+++ b/compiler/rustc_target/src/spec/riscv32imac_unknown_none_elf.rs
@@ -14,7 +14,6 @@ pub fn target() -> Target {
             cpu: "generic-rv32".into(),
             max_atomic_width: Some(32),
             features: "+m,+a,+c".into(),
-            executables: true,
             panic_strategy: PanicStrategy::Abort,
             relocation_model: RelocModel::Static,
             emit_debug_gdb_scripts: false,
diff --git a/compiler/rustc_target/src/spec/riscv32imac_unknown_xous_elf.rs b/compiler/rustc_target/src/spec/riscv32imac_unknown_xous_elf.rs
index b46ca159370..a5de645c984 100644
--- a/compiler/rustc_target/src/spec/riscv32imac_unknown_xous_elf.rs
+++ b/compiler/rustc_target/src/spec/riscv32imac_unknown_xous_elf.rs
@@ -15,7 +15,6 @@ pub fn target() -> Target {
             cpu: "generic-rv32".into(),
             max_atomic_width: Some(32),
             features: "+m,+a,+c".into(),
-            executables: true,
             panic_strategy: PanicStrategy::Abort,
             relocation_model: RelocModel::Static,
             ..Default::default()
diff --git a/compiler/rustc_target/src/spec/riscv32imc_esp_espidf.rs b/compiler/rustc_target/src/spec/riscv32imc_esp_espidf.rs
index 0200862c7e0..03baef65c0d 100644
--- a/compiler/rustc_target/src/spec/riscv32imc_esp_espidf.rs
+++ b/compiler/rustc_target/src/spec/riscv32imc_esp_espidf.rs
@@ -26,7 +26,6 @@ pub fn target() -> Target {
             atomic_cas: true,
 
             features: "+m,+c".into(),
-            executables: true,
             panic_strategy: PanicStrategy::Abort,
             relocation_model: RelocModel::Static,
             emit_debug_gdb_scripts: false,
diff --git a/compiler/rustc_target/src/spec/riscv32imc_unknown_none_elf.rs b/compiler/rustc_target/src/spec/riscv32imc_unknown_none_elf.rs
index 4216968cb77..bf510d204a7 100644
--- a/compiler/rustc_target/src/spec/riscv32imc_unknown_none_elf.rs
+++ b/compiler/rustc_target/src/spec/riscv32imc_unknown_none_elf.rs
@@ -15,7 +15,6 @@ pub fn target() -> Target {
             max_atomic_width: Some(0),
             atomic_cas: false,
             features: "+m,+c".into(),
-            executables: true,
             panic_strategy: PanicStrategy::Abort,
             relocation_model: RelocModel::Static,
             emit_debug_gdb_scripts: false,
diff --git a/compiler/rustc_target/src/spec/riscv64gc_unknown_none_elf.rs b/compiler/rustc_target/src/spec/riscv64gc_unknown_none_elf.rs
index 2a93459ef4f..03b3cfd1eb1 100644
--- a/compiler/rustc_target/src/spec/riscv64gc_unknown_none_elf.rs
+++ b/compiler/rustc_target/src/spec/riscv64gc_unknown_none_elf.rs
@@ -15,7 +15,6 @@ pub fn target() -> Target {
             cpu: "generic-rv64".into(),
             max_atomic_width: Some(64),
             features: "+m,+a,+f,+d,+c".into(),
-            executables: true,
             panic_strategy: PanicStrategy::Abort,
             relocation_model: RelocModel::Static,
             code_model: Some(CodeModel::Medium),
diff --git a/compiler/rustc_target/src/spec/riscv64imac_unknown_none_elf.rs b/compiler/rustc_target/src/spec/riscv64imac_unknown_none_elf.rs
index 6a8d8a97de6..2a94c9dd233 100644
--- a/compiler/rustc_target/src/spec/riscv64imac_unknown_none_elf.rs
+++ b/compiler/rustc_target/src/spec/riscv64imac_unknown_none_elf.rs
@@ -14,7 +14,6 @@ pub fn target() -> Target {
             cpu: "generic-rv64".into(),
             max_atomic_width: Some(64),
             features: "+m,+a,+c".into(),
-            executables: true,
             panic_strategy: PanicStrategy::Abort,
             relocation_model: RelocModel::Static,
             code_model: Some(CodeModel::Medium),
diff --git a/compiler/rustc_target/src/spec/solaris_base.rs b/compiler/rustc_target/src/spec/solaris_base.rs
index d61e1b2ec10..b7e8e8cf7f5 100644
--- a/compiler/rustc_target/src/spec/solaris_base.rs
+++ b/compiler/rustc_target/src/spec/solaris_base.rs
@@ -4,7 +4,6 @@ pub fn opts() -> TargetOptions {
     TargetOptions {
         os: "solaris".into(),
         dynamic_linking: true,
-        executables: true,
         has_rpath: true,
         families: cvs!["unix"],
         is_like_solaris: true,
diff --git a/compiler/rustc_target/src/spec/solid_base.rs b/compiler/rustc_target/src/spec/solid_base.rs
index c5602a4513a..c585a6cd58e 100644
--- a/compiler/rustc_target/src/spec/solid_base.rs
+++ b/compiler/rustc_target/src/spec/solid_base.rs
@@ -5,6 +5,7 @@ pub fn opts(kernel: &str) -> TargetOptions {
     TargetOptions {
         os: format!("solid_{}", kernel).into(),
         vendor: "kmc".into(),
+        executables: false,
         frame_pointer: FramePointer::NonLeaf,
         has_thread_local: true,
         ..Default::default()
diff --git a/compiler/rustc_target/src/spec/thumb_base.rs b/compiler/rustc_target/src/spec/thumb_base.rs
index ef6038e6120..049142b89f1 100644
--- a/compiler/rustc_target/src/spec/thumb_base.rs
+++ b/compiler/rustc_target/src/spec/thumb_base.rs
@@ -34,7 +34,6 @@ pub fn opts() -> TargetOptions {
     // See rust-lang/rfcs#1645 for a discussion about these defaults
     TargetOptions {
         linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
-        executables: true,
         // In most cases, LLD is good enough
         linker: Some("rust-lld".into()),
         // Because these devices have very little resources having an unwinder is too onerous so we
diff --git a/compiler/rustc_target/src/spec/vxworks_base.rs b/compiler/rustc_target/src/spec/vxworks_base.rs
index 2beb279e398..aa4784b63e7 100644
--- a/compiler/rustc_target/src/spec/vxworks_base.rs
+++ b/compiler/rustc_target/src/spec/vxworks_base.rs
@@ -8,7 +8,6 @@ pub fn opts() -> TargetOptions {
         linker: Some("wr-c++".into()),
         exe_suffix: ".vxe".into(),
         dynamic_linking: true,
-        executables: true,
         families: cvs!["unix"],
         has_rpath: true,
         has_thread_local: true,
diff --git a/compiler/rustc_target/src/spec/wasm_base.rs b/compiler/rustc_target/src/spec/wasm_base.rs
index 5736402ae14..9216d3e7b65 100644
--- a/compiler/rustc_target/src/spec/wasm_base.rs
+++ b/compiler/rustc_target/src/spec/wasm_base.rs
@@ -62,9 +62,6 @@ pub fn options() -> TargetOptions {
         dynamic_linking: true,
         only_cdylib: true,
 
-        // This means we'll just embed a `#[start]` function in the wasm module
-        executables: true,
-
         // relatively self-explanatory!
         exe_suffix: ".wasm".into(),
         dll_prefix: "".into(),
diff --git a/compiler/rustc_target/src/spec/windows_gnu_base.rs b/compiler/rustc_target/src/spec/windows_gnu_base.rs
index a0480f386f7..90e0af3e38a 100644
--- a/compiler/rustc_target/src/spec/windows_gnu_base.rs
+++ b/compiler/rustc_target/src/spec/windows_gnu_base.rs
@@ -67,7 +67,6 @@ pub fn opts() -> TargetOptions {
         function_sections: false,
         linker: Some("gcc".into()),
         dynamic_linking: true,
-        executables: true,
         dll_prefix: "".into(),
         dll_suffix: ".dll".into(),
         exe_suffix: ".exe".into(),
diff --git a/compiler/rustc_target/src/spec/windows_gnullvm_base.rs b/compiler/rustc_target/src/spec/windows_gnullvm_base.rs
index 30f995007a9..bae007dc9f3 100644
--- a/compiler/rustc_target/src/spec/windows_gnullvm_base.rs
+++ b/compiler/rustc_target/src/spec/windows_gnullvm_base.rs
@@ -20,7 +20,6 @@ pub fn opts() -> TargetOptions {
         abi: "llvm".into(),
         linker: Some("clang".into()),
         dynamic_linking: true,
-        executables: true,
         dll_prefix: "".into(),
         dll_suffix: ".dll".into(),
         exe_suffix: ".exe".into(),
diff --git a/compiler/rustc_target/src/spec/windows_uwp_gnu_base.rs b/compiler/rustc_target/src/spec/windows_uwp_gnu_base.rs
index 334dec43ef7..fa69b919cec 100644
--- a/compiler/rustc_target/src/spec/windows_uwp_gnu_base.rs
+++ b/compiler/rustc_target/src/spec/windows_uwp_gnu_base.rs
@@ -24,7 +24,6 @@ pub fn opts() -> TargetOptions {
     TargetOptions {
         abi: "uwp".into(),
         vendor: "uwp".into(),
-        executables: false,
         limit_rdylib_exports: false,
         late_link_args,
         late_link_args_dynamic,
diff --git a/compiler/rustc_target/src/spec/x86_64_fortanix_unknown_sgx.rs b/compiler/rustc_target/src/spec/x86_64_fortanix_unknown_sgx.rs
index 4348d924579..9d597ea2e62 100644
--- a/compiler/rustc_target/src/spec/x86_64_fortanix_unknown_sgx.rs
+++ b/compiler/rustc_target/src/spec/x86_64_fortanix_unknown_sgx.rs
@@ -62,7 +62,6 @@ pub fn target() -> Target {
         vendor: "fortanix".into(),
         abi: "fortanix".into(),
         linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
-        executables: true,
         linker: Some("rust-lld".into()),
         max_atomic_width: Some(64),
         cpu: "x86-64".into(),
diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_none.rs b/compiler/rustc_target/src/spec/x86_64_unknown_none.rs
index 0c510dfaa12..809fd642d41 100644
--- a/compiler/rustc_target/src/spec/x86_64_unknown_none.rs
+++ b/compiler/rustc_target/src/spec/x86_64_unknown_none.rs
@@ -24,7 +24,6 @@ pub fn target() -> Target {
         features:
             "-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-3dnow,-3dnowa,-avx,-avx2,+soft-float"
                 .into(),
-        executables: true,
         disable_redzone: true,
         panic_strategy: PanicStrategy::Abort,
         code_model: Some(CodeModel::Kernel),
diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs
index 282ee632ce5..c33629192cb 100644
--- a/compiler/rustc_trait_selection/src/lib.rs
+++ b/compiler/rustc_trait_selection/src/lib.rs
@@ -17,7 +17,7 @@
 #![feature(drain_filter)]
 #![feature(hash_drain_filter)]
 #![feature(label_break_value)]
-#![feature(let_chains)]
+#![cfg_attr(bootstrap, feature(let_chains))]
 #![feature(let_else)]
 #![feature(if_let_guard)]
 #![feature(never_type)]
diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs
index 65ff9ceb67e..5763e6d1b55 100644
--- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs
+++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs
@@ -793,9 +793,7 @@ impl<'tcx> AutoTraitFinder<'tcx> {
                 }
                 ty::PredicateKind::RegionOutlives(binder) => {
                     let binder = bound_predicate.rebind(binder);
-                    if select.infcx().region_outlives_predicate(&dummy_cause, binder).is_err() {
-                        return false;
-                    }
+                    select.infcx().region_outlives_predicate(&dummy_cause, binder)
                 }
                 ty::PredicateKind::TypeOutlives(binder) => {
                     let binder = bound_predicate.rebind(binder);
diff --git a/compiler/rustc_trait_selection/src/traits/codegen.rs b/compiler/rustc_trait_selection/src/traits/codegen.rs
index 6ca630b74cc..5fcaa52d417 100644
--- a/compiler/rustc_trait_selection/src/traits/codegen.rs
+++ b/compiler/rustc_trait_selection/src/traits/codegen.rs
@@ -3,7 +3,7 @@
 // seems likely that they should eventually be merged into more
 // general routines.
 
-use crate::infer::TyCtxtInferExt;
+use crate::infer::{DefiningAnchor, TyCtxtInferExt};
 use crate::traits::{
     FulfillmentContext, ImplSource, Obligation, ObligationCause, SelectionContext, TraitEngine,
     Unimplemented,
@@ -30,7 +30,11 @@ pub fn codegen_fulfill_obligation<'tcx>(
 
     // Do the initial selection for the obligation. This yields the
     // shallow result we are looking for -- that is, what specific impl.
-    tcx.infer_ctxt().enter(|infcx| {
+    let mut infcx_builder =
+        tcx.infer_ctxt().ignoring_regions().with_opaque_type_inference(DefiningAnchor::Bubble);
+    infcx_builder.enter(|infcx| {
+        //~^ HACK `Bubble` is required for
+        // this test to pass: type-alias-impl-trait/assoc-projection-ice.rs
         let mut selcx = SelectionContext::new(&infcx);
 
         let obligation_cause = ObligationCause::dummy();
@@ -69,7 +73,8 @@ pub fn codegen_fulfill_obligation<'tcx>(
 
         // Opaque types may have gotten their hidden types constrained, but we can ignore them safely
         // as they will get constrained elsewhere, too.
-        let _opaque_types = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types();
+        // (ouz-a) This is required for `type-alias-impl-trait/assoc-projection-ice.rs` to pass
+        let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types();
 
         debug!("Cache miss: {trait_ref:?} => {impl_source:?}");
         Ok(&*tcx.arena.alloc(impl_source))
diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs
index 52ca23c4b30..fa94aa19abd 100644
--- a/compiler/rustc_trait_selection/src/traits/coherence.rs
+++ b/compiler/rustc_trait_selection/src/traits/coherence.rs
@@ -22,11 +22,12 @@ use rustc_middle::traits::specialization_graph::OverlapMode;
 use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
 use rustc_middle::ty::subst::Subst;
 use rustc_middle::ty::visit::TypeVisitable;
-use rustc_middle::ty::{self, ImplSubject, Ty, TyCtxt};
+use rustc_middle::ty::{self, ImplSubject, Ty, TyCtxt, TypeVisitor};
 use rustc_span::symbol::sym;
 use rustc_span::DUMMY_SP;
 use std::fmt::Debug;
 use std::iter;
+use std::ops::ControlFlow;
 
 /// Whether we do the orphan check relative to this crate or
 /// to some remote crate.
@@ -578,220 +579,175 @@ fn orphan_check_trait_ref<'tcx>(
         );
     }
 
-    // Given impl<P1..=Pn> Trait<T1..=Tn> for T0, an impl is valid only
-    // if at least one of the following is true:
-    //
-    // - Trait is a local trait
-    // (already checked in orphan_check prior to calling this function)
-    // - All of
-    //     - At least one of the types T0..=Tn must be a local type.
-    //      Let Ti be the first such type.
-    //     - No uncovered type parameters P1..=Pn may appear in T0..Ti (excluding Ti)
-    //
-    fn uncover_fundamental_ty<'tcx>(
-        tcx: TyCtxt<'tcx>,
-        ty: Ty<'tcx>,
-        in_crate: InCrate,
-    ) -> Vec<Ty<'tcx>> {
-        // FIXME: this is currently somewhat overly complicated,
-        // but fixing this requires a more complicated refactor.
-        if !contained_non_local_types(tcx, ty, in_crate).is_empty() {
-            if let Some(inner_tys) = fundamental_ty_inner_tys(tcx, ty) {
-                return inner_tys
-                    .flat_map(|ty| uncover_fundamental_ty(tcx, ty, in_crate))
-                    .collect();
+    let mut checker = OrphanChecker::new(tcx, in_crate);
+    match trait_ref.visit_with(&mut checker) {
+        ControlFlow::Continue(()) => Err(OrphanCheckErr::NonLocalInputType(checker.non_local_tys)),
+        ControlFlow::Break(OrphanCheckEarlyExit::ParamTy(ty)) => {
+            // Does there exist some local type after the `ParamTy`.
+            checker.search_first_local_ty = true;
+            if let Some(OrphanCheckEarlyExit::LocalTy(local_ty)) =
+                trait_ref.visit_with(&mut checker).break_value()
+            {
+                Err(OrphanCheckErr::UncoveredTy(ty, Some(local_ty)))
+            } else {
+                Err(OrphanCheckErr::UncoveredTy(ty, None))
             }
         }
-
-        vec![ty]
-    }
-
-    let mut non_local_spans = vec![];
-    for (i, input_ty) in trait_ref
-        .substs
-        .types()
-        .flat_map(|ty| uncover_fundamental_ty(tcx, ty, in_crate))
-        .enumerate()
-    {
-        debug!("orphan_check_trait_ref: check ty `{:?}`", input_ty);
-        let non_local_tys = contained_non_local_types(tcx, input_ty, in_crate);
-        if non_local_tys.is_empty() {
-            debug!("orphan_check_trait_ref: ty_is_local `{:?}`", input_ty);
-            return Ok(());
-        } else if let ty::Param(_) = input_ty.kind() {
-            debug!("orphan_check_trait_ref: uncovered ty: `{:?}`", input_ty);
-            let local_type = trait_ref
-                .substs
-                .types()
-                .flat_map(|ty| uncover_fundamental_ty(tcx, ty, in_crate))
-                .find(|&ty| ty_is_local_constructor(tcx, ty, in_crate));
-
-            debug!("orphan_check_trait_ref: uncovered ty local_type: `{:?}`", local_type);
-
-            return Err(OrphanCheckErr::UncoveredTy(input_ty, local_type));
-        }
-
-        non_local_spans.extend(non_local_tys.into_iter().map(|input_ty| (input_ty, i == 0)));
+        ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(_)) => Ok(()),
     }
-    // If we exit above loop, never found a local type.
-    debug!("orphan_check_trait_ref: no local type");
-    Err(OrphanCheckErr::NonLocalInputType(non_local_spans))
 }
 
-/// Returns a list of relevant non-local types for `ty`.
-///
-/// This is just `ty` itself unless `ty` is `#[fundamental]`,
-/// in which case we recursively look into this type.
-///
-/// If `ty` is local itself, this method returns an empty `Vec`.
-///
-/// # Examples
-///
-/// - `u32` is not local, so this returns `[u32]`.
-/// - for `Foo<u32>`, where `Foo` is a local type, this returns `[]`.
-/// - `&mut u32` returns `[u32]`, as `&mut` is a fundamental type, similar to `Box`.
-/// - `Box<Foo<u32>>` returns `[]`, as `Box` is a fundamental type and `Foo` is local.
-fn contained_non_local_types<'tcx>(
+struct OrphanChecker<'tcx> {
     tcx: TyCtxt<'tcx>,
-    ty: Ty<'tcx>,
     in_crate: InCrate,
-) -> Vec<Ty<'tcx>> {
-    if ty_is_local_constructor(tcx, ty, in_crate) {
-        Vec::new()
-    } else {
-        match fundamental_ty_inner_tys(tcx, ty) {
-            Some(inner_tys) => {
-                inner_tys.flat_map(|ty| contained_non_local_types(tcx, ty, in_crate)).collect()
-            }
-            None => vec![ty],
+    in_self_ty: bool,
+    /// Ignore orphan check failures and exclusively search for the first
+    /// local type.
+    search_first_local_ty: bool,
+    non_local_tys: Vec<(Ty<'tcx>, bool)>,
+}
+
+impl<'tcx> OrphanChecker<'tcx> {
+    fn new(tcx: TyCtxt<'tcx>, in_crate: InCrate) -> Self {
+        OrphanChecker {
+            tcx,
+            in_crate,
+            in_self_ty: true,
+            search_first_local_ty: false,
+            non_local_tys: Vec::new(),
         }
     }
-}
 
-/// For `#[fundamental]` ADTs and `&T` / `&mut T`, returns `Some` with the
-/// type parameters of the ADT, or `T`, respectively. For non-fundamental
-/// types, returns `None`.
-fn fundamental_ty_inner_tys<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    ty: Ty<'tcx>,
-) -> Option<impl Iterator<Item = Ty<'tcx>>> {
-    let (first_ty, rest_tys) = match *ty.kind() {
-        ty::Ref(_, ty, _) => (ty, ty::subst::InternalSubsts::empty().types()),
-        ty::Adt(def, substs) if def.is_fundamental() => {
-            let mut types = substs.types();
-
-            // FIXME(eddyb) actually validate `#[fundamental]` up-front.
-            match types.next() {
-                None => {
-                    tcx.sess.span_err(
-                        tcx.def_span(def.did()),
-                        "`#[fundamental]` requires at least one type parameter",
-                    );
-
-                    return None;
-                }
+    fn found_non_local_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<OrphanCheckEarlyExit<'tcx>> {
+        self.non_local_tys.push((t, self.in_self_ty));
+        ControlFlow::CONTINUE
+    }
 
-                Some(first_ty) => (first_ty, types),
-            }
+    fn found_param_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<OrphanCheckEarlyExit<'tcx>> {
+        if self.search_first_local_ty {
+            ControlFlow::CONTINUE
+        } else {
+            ControlFlow::Break(OrphanCheckEarlyExit::ParamTy(t))
         }
-        _ => return None,
-    };
+    }
 
-    Some(iter::once(first_ty).chain(rest_tys))
+    fn def_id_is_local(&mut self, def_id: DefId) -> bool {
+        match self.in_crate {
+            InCrate::Local => def_id.is_local(),
+            InCrate::Remote => false,
+        }
+    }
 }
 
-fn def_id_is_local(def_id: DefId, in_crate: InCrate) -> bool {
-    match in_crate {
-        // The type is local to *this* crate - it will not be
-        // local in any other crate.
-        InCrate::Remote => false,
-        InCrate::Local => def_id.is_local(),
-    }
+enum OrphanCheckEarlyExit<'tcx> {
+    ParamTy(Ty<'tcx>),
+    LocalTy(Ty<'tcx>),
 }
 
-fn ty_is_local_constructor(tcx: TyCtxt<'_>, ty: Ty<'_>, in_crate: InCrate) -> bool {
-    debug!("ty_is_local_constructor({:?})", ty);
-
-    match *ty.kind() {
-        ty::Bool
-        | ty::Char
-        | ty::Int(..)
-        | ty::Uint(..)
-        | ty::Float(..)
-        | ty::Str
-        | ty::FnDef(..)
-        | ty::FnPtr(_)
-        | ty::Array(..)
-        | ty::Slice(..)
-        | ty::RawPtr(..)
-        | ty::Ref(..)
-        | ty::Never
-        | ty::Tuple(..)
-        | ty::Param(..)
-        | ty::Projection(..) => false,
-
-        ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) => match in_crate {
-            InCrate::Local => false,
-            // The inference variable might be unified with a local
-            // type in that remote crate.
-            InCrate::Remote => true,
-        },
-
-        ty::Adt(def, _) => def_id_is_local(def.did(), in_crate),
-        ty::Foreign(did) => def_id_is_local(did, in_crate),
-        ty::Opaque(..) => {
-            // This merits some explanation.
-            // Normally, opaque types are not involved when performing
-            // coherence checking, since it is illegal to directly
-            // implement a trait on an opaque type. However, we might
-            // end up looking at an opaque type during coherence checking
-            // if an opaque type gets used within another type (e.g. as
-            // a type parameter). This requires us to decide whether or
-            // not an opaque type should be considered 'local' or not.
-            //
-            // We choose to treat all opaque types as non-local, even
-            // those that appear within the same crate. This seems
-            // somewhat surprising at first, but makes sense when
-            // you consider that opaque types are supposed to hide
-            // the underlying type *within the same crate*. When an
-            // opaque type is used from outside the module
-            // where it is declared, it should be impossible to observe
-            // anything about it other than the traits that it implements.
-            //
-            // The alternative would be to look at the underlying type
-            // to determine whether or not the opaque type itself should
-            // be considered local. However, this could make it a breaking change
-            // to switch the underlying ('defining') type from a local type
-            // to a remote type. This would violate the rule that opaque
-            // types should be completely opaque apart from the traits
-            // that they implement, so we don't use this behavior.
-            false
-        }
+impl<'tcx> TypeVisitor<'tcx> for OrphanChecker<'tcx> {
+    type BreakTy = OrphanCheckEarlyExit<'tcx>;
+    fn visit_region(&mut self, _r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
+        ControlFlow::CONTINUE
+    }
 
-        ty::Dynamic(ref tt, ..) => {
-            if let Some(principal) = tt.principal() {
-                def_id_is_local(principal.def_id(), in_crate)
-            } else {
-                false
+    fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+        let result = match *ty.kind() {
+            ty::Bool
+            | ty::Char
+            | ty::Int(..)
+            | ty::Uint(..)
+            | ty::Float(..)
+            | ty::Str
+            | ty::FnDef(..)
+            | ty::FnPtr(_)
+            | ty::Array(..)
+            | ty::Slice(..)
+            | ty::RawPtr(..)
+            | ty::Never
+            | ty::Tuple(..)
+            | ty::Projection(..) => self.found_non_local_ty(ty),
+
+            ty::Param(..) => self.found_param_ty(ty),
+
+            ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) => match self.in_crate {
+                InCrate::Local => self.found_non_local_ty(ty),
+                // The inference variable might be unified with a local
+                // type in that remote crate.
+                InCrate::Remote => ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)),
+            },
+
+            // For fundamental types, we just look inside of them.
+            ty::Ref(_, ty, _) => ty.visit_with(self),
+            ty::Adt(def, substs) => {
+                if self.def_id_is_local(def.did()) {
+                    ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty))
+                } else if def.is_fundamental() {
+                    substs.visit_with(self)
+                } else {
+                    self.found_non_local_ty(ty)
+                }
             }
-        }
+            ty::Foreign(def_id) => {
+                if self.def_id_is_local(def_id) {
+                    ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty))
+                } else {
+                    self.found_non_local_ty(ty)
+                }
+            }
+            ty::Dynamic(tt, ..) => {
+                let principal = tt.principal().map(|p| p.def_id());
+                if principal.map_or(false, |p| self.def_id_is_local(p)) {
+                    ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty))
+                } else {
+                    self.found_non_local_ty(ty)
+                }
+            }
+            ty::Error(_) => ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)),
+            ty::Closure(..) | ty::Generator(..) | ty::GeneratorWitness(..) => {
+                self.tcx.sess.delay_span_bug(
+                    DUMMY_SP,
+                    format!("ty_is_local invoked on closure or generator: {:?}", ty),
+                );
+                ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty))
+            }
+            ty::Opaque(..) => {
+                // This merits some explanation.
+                // Normally, opaque types are not involved when performing
+                // coherence checking, since it is illegal to directly
+                // implement a trait on an opaque type. However, we might
+                // end up looking at an opaque type during coherence checking
+                // if an opaque type gets used within another type (e.g. as
+                // the type of a field) when checking for auto trait or `Sized`
+                // impls. This requires us to decide whether or not an opaque
+                // type should be considered 'local' or not.
+                //
+                // We choose to treat all opaque types as non-local, even
+                // those that appear within the same crate. This seems
+                // somewhat surprising at first, but makes sense when
+                // you consider that opaque types are supposed to hide
+                // the underlying type *within the same crate*. When an
+                // opaque type is used from outside the module
+                // where it is declared, it should be impossible to observe
+                // anything about it other than the traits that it implements.
+                //
+                // The alternative would be to look at the underlying type
+                // to determine whether or not the opaque type itself should
+                // be considered local. However, this could make it a breaking change
+                // to switch the underlying ('defining') type from a local type
+                // to a remote type. This would violate the rule that opaque
+                // types should be completely opaque apart from the traits
+                // that they implement, so we don't use this behavior.
+                self.found_non_local_ty(ty)
+            }
+        };
+        // A bit of a hack, the `OrphanChecker` is only used to visit a `TraitRef`, so
+        // the first type we visit is always the self type.
+        self.in_self_ty = false;
+        result
+    }
 
-        ty::Error(_) => true,
-
-        // These variants should never appear during coherence checking because they
-        // cannot be named directly.
-        //
-        // They could be indirectly used through an opaque type. While using opaque types
-        // in impls causes an error, this path can still be hit afterwards.
-        //
-        // See `test/ui/coherence/coherence-with-closure.rs` for an example where this
-        // could happens.
-        ty::Closure(..) | ty::Generator(..) | ty::GeneratorWitness(..) => {
-            tcx.sess.delay_span_bug(
-                DUMMY_SP,
-                format!("ty_is_local invoked on closure or generator: {:?}", ty),
-            );
-            true
-        }
+    // FIXME: Constants should participate in orphan checking.
+    fn visit_const(&mut self, _c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
+        ControlFlow::CONTINUE
     }
 }
diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
index 3a152eff485..254bc4ab663 100644
--- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
+++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
@@ -10,22 +10,153 @@
 //! generic constants mentioned in the `caller_bounds` of the current environment.
 use rustc_errors::ErrorGuaranteed;
 use rustc_hir::def::DefKind;
-use rustc_index::vec::IndexVec;
 use rustc_infer::infer::InferCtxt;
-use rustc_middle::mir;
-use rustc_middle::mir::interpret::{ErrorHandled, LitToConstError, LitToConstInput};
-use rustc_middle::thir;
-use rustc_middle::thir::abstract_const::{self, Node, NodeId, NotConstEvaluatable};
-use rustc_middle::ty::subst::{Subst, SubstsRef};
-use rustc_middle::ty::{self, DelaySpanBugEmitted, EarlyBinder, TyCtxt, TypeVisitable};
+use rustc_middle::mir::interpret::ErrorHandled;
+use rustc_middle::ty::abstract_const::{
+    walk_abstract_const, AbstractConst, FailureKind, Node, NotConstEvaluatable,
+};
+use rustc_middle::ty::{self, TyCtxt, TypeVisitable};
 use rustc_session::lint;
-use rustc_span::def_id::LocalDefId;
 use rustc_span::Span;
 
-use std::cmp;
 use std::iter;
 use std::ops::ControlFlow;
 
+pub struct ConstUnifyCtxt<'tcx> {
+    pub tcx: TyCtxt<'tcx>,
+    pub param_env: ty::ParamEnv<'tcx>,
+}
+
+impl<'tcx> ConstUnifyCtxt<'tcx> {
+    // Substitutes generics repeatedly to allow AbstractConsts to unify where a
+    // ConstKind::Unevaluated could be turned into an AbstractConst that would unify e.g.
+    // Param(N) should unify with Param(T), substs: [Unevaluated("T2", [Unevaluated("T3", [Param(N)])])]
+    #[inline]
+    #[instrument(skip(self), level = "debug")]
+    fn try_replace_substs_in_root(
+        &self,
+        mut abstr_const: AbstractConst<'tcx>,
+    ) -> Option<AbstractConst<'tcx>> {
+        while let Node::Leaf(ct) = abstr_const.root(self.tcx) {
+            match AbstractConst::from_const(self.tcx, ct) {
+                Ok(Some(act)) => abstr_const = act,
+                Ok(None) => break,
+                Err(_) => return None,
+            }
+        }
+
+        Some(abstr_const)
+    }
+
+    /// Tries to unify two abstract constants using structural equality.
+    #[instrument(skip(self), level = "debug")]
+    pub fn try_unify(&self, a: AbstractConst<'tcx>, b: AbstractConst<'tcx>) -> bool {
+        let a = if let Some(a) = self.try_replace_substs_in_root(a) {
+            a
+        } else {
+            return true;
+        };
+
+        let b = if let Some(b) = self.try_replace_substs_in_root(b) {
+            b
+        } else {
+            return true;
+        };
+
+        let a_root = a.root(self.tcx);
+        let b_root = b.root(self.tcx);
+        debug!(?a_root, ?b_root);
+
+        match (a_root, b_root) {
+            (Node::Leaf(a_ct), Node::Leaf(b_ct)) => {
+                let a_ct = a_ct.eval(self.tcx, self.param_env);
+                debug!("a_ct evaluated: {:?}", a_ct);
+                let b_ct = b_ct.eval(self.tcx, self.param_env);
+                debug!("b_ct evaluated: {:?}", b_ct);
+
+                if a_ct.ty() != b_ct.ty() {
+                    return false;
+                }
+
+                match (a_ct.kind(), b_ct.kind()) {
+                    // We can just unify errors with everything to reduce the amount of
+                    // emitted errors here.
+                    (ty::ConstKind::Error(_), _) | (_, ty::ConstKind::Error(_)) => true,
+                    (ty::ConstKind::Param(a_param), ty::ConstKind::Param(b_param)) => {
+                        a_param == b_param
+                    }
+                    (ty::ConstKind::Value(a_val), ty::ConstKind::Value(b_val)) => a_val == b_val,
+                    // If we have `fn a<const N: usize>() -> [u8; N + 1]` and `fn b<const M: usize>() -> [u8; 1 + M]`
+                    // we do not want to use `assert_eq!(a(), b())` to infer that `N` and `M` have to be `1`. This
+                    // means that we only allow inference variables if they are equal.
+                    (ty::ConstKind::Infer(a_val), ty::ConstKind::Infer(b_val)) => a_val == b_val,
+                    // We expand generic anonymous constants at the start of this function, so this
+                    // branch should only be taking when dealing with associated constants, at
+                    // which point directly comparing them seems like the desired behavior.
+                    //
+                    // FIXME(generic_const_exprs): This isn't actually the case.
+                    // We also take this branch for concrete anonymous constants and
+                    // expand generic anonymous constants with concrete substs.
+                    (ty::ConstKind::Unevaluated(a_uv), ty::ConstKind::Unevaluated(b_uv)) => {
+                        a_uv == b_uv
+                    }
+                    // FIXME(generic_const_exprs): We may want to either actually try
+                    // to evaluate `a_ct` and `b_ct` if they are are fully concrete or something like
+                    // this, for now we just return false here.
+                    _ => false,
+                }
+            }
+            (Node::Binop(a_op, al, ar), Node::Binop(b_op, bl, br)) if a_op == b_op => {
+                self.try_unify(a.subtree(al), b.subtree(bl))
+                    && self.try_unify(a.subtree(ar), b.subtree(br))
+            }
+            (Node::UnaryOp(a_op, av), Node::UnaryOp(b_op, bv)) if a_op == b_op => {
+                self.try_unify(a.subtree(av), b.subtree(bv))
+            }
+            (Node::FunctionCall(a_f, a_args), Node::FunctionCall(b_f, b_args))
+                if a_args.len() == b_args.len() =>
+            {
+                self.try_unify(a.subtree(a_f), b.subtree(b_f))
+                    && iter::zip(a_args, b_args)
+                        .all(|(&an, &bn)| self.try_unify(a.subtree(an), b.subtree(bn)))
+            }
+            (Node::Cast(a_kind, a_operand, a_ty), Node::Cast(b_kind, b_operand, b_ty))
+                if (a_ty == b_ty) && (a_kind == b_kind) =>
+            {
+                self.try_unify(a.subtree(a_operand), b.subtree(b_operand))
+            }
+            // use this over `_ => false` to make adding variants to `Node` less error prone
+            (Node::Cast(..), _)
+            | (Node::FunctionCall(..), _)
+            | (Node::UnaryOp(..), _)
+            | (Node::Binop(..), _)
+            | (Node::Leaf(..), _) => false,
+        }
+    }
+}
+
+#[instrument(skip(tcx), level = "debug")]
+pub fn try_unify_abstract_consts<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    (a, b): (ty::Unevaluated<'tcx, ()>, ty::Unevaluated<'tcx, ()>),
+    param_env: ty::ParamEnv<'tcx>,
+) -> bool {
+    (|| {
+        if let Some(a) = AbstractConst::new(tcx, a)? {
+            if let Some(b) = AbstractConst::new(tcx, b)? {
+                let const_unify_ctxt = ConstUnifyCtxt { tcx, param_env };
+                return Ok(const_unify_ctxt.try_unify(a, b));
+            }
+        }
+
+        Ok(false)
+    })()
+    .unwrap_or_else(|_: ErrorGuaranteed| true)
+    // FIXME(generic_const_exprs): We should instead have this
+    // method return the resulting `ty::Const` and return `ConstKind::Error`
+    // on `ErrorGuaranteed`.
+}
+
 /// Check if a given constant can be evaluated.
 #[instrument(skip(infcx), level = "debug")]
 pub fn is_const_evaluatable<'cx, 'tcx>(
@@ -41,48 +172,7 @@ pub fn is_const_evaluatable<'cx, 'tcx>(
             if satisfied_from_param_env(tcx, ct, param_env)? {
                 return Ok(());
             }
-
-            // We were unable to unify the abstract constant with
-            // a constant found in the caller bounds, there are
-            // now three possible cases here.
-            #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
-            enum FailureKind {
-                /// The abstract const still references an inference
-                /// variable, in this case we return `TooGeneric`.
-                MentionsInfer,
-                /// The abstract const references a generic parameter,
-                /// this means that we emit an error here.
-                MentionsParam,
-                /// The substs are concrete enough that we can simply
-                /// try and evaluate the given constant.
-                Concrete,
-            }
-            let mut failure_kind = FailureKind::Concrete;
-            walk_abstract_const::<!, _>(tcx, ct, |node| match node.root(tcx) {
-                Node::Leaf(leaf) => {
-                    if leaf.has_infer_types_or_consts() {
-                        failure_kind = FailureKind::MentionsInfer;
-                    } else if leaf.has_param_types_or_consts() {
-                        failure_kind = cmp::min(failure_kind, FailureKind::MentionsParam);
-                    }
-
-                    ControlFlow::CONTINUE
-                }
-                Node::Cast(_, _, ty) => {
-                    if ty.has_infer_types_or_consts() {
-                        failure_kind = FailureKind::MentionsInfer;
-                    } else if ty.has_param_types_or_consts() {
-                        failure_kind = cmp::min(failure_kind, FailureKind::MentionsParam);
-                    }
-
-                    ControlFlow::CONTINUE
-                }
-                Node::Binop(_, _, _) | Node::UnaryOp(_, _) | Node::FunctionCall(_, _) => {
-                    ControlFlow::CONTINUE
-                }
-            });
-
-            match failure_kind {
+            match ct.unify_failure_kind(tcx) {
                 FailureKind::MentionsInfer => {
                     return Err(NotConstEvaluatable::MentionsInfer);
                 }
@@ -95,15 +185,12 @@ pub fn is_const_evaluatable<'cx, 'tcx>(
         }
         let concrete = infcx.const_eval_resolve(param_env, uv.expand(), Some(span));
         match concrete {
-            Err(ErrorHandled::TooGeneric) => Err(if !uv.has_infer_types_or_consts() {
-                infcx
-                    .tcx
-                    .sess
-                    .delay_span_bug(span, &format!("unexpected `TooGeneric` for {:?}", uv));
-                NotConstEvaluatable::MentionsParam
-            } else {
-                NotConstEvaluatable::MentionsInfer
-            }),
+            Err(ErrorHandled::TooGeneric) => {
+                Err(NotConstEvaluatable::Error(infcx.tcx.sess.delay_span_bug(
+                    span,
+                    format!("Missing value for constant, but no error reported?"),
+                )))
+            }
             Err(ErrorHandled::Linted) => {
                 let reported = infcx
                     .tcx
@@ -150,8 +237,11 @@ pub fn is_const_evaluatable<'cx, 'tcx>(
 
             Err(ErrorHandled::TooGeneric) => Err(if uv.has_infer_types_or_consts() {
                 NotConstEvaluatable::MentionsInfer
-                } else {
+                } else if uv.has_param_types_or_consts() {
                 NotConstEvaluatable::MentionsParam
+            } else {
+                let guar = infcx.tcx.sess.delay_span_bug(span, format!("Missing value for constant, but no error reported?"));
+                NotConstEvaluatable::Error(guar)
             }),
             Err(ErrorHandled::Linted) => {
                 let reported =
@@ -216,593 +306,3 @@ fn satisfied_from_param_env<'tcx>(
 
     Ok(false)
 }
-
-/// A tree representing an anonymous constant.
-///
-/// This is only able to represent a subset of `MIR`,
-/// and should not leak any information about desugarings.
-#[derive(Debug, Clone, Copy)]
-pub struct AbstractConst<'tcx> {
-    // FIXME: Consider adding something like `IndexSlice`
-    // and use this here.
-    inner: &'tcx [Node<'tcx>],
-    substs: SubstsRef<'tcx>,
-}
-
-impl<'tcx> AbstractConst<'tcx> {
-    pub fn new(
-        tcx: TyCtxt<'tcx>,
-        uv: ty::Unevaluated<'tcx, ()>,
-    ) -> Result<Option<AbstractConst<'tcx>>, ErrorGuaranteed> {
-        let inner = tcx.thir_abstract_const_opt_const_arg(uv.def)?;
-        debug!("AbstractConst::new({:?}) = {:?}", uv, inner);
-        Ok(inner.map(|inner| AbstractConst { inner, substs: tcx.erase_regions(uv.substs) }))
-    }
-
-    pub fn from_const(
-        tcx: TyCtxt<'tcx>,
-        ct: ty::Const<'tcx>,
-    ) -> Result<Option<AbstractConst<'tcx>>, ErrorGuaranteed> {
-        match ct.kind() {
-            ty::ConstKind::Unevaluated(uv) => AbstractConst::new(tcx, uv.shrink()),
-            ty::ConstKind::Error(DelaySpanBugEmitted { reported, .. }) => Err(reported),
-            _ => Ok(None),
-        }
-    }
-
-    #[inline]
-    pub fn subtree(self, node: NodeId) -> AbstractConst<'tcx> {
-        AbstractConst { inner: &self.inner[..=node.index()], substs: self.substs }
-    }
-
-    #[inline]
-    pub fn root(self, tcx: TyCtxt<'tcx>) -> Node<'tcx> {
-        let node = self.inner.last().copied().unwrap();
-        match node {
-            Node::Leaf(leaf) => Node::Leaf(EarlyBinder(leaf).subst(tcx, self.substs)),
-            Node::Cast(kind, operand, ty) => {
-                Node::Cast(kind, operand, EarlyBinder(ty).subst(tcx, self.substs))
-            }
-            // Don't perform substitution on the following as they can't directly contain generic params
-            Node::Binop(_, _, _) | Node::UnaryOp(_, _) | Node::FunctionCall(_, _) => node,
-        }
-    }
-}
-
-struct AbstractConstBuilder<'a, 'tcx> {
-    tcx: TyCtxt<'tcx>,
-    body_id: thir::ExprId,
-    body: &'a thir::Thir<'tcx>,
-    /// The current WIP node tree.
-    nodes: IndexVec<NodeId, Node<'tcx>>,
-}
-
-impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
-    fn root_span(&self) -> Span {
-        self.body.exprs[self.body_id].span
-    }
-
-    fn error(&mut self, span: Span, msg: &str) -> Result<!, ErrorGuaranteed> {
-        let reported = self
-            .tcx
-            .sess
-            .struct_span_err(self.root_span(), "overly complex generic constant")
-            .span_label(span, msg)
-            .help("consider moving this anonymous constant into a `const` function")
-            .emit();
-
-        Err(reported)
-    }
-    fn maybe_supported_error(&mut self, span: Span, msg: &str) -> Result<!, ErrorGuaranteed> {
-        let reported = self
-            .tcx
-            .sess
-            .struct_span_err(self.root_span(), "overly complex generic constant")
-            .span_label(span, msg)
-            .help("consider moving this anonymous constant into a `const` function")
-            .note("this operation may be supported in the future")
-            .emit();
-
-        Err(reported)
-    }
-
-    #[instrument(skip(tcx, body, body_id), level = "debug")]
-    fn new(
-        tcx: TyCtxt<'tcx>,
-        (body, body_id): (&'a thir::Thir<'tcx>, thir::ExprId),
-    ) -> Result<Option<AbstractConstBuilder<'a, 'tcx>>, ErrorGuaranteed> {
-        let builder = AbstractConstBuilder { tcx, body_id, body, nodes: IndexVec::new() };
-
-        struct IsThirPolymorphic<'a, 'tcx> {
-            is_poly: bool,
-            thir: &'a thir::Thir<'tcx>,
-        }
-
-        use crate::rustc_middle::thir::visit::Visitor;
-        use thir::visit;
-
-        impl<'a, 'tcx> IsThirPolymorphic<'a, 'tcx> {
-            fn expr_is_poly(&mut self, expr: &thir::Expr<'tcx>) -> bool {
-                if expr.ty.has_param_types_or_consts() {
-                    return true;
-                }
-
-                match expr.kind {
-                    thir::ExprKind::NamedConst { substs, .. } => substs.has_param_types_or_consts(),
-                    thir::ExprKind::ConstParam { .. } => true,
-                    thir::ExprKind::Repeat { value, count } => {
-                        self.visit_expr(&self.thir()[value]);
-                        count.has_param_types_or_consts()
-                    }
-                    _ => false,
-                }
-            }
-
-            fn pat_is_poly(&mut self, pat: &thir::Pat<'tcx>) -> bool {
-                if pat.ty.has_param_types_or_consts() {
-                    return true;
-                }
-
-                match pat.kind.as_ref() {
-                    thir::PatKind::Constant { value } => value.has_param_types_or_consts(),
-                    thir::PatKind::Range(thir::PatRange { lo, hi, .. }) => {
-                        lo.has_param_types_or_consts() || hi.has_param_types_or_consts()
-                    }
-                    _ => false,
-                }
-            }
-        }
-
-        impl<'a, 'tcx> visit::Visitor<'a, 'tcx> for IsThirPolymorphic<'a, 'tcx> {
-            fn thir(&self) -> &'a thir::Thir<'tcx> {
-                &self.thir
-            }
-
-            #[instrument(skip(self), level = "debug")]
-            fn visit_expr(&mut self, expr: &thir::Expr<'tcx>) {
-                self.is_poly |= self.expr_is_poly(expr);
-                if !self.is_poly {
-                    visit::walk_expr(self, expr)
-                }
-            }
-
-            #[instrument(skip(self), level = "debug")]
-            fn visit_pat(&mut self, pat: &thir::Pat<'tcx>) {
-                self.is_poly |= self.pat_is_poly(pat);
-                if !self.is_poly {
-                    visit::walk_pat(self, pat);
-                }
-            }
-        }
-
-        let mut is_poly_vis = IsThirPolymorphic { is_poly: false, thir: body };
-        visit::walk_expr(&mut is_poly_vis, &body[body_id]);
-        debug!("AbstractConstBuilder: is_poly={}", is_poly_vis.is_poly);
-        if !is_poly_vis.is_poly {
-            return Ok(None);
-        }
-
-        Ok(Some(builder))
-    }
-
-    /// We do not allow all binary operations in abstract consts, so filter disallowed ones.
-    fn check_binop(op: mir::BinOp) -> bool {
-        use mir::BinOp::*;
-        match op {
-            Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr | Shl | Shr | Eq | Lt | Le
-            | Ne | Ge | Gt => true,
-            Offset => false,
-        }
-    }
-
-    /// While we currently allow all unary operations, we still want to explicitly guard against
-    /// future changes here.
-    fn check_unop(op: mir::UnOp) -> bool {
-        use mir::UnOp::*;
-        match op {
-            Not | Neg => true,
-        }
-    }
-
-    /// Builds the abstract const by walking the thir and bailing out when
-    /// encountering an unsupported operation.
-    fn build(mut self) -> Result<&'tcx [Node<'tcx>], ErrorGuaranteed> {
-        debug!("Abstractconstbuilder::build: body={:?}", &*self.body);
-        self.recurse_build(self.body_id)?;
-
-        for n in self.nodes.iter() {
-            if let Node::Leaf(ct) = n {
-                if let ty::ConstKind::Unevaluated(ct) = ct.kind() {
-                    // `AbstractConst`s should not contain any promoteds as they require references which
-                    // are not allowed.
-                    assert_eq!(ct.promoted, None);
-                    assert_eq!(ct, self.tcx.erase_regions(ct));
-                }
-            }
-        }
-
-        Ok(self.tcx.arena.alloc_from_iter(self.nodes.into_iter()))
-    }
-
-    fn recurse_build(&mut self, node: thir::ExprId) -> Result<NodeId, ErrorGuaranteed> {
-        use thir::ExprKind;
-        let node = &self.body.exprs[node];
-        Ok(match &node.kind {
-            // I dont know if handling of these 3 is correct
-            &ExprKind::Scope { value, .. } => self.recurse_build(value)?,
-            &ExprKind::PlaceTypeAscription { source, .. }
-            | &ExprKind::ValueTypeAscription { source, .. } => self.recurse_build(source)?,
-            &ExprKind::Literal { lit, neg} => {
-                let sp = node.span;
-                let constant =
-                    match self.tcx.at(sp).lit_to_const(LitToConstInput { lit: &lit.node, ty: node.ty, neg }) {
-                        Ok(c) => c,
-                        Err(LitToConstError::Reported) => {
-                            self.tcx.const_error(node.ty)
-                        }
-                        Err(LitToConstError::TypeError) => {
-                            bug!("encountered type error in lit_to_const")
-                        }
-                    };
-
-                self.nodes.push(Node::Leaf(constant))
-            }
-            &ExprKind::NonHirLiteral { lit , user_ty: _} => {
-                let val = ty::ValTree::from_scalar_int(lit);
-                self.nodes.push(Node::Leaf(ty::Const::from_value(self.tcx, val, node.ty)))
-            }
-            &ExprKind::ZstLiteral { user_ty: _ } => {
-                let val = ty::ValTree::zst();
-                self.nodes.push(Node::Leaf(ty::Const::from_value(self.tcx, val, node.ty)))
-            }
-            &ExprKind::NamedConst { def_id, substs, user_ty: _ } => {
-                let uneval = ty::Unevaluated::new(ty::WithOptConstParam::unknown(def_id), substs);
-
-                let constant = self.tcx.mk_const(ty::ConstS {
-                                kind: ty::ConstKind::Unevaluated(uneval),
-                                ty: node.ty,
-                            });
-
-                self.nodes.push(Node::Leaf(constant))
-            }
-
-            ExprKind::ConstParam {param, ..} => {
-                let const_param = self.tcx.mk_const(ty::ConstS {
-                        kind: ty::ConstKind::Param(*param),
-                        ty: node.ty,
-                    });
-                self.nodes.push(Node::Leaf(const_param))
-            }
-
-            ExprKind::Call { fun, args, .. } => {
-                let fun = self.recurse_build(*fun)?;
-
-                let mut new_args = Vec::<NodeId>::with_capacity(args.len());
-                for &id in args.iter() {
-                    new_args.push(self.recurse_build(id)?);
-                }
-                let new_args = self.tcx.arena.alloc_slice(&new_args);
-                self.nodes.push(Node::FunctionCall(fun, new_args))
-            }
-            &ExprKind::Binary { op, lhs, rhs } if Self::check_binop(op) => {
-                let lhs = self.recurse_build(lhs)?;
-                let rhs = self.recurse_build(rhs)?;
-                self.nodes.push(Node::Binop(op, lhs, rhs))
-            }
-            &ExprKind::Unary { op, arg } if Self::check_unop(op) => {
-                let arg = self.recurse_build(arg)?;
-                self.nodes.push(Node::UnaryOp(op, arg))
-            }
-            // This is necessary so that the following compiles:
-            //
-            // ```
-            // fn foo<const N: usize>(a: [(); N + 1]) {
-            //     bar::<{ N + 1 }>();
-            // }
-            // ```
-            ExprKind::Block { body: thir::Block { stmts: box [], expr: Some(e), .. } } => {
-                self.recurse_build(*e)?
-            }
-            // `ExprKind::Use` happens when a `hir::ExprKind::Cast` is a
-            // "coercion cast" i.e. using a coercion or is a no-op.
-            // This is important so that `N as usize as usize` doesnt unify with `N as usize`. (untested)
-            &ExprKind::Use { source } => {
-                let arg = self.recurse_build(source)?;
-                self.nodes.push(Node::Cast(abstract_const::CastKind::Use, arg, node.ty))
-            }
-            &ExprKind::Cast { source } => {
-                let arg = self.recurse_build(source)?;
-                self.nodes.push(Node::Cast(abstract_const::CastKind::As, arg, node.ty))
-            }
-            ExprKind::Borrow{ arg, ..} => {
-                let arg_node = &self.body.exprs[*arg];
-
-                // Skip reborrows for now until we allow Deref/Borrow/AddressOf
-                // expressions.
-                // FIXME(generic_const_exprs): Verify/explain why this is sound
-                if let ExprKind::Deref {arg} = arg_node.kind {
-                    self.recurse_build(arg)?
-                } else {
-                    self.maybe_supported_error(
-                        node.span,
-                        "borrowing is not supported in generic constants",
-                    )?
-                }
-            }
-            // FIXME(generic_const_exprs): We may want to support these.
-            ExprKind::AddressOf { .. } | ExprKind::Deref {..}=> self.maybe_supported_error(
-                node.span,
-                "dereferencing or taking the address is not supported in generic constants",
-            )?,
-            ExprKind::Repeat { .. } | ExprKind::Array { .. } =>  self.maybe_supported_error(
-                node.span,
-                "array construction is not supported in generic constants",
-            )?,
-            ExprKind::Block { .. } => self.maybe_supported_error(
-                node.span,
-                "blocks are not supported in generic constant",
-            )?,
-            ExprKind::NeverToAny { .. } => self.maybe_supported_error(
-                node.span,
-                "converting nevers to any is not supported in generic constant",
-            )?,
-            ExprKind::Tuple { .. } => self.maybe_supported_error(
-                node.span,
-                "tuple construction is not supported in generic constants",
-            )?,
-            ExprKind::Index { .. } => self.maybe_supported_error(
-                node.span,
-                "indexing is not supported in generic constant",
-            )?,
-            ExprKind::Field { .. } => self.maybe_supported_error(
-                node.span,
-                "field access is not supported in generic constant",
-            )?,
-            ExprKind::ConstBlock { .. } => self.maybe_supported_error(
-                node.span,
-                "const blocks are not supported in generic constant",
-            )?,
-            ExprKind::Adt(_) => self.maybe_supported_error(
-                node.span,
-                "struct/enum construction is not supported in generic constants",
-            )?,
-            // dont know if this is correct
-            ExprKind::Pointer { .. } =>
-                self.error(node.span, "pointer casts are not allowed in generic constants")?,
-            ExprKind::Yield { .. } =>
-                self.error(node.span, "generator control flow is not allowed in generic constants")?,
-            ExprKind::Continue { .. } | ExprKind::Break { .. } | ExprKind::Loop { .. } => self
-                .error(
-                    node.span,
-                    "loops and loop control flow are not supported in generic constants",
-                )?,
-            ExprKind::Box { .. } =>
-                self.error(node.span, "allocations are not allowed in generic constants")?,
-
-            ExprKind::Unary { .. } => unreachable!(),
-            // we handle valid unary/binary ops above
-            ExprKind::Binary { .. } =>
-                self.error(node.span, "unsupported binary operation in generic constants")?,
-            ExprKind::LogicalOp { .. } =>
-                self.error(node.span, "unsupported operation in generic constants, short-circuiting operations would imply control flow")?,
-            ExprKind::Assign { .. } | ExprKind::AssignOp { .. } => {
-                self.error(node.span, "assignment is not supported in generic constants")?
-            }
-            ExprKind::Closure { .. } | ExprKind::Return { .. } => self.error(
-                node.span,
-                "closures and function keywords are not supported in generic constants",
-            )?,
-            // let expressions imply control flow
-            ExprKind::Match { .. } | ExprKind::If { .. } | ExprKind::Let { .. } =>
-                self.error(node.span, "control flow is not supported in generic constants")?,
-            ExprKind::InlineAsm { .. } => {
-                self.error(node.span, "assembly is not supported in generic constants")?
-            }
-
-            // we dont permit let stmts so `VarRef` and `UpvarRef` cant happen
-            ExprKind::VarRef { .. }
-            | ExprKind::UpvarRef { .. }
-            | ExprKind::StaticRef { .. }
-            | ExprKind::ThreadLocalRef(_) => {
-                self.error(node.span, "unsupported operation in generic constant")?
-            }
-        })
-    }
-}
-
-/// Builds an abstract const, do not use this directly, but use `AbstractConst::new` instead.
-pub(super) fn thir_abstract_const<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    def: ty::WithOptConstParam<LocalDefId>,
-) -> Result<Option<&'tcx [thir::abstract_const::Node<'tcx>]>, ErrorGuaranteed> {
-    if tcx.features().generic_const_exprs {
-        match tcx.def_kind(def.did) {
-            // FIXME(generic_const_exprs): We currently only do this for anonymous constants,
-            // meaning that we do not look into associated constants. I(@lcnr) am not yet sure whether
-            // we want to look into them or treat them as opaque projections.
-            //
-            // Right now we do neither of that and simply always fail to unify them.
-            DefKind::AnonConst | DefKind::InlineConst => (),
-            _ => return Ok(None),
-        }
-
-        let body = tcx.thir_body(def)?;
-
-        AbstractConstBuilder::new(tcx, (&*body.0.borrow(), body.1))?
-            .map(AbstractConstBuilder::build)
-            .transpose()
-    } else {
-        Ok(None)
-    }
-}
-
-#[instrument(skip(tcx), level = "debug")]
-pub(super) fn try_unify_abstract_consts<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    (a, b): (ty::Unevaluated<'tcx, ()>, ty::Unevaluated<'tcx, ()>),
-    param_env: ty::ParamEnv<'tcx>,
-) -> bool {
-    (|| {
-        if let Some(a) = AbstractConst::new(tcx, a)? {
-            if let Some(b) = AbstractConst::new(tcx, b)? {
-                let const_unify_ctxt = ConstUnifyCtxt { tcx, param_env };
-                return Ok(const_unify_ctxt.try_unify(a, b));
-            }
-        }
-
-        Ok(false)
-    })()
-    .unwrap_or_else(|_: ErrorGuaranteed| true)
-    // FIXME(generic_const_exprs): We should instead have this
-    // method return the resulting `ty::Const` and return `ConstKind::Error`
-    // on `ErrorGuaranteed`.
-}
-
-#[instrument(skip(tcx, f), level = "debug")]
-pub fn walk_abstract_const<'tcx, R, F>(
-    tcx: TyCtxt<'tcx>,
-    ct: AbstractConst<'tcx>,
-    mut f: F,
-) -> ControlFlow<R>
-where
-    F: FnMut(AbstractConst<'tcx>) -> ControlFlow<R>,
-{
-    #[instrument(skip(tcx, f), level = "debug")]
-    fn recurse<'tcx, R>(
-        tcx: TyCtxt<'tcx>,
-        ct: AbstractConst<'tcx>,
-        f: &mut dyn FnMut(AbstractConst<'tcx>) -> ControlFlow<R>,
-    ) -> ControlFlow<R> {
-        f(ct)?;
-        let root = ct.root(tcx);
-        debug!(?root);
-        match root {
-            Node::Leaf(_) => ControlFlow::CONTINUE,
-            Node::Binop(_, l, r) => {
-                recurse(tcx, ct.subtree(l), f)?;
-                recurse(tcx, ct.subtree(r), f)
-            }
-            Node::UnaryOp(_, v) => recurse(tcx, ct.subtree(v), f),
-            Node::FunctionCall(func, args) => {
-                recurse(tcx, ct.subtree(func), f)?;
-                args.iter().try_for_each(|&arg| recurse(tcx, ct.subtree(arg), f))
-            }
-            Node::Cast(_, operand, _) => recurse(tcx, ct.subtree(operand), f),
-        }
-    }
-
-    recurse(tcx, ct, &mut f)
-}
-
-struct ConstUnifyCtxt<'tcx> {
-    tcx: TyCtxt<'tcx>,
-    param_env: ty::ParamEnv<'tcx>,
-}
-
-impl<'tcx> ConstUnifyCtxt<'tcx> {
-    // Substitutes generics repeatedly to allow AbstractConsts to unify where a
-    // ConstKind::Unevaluated could be turned into an AbstractConst that would unify e.g.
-    // Param(N) should unify with Param(T), substs: [Unevaluated("T2", [Unevaluated("T3", [Param(N)])])]
-    #[inline]
-    #[instrument(skip(self), level = "debug")]
-    fn try_replace_substs_in_root(
-        &self,
-        mut abstr_const: AbstractConst<'tcx>,
-    ) -> Option<AbstractConst<'tcx>> {
-        while let Node::Leaf(ct) = abstr_const.root(self.tcx) {
-            match AbstractConst::from_const(self.tcx, ct) {
-                Ok(Some(act)) => abstr_const = act,
-                Ok(None) => break,
-                Err(_) => return None,
-            }
-        }
-
-        Some(abstr_const)
-    }
-
-    /// Tries to unify two abstract constants using structural equality.
-    #[instrument(skip(self), level = "debug")]
-    fn try_unify(&self, a: AbstractConst<'tcx>, b: AbstractConst<'tcx>) -> bool {
-        let a = if let Some(a) = self.try_replace_substs_in_root(a) {
-            a
-        } else {
-            return true;
-        };
-
-        let b = if let Some(b) = self.try_replace_substs_in_root(b) {
-            b
-        } else {
-            return true;
-        };
-
-        let a_root = a.root(self.tcx);
-        let b_root = b.root(self.tcx);
-        debug!(?a_root, ?b_root);
-
-        match (a_root, b_root) {
-            (Node::Leaf(a_ct), Node::Leaf(b_ct)) => {
-                let a_ct = a_ct.eval(self.tcx, self.param_env);
-                debug!("a_ct evaluated: {:?}", a_ct);
-                let b_ct = b_ct.eval(self.tcx, self.param_env);
-                debug!("b_ct evaluated: {:?}", b_ct);
-
-                if a_ct.ty() != b_ct.ty() {
-                    return false;
-                }
-
-                match (a_ct.kind(), b_ct.kind()) {
-                    // We can just unify errors with everything to reduce the amount of
-                    // emitted errors here.
-                    (ty::ConstKind::Error(_), _) | (_, ty::ConstKind::Error(_)) => true,
-                    (ty::ConstKind::Param(a_param), ty::ConstKind::Param(b_param)) => {
-                        a_param == b_param
-                    }
-                    (ty::ConstKind::Value(a_val), ty::ConstKind::Value(b_val)) => a_val == b_val,
-                    // If we have `fn a<const N: usize>() -> [u8; N + 1]` and `fn b<const M: usize>() -> [u8; 1 + M]`
-                    // we do not want to use `assert_eq!(a(), b())` to infer that `N` and `M` have to be `1`. This
-                    // means that we only allow inference variables if they are equal.
-                    (ty::ConstKind::Infer(a_val), ty::ConstKind::Infer(b_val)) => a_val == b_val,
-                    // We expand generic anonymous constants at the start of this function, so this
-                    // branch should only be taking when dealing with associated constants, at
-                    // which point directly comparing them seems like the desired behavior.
-                    //
-                    // FIXME(generic_const_exprs): This isn't actually the case.
-                    // We also take this branch for concrete anonymous constants and
-                    // expand generic anonymous constants with concrete substs.
-                    (ty::ConstKind::Unevaluated(a_uv), ty::ConstKind::Unevaluated(b_uv)) => {
-                        a_uv == b_uv
-                    }
-                    // FIXME(generic_const_exprs): We may want to either actually try
-                    // to evaluate `a_ct` and `b_ct` if they are are fully concrete or something like
-                    // this, for now we just return false here.
-                    _ => false,
-                }
-            }
-            (Node::Binop(a_op, al, ar), Node::Binop(b_op, bl, br)) if a_op == b_op => {
-                self.try_unify(a.subtree(al), b.subtree(bl))
-                    && self.try_unify(a.subtree(ar), b.subtree(br))
-            }
-            (Node::UnaryOp(a_op, av), Node::UnaryOp(b_op, bv)) if a_op == b_op => {
-                self.try_unify(a.subtree(av), b.subtree(bv))
-            }
-            (Node::FunctionCall(a_f, a_args), Node::FunctionCall(b_f, b_args))
-                if a_args.len() == b_args.len() =>
-            {
-                self.try_unify(a.subtree(a_f), b.subtree(b_f))
-                    && iter::zip(a_args, b_args)
-                        .all(|(&an, &bn)| self.try_unify(a.subtree(an), b.subtree(bn)))
-            }
-            (Node::Cast(a_kind, a_operand, a_ty), Node::Cast(b_kind, b_operand, b_ty))
-                if (a_ty == b_ty) && (a_kind == b_kind) =>
-            {
-                self.try_unify(a.subtree(a_operand), b.subtree(b_operand))
-            }
-            // use this over `_ => false` to make adding variants to `Node` less error prone
-            (Node::Cast(..), _)
-            | (Node::FunctionCall(..), _)
-            | (Node::UnaryOp(..), _)
-            | (Node::Binop(..), _)
-            | (Node::Leaf(..), _) => false,
-        }
-    }
-}
diff --git a/compiler/rustc_trait_selection/src/traits/engine.rs b/compiler/rustc_trait_selection/src/traits/engine.rs
index 4d477886979..6c177f63887 100644
--- a/compiler/rustc_trait_selection/src/traits/engine.rs
+++ b/compiler/rustc_trait_selection/src/traits/engine.rs
@@ -1,7 +1,17 @@
-use rustc_middle::ty::TyCtxt;
+use std::cell::RefCell;
 
 use super::TraitEngine;
 use super::{ChalkFulfillmentContext, FulfillmentContext};
+use crate::infer::InferCtxtExt;
+use rustc_hir::def_id::DefId;
+use rustc_infer::infer::{InferCtxt, InferOk};
+use rustc_infer::traits::{
+    FulfillmentError, Obligation, ObligationCause, PredicateObligation, TraitEngineExt as _,
+};
+use rustc_middle::ty::error::TypeError;
+use rustc_middle::ty::ToPredicate;
+use rustc_middle::ty::TypeFoldable;
+use rustc_middle::ty::{self, Ty, TyCtxt};
 
 pub trait TraitEngineExt<'tcx> {
     fn new(tcx: TyCtxt<'tcx>) -> Box<Self>;
@@ -9,10 +19,94 @@ pub trait TraitEngineExt<'tcx> {
 
 impl<'tcx> TraitEngineExt<'tcx> for dyn TraitEngine<'tcx> {
     fn new(tcx: TyCtxt<'tcx>) -> Box<Self> {
-        if tcx.sess.opts.debugging_opts.chalk {
+        if tcx.sess.opts.unstable_opts.chalk {
             Box::new(ChalkFulfillmentContext::new())
         } else {
             Box::new(FulfillmentContext::new())
         }
     }
 }
+
+/// Used if you want to have pleasant experience when dealing
+/// with obligations outside of hir or mir typeck.
+pub struct ObligationCtxt<'a, 'tcx> {
+    pub infcx: &'a InferCtxt<'a, 'tcx>,
+    engine: RefCell<Box<dyn TraitEngine<'tcx>>>,
+}
+
+impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> {
+    pub fn new(infcx: &'a InferCtxt<'a, 'tcx>) -> Self {
+        Self { infcx, engine: RefCell::new(<dyn TraitEngine<'_>>::new(infcx.tcx)) }
+    }
+
+    pub fn register_obligation(&self, obligation: PredicateObligation<'tcx>) {
+        self.engine.borrow_mut().register_predicate_obligation(self.infcx, obligation);
+    }
+
+    pub fn register_obligations(
+        &self,
+        obligations: impl IntoIterator<Item = PredicateObligation<'tcx>>,
+    ) {
+        // Can't use `register_predicate_obligations` because the iterator
+        // may also use this `ObligationCtxt`.
+        for obligation in obligations {
+            self.engine.borrow_mut().register_predicate_obligation(self.infcx, obligation)
+        }
+    }
+
+    pub fn register_infer_ok_obligations<T>(&self, infer_ok: InferOk<'tcx, T>) -> T {
+        let InferOk { value, obligations } = infer_ok;
+        self.engine.borrow_mut().register_predicate_obligations(self.infcx, obligations);
+        value
+    }
+
+    /// Requires that `ty` must implement the trait with `def_id` in
+    /// the given environment. This trait must not have any type
+    /// parameters (except for `Self`).
+    pub fn register_bound(
+        &self,
+        cause: ObligationCause<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        ty: Ty<'tcx>,
+        def_id: DefId,
+    ) {
+        let tcx = self.infcx.tcx;
+        let trait_ref = ty::TraitRef { def_id, substs: tcx.mk_substs_trait(ty, &[]) };
+        self.register_obligation(Obligation {
+            cause,
+            recursion_depth: 0,
+            param_env,
+            predicate: ty::Binder::dummy(trait_ref).without_const().to_predicate(tcx),
+        });
+    }
+
+    pub fn normalize<T: TypeFoldable<'tcx>>(
+        &self,
+        cause: ObligationCause<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        value: T,
+    ) -> T {
+        let infer_ok = self.infcx.partially_normalize_associated_types_in(cause, param_env, value);
+        self.register_infer_ok_obligations(infer_ok)
+    }
+
+    pub fn equate_types(
+        &self,
+        cause: &ObligationCause<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        expected: Ty<'tcx>,
+        actual: Ty<'tcx>,
+    ) -> Result<(), TypeError<'tcx>> {
+        match self.infcx.at(cause, param_env).eq(expected, actual) {
+            Ok(InferOk { obligations, value: () }) => {
+                self.register_obligations(obligations);
+                Ok(())
+            }
+            Err(e) => Err(e),
+        }
+    }
+
+    pub fn select_all_or_error(&self) -> Vec<FulfillmentError<'tcx>> {
+        self.engine.borrow_mut().select_all_or_error(self.infcx)
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
index 8d7c6b26ba1..25ba520ace2 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
@@ -22,10 +22,9 @@ use rustc_hir::intravisit::Visitor;
 use rustc_hir::GenericParam;
 use rustc_hir::Item;
 use rustc_hir::Node;
-use rustc_infer::infer::error_reporting::same_type_modulo_infer;
-use rustc_infer::traits::{AmbiguousSelection, TraitEngine};
-use rustc_middle::thir::abstract_const::NotConstEvaluatable;
+use rustc_infer::traits::TraitEngine;
 use rustc_middle::traits::select::OverflowError;
+use rustc_middle::ty::abstract_const::NotConstEvaluatable;
 use rustc_middle::ty::error::ExpectedFound;
 use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable};
 use rustc_middle::ty::{
@@ -302,13 +301,10 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
                         span = obligation.cause.span;
                     }
                 }
-                if let ObligationCauseCode::CompareImplMethodObligation {
-                    impl_item_def_id,
-                    trait_item_def_id,
-                }
-                | ObligationCauseCode::CompareImplTypeObligation {
+                if let ObligationCauseCode::CompareImplItemObligation {
                     impl_item_def_id,
                     trait_item_def_id,
+                    kind: _,
                 } = *obligation.cause.code()
                 {
                     self.report_extra_impl_obligation(
@@ -474,7 +470,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
                         if let Some(ref s) = label {
                             // If it has a custom `#[rustc_on_unimplemented]`
                             // error message, let's display it as the label!
-                            err.span_label(span, s.as_str());
+                            err.span_label(span, s);
                             if !matches!(trait_ref.skip_binder().self_ty().kind(), ty::Param(_)) {
                                 // When the self type is a type param We don't need to "the trait
                                 // `std::marker::Sized` is not implemented for `T`" as we will point
@@ -531,7 +527,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
                             let enclosing_scope_span =
                                 tcx.hir().span_with_body(tcx.hir().local_def_id_to_hir_id(body));
 
-                            err.span_label(enclosing_scope_span, s.as_str());
+                            err.span_label(enclosing_scope_span, s);
                         }
 
                         self.suggest_floating_point_literal(&obligation, &mut err, &trait_ref);
@@ -635,12 +631,12 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
                                         &format!(
                                             "expected a closure taking {} argument{}, but one taking {} argument{} was given",
                                             given.len(),
-                                            if given.len() == 1 { "" } else { "s" },
+                                            pluralize!(given.len()),
                                             expected.len(),
-                                            if expected.len() == 1 { "" } else { "s" },
+                                            pluralize!(expected.len()),
                                         )
                                     );
-                                } else if !same_type_modulo_infer(given_ty, expected_ty) {
+                                } else if !self.same_type_modulo_infer(given_ty, expected_ty) {
                                     // Print type mismatch
                                     let (expected_args, given_args) =
                                         self.cmp(given_ty, expected_ty);
@@ -665,11 +661,12 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
                             self.suggest_restricting_param_bound(
                                 &mut err,
                                 trait_predicate,
+                                None,
                                 obligation.cause.body_id,
                             );
                         } else if !suggested {
                             // Can't show anything else useful, try to find similar impls.
-                            let impl_candidates = self.find_similar_impl_candidates(trait_ref);
+                            let impl_candidates = self.find_similar_impl_candidates(trait_predicate);
                             if !self.report_similar_impl_candidates(
                                 impl_candidates,
                                 trait_ref,
@@ -704,7 +701,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
                                 {
                                     let trait_ref = trait_pred.to_poly_trait_ref();
                                     let impl_candidates =
-                                        self.find_similar_impl_candidates(trait_ref);
+                                        self.find_similar_impl_candidates(trait_pred);
                                     self.report_similar_impl_candidates(
                                         impl_candidates,
                                         trait_ref,
@@ -788,24 +785,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
                         span_bug!(span, "coerce requirement gave wrong error: `{:?}`", predicate)
                     }
 
-                    ty::PredicateKind::RegionOutlives(predicate) => {
-                        let predicate = bound_predicate.rebind(predicate);
-                        let predicate = self.resolve_vars_if_possible(predicate);
-                        let err = self
-                            .region_outlives_predicate(&obligation.cause, predicate)
-                            .err()
-                            .unwrap();
-                        struct_span_err!(
-                            self.tcx.sess,
-                            span,
-                            E0279,
-                            "the requirement `{}` is not satisfied (`{}`)",
-                            predicate,
-                            err,
-                        )
-                    }
-
-                    ty::PredicateKind::Projection(..) | ty::PredicateKind::TypeOutlives(..) => {
+                    ty::PredicateKind::RegionOutlives(..)
+                    | ty::PredicateKind::Projection(..)
+                    | ty::PredicateKind::TypeOutlives(..) => {
                         let predicate = self.resolve_vars_if_possible(obligation.predicate);
                         struct_span_err!(
                             self.tcx.sess,
@@ -823,10 +805,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
 
                     ty::PredicateKind::ClosureKind(closure_def_id, closure_substs, kind) => {
                         let found_kind = self.closure_kind(closure_substs).unwrap();
-                        let closure_span =
-                            self.tcx.sess.source_map().guess_head_span(
-                                self.tcx.hir().span_if_local(closure_def_id).unwrap(),
-                            );
+                        let closure_span = self.tcx.def_span(closure_def_id);
                         let mut err = struct_span_err!(
                             self.tcx.sess,
                             closure_span,
@@ -884,7 +863,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
                     }
 
                     ty::PredicateKind::WellFormed(ty) => {
-                        if !self.tcx.sess.opts.debugging_opts.chalk {
+                        if !self.tcx.sess.opts.unstable_opts.chalk {
                             // WF predicates cannot themselves make
                             // errors. They can only block due to
                             // ambiguity; otherwise, they always
@@ -951,9 +930,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
                     _ => None,
                 };
 
-                let found_span = found_did
-                    .and_then(|did| self.tcx.hir().span_if_local(did))
-                    .map(|sp| self.tcx.sess.source_map().guess_head_span(sp)); // the sp could be an fn def
+                let found_span = found_did.and_then(|did| self.tcx.hir().span_if_local(did));
 
                 if self.reported_closure_mismatch.borrow().contains(&(span, found_span)) {
                     // We check closures twice, with obligations flowing in different directions,
@@ -1086,10 +1063,10 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
         let hir = self.tcx.hir();
         Some(match node {
             Node::Expr(&hir::Expr {
-                kind: hir::ExprKind::Closure { body, fn_decl_span, .. },
+                kind: hir::ExprKind::Closure(&hir::Closure { body, fn_decl_span, .. }),
                 ..
             }) => (
-                sm.guess_head_span(fn_decl_span),
+                fn_decl_span,
                 hir.body(body)
                     .params
                     .iter()
@@ -1348,7 +1325,7 @@ trait InferCtxtPrivExt<'hir, 'tcx> {
 
     fn find_similar_impl_candidates(
         &self,
-        trait_ref: ty::PolyTraitRef<'tcx>,
+        trait_pred: ty::PolyTraitPredicate<'tcx>,
     ) -> Vec<ImplCandidate<'tcx>>;
 
     fn report_similar_impl_candidates(
@@ -1407,7 +1384,7 @@ trait InferCtxtPrivExt<'hir, 'tcx> {
     fn annotate_source_of_ambiguity(
         &self,
         err: &mut Diagnostic,
-        impls: &[AmbiguousSelection],
+        impls: &[DefId],
         predicate: ty::Predicate<'tcx>,
     );
 
@@ -1717,18 +1694,22 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
 
     fn find_similar_impl_candidates(
         &self,
-        trait_ref: ty::PolyTraitRef<'tcx>,
+        trait_pred: ty::PolyTraitPredicate<'tcx>,
     ) -> Vec<ImplCandidate<'tcx>> {
         self.tcx
-            .all_impls(trait_ref.def_id())
+            .all_impls(trait_pred.def_id())
             .filter_map(|def_id| {
-                if self.tcx.impl_polarity(def_id) == ty::ImplPolarity::Negative {
+                if self.tcx.impl_polarity(def_id) == ty::ImplPolarity::Negative
+                    || !trait_pred
+                        .skip_binder()
+                        .is_constness_satisfied_by(self.tcx.constness(def_id))
+                {
                     return None;
                 }
 
                 let imp = self.tcx.impl_trait_ref(def_id).unwrap();
 
-                self.fuzzy_match_tys(trait_ref.skip_binder().self_ty(), imp.self_ty(), false)
+                self.fuzzy_match_tys(trait_pred.skip_binder().self_ty(), imp.self_ty(), false)
                     .map(|similarity| ImplCandidate { trait_ref: imp, similarity })
             })
             .collect()
@@ -2040,14 +2021,6 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
                 );
                 match selcx.select_from_obligation(&obligation) {
                     Err(SelectionError::Ambiguous(impls)) if impls.len() > 1 => {
-                        if self.is_tainted_by_errors() && subst.is_none() {
-                            // If `subst.is_none()`, then this is probably two param-env
-                            // candidates or impl candidates that are equal modulo lifetimes.
-                            // Therefore, if we've already emitted an error, just skip this
-                            // one, since it's not particularly actionable.
-                            err.cancel();
-                            return;
-                        }
                         self.annotate_source_of_ambiguity(&mut err, &impls, predicate);
                     }
                     _ => {
@@ -2116,6 +2089,98 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
                         );
                     }
                 }
+
+                if let (Some(body_id), Some(ty::subst::GenericArgKind::Type(_))) =
+                    (body_id, subst.map(|subst| subst.unpack()))
+                {
+                    struct FindExprBySpan<'hir> {
+                        span: Span,
+                        result: Option<&'hir hir::Expr<'hir>>,
+                    }
+
+                    impl<'v> hir::intravisit::Visitor<'v> for FindExprBySpan<'v> {
+                        fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
+                            if self.span == ex.span {
+                                self.result = Some(ex);
+                            } else {
+                                hir::intravisit::walk_expr(self, ex);
+                            }
+                        }
+                    }
+
+                    let mut expr_finder = FindExprBySpan { span, result: None };
+
+                    expr_finder.visit_expr(&self.tcx.hir().body(body_id).value);
+
+                    if let Some(hir::Expr {
+                        kind: hir::ExprKind::Path(hir::QPath::Resolved(None, path)), .. }
+                    ) = expr_finder.result
+                        && let [
+                            ..,
+                            trait_path_segment @ hir::PathSegment {
+                                res: Some(rustc_hir::def::Res::Def(rustc_hir::def::DefKind::Trait, trait_id)),
+                                ..
+                            },
+                            hir::PathSegment {
+                                ident: assoc_item_name,
+                                res: Some(rustc_hir::def::Res::Def(_, item_id)),
+                                ..
+                            }
+                        ] = path.segments
+                        && data.trait_ref.def_id == *trait_id
+                        && self.tcx.trait_of_item(item_id) == Some(*trait_id)
+                        && !self.is_tainted_by_errors()
+                    {
+                        let (verb, noun) = match self.tcx.associated_item(item_id).kind {
+                            ty::AssocKind::Const => ("refer to the", "constant"),
+                            ty::AssocKind::Fn => ("call", "function"),
+                            ty::AssocKind::Type => ("refer to the", "type"), // this is already covered by E0223, but this single match arm doesn't hurt here
+                        };
+
+                        // Replace the more general E0283 with a more specific error
+                        err.cancel();
+                        err = self.tcx.sess.struct_span_err_with_code(
+                            span,
+                            &format!(
+                                "cannot {verb} associated {noun} on trait without specifying the corresponding `impl` type",
+                             ),
+                            rustc_errors::error_code!(E0790),
+                        );
+
+                        if let Some(local_def_id) = data.trait_ref.def_id.as_local()
+                            && let Some(hir::Node::Item(hir::Item { ident: trait_name, kind: hir::ItemKind::Trait(_, _, _, _, trait_item_refs), .. })) = self.tcx.hir().find_by_def_id(local_def_id)
+                            && let Some(method_ref) = trait_item_refs.iter().find(|item_ref| item_ref.ident == *assoc_item_name) {
+                            err.span_label(method_ref.span, format!("`{}::{}` defined here", trait_name, assoc_item_name));
+                        }
+
+                        err.span_label(span, format!("cannot {verb} associated {noun} of trait"));
+
+                        let trait_impls = self.tcx.trait_impls_of(data.trait_ref.def_id);
+
+                        if trait_impls.blanket_impls().is_empty()
+                            && let Some((impl_ty, _)) = trait_impls.non_blanket_impls().iter().next()
+                            && let Some(impl_def_id) = impl_ty.def() {
+                            let message = if trait_impls.non_blanket_impls().len() == 1 {
+                                "use the fully-qualified path to the only available implementation".to_string()
+                            } else {
+                                format!(
+                                    "use a fully-qualified path to a specific available implementation ({} found)",
+                                    trait_impls.non_blanket_impls().len()
+                                )
+                            };
+
+                            err.multipart_suggestion(
+                                message,
+                                vec![
+                                    (trait_path_segment.ident.span.shrink_to_lo(), format!("<{} as ", self.tcx.def_path(impl_def_id).to_string_no_crate_verbose())),
+                                    (trait_path_segment.ident.span.shrink_to_hi(), format!(">"))
+                                ],
+                                Applicability::MaybeIncorrect
+                            );
+                        }
+                    }
+                };
+
                 err
             }
 
@@ -2179,6 +2244,33 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
                 }
             }
 
+            ty::PredicateKind::ConstEvaluatable(data) => {
+                if predicate.references_error() || self.is_tainted_by_errors() {
+                    return;
+                }
+                let subst = data.substs.iter().find(|g| g.has_infer_types_or_consts());
+                if let Some(subst) = subst {
+                    let err = self.emit_inference_failure_err(
+                        body_id,
+                        span,
+                        subst,
+                        ErrorCode::E0284,
+                        true,
+                    );
+                    err
+                } else {
+                    // If we can't find a substitution, just print a generic error
+                    let mut err = struct_span_err!(
+                        self.tcx.sess,
+                        span,
+                        E0284,
+                        "type annotations needed: cannot satisfy `{}`",
+                        predicate,
+                    );
+                    err.span_label(span, &format!("cannot satisfy `{}`", predicate));
+                    err
+                }
+            }
             _ => {
                 if self.tcx.sess.has_errors().is_some() || self.is_tainted_by_errors() {
                     return;
@@ -2201,35 +2293,24 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
     fn annotate_source_of_ambiguity(
         &self,
         err: &mut Diagnostic,
-        impls: &[AmbiguousSelection],
+        impls: &[DefId],
         predicate: ty::Predicate<'tcx>,
     ) {
         let mut spans = vec![];
         let mut crates = vec![];
         let mut post = vec![];
-        let mut or_where_clause = false;
-        for ambig in impls {
-            match ambig {
-                AmbiguousSelection::Impl(def_id) => match self.tcx.span_of_impl(*def_id) {
-                    Ok(span) => spans.push(span),
-                    Err(name) => {
-                        crates.push(name);
-                        if let Some(header) = to_pretty_impl_header(self.tcx, *def_id) {
-                            post.push(header);
-                        }
+        for def_id in impls {
+            match self.tcx.span_of_impl(*def_id) {
+                Ok(span) => spans.push(span),
+                Err(name) => {
+                    crates.push(name);
+                    if let Some(header) = to_pretty_impl_header(self.tcx, *def_id) {
+                        post.push(header);
                     }
-                },
-                AmbiguousSelection::ParamEnv(span) => {
-                    or_where_clause = true;
-                    spans.push(*span);
                 }
             }
         }
-        let msg = format!(
-            "multiple `impl`s{} satisfying `{}` found",
-            if or_where_clause { " or `where` clauses" } else { "" },
-            predicate
-        );
+        let msg = format!("multiple `impl`s satisfying `{}` found", predicate);
         let mut crate_names: Vec<_> = crates.iter().map(|n| format!("`{}`", n)).collect();
         crate_names.sort();
         crate_names.dedup();
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs
index 7c9ee64a0c2..7e8872d9018 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs
@@ -103,7 +103,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
                 })
             }),
             hir::Node::Expr(hir::Expr {
-                kind: hir::ExprKind::Closure { body, movability, .. },
+                kind: hir::ExprKind::Closure(hir::Closure { body, movability, .. }),
                 ..
             }) => self.describe_generator(*body).or_else(|| {
                 Some(if movability.is_some() { "an async closure" } else { "a closure" })
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
index 33585de6001..6c8faed0df4 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -24,7 +24,8 @@ use rustc_middle::hir::map;
 use rustc_middle::ty::{
     self, suggest_arbitrary_trait_bound, suggest_constraining_type_param, AdtKind, DefIdTree,
     GeneratorDiagnosticData, GeneratorInteriorTypeCause, Infer, InferTy, IsSuggestable,
-    ToPredicate, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitable,
+    ProjectionPredicate, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable,
+    TypeVisitable,
 };
 use rustc_middle::ty::{TypeAndMut, TypeckResults};
 use rustc_session::Limit;
@@ -172,6 +173,7 @@ pub trait InferCtxtExt<'tcx> {
         &self,
         err: &mut Diagnostic,
         trait_pred: ty::PolyTraitPredicate<'tcx>,
+        proj_pred: Option<ty::PolyProjectionPredicate<'tcx>>,
         body_id: hir::HirId,
     );
 
@@ -182,7 +184,7 @@ pub trait InferCtxtExt<'tcx> {
         trait_pred: ty::PolyTraitPredicate<'tcx>,
     ) -> bool;
 
-    fn get_closure_name(&self, def_id: DefId, err: &mut Diagnostic, msg: &str) -> Option<String>;
+    fn get_closure_name(&self, def_id: DefId, err: &mut Diagnostic, msg: &str) -> Option<Symbol>;
 
     fn suggest_fn_call(
         &self,
@@ -376,7 +378,7 @@ fn suggest_restriction<'tcx>(
             replace_ty: ty::ParamTy::new(generics.count() as u32, Symbol::intern(&type_param_name))
                 .to_ty(tcx),
         });
-        if !trait_pred.is_suggestable(tcx) {
+        if !trait_pred.is_suggestable(tcx, false) {
             return;
         }
         // We know we have an `impl Trait` that doesn't satisfy a required projection.
@@ -415,7 +417,7 @@ fn suggest_restriction<'tcx>(
             Applicability::MaybeIncorrect,
         );
     } else {
-        if !trait_pred.is_suggestable(tcx) {
+        if !trait_pred.is_suggestable(tcx, false) {
             return;
         }
         // Trivial case: `T` needs an extra bound: `T: Bound`.
@@ -457,6 +459,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
         &self,
         mut err: &mut Diagnostic,
         trait_pred: ty::PolyTraitPredicate<'tcx>,
+        proj_pred: Option<ty::PolyProjectionPredicate<'tcx>>,
         body_id: hir::HirId,
     ) {
         let trait_pred = self.resolve_numeric_literals_with_default(trait_pred);
@@ -583,15 +586,34 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
                     // else in the predicate.
                     if !trait_pred.skip_binder().trait_ref.substs[1..]
                         .iter()
-                        .all(|g| g.is_suggestable(self.tcx))
+                        .all(|g| g.is_suggestable(self.tcx, false))
                     {
                         return;
                     }
                     // Missing generic type parameter bound.
                     let param_name = self_ty.to_string();
-                    let constraint = with_no_trimmed_paths!(
+                    let mut constraint = with_no_trimmed_paths!(
                         trait_pred.print_modifiers_and_trait_path().to_string()
                     );
+
+                    if let Some(proj_pred) = proj_pred {
+                        let ProjectionPredicate { projection_ty, term } = proj_pred.skip_binder();
+                        let item = self.tcx.associated_item(projection_ty.item_def_id);
+
+                        // FIXME: this case overlaps with code in TyCtxt::note_and_explain_type_err.
+                        // That should be extracted into a helper function.
+                        if constraint.ends_with('>') {
+                            constraint = format!(
+                                "{}, {}={}>",
+                                &constraint[..constraint.len() - 1],
+                                item.name,
+                                term
+                            );
+                        } else {
+                            constraint.push_str(&format!("<{}={}>", item.name, term));
+                        }
+                    }
+
                     if suggest_constraining_type_param(
                         self.tcx,
                         generics,
@@ -715,13 +737,13 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
     /// Given a closure's `DefId`, return the given name of the closure.
     ///
     /// This doesn't account for reassignments, but it's only used for suggestions.
-    fn get_closure_name(&self, def_id: DefId, err: &mut Diagnostic, msg: &str) -> Option<String> {
-        let get_name = |err: &mut Diagnostic, kind: &hir::PatKind<'_>| -> Option<String> {
+    fn get_closure_name(&self, def_id: DefId, err: &mut Diagnostic, msg: &str) -> Option<Symbol> {
+        let get_name = |err: &mut Diagnostic, kind: &hir::PatKind<'_>| -> Option<Symbol> {
             // Get the local name of this closure. This can be inaccurate because
             // of the possibility of reassignment, but this should be good enough.
             match &kind {
-                hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, _, name, None) => {
-                    Some(format!("{}", name))
+                hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, _, ident, None) => {
+                    Some(ident.name)
                 }
                 _ => {
                     err.note(msg);
@@ -786,7 +808,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
         // Get the name of the callable and the arguments to be used in the suggestion.
         let (snippet, sugg) = match hir.get_if_local(def_id) {
             Some(hir::Node::Expr(hir::Expr {
-                kind: hir::ExprKind::Closure { fn_decl, fn_decl_span, .. },
+                kind: hir::ExprKind::Closure(hir::Closure { fn_decl, fn_decl_span, .. }),
                 ..
             })) => {
                 err.span_label(*fn_decl_span, "consider calling this closure");
@@ -1311,7 +1333,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
         visitor.visit_body(&body);
 
         let typeck_results = self.in_progress_typeck_results.map(|t| t.borrow()).unwrap();
-        let Some(liberated_sig) = typeck_results.liberated_fn_sigs().get(fn_hir_id) else { return false; };
+        let Some(liberated_sig) = typeck_results.liberated_fn_sigs().get(fn_hir_id).copied() else { return false; };
 
         let ret_types = visitor
             .returns
@@ -1543,7 +1565,6 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
             ty::Generator(..) => "generator",
             _ => "function",
         };
-        let span = self.tcx.sess.source_map().guess_head_span(span);
         let mut err = struct_span_err!(
             self.tcx.sess,
             span,
@@ -2661,11 +2682,11 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
                     )
                 });
             }
-            ObligationCauseCode::CompareImplMethodObligation { trait_item_def_id, .. } => {
+            ObligationCauseCode::CompareImplItemObligation { trait_item_def_id, kind, .. } => {
                 let item_name = self.tcx.item_name(trait_item_def_id);
                 let msg = format!(
-                    "the requirement `{}` appears on the impl method `{}` but not on the \
-                     corresponding trait method",
+                    "the requirement `{}` appears on the `impl`'s {kind} `{}` but not on the \
+                     corresponding trait's {kind}",
                     predicate, item_name,
                 );
                 let sp = self
@@ -2676,32 +2697,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
                 let mut assoc_span: MultiSpan = sp.into();
                 assoc_span.push_span_label(
                     sp,
-                    format!("this trait method doesn't have the requirement `{}`", predicate),
-                );
-                if let Some(ident) = self
-                    .tcx
-                    .opt_associated_item(trait_item_def_id)
-                    .and_then(|i| self.tcx.opt_item_ident(i.container.id()))
-                {
-                    assoc_span.push_span_label(ident.span, "in this trait");
-                }
-                err.span_note(assoc_span, &msg);
-            }
-            ObligationCauseCode::CompareImplTypeObligation { trait_item_def_id, .. } => {
-                let item_name = self.tcx.item_name(trait_item_def_id);
-                let msg = format!(
-                    "the requirement `{}` appears on the associated impl type `{}` but not on the \
-                     corresponding associated trait type",
-                    predicate, item_name,
-                );
-                let sp = self.tcx.def_span(trait_item_def_id);
-                let mut assoc_span: MultiSpan = sp.into();
-                assoc_span.push_span_label(
-                    sp,
-                    format!(
-                        "this trait associated type doesn't have the requirement `{}`",
-                        predicate,
-                    ),
+                    format!("this trait's {kind} doesn't have the requirement `{}`", predicate),
                 );
                 if let Some(ident) = self
                     .tcx
@@ -2712,13 +2708,6 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
                 }
                 err.span_note(assoc_span, &msg);
             }
-            ObligationCauseCode::CompareImplConstObligation => {
-                err.note(&format!(
-                    "the requirement `{}` appears on the associated impl constant \
-                     but not on the corresponding associated trait constant",
-                    predicate
-                ));
-            }
             ObligationCauseCode::TrivialBound => {
                 err.help("see issue #48214");
                 if tcx.sess.opts.unstable_features.is_nightly_build() {
@@ -2826,7 +2815,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
         trait_ref: &ty::PolyTraitRef<'tcx>,
     ) {
         let rhs_span = match obligation.cause.code() {
-            ObligationCauseCode::BinOp { rhs_span: Some(span), is_lit } if *is_lit => span,
+            ObligationCauseCode::BinOp { rhs_span: Some(span), is_lit, .. } if *is_lit => span,
             _ => return,
         };
         match (
diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs
index 78600652254..556ef466cd1 100644
--- a/compiler/rustc_trait_selection/src/traits/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs
@@ -6,7 +6,7 @@ use rustc_data_structures::obligation_forest::{ObligationForest, ObligationProce
 use rustc_infer::traits::ProjectionCacheKey;
 use rustc_infer::traits::{SelectionError, TraitEngine, TraitEngineExt as _, TraitObligation};
 use rustc_middle::mir::interpret::ErrorHandled;
-use rustc_middle::thir::abstract_const::NotConstEvaluatable;
+use rustc_middle::ty::abstract_const::NotConstEvaluatable;
 use rustc_middle::ty::error::{ExpectedFound, TypeError};
 use rustc_middle::ty::subst::SubstsRef;
 use rustc_middle::ty::ToPredicate;
@@ -58,19 +58,6 @@ pub struct FulfillmentContext<'tcx> {
 
     relationships: FxHashMap<ty::TyVid, ty::FoundRelationships>,
 
-    // Should this fulfillment context register type-lives-for-region
-    // obligations on its parent infcx? In some cases, region
-    // obligations are either already known to hold (normalization) or
-    // hopefully verified elsewhere (type-impls-bound), and therefore
-    // should not be checked.
-    //
-    // Note that if we are normalizing a type that we already
-    // know is well-formed, there should be no harm setting this
-    // to true - all the region variables should be determinable
-    // using the RFC 447 rules, which don't depend on
-    // type-lives-for-region constraints, and because the type
-    // is well-formed, the constraints should hold.
-    register_region_obligations: bool,
     // Is it OK to register obligations into this infcx inside
     // an infcx snapshot?
     //
@@ -103,7 +90,6 @@ impl<'a, 'tcx> FulfillmentContext<'tcx> {
         FulfillmentContext {
             predicates: ObligationForest::new(),
             relationships: FxHashMap::default(),
-            register_region_obligations: true,
             usable_in_snapshot: false,
         }
     }
@@ -112,30 +98,18 @@ impl<'a, 'tcx> FulfillmentContext<'tcx> {
         FulfillmentContext {
             predicates: ObligationForest::new(),
             relationships: FxHashMap::default(),
-            register_region_obligations: true,
             usable_in_snapshot: true,
         }
     }
 
-    pub fn new_ignoring_regions() -> FulfillmentContext<'tcx> {
-        FulfillmentContext {
-            predicates: ObligationForest::new(),
-            relationships: FxHashMap::default(),
-            register_region_obligations: false,
-            usable_in_snapshot: false,
-        }
-    }
-
     /// Attempts to select obligations using `selcx`.
     fn select(&mut self, selcx: &mut SelectionContext<'a, 'tcx>) -> Vec<FulfillmentError<'tcx>> {
         let span = debug_span!("select", obligation_forest_size = ?self.predicates.len());
         let _enter = span.enter();
 
         // Process pending obligations.
-        let outcome: Outcome<_, _> = self.predicates.process_obligations(&mut FulfillProcessor {
-            selcx,
-            register_region_obligations: self.register_region_obligations,
-        });
+        let outcome: Outcome<_, _> =
+            self.predicates.process_obligations(&mut FulfillProcessor { selcx });
 
         // FIXME: if we kept the original cache key, we could mark projection
         // obligations as complete for the projection cache here.
@@ -239,7 +213,6 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> {
 
 struct FulfillProcessor<'a, 'b, 'tcx> {
     selcx: &'a mut SelectionContext<'b, 'tcx>,
-    register_region_obligations: bool,
 }
 
 fn mk_pending(os: Vec<PredicateObligation<'_>>) -> Vec<PendingPredicateObligation<'_>> {
@@ -295,6 +268,7 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> {
     /// This is called much less often than `needs_process_obligation`, so we
     /// never inline it.
     #[inline(never)]
+    #[instrument(level = "debug", skip(self, pending_obligation))]
     fn process_obligation(
         &mut self,
         pending_obligation: &mut PendingPredicateObligation<'tcx>,
@@ -303,7 +277,7 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> {
 
         let obligation = &mut pending_obligation.obligation;
 
-        debug!(?obligation, "process_obligation pre-resolve");
+        debug!(?obligation, "pre-resolve");
 
         if obligation.predicate.has_infer_types_or_consts() {
             obligation.predicate =
@@ -312,8 +286,6 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> {
 
         let obligation = &pending_obligation.obligation;
 
-        debug!(?obligation, ?obligation.cause, "process_obligation");
-
         let infcx = self.selcx.infcx();
 
         if obligation.predicate.has_projections() {
@@ -386,19 +358,16 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> {
                 }
 
                 ty::PredicateKind::RegionOutlives(data) => {
-                    match infcx.region_outlives_predicate(&obligation.cause, Binder::dummy(data)) {
-                        Ok(()) => ProcessResult::Changed(vec![]),
-                        Err(_) => ProcessResult::Error(CodeSelectionError(Unimplemented)),
+                    if infcx.considering_regions || data.has_placeholders() {
+                        infcx.region_outlives_predicate(&obligation.cause, Binder::dummy(data));
                     }
+
+                    ProcessResult::Changed(vec![])
                 }
 
                 ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(t_a, r_b)) => {
-                    if self.register_region_obligations {
-                        self.selcx.infcx().register_region_obligation_with_cause(
-                            t_a,
-                            r_b,
-                            &obligation.cause,
-                        );
+                    if infcx.considering_regions {
+                        infcx.register_region_obligation_with_cause(t_a, r_b, &obligation.cause);
                     }
                     ProcessResult::Changed(vec![])
                 }
@@ -603,7 +572,7 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> {
                         ),
                         (Err(ErrorHandled::Linted), _) | (_, Err(ErrorHandled::Linted)) => {
                             span_bug!(
-                                obligation.cause.span(self.selcx.tcx()),
+                                obligation.cause.span(),
                                 "ConstEquate: const_eval_resolve returned an unexpected error"
                             )
                         }
diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs
index 74d2eb17b6b..d0a17f712d3 100644
--- a/compiler/rustc_trait_selection/src/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/mod.rs
@@ -47,7 +47,7 @@ pub use self::SelectionError::*;
 
 pub use self::coherence::{add_placeholder_note, orphan_check, overlapping_impls};
 pub use self::coherence::{OrphanCheckErr, OverlapResult};
-pub use self::engine::TraitEngineExt;
+pub use self::engine::{ObligationCtxt, TraitEngineExt};
 pub use self::fulfill::{FulfillmentContext, PendingPredicateObligation};
 pub use self::object_safety::astconv_object_safety_violations;
 pub use self::object_safety::is_vtable_safe_method;
@@ -60,8 +60,9 @@ pub use self::select::{EvaluationResult, IntercrateAmbiguityCause, OverflowError
 pub use self::specialize::specialization_graph::FutureCompatOverlapError;
 pub use self::specialize::specialization_graph::FutureCompatOverlapErrorKind;
 pub use self::specialize::{specialization_graph, translate_substs, OverlapError};
-pub use self::structural_match::search_for_structural_match_violation;
-pub use self::structural_match::{NonStructuralMatchTy, NonStructuralMatchTyKind};
+pub use self::structural_match::{
+    search_for_adt_const_param_violation, search_for_structural_match_violation,
+};
 pub use self::util::{
     elaborate_obligations, elaborate_predicates, elaborate_predicates_with_span,
     elaborate_trait_ref, elaborate_trait_refs,
@@ -163,7 +164,7 @@ pub fn type_known_to_meet_bound_modulo_regions<'a, 'tcx>(
         // The handling of regions in this area of the code is terrible,
         // see issue #29149. We should be able to improve on this with
         // NLL.
-        let mut fulfill_cx = FulfillmentContext::new_ignoring_regions();
+        let mut fulfill_cx = FulfillmentContext::new();
 
         // We can use a dummy node-id here because we won't pay any mind
         // to region obligations that arise (there shouldn't really be any
@@ -207,21 +208,21 @@ fn do_normalize_predicates<'tcx>(
     predicates: Vec<ty::Predicate<'tcx>>,
 ) -> Result<Vec<ty::Predicate<'tcx>>, ErrorGuaranteed> {
     let span = cause.span;
-    tcx.infer_ctxt().enter(|infcx| {
-        // FIXME. We should really... do something with these region
-        // obligations. But this call just continues the older
-        // behavior (i.e., doesn't cause any new bugs), and it would
-        // take some further refactoring to actually solve them. In
-        // particular, we would have to handle implied bounds
-        // properly, and that code is currently largely confined to
-        // regionck (though I made some efforts to extract it
-        // out). -nmatsakis
-        //
-        // @arielby: In any case, these obligations are checked
-        // by wfcheck anyway, so I'm not sure we have to check
-        // them here too, and we will remove this function when
-        // we move over to lazy normalization *anyway*.
-        let fulfill_cx = FulfillmentContext::new_ignoring_regions();
+    // FIXME. We should really... do something with these region
+    // obligations. But this call just continues the older
+    // behavior (i.e., doesn't cause any new bugs), and it would
+    // take some further refactoring to actually solve them. In
+    // particular, we would have to handle implied bounds
+    // properly, and that code is currently largely confined to
+    // regionck (though I made some efforts to extract it
+    // out). -nmatsakis
+    //
+    // @arielby: In any case, these obligations are checked
+    // by wfcheck anyway, so I'm not sure we have to check
+    // them here too, and we will remove this function when
+    // we move over to lazy normalization *anyway*.
+    tcx.infer_ctxt().ignoring_regions().enter(|infcx| {
+        let fulfill_cx = FulfillmentContext::new();
         let predicates =
             match fully_normalize(&infcx, fulfill_cx, cause, elaborated_env, predicates) {
                 Ok(predicates) => predicates,
@@ -237,29 +238,37 @@ fn do_normalize_predicates<'tcx>(
         // cares about declarations like `'a: 'b`.
         let outlives_env = OutlivesEnvironment::new(elaborated_env);
 
-        infcx.resolve_regions_and_report_errors(&outlives_env);
+        // FIXME: It's very weird that we ignore region obligations but apparently
+        // still need to use `resolve_regions` as we need the resolved regions in
+        // the normalized predicates.
+        let errors = infcx.resolve_regions(&outlives_env);
+        if !errors.is_empty() {
+            tcx.sess.delay_span_bug(
+                span,
+                format!(
+                    "failed region resolution while normalizing {elaborated_env:?}: {errors:?}"
+                ),
+            );
+        }
 
-        let predicates = match infcx.fully_resolve(predicates) {
-            Ok(predicates) => predicates,
+        match infcx.fully_resolve(predicates) {
+            Ok(predicates) => Ok(predicates),
             Err(fixup_err) => {
                 // If we encounter a fixup error, it means that some type
                 // variable wound up unconstrained. I actually don't know
                 // if this can happen, and I certainly don't expect it to
                 // happen often, but if it did happen it probably
                 // represents a legitimate failure due to some kind of
-                // unconstrained variable, and it seems better not to ICE,
-                // all things considered.
-                let reported = tcx.sess.span_err(span, &fixup_err.to_string());
-                return Err(reported);
+                // unconstrained variable.
+                //
+                // @lcnr: Let's still ICE here for now. I want a test case
+                // for that.
+                span_bug!(
+                    span,
+                    "inference variables in normalized parameter environment: {}",
+                    fixup_err
+                );
             }
-        };
-        if predicates.needs_infer() {
-            let reported = tcx
-                .sess
-                .delay_span_bug(span, "encountered inference variables after `fully_resolve`");
-            Err(reported)
-        } else {
-            Ok(predicates)
         }
     })
 }
@@ -845,20 +854,6 @@ pub fn provide(providers: &mut ty::query::Providers) {
         vtable_entries,
         vtable_trait_upcasting_coercion_new_vptr_slot,
         subst_and_check_impossible_predicates,
-        thir_abstract_const: |tcx, def_id| {
-            let def_id = def_id.expect_local();
-            if let Some(def) = ty::WithOptConstParam::try_lookup(def_id, tcx) {
-                tcx.thir_abstract_const_of_const_arg(def)
-            } else {
-                const_evaluatable::thir_abstract_const(tcx, ty::WithOptConstParam::unknown(def_id))
-            }
-        },
-        thir_abstract_const_of_const_arg: |tcx, (did, param_did)| {
-            const_evaluatable::thir_abstract_const(
-                tcx,
-                ty::WithOptConstParam { did, const_param_did: Some(param_did) },
-            )
-        },
         try_unify_abstract_consts: |tcx, param_env_and| {
             let (param_env, (a, b)) = param_env_and.into_parts();
             const_evaluatable::try_unify_abstract_consts(tcx, (a, b), param_env)
diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs
index ac1811244ca..2921ce0ffef 100644
--- a/compiler/rustc_trait_selection/src/traits/object_safety.rs
+++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs
@@ -11,12 +11,12 @@
 use super::elaborate_predicates;
 
 use crate::infer::TyCtxtInferExt;
-use crate::traits::const_evaluatable::{self, AbstractConst};
 use crate::traits::query::evaluate_obligation::InferCtxtExt;
 use crate::traits::{self, Obligation, ObligationCause};
 use rustc_errors::{FatalError, MultiSpan};
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
+use rustc_middle::ty::abstract_const::{walk_abstract_const, AbstractConst};
 use rustc_middle::ty::subst::{GenericArg, InternalSubsts, Subst};
 use rustc_middle::ty::{
     self, EarlyBinder, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor,
@@ -841,15 +841,13 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable<'tcx>>(
             //
             // This shouldn't really matter though as we can't really use any
             // constants which are not considered const evaluatable.
-            use rustc_middle::thir::abstract_const::Node;
+            use rustc_middle::ty::abstract_const::Node;
             if let Ok(Some(ct)) = AbstractConst::new(self.tcx, uv.shrink()) {
-                const_evaluatable::walk_abstract_const(self.tcx, ct, |node| {
-                    match node.root(self.tcx) {
-                        Node::Leaf(leaf) => self.visit_const(leaf),
-                        Node::Cast(_, _, ty) => self.visit_ty(ty),
-                        Node::Binop(..) | Node::UnaryOp(..) | Node::FunctionCall(_, _) => {
-                            ControlFlow::CONTINUE
-                        }
+                walk_abstract_const(self.tcx, ct, |node| match node.root(self.tcx) {
+                    Node::Leaf(leaf) => self.visit_const(leaf),
+                    Node::Cast(_, _, ty) => self.visit_ty(ty),
+                    Node::Binop(..) | Node::UnaryOp(..) | Node::FunctionCall(_, _) => {
+                        ControlFlow::CONTINUE
                     }
                 })
             } else {
diff --git a/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs
index ed7d16f7a54..4d3b0b4cf07 100644
--- a/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs
+++ b/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs
@@ -337,7 +337,7 @@ impl<'tcx> OnUnimplementedFormatString {
                         }
                     }
                     // `{:1}` and `{}` are not to be used
-                    Position::ArgumentIs(_) | Position::ArgumentImplicitlyIs(_) => {
+                    Position::ArgumentIs(..) | Position::ArgumentImplicitlyIs(_) => {
                         let reported = struct_span_err!(
                             tcx.sess,
                             span,
diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs
index b3e7fbb3578..9de4d3a646c 100644
--- a/compiler/rustc_trait_selection/src/traits/project.rs
+++ b/compiler/rustc_trait_selection/src/traits/project.rs
@@ -753,10 +753,13 @@ impl<'tcx> TypeFolder<'tcx> for BoundVarReplacer<'_, 'tcx> {
                     .tcx
                     .mk_const(ty::ConstS { kind: ty::ConstKind::Placeholder(p), ty: ct.ty() })
             }
-            _ if ct.has_vars_bound_at_or_above(self.current_index) => ct.super_fold_with(self),
-            _ => ct,
+            _ => ct.super_fold_with(self),
         }
     }
+
+    fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> {
+        if p.has_vars_bound_at_or_above(self.current_index) { p.super_fold_with(self) } else { p }
+    }
 }
 
 // The inverse of `BoundVarReplacer`: replaces placeholders with the bound vars from which they came.
diff --git a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs
index db45ee3fed7..32669e23db9 100644
--- a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs
@@ -84,7 +84,7 @@ impl<'cx, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'cx, 'tcx> {
         // Run canonical query. If overflow occurs, rerun from scratch but this time
         // in standard trait query mode so that overflow is handled appropriately
         // within `SelectionContext`.
-        self.tcx.at(obligation.cause.span(self.tcx)).evaluate_obligation(c_pred)
+        self.tcx.at(obligation.cause.span()).evaluate_obligation(c_pred)
     }
 
     // Helper function that canonicalizes and runs the query. If an
diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
index eccfb3477b9..449d7a7b47b 100644
--- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
@@ -254,7 +254,7 @@ impl<'cx, 'tcx> FallibleTypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> {
                 let result = tcx.normalize_projection_ty(c_data)?;
                 // We don't expect ambiguity.
                 if result.is_ambiguous() {
-                    return Err(NoSolution);
+                    bug!("unexpected ambiguity: {:?} {:?}", c_data, result);
                 }
                 let InferOk { value: result, obligations } =
                     self.infcx.instantiate_query_response_and_region_obligations(
@@ -293,7 +293,7 @@ impl<'cx, 'tcx> FallibleTypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> {
                 let result = tcx.normalize_projection_ty(c_data)?;
                 // We don't expect ambiguity.
                 if result.is_ambiguous() {
-                    return Err(NoSolution);
+                    bug!("unexpected ambiguity: {:?} {:?}", c_data, result);
                 }
                 let InferOk { value: result, obligations } =
                     self.infcx.instantiate_query_response_and_region_obligations(
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs
index c9d46b2810d..c99564936aa 100644
--- a/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs
@@ -9,7 +9,6 @@ use rustc_infer::traits::TraitEngineExt as _;
 use rustc_span::source_map::DUMMY_SP;
 
 use std::fmt;
-use std::rc::Rc;
 
 pub struct CustomTypeOp<F, G> {
     closure: F,
@@ -109,7 +108,7 @@ pub fn scrape_region_constraints<'tcx, Op: super::TypeOp<'tcx, Output = R>, R>(
         Ok((
             TypeOpOutput {
                 output: value,
-                constraints: Some(Rc::new(region_constraints)),
+                constraints: Some(infcx.tcx.arena.alloc(region_constraints)),
                 error_info: None,
             },
             region_constraint_data,
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs
index 1e72dd69339..578e1d00cf9 100644
--- a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs
@@ -10,7 +10,6 @@ use rustc_infer::traits::PredicateObligations;
 use rustc_middle::ty::fold::TypeFoldable;
 use rustc_middle::ty::{ParamEnvAnd, TyCtxt};
 use std::fmt;
-use std::rc::Rc;
 
 pub mod ascribe_user_type;
 pub mod custom;
@@ -41,7 +40,7 @@ pub struct TypeOpOutput<'tcx, Op: TypeOp<'tcx>> {
     /// The output from the type op.
     pub output: Op::Output,
     /// Any region constraints from performing the type op.
-    pub constraints: Option<Rc<QueryRegionConstraints<'tcx>>>,
+    pub constraints: Option<&'tcx QueryRegionConstraints<'tcx>>,
     /// Used for error reporting to be able to rerun the query
     pub error_info: Option<Op::ErrorInfo>,
 }
@@ -156,11 +155,14 @@ where
             }
         }
 
-        // Promote the final query-region-constraints into a
-        // (optional) ref-counted vector:
-        let region_constraints =
-            if region_constraints.is_empty() { None } else { Some(Rc::new(region_constraints)) };
-
-        Ok(TypeOpOutput { output, constraints: region_constraints, error_info })
+        Ok(TypeOpOutput {
+            output,
+            constraints: if region_constraints.is_empty() {
+                None
+            } else {
+                Some(infcx.tcx.arena.alloc(region_constraints))
+            },
+            error_info,
+        })
     }
 }
diff --git a/compiler/rustc_trait_selection/src/traits/relationships.rs b/compiler/rustc_trait_selection/src/traits/relationships.rs
index 56bdeafeeca..8148e2b7871 100644
--- a/compiler/rustc_trait_selection/src/traits/relationships.rs
+++ b/compiler/rustc_trait_selection/src/traits/relationships.rs
@@ -31,14 +31,14 @@ pub(crate) fn update<'tcx, T>(
             obligation
                 .predicate
                 .kind()
-                .map_bound(|_| {
+                .rebind(
                     // (*) binder moved here
                     ty::PredicateKind::Trait(ty::TraitPredicate {
                         trait_ref,
                         constness: tpred.constness,
                         polarity: tpred.polarity,
                     })
-                })
+                )
                 .to_predicate(infcx.tcx),
         );
         // Don't report overflow errors. Otherwise equivalent to may_hold.
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 96d83deeeb7..6e8581128dd 100644
--- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
@@ -6,11 +6,9 @@
 //!
 //! [rustc dev guide]:https://rustc-dev-guide.rust-lang.org/traits/resolution.html#candidate-assembly
 use hir::LangItem;
-use rustc_data_structures::fx::FxHashMap;
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
-use rustc_infer::traits::util::elaborate_predicates_with_span;
-use rustc_infer::traits::{AmbiguousSelection, TraitEngine};
+use rustc_infer::traits::TraitEngine;
 use rustc_infer::traits::{Obligation, SelectionError, TraitObligation};
 use rustc_lint_defs::builtin::DEREF_INTO_DYN_SUPERTRAIT;
 use rustc_middle::ty::print::with_no_trimmed_paths;
@@ -201,48 +199,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                     // and report ambiguity.
                     if i > 1 {
                         debug!("multiple matches, ambig");
-
-                        // Collect a list of (probable) spans that point to a param-env candidate
-                        let tcx = self.infcx.tcx;
-                        let owner = stack.obligation.cause.body_id.owner.to_def_id();
-                        let predicates = tcx.predicates_of(owner).instantiate_identity(tcx);
-                        let param_env_spans: FxHashMap<_, _> = elaborate_predicates_with_span(
-                            tcx,
-                            std::iter::zip(predicates.predicates, predicates.spans),
-                        )
-                        .filter_map(|obligation| {
-                            let kind = obligation.predicate.kind();
-                            if let ty::PredicateKind::Trait(trait_pred) = kind.skip_binder() {
-                                if trait_pred.trait_ref
-                                    == ty::TraitRef::identity(tcx, trait_pred.def_id())
-                                        .skip_binder()
-                                {
-                                    // HACK: Remap the `Self: Trait` predicate that every trait has to a more useful span
-                                    Some((
-                                        kind.rebind(trait_pred),
-                                        tcx.def_span(trait_pred.def_id()),
-                                    ))
-                                } else {
-                                    Some((kind.rebind(trait_pred), obligation.cause.span))
-                                }
-                            } else {
-                                None
-                            }
-                        })
-                        .collect();
-
                         return Err(Ambiguous(
                             candidates
                                 .into_iter()
                                 .filter_map(|c| match c.candidate {
-                                    SelectionCandidate::ImplCandidate(def_id) => {
-                                        Some(AmbiguousSelection::Impl(def_id))
-                                    }
-                                    SelectionCandidate::ParamCandidate(predicate) => {
-                                        Some(AmbiguousSelection::ParamEnv(
-                                            *param_env_spans.get(&predicate)?,
-                                        ))
-                                    }
+                                    SelectionCandidate::ImplCandidate(def_id) => Some(def_id),
                                     _ => None,
                                 })
                                 .collect(),
diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
index e1131140c39..d4c9fd1c5f9 100644
--- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
@@ -12,7 +12,7 @@ use rustc_index::bit_set::GrowableBitSet;
 use rustc_infer::infer::InferOk;
 use rustc_infer::infer::LateBoundRegionConversionTime::HigherRankedType;
 use rustc_middle::ty::subst::{GenericArg, GenericArgKind, InternalSubsts, Subst, SubstsRef};
-use rustc_middle::ty::{self, EarlyBinder, GenericParamDefKind, Ty, TyCtxt};
+use rustc_middle::ty::{self, GenericParamDefKind, Ty, TyCtxt};
 use rustc_middle::ty::{ToPolyTraitRef, ToPredicate};
 use rustc_span::def_id::DefId;
 
@@ -42,115 +42,96 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         obligation: &TraitObligation<'tcx>,
         candidate: SelectionCandidate<'tcx>,
     ) -> Result<Selection<'tcx>, SelectionError<'tcx>> {
-        let mut obligation = obligation;
-        let new_obligation;
-
-        // HACK(const_trait_impl): the surrounding environment is remapped to a non-const context
-        // because nested obligations might be actually `~const` then (incorrectly) requiring
-        // const impls. for example:
-        // ```
-        // pub trait Super {}
-        // pub trait Sub: Super {}
-        //
-        // impl<A> const Super for &A where A: ~const Super {}
-        // impl<A> const Sub for &A where A: ~const Sub {}
-        // ```
-        //
-        // The procedure to check the code above without the remapping code is as follows:
-        // ```
-        // CheckWf(impl const Sub for &A where A: ~const Sub) // <- const env
-        // CheckPredicate(&A: Super)
-        // CheckPredicate(A: ~const Super) // <- still const env, failure
-        // ```
-        if obligation.param_env.is_const() && !obligation.predicate.is_const_if_const() {
-            new_obligation = TraitObligation {
-                cause: obligation.cause.clone(),
-                param_env: obligation.param_env.without_const(),
-                ..*obligation
-            };
-            obligation = &new_obligation;
-        }
-
-        match candidate {
+        let mut impl_src = match candidate {
             BuiltinCandidate { has_nested } => {
                 let data = self.confirm_builtin_candidate(obligation, has_nested);
-                Ok(ImplSource::Builtin(data))
+                ImplSource::Builtin(data)
             }
 
             ParamCandidate(param) => {
                 let obligations =
                     self.confirm_param_candidate(obligation, param.map_bound(|t| t.trait_ref));
-                Ok(ImplSource::Param(obligations, param.skip_binder().constness))
+                ImplSource::Param(obligations, param.skip_binder().constness)
             }
 
             ImplCandidate(impl_def_id) => {
-                Ok(ImplSource::UserDefined(self.confirm_impl_candidate(obligation, impl_def_id)))
+                ImplSource::UserDefined(self.confirm_impl_candidate(obligation, impl_def_id))
             }
 
             AutoImplCandidate(trait_def_id) => {
                 let data = self.confirm_auto_impl_candidate(obligation, trait_def_id);
-                Ok(ImplSource::AutoImpl(data))
+                ImplSource::AutoImpl(data)
             }
 
             ProjectionCandidate(idx) => {
                 let obligations = self.confirm_projection_candidate(obligation, idx)?;
                 // FIXME(jschievink): constness
-                Ok(ImplSource::Param(obligations, ty::BoundConstness::NotConst))
+                ImplSource::Param(obligations, ty::BoundConstness::NotConst)
             }
 
             ObjectCandidate(idx) => {
                 let data = self.confirm_object_candidate(obligation, idx)?;
-                Ok(ImplSource::Object(data))
+                ImplSource::Object(data)
             }
 
             ClosureCandidate => {
                 let vtable_closure = self.confirm_closure_candidate(obligation)?;
-                Ok(ImplSource::Closure(vtable_closure))
+                ImplSource::Closure(vtable_closure)
             }
 
             GeneratorCandidate => {
                 let vtable_generator = self.confirm_generator_candidate(obligation)?;
-                Ok(ImplSource::Generator(vtable_generator))
+                ImplSource::Generator(vtable_generator)
             }
 
             FnPointerCandidate { .. } => {
                 let data = self.confirm_fn_pointer_candidate(obligation)?;
-                Ok(ImplSource::FnPointer(data))
+                ImplSource::FnPointer(data)
             }
 
             DiscriminantKindCandidate => {
-                Ok(ImplSource::DiscriminantKind(ImplSourceDiscriminantKindData))
+                ImplSource::DiscriminantKind(ImplSourceDiscriminantKindData)
             }
 
-            PointeeCandidate => Ok(ImplSource::Pointee(ImplSourcePointeeData)),
+            PointeeCandidate => ImplSource::Pointee(ImplSourcePointeeData),
 
             TraitAliasCandidate(alias_def_id) => {
                 let data = self.confirm_trait_alias_candidate(obligation, alias_def_id);
-                Ok(ImplSource::TraitAlias(data))
+                ImplSource::TraitAlias(data)
             }
 
             BuiltinObjectCandidate => {
                 // This indicates something like `Trait + Send: Send`. In this case, we know that
                 // this holds because that's what the object type is telling us, and there's really
                 // no additional obligations to prove and no types in particular to unify, etc.
-                Ok(ImplSource::Param(Vec::new(), ty::BoundConstness::NotConst))
+                ImplSource::Param(Vec::new(), ty::BoundConstness::NotConst)
             }
 
             BuiltinUnsizeCandidate => {
                 let data = self.confirm_builtin_unsize_candidate(obligation)?;
-                Ok(ImplSource::Builtin(data))
+                ImplSource::Builtin(data)
             }
 
             TraitUpcastingUnsizeCandidate(idx) => {
                 let data = self.confirm_trait_upcasting_unsize_candidate(obligation, idx)?;
-                Ok(ImplSource::TraitUpcasting(data))
+                ImplSource::TraitUpcasting(data)
             }
 
             ConstDestructCandidate(def_id) => {
                 let data = self.confirm_const_destruct_candidate(obligation, def_id)?;
-                Ok(ImplSource::ConstDestruct(data))
+                ImplSource::ConstDestruct(data)
             }
+        };
+
+        if !obligation.predicate.is_const_if_const() {
+            // normalize nested predicates according to parent predicate's constness.
+            impl_src = impl_src.map(|mut o| {
+                o.predicate = o.predicate.without_const(self.tcx());
+                o
+            });
         }
+
+        Ok(impl_src)
     }
 
     fn confirm_projection_candidate(
@@ -555,7 +536,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
 
                         let bound_vars = tcx.mk_bound_variable_kinds(bound_vars.into_iter());
                         let bound =
-                            EarlyBinder(bound.0.kind().skip_binder()).subst(tcx, assoc_ty_substs);
+                            bound.map_bound(|b| b.kind().skip_binder()).subst(tcx, assoc_ty_substs);
                         tcx.mk_predicate(ty::Binder::bind_with_vars(bound, bound_vars))
                     };
                 let normalized_bound = normalize_with_depth_to(
@@ -683,7 +664,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
 
         // FIXME: Chalk
 
-        if !self.tcx().sess.opts.debugging_opts.chalk {
+        if !self.tcx().sess.opts.unstable_opts.chalk {
             nested.push(Obligation::new(
                 obligation.cause.clone(),
                 obligation.param_env,
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index 2bb53a466ca..17f34012d1d 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -32,7 +32,7 @@ use rustc_hir::def_id::DefId;
 use rustc_infer::infer::LateBoundRegionConversionTime;
 use rustc_middle::dep_graph::{DepKind, DepNodeIndex};
 use rustc_middle::mir::interpret::ErrorHandled;
-use rustc_middle::thir::abstract_const::NotConstEvaluatable;
+use rustc_middle::ty::abstract_const::NotConstEvaluatable;
 use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
 use rustc_middle::ty::fold::BottomUpFolder;
 use rustc_middle::ty::print::with_no_trimmed_paths;
@@ -742,7 +742,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                         | (_, Err(ErrorHandled::Reported(_))) => Ok(EvaluatedToErr),
                         (Err(ErrorHandled::Linted), _) | (_, Err(ErrorHandled::Linted)) => {
                             span_bug!(
-                                obligation.cause.span(self.tcx()),
+                                obligation.cause.span(),
                                 "ConstEquate: const_eval_resolve returned an unexpected error"
                             )
                         }
@@ -789,7 +789,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         let mut param_env = obligation.param_env;
 
         fresh_trait_pred = fresh_trait_pred.map_bound(|mut pred| {
-            pred.remap_constness(self.tcx(), &mut param_env);
+            pred.remap_constness(&mut param_env);
             pred
         });
 
@@ -1321,7 +1321,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         }
         let tcx = self.tcx();
         let mut pred = cache_fresh_trait_pred.skip_binder();
-        pred.remap_constness(tcx, &mut param_env);
+        pred.remap_constness(&mut param_env);
 
         if self.can_use_global_caches(param_env) {
             if let Some(res) = tcx.selection_cache.get(&(param_env, pred), tcx) {
@@ -1375,7 +1375,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         let tcx = self.tcx();
         let mut pred = cache_fresh_trait_pred.skip_binder();
 
-        pred.remap_constness(tcx, &mut param_env);
+        pred.remap_constness(&mut param_env);
 
         if !self.can_cache_candidate(&candidate) {
             debug!(?pred, ?candidate, "insert_candidate_cache - candidate is not cacheable");
diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
index 2c4a453aefc..5f77aae6f22 100644
--- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
@@ -207,18 +207,7 @@ fn fulfill_implication<'a, 'tcx>(
     // (which are packed up in penv)
 
     infcx.save_and_restore_in_snapshot_flag(|infcx| {
-        // If we came from `translate_substs`, we already know that the
-        // predicates for our impl hold (after all, we know that a more
-        // specialized impl holds, so our impl must hold too), and
-        // we only want to process the projections to determine the
-        // the types in our substs using RFC 447, so we can safely
-        // ignore region obligations, which allows us to avoid threading
-        // a node-id to assign them with.
-        //
-        // If we came from specialization graph construction, then
-        // we already make a mockery out of the region system, so
-        // why not ignore them a bit earlier?
-        let mut fulfill_cx = FulfillmentContext::new_ignoring_regions();
+        let mut fulfill_cx = FulfillmentContext::new();
         for oblig in obligations.chain(more_obligations) {
             fulfill_cx.register_predicate_obligation(&infcx, oblig);
         }
diff --git a/compiler/rustc_trait_selection/src/traits/structural_match.rs b/compiler/rustc_trait_selection/src/traits/structural_match.rs
index 6c0b83fbd03..c278752e3d9 100644
--- a/compiler/rustc_trait_selection/src/traits/structural_match.rs
+++ b/compiler/rustc_trait_selection/src/traits/structural_match.rs
@@ -6,29 +6,10 @@ use rustc_data_structures::fx::FxHashSet;
 use rustc_hir as hir;
 use rustc_hir::lang_items::LangItem;
 use rustc_middle::ty::query::Providers;
-use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor};
+use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor};
 use rustc_span::Span;
 use std::ops::ControlFlow;
 
-#[derive(Debug)]
-pub struct NonStructuralMatchTy<'tcx> {
-    pub ty: Ty<'tcx>,
-    pub kind: NonStructuralMatchTyKind<'tcx>,
-}
-
-#[derive(Debug)]
-pub enum NonStructuralMatchTyKind<'tcx> {
-    Adt(AdtDef<'tcx>),
-    Param,
-    Dynamic,
-    Foreign,
-    Opaque,
-    Closure,
-    Generator,
-    Projection,
-    Float,
-}
-
 /// This method traverses the structure of `ty`, trying to find an
 /// instance of an ADT (i.e. struct or enum) that doesn't implement
 /// the structural-match traits, or a generic type parameter
@@ -54,15 +35,28 @@ pub enum NonStructuralMatchTyKind<'tcx> {
 /// For more background on why Rust has this requirement, and issues
 /// that arose when the requirement was not enforced completely, see
 /// Rust RFC 1445, rust-lang/rust#61188, and rust-lang/rust#62307.
-///
-/// The floats_allowed flag is used to deny constants in floating point
 pub fn search_for_structural_match_violation<'tcx>(
     span: Span,
     tcx: TyCtxt<'tcx>,
     ty: Ty<'tcx>,
-    floats_allowed: bool,
-) -> Option<NonStructuralMatchTy<'tcx>> {
-    ty.visit_with(&mut Search { tcx, span, seen: FxHashSet::default(), floats_allowed })
+) -> Option<Ty<'tcx>> {
+    ty.visit_with(&mut Search { tcx, span, seen: FxHashSet::default(), adt_const_param: false })
+        .break_value()
+}
+
+/// This method traverses the structure of `ty`, trying to find any
+/// types that are not allowed to be used in a const generic.
+///
+/// This is either because the type does not implement `StructuralEq`
+/// and `StructuralPartialEq`, or because the type is intentionally
+/// not supported in const generics (such as floats and raw pointers,
+/// which are allowed in match blocks).
+pub fn search_for_adt_const_param_violation<'tcx>(
+    span: Span,
+    tcx: TyCtxt<'tcx>,
+    ty: Ty<'tcx>,
+) -> Option<Ty<'tcx>> {
+    ty.visit_with(&mut Search { tcx, span, seen: FxHashSet::default(), adt_const_param: true })
         .break_value()
 }
 
@@ -125,7 +119,10 @@ struct Search<'tcx> {
     /// we will not recur on them again.
     seen: FxHashSet<hir::def_id::DefId>,
 
-    floats_allowed: bool,
+    // Additionally deny things that have been allowed in patterns,
+    // but are not allowed in adt const params, such as floats and
+    // fn ptrs.
+    adt_const_param: bool,
 }
 
 impl<'tcx> Search<'tcx> {
@@ -135,7 +132,7 @@ impl<'tcx> Search<'tcx> {
 }
 
 impl<'tcx> TypeVisitor<'tcx> for Search<'tcx> {
-    type BreakTy = NonStructuralMatchTy<'tcx>;
+    type BreakTy = Ty<'tcx>;
 
     fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
         debug!("Search visiting ty: {:?}", ty);
@@ -143,51 +140,27 @@ impl<'tcx> TypeVisitor<'tcx> for Search<'tcx> {
         let (adt_def, substs) = match *ty.kind() {
             ty::Adt(adt_def, substs) => (adt_def, substs),
             ty::Param(_) => {
-                let kind = NonStructuralMatchTyKind::Param;
-                return ControlFlow::Break(NonStructuralMatchTy { ty, kind });
+                return ControlFlow::Break(ty);
             }
             ty::Dynamic(..) => {
-                let kind = NonStructuralMatchTyKind::Dynamic;
-                return ControlFlow::Break(NonStructuralMatchTy { ty, kind });
+                return ControlFlow::Break(ty);
             }
             ty::Foreign(_) => {
-                let kind = NonStructuralMatchTyKind::Foreign;
-                return ControlFlow::Break(NonStructuralMatchTy { ty, kind });
+                return ControlFlow::Break(ty);
             }
             ty::Opaque(..) => {
-                let kind = NonStructuralMatchTyKind::Opaque;
-                return ControlFlow::Break(NonStructuralMatchTy { ty, kind });
+                return ControlFlow::Break(ty);
             }
             ty::Projection(..) => {
-                let kind = NonStructuralMatchTyKind::Projection;
-                return ControlFlow::Break(NonStructuralMatchTy { ty, kind });
+                return ControlFlow::Break(ty);
             }
             ty::Closure(..) => {
-                let kind = NonStructuralMatchTyKind::Closure;
-                return ControlFlow::Break(NonStructuralMatchTy { ty, kind });
+                return ControlFlow::Break(ty);
             }
             ty::Generator(..) | ty::GeneratorWitness(..) => {
-                let kind = NonStructuralMatchTyKind::Generator;
-                return ControlFlow::Break(NonStructuralMatchTy { ty, kind });
-            }
-            ty::RawPtr(..) => {
-                // structural-match ignores substructure of
-                // `*const _`/`*mut _`, so skip `super_visit_with`.
-                //
-                // For example, if you have:
-                // ```
-                // struct NonStructural;
-                // #[derive(PartialEq, Eq)]
-                // struct T(*const NonStructural);
-                // const C: T = T(std::ptr::null());
-                // ```
-                //
-                // Even though `NonStructural` does not implement `PartialEq`,
-                // structural equality on `T` does not recur into the raw
-                // pointer. Therefore, one can still use `C` in a pattern.
-                return ControlFlow::CONTINUE;
+                return ControlFlow::Break(ty);
             }
-            ty::FnDef(..) | ty::FnPtr(..) => {
+            ty::FnDef(..) => {
                 // Types of formals and return in `fn(_) -> _` are also irrelevant;
                 // so we do not recur into them via `super_visit_with`
                 return ControlFlow::CONTINUE;
@@ -206,14 +179,41 @@ impl<'tcx> TypeVisitor<'tcx> for Search<'tcx> {
                 return ControlFlow::CONTINUE;
             }
 
+            ty::FnPtr(..) => {
+                if !self.adt_const_param {
+                    return ControlFlow::CONTINUE;
+                } else {
+                    return ControlFlow::Break(ty);
+                }
+            }
+
+            ty::RawPtr(..) => {
+                if !self.adt_const_param {
+                    // structural-match ignores substructure of
+                    // `*const _`/`*mut _`, so skip `super_visit_with`.
+                    //
+                    // For example, if you have:
+                    // ```
+                    // struct NonStructural;
+                    // #[derive(PartialEq, Eq)]
+                    // struct T(*const NonStructural);
+                    // const C: T = T(std::ptr::null());
+                    // ```
+                    //
+                    // Even though `NonStructural` does not implement `PartialEq`,
+                    // structural equality on `T` does not recur into the raw
+                    // pointer. Therefore, one can still use `C` in a pattern.
+                    return ControlFlow::CONTINUE;
+                } else {
+                    return ControlFlow::Break(ty);
+                }
+            }
+
             ty::Float(_) => {
-                if self.floats_allowed {
+                if !self.adt_const_param {
                     return ControlFlow::CONTINUE;
                 } else {
-                    return ControlFlow::Break(NonStructuralMatchTy {
-                        ty,
-                        kind: NonStructuralMatchTyKind::Float,
-                    });
+                    return ControlFlow::Break(ty);
                 }
             }
 
@@ -239,8 +239,7 @@ impl<'tcx> TypeVisitor<'tcx> for Search<'tcx> {
 
         if !self.type_marked_structural(ty) {
             debug!("Search found ty: {:?}", ty);
-            let kind = NonStructuralMatchTyKind::Adt(adt_def);
-            return ControlFlow::Break(NonStructuralMatchTy { ty, kind });
+            return ControlFlow::Break(ty);
         }
 
         // structural-match does not care about the
diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs
index 7b5e1498f26..414857f0acc 100644
--- a/compiler/rustc_trait_selection/src/traits/wf.rs
+++ b/compiler/rustc_trait_selection/src/traits/wf.rs
@@ -60,12 +60,19 @@ pub fn obligations<'a, 'tcx>(
         GenericArgKind::Lifetime(..) => return Some(Vec::new()),
     };
 
-    let mut wf =
-        WfPredicates { infcx, param_env, body_id, span, out: vec![], recursion_depth, item: None };
+    let mut wf = WfPredicates {
+        tcx: infcx.tcx,
+        param_env,
+        body_id,
+        span,
+        out: vec![],
+        recursion_depth,
+        item: None,
+    };
     wf.compute(arg);
     debug!("wf::obligations({:?}, body_id={:?}) = {:?}", arg, body_id, wf.out);
 
-    let result = wf.normalize();
+    let result = wf.normalize(infcx);
     debug!("wf::obligations({:?}, body_id={:?}) ~~> {:?}", arg, body_id, result);
     Some(result)
 }
@@ -78,12 +85,12 @@ pub fn trait_obligations<'a, 'tcx>(
     infcx: &InferCtxt<'a, 'tcx>,
     param_env: ty::ParamEnv<'tcx>,
     body_id: hir::HirId,
-    trait_ref: &ty::TraitRef<'tcx>,
+    trait_pred: &ty::TraitPredicate<'tcx>,
     span: Span,
     item: &'tcx hir::Item<'tcx>,
 ) -> Vec<traits::PredicateObligation<'tcx>> {
     let mut wf = WfPredicates {
-        infcx,
+        tcx: infcx.tcx,
         param_env,
         body_id,
         span,
@@ -91,9 +98,9 @@ pub fn trait_obligations<'a, 'tcx>(
         recursion_depth: 0,
         item: Some(item),
     };
-    wf.compute_trait_ref(trait_ref, Elaborate::All);
+    wf.compute_trait_pred(trait_pred, Elaborate::All);
     debug!(obligations = ?wf.out);
-    wf.normalize()
+    wf.normalize(infcx)
 }
 
 pub fn predicate_obligations<'a, 'tcx>(
@@ -104,7 +111,7 @@ pub fn predicate_obligations<'a, 'tcx>(
     span: Span,
 ) -> Vec<traits::PredicateObligation<'tcx>> {
     let mut wf = WfPredicates {
-        infcx,
+        tcx: infcx.tcx,
         param_env,
         body_id,
         span,
@@ -116,7 +123,7 @@ pub fn predicate_obligations<'a, 'tcx>(
     // It's ok to skip the binder here because wf code is prepared for it
     match predicate.kind().skip_binder() {
         ty::PredicateKind::Trait(t) => {
-            wf.compute_trait_ref(&t.trait_ref, Elaborate::None);
+            wf.compute_trait_pred(&t, Elaborate::None);
         }
         ty::PredicateKind::RegionOutlives(..) => {}
         ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty, _reg)) => {
@@ -159,11 +166,11 @@ pub fn predicate_obligations<'a, 'tcx>(
         }
     }
 
-    wf.normalize()
+    wf.normalize(infcx)
 }
 
-struct WfPredicates<'a, 'tcx> {
-    infcx: &'a InferCtxt<'a, 'tcx>,
+struct WfPredicates<'tcx> {
+    tcx: TyCtxt<'tcx>,
     param_env: ty::ParamEnv<'tcx>,
     body_id: hir::HirId,
     span: Span,
@@ -260,18 +267,17 @@ fn extend_cause_with_original_assoc_item_obligation<'tcx>(
     }
 }
 
-impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
+impl<'tcx> WfPredicates<'tcx> {
     fn tcx(&self) -> TyCtxt<'tcx> {
-        self.infcx.tcx
+        self.tcx
     }
 
     fn cause(&self, code: traits::ObligationCauseCode<'tcx>) -> traits::ObligationCause<'tcx> {
         traits::ObligationCause::new(self.span, self.body_id, code)
     }
 
-    fn normalize(mut self) -> Vec<traits::PredicateObligation<'tcx>> {
+    fn normalize(self, infcx: &InferCtxt<'_, 'tcx>) -> Vec<traits::PredicateObligation<'tcx>> {
         let cause = self.cause(traits::WellFormed(None));
-        let infcx = &mut self.infcx;
         let param_env = self.param_env;
         let mut obligations = Vec::with_capacity(self.out.len());
         for mut obligation in self.out {
@@ -295,11 +301,18 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
     }
 
     /// Pushes the obligations required for `trait_ref` to be WF into `self.out`.
-    fn compute_trait_ref(&mut self, trait_ref: &ty::TraitRef<'tcx>, elaborate: Elaborate) {
-        let tcx = self.infcx.tcx;
-        let obligations = self.nominal_obligations(trait_ref.def_id, trait_ref.substs);
+    fn compute_trait_pred(&mut self, trait_pred: &ty::TraitPredicate<'tcx>, elaborate: Elaborate) {
+        let tcx = self.tcx;
+        let trait_ref = &trait_pred.trait_ref;
+
+        // if the trait predicate is not const, the wf obligations should not be const as well.
+        let obligations = if trait_pred.constness == ty::BoundConstness::NotConst {
+            self.nominal_obligations_without_const(trait_ref.def_id, trait_ref.substs)
+        } else {
+            self.nominal_obligations(trait_ref.def_id, trait_ref.substs)
+        };
 
-        debug!("compute_trait_ref obligations {:?}", obligations);
+        debug!("compute_trait_pred obligations {:?}", obligations);
         let param_env = self.param_env;
         let depth = self.recursion_depth;
 
@@ -410,14 +423,14 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
         if !subty.has_escaping_bound_vars() {
             let cause = self.cause(cause);
             let trait_ref = ty::TraitRef {
-                def_id: self.infcx.tcx.require_lang_item(LangItem::Sized, None),
-                substs: self.infcx.tcx.mk_substs_trait(subty, &[]),
+                def_id: self.tcx.require_lang_item(LangItem::Sized, None),
+                substs: self.tcx.mk_substs_trait(subty, &[]),
             };
             self.out.push(traits::Obligation::with_depth(
                 cause,
                 self.recursion_depth,
                 self.param_env,
-                ty::Binder::dummy(trait_ref).without_const().to_predicate(self.infcx.tcx),
+                ty::Binder::dummy(trait_ref).without_const().to_predicate(self.tcx),
             ));
         }
     }
@@ -452,26 +465,16 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
                                 predicate,
                             ));
                         }
-                        ty::ConstKind::Infer(infer) => {
-                            let resolved = self.infcx.shallow_resolve(infer);
-                            // the `InferConst` changed, meaning that we made progress.
-                            if resolved != infer {
-                                let cause = self.cause(traits::WellFormed(None));
-
-                                let resolved_constant = self.infcx.tcx.mk_const(ty::ConstS {
-                                    kind: ty::ConstKind::Infer(resolved),
-                                    ty: constant.ty(),
-                                });
-                                self.out.push(traits::Obligation::with_depth(
-                                    cause,
-                                    self.recursion_depth,
-                                    self.param_env,
-                                    ty::Binder::dummy(ty::PredicateKind::WellFormed(
-                                        resolved_constant.into(),
-                                    ))
+                        ty::ConstKind::Infer(_) => {
+                            let cause = self.cause(traits::WellFormed(None));
+
+                            self.out.push(traits::Obligation::with_depth(
+                                cause,
+                                self.recursion_depth,
+                                self.param_env,
+                                ty::Binder::dummy(ty::PredicateKind::WellFormed(constant.into()))
                                     .to_predicate(self.tcx()),
-                                ));
-                            }
+                            ));
                         }
                         ty::ConstKind::Error(_)
                         | ty::ConstKind::Param(_)
@@ -563,7 +566,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
                     }
                 }
 
-                ty::Generator(..) => {
+                ty::Generator(did, substs, ..) => {
                     // Walk ALL the types in the generator: this will
                     // include the upvar types as well as the yield
                     // type. Note that this is mildly distinct from
@@ -571,6 +574,8 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
                     // about the signature of the closure. We don't
                     // have the problem of implied bounds here since
                     // generators don't take arguments.
+                    let obligations = self.nominal_obligations(did, substs);
+                    self.out.extend(obligations);
                 }
 
                 ty::Closure(did, substs) => {
@@ -622,12 +627,10 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
                 }
 
                 ty::Opaque(did, substs) => {
-                    // all of the requirements on type parameters
-                    // should've been checked by the instantiation
-                    // of whatever returned this exact `impl Trait`.
-
-                    // for named opaque `impl Trait` types we still need to check them
-                    if ty::is_impl_trait_defn(self.infcx.tcx, did).is_none() {
+                    // All of the requirements on type parameters
+                    // have already been checked for `impl Trait` in
+                    // return position. We do need to check type-alias-impl-trait though.
+                    if ty::is_impl_trait_defn(self.tcx, did).is_none() {
                         let obligations = self.nominal_obligations(did, substs);
                         self.out.extend(obligations);
                     }
@@ -675,57 +678,71 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
                 // See also the comment on `fn obligations`, describing "livelock"
                 // prevention, which happens before this can be reached.
                 ty::Infer(_) => {
-                    let ty = self.infcx.shallow_resolve(ty);
-                    if let ty::Infer(ty::TyVar(_)) = ty.kind() {
-                        // Not yet resolved, but we've made progress.
-                        let cause = self.cause(traits::WellFormed(None));
-                        self.out.push(traits::Obligation::with_depth(
-                            cause,
-                            self.recursion_depth,
-                            param_env,
-                            ty::Binder::dummy(ty::PredicateKind::WellFormed(ty.into()))
-                                .to_predicate(self.tcx()),
-                        ));
-                    } else {
-                        // Yes, resolved, proceed with the result.
-                        // FIXME(eddyb) add the type to `walker` instead of recursing.
-                        self.compute(ty.into());
-                    }
+                    let cause = self.cause(traits::WellFormed(None));
+                    self.out.push(traits::Obligation::with_depth(
+                        cause,
+                        self.recursion_depth,
+                        param_env,
+                        ty::Binder::dummy(ty::PredicateKind::WellFormed(ty.into()))
+                            .to_predicate(self.tcx()),
+                    ));
                 }
             }
         }
     }
 
-    fn nominal_obligations(
+    #[instrument(level = "debug", skip(self))]
+    fn nominal_obligations_inner(
         &mut self,
         def_id: DefId,
         substs: SubstsRef<'tcx>,
+        remap_constness: bool,
     ) -> Vec<traits::PredicateObligation<'tcx>> {
-        let predicates = self.infcx.tcx.predicates_of(def_id);
+        let predicates = self.tcx.predicates_of(def_id);
         let mut origins = vec![def_id; predicates.predicates.len()];
         let mut head = predicates;
         while let Some(parent) = head.parent {
-            head = self.infcx.tcx.predicates_of(parent);
+            head = self.tcx.predicates_of(parent);
             origins.extend(iter::repeat(parent).take(head.predicates.len()));
         }
 
-        let predicates = predicates.instantiate(self.infcx.tcx, substs);
+        let predicates = predicates.instantiate(self.tcx, substs);
+        trace!("{:#?}", predicates);
         debug_assert_eq!(predicates.predicates.len(), origins.len());
 
         iter::zip(iter::zip(predicates.predicates, predicates.spans), origins.into_iter().rev())
-            .map(|((pred, span), origin_def_id)| {
+            .map(|((mut pred, span), origin_def_id)| {
                 let code = if span.is_dummy() {
                     traits::MiscObligation
                 } else {
                     traits::BindingObligation(origin_def_id, span)
                 };
                 let cause = self.cause(code);
+                if remap_constness {
+                    pred = pred.without_const(self.tcx);
+                }
                 traits::Obligation::with_depth(cause, self.recursion_depth, self.param_env, pred)
             })
             .filter(|pred| !pred.has_escaping_bound_vars())
             .collect()
     }
 
+    fn nominal_obligations(
+        &mut self,
+        def_id: DefId,
+        substs: SubstsRef<'tcx>,
+    ) -> Vec<traits::PredicateObligation<'tcx>> {
+        self.nominal_obligations_inner(def_id, substs, false)
+    }
+
+    fn nominal_obligations_without_const(
+        &mut self,
+        def_id: DefId,
+        substs: SubstsRef<'tcx>,
+    ) -> Vec<traits::PredicateObligation<'tcx>> {
+        self.nominal_obligations_inner(def_id, substs, true)
+    }
+
     fn from_object_ty(
         &mut self,
         ty: Ty<'tcx>,
@@ -764,7 +781,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
         // Note: in fact we only permit builtin traits, not `Bar<'d>`, I
         // am looking forward to the future here.
         if !data.has_escaping_bound_vars() && !region.has_escaping_bound_vars() {
-            let implicit_bounds = object_region_bounds(self.infcx.tcx, data);
+            let implicit_bounds = object_region_bounds(self.tcx, data);
 
             let explicit_bound = region;
 
@@ -777,7 +794,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
                     cause,
                     self.recursion_depth,
                     self.param_env,
-                    outlives.to_predicate(self.infcx.tcx),
+                    outlives.to_predicate(self.tcx),
                 ));
             }
         }
diff --git a/compiler/rustc_traits/src/evaluate_obligation.rs b/compiler/rustc_traits/src/evaluate_obligation.rs
index 3fc141471b9..49c9ba45963 100644
--- a/compiler/rustc_traits/src/evaluate_obligation.rs
+++ b/compiler/rustc_traits/src/evaluate_obligation.rs
@@ -1,4 +1,4 @@
-use rustc_infer::infer::TyCtxtInferExt;
+use rustc_infer::infer::{DefiningAnchor, TyCtxtInferExt};
 use rustc_middle::ty::query::Providers;
 use rustc_middle::ty::{ParamEnvAnd, TyCtxt};
 use rustc_span::source_map::DUMMY_SP;
@@ -16,7 +16,9 @@ fn evaluate_obligation<'tcx>(
     canonical_goal: CanonicalPredicateGoal<'tcx>,
 ) -> Result<EvaluationResult, OverflowError> {
     debug!("evaluate_obligation(canonical_goal={:#?})", canonical_goal);
-    tcx.infer_ctxt().enter_with_canonical(
+    // HACK This bubble is required for this tests to pass:
+    // impl-trait/issue99642.rs
+    tcx.infer_ctxt().with_opaque_type_inference(DefiningAnchor::Bubble).enter_with_canonical(
         DUMMY_SP,
         &canonical_goal,
         |ref infcx, goal, _canonical_inference_vars| {
diff --git a/compiler/rustc_traits/src/implied_outlives_bounds.rs b/compiler/rustc_traits/src/implied_outlives_bounds.rs
index c7cac8fca89..4d4d55de5f4 100644
--- a/compiler/rustc_traits/src/implied_outlives_bounds.rs
+++ b/compiler/rustc_traits/src/implied_outlives_bounds.rs
@@ -47,7 +47,7 @@ fn compute_implied_outlives_bounds<'tcx>(
     // process it next. Because the resulting predicates aren't always
     // guaranteed to be a subset of the original type, so we need to store the
     // WF args we've computed in a set.
-    let mut checked_wf_args = rustc_data_structures::stable_set::FxHashSet::default();
+    let mut checked_wf_args = rustc_data_structures::fx::FxHashSet::default();
     let mut wf_args = vec![ty.into()];
 
     let mut implied_bounds = vec![];
diff --git a/compiler/rustc_traits/src/type_op.rs b/compiler/rustc_traits/src/type_op.rs
index f8bac1d7b26..d895b647db0 100644
--- a/compiler/rustc_traits/src/type_op.rs
+++ b/compiler/rustc_traits/src/type_op.rs
@@ -2,7 +2,7 @@ use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_infer::infer::at::ToTrace;
 use rustc_infer::infer::canonical::{Canonical, QueryResponse};
-use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
+use rustc_infer::infer::{DefiningAnchor, InferCtxt, TyCtxtInferExt};
 use rustc_infer::traits::TraitEngineExt as _;
 use rustc_middle::ty::query::Providers;
 use rustc_middle::ty::subst::{GenericArg, Subst, UserSelfTy, UserSubsts};
@@ -258,10 +258,15 @@ fn type_op_prove_predicate<'tcx>(
     tcx: TyCtxt<'tcx>,
     canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, ProvePredicate<'tcx>>>,
 ) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, ()>>, NoSolution> {
-    tcx.infer_ctxt().enter_canonical_trait_query(&canonicalized, |infcx, fulfill_cx, key| {
-        type_op_prove_predicate_with_cause(infcx, fulfill_cx, key, ObligationCause::dummy());
-        Ok(())
-    })
+    // HACK This bubble is required for this test to pass:
+    // impl-trait/issue-99642.rs
+    tcx.infer_ctxt().with_opaque_type_inference(DefiningAnchor::Bubble).enter_canonical_trait_query(
+        &canonicalized,
+        |infcx, fulfill_cx, key| {
+            type_op_prove_predicate_with_cause(infcx, fulfill_cx, key, ObligationCause::dummy());
+            Ok(())
+        },
+    )
 }
 
 /// The core of the `type_op_prove_predicate` query: for diagnostics purposes in NLL HRTB errors,
diff --git a/compiler/rustc_ty_utils/Cargo.toml b/compiler/rustc_ty_utils/Cargo.toml
index d03d675bfd2..caad2ed4274 100644
--- a/compiler/rustc_ty_utils/Cargo.toml
+++ b/compiler/rustc_ty_utils/Cargo.toml
@@ -15,3 +15,4 @@ rustc_session = { path = "../rustc_session" }
 rustc_target = { path = "../rustc_target" }
 rustc_trait_selection = { path = "../rustc_trait_selection" }
 rustc_type_ir = { path = "../rustc_type_ir" }
+rustc_index = { path = "../rustc_index" }
diff --git a/compiler/rustc_ty_utils/src/consts.rs b/compiler/rustc_ty_utils/src/consts.rs
index 0b83cdb78dc..7c2f4db94ff 100644
--- a/compiler/rustc_ty_utils/src/consts.rs
+++ b/compiler/rustc_ty_utils/src/consts.rs
@@ -1,4 +1,12 @@
-use rustc_middle::ty::{self, TyCtxt};
+use rustc_errors::ErrorGuaranteed;
+use rustc_hir::def::DefKind;
+use rustc_hir::def_id::LocalDefId;
+use rustc_index::vec::IndexVec;
+use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput};
+use rustc_middle::ty::abstract_const::{CastKind, Node, NodeId};
+use rustc_middle::ty::{self, TyCtxt, TypeVisitable};
+use rustc_middle::{mir, thir};
+use rustc_span::Span;
 use rustc_target::abi::VariantIdx;
 
 use std::iter;
@@ -72,6 +80,390 @@ pub(crate) fn destructure_const<'tcx>(
     ty::DestructuredConst { variant, fields }
 }
 
+pub struct AbstractConstBuilder<'a, 'tcx> {
+    tcx: TyCtxt<'tcx>,
+    body_id: thir::ExprId,
+    body: &'a thir::Thir<'tcx>,
+    /// The current WIP node tree.
+    nodes: IndexVec<NodeId, Node<'tcx>>,
+}
+
+impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
+    fn root_span(&self) -> Span {
+        self.body.exprs[self.body_id].span
+    }
+
+    fn error(&mut self, span: Span, msg: &str) -> Result<!, ErrorGuaranteed> {
+        let reported = self
+            .tcx
+            .sess
+            .struct_span_err(self.root_span(), "overly complex generic constant")
+            .span_label(span, msg)
+            .help("consider moving this anonymous constant into a `const` function")
+            .emit();
+
+        Err(reported)
+    }
+    fn maybe_supported_error(&mut self, span: Span, msg: &str) -> Result<!, ErrorGuaranteed> {
+        let reported = self
+            .tcx
+            .sess
+            .struct_span_err(self.root_span(), "overly complex generic constant")
+            .span_label(span, msg)
+            .help("consider moving this anonymous constant into a `const` function")
+            .note("this operation may be supported in the future")
+            .emit();
+
+        Err(reported)
+    }
+
+    #[instrument(skip(tcx, body, body_id), level = "debug")]
+    pub fn new(
+        tcx: TyCtxt<'tcx>,
+        (body, body_id): (&'a thir::Thir<'tcx>, thir::ExprId),
+    ) -> Result<Option<AbstractConstBuilder<'a, 'tcx>>, ErrorGuaranteed> {
+        let builder = AbstractConstBuilder { tcx, body_id, body, nodes: IndexVec::new() };
+
+        struct IsThirPolymorphic<'a, 'tcx> {
+            is_poly: bool,
+            thir: &'a thir::Thir<'tcx>,
+        }
+
+        use crate::rustc_middle::thir::visit::Visitor;
+        use thir::visit;
+
+        impl<'a, 'tcx> IsThirPolymorphic<'a, 'tcx> {
+            fn expr_is_poly(&mut self, expr: &thir::Expr<'tcx>) -> bool {
+                if expr.ty.has_param_types_or_consts() {
+                    return true;
+                }
+
+                match expr.kind {
+                    thir::ExprKind::NamedConst { substs, .. } => substs.has_param_types_or_consts(),
+                    thir::ExprKind::ConstParam { .. } => true,
+                    thir::ExprKind::Repeat { value, count } => {
+                        self.visit_expr(&self.thir()[value]);
+                        count.has_param_types_or_consts()
+                    }
+                    _ => false,
+                }
+            }
+
+            fn pat_is_poly(&mut self, pat: &thir::Pat<'tcx>) -> bool {
+                if pat.ty.has_param_types_or_consts() {
+                    return true;
+                }
+
+                match pat.kind.as_ref() {
+                    thir::PatKind::Constant { value } => value.has_param_types_or_consts(),
+                    thir::PatKind::Range(thir::PatRange { lo, hi, .. }) => {
+                        lo.has_param_types_or_consts() || hi.has_param_types_or_consts()
+                    }
+                    _ => false,
+                }
+            }
+        }
+
+        impl<'a, 'tcx> visit::Visitor<'a, 'tcx> for IsThirPolymorphic<'a, 'tcx> {
+            fn thir(&self) -> &'a thir::Thir<'tcx> {
+                &self.thir
+            }
+
+            #[instrument(skip(self), level = "debug")]
+            fn visit_expr(&mut self, expr: &thir::Expr<'tcx>) {
+                self.is_poly |= self.expr_is_poly(expr);
+                if !self.is_poly {
+                    visit::walk_expr(self, expr)
+                }
+            }
+
+            #[instrument(skip(self), level = "debug")]
+            fn visit_pat(&mut self, pat: &thir::Pat<'tcx>) {
+                self.is_poly |= self.pat_is_poly(pat);
+                if !self.is_poly {
+                    visit::walk_pat(self, pat);
+                }
+            }
+        }
+
+        let mut is_poly_vis = IsThirPolymorphic { is_poly: false, thir: body };
+        visit::walk_expr(&mut is_poly_vis, &body[body_id]);
+        debug!("AbstractConstBuilder: is_poly={}", is_poly_vis.is_poly);
+        if !is_poly_vis.is_poly {
+            return Ok(None);
+        }
+
+        Ok(Some(builder))
+    }
+
+    /// We do not allow all binary operations in abstract consts, so filter disallowed ones.
+    fn check_binop(op: mir::BinOp) -> bool {
+        use mir::BinOp::*;
+        match op {
+            Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr | Shl | Shr | Eq | Lt | Le
+            | Ne | Ge | Gt => true,
+            Offset => false,
+        }
+    }
+
+    /// While we currently allow all unary operations, we still want to explicitly guard against
+    /// future changes here.
+    fn check_unop(op: mir::UnOp) -> bool {
+        use mir::UnOp::*;
+        match op {
+            Not | Neg => true,
+        }
+    }
+
+    /// Builds the abstract const by walking the thir and bailing out when
+    /// encountering an unsupported operation.
+    pub fn build(mut self) -> Result<&'tcx [Node<'tcx>], ErrorGuaranteed> {
+        debug!("AbstractConstBuilder::build: body={:?}", &*self.body);
+        self.recurse_build(self.body_id)?;
+
+        for n in self.nodes.iter() {
+            if let Node::Leaf(ct) = n {
+                if let ty::ConstKind::Unevaluated(ct) = ct.kind() {
+                    // `AbstractConst`s should not contain any promoteds as they require references which
+                    // are not allowed.
+                    assert_eq!(ct.promoted, None);
+                    assert_eq!(ct, self.tcx.erase_regions(ct));
+                }
+            }
+        }
+
+        Ok(self.tcx.arena.alloc_from_iter(self.nodes.into_iter()))
+    }
+
+    fn recurse_build(&mut self, node: thir::ExprId) -> Result<NodeId, ErrorGuaranteed> {
+        use thir::ExprKind;
+        let node = &self.body.exprs[node];
+        Ok(match &node.kind {
+            // I dont know if handling of these 3 is correct
+            &ExprKind::Scope { value, .. } => self.recurse_build(value)?,
+            &ExprKind::PlaceTypeAscription { source, .. }
+            | &ExprKind::ValueTypeAscription { source, .. } => self.recurse_build(source)?,
+            &ExprKind::Literal { lit, neg} => {
+                let sp = node.span;
+                let constant =
+                    match self.tcx.at(sp).lit_to_const(LitToConstInput { lit: &lit.node, ty: node.ty, neg }) {
+                        Ok(c) => c,
+                        Err(LitToConstError::Reported) => {
+                            self.tcx.const_error(node.ty)
+                        }
+                        Err(LitToConstError::TypeError) => {
+                            bug!("encountered type error in lit_to_const")
+                        }
+                    };
+
+                self.nodes.push(Node::Leaf(constant))
+            }
+            &ExprKind::NonHirLiteral { lit , user_ty: _} => {
+                let val = ty::ValTree::from_scalar_int(lit);
+                self.nodes.push(Node::Leaf(ty::Const::from_value(self.tcx, val, node.ty)))
+            }
+            &ExprKind::ZstLiteral { user_ty: _ } => {
+                let val = ty::ValTree::zst();
+                self.nodes.push(Node::Leaf(ty::Const::from_value(self.tcx, val, node.ty)))
+            }
+            &ExprKind::NamedConst { def_id, substs, user_ty: _ } => {
+                let uneval = ty::Unevaluated::new(ty::WithOptConstParam::unknown(def_id), substs);
+
+                let constant = self.tcx.mk_const(ty::ConstS {
+                                kind: ty::ConstKind::Unevaluated(uneval),
+                                ty: node.ty,
+                            });
+
+                self.nodes.push(Node::Leaf(constant))
+            }
+
+            ExprKind::ConstParam {param, ..} => {
+                let const_param = self.tcx.mk_const(ty::ConstS {
+                        kind: ty::ConstKind::Param(*param),
+                        ty: node.ty,
+                    });
+                self.nodes.push(Node::Leaf(const_param))
+            }
+
+            ExprKind::Call { fun, args, .. } => {
+                let fun = self.recurse_build(*fun)?;
+
+                let mut new_args = Vec::<NodeId>::with_capacity(args.len());
+                for &id in args.iter() {
+                    new_args.push(self.recurse_build(id)?);
+                }
+                let new_args = self.tcx.arena.alloc_slice(&new_args);
+                self.nodes.push(Node::FunctionCall(fun, new_args))
+            }
+            &ExprKind::Binary { op, lhs, rhs } if Self::check_binop(op) => {
+                let lhs = self.recurse_build(lhs)?;
+                let rhs = self.recurse_build(rhs)?;
+                self.nodes.push(Node::Binop(op, lhs, rhs))
+            }
+            &ExprKind::Unary { op, arg } if Self::check_unop(op) => {
+                let arg = self.recurse_build(arg)?;
+                self.nodes.push(Node::UnaryOp(op, arg))
+            }
+            // This is necessary so that the following compiles:
+            //
+            // ```
+            // fn foo<const N: usize>(a: [(); N + 1]) {
+            //     bar::<{ N + 1 }>();
+            // }
+            // ```
+            ExprKind::Block { body: thir::Block { stmts: box [], expr: Some(e), .. } } => {
+                self.recurse_build(*e)?
+            }
+            // `ExprKind::Use` happens when a `hir::ExprKind::Cast` is a
+            // "coercion cast" i.e. using a coercion or is a no-op.
+            // This is important so that `N as usize as usize` doesnt unify with `N as usize`. (untested)
+            &ExprKind::Use { source } => {
+                let arg = self.recurse_build(source)?;
+                self.nodes.push(Node::Cast(CastKind::Use, arg, node.ty))
+            }
+            &ExprKind::Cast { source } => {
+                let arg = self.recurse_build(source)?;
+                self.nodes.push(Node::Cast(CastKind::As, arg, node.ty))
+            }
+            ExprKind::Borrow{ arg, ..} => {
+                let arg_node = &self.body.exprs[*arg];
+
+                // Skip reborrows for now until we allow Deref/Borrow/AddressOf
+                // expressions.
+                // FIXME(generic_const_exprs): Verify/explain why this is sound
+                if let ExprKind::Deref { arg } = arg_node.kind {
+                    self.recurse_build(arg)?
+                } else {
+                    self.maybe_supported_error(
+                        node.span,
+                        "borrowing is not supported in generic constants",
+                    )?
+                }
+            }
+            // FIXME(generic_const_exprs): We may want to support these.
+            ExprKind::AddressOf { .. } | ExprKind::Deref {..}=> self.maybe_supported_error(
+                node.span,
+                "dereferencing or taking the address is not supported in generic constants",
+            )?,
+            ExprKind::Repeat { .. } | ExprKind::Array { .. } =>  self.maybe_supported_error(
+                node.span,
+                "array construction is not supported in generic constants",
+            )?,
+            ExprKind::Block { .. } => self.maybe_supported_error(
+                node.span,
+                "blocks are not supported in generic constant",
+            )?,
+            ExprKind::NeverToAny { .. } => self.maybe_supported_error(
+                node.span,
+                "converting nevers to any is not supported in generic constant",
+            )?,
+            ExprKind::Tuple { .. } => self.maybe_supported_error(
+                node.span,
+                "tuple construction is not supported in generic constants",
+            )?,
+            ExprKind::Index { .. } => self.maybe_supported_error(
+                node.span,
+                "indexing is not supported in generic constant",
+            )?,
+            ExprKind::Field { .. } => self.maybe_supported_error(
+                node.span,
+                "field access is not supported in generic constant",
+            )?,
+            ExprKind::ConstBlock { .. } => self.maybe_supported_error(
+                node.span,
+                "const blocks are not supported in generic constant",
+            )?,
+            ExprKind::Adt(_) => self.maybe_supported_error(
+                node.span,
+                "struct/enum construction is not supported in generic constants",
+            )?,
+            // dont know if this is correct
+            ExprKind::Pointer { .. } =>
+                self.error(node.span, "pointer casts are not allowed in generic constants")?,
+            ExprKind::Yield { .. } =>
+                self.error(node.span, "generator control flow is not allowed in generic constants")?,
+            ExprKind::Continue { .. } | ExprKind::Break { .. } | ExprKind::Loop { .. } => self
+                .error(
+                    node.span,
+                    "loops and loop control flow are not supported in generic constants",
+                )?,
+            ExprKind::Box { .. } =>
+                self.error(node.span, "allocations are not allowed in generic constants")?,
+
+            ExprKind::Unary { .. } => unreachable!(),
+            // we handle valid unary/binary ops above
+            ExprKind::Binary { .. } =>
+                self.error(node.span, "unsupported binary operation in generic constants")?,
+            ExprKind::LogicalOp { .. } =>
+                self.error(node.span, "unsupported operation in generic constants, short-circuiting operations would imply control flow")?,
+            ExprKind::Assign { .. } | ExprKind::AssignOp { .. } => {
+                self.error(node.span, "assignment is not supported in generic constants")?
+            }
+            ExprKind::Closure { .. } | ExprKind::Return { .. } => self.error(
+                node.span,
+                "closures and function keywords are not supported in generic constants",
+            )?,
+            // let expressions imply control flow
+            ExprKind::Match { .. } | ExprKind::If { .. } | ExprKind::Let { .. } =>
+                self.error(node.span, "control flow is not supported in generic constants")?,
+            ExprKind::InlineAsm { .. } => {
+                self.error(node.span, "assembly is not supported in generic constants")?
+            }
+
+            // we dont permit let stmts so `VarRef` and `UpvarRef` cant happen
+            ExprKind::VarRef { .. }
+            | ExprKind::UpvarRef { .. }
+            | ExprKind::StaticRef { .. }
+            | ExprKind::ThreadLocalRef(_) => {
+                self.error(node.span, "unsupported operation in generic constant")?
+            }
+        })
+    }
+}
+
+/// Builds an abstract const, do not use this directly, but use `AbstractConst::new` instead.
+pub fn thir_abstract_const<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    def: ty::WithOptConstParam<LocalDefId>,
+) -> Result<Option<&'tcx [Node<'tcx>]>, ErrorGuaranteed> {
+    if tcx.features().generic_const_exprs {
+        match tcx.def_kind(def.did) {
+            // FIXME(generic_const_exprs): We currently only do this for anonymous constants,
+            // meaning that we do not look into associated constants. I(@lcnr) am not yet sure whether
+            // we want to look into them or treat them as opaque projections.
+            //
+            // Right now we do neither of that and simply always fail to unify them.
+            DefKind::AnonConst | DefKind::InlineConst => (),
+            _ => return Ok(None),
+        }
+
+        let body = tcx.thir_body(def)?;
+
+        AbstractConstBuilder::new(tcx, (&*body.0.borrow(), body.1))?
+            .map(AbstractConstBuilder::build)
+            .transpose()
+    } else {
+        Ok(None)
+    }
+}
+
 pub fn provide(providers: &mut ty::query::Providers) {
-    *providers = ty::query::Providers { destructure_const, ..*providers };
+    *providers = ty::query::Providers {
+        destructure_const,
+        thir_abstract_const: |tcx, def_id| {
+            let def_id = def_id.expect_local();
+            if let Some(def) = ty::WithOptConstParam::try_lookup(def_id, tcx) {
+                tcx.thir_abstract_const_of_const_arg(def)
+            } else {
+                thir_abstract_const(tcx, ty::WithOptConstParam::unknown(def_id))
+            }
+        },
+        thir_abstract_const_of_const_arg: |tcx, (did, param_did)| {
+            thir_abstract_const(
+                tcx,
+                ty::WithOptConstParam { did, const_param_did: Some(param_did) },
+            )
+        },
+        ..*providers
+    };
 }
diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs
index 5e58f237982..979e997f244 100644
--- a/compiler/rustc_ty_utils/src/instance.rs
+++ b/compiler/rustc_ty_utils/src/instance.rs
@@ -280,6 +280,11 @@ fn resolve_associated_item<'tcx>(
                 return Ok(None);
             }
 
+            // If the item does not have a value, then we cannot return an instance.
+            if !leaf_def.item.defaultness.has_value() {
+                return Ok(None);
+            }
+
             let substs = tcx.erase_regions(substs);
 
             // Check if we just resolved an associated `const` declaration from
diff --git a/compiler/rustc_ty_utils/src/lib.rs b/compiler/rustc_ty_utils/src/lib.rs
index 7624d31b40b..09f5c2a11aa 100644
--- a/compiler/rustc_ty_utils/src/lib.rs
+++ b/compiler/rustc_ty_utils/src/lib.rs
@@ -7,6 +7,8 @@
 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
 #![feature(control_flow_enum)]
 #![feature(let_else)]
+#![feature(never_type)]
+#![feature(box_patterns)]
 #![recursion_limit = "256"]
 
 #[macro_use]
diff --git a/compiler/rustc_ty_utils/src/representability.rs b/compiler/rustc_ty_utils/src/representability.rs
index 7efc82efd15..eded7891682 100644
--- a/compiler/rustc_ty_utils/src/representability.rs
+++ b/compiler/rustc_ty_utils/src/representability.rs
@@ -1,5 +1,5 @@
 //! Check whether a type is representable.
-use rustc_data_structures::stable_map::FxHashMap;
+use rustc_data_structures::fx::FxHashMap;
 use rustc_hir as hir;
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_span::Span;
diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs
index 1d345caf699..d542a9b5997 100644
--- a/compiler/rustc_ty_utils/src/ty.rs
+++ b/compiler/rustc_ty_utils/src/ty.rs
@@ -126,7 +126,7 @@ fn param_env(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> {
     // are any errors at that point, so outside of type inference you can be
     // sure that this will succeed without errors anyway.
 
-    if tcx.sess.opts.debugging_opts.chalk {
+    if tcx.sess.opts.unstable_opts.chalk {
         let environment = well_formed_types_in_env(tcx, def_id);
         predicates.extend(environment);
     }
diff --git a/compiler/rustc_type_ir/src/codec.rs b/compiler/rustc_type_ir/src/codec.rs
index 6a9ea790a30..ee249050cc6 100644
--- a/compiler/rustc_type_ir/src/codec.rs
+++ b/compiler/rustc_type_ir/src/codec.rs
@@ -1,6 +1,6 @@
 use crate::Interner;
 
-use rustc_data_structures::stable_map::FxHashMap;
+use rustc_data_structures::fx::FxHashMap;
 use rustc_serialize::{Decoder, Encoder};
 
 /// The shorthand encoding uses an enum's variant index `usize`
diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs
index fd6376ef6ee..791e9e0f5a3 100644
--- a/compiler/rustc_type_ir/src/lib.rs
+++ b/compiler/rustc_type_ir/src/lib.rs
@@ -310,6 +310,7 @@ impl DebruijnIndex {
     ///    for<'a> fn(for<'b> fn(&'a x))
     ///
     /// you would need to shift the index for `'a` into a new binder.
+    #[inline]
     #[must_use]
     pub fn shifted_in(self, amount: u32) -> DebruijnIndex {
         DebruijnIndex::from_u32(self.as_u32() + amount)
@@ -317,18 +318,21 @@ impl DebruijnIndex {
 
     /// Update this index in place by shifting it "in" through
     /// `amount` number of binders.
+    #[inline]
     pub fn shift_in(&mut self, amount: u32) {
         *self = self.shifted_in(amount);
     }
 
     /// Returns the resulting index when this value is moved out from
     /// `amount` number of new binders.
+    #[inline]
     #[must_use]
     pub fn shifted_out(self, amount: u32) -> DebruijnIndex {
         DebruijnIndex::from_u32(self.as_u32() - amount)
     }
 
     /// Update in place by shifting out from `amount` binders.
+    #[inline]
     pub fn shift_out(&mut self, amount: u32) {
         *self = self.shifted_out(amount);
     }
@@ -353,6 +357,7 @@ impl DebruijnIndex {
     /// If we invoke `shift_out_to_binder` and the region is in fact
     /// bound by one of the binders we are shifting out of, that is an
     /// error (and should fail an assertion failure).
+    #[inline]
     pub fn shifted_out_to_binder(self, to_binder: DebruijnIndex) -> Self {
         self.shifted_out(to_binder.as_u32() - INNERMOST.as_u32())
     }
diff --git a/compiler/rustc_typeck/src/astconv/errors.rs b/compiler/rustc_typeck/src/astconv/errors.rs
index c873cf27e42..99a8101dc96 100644
--- a/compiler/rustc_typeck/src/astconv/errors.rs
+++ b/compiler/rustc_typeck/src/astconv/errors.rs
@@ -8,7 +8,7 @@ use rustc_middle::ty;
 use rustc_session::parse::feature_err;
 use rustc_span::lev_distance::find_best_match_for_name;
 use rustc_span::symbol::{sym, Ident};
-use rustc_span::{Span, DUMMY_SP};
+use rustc_span::{Span, Symbol, DUMMY_SP};
 
 use std::collections::BTreeSet;
 
@@ -17,7 +17,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
     /// the type parameter's name as a placeholder.
     pub(crate) fn complain_about_missing_type_params(
         &self,
-        missing_type_params: Vec<String>,
+        missing_type_params: Vec<Symbol>,
         def_id: DefId,
         span: Span,
         empty_generic_args: bool,
diff --git a/compiler/rustc_typeck/src/astconv/generics.rs b/compiler/rustc_typeck/src/astconv/generics.rs
index 612dc384521..40aa27a29e9 100644
--- a/compiler/rustc_typeck/src/astconv/generics.rs
+++ b/compiler/rustc_typeck/src/astconv/generics.rs
@@ -86,7 +86,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                         let param_type = tcx.infer_ctxt().enter(|infcx| {
                             infcx.resolve_numeric_literals_with_default(tcx.type_of(param.def_id))
                         });
-                        if param_type.is_suggestable(tcx) {
+                        if param_type.is_suggestable(tcx, false) {
                             err.span_suggestion(
                                 tcx.def_span(src_def_id),
                                 "consider changing this type parameter to be a `const` generic",
diff --git a/compiler/rustc_typeck/src/astconv/mod.rs b/compiler/rustc_typeck/src/astconv/mod.rs
index 0a2b54eec47..08e8e6f7d0f 100644
--- a/compiler/rustc_typeck/src/astconv/mod.rs
+++ b/compiler/rustc_typeck/src/astconv/mod.rs
@@ -221,14 +221,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                 tcx.mk_region(ty::ReLateBound(debruijn, br))
             }
 
-            Some(rl::Region::LateBoundAnon(debruijn, index, anon_index)) => {
-                let br = ty::BoundRegion {
-                    var: ty::BoundVar::from_u32(index),
-                    kind: ty::BrAnon(anon_index),
-                };
-                tcx.mk_region(ty::ReLateBound(debruijn, br))
-            }
-
             Some(rl::Region::EarlyBound(index, id)) => {
                 let name = lifetime_name(id.expect_local());
                 tcx.mk_region(ty::ReEarlyBound(ty::EarlyBoundRegion { def_id: id, index, name }))
@@ -382,7 +374,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
             def_id: DefId,
             generic_args: &'a GenericArgs<'a>,
             span: Span,
-            missing_type_params: Vec<String>,
+            missing_type_params: Vec<Symbol>,
             inferred_params: Vec<Span>,
             infer_args: bool,
             is_object: bool,
@@ -439,7 +431,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                                 // as the rest of the type. As such, we ignore missing
                                 // stability attributes.
                             },
-                        )
+                        );
                     }
                     if let (hir::TyKind::Infer, false) = (&ty.kind, self.astconv.allow_ty_infer()) {
                         self.inferred_params.push(ty.span);
@@ -514,7 +506,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                             // defaults. This will lead to an ICE if we are not
                             // careful!
                             if self.default_needs_object_self(param) {
-                                self.missing_type_params.push(param.name.to_string());
+                                self.missing_type_params.push(param.name);
                                 tcx.ty_error().into()
                             } else {
                                 // This is a default type parameter.
@@ -550,7 +542,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                     GenericParamDefKind::Const { has_default } => {
                         let ty = tcx.at(self.span).type_of(param.def_id);
                         if !infer_args && has_default {
-                            EarlyBinder(tcx.const_param_default(param.def_id))
+                            tcx.bound_const_param_default(param.def_id)
                                 .subst(tcx, substs.unwrap())
                                 .into()
                         } else {
@@ -1150,17 +1142,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
             .expect("missing associated type");
 
         if !assoc_item.vis.is_accessible_from(def_scope, tcx) {
-            let kind = match assoc_item.kind {
-                ty::AssocKind::Type => "type",
-                ty::AssocKind::Const => "const",
-                _ => unreachable!(),
-            };
             tcx.sess
                 .struct_span_err(
                     binding.span,
-                    &format!("associated {kind} `{}` is private", binding.item_name),
+                    &format!("{} `{}` is private", assoc_item.kind, binding.item_name),
                 )
-                .span_label(binding.span, &format!("private associated {kind}"))
+                .span_label(binding.span, &format!("private {}", assoc_item.kind))
                 .emit();
         }
         tcx.check_stability(assoc_item.def_id, Some(hir_ref_id), binding.span, None);
@@ -1958,11 +1945,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                         );
                     }
 
-                    if adt_def.did().is_local() {
-                        err.span_label(
-                            tcx.def_span(adt_def.did()),
-                            format!("variant `{assoc_ident}` not found for this enum"),
-                        );
+                    if let Some(sp) = tcx.hir().span_if_local(adt_def.did()) {
+                        err.span_label(sp, format!("variant `{}` not found here", assoc_ident));
                     }
 
                     err.emit()
@@ -2489,7 +2473,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                                         concrete type's name `{type_name}` instead if you want to \
                                         specify its type parameters"
                                     ),
-                                    type_name.to_string(),
+                                    type_name,
                                     Applicability::MaybeIncorrect,
                                 );
                             }
@@ -2679,7 +2663,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                     span,
                     ty,
                     opt_sugg: Some((span, Applicability::MachineApplicable))
-                        .filter(|_| ty.is_suggestable(tcx)),
+                        .filter(|_| ty.is_suggestable(tcx, false)),
                 });
 
                 ty
diff --git a/compiler/rustc_typeck/src/check/_match.rs b/compiler/rustc_typeck/src/check/_match.rs
index 79e402b542a..147d87e7594 100644
--- a/compiler/rustc_typeck/src/check/_match.rs
+++ b/compiler/rustc_typeck/src/check/_match.rs
@@ -1,6 +1,6 @@
 use crate::check::coercion::{AsCoercionSite, CoerceMany};
 use crate::check::{Diverges, Expectation, FnCtxt, Needs};
-use rustc_errors::{Applicability, Diagnostic, MultiSpan};
+use rustc_errors::{Applicability, MultiSpan};
 use rustc_hir::{self as hir, ExprKind};
 use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
 use rustc_infer::traits::Obligation;
@@ -9,7 +9,6 @@ use rustc_span::Span;
 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
 use rustc_trait_selection::traits::{
     IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode,
-    StatementAsExpression,
 };
 
 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
@@ -75,8 +74,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         };
 
         let mut other_arms = vec![]; // Used only for diagnostics.
-        let mut prior_arm_ty = None;
-        for (i, arm) in arms.iter().enumerate() {
+        let mut prior_arm = None;
+        for arm in arms {
             if let Some(g) = &arm.guard {
                 self.diverges.set(Diverges::Maybe);
                 match g {
@@ -96,21 +95,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
             let opt_suggest_box_span = self.opt_suggest_box_span(arm_ty, orig_expected);
 
-            let (arm_span, semi_span) =
-                self.get_appropriate_arm_semicolon_removal_span(&arms, i, prior_arm_ty, arm_ty);
-            let (span, code) = match i {
+            let (arm_block_id, arm_span) = if let hir::ExprKind::Block(blk, _) = arm.body.kind {
+                (Some(blk.hir_id), self.find_block_span(blk))
+            } else {
+                (None, arm.body.span)
+            };
+
+            let (span, code) = match prior_arm {
                 // The reason for the first arm to fail is not that the match arms diverge,
                 // but rather that there's a prior obligation that doesn't hold.
-                0 => (arm_span, ObligationCauseCode::BlockTailExpression(arm.body.hir_id)),
-                _ => (
+                None => (arm_span, ObligationCauseCode::BlockTailExpression(arm.body.hir_id)),
+                Some((prior_arm_block_id, prior_arm_ty, prior_arm_span)) => (
                     expr.span,
                     ObligationCauseCode::MatchExpressionArm(Box::new(MatchExpressionArmCause {
+                        arm_block_id,
                         arm_span,
+                        arm_ty,
+                        prior_arm_block_id,
+                        prior_arm_ty,
+                        prior_arm_span,
                         scrut_span: scrut.span,
-                        semi_span,
                         source: match_src,
                         prior_arms: other_arms.clone(),
-                        last_ty: prior_arm_ty.unwrap(),
                         scrut_hir_id: scrut.hir_id,
                         opt_suggest_box_span,
                     })),
@@ -127,7 +133,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 &cause,
                 Some(&arm.body),
                 arm_ty,
-                Some(&mut |err: &mut Diagnostic| {
+                Some(&mut |err| {
                     let Some(ret) = self.ret_type_span else {
                         return;
                     };
@@ -139,7 +145,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             let ret_ty = ret_coercion.borrow().expected_ty();
                             let ret_ty = self.inh.infcx.shallow_resolve(ret_ty);
                             self.can_coerce(arm_ty, ret_ty)
-                                && prior_arm_ty.map_or(true, |t| self.can_coerce(t, ret_ty))
+                                && prior_arm.map_or(true, |(_, t, _)| self.can_coerce(t, ret_ty))
                                 // The match arms need to unify for the case of `impl Trait`.
                                 && !matches!(ret_ty.kind(), ty::Opaque(..))
                         }
@@ -181,7 +187,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             if other_arms.len() > 5 {
                 other_arms.remove(0);
             }
-            prior_arm_ty = Some(arm_ty);
+
+            prior_arm = Some((arm_block_id, arm_ty, arm_span));
         }
 
         // If all of the arms in the `match` diverge,
@@ -207,28 +214,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         match_ty
     }
 
-    fn get_appropriate_arm_semicolon_removal_span(
-        &self,
-        arms: &'tcx [hir::Arm<'tcx>],
-        i: usize,
-        prior_arm_ty: Option<Ty<'tcx>>,
-        arm_ty: Ty<'tcx>,
-    ) -> (Span, Option<(Span, StatementAsExpression)>) {
-        let arm = &arms[i];
-        let (arm_span, mut semi_span) = if let hir::ExprKind::Block(blk, _) = &arm.body.kind {
-            self.find_block_span(blk, prior_arm_ty)
-        } else {
-            (arm.body.span, None)
-        };
-        if semi_span.is_none() && i > 0 {
-            if let hir::ExprKind::Block(blk, _) = &arms[i - 1].body.kind {
-                let (_, semi_span_prev) = self.find_block_span(blk, Some(arm_ty));
-                semi_span = semi_span_prev;
-            }
-        }
-        (arm_span, semi_span)
-    }
-
     /// When the previously checked expression (the scrutinee) diverges,
     /// warn the user about the match arms being unreachable.
     fn warn_arms_when_scrutinee_diverges(&self, arms: &'tcx [hir::Arm<'tcx>]) {
@@ -259,7 +244,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             &cause,
             &mut |err| {
                 if let Some((span, msg)) = &ret_reason {
-                    err.span_label(*span, msg.as_str());
+                    err.span_label(*span, msg);
                 } else if let ExprKind::Block(block, _) = &then_expr.kind
                     && let Some(expr) = &block.expr
                 {
@@ -313,7 +298,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         else_ty: Ty<'tcx>,
         opt_suggest_box_span: Option<Span>,
     ) -> ObligationCause<'tcx> {
-        let mut outer_sp = if self.tcx.sess.source_map().is_multiline(span) {
+        let mut outer_span = if self.tcx.sess.source_map().is_multiline(span) {
             // The `if`/`else` isn't in one line in the output, include some context to make it
             // clear it is an if/else expression:
             // ```
@@ -339,69 +324,67 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             None
         };
 
-        let mut remove_semicolon = None;
-        let error_sp = if let ExprKind::Block(block, _) = &else_expr.kind {
-            let (error_sp, semi_sp) = self.find_block_span(block, Some(then_ty));
-            remove_semicolon = semi_sp;
-            if block.expr.is_none() && block.stmts.is_empty() {
-                // Avoid overlapping spans that aren't as readable:
-                // ```
-                // 2 |        let x = if true {
-                //   |   _____________-
-                // 3 |  |         3
-                //   |  |         - expected because of this
-                // 4 |  |     } else {
-                //   |  |____________^
-                // 5 | ||
-                // 6 | ||     };
-                //   | ||     ^
-                //   | ||_____|
-                //   | |______if and else have incompatible types
-                //   |        expected integer, found `()`
-                // ```
-                // by not pointing at the entire expression:
-                // ```
-                // 2 |       let x = if true {
-                //   |               ------- `if` and `else` have incompatible types
-                // 3 |           3
-                //   |           - expected because of this
-                // 4 |       } else {
-                //   |  ____________^
-                // 5 | |
-                // 6 | |     };
-                //   | |_____^ expected integer, found `()`
-                // ```
-                if outer_sp.is_some() {
-                    outer_sp = Some(self.tcx.sess.source_map().guess_head_span(span));
-                }
+        let (error_sp, else_id) = if let ExprKind::Block(block, _) = &else_expr.kind {
+            let block = block.innermost_block();
+
+            // Avoid overlapping spans that aren't as readable:
+            // ```
+            // 2 |        let x = if true {
+            //   |   _____________-
+            // 3 |  |         3
+            //   |  |         - expected because of this
+            // 4 |  |     } else {
+            //   |  |____________^
+            // 5 | ||
+            // 6 | ||     };
+            //   | ||     ^
+            //   | ||_____|
+            //   | |______if and else have incompatible types
+            //   |        expected integer, found `()`
+            // ```
+            // by not pointing at the entire expression:
+            // ```
+            // 2 |       let x = if true {
+            //   |               ------- `if` and `else` have incompatible types
+            // 3 |           3
+            //   |           - expected because of this
+            // 4 |       } else {
+            //   |  ____________^
+            // 5 | |
+            // 6 | |     };
+            //   | |_____^ expected integer, found `()`
+            // ```
+            if block.expr.is_none() && block.stmts.is_empty()
+                && let Some(outer_span) = &mut outer_span
+            {
+                *outer_span = self.tcx.sess.source_map().guess_head_span(*outer_span);
             }
-            error_sp
+
+            (self.find_block_span(block), block.hir_id)
         } else {
-            // shouldn't happen unless the parser has done something weird
-            else_expr.span
+            (else_expr.span, else_expr.hir_id)
         };
 
-        // Compute `Span` of `then` part of `if`-expression.
-        let then_sp = if let ExprKind::Block(block, _) = &then_expr.kind {
-            let (then_sp, semi_sp) = self.find_block_span(block, Some(else_ty));
-            remove_semicolon = remove_semicolon.or(semi_sp);
+        let then_id = if let ExprKind::Block(block, _) = &then_expr.kind {
+            let block = block.innermost_block();
+            // Exclude overlapping spans
             if block.expr.is_none() && block.stmts.is_empty() {
-                outer_sp = None; // same as in `error_sp`; cleanup output
+                outer_span = None;
             }
-            then_sp
+            block.hir_id
         } else {
-            // shouldn't happen unless the parser has done something weird
-            then_expr.span
+            then_expr.hir_id
         };
 
         // Finally construct the cause:
         self.cause(
             error_sp,
             ObligationCauseCode::IfExpression(Box::new(IfExpressionCause {
-                then: then_sp,
-                else_sp: error_sp,
-                outer: outer_sp,
-                semicolon: remove_semicolon,
+                else_id,
+                then_id,
+                then_ty,
+                else_ty,
+                outer_span,
                 opt_suggest_box_span,
             })),
         )
@@ -482,22 +465,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
     }
 
-    fn find_block_span(
-        &self,
-        block: &'tcx hir::Block<'tcx>,
-        expected_ty: Option<Ty<'tcx>>,
-    ) -> (Span, Option<(Span, StatementAsExpression)>) {
-        if let Some(expr) = &block.expr {
-            (expr.span, None)
-        } else if let Some(stmt) = block.stmts.last() {
-            // possibly incorrect trailing `;` in the else arm
-            (stmt.span, expected_ty.and_then(|ty| self.could_remove_semicolon(block, ty)))
-        } else {
-            // empty block; point at its entirety
-            (block.span, None)
-        }
-    }
-
     // When we have a `match` as a tail expression in a `fn` with a returned `impl Trait`
     // we check if the different arms would work with boxed trait objects instead and
     // provide a structured suggestion in that case.
@@ -521,7 +488,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                 ty::Binder::dummy(ty::PredicateKind::Trait(ty::TraitPredicate {
                                     trait_ref: ty::TraitRef {
                                         def_id: t.def_id(),
-                                        substs: self.infcx.tcx.mk_substs_trait(outer_ty, &[]),
+                                        substs: self.tcx.mk_substs_trait(outer_ty, &[]),
                                     },
                                     constness: t.constness,
                                     polarity: t.polarity,
@@ -529,9 +496,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             let obl = Obligation::new(
                                 o.cause.clone(),
                                 self.param_env,
-                                pred.to_predicate(self.infcx.tcx),
+                                pred.to_predicate(self.tcx),
                             );
-                            suggest_box &= self.infcx.predicate_must_hold_modulo_regions(&obl);
+                            suggest_box &= self.predicate_must_hold_modulo_regions(&obl);
                             if !suggest_box {
                                 // We've encountered some obligation that didn't hold, so the
                                 // return expression can't just be boxed. We don't need to
diff --git a/compiler/rustc_typeck/src/check/callee.rs b/compiler/rustc_typeck/src/check/callee.rs
index 3af73abe5ce..0836f15a122 100644
--- a/compiler/rustc_typeck/src/check/callee.rs
+++ b/compiler/rustc_typeck/src/check/callee.rs
@@ -4,7 +4,7 @@ use crate::type_error_struct;
 
 use rustc_errors::{struct_span_err, Applicability, Diagnostic};
 use rustc_hir as hir;
-use rustc_hir::def::{Namespace, Res};
+use rustc_hir::def::{self, Namespace, Res};
 use rustc_hir::def_id::{DefId, LOCAL_CRATE};
 use rustc_infer::{
     infer,
@@ -285,29 +285,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let parent_node = hir.get(parent_hir_id);
         if let (
             hir::Node::Expr(hir::Expr {
-                kind: hir::ExprKind::Closure { fn_decl_span, body, .. },
+                kind: hir::ExprKind::Closure(&hir::Closure { fn_decl_span, body, .. }),
                 ..
             }),
             hir::ExprKind::Block(..),
         ) = (parent_node, callee_node)
         {
-            let fn_decl_span = if hir.body(*body).generator_kind
+            let fn_decl_span = if hir.body(body).generator_kind
                 == Some(hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Closure))
             {
                 // Actually need to unwrap a few more layers of HIR to get to
                 // the _real_ closure...
                 let async_closure = hir.get_parent_node(hir.get_parent_node(parent_hir_id));
                 if let hir::Node::Expr(hir::Expr {
-                    kind: hir::ExprKind::Closure { fn_decl_span, .. },
+                    kind: hir::ExprKind::Closure(&hir::Closure { fn_decl_span, .. }),
                     ..
                 }) = hir.get(async_closure)
                 {
-                    *fn_decl_span
+                    fn_decl_span
                 } else {
                     return;
                 }
             } else {
-                *fn_decl_span
+                fn_decl_span
             };
 
             let start = fn_decl_span.shrink_to_lo();
@@ -376,7 +376,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             self.param_env,
                             *predicate,
                         );
-                        let result = self.infcx.evaluate_obligation(&obligation);
+                        let result = self.evaluate_obligation(&obligation);
                         self.tcx
                             .sess
                             .struct_span_err(
@@ -390,17 +390,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 (fn_sig, Some(def_id))
             }
             ty::FnPtr(sig) => (sig, None),
-            ref t => {
+            _ => {
                 let mut unit_variant = None;
-                let mut removal_span = call_expr.span;
-                if let ty::Adt(adt_def, ..) = t
-                    && adt_def.is_enum()
-                    && let hir::ExprKind::Call(expr, _) = call_expr.kind
+                if let hir::ExprKind::Path(qpath) = &callee_expr.kind
+                    && let Res::Def(def::DefKind::Ctor(kind, def::CtorKind::Const), _)
+                        = self.typeck_results.borrow().qpath_res(qpath, callee_expr.hir_id)
+                    // Only suggest removing parens if there are no arguments
+                    && arg_exprs.is_empty()
                 {
-                    removal_span =
-                        expr.span.shrink_to_hi().to(call_expr.span.shrink_to_hi());
+                    let descr = match kind {
+                        def::CtorOf::Struct => "struct",
+                        def::CtorOf::Variant => "enum variant",
+                    };
+                    let removal_span =
+                        callee_expr.span.shrink_to_hi().to(call_expr.span.shrink_to_hi());
                     unit_variant =
-                        self.tcx.sess.source_map().span_to_snippet(expr.span).ok();
+                        Some((removal_span, descr, rustc_hir_pretty::qpath_to_string(qpath)));
                 }
 
                 let callee_ty = self.resolve_vars_if_possible(callee_ty);
@@ -410,8 +415,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     callee_ty,
                     E0618,
                     "expected function, found {}",
-                    match unit_variant {
-                        Some(ref path) => format!("enum variant `{path}`"),
+                    match &unit_variant {
+                        Some((_, kind, path)) => format!("{kind} `{path}`"),
                         None => format!("`{callee_ty}`"),
                     }
                 );
@@ -423,11 +428,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     callee_expr.span,
                 );
 
-                if let Some(ref path) = unit_variant {
+                if let Some((removal_span, kind, path)) = &unit_variant {
                     err.span_suggestion_verbose(
-                        removal_span,
+                        *removal_span,
                         &format!(
-                            "`{path}` is a unit variant, you need to write it without the parentheses",
+                            "`{path}` is a unit {kind}, and does not take parentheses to be constructed",
                         ),
                         "",
                         Applicability::MachineApplicable,
@@ -470,7 +475,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 if let Some(span) = self.tcx.hir().res_span(def) {
                     let callee_ty = callee_ty.to_string();
                     let label = match (unit_variant, inner_callee_path) {
-                        (Some(path), _) => Some(format!("`{path}` defined here")),
+                        (Some((_, kind, path)), _) => Some(format!("{kind} `{path}` defined here")),
                         (_, Some(hir::QPath::Resolved(_, path))) => self
                             .tcx
                             .sess
diff --git a/compiler/rustc_typeck/src/check/cast.rs b/compiler/rustc_typeck/src/check/cast.rs
index 66dd5582490..7aaddc2bd7a 100644
--- a/compiler/rustc_typeck/src/check/cast.rs
+++ b/compiler/rustc_typeck/src/check/cast.rs
@@ -69,7 +69,7 @@ enum PointerKind<'tcx> {
     /// No metadata attached, ie pointer to sized type or foreign type
     Thin,
     /// A trait object
-    Vtable(Option<DefId>),
+    VTable(Option<DefId>),
     /// Slice
     Length,
     /// The unsize info of this projection
@@ -102,7 +102,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         Ok(match *t.kind() {
             ty::Slice(_) | ty::Str => Some(PointerKind::Length),
-            ty::Dynamic(ref tty, ..) => Some(PointerKind::Vtable(tty.principal_def_id())),
+            ty::Dynamic(ref tty, ..) => Some(PointerKind::VTable(tty.principal_def_id())),
             ty::Adt(def, substs) if def.is_struct() => match def.non_enum_variant().fields.last() {
                 None => Some(PointerKind::Thin),
                 Some(f) => {
@@ -951,7 +951,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
         match fcx.pointer_kind(m_cast.ty, self.span)? {
             None => Err(CastError::UnknownCastPtrKind),
             Some(PointerKind::Thin) => Ok(CastKind::AddrPtrCast),
-            Some(PointerKind::Vtable(_)) => Err(CastError::IntToFatCast(Some("a vtable"))),
+            Some(PointerKind::VTable(_)) => Err(CastError::IntToFatCast(Some("a vtable"))),
             Some(PointerKind::Length) => Err(CastError::IntToFatCast(Some("a length"))),
             Some(
                 PointerKind::OfProjection(_)
diff --git a/compiler/rustc_typeck/src/check/check.rs b/compiler/rustc_typeck/src/check/check.rs
index e9709b64d93..9497d5c4528 100644
--- a/compiler/rustc_typeck/src/check/check.rs
+++ b/compiler/rustc_typeck/src/check/check.rs
@@ -1,10 +1,9 @@
-use crate::check::wfcheck::for_item;
+use crate::check::intrinsicck::InlineAsmCtxt;
 
 use super::coercion::CoerceMany;
 use super::compare_method::check_type_bounds;
 use super::compare_method::{compare_const_impl, compare_impl_method, compare_ty_impl};
 use super::*;
-
 use rustc_attr as attr;
 use rustc_errors::{Applicability, ErrorGuaranteed, MultiSpan};
 use rustc_hir as hir;
@@ -15,8 +14,9 @@ use rustc_hir::lang_items::LangItem;
 use rustc_hir::{ItemKind, Node, PathSegment};
 use rustc_infer::infer::outlives::env::OutlivesEnvironment;
 use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
-use rustc_infer::infer::{RegionVariableOrigin, TyCtxtInferExt};
+use rustc_infer::infer::{DefiningAnchor, RegionVariableOrigin, TyCtxtInferExt};
 use rustc_infer::traits::Obligation;
+use rustc_lint::builtin::REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS;
 use rustc_middle::hir::nested_filter;
 use rustc_middle::ty::layout::{LayoutError, MAX_SIMD_LANES};
 use rustc_middle::ty::subst::GenericArgKind;
@@ -28,8 +28,8 @@ use rustc_session::lint::builtin::{UNINHABITED_STATIC, UNSUPPORTED_CALLING_CONVE
 use rustc_span::symbol::sym;
 use rustc_span::{self, Span};
 use rustc_target::spec::abi::Abi;
-use rustc_trait_selection::traits;
 use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
+use rustc_trait_selection::traits::{self, ObligationCtxt};
 use rustc_ty_utils::representability::{self, Representability};
 
 use std::iter;
@@ -374,8 +374,9 @@ fn check_alloc_error_fn(
     }
 }
 
-fn check_struct(tcx: TyCtxt<'_>, def_id: LocalDefId, span: Span) {
+fn check_struct(tcx: TyCtxt<'_>, def_id: LocalDefId) {
     let def = tcx.adt_def(def_id);
+    let span = tcx.def_span(def_id);
     def.destructor(tcx); // force the destructor to be evaluated
     check_representable(tcx, span, def_id);
 
@@ -387,8 +388,9 @@ fn check_struct(tcx: TyCtxt<'_>, def_id: LocalDefId, span: Span) {
     check_packed(tcx, span, def);
 }
 
-fn check_union(tcx: TyCtxt<'_>, def_id: LocalDefId, span: Span) {
+fn check_union(tcx: TyCtxt<'_>, def_id: LocalDefId) {
     let def = tcx.adt_def(def_id);
+    let span = tcx.def_span(def_id);
     def.destructor(tcx); // force the destructor to be evaluated
     check_representable(tcx, span, def_id);
     check_transparent(tcx, span, def);
@@ -401,11 +403,37 @@ fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> b
     let item_type = tcx.type_of(item_def_id);
     if let ty::Adt(def, substs) = item_type.kind() {
         assert!(def.is_union());
-        let fields = &def.non_enum_variant().fields;
+
+        fn allowed_union_field<'tcx>(
+            ty: Ty<'tcx>,
+            tcx: TyCtxt<'tcx>,
+            param_env: ty::ParamEnv<'tcx>,
+            span: Span,
+        ) -> bool {
+            // We don't just accept all !needs_drop fields, due to semver concerns.
+            match ty.kind() {
+                ty::Ref(..) => true, // references never drop (even mutable refs, which are non-Copy and hence fail the later check)
+                ty::Tuple(tys) => {
+                    // allow tuples of allowed types
+                    tys.iter().all(|ty| allowed_union_field(ty, tcx, param_env, span))
+                }
+                ty::Array(elem, _len) => {
+                    // Like `Copy`, we do *not* special-case length 0.
+                    allowed_union_field(*elem, tcx, param_env, span)
+                }
+                _ => {
+                    // Fallback case: allow `ManuallyDrop` and things that are `Copy`.
+                    ty.ty_adt_def().is_some_and(|adt_def| adt_def.is_manually_drop())
+                        || ty.is_copy_modulo_regions(tcx.at(span), param_env)
+                }
+            }
+        }
+
         let param_env = tcx.param_env(item_def_id);
-        for field in fields {
+        for field in &def.non_enum_variant().fields {
             let field_ty = field.ty(tcx, substs);
-            if field_ty.needs_drop(tcx, param_env) {
+
+            if !allowed_union_field(field_ty, tcx, param_env, span) {
                 let (field_span, ty_span) = match tcx.hir().get_if_local(field.did) {
                     // We are currently checking the type this field came from, so it must be local.
                     Some(Node::Field(field)) => (field.span, field.ty.span),
@@ -432,6 +460,9 @@ fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> b
                 )
                 .emit();
                 return false;
+            } else if field_ty.needs_drop(tcx, param_env) {
+                // This should never happen. But we can get here e.g. in case of name resolution errors.
+                tcx.sess.delay_span_bug(span, "we should never accept maybe-dropping union fields");
             }
         }
     } else {
@@ -441,13 +472,14 @@ fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> b
 }
 
 /// Check that a `static` is inhabited.
-fn check_static_inhabited<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, span: Span) {
+fn check_static_inhabited<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) {
     // Make sure statics are inhabited.
     // Other parts of the compiler assume that there are no uninhabited places. In principle it
     // would be enough to check this for `extern` statics, as statics with an initializer will
     // have UB during initialization if they are uninhabited, but there also seems to be no good
     // reason to allow any statics to be uninhabited.
     let ty = tcx.type_of(def_id);
+    let span = tcx.def_span(def_id);
     let layout = match tcx.layout_of(ParamEnv::reveal_all().and(ty)) {
         Ok(l) => l,
         // Foreign statics that overflow their allowed size should emit an error
@@ -494,9 +526,9 @@ pub(super) fn check_opaque<'tcx>(
     tcx: TyCtxt<'tcx>,
     def_id: LocalDefId,
     substs: SubstsRef<'tcx>,
-    span: Span,
     origin: &hir::OpaqueTyOrigin,
 ) {
+    let span = tcx.def_span(def_id);
     check_opaque_for_inheriting_lifetimes(tcx, def_id, span);
     if tcx.type_of(def_id).references_error() {
         return;
@@ -699,50 +731,52 @@ fn check_opaque_meets_bounds<'tcx>(
     };
     let param_env = tcx.param_env(defining_use_anchor);
 
-    tcx.infer_ctxt().with_opaque_type_inference(defining_use_anchor).enter(move |infcx| {
-        let inh = Inherited::new(infcx, def_id);
-        let infcx = &inh.infcx;
-        let opaque_ty = tcx.mk_opaque(def_id.to_def_id(), substs);
+    tcx.infer_ctxt().with_opaque_type_inference(DefiningAnchor::Bind(defining_use_anchor)).enter(
+        move |infcx| {
+            let ocx = ObligationCtxt::new(&infcx);
+            let opaque_ty = tcx.mk_opaque(def_id.to_def_id(), substs);
 
-        let misc_cause = traits::ObligationCause::misc(span, hir_id);
+            let misc_cause = traits::ObligationCause::misc(span, hir_id);
 
-        match infcx.at(&misc_cause, param_env).eq(opaque_ty, hidden_type) {
-            Ok(infer_ok) => inh.register_infer_ok_obligations(infer_ok),
-            Err(ty_err) => {
-                tcx.sess.delay_span_bug(
-                    span,
-                    &format!("could not unify `{hidden_type}` with revealed type:\n{ty_err}"),
-                );
+            match infcx.at(&misc_cause, param_env).eq(opaque_ty, hidden_type) {
+                Ok(infer_ok) => ocx.register_infer_ok_obligations(infer_ok),
+                Err(ty_err) => {
+                    tcx.sess.delay_span_bug(
+                        span,
+                        &format!("could not unify `{hidden_type}` with revealed type:\n{ty_err}"),
+                    );
+                }
             }
-        }
 
-        // Additionally require the hidden type to be well-formed with only the generics of the opaque type.
-        // Defining use functions may have more bounds than the opaque type, which is ok, as long as the
-        // hidden type is well formed even without those bounds.
-        let predicate =
-            ty::Binder::dummy(ty::PredicateKind::WellFormed(hidden_type.into())).to_predicate(tcx);
-        inh.register_predicate(Obligation::new(misc_cause, param_env, predicate));
-
-        // Check that all obligations are satisfied by the implementation's
-        // version.
-        let errors = inh.fulfillment_cx.borrow_mut().select_all_or_error(&infcx);
-        if !errors.is_empty() {
-            infcx.report_fulfillment_errors(&errors, None, false);
-        }
-
-        match origin {
-            // Checked when type checking the function containing them.
-            hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) => {}
-            // Can have different predicates to their defining use
-            hir::OpaqueTyOrigin::TyAlias => {
-                let outlives_environment = OutlivesEnvironment::new(param_env);
-                infcx.check_region_obligations_and_report_errors(&outlives_environment);
+            // Additionally require the hidden type to be well-formed with only the generics of the opaque type.
+            // Defining use functions may have more bounds than the opaque type, which is ok, as long as the
+            // hidden type is well formed even without those bounds.
+            let predicate = ty::Binder::dummy(ty::PredicateKind::WellFormed(hidden_type.into()))
+                .to_predicate(tcx);
+            ocx.register_obligation(Obligation::new(misc_cause, param_env, predicate));
+
+            // Check that all obligations are satisfied by the implementation's
+            // version.
+            let errors = ocx.select_all_or_error();
+            if !errors.is_empty() {
+                infcx.report_fulfillment_errors(&errors, None, false);
             }
-        }
-
-        // Clean up after ourselves
-        let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types();
-    });
+            match origin {
+                // Checked when type checking the function containing them.
+                hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) => {}
+                // Can have different predicates to their defining use
+                hir::OpaqueTyOrigin::TyAlias => {
+                    let outlives_environment = OutlivesEnvironment::new(param_env);
+                    infcx.check_region_obligations_and_report_errors(
+                        defining_use_anchor,
+                        &outlives_environment,
+                    );
+                }
+            }
+            // Clean up after ourselves
+            let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types();
+        },
+    );
 }
 
 fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, id: hir::ItemId) {
@@ -755,8 +789,8 @@ fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, id: hir::ItemId) {
     match tcx.def_kind(id.def_id) {
         DefKind::Static(..) => {
             tcx.ensure().typeck(id.def_id);
-            maybe_check_static_with_link_section(tcx, id.def_id, tcx.def_span(id.def_id));
-            check_static_inhabited(tcx, id.def_id, tcx.def_span(id.def_id));
+            maybe_check_static_with_link_section(tcx, id.def_id);
+            check_static_inhabited(tcx, id.def_id);
         }
         DefKind::Const => {
             tcx.ensure().typeck(id.def_id);
@@ -766,7 +800,7 @@ fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, id: hir::ItemId) {
             let hir::ItemKind::Enum(ref enum_definition, _) = item.kind else {
                 return;
             };
-            check_enum(tcx, item.span, &enum_definition.variants, item.def_id);
+            check_enum(tcx, &enum_definition.variants, item.def_id);
         }
         DefKind::Fn => {} // entirely within check_item_body
         DefKind::Impl => {
@@ -817,10 +851,10 @@ fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, id: hir::ItemId) {
             }
         }
         DefKind::Struct => {
-            check_struct(tcx, id.def_id, tcx.def_span(id.def_id));
+            check_struct(tcx, id.def_id);
         }
         DefKind::Union => {
-            check_union(tcx, id.def_id, tcx.def_span(id.def_id));
+            check_union(tcx, id.def_id);
         }
         DefKind::OpaqueTy => {
             let item = tcx.hir().item(id);
@@ -833,7 +867,7 @@ fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, id: hir::ItemId) {
             // See https://github.com/rust-lang/rust/issues/75100
             if !tcx.sess.opts.actually_rustdoc {
                 let substs = InternalSubsts::identity_for_item(tcx, item.def_id.to_def_id());
-                check_opaque(tcx, item.def_id, substs, item.span, &origin);
+                check_opaque(tcx, item.def_id, substs, &origin);
             }
         }
         DefKind::TyAlias => {
@@ -897,7 +931,7 @@ fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, id: hir::ItemId) {
                             require_c_abi_if_c_variadic(tcx, fn_decl, abi, item.span);
                         }
                         hir::ForeignItemKind::Static(..) => {
-                            check_static_inhabited(tcx, def_id, item.span);
+                            check_static_inhabited(tcx, def_id);
                         }
                         _ => {}
                     }
@@ -907,10 +941,7 @@ fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, id: hir::ItemId) {
         DefKind::GlobalAsm => {
             let it = tcx.hir().item(id);
             let hir::ItemKind::GlobalAsm(asm) = it.kind else { span_bug!(it.span, "DefKind::GlobalAsm but got {:#?}", it) };
-            for_item(tcx, it).with_fcx(|fcx| {
-                fcx.check_asm(asm, it.hir_id());
-                Default::default()
-            })
+            InlineAsmCtxt::new_global_asm(tcx).check_asm(asm, id.hir_id());
         }
         _ => {}
     }
@@ -1298,7 +1329,6 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, sp: Span, adt: ty::AdtD
     if !adt.repr().transparent() {
         return;
     }
-    let sp = tcx.sess.source_map().guess_head_span(sp);
 
     if adt.is_union() && !tcx.features().transparent_unions {
         feature_err(
@@ -1318,7 +1348,8 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, sp: Span, adt: ty::AdtD
         }
     }
 
-    // For each field, figure out if it's known to be a ZST and align(1)
+    // For each field, figure out if it's known to be a ZST and align(1), with "known"
+    // respecting #[non_exhaustive] attributes.
     let field_infos = adt.all_fields().map(|field| {
         let ty = field.ty(tcx, InternalSubsts::identity_for_item(tcx, field.did));
         let param_env = tcx.param_env(field.did);
@@ -1327,16 +1358,56 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, sp: Span, adt: ty::AdtD
         let span = tcx.hir().span_if_local(field.did).unwrap();
         let zst = layout.map_or(false, |layout| layout.is_zst());
         let align1 = layout.map_or(false, |layout| layout.align.abi.bytes() == 1);
-        (span, zst, align1)
+        if !zst {
+            return (span, zst, align1, None);
+        }
+
+        fn check_non_exhaustive<'tcx>(
+            tcx: TyCtxt<'tcx>,
+            t: Ty<'tcx>,
+        ) -> ControlFlow<(&'static str, DefId, SubstsRef<'tcx>, bool)> {
+            match t.kind() {
+                ty::Tuple(list) => list.iter().try_for_each(|t| check_non_exhaustive(tcx, t)),
+                ty::Array(ty, _) => check_non_exhaustive(tcx, *ty),
+                ty::Adt(def, subst) => {
+                    if !def.did().is_local() {
+                        let non_exhaustive = def.is_variant_list_non_exhaustive()
+                            || def
+                                .variants()
+                                .iter()
+                                .any(ty::VariantDef::is_field_list_non_exhaustive);
+                        let has_priv = def.all_fields().any(|f| !f.vis.is_public());
+                        if non_exhaustive || has_priv {
+                            return ControlFlow::Break((
+                                def.descr(),
+                                def.did(),
+                                subst,
+                                non_exhaustive,
+                            ));
+                        }
+                    }
+                    def.all_fields()
+                        .map(|field| field.ty(tcx, subst))
+                        .try_for_each(|t| check_non_exhaustive(tcx, t))
+                }
+                _ => ControlFlow::Continue(()),
+            }
+        }
+
+        (span, zst, align1, check_non_exhaustive(tcx, ty).break_value())
     });
 
-    let non_zst_fields =
-        field_infos.clone().filter_map(|(span, zst, _align1)| if !zst { Some(span) } else { None });
+    let non_zst_fields = field_infos
+        .clone()
+        .filter_map(|(span, zst, _align1, _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, sp);
     }
-    for (span, zst, align1) in field_infos {
+    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!(
                 tcx.sess,
@@ -1348,17 +1419,32 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, sp: Span, adt: ty::AdtD
             .span_label(span, "has alignment larger than 1")
             .emit();
         }
+        if incompat && let Some((descr, def_id, substs, non_exhaustive)) = non_exhaustive {
+            tcx.struct_span_lint_hir(
+                REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS,
+                tcx.hir().local_def_id_to_hir_id(adt.did().expect_local()),
+                span,
+                |lint| {
+                    let note = if non_exhaustive {
+                        "is marked with `#[non_exhaustive]`"
+                    } else {
+                        "contains private fields"
+                    };
+                    let field_ty = tcx.def_path_str_with_substs(def_id, substs);
+                    lint.build("zero-sized fields in repr(transparent) cannot contain external non-exhaustive types")
+                        .note(format!("this {descr} contains `{field_ty}`, which {note}, \
+                            and makes it not a breaking change to become non-zero-sized in the future."))
+                        .emit();
+                },
+            )
+        }
     }
 }
 
 #[allow(trivial_numeric_casts)]
-fn check_enum<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    sp: Span,
-    vs: &'tcx [hir::Variant<'tcx>],
-    def_id: LocalDefId,
-) {
+fn check_enum<'tcx>(tcx: TyCtxt<'tcx>, vs: &'tcx [hir::Variant<'tcx>], def_id: LocalDefId) {
     let def = tcx.adt_def(def_id);
+    let sp = tcx.def_span(def_id);
     def.destructor(tcx); // force the destructor to be evaluated
 
     if vs.is_empty() {
diff --git a/compiler/rustc_typeck/src/check/closure.rs b/compiler/rustc_typeck/src/check/closure.rs
index 1681e6af812..fee872155f5 100644
--- a/compiler/rustc_typeck/src/check/closure.rs
+++ b/compiler/rustc_typeck/src/check/closure.rs
@@ -96,7 +96,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             self.tcx.typeck_root_def_id(expr_def_id.to_def_id()),
         );
 
-        let tupled_upvars_ty = self.infcx.next_ty_var(TypeVariableOrigin {
+        let tupled_upvars_ty = self.next_ty_var(TypeVariableOrigin {
             kind: TypeVariableOriginKind::ClosureSynthetic,
             span: self.tcx.hir().span(expr.hir_id),
         });
@@ -141,7 +141,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
             // Create a type variable (for now) to represent the closure kind.
             // It will be unified during the upvar inference phase (`upvar.rs`)
-            None => self.infcx.next_ty_var(TypeVariableOrigin {
+            None => self.next_ty_var(TypeVariableOrigin {
                 // FIXME(eddyb) distinguish closure kind inference variables from the rest.
                 kind: TypeVariableOriginKind::ClosureSynthetic,
                 span: expr.span,
@@ -182,9 +182,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         ty::PredicateKind::Projection(proj_predicate) => self
                             .deduce_sig_from_projection(
                                 Some(span.0),
-                                pred.0.kind().rebind(
-                                    pred.map_bound(|_| proj_predicate).subst(self.tcx, substs),
-                                ),
+                                pred.0
+                                    .kind()
+                                    .rebind(pred.rebind(proj_predicate).subst(self.tcx, substs)),
                             ),
                         _ => None,
                     });
@@ -531,7 +531,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         //
         // [c1]: https://github.com/rust-lang/rust/pull/45072#issuecomment-341089706
         // [c2]: https://github.com/rust-lang/rust/pull/45072#issuecomment-341096796
-        self.infcx.commit_if_ok(|_| {
+        self.commit_if_ok(|_| {
             let mut all_obligations = vec![];
 
             // The liberated version of this signature should be a subtype
@@ -544,7 +544,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 expected_sigs.liberated_sig.inputs(), // `liberated_sig` is E'.
             ) {
                 // Instantiate (this part of..) S to S', i.e., with fresh variables.
-                let supplied_ty = self.infcx.replace_bound_vars_with_fresh_vars(
+                let supplied_ty = self.replace_bound_vars_with_fresh_vars(
                     hir_ty.span,
                     LateBoundRegionConversionTime::FnCall,
                     supplied_sig.inputs().rebind(supplied_ty),
@@ -557,7 +557,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 all_obligations.extend(obligations);
             }
 
-            let supplied_output_ty = self.infcx.replace_bound_vars_with_fresh_vars(
+            let supplied_output_ty = self.replace_bound_vars_with_fresh_vars(
                 decl.output.span(),
                 LateBoundRegionConversionTime::FnCall,
                 supplied_sig.output(),
diff --git a/compiler/rustc_typeck/src/check/coercion.rs b/compiler/rustc_typeck/src/check/coercion.rs
index acd7e4a92dc..639cab98f17 100644
--- a/compiler/rustc_typeck/src/check/coercion.rs
+++ b/compiler/rustc_typeck/src/check/coercion.rs
@@ -241,13 +241,13 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
         make_adjustments: impl FnOnce(Ty<'tcx>) -> Vec<Adjustment<'tcx>>,
     ) -> CoerceResult<'tcx> {
         debug!("coerce_from_inference_variable(a={:?}, b={:?})", a, b);
-        assert!(a.is_ty_var() && self.infcx.shallow_resolve(a) == a);
-        assert!(self.infcx.shallow_resolve(b) == b);
+        assert!(a.is_ty_var() && self.shallow_resolve(a) == a);
+        assert!(self.shallow_resolve(b) == b);
 
         if b.is_ty_var() {
             // Two unresolved type variables: create a `Coerce` predicate.
             let target_ty = if self.use_lub {
-                self.infcx.next_ty_var(TypeVariableOrigin {
+                self.next_ty_var(TypeVariableOrigin {
                     kind: TypeVariableOriginKind::LatticeVariable,
                     span: self.cause.span,
                 })
@@ -991,7 +991,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         self.autoderef(rustc_span::DUMMY_SP, expr_ty).nth(1).and_then(|(deref_ty, _)| {
             self.infcx
                 .type_implements_trait(
-                    self.infcx.tcx.lang_items().deref_mut_trait()?,
+                    self.tcx.lang_items().deref_mut_trait()?,
                     expr_ty,
                     ty::List::empty(),
                     self.param_env,
@@ -1577,8 +1577,8 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
         let parent_id = fcx.tcx.hir().get_parent_node(id);
         let parent = fcx.tcx.hir().get(parent_id);
         if let Some(expr) = expression
-            && let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Closure { body, .. }, .. }) = parent
-            && !matches!(fcx.tcx.hir().body(*body).value.kind, hir::ExprKind::Block(..))
+            && let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(&hir::Closure { body, .. }), .. }) = parent
+            && !matches!(fcx.tcx.hir().body(body).value.kind, hir::ExprKind::Block(..))
         {
             fcx.suggest_missing_semicolon(&mut err, expr, expected, true);
         }
diff --git a/compiler/rustc_typeck/src/check/compare_method.rs b/compiler/rustc_typeck/src/check/compare_method.rs
index 2bfb9343877..af77efc3c2d 100644
--- a/compiler/rustc_typeck/src/check/compare_method.rs
+++ b/compiler/rustc_typeck/src/check/compare_method.rs
@@ -1,13 +1,15 @@
+use super::potentially_plural_count;
 use crate::check::regionck::OutlivesEnvironmentExt;
+use crate::check::wfcheck;
 use crate::errors::LifetimesOrBoundsMismatchOnTrait;
-use rustc_data_structures::stable_set::FxHashSet;
+use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticId, ErrorGuaranteed};
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::intravisit;
 use rustc_hir::{GenericParamKind, ImplItemKind, TraitItemKind};
 use rustc_infer::infer::outlives::env::OutlivesEnvironment;
-use rustc_infer::infer::{self, InferOk, TyCtxtInferExt};
+use rustc_infer::infer::{self, TyCtxtInferExt};
 use rustc_infer::traits::util;
 use rustc_middle::ty::error::{ExpectedFound, TypeError};
 use rustc_middle::ty::subst::{InternalSubsts, Subst};
@@ -16,11 +18,11 @@ use rustc_middle::ty::{self, DefIdTree};
 use rustc_middle::ty::{GenericParamDefKind, ToPredicate, TyCtxt};
 use rustc_span::Span;
 use rustc_trait_selection::traits::error_reporting::InferCtxtExt;
-use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode, Reveal};
+use rustc_trait_selection::traits::{
+    self, ObligationCause, ObligationCauseCode, ObligationCtxt, Reveal,
+};
 use std::iter;
 
-use super::{potentially_plural_count, FnCtxt, Inherited};
-
 /// Checks that a method from an impl conforms to the signature of
 /// the same method as declared in the trait.
 ///
@@ -88,9 +90,10 @@ fn compare_predicate_entailment<'tcx>(
     let mut cause = ObligationCause::new(
         impl_m_span,
         impl_m_hir_id,
-        ObligationCauseCode::CompareImplMethodObligation {
+        ObligationCauseCode::CompareImplItemObligation {
             impl_item_def_id: impl_m.def_id.expect_local(),
             trait_item_def_id: trait_m.def_id,
+            kind: impl_m.kind,
         },
     );
 
@@ -171,14 +174,7 @@ fn compare_predicate_entailment<'tcx>(
     let trait_m_predicates = tcx.predicates_of(trait_m.def_id);
 
     // Check region bounds.
-    check_region_bounds_on_impl_item(
-        tcx,
-        impl_m_span,
-        impl_m,
-        trait_m,
-        &trait_m_generics,
-        &impl_m_generics,
-    )?;
+    check_region_bounds_on_impl_item(tcx, impl_m, trait_m, &trait_m_generics, &impl_m_generics)?;
 
     // Create obligations for each predicate declared by the impl
     // definition in the context of the trait's parameter
@@ -212,30 +208,29 @@ fn compare_predicate_entailment<'tcx>(
     );
     let param_env = traits::normalize_param_env_or_error(tcx, param_env, normalize_cause);
 
-    tcx.infer_ctxt().enter(|infcx| {
-        let inh = Inherited::new(infcx, impl_m.def_id.expect_local());
-        let infcx = &inh.infcx;
+    tcx.infer_ctxt().enter(|ref infcx| {
+        let ocx = ObligationCtxt::new(infcx);
 
         debug!("compare_impl_method: caller_bounds={:?}", param_env.caller_bounds());
 
         let mut selcx = traits::SelectionContext::new(&infcx);
-
         let impl_m_own_bounds = impl_m_predicates.instantiate_own(tcx, impl_to_placeholder_substs);
         for (predicate, span) in iter::zip(impl_m_own_bounds.predicates, impl_m_own_bounds.spans) {
             let normalize_cause = traits::ObligationCause::misc(span, impl_m_hir_id);
             let traits::Normalized { value: predicate, obligations } =
                 traits::normalize(&mut selcx, param_env, normalize_cause, predicate);
 
-            inh.register_predicates(obligations);
+            ocx.register_obligations(obligations);
             let cause = ObligationCause::new(
                 span,
                 impl_m_hir_id,
-                ObligationCauseCode::CompareImplMethodObligation {
+                ObligationCauseCode::CompareImplItemObligation {
                     impl_item_def_id: impl_m.def_id.expect_local(),
                     trait_item_def_id: trait_m.def_id,
+                    kind: impl_m.kind,
                 },
             );
-            inh.register_predicate(traits::Obligation::new(cause, param_env, predicate));
+            ocx.register_obligation(traits::Obligation::new(cause, param_env, predicate));
         }
 
         // We now need to check that the signature of the impl method is
@@ -261,32 +256,31 @@ fn compare_predicate_entailment<'tcx>(
             infer::HigherRankedType,
             tcx.fn_sig(impl_m.def_id),
         );
-        let impl_sig =
-            inh.normalize_associated_types_in(impl_m_span, impl_m_hir_id, param_env, impl_sig);
+
+        let norm_cause = ObligationCause::misc(impl_m_span, impl_m_hir_id);
+        let impl_sig = ocx.normalize(norm_cause.clone(), param_env, impl_sig);
         let impl_fty = tcx.mk_fn_ptr(ty::Binder::dummy(impl_sig));
         debug!("compare_impl_method: impl_fty={:?}", impl_fty);
 
         let trait_sig = tcx.bound_fn_sig(trait_m.def_id).subst(tcx, trait_to_placeholder_substs);
         let trait_sig = tcx.liberate_late_bound_regions(impl_m.def_id, trait_sig);
-        let trait_sig =
-            inh.normalize_associated_types_in(impl_m_span, impl_m_hir_id, param_env, trait_sig);
+        let trait_sig = ocx.normalize(norm_cause, param_env, trait_sig);
         // Add the resulting inputs and output as well-formed.
         wf_tys.extend(trait_sig.inputs_and_output.iter());
         let trait_fty = tcx.mk_fn_ptr(ty::Binder::dummy(trait_sig));
 
         debug!("compare_impl_method: trait_fty={:?}", trait_fty);
 
-        let sub_result = infcx.at(&cause, param_env).sup(trait_fty, impl_fty).map(
-            |InferOk { obligations, .. }| {
-                // FIXME: We'd want to keep more accurate spans than "the method signature" when
-                // processing the comparison between the trait and impl fn, but we sadly lose them
-                // and point at the whole signature when a trait bound or specific input or output
-                // type would be more appropriate. In other places we have a `Vec<Span>`
-                // corresponding to their `Vec<Predicate>`, but we don't have that here.
-                // Fixing this would improve the output of test `issue-83765.rs`.
-                inh.register_predicates(obligations);
-            },
-        );
+        // FIXME: We'd want to keep more accurate spans than "the method signature" when
+        // processing the comparison between the trait and impl fn, but we sadly lose them
+        // and point at the whole signature when a trait bound or specific input or output
+        // type would be more appropriate. In other places we have a `Vec<Span>`
+        // corresponding to their `Vec<Predicate>`, but we don't have that here.
+        // Fixing this would improve the output of test `issue-83765.rs`.
+        let sub_result = infcx
+            .at(&cause, param_env)
+            .sup(trait_fty, impl_fty)
+            .map(|infer_ok| ocx.register_infer_ok_obligations(infer_ok));
 
         if let Err(terr) = sub_result {
             debug!("sub_types failed: impl ty {:?}, trait ty {:?}", impl_fty, trait_fty);
@@ -298,7 +292,7 @@ fn compare_predicate_entailment<'tcx>(
 
             let mut diag = struct_span_err!(
                 tcx.sess,
-                cause.span(tcx),
+                cause.span(),
                 E0053,
                 "method `{}` has an incompatible type for trait",
                 trait_m.name
@@ -392,7 +386,7 @@ fn compare_predicate_entailment<'tcx>(
 
         // Check that all obligations are satisfied by the implementation's
         // version.
-        let errors = inh.fulfillment_cx.borrow_mut().select_all_or_error(&infcx);
+        let errors = ocx.select_all_or_error();
         if !errors.is_empty() {
             let reported = infcx.report_fulfillment_errors(&errors, None, false);
             return Err(reported);
@@ -402,7 +396,10 @@ fn compare_predicate_entailment<'tcx>(
         // lifetime parameters.
         let mut outlives_environment = OutlivesEnvironment::new(param_env);
         outlives_environment.add_implied_bounds(infcx, wf_tys, impl_m_hir_id);
-        infcx.check_region_obligations_and_report_errors(&outlives_environment);
+        infcx.check_region_obligations_and_report_errors(
+            impl_m.def_id.expect_local(),
+            &outlives_environment,
+        );
 
         Ok(())
     })
@@ -410,7 +407,6 @@ fn compare_predicate_entailment<'tcx>(
 
 fn check_region_bounds_on_impl_item<'tcx>(
     tcx: TyCtxt<'tcx>,
-    span: Span,
     impl_m: &ty::AssocItem,
     trait_m: &ty::AssocItem,
     trait_generics: &ty::Generics,
@@ -436,21 +432,25 @@ fn check_region_bounds_on_impl_item<'tcx>(
     // are zero. Since I don't quite know how to phrase things at
     // the moment, give a kind of vague error message.
     if trait_params != impl_params {
-        let item_kind = assoc_item_kind_str(impl_m);
-        let def_span = tcx.sess.source_map().guess_head_span(span);
-        let span = impl_m
-            .def_id
-            .as_local()
-            .and_then(|did| tcx.hir().get_generics(did))
-            .map_or(def_span, |g| g.span);
-        let generics_span = trait_m.def_id.as_local().map(|did| {
-            let def_sp = tcx.def_span(did);
-            tcx.hir().get_generics(did).map_or(def_sp, |g| g.span)
-        });
+        let span = tcx
+            .hir()
+            .get_generics(impl_m.def_id.expect_local())
+            .expect("expected impl item to have generics or else we can't compare them")
+            .span;
+        let generics_span = if let Some(local_def_id) = trait_m.def_id.as_local() {
+            Some(
+                tcx.hir()
+                    .get_generics(local_def_id)
+                    .expect("expected trait item to have generics or else we can't compare them")
+                    .span,
+            )
+        } else {
+            None
+        };
 
         let reported = tcx.sess.emit_err(LifetimesOrBoundsMismatchOnTrait {
             span,
-            item_kind,
+            item_kind: assoc_item_kind_str(impl_m),
             ident: impl_m.ident(tcx),
             generics_span,
         });
@@ -490,7 +490,7 @@ fn extract_spans_for_error_reporting<'a, 'tcx>(
         TypeError::ArgumentSorts(ExpectedFound { .. }, i) => {
             (impl_args.nth(i).unwrap(), trait_args.and_then(|mut args| args.nth(i)))
         }
-        _ => (cause.span(tcx), tcx.hir().span_if_local(trait_m.def_id)),
+        _ => (cause.span(), tcx.hir().span_if_local(trait_m.def_id)),
     }
 }
 
@@ -546,7 +546,7 @@ fn compare_self_type<'tcx>(
             if let Some(span) = tcx.hir().span_if_local(trait_m.def_id) {
                 err.span_label(span, format!("trait method declared without `{self_descr}`"));
             } else {
-                err.note_trait_signature(trait_m.name.to_string(), trait_m.signature(tcx));
+                err.note_trait_signature(trait_m.name, trait_m.signature(tcx));
             }
             let reported = err.emit();
             return Err(reported);
@@ -566,7 +566,7 @@ fn compare_self_type<'tcx>(
             if let Some(span) = tcx.hir().span_if_local(trait_m.def_id) {
                 err.span_label(span, format!("`{self_descr}` used in trait"));
             } else {
-                err.note_trait_signature(trait_m.name.to_string(), trait_m.signature(tcx));
+                err.note_trait_signature(trait_m.name, trait_m.signature(tcx));
             }
             let reported = err.emit();
             return Err(reported);
@@ -805,7 +805,7 @@ fn compare_number_of_method_arguments<'tcx>(
                 ),
             );
         } else {
-            err.note_trait_signature(trait_m.name.to_string(), trait_m.signature(tcx));
+            err.note_trait_signature(trait_m.name, trait_m.signature(tcx));
         }
         err.span_label(
             impl_span,
@@ -1062,8 +1062,7 @@ pub(crate) fn compare_const_impl<'tcx>(
 
     tcx.infer_ctxt().enter(|infcx| {
         let param_env = tcx.param_env(impl_c.def_id);
-        let inh = Inherited::new(infcx, impl_c.def_id.expect_local());
-        let infcx = &inh.infcx;
+        let ocx = ObligationCtxt::new(&infcx);
 
         // The below is for the most part highly similar to the procedure
         // for methods above. It is simpler in many respects, especially
@@ -1082,24 +1081,26 @@ pub(crate) fn compare_const_impl<'tcx>(
         let mut cause = ObligationCause::new(
             impl_c_span,
             impl_c_hir_id,
-            ObligationCauseCode::CompareImplConstObligation,
+            ObligationCauseCode::CompareImplItemObligation {
+                impl_item_def_id: impl_c.def_id.expect_local(),
+                trait_item_def_id: trait_c.def_id,
+                kind: impl_c.kind,
+            },
         );
 
         // There is no "body" here, so just pass dummy id.
-        let impl_ty =
-            inh.normalize_associated_types_in(impl_c_span, impl_c_hir_id, param_env, impl_ty);
+        let impl_ty = ocx.normalize(cause.clone(), param_env, impl_ty);
 
         debug!("compare_const_impl: impl_ty={:?}", impl_ty);
 
-        let trait_ty =
-            inh.normalize_associated_types_in(impl_c_span, impl_c_hir_id, param_env, trait_ty);
+        let trait_ty = ocx.normalize(cause.clone(), param_env, trait_ty);
 
         debug!("compare_const_impl: trait_ty={:?}", trait_ty);
 
         let err = infcx
             .at(&cause, param_env)
             .sup(trait_ty, impl_ty)
-            .map(|ok| inh.register_infer_ok_obligations(ok));
+            .map(|ok| ocx.register_infer_ok_obligations(ok));
 
         if let Err(terr) = err {
             debug!(
@@ -1146,14 +1147,15 @@ pub(crate) fn compare_const_impl<'tcx>(
 
         // Check that all obligations are satisfied by the implementation's
         // version.
-        let errors = inh.fulfillment_cx.borrow_mut().select_all_or_error(&infcx);
+        let errors = ocx.select_all_or_error();
         if !errors.is_empty() {
             infcx.report_fulfillment_errors(&errors, None, false);
             return;
         }
 
         let outlives_environment = OutlivesEnvironment::new(param_env);
-        infcx.resolve_regions_and_report_errors(&outlives_environment);
+        infcx
+            .resolve_regions_and_report_errors(impl_c.def_id.expect_local(), &outlives_environment);
     });
 }
 
@@ -1199,7 +1201,6 @@ fn compare_type_predicate_entailment<'tcx>(
 
     check_region_bounds_on_impl_item(
         tcx,
-        impl_ty_span,
         impl_ty,
         trait_ty,
         &trait_ty_generics,
@@ -1217,15 +1218,6 @@ fn compare_type_predicate_entailment<'tcx>(
     // `ObligationCause` (and the `FnCtxt`). This is what
     // `regionck_item` expects.
     let impl_ty_hir_id = tcx.hir().local_def_id_to_hir_id(impl_ty.def_id.expect_local());
-    let cause = ObligationCause::new(
-        impl_ty_span,
-        impl_ty_hir_id,
-        ObligationCauseCode::CompareImplTypeObligation {
-            impl_item_def_id: impl_ty.def_id.expect_local(),
-            trait_item_def_id: trait_ty.def_id,
-        },
-    );
-
     debug!("compare_type_predicate_entailment: trait_to_impl_substs={:?}", trait_to_impl_substs);
 
     // The predicates declared by the impl definition, the trait and the
@@ -1244,26 +1236,38 @@ fn compare_type_predicate_entailment<'tcx>(
         Reveal::UserFacing,
         hir::Constness::NotConst,
     );
-    let param_env = traits::normalize_param_env_or_error(tcx, param_env, normalize_cause.clone());
+    let param_env = traits::normalize_param_env_or_error(tcx, param_env, normalize_cause);
     tcx.infer_ctxt().enter(|infcx| {
-        let inh = Inherited::new(infcx, impl_ty.def_id.expect_local());
-        let infcx = &inh.infcx;
+        let ocx = ObligationCtxt::new(&infcx);
 
         debug!("compare_type_predicate_entailment: caller_bounds={:?}", param_env.caller_bounds());
 
         let mut selcx = traits::SelectionContext::new(&infcx);
 
-        for predicate in impl_ty_own_bounds.predicates {
+        assert_eq!(impl_ty_own_bounds.predicates.len(), impl_ty_own_bounds.spans.len());
+        for (span, predicate) in
+            std::iter::zip(impl_ty_own_bounds.spans, impl_ty_own_bounds.predicates)
+        {
+            let cause = ObligationCause::misc(span, impl_ty_hir_id);
             let traits::Normalized { value: predicate, obligations } =
-                traits::normalize(&mut selcx, param_env, normalize_cause.clone(), predicate);
+                traits::normalize(&mut selcx, param_env, cause, predicate);
 
-            inh.register_predicates(obligations);
-            inh.register_predicate(traits::Obligation::new(cause.clone(), param_env, predicate));
+            let cause = ObligationCause::new(
+                span,
+                impl_ty_hir_id,
+                ObligationCauseCode::CompareImplItemObligation {
+                    impl_item_def_id: impl_ty.def_id.expect_local(),
+                    trait_item_def_id: trait_ty.def_id,
+                    kind: impl_ty.kind,
+                },
+            );
+            ocx.register_obligations(obligations);
+            ocx.register_obligation(traits::Obligation::new(cause, param_env, predicate));
         }
 
         // Check that all obligations are satisfied by the implementation's
         // version.
-        let errors = inh.fulfillment_cx.borrow_mut().select_all_or_error(&infcx);
+        let errors = ocx.select_all_or_error();
         if !errors.is_empty() {
             let reported = infcx.report_fulfillment_errors(&errors, None, false);
             return Err(reported);
@@ -1272,7 +1276,10 @@ fn compare_type_predicate_entailment<'tcx>(
         // Finally, resolve all regions. This catches wily misuses of
         // lifetime parameters.
         let outlives_environment = OutlivesEnvironment::new(param_env);
-        infcx.check_region_obligations_and_report_errors(&outlives_environment);
+        infcx.check_region_obligations_and_report_errors(
+            impl_ty.def_id.expect_local(),
+            &outlives_environment,
+        );
 
         Ok(())
     })
@@ -1436,10 +1443,9 @@ pub fn check_type_bounds<'tcx>(
         impl_ty_substs.rebase_onto(tcx, impl_ty.container.id(), impl_trait_ref.substs);
 
     tcx.infer_ctxt().enter(move |infcx| {
-        let inh = Inherited::new(infcx, impl_ty.def_id.expect_local());
-        let infcx = &inh.infcx;
-        let mut selcx = traits::SelectionContext::new(&infcx);
+        let ocx = ObligationCtxt::new(&infcx);
 
+        let mut selcx = traits::SelectionContext::new(&infcx);
         let impl_ty_hir_id = tcx.hir().local_def_id_to_hir_id(impl_ty.def_id.expect_local());
         let normalize_cause = ObligationCause::new(
             impl_ty_span,
@@ -1464,6 +1470,7 @@ pub fn check_type_bounds<'tcx>(
             .map(|e| e.map_bound(|e| *e).transpose_tuple2())
             .map(|(bound, span)| {
                 debug!(?bound);
+                // this is where opaque type is found
                 let concrete_ty_bound = bound.subst(tcx, rebased_substs);
                 debug!("check_type_bounds: concrete_ty_bound = {:?}", concrete_ty_bound);
 
@@ -1482,13 +1489,12 @@ pub fn check_type_bounds<'tcx>(
             debug!("compare_projection_bounds: normalized predicate = {:?}", normalized_predicate);
             obligation.predicate = normalized_predicate;
 
-            inh.register_predicates(obligations);
-            inh.register_predicate(obligation);
+            ocx.register_obligations(obligations);
+            ocx.register_obligation(obligation);
         }
-
         // Check that all obligations are satisfied by the implementation's
         // version.
-        let errors = inh.fulfillment_cx.borrow_mut().select_all_or_error(&infcx);
+        let errors = ocx.select_all_or_error();
         if !errors.is_empty() {
             let reported = infcx.report_fulfillment_errors(&errors, None, false);
             return Err(reported);
@@ -1496,16 +1502,33 @@ pub fn check_type_bounds<'tcx>(
 
         // Finally, resolve all regions. This catches wily misuses of
         // lifetime parameters.
-        //
-        // FIXME: Remove that `FnCtxt`.
-        let fcx = FnCtxt::new(&inh, param_env, impl_ty_hir_id);
         let implied_bounds = match impl_ty.container {
             ty::TraitContainer(_) => FxHashSet::default(),
-            ty::ImplContainer(def_id) => fcx.impl_implied_bounds(def_id, impl_ty_span),
+            ty::ImplContainer(def_id) => {
+                wfcheck::impl_implied_bounds(tcx, param_env, def_id.expect_local(), impl_ty_span)
+            }
         };
         let mut outlives_environment = OutlivesEnvironment::new(param_env);
-        outlives_environment.add_implied_bounds(infcx, implied_bounds, impl_ty_hir_id);
-        infcx.check_region_obligations_and_report_errors(&outlives_environment);
+        outlives_environment.add_implied_bounds(&infcx, implied_bounds, impl_ty_hir_id);
+        infcx.check_region_obligations_and_report_errors(
+            impl_ty.def_id.expect_local(),
+            &outlives_environment,
+        );
+
+        let constraints = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types();
+        for (key, value) in constraints {
+            infcx
+                .report_mismatched_types(
+                    &ObligationCause::misc(
+                        value.hidden_type.span,
+                        tcx.hir().local_def_id_to_hir_id(impl_ty.def_id.expect_local()),
+                    ),
+                    tcx.mk_opaque(key.def_id.to_def_id(), key.substs),
+                    value.hidden_type.ty,
+                    TypeError::Mismatch,
+                )
+                .emit();
+        }
 
         Ok(())
     })
diff --git a/compiler/rustc_typeck/src/check/demand.rs b/compiler/rustc_typeck/src/check/demand.rs
index dc553d1441e..58b0399c5c9 100644
--- a/compiler/rustc_typeck/src/check/demand.rs
+++ b/compiler/rustc_typeck/src/check/demand.rs
@@ -33,6 +33,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         self.annotate_expected_due_to_let_ty(err, expr, error);
         self.suggest_deref_ref_or_into(err, expr, expected, expr_ty, expected_ty_expr);
         self.suggest_compatible_variants(err, expr, expected, expr_ty);
+        self.suggest_non_zero_new_unwrap(err, expr, expected, expr_ty);
         if self.suggest_calling_boxed_future_when_appropriate(err, expr, expected, expr_ty) {
             return;
         }
@@ -286,6 +287,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         expr_ty: Ty<'tcx>,
     ) {
         if let ty::Adt(expected_adt, substs) = expected.kind() {
+            if let hir::ExprKind::Field(base, ident) = expr.kind {
+                let base_ty = self.typeck_results.borrow().expr_ty(base);
+                if self.can_eq(self.param_env, base_ty, expected).is_ok()
+                    && let Some(base_span) = base.span.find_ancestor_inside(expr.span)
+                {
+                    err.span_suggestion_verbose(
+                        expr.span.with_lo(base_span.hi()),
+                        format!("consider removing the tuple struct field `{ident}`"),
+                        "",
+                        Applicability::MaybeIncorrect,
+                    );
+                    return
+                }
+            }
+
             // If the expression is of type () and it's the return expression of a block,
             // we suggest adding a separate return expression instead.
             // (To avoid things like suggesting `Ok(while .. { .. })`.)
@@ -347,7 +363,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 }
             }
 
-            let compatible_variants: Vec<String> = expected_adt
+            let compatible_variants: Vec<(String, Option<String>)> = expected_adt
                 .variants()
                 .iter()
                 .filter(|variant| {
@@ -355,6 +371,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 })
                 .filter_map(|variant| {
                     let sole_field = &variant.fields[0];
+
+                    let field_is_local = sole_field.did.is_local();
+                    let field_is_accessible =
+                        sole_field.vis.is_accessible_from(expr.hir_id.owner.to_def_id(), self.tcx);
+
+                    if !field_is_local && !field_is_accessible {
+                        return None;
+                    }
+
+                    let note_about_variant_field_privacy = (field_is_local && !field_is_accessible)
+                        .then(|| format!(" (its field is private, but it's local to this crate and its privacy can be changed)"));
+
                     let sole_field_ty = sole_field.ty(self.tcx, substs);
                     if self.can_coerce(expr_ty, sole_field_ty) {
                         let variant_path =
@@ -363,9 +391,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         if let Some(path) = variant_path.strip_prefix("std::prelude::")
                             && let Some((_, path)) = path.split_once("::")
                         {
-                            return Some(path.to_string());
+                            return Some((path.to_string(), note_about_variant_field_privacy));
                         }
-                        Some(variant_path)
+                        Some((variant_path, note_about_variant_field_privacy))
                     } else {
                         None
                     }
@@ -379,10 +407,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
             match &compatible_variants[..] {
                 [] => { /* No variants to format */ }
-                [variant] => {
+                [(variant, note)] => {
                     // Just a single matching variant.
                     err.multipart_suggestion_verbose(
-                        &format!("try wrapping the expression in `{variant}`"),
+                        &format!(
+                            "try wrapping the expression in `{variant}`{note}",
+                            note = note.as_deref().unwrap_or("")
+                        ),
                         vec![
                             (expr.span.shrink_to_lo(), format!("{prefix}{variant}(")),
                             (expr.span.shrink_to_hi(), ")".to_string()),
@@ -397,7 +428,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             "try wrapping the expression in a variant of `{}`",
                             self.tcx.def_path_str(expected_adt.did())
                         ),
-                        compatible_variants.into_iter().map(|variant| {
+                        compatible_variants.into_iter().map(|(variant, _)| {
                             vec![
                                 (expr.span.shrink_to_lo(), format!("{prefix}{variant}(")),
                                 (expr.span.shrink_to_hi(), ")".to_string()),
@@ -410,6 +441,57 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
     }
 
+    fn suggest_non_zero_new_unwrap(
+        &self,
+        err: &mut Diagnostic,
+        expr: &hir::Expr<'_>,
+        expected: Ty<'tcx>,
+        expr_ty: Ty<'tcx>,
+    ) {
+        let tcx = self.tcx;
+        let (adt, unwrap) = match expected.kind() {
+            // In case Option<NonZero*> is wanted, but * is provided, suggest calling new
+            ty::Adt(adt, substs) if tcx.is_diagnostic_item(sym::Option, adt.did()) => {
+                // Unwrap option
+                let ty::Adt(adt, _) = substs.type_at(0).kind() else { return };
+
+                (adt, "")
+            }
+            // In case NonZero* is wanted, but * is provided also add `.unwrap()` to satisfy types
+            ty::Adt(adt, _) => (adt, ".unwrap()"),
+            _ => return,
+        };
+
+        let map = [
+            (sym::NonZeroU8, tcx.types.u8),
+            (sym::NonZeroU16, tcx.types.u16),
+            (sym::NonZeroU32, tcx.types.u32),
+            (sym::NonZeroU64, tcx.types.u64),
+            (sym::NonZeroU128, tcx.types.u128),
+            (sym::NonZeroI8, tcx.types.i8),
+            (sym::NonZeroI16, tcx.types.i16),
+            (sym::NonZeroI32, tcx.types.i32),
+            (sym::NonZeroI64, tcx.types.i64),
+            (sym::NonZeroI128, tcx.types.i128),
+        ];
+
+        let Some((s, _)) = map
+            .iter()
+            .find(|&&(s, t)| self.tcx.is_diagnostic_item(s, adt.did()) && self.can_coerce(expr_ty, t))
+            else { return };
+
+        let path = self.tcx.def_path_str(adt.non_enum_variant().def_id);
+
+        err.multipart_suggestion(
+            format!("consider calling `{s}::new`"),
+            vec![
+                (expr.span.shrink_to_lo(), format!("{path}::new(")),
+                (expr.span.shrink_to_hi(), format!("){unwrap}")),
+            ],
+            Applicability::MaybeIncorrect,
+        );
+    }
+
     pub fn get_conversion_methods(
         &self,
         span: Span,
@@ -483,7 +565,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let param_parent = self.tcx.hir().get_parent_node(*param_hir_id);
         let Some(Node::Expr(hir::Expr {
             hir_id: expr_hir_id,
-            kind: hir::ExprKind::Closure { fn_decl: closure_fn_decl, .. },
+            kind: hir::ExprKind::Closure(hir::Closure { fn_decl: closure_fn_decl, .. }),
             ..
         })) = self.tcx.hir().find(param_parent) else {
             return None;
@@ -748,7 +830,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, ref expr),
                 _,
                 &ty::Ref(_, checked, _),
-            ) if self.infcx.can_sub(self.param_env, checked, expected).is_ok() => {
+            ) if self.can_sub(self.param_env, checked, expected).is_ok() => {
                 // We have `&T`, check if what was expected was `T`. If so,
                 // we may want to suggest removing a `&`.
                 if sm.is_imported(expr.span) {
@@ -892,7 +974,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
                     // For this suggestion to make sense, the type would need to be `Copy`,
                     // or we have to be moving out of a `Box<T>`
-                    if self.infcx.type_is_copy_modulo_regions(self.param_env, expected, sp)
+                    if self.type_is_copy_modulo_regions(self.param_env, expected, sp)
                         // FIXME(compiler-errors): We can actually do this if the checked_ty is
                         // `steps` layers of boxes, not just one, but this is easier and most likely.
                         || (checked_ty.is_box() && steps == 1)
diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs
index 58c01a34cad..ba5ef5edc86 100644
--- a/compiler/rustc_typeck/src/check/expr.rs
+++ b/compiler/rustc_typeck/src/check/expr.rs
@@ -35,7 +35,7 @@ use rustc_hir::def::{CtorKind, DefKind, Res};
 use rustc_hir::def_id::DefId;
 use rustc_hir::intravisit::Visitor;
 use rustc_hir::lang_items::LangItem;
-use rustc_hir::{ExprKind, HirId, QPath};
+use rustc_hir::{Closure, ExprKind, HirId, QPath};
 use rustc_infer::infer;
 use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
 use rustc_infer::infer::InferOk;
@@ -48,7 +48,7 @@ use rustc_middle::ty::{self, AdtKind, DefIdTree, Ty, TypeVisitable};
 use rustc_session::parse::feature_err;
 use rustc_span::hygiene::DesugaringKind;
 use rustc_span::lev_distance::find_best_match_for_name;
-use rustc_span::source_map::Span;
+use rustc_span::source_map::{Span, Spanned};
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use rustc_span::{BytePos, Pos};
 use rustc_target::spec::abi::Abi::RustIntrinsic;
@@ -282,11 +282,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         match expr.kind {
             ExprKind::Box(subexpr) => self.check_expr_box(subexpr, expected),
             ExprKind::Lit(ref lit) => self.check_lit(&lit, expected),
-            ExprKind::Binary(op, lhs, rhs) => self.check_binop(expr, op, lhs, rhs),
+            ExprKind::Binary(op, lhs, rhs) => self.check_binop(expr, op, lhs, rhs, expected),
             ExprKind::Assign(lhs, rhs, span) => {
                 self.check_expr_assign(expr, expected, lhs, rhs, span)
             }
-            ExprKind::AssignOp(op, lhs, rhs) => self.check_binop_assign(expr, op, lhs, rhs),
+            ExprKind::AssignOp(op, lhs, rhs) => {
+                self.check_binop_assign(expr, op, lhs, rhs, expected)
+            }
             ExprKind::Unary(unop, oprnd) => self.check_expr_unary(unop, oprnd, expected, expr),
             ExprKind::AddrOf(kind, mutbl, oprnd) => {
                 self.check_expr_addr_of(kind, mutbl, oprnd, expected, expr)
@@ -319,7 +321,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             ExprKind::Match(discrim, arms, match_src) => {
                 self.check_match(expr, &discrim, arms, expected, match_src)
             }
-            ExprKind::Closure { capture_clause, fn_decl, body, movability, .. } => {
+            ExprKind::Closure(&Closure { capture_clause, fn_decl, body, movability, .. }) => {
                 self.check_expr_closure(expr, capture_clause, &fn_decl, body, movability, expected)
             }
             ExprKind::Block(body, _) => self.check_block_with_expected(&body, expected),
@@ -404,14 +406,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     }
                 }
                 hir::UnOp::Not => {
-                    let result = self.check_user_unop(expr, oprnd_t, unop);
+                    let result = self.check_user_unop(expr, oprnd_t, unop, expected_inner);
                     // If it's builtin, we can reuse the type, this helps inference.
                     if !(oprnd_t.is_integral() || *oprnd_t.kind() == ty::Bool) {
                         oprnd_t = result;
                     }
                 }
                 hir::UnOp::Neg => {
-                    let result = self.check_user_unop(expr, oprnd_t, unop);
+                    let result = self.check_user_unop(expr, oprnd_t, unop, expected_inner);
                     // If it's builtin, we can reuse the type, this helps inference.
                     if !oprnd_t.is_numeric() {
                         oprnd_t = result;
@@ -529,7 +531,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 tcx.ty_error()
             }
             Res::Def(DefKind::Ctor(_, CtorKind::Fictive), _) => {
-                report_unexpected_variant_res(tcx, res, expr.span);
+                report_unexpected_variant_res(tcx, res, qpath, expr.span);
                 tcx.ty_error()
             }
             _ => self.instantiate_value_path(segs, opt_ty, res, expr.span, expr.hir_id).0,
@@ -997,26 +999,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         coerce.coerce(self, &self.misc(sp), then_expr, then_ty);
 
         if let Some(else_expr) = opt_else_expr {
-            let else_ty = if sp.desugaring_kind() == Some(DesugaringKind::LetElse) {
-                // todo introduce `check_expr_with_expectation(.., Expectation::LetElse)`
-                //   for errors that point to the offending expression rather than the entire block.
-                //   We could use `check_expr_eq_type(.., tcx.types.never)`, but then there is no
-                //   way to detect that the expected type originated from let-else and provide
-                //   a customized error.
-                let else_ty = self.check_expr(else_expr);
-                let cause = self.cause(else_expr.span, ObligationCauseCode::LetElse);
-
-                if let Some(mut err) =
-                    self.demand_eqtype_with_origin(&cause, self.tcx.types.never, else_ty)
-                {
-                    err.emit();
-                    self.tcx.ty_error()
-                } else {
-                    else_ty
-                }
-            } else {
-                self.check_expr_with_expectation(else_expr, expected)
-            };
+            let else_ty = self.check_expr_with_expectation(else_expr, expected);
             let else_diverges = self.diverges.get();
 
             let opt_suggest_box_span = self.opt_suggest_box_span(else_ty, orig_expected);
@@ -1820,7 +1803,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 .source_map()
                 .span_to_snippet(range_end.expr.span)
                 .map(|s| format!(" from `{s}`"))
-                .unwrap_or(String::new());
+                .unwrap_or_default();
             err.span_suggestion(
                 range_start.span.shrink_to_hi(),
                 &format!("to set the remaining fields{instead}, separate the last named field with a comma"),
@@ -1873,7 +1856,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             let remaining_private_fields_len = remaining_private_fields.len();
             let names = match &remaining_private_fields
                 .iter()
-                .map(|(name, _, _)| name.to_string())
+                .map(|(name, _, _)| name)
                 .collect::<Vec<_>>()[..]
             {
                 _ if remaining_private_fields_len > 6 => String::new(),
@@ -2162,14 +2145,55 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         } else if !expr_t.is_primitive_ty() {
             self.ban_nonexisting_field(field, base, expr, expr_t);
         } else {
-            type_error_struct!(
+            let field_name = field.to_string();
+            let mut err = type_error_struct!(
                 self.tcx().sess,
                 field.span,
                 expr_t,
                 E0610,
                 "`{expr_t}` is a primitive type and therefore doesn't have fields",
-            )
-            .emit();
+            );
+            let is_valid_suffix = |field: String| {
+                if field == "f32" || field == "f64" {
+                    return true;
+                }
+                let mut chars = field.chars().peekable();
+                match chars.peek() {
+                    Some('e') | Some('E') => {
+                        chars.next();
+                        if let Some(c) = chars.peek()
+                            && !c.is_numeric() && *c != '-' && *c != '+'
+                        {
+                            return false;
+                        }
+                        while let Some(c) = chars.peek() {
+                            if !c.is_numeric() {
+                                break;
+                            }
+                            chars.next();
+                        }
+                    }
+                    _ => (),
+                }
+                let suffix = chars.collect::<String>();
+                suffix.is_empty() || suffix == "f32" || suffix == "f64"
+            };
+            if let ty::Infer(ty::IntVar(_)) = expr_t.kind()
+                && let ExprKind::Lit(Spanned {
+                    node: ast::LitKind::Int(_, ast::LitIntType::Unsuffixed),
+                    ..
+                }) = base.kind
+                && !base.span.from_expansion()
+                && is_valid_suffix(field_name)
+            {
+                err.span_suggestion_verbose(
+                    field.span.shrink_to_lo(),
+                    "If the number is meant to be a floating point number, consider adding a `0` after the period",
+                    '0',
+                    Applicability::MaybeIncorrect,
+                );
+            }
+            err.emit();
         }
 
         self.tcx().ty_error()
@@ -2211,7 +2235,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         base: &'tcx hir::Expr<'tcx>,
         ty: Ty<'tcx>,
     ) {
-        let output_ty = match self.infcx.get_impl_future_output_ty(ty) {
+        let output_ty = match self.get_impl_future_output_ty(ty) {
             Some(output_ty) => self.resolve_vars_if_possible(output_ty),
             _ => return,
         };
@@ -2338,7 +2362,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 false
             };
         let expr_snippet =
-            self.tcx.sess.source_map().span_to_snippet(expr.span).unwrap_or(String::new());
+            self.tcx.sess.source_map().span_to_snippet(expr.span).unwrap_or_default();
         let is_wrapped = expr_snippet.starts_with('(') && expr_snippet.ends_with(')');
         let after_open = expr.span.lo() + rustc_span::BytePos(1);
         let before_close = expr.span.hi() - rustc_span::BytePos(1);
diff --git a/compiler/rustc_typeck/src/check/fallback.rs b/compiler/rustc_typeck/src/check/fallback.rs
index 15788f410f1..4059b3403b1 100644
--- a/compiler/rustc_typeck/src/check/fallback.rs
+++ b/compiler/rustc_typeck/src/check/fallback.rs
@@ -1,9 +1,8 @@
 use crate::check::FnCtxt;
 use rustc_data_structures::{
-    fx::FxHashMap,
+    fx::{FxHashMap, FxHashSet},
     graph::WithSuccessors,
     graph::{iterate::DepthFirstSearch, vec_graph::VecGraph},
-    stable_set::FxHashSet,
 };
 use rustc_middle::ty::{self, Ty};
 
@@ -219,9 +218,9 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
             .diverging_type_vars
             .borrow()
             .iter()
-            .map(|&ty| self.infcx.shallow_resolve(ty))
+            .map(|&ty| self.shallow_resolve(ty))
             .filter_map(|ty| ty.ty_vid())
-            .map(|vid| self.infcx.root_var(vid))
+            .map(|vid| self.root_var(vid))
             .collect();
         debug!(
             "calculate_diverging_fallback: diverging_type_vars={:?}",
@@ -237,7 +236,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
         let mut diverging_vids = vec![];
         let mut non_diverging_vids = vec![];
         for unsolved_vid in unsolved_vids {
-            let root_vid = self.infcx.root_var(unsolved_vid);
+            let root_vid = self.root_var(unsolved_vid);
             debug!(
                 "calculate_diverging_fallback: unsolved_vid={:?} root_vid={:?} diverges={:?}",
                 unsolved_vid,
@@ -272,7 +271,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
         // variables. (Note that this set consists of "root variables".)
         let mut roots_reachable_from_non_diverging = DepthFirstSearch::new(&coercion_graph);
         for &non_diverging_vid in &non_diverging_vids {
-            let root_vid = self.infcx.root_var(non_diverging_vid);
+            let root_vid = self.root_var(non_diverging_vid);
             if roots_reachable_from_diverging.visited(root_vid) {
                 continue;
             }
@@ -295,7 +294,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
         diverging_fallback.reserve(diverging_vids.len());
         for &diverging_vid in &diverging_vids {
             let diverging_ty = self.tcx.mk_ty_var(diverging_vid);
-            let root_vid = self.infcx.root_var(diverging_vid);
+            let root_vid = self.root_var(diverging_vid);
             let can_reach_non_diverging = coercion_graph
                 .depth_first_search(root_vid)
                 .any(|n| roots_reachable_from_non_diverging.visited(n));
@@ -303,7 +302,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
             let mut relationship = ty::FoundRelationships { self_in_trait: false, output: false };
 
             for (vid, rel) in relationships.iter() {
-                if self.infcx.root_var(*vid) == root_vid {
+                if self.root_var(*vid) == root_vid {
                     relationship.self_in_trait |= rel.self_in_trait;
                     relationship.output |= rel.output;
                 }
@@ -388,12 +387,12 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
             })
             .collect();
         debug!("create_coercion_graph: coercion_edges={:?}", coercion_edges);
-        let num_ty_vars = self.infcx.num_ty_vars();
+        let num_ty_vars = self.num_ty_vars();
         VecGraph::new(num_ty_vars, coercion_edges)
     }
 
     /// If `ty` is an unresolved type variable, returns its root vid.
     fn root_vid(&self, ty: Ty<'tcx>) -> Option<ty::TyVid> {
-        Some(self.infcx.root_var(self.infcx.shallow_resolve(ty).ty_vid()?))
+        Some(self.root_var(self.shallow_resolve(ty).ty_vid()?))
     }
 }
diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs
index cf7de1dc016..d1c10a3b63c 100644
--- a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs
+++ b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs
@@ -30,18 +30,15 @@ use rustc_middle::ty::{
 };
 use rustc_session::lint;
 use rustc_span::hygiene::DesugaringKind;
-use rustc_span::source_map::{original_sp, DUMMY_SP};
 use rustc_span::symbol::{kw, sym, Ident};
-use rustc_span::{self, BytePos, Span};
+use rustc_span::{Span, DUMMY_SP};
 use rustc_trait_selection::infer::InferCtxtExt as _;
 use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
 use rustc_trait_selection::traits::{
-    self, ObligationCause, ObligationCauseCode, StatementAsExpression, TraitEngine, TraitEngineExt,
-    WellFormedLoc,
+    self, ObligationCause, ObligationCauseCode, TraitEngine, TraitEngineExt,
 };
 
 use std::collections::hash_map::Entry;
-use std::iter;
 use std::slice;
 
 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
@@ -188,12 +185,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         if !method.substs.is_empty() {
             let method_generics = self.tcx.generics_of(method.def_id);
             if !method_generics.params.is_empty() {
-                let user_type_annotation = self.infcx.probe(|_| {
+                let user_type_annotation = self.probe(|_| {
                     let user_substs = UserSubsts {
                         substs: InternalSubsts::for_item(self.tcx, method.def_id, |param, _| {
                             let i = param.index as usize;
                             if i < method_generics.parent_count {
-                                self.infcx.var_for_def(DUMMY_SP, param)
+                                self.var_for_def(DUMMY_SP, param)
                             } else {
                                 method.substs[i]
                             }
@@ -201,7 +198,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         user_self_ty: None, // not relevant here
                     };
 
-                    self.infcx.canonicalize_user_type_annotation(UserType::TypeOf(
+                    self.canonicalize_user_type_annotation(UserType::TypeOf(
                         method.def_id,
                         user_substs,
                     ))
@@ -239,7 +236,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         debug!("fcx {}", self.tag());
 
         if Self::can_contain_user_lifetime_bounds((substs, user_self_ty)) {
-            let canonicalized = self.infcx.canonicalize_user_type_annotation(UserType::TypeOf(
+            let canonicalized = self.canonicalize_user_type_annotation(UserType::TypeOf(
                 def_id,
                 UserSubsts { substs, user_self_ty },
             ));
@@ -375,29 +372,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         (result, spans)
     }
 
-    /// Convenience method which tracks extra diagnostic information for normalization
-    /// that occurs as a result of WF checking. The `hir_id` is the `HirId` of the hir item
-    /// whose type is being wf-checked - this is used to construct a more precise span if
-    /// an error occurs.
-    ///
-    /// It is never necessary to call this method - calling `normalize_associated_types_in` will
-    /// just result in a slightly worse diagnostic span, and will still be sound.
-    pub(in super::super) fn normalize_associated_types_in_wf<T>(
-        &self,
-        span: Span,
-        value: T,
-        loc: WellFormedLoc,
-    ) -> T
-    where
-        T: TypeFoldable<'tcx>,
-    {
-        self.inh.normalize_associated_types_in_with_cause(
-            ObligationCause::new(span, self.body_id, ObligationCauseCode::WellFormed(Some(loc))),
-            self.param_env,
-            value,
-        )
-    }
-
     pub(in super::super) fn normalize_associated_types_in<T>(&self, span: Span, value: T) -> T
     where
         T: TypeFoldable<'tcx>,
@@ -437,6 +411,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     rhs_span: opt_input_expr.map(|expr| expr.span),
                     is_lit: opt_input_expr
                         .map_or(false, |expr| matches!(expr.kind, ExprKind::Lit(_))),
+                    output_pred: None,
                 },
             ),
             self.param_env,
@@ -505,7 +480,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         debug!("to_ty_saving_user_provided_ty: ty={:?}", ty);
 
         if Self::can_contain_user_lifetime_bounds(ty) {
-            let c_ty = self.infcx.canonicalize_response(UserType::Ty(ty));
+            let c_ty = self.canonicalize_response(UserType::Ty(ty));
             debug!("to_ty_saving_user_provided_ty: c_ty={:?}", c_ty);
             self.typeck_results.borrow_mut().user_provided_types_mut().insert(ast_ty.hir_id, c_ty);
         }
@@ -786,12 +761,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // src/test/ui/impl-trait/hidden-type-is-opaque-2.rs for examples that hit this path.
         if formal_ret.has_infer_types() {
             for ty in ret_ty.walk() {
-                if let ty::subst::GenericArgKind::Type(ty) = ty.unpack() {
-                    if let ty::Opaque(def_id, _) = *ty.kind() {
-                        if self.infcx.opaque_type_origin(def_id, DUMMY_SP).is_some() {
-                            return None;
-                        }
-                    }
+                if let ty::subst::GenericArgKind::Type(ty) = ty.unpack()
+                    && let ty::Opaque(def_id, _) = *ty.kind()
+                    && let Some(def_id) = def_id.as_local()
+                    && self.opaque_type_origin(def_id, DUMMY_SP).is_some() {
+                    return None;
                 }
             }
         }
@@ -852,7 +826,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         } else {
             self.tcx.bound_type_of(def_id)
         };
-        let substs = self.infcx.fresh_substs_for_item(span, def_id);
+        let substs = self.fresh_substs_for_item(span, def_id);
         let ty = item_ty.subst(self.tcx, substs);
 
         self.write_resolution(hir_id, Ok((def_kind, def_id)));
@@ -1083,84 +1057,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         ));
     }
 
-    pub(in super::super) fn could_remove_semicolon(
-        &self,
-        blk: &'tcx hir::Block<'tcx>,
-        expected_ty: Ty<'tcx>,
-    ) -> Option<(Span, StatementAsExpression)> {
-        // Be helpful when the user wrote `{... expr;}` and
-        // taking the `;` off is enough to fix the error.
-        let last_stmt = blk.stmts.last()?;
-        let hir::StmtKind::Semi(ref last_expr) = last_stmt.kind else {
-            return None;
-        };
-        let last_expr_ty = self.node_ty(last_expr.hir_id);
-        let needs_box = match (last_expr_ty.kind(), expected_ty.kind()) {
-            (ty::Opaque(last_def_id, _), ty::Opaque(exp_def_id, _))
-                if last_def_id == exp_def_id =>
-            {
-                StatementAsExpression::CorrectType
-            }
-            (ty::Opaque(last_def_id, last_bounds), ty::Opaque(exp_def_id, exp_bounds)) => {
-                debug!(
-                    "both opaque, likely future {:?} {:?} {:?} {:?}",
-                    last_def_id, last_bounds, exp_def_id, exp_bounds
-                );
-
-                let last_local_id = last_def_id.as_local()?;
-                let exp_local_id = exp_def_id.as_local()?;
-
-                match (
-                    &self.tcx.hir().expect_item(last_local_id).kind,
-                    &self.tcx.hir().expect_item(exp_local_id).kind,
-                ) {
-                    (
-                        hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: last_bounds, .. }),
-                        hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: exp_bounds, .. }),
-                    ) if iter::zip(*last_bounds, *exp_bounds).all(|(left, right)| {
-                        match (left, right) {
-                            (
-                                hir::GenericBound::Trait(tl, ml),
-                                hir::GenericBound::Trait(tr, mr),
-                            ) if tl.trait_ref.trait_def_id() == tr.trait_ref.trait_def_id()
-                                && ml == mr =>
-                            {
-                                true
-                            }
-                            (
-                                hir::GenericBound::LangItemTrait(langl, _, _, argsl),
-                                hir::GenericBound::LangItemTrait(langr, _, _, argsr),
-                            ) if langl == langr => {
-                                // FIXME: consider the bounds!
-                                debug!("{:?} {:?}", argsl, argsr);
-                                true
-                            }
-                            _ => false,
-                        }
-                    }) =>
-                    {
-                        StatementAsExpression::NeedsBoxing
-                    }
-                    _ => StatementAsExpression::CorrectType,
-                }
-            }
-            _ => StatementAsExpression::CorrectType,
-        };
-        if (matches!(last_expr_ty.kind(), ty::Error(_))
-            || self.can_sub(self.param_env, last_expr_ty, expected_ty).is_err())
-            && matches!(needs_box, StatementAsExpression::CorrectType)
-        {
-            return None;
-        }
-        let span = if last_stmt.span.from_expansion() {
-            let mac_call = original_sp(last_stmt.span, blk.span);
-            self.tcx.sess.source_map().mac_call_stmt_semi_span(mac_call)?
-        } else {
-            last_stmt.span.with_lo(last_stmt.span.hi() - BytePos(1))
-        };
-        Some((span, needs_box))
-    }
-
     // Instantiates the given path, which must refer to an item with the given
     // number of type parameters and type.
     #[instrument(skip(self, span), level = "debug")]
@@ -1426,7 +1322,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     }
                     GenericParamDefKind::Const { has_default } => {
                         if !infer_args && has_default {
-                            EarlyBinder(tcx.const_param_default(param.def_id))
+                            tcx.bound_const_param_default(param.def_id)
                                 .subst(tcx, substs.unwrap())
                                 .into()
                         } else {
diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs
index a7c7089234a..eb22938fb61 100644
--- a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs
+++ b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs
@@ -4,6 +4,7 @@ use crate::check::fn_ctxt::arg_matrix::{
     ArgMatrix, Compatibility, Error, ExpectedIdx, ProvidedIdx,
 };
 use crate::check::gather_locals::Declaration;
+use crate::check::intrinsicck::InlineAsmCtxt;
 use crate::check::method::MethodCallee;
 use crate::check::Expectation::*;
 use crate::check::TupleArgumentsFlag::*;
@@ -14,22 +15,23 @@ use crate::check::{
 use crate::structured_errors::StructuredDiagnostic;
 
 use rustc_ast as ast;
-use rustc_errors::{Applicability, Diagnostic, DiagnosticId, MultiSpan};
+use rustc_errors::{pluralize, Applicability, Diagnostic, DiagnosticId, MultiSpan};
 use rustc_hir as hir;
 use rustc_hir::def::{CtorOf, DefKind, Res};
 use rustc_hir::def_id::DefId;
 use rustc_hir::{ExprKind, Node, QPath};
 use rustc_index::vec::IndexVec;
 use rustc_infer::infer::error_reporting::{FailureCode, ObligationCauseExt};
+use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
 use rustc_infer::infer::InferOk;
 use rustc_infer::infer::TypeTrace;
 use rustc_middle::ty::adjustment::AllowTwoPhase;
 use rustc_middle::ty::visit::TypeVisitable;
-use rustc_middle::ty::{self, IsSuggestable, Ty, TyCtxt};
+use rustc_middle::ty::{self, DefIdTree, IsSuggestable, Ty};
 use rustc_session::Session;
 use rustc_span::symbol::Ident;
 use rustc_span::{self, Span};
-use rustc_trait_selection::traits::{self, ObligationCauseCode, StatementAsExpression};
+use rustc_trait_selection::traits::{self, ObligationCauseCode, SelectionContext};
 
 use std::iter;
 use std::slice;
@@ -56,7 +58,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         debug!("FnCtxt::check_asm: {} deferred checks", deferred_asm_checks.len());
         for (asm, hir_id) in deferred_asm_checks.drain(..) {
             let enclosing_id = self.tcx.hir().enclosing_body_owner(hir_id);
-            self.check_asm(asm, enclosing_id);
+            InlineAsmCtxt::new_in_fn(self).check_asm(asm, enclosing_id);
         }
     }
 
@@ -89,7 +91,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 args_no_rcvr,
                 false,
                 tuple_arguments,
-                None,
+                method.ok().map(|method| method.def_id),
             );
             return self.tcx.ty_error();
         }
@@ -393,41 +395,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
 
         if !call_appears_satisfied {
-            // Next, let's construct the error
-            let (error_span, full_call_span, ctor_of) = match &call_expr.kind {
-                hir::ExprKind::Call(
-                    hir::Expr {
-                        span,
-                        kind:
-                            hir::ExprKind::Path(hir::QPath::Resolved(
-                                _,
-                                hir::Path { res: Res::Def(DefKind::Ctor(of, _), _), .. },
-                            )),
-                        ..
-                    },
-                    _,
-                ) => (call_span, *span, Some(of)),
-                hir::ExprKind::Call(hir::Expr { span, .. }, _) => (call_span, *span, None),
-                hir::ExprKind::MethodCall(path_segment, _, span) => {
-                    let ident_span = path_segment.ident.span;
-                    let ident_span = if let Some(args) = path_segment.args {
-                        ident_span.with_hi(args.span_ext.hi())
-                    } else {
-                        ident_span
-                    };
-                    (
-                        *span, ident_span, None, // methods are never ctors
-                    )
-                }
-                k => span_bug!(call_span, "checking argument types on a non-call: `{:?}`", k),
-            };
-            let args_span = error_span.trim_start(full_call_span).unwrap_or(error_span);
-            let call_name = match ctor_of {
-                Some(CtorOf::Struct) => "struct",
-                Some(CtorOf::Variant) => "enum variant",
-                None => "function",
-            };
-
             let compatibility_diagonal = IndexVec::from_raw(compatibility_diagonal);
             let provided_args = IndexVec::from_iter(provided_args.iter().take(if c_variadic {
                 minimum_input_count
@@ -451,13 +418,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 compatibility_diagonal,
                 formal_and_expected_inputs,
                 provided_args,
-                full_call_span,
-                error_span,
-                args_span,
-                call_name,
                 c_variadic,
                 err_code,
                 fn_def_id,
+                call_span,
+                call_expr,
             );
         }
     }
@@ -467,14 +432,47 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         compatibility_diagonal: IndexVec<ProvidedIdx, Compatibility<'tcx>>,
         formal_and_expected_inputs: IndexVec<ExpectedIdx, (Ty<'tcx>, Ty<'tcx>)>,
         provided_args: IndexVec<ProvidedIdx, &'tcx hir::Expr<'tcx>>,
-        full_call_span: Span,
-        error_span: Span,
-        args_span: Span,
-        call_name: &str,
         c_variadic: bool,
         err_code: &str,
         fn_def_id: Option<DefId>,
+        call_span: Span,
+        call_expr: &hir::Expr<'tcx>,
     ) {
+        // Next, let's construct the error
+        let (error_span, full_call_span, ctor_of) = match &call_expr.kind {
+            hir::ExprKind::Call(
+                hir::Expr { hir_id, span, kind: hir::ExprKind::Path(qpath), .. },
+                _,
+            ) => {
+                if let Res::Def(DefKind::Ctor(of, _), _) =
+                    self.typeck_results.borrow().qpath_res(qpath, *hir_id)
+                {
+                    (call_span, *span, Some(of))
+                } else {
+                    (call_span, *span, None)
+                }
+            }
+            hir::ExprKind::Call(hir::Expr { span, .. }, _) => (call_span, *span, None),
+            hir::ExprKind::MethodCall(path_segment, _, span) => {
+                let ident_span = path_segment.ident.span;
+                let ident_span = if let Some(args) = path_segment.args {
+                    ident_span.with_hi(args.span_ext.hi())
+                } else {
+                    ident_span
+                };
+                (
+                    *span, ident_span, None, // methods are never ctors
+                )
+            }
+            k => span_bug!(call_span, "checking argument types on a non-call: `{:?}`", k),
+        };
+        let args_span = error_span.trim_start(full_call_span).unwrap_or(error_span);
+        let call_name = match ctor_of {
+            Some(CtorOf::Struct) => "struct",
+            Some(CtorOf::Variant) => "enum variant",
+            None => "function",
+        };
+
         // Don't print if it has error types or is just plain `_`
         fn has_error_or_infer<'tcx>(tys: impl IntoIterator<Item = Ty<'tcx>>) -> bool {
             tys.into_iter().any(|ty| ty.references_error() || ty.is_ty_var())
@@ -483,6 +481,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         self.set_tainted_by_errors();
         let tcx = self.tcx;
 
+        // Get the argument span in the context of the call span so that
+        // suggestions and labels are (more) correct when an arg is a
+        // macro invocation.
+        let normalize_span = |span: Span| -> Span {
+            let normalized_span = span.find_ancestor_inside(error_span).unwrap_or(span);
+            // Sometimes macros mess up the spans, so do not normalize the
+            // arg span to equal the error span, because that's less useful
+            // than pointing out the arg expr in the wrong context.
+            if normalized_span.source_equal(error_span) { span } else { normalized_span }
+        };
+
         // Precompute the provided types and spans, since that's all we typically need for below
         let provided_arg_tys: IndexVec<ProvidedIdx, (Ty<'tcx>, Span)> = provided_args
             .iter()
@@ -492,9 +501,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     .borrow()
                     .expr_ty_adjusted_opt(*expr)
                     .unwrap_or_else(|| tcx.ty_error());
-                (self.resolve_vars_if_possible(ty), expr.span)
+                (self.resolve_vars_if_possible(ty), normalize_span(expr.span))
             })
             .collect();
+        let callee_expr = match &call_expr.peel_blocks().kind {
+            hir::ExprKind::Call(callee, _) => Some(*callee),
+            hir::ExprKind::MethodCall(_, callee, _) => {
+                if let Some((DefKind::AssocFn, def_id)) =
+                    self.typeck_results.borrow().type_dependent_def(call_expr.hir_id)
+                    && let Some(assoc) = tcx.opt_associated_item(def_id)
+                    && assoc.fn_has_self_parameter
+                {
+                    Some(&callee[0])
+                } else {
+                    None
+                }
+            }
+            _ => None,
+        };
+        let callee_ty = callee_expr
+            .and_then(|callee_expr| self.typeck_results.borrow().expr_ty_adjusted_opt(callee_expr));
 
         // A "softer" version of the `demand_compatible`, which checks types without persisting them,
         // and treats error types differently
@@ -558,6 +584,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             // If so, we might have just forgotten to wrap some args in a tuple.
             if let Some(ty::Tuple(tys)) =
                 formal_and_expected_inputs.get(mismatch_idx.into()).map(|tys| tys.1.kind())
+                // If the tuple is unit, we're not actually wrapping any arguments.
+                && !tys.is_empty()
                 && provided_arg_tys.len() == formal_and_expected_inputs.len() - 1 + tys.len()
             {
                 // Wrap up the N provided arguments starting at this position in a tuple.
@@ -583,11 +611,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 // Take some care with spans, so we don't suggest wrapping a macro's
                 // innards in parenthesis, for example.
                 if satisfied
-                    && let Some(lo) =
-                        provided_args[mismatch_idx.into()].span.find_ancestor_inside(error_span)
-                    && let Some(hi) = provided_args[(mismatch_idx + tys.len() - 1).into()]
-                        .span
-                        .find_ancestor_inside(error_span)
+                    && let Some((_, lo)) =
+                        provided_arg_tys.get(ProvidedIdx::from_usize(mismatch_idx))
+                    && let Some((_, hi)) =
+                        provided_arg_tys.get(ProvidedIdx::from_usize(mismatch_idx + tys.len() - 1))
                 {
                     let mut err;
                     if tys.len() == 1 {
@@ -595,7 +622,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         // so don't do anything special here.
                         err = self.report_and_explain_type_error(
                             TypeTrace::types(
-                                &self.misc(lo),
+                                &self.misc(*lo),
                                 true,
                                 formal_and_expected_inputs[mismatch_idx.into()].1,
                                 provided_arg_tys[mismatch_idx.into()].0,
@@ -618,7 +645,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                     "argument"
                                 ),
                                 potentially_plural_count(provided_args.len(), "argument"),
-                                if provided_args.len() == 1 { "was" } else { "were" }
+                                pluralize!("was", provided_args.len())
                             ),
                             DiagnosticId::Error(err_code.to_owned()),
                         );
@@ -631,7 +658,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             Applicability::MachineApplicable,
                         );
                     };
-                    label_fn_like(tcx, &mut err, fn_def_id);
+                    self.label_fn_like(&mut err, fn_def_id, callee_ty);
                     err.emit();
                     return;
                 }
@@ -721,7 +748,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 format!("arguments to this {} are incorrect", call_name),
             );
             // Call out where the function is defined
-            label_fn_like(tcx, &mut err, fn_def_id);
+            self.label_fn_like(&mut err, fn_def_id, callee_ty);
             err.emit();
             return;
         }
@@ -743,7 +770,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     if c_variadic { "at least " } else { "" },
                     potentially_plural_count(formal_and_expected_inputs.len(), "argument"),
                     potentially_plural_count(provided_args.len(), "argument"),
-                    if provided_args.len() == 1 { "was" } else { "were" }
+                    pluralize!("was", provided_args.len())
                 ),
                 DiagnosticId::Error(err_code.to_owned()),
             )
@@ -1003,7 +1030,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
 
         // Call out where the function is defined
-        label_fn_like(tcx, &mut err, fn_def_id);
+        self.label_fn_like(&mut err, fn_def_id, callee_ty);
 
         // And add a suggestion block for all of the parameters
         let suggestion_text = match suggestion_text {
@@ -1035,7 +1062,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 let suggestion_text = if let Some(provided_idx) = provided_idx
                     && let (_, provided_span) = provided_arg_tys[*provided_idx]
                     && let Ok(arg_text) =
-                        source_map.span_to_snippet(provided_span.source_callsite())
+                        source_map.span_to_snippet(provided_span)
                 {
                     arg_text
                 } else {
@@ -1043,7 +1070,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     let (_, expected_ty) = formal_and_expected_inputs[expected_idx];
                     if expected_ty.is_unit() {
                         "()".to_string()
-                    } else if expected_ty.is_suggestable(tcx) {
+                    } else if expected_ty.is_suggestable(tcx, false) {
                         format!("/* {} */", expected_ty)
                     } else {
                         "/* value */".to_string()
@@ -1215,6 +1242,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         self.check_pat_top(&decl.pat, decl_ty, ty_span, origin_expr);
         let pat_ty = self.node_ty(decl.pat.hir_id);
         self.overwrite_local_ty_if_err(decl.hir_id, decl.pat, decl_ty, pat_ty);
+
+        if let Some(blk) = decl.els {
+            let previous_diverges = self.diverges.get();
+            let else_ty = self.check_block_with_expected(blk, NoExpectation);
+            let cause = self.cause(blk.span, ObligationCauseCode::LetElse);
+            if let Some(mut err) =
+                self.demand_eqtype_with_origin(&cause, self.tcx.types.never, else_ty)
+            {
+                err.emit();
+            }
+            self.diverges.set(previous_diverges);
+        }
     }
 
     /// Type check a `let` statement.
@@ -1236,8 +1275,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let old_has_errors = self.has_errors.replace(false);
 
         match stmt.kind {
-            hir::StmtKind::Local(ref l) => {
-                self.check_decl_local(&l);
+            hir::StmtKind::Local(l) => {
+                self.check_decl_local(l);
             }
             // Ignore for now.
             hir::StmtKind::Item(_) => {}
@@ -1381,7 +1420,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         &self.misc(sp),
                         &mut |err| {
                             if let Some(expected_ty) = expected.only_has_type(self) {
-                                self.consider_hint_about_removing_semicolon(blk, expected_ty, err);
+                                if !self.consider_removing_semicolon(blk, expected_ty, err) {
+                                    self.consider_returning_binding(blk, expected_ty, err);
+                                }
                                 if expected_ty == self.tcx.types.bool {
                                     // If this is caused by a missing `let` in a `while let`,
                                     // silence this redundant error, as we already emit E0070.
@@ -1449,42 +1490,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         ty
     }
 
-    /// A common error is to add an extra semicolon:
-    ///
-    /// ```compile_fail,E0308
-    /// fn foo() -> usize {
-    ///     22;
-    /// }
-    /// ```
-    ///
-    /// This routine checks if the final statement in a block is an
-    /// expression with an explicit semicolon whose type is compatible
-    /// with `expected_ty`. If so, it suggests removing the semicolon.
-    fn consider_hint_about_removing_semicolon(
-        &self,
-        blk: &'tcx hir::Block<'tcx>,
-        expected_ty: Ty<'tcx>,
-        err: &mut Diagnostic,
-    ) {
-        if let Some((span_semi, boxed)) = self.could_remove_semicolon(blk, expected_ty) {
-            if let StatementAsExpression::NeedsBoxing = boxed {
-                err.span_suggestion_verbose(
-                    span_semi,
-                    "consider removing this semicolon and boxing the expression",
-                    "",
-                    Applicability::HasPlaceholders,
-                );
-            } else {
-                err.span_suggestion_short(
-                    span_semi,
-                    "remove this semicolon",
-                    "",
-                    Applicability::MachineApplicable,
-                );
-            }
-        }
-    }
-
     fn parent_item_span(&self, id: hir::HirId) -> Option<Span> {
         let node = self.tcx.hir().get_by_def_id(self.tcx.hir().get_parent_item(id));
         match node {
@@ -1515,21 +1520,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// ```
     fn get_expr_coercion_span(&self, expr: &hir::Expr<'_>) -> rustc_span::Span {
         let check_in_progress = |elem: &hir::Expr<'_>| {
-            self.in_progress_typeck_results
-                .and_then(|typeck_results| typeck_results.borrow().node_type_opt(elem.hir_id))
-                .and_then(|ty| {
-                    if ty.is_never() {
-                        None
-                    } else {
-                        Some(match elem.kind {
-                            // Point at the tail expression when possible.
-                            hir::ExprKind::Block(block, _) => {
-                                block.expr.map_or(block.span, |e| e.span)
-                            }
-                            _ => elem.span,
-                        })
-                    }
-                })
+            self.typeck_results.borrow().node_type_opt(elem.hir_id).filter(|ty| !ty.is_never()).map(
+                |_| match elem.kind {
+                    // Point at the tail expression when possible.
+                    hir::ExprKind::Block(block, _) => block.expr.map_or(block.span, |e| e.span),
+                    _ => elem.span,
+                },
+            )
         };
 
         if let hir::ExprKind::If(_, _, Some(el)) = expr.kind {
@@ -1762,19 +1759,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             .flat_map(|a| a.args.iter())
                         {
                             if let hir::GenericArg::Type(hir_ty) = &arg {
-                                if let hir::TyKind::Path(hir::QPath::TypeRelative(..)) =
-                                    &hir_ty.kind
-                                {
-                                    // Avoid ICE with associated types. As this is best
-                                    // effort only, it's ok to ignore the case. It
-                                    // would trigger in `is_send::<T::AssocType>();`
-                                    // from `typeck-default-trait-impl-assoc-type.rs`.
-                                } else {
-                                    let ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, hir_ty);
-                                    let ty = self.resolve_vars_if_possible(ty);
-                                    if ty == predicate.self_ty() {
-                                        error.obligation.cause.span = hir_ty.span;
-                                    }
+                                let ty = self.resolve_vars_if_possible(
+                                    self.typeck_results.borrow().node_type(hir_ty.hir_id),
+                                );
+                                if ty == predicate.self_ty() {
+                                    error.obligation.cause.span = hir_ty.span;
                                 }
                             }
                         }
@@ -1783,47 +1772,126 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             }
         }
     }
-}
 
-fn label_fn_like<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    err: &mut rustc_errors::DiagnosticBuilder<'tcx, rustc_errors::ErrorGuaranteed>,
-    def_id: Option<DefId>,
-) {
-    let Some(def_id) = def_id else {
-        return;
-    };
-
-    if let Some(def_span) = tcx.def_ident_span(def_id) {
-        let mut spans: MultiSpan = def_span.into();
-
-        let params = tcx
-            .hir()
-            .get_if_local(def_id)
-            .and_then(|node| node.body_id())
-            .into_iter()
-            .flat_map(|id| tcx.hir().body(id).params);
-
-        for param in params {
-            spans.push_span_label(param.span, "");
+    fn label_fn_like(
+        &self,
+        err: &mut rustc_errors::DiagnosticBuilder<'tcx, rustc_errors::ErrorGuaranteed>,
+        callable_def_id: Option<DefId>,
+        callee_ty: Option<Ty<'tcx>>,
+    ) {
+        let Some(mut def_id) = callable_def_id else {
+            return;
+        };
+
+        if let Some(assoc_item) = self.tcx.opt_associated_item(def_id)
+            // Possibly points at either impl or trait item, so try to get it
+            // to point to trait item, then get the parent.
+            // This parent might be an impl in the case of an inherent function,
+            // but the next check will fail.
+            && let maybe_trait_item_def_id = assoc_item.trait_item_def_id.unwrap_or(def_id)
+            && let maybe_trait_def_id = self.tcx.parent(maybe_trait_item_def_id)
+            // Just an easy way to check "trait_def_id == Fn/FnMut/FnOnce"
+            && let Some(call_kind) = ty::ClosureKind::from_def_id(self.tcx, maybe_trait_def_id)
+            && let Some(callee_ty) = callee_ty
+        {
+            let callee_ty = callee_ty.peel_refs();
+            match *callee_ty.kind() {
+                ty::Param(param) => {
+                    let param =
+                        self.tcx.generics_of(self.body_id.owner).type_param(&param, self.tcx);
+                    if param.kind.is_synthetic() {
+                        // if it's `impl Fn() -> ..` then just fall down to the def-id based logic
+                        def_id = param.def_id;
+                    } else {
+                        // Otherwise, find the predicate that makes this generic callable,
+                        // and point at that.
+                        let instantiated = self
+                            .tcx
+                            .explicit_predicates_of(self.body_id.owner)
+                            .instantiate_identity(self.tcx);
+                        // FIXME(compiler-errors): This could be problematic if something has two
+                        // fn-like predicates with different args, but callable types really never
+                        // do that, so it's OK.
+                        for (predicate, span) in
+                            std::iter::zip(instantiated.predicates, instantiated.spans)
+                        {
+                            if let ty::PredicateKind::Trait(pred) = predicate.kind().skip_binder()
+                                && pred.self_ty().peel_refs() == callee_ty
+                                && ty::ClosureKind::from_def_id(self.tcx, pred.def_id()).is_some()
+                            {
+                                err.span_note(span, "callable defined here");
+                                return;
+                            }
+                        }
+                    }
+                }
+                ty::Opaque(new_def_id, _)
+                | ty::Closure(new_def_id, _)
+                | ty::FnDef(new_def_id, _) => {
+                    def_id = new_def_id;
+                }
+                _ => {
+                    // Look for a user-provided impl of a `Fn` trait, and point to it.
+                    let new_def_id = self.probe(|_| {
+                        let trait_ref = ty::TraitRef::new(
+                            call_kind.to_def_id(self.tcx),
+                            self.tcx.mk_substs([
+                                ty::GenericArg::from(callee_ty),
+                                self.next_ty_var(TypeVariableOrigin {
+                                    kind: TypeVariableOriginKind::MiscVariable,
+                                    span: rustc_span::DUMMY_SP,
+                                })
+                                .into(),
+                            ].into_iter()),
+                        );
+                        let obligation = traits::Obligation::new(
+                            traits::ObligationCause::dummy(),
+                            self.param_env,
+                            ty::Binder::dummy(ty::TraitPredicate {
+                                trait_ref,
+                                constness: ty::BoundConstness::NotConst,
+                                polarity: ty::ImplPolarity::Positive,
+                            }),
+                        );
+                        match SelectionContext::new(&self).select(&obligation) {
+                            Ok(Some(traits::ImplSource::UserDefined(impl_source))) => {
+                                Some(impl_source.impl_def_id)
+                            }
+                            _ => None
+                        }
+                    });
+                    if let Some(new_def_id) = new_def_id {
+                        def_id = new_def_id;
+                    } else {
+                        return;
+                    }
+                }
+            }
         }
 
-        let def_kind = tcx.def_kind(def_id);
-        err.span_note(spans, &format!("{} defined here", def_kind.descr(def_id)));
-    } else {
-        match tcx.hir().get_if_local(def_id) {
-            Some(hir::Node::Expr(hir::Expr {
-                kind: hir::ExprKind::Closure { fn_decl_span, .. },
-                ..
-            })) => {
-                let spans: MultiSpan = (*fn_decl_span).into();
+        if let Some(def_span) = self.tcx.def_ident_span(def_id) && !def_span.is_dummy() {
+            let mut spans: MultiSpan = def_span.into();
 
-                // Note: We don't point to param spans here because they overlap
-                // with the closure span itself
+            let params = self
+                .tcx
+                .hir()
+                .get_if_local(def_id)
+                .and_then(|node| node.body_id())
+                .into_iter()
+                .flat_map(|id| self.tcx.hir().body(id).params);
 
-                err.span_note(spans, "closure defined here");
+            for param in params {
+                spans.push_span_label(param.span, "");
             }
-            _ => {}
+
+            let def_kind = self.tcx.def_kind(def_id);
+            err.span_note(spans, &format!("{} defined here", def_kind.descr(def_id)));
+        } else {
+            let def_kind = self.tcx.def_kind(def_id);
+            err.span_note(
+                self.tcx.def_span(def_id),
+                &format!("{} defined here", def_kind.descr(def_id)),
+            );
         }
     }
 }
diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
index 863a981134f..097fff6418e 100644
--- a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
@@ -11,14 +11,12 @@ use rustc_hir::{
     Expr, ExprKind, GenericBound, Node, Path, QPath, Stmt, StmtKind, TyKind, WherePredicate,
 };
 use rustc_infer::infer::{self, TyCtxtInferExt};
-use rustc_infer::traits;
+use rustc_infer::traits::{self, StatementAsExpression};
 use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty::{self, Binder, IsSuggestable, Subst, ToPredicate, Ty};
 use rustc_span::symbol::sym;
 use rustc_span::Span;
-use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
-
-use std::iter;
+use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
 
 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     pub(in super::super) fn suggest_semicolon_at_end(&self, span: Span, err: &mut Diagnostic) {
@@ -183,59 +181,60 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         } else if let (ty::FnDef(def_id, ..), true) =
             (&found.kind(), self.suggest_fn_call(err, expr, expected, found))
         {
-            if def_id.is_local() {
-                err.span_label(self.tcx.def_span(def_id), &format!("{} defined here", found));
+            if let Some(sp) = self.tcx.hir().span_if_local(*def_id) {
+                err.span_label(sp, format!("{found} defined here"));
             }
         } else if !self.check_for_cast(err, expr, found, expected, expected_ty_expr) {
-            let is_struct_pat_shorthand_field =
-                self.maybe_get_struct_pattern_shorthand_field(expr).is_some();
             let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id);
             if !methods.is_empty() {
-                if let Ok(expr_text) = self.sess().source_map().span_to_snippet(expr.span) {
-                    let mut suggestions = iter::zip(iter::repeat(&expr_text), &methods)
-                        .filter_map(|(receiver, method)| {
-                            let method_call = format!(".{}()", method.name);
-                            if receiver.ends_with(&method_call) {
-                                None // do not suggest code that is already there (#53348)
-                            } else {
-                                let method_call_list = [".to_vec()", ".to_string()"];
-                                let mut sugg = if receiver.ends_with(".clone()")
-                                    && method_call_list.contains(&method_call.as_str())
-                                {
-                                    let max_len = receiver.rfind('.').unwrap();
-                                    vec![(
-                                        expr.span,
-                                        format!("{}{}", &receiver[..max_len], method_call),
-                                    )]
-                                } else {
-                                    if expr.precedence().order()
-                                        < ExprPrecedence::MethodCall.order()
-                                    {
-                                        vec![
-                                            (expr.span.shrink_to_lo(), "(".to_string()),
-                                            (expr.span.shrink_to_hi(), format!("){}", method_call)),
-                                        ]
-                                    } else {
-                                        vec![(expr.span.shrink_to_hi(), method_call)]
-                                    }
-                                };
-                                if is_struct_pat_shorthand_field {
-                                    sugg.insert(
-                                        0,
-                                        (expr.span.shrink_to_lo(), format!("{}: ", receiver)),
-                                    );
-                                }
-                                Some(sugg)
-                            }
-                        })
-                        .peekable();
-                    if suggestions.peek().is_some() {
-                        err.multipart_suggestions(
-                            "try using a conversion method",
-                            suggestions,
-                            Applicability::MaybeIncorrect,
-                        );
-                    }
+                let mut suggestions = methods.iter()
+                    .filter_map(|conversion_method| {
+                        let receiver_method_ident = expr.method_ident();
+                        if let Some(method_ident) = receiver_method_ident
+                            && method_ident.name == conversion_method.name
+                        {
+                            return None // do not suggest code that is already there (#53348)
+                        }
+
+                        let method_call_list = [sym::to_vec, sym::to_string];
+                        let mut sugg = if let ExprKind::MethodCall(receiver_method, ..) = expr.kind
+                            && receiver_method.ident.name == sym::clone
+                            && method_call_list.contains(&conversion_method.name)
+                            // If receiver is `.clone()` and found type has one of those methods,
+                            // we guess that the user wants to convert from a slice type (`&[]` or `&str`)
+                            // to an owned type (`Vec` or `String`).  These conversions clone internally,
+                            // so we remove the user's `clone` call.
+                        {
+                            vec![(
+                                receiver_method.ident.span,
+                                conversion_method.name.to_string()
+                            )]
+                        } else if expr.precedence().order()
+                            < ExprPrecedence::MethodCall.order()
+                        {
+                            vec![
+                                (expr.span.shrink_to_lo(), "(".to_string()),
+                                (expr.span.shrink_to_hi(), format!(").{}()", conversion_method.name)),
+                            ]
+                        } else {
+                            vec![(expr.span.shrink_to_hi(), format!(".{}()", conversion_method.name))]
+                        };
+                        let struct_pat_shorthand_field = self.maybe_get_struct_pattern_shorthand_field(expr);
+                        if let Some(name) = struct_pat_shorthand_field {
+                            sugg.insert(
+                                0,
+                                (expr.span.shrink_to_lo(), format!("{}: ", name)),
+                            );
+                        }
+                        Some(sugg)
+                    })
+                    .peekable();
+                if suggestions.peek().is_some() {
+                    err.multipart_suggestions(
+                        "try using a conversion method",
+                        suggestions,
+                        Applicability::MaybeIncorrect,
+                    );
                 }
             } else if let ty::Adt(found_adt, found_substs) = found.kind()
                 && self.tcx.is_diagnostic_item(sym::Option, found_adt.did())
@@ -507,7 +506,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             self.resolve_numeric_literals_with_default(self.resolve_vars_if_possible(found));
         // Only suggest changing the return type for methods that
         // haven't set a return type at all (and aren't `fn main()` or an impl).
-        match (&fn_decl.output, found.is_suggestable(self.tcx), can_suggest, expected.is_unit()) {
+        match (
+            &fn_decl.output,
+            found.is_suggestable(self.tcx, false),
+            can_suggest,
+            expected.is_unit(),
+        ) {
             (&hir::FnRetTy::DefaultReturn(span), true, true, true) => {
                 err.subdiagnostic(AddReturnTypeSuggestion::Add { span, found });
                 true
@@ -865,4 +869,43 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             );
         }
     }
+
+    /// A common error is to add an extra semicolon:
+    ///
+    /// ```compile_fail,E0308
+    /// fn foo() -> usize {
+    ///     22;
+    /// }
+    /// ```
+    ///
+    /// This routine checks if the final statement in a block is an
+    /// expression with an explicit semicolon whose type is compatible
+    /// with `expected_ty`. If so, it suggests removing the semicolon.
+    pub(crate) fn consider_removing_semicolon(
+        &self,
+        blk: &'tcx hir::Block<'tcx>,
+        expected_ty: Ty<'tcx>,
+        err: &mut Diagnostic,
+    ) -> bool {
+        if let Some((span_semi, boxed)) = self.could_remove_semicolon(blk, expected_ty) {
+            if let StatementAsExpression::NeedsBoxing = boxed {
+                err.span_suggestion_verbose(
+                    span_semi,
+                    "consider removing this semicolon and boxing the expression",
+                    "",
+                    Applicability::HasPlaceholders,
+                );
+            } else {
+                err.span_suggestion_short(
+                    span_semi,
+                    "remove this semicolon",
+                    "",
+                    Applicability::MachineApplicable,
+                );
+            }
+            true
+        } else {
+            false
+        }
+    }
 }
diff --git a/compiler/rustc_typeck/src/check/gather_locals.rs b/compiler/rustc_typeck/src/check/gather_locals.rs
index 576dc6f127c..8f34a970f6f 100644
--- a/compiler/rustc_typeck/src/check/gather_locals.rs
+++ b/compiler/rustc_typeck/src/check/gather_locals.rs
@@ -16,19 +16,20 @@ pub(super) struct Declaration<'a> {
     pub ty: Option<&'a hir::Ty<'a>>,
     pub span: Span,
     pub init: Option<&'a hir::Expr<'a>>,
+    pub els: Option<&'a hir::Block<'a>>,
 }
 
 impl<'a> From<&'a hir::Local<'a>> for Declaration<'a> {
     fn from(local: &'a hir::Local<'a>) -> Self {
-        let hir::Local { hir_id, pat, ty, span, init, .. } = *local;
-        Declaration { hir_id, pat, ty, span, init }
+        let hir::Local { hir_id, pat, ty, span, init, els, source: _ } = *local;
+        Declaration { hir_id, pat, ty, span, init, els }
     }
 }
 
 impl<'a> From<&'a hir::Let<'a>> for Declaration<'a> {
     fn from(let_expr: &'a hir::Let<'a>) -> Self {
         let hir::Let { hir_id, pat, ty, span, init } = *let_expr;
-        Declaration { hir_id, pat, ty, span, init: Some(init) }
+        Declaration { hir_id, pat, ty, span, init: Some(init), els: None }
     }
 }
 
@@ -101,7 +102,7 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> {
     // Add explicitly-declared locals.
     fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>) {
         self.declare(local.into());
-        intravisit::walk_local(self, local);
+        intravisit::walk_local(self, local)
     }
 
     fn visit_let_expr(&mut self, let_expr: &'tcx hir::Let<'tcx>) {
diff --git a/compiler/rustc_typeck/src/check/generator_interior.rs b/compiler/rustc_typeck/src/check/generator_interior.rs
index 6ee989070b4..d4f8001493c 100644
--- a/compiler/rustc_typeck/src/check/generator_interior.rs
+++ b/compiler/rustc_typeck/src/check/generator_interior.rs
@@ -14,7 +14,7 @@ use rustc_hir::hir_id::HirIdSet;
 use rustc_hir::intravisit::{self, Visitor};
 use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind};
 use rustc_middle::middle::region::{self, Scope, ScopeData, YieldData};
-use rustc_middle::ty::{self, RvalueScopes, Ty, TyCtxt};
+use rustc_middle::ty::{self, RvalueScopes, Ty, TyCtxt, TypeVisitable};
 use rustc_span::symbol::sym;
 use rustc_span::Span;
 use tracing::debug;
@@ -69,7 +69,7 @@ impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> {
                                 yield_data.expr_and_pat_count, self.expr_count, source_span
                             );
 
-                            if self.fcx.sess().opts.debugging_opts.drop_tracking
+                            if self.fcx.sess().opts.unstable_opts.drop_tracking
                                 && self
                                     .drop_ranges
                                     .is_dropped_at(hir_id, yield_data.expr_and_pat_count)
@@ -376,6 +376,17 @@ impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> {
 
         debug!("is_borrowed_temporary: {:?}", self.drop_ranges.is_borrowed_temporary(expr));
 
+        let ty = self.fcx.typeck_results.borrow().expr_ty_adjusted_opt(expr);
+        let may_need_drop = |ty: Ty<'tcx>| {
+            // Avoid ICEs in needs_drop.
+            let ty = self.fcx.resolve_vars_if_possible(ty);
+            let ty = self.fcx.tcx.erase_regions(ty);
+            if ty.needs_infer() {
+                return true;
+            }
+            ty.needs_drop(self.fcx.tcx, self.fcx.param_env)
+        };
+
         // Typically, the value produced by an expression is consumed by its parent in some way,
         // so we only have to check if the parent contains a yield (note that the parent may, for
         // example, store the value into a local variable, but then we already consider local
@@ -384,7 +395,18 @@ impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> {
         // However, in the case of temporary values, we are going to store the value into a
         // temporary on the stack that is live for the current temporary scope and then return a
         // reference to it. That value may be live across the entire temporary scope.
-        let scope = if self.drop_ranges.is_borrowed_temporary(expr) {
+        //
+        // There's another subtlety: if the type has an observable drop, it must be dropped after
+        // the yield, even if it's not borrowed or referenced after the yield. Ideally this would
+        // *only* happen for types with observable drop, not all types which wrap them, but that
+        // doesn't match the behavior of MIR borrowck and causes ICEs. See the FIXME comment in
+        // src/test/ui/generator/drop-tracking-parent-expression.rs.
+        let scope = if self.drop_ranges.is_borrowed_temporary(expr)
+            || ty.map_or(true, |ty| {
+                let needs_drop = may_need_drop(ty);
+                debug!(?needs_drop, ?ty);
+                needs_drop
+            }) {
             self.rvalue_scopes.temporary_scope(self.region_scope_tree, expr.hir_id.local_id)
         } else {
             debug!("parent_node: {:?}", self.fcx.tcx.hir().find_parent_node(expr.hir_id));
@@ -398,7 +420,7 @@ impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> {
 
         // If there are adjustments, then record the final type --
         // this is the actual value that is being produced.
-        if let Some(adjusted_ty) = self.fcx.typeck_results.borrow().expr_ty_adjusted_opt(expr) {
+        if let Some(adjusted_ty) = ty {
             self.record(adjusted_ty, expr.hir_id, scope, Some(expr), expr.span);
         }
 
diff --git a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges.rs b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges.rs
index 887c791af76..518cd734236 100644
--- a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges.rs
+++ b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges.rs
@@ -17,8 +17,7 @@ use self::record_consumed_borrow::find_consumed_and_borrowed;
 use crate::check::FnCtxt;
 use hir::def_id::DefId;
 use hir::{Body, HirId, HirIdMap, Node};
-use rustc_data_structures::fx::FxHashMap;
-use rustc_data_structures::stable_set::FxHashSet;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_hir as hir;
 use rustc_index::bit_set::BitSet;
 use rustc_index::vec::IndexVec;
@@ -38,7 +37,7 @@ pub fn compute_drop_ranges<'a, 'tcx>(
     def_id: DefId,
     body: &'tcx Body<'tcx>,
 ) -> DropRanges {
-    if fcx.sess().opts.debugging_opts.drop_tracking {
+    if fcx.sess().opts.unstable_opts.drop_tracking {
         let consumed_borrowed_places = find_consumed_and_borrowed(fcx, def_id, body);
 
         let typeck_results = &fcx.typeck_results.borrow();
diff --git a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs
index 111d534abf8..a2c23db162b 100644
--- a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs
+++ b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs
@@ -6,7 +6,7 @@ use hir::{
     intravisit::{self, Visitor},
     Body, Expr, ExprKind, Guard, HirId, LoopIdError,
 };
-use rustc_data_structures::{fx::FxHashMap, stable_set::FxHashSet};
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_hir as hir;
 use rustc_index::vec::IndexVec;
 use rustc_middle::{
@@ -33,7 +33,7 @@ pub(super) fn build_control_flow_graph<'tcx>(
     intravisit::walk_body(&mut drop_range_visitor, body);
 
     drop_range_visitor.drop_ranges.process_deferred_edges();
-    if let Some(filename) = &tcx.sess.opts.debugging_opts.dump_drop_tracking_cfg {
+    if let Some(filename) = &tcx.sess.opts.unstable_opts.dump_drop_tracking_cfg {
         super::cfg_visualize::write_graph_to_file(&drop_range_visitor.drop_ranges, filename, tcx);
     }
 
diff --git a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/record_consumed_borrow.rs b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/record_consumed_borrow.rs
index 67cc46f21f0..ded0888c33e 100644
--- a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/record_consumed_borrow.rs
+++ b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/record_consumed_borrow.rs
@@ -4,7 +4,7 @@ use crate::{
     expr_use_visitor::{self, ExprUseVisitor},
 };
 use hir::{def_id::DefId, Body, HirId, HirIdMap};
-use rustc_data_structures::stable_set::FxHashSet;
+use rustc_data_structures::fx::FxHashSet;
 use rustc_hir as hir;
 use rustc_middle::hir::place::{PlaceBase, Projection, ProjectionKind};
 use rustc_middle::ty::{ParamEnv, TyCtxt};
@@ -72,9 +72,8 @@ impl<'tcx> ExprUseDelegate<'tcx> {
     }
 
     fn mark_consumed(&mut self, consumer: HirId, target: TrackedValue) {
-        if !self.places.consumed.contains_key(&consumer) {
-            self.places.consumed.insert(consumer, <_>::default());
-        }
+        self.places.consumed.entry(consumer).or_insert_with(|| <_>::default());
+
         debug!(?consumer, ?target, "mark_consumed");
         self.places.consumed.get_mut(&consumer).map(|places| places.insert(target));
     }
diff --git a/compiler/rustc_typeck/src/check/inherited.rs b/compiler/rustc_typeck/src/check/inherited.rs
index 2ce258cf69c..a499179b95f 100644
--- a/compiler/rustc_typeck/src/check/inherited.rs
+++ b/compiler/rustc_typeck/src/check/inherited.rs
@@ -1,5 +1,4 @@
 use super::callee::DeferredCallResolution;
-use super::MaybeInProgressTables;
 
 use rustc_data_structures::fx::FxHashSet;
 use rustc_hir as hir;
@@ -29,7 +28,7 @@ use std::ops::Deref;
 pub struct Inherited<'a, 'tcx> {
     pub(super) infcx: InferCtxt<'a, 'tcx>,
 
-    pub(super) typeck_results: super::MaybeInProgressTables<'a, 'tcx>,
+    pub(super) typeck_results: &'a RefCell<ty::TypeckResults<'tcx>>,
 
     pub(super) locals: RefCell<HirIdMap<super::LocalTy<'tcx>>>,
 
@@ -86,7 +85,10 @@ impl<'tcx> Inherited<'_, 'tcx> {
         let hir_owner = tcx.hir().local_def_id_to_hir_id(def_id).owner;
 
         InheritedBuilder {
-            infcx: tcx.infer_ctxt().with_fresh_in_progress_typeck_results(hir_owner),
+            infcx: tcx
+                .infer_ctxt()
+                .ignoring_regions()
+                .with_fresh_in_progress_typeck_results(hir_owner),
             def_id,
         }
     }
@@ -103,15 +105,15 @@ impl<'tcx> InheritedBuilder<'tcx> {
 }
 
 impl<'a, 'tcx> Inherited<'a, 'tcx> {
-    pub(super) fn new(infcx: InferCtxt<'a, 'tcx>, def_id: LocalDefId) -> Self {
+    fn new(infcx: InferCtxt<'a, 'tcx>, def_id: LocalDefId) -> Self {
         let tcx = infcx.tcx;
         let item_id = tcx.hir().local_def_id_to_hir_id(def_id);
         let body_id = tcx.hir().maybe_body_owned_by(item_id);
+        let typeck_results =
+            infcx.in_progress_typeck_results.expect("building `FnCtxt` without typeck results");
 
         Inherited {
-            typeck_results: MaybeInProgressTables {
-                maybe_typeck_results: infcx.in_progress_typeck_results,
-            },
+            typeck_results,
             infcx,
             fulfillment_cx: RefCell::new(<dyn TraitEngine<'_>>::new(tcx)),
             locals: RefCell::new(Default::default()),
diff --git a/compiler/rustc_typeck/src/check/intrinsic.rs b/compiler/rustc_typeck/src/check/intrinsic.rs
index 7fe710cf8f4..3f2a0da8d65 100644
--- a/compiler/rustc_typeck/src/check/intrinsic.rs
+++ b/compiler/rustc_typeck/src/check/intrinsic.rs
@@ -400,6 +400,10 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
 
             sym::const_eval_select => (4, vec![param(0), param(1), param(2)], param(3)),
 
+            sym::vtable_size | sym::vtable_align => {
+                (0, vec![tcx.mk_imm_ptr(tcx.mk_unit())], tcx.types.usize)
+            }
+
             other => {
                 tcx.sess.emit_err(UnrecognizedIntrinsicFunction { span: it.span, name: other });
                 return;
diff --git a/compiler/rustc_typeck/src/check/intrinsicck.rs b/compiler/rustc_typeck/src/check/intrinsicck.rs
index a5add1e9a8a..df94abbafb1 100644
--- a/compiler/rustc_typeck/src/check/intrinsicck.rs
+++ b/compiler/rustc_typeck/src/check/intrinsicck.rs
@@ -1,5 +1,5 @@
 use rustc_ast::InlineAsmTemplatePiece;
-use rustc_data_structures::stable_set::FxHashSet;
+use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::struct_span_err;
 use rustc_hir as hir;
 use rustc_index::vec::Idx;
@@ -111,6 +111,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
         false
     }
+}
+
+pub struct InlineAsmCtxt<'a, 'tcx> {
+    tcx: TyCtxt<'tcx>,
+    fcx: Option<&'a FnCtxt<'a, 'tcx>>,
+}
+
+impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
+    pub fn new_global_asm(tcx: TyCtxt<'tcx>) -> Self {
+        InlineAsmCtxt { tcx, fcx: None }
+    }
+
+    pub fn new_in_fn(fcx: &'a FnCtxt<'a, 'tcx>) -> Self {
+        InlineAsmCtxt { tcx: fcx.tcx, fcx: Some(fcx) }
+    }
 
     fn check_asm_operand_type(
         &self,
@@ -122,9 +137,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         tied_input: Option<(&hir::Expr<'tcx>, Option<InlineAsmType>)>,
         target_features: &FxHashSet<Symbol>,
     ) -> Option<InlineAsmType> {
+        let fcx = self.fcx.unwrap_or_else(|| span_bug!(expr.span, "asm operand for global asm"));
         // Check the type against the allowed types for inline asm.
-        let ty = self.typeck_results.borrow().expr_ty_adjusted(expr);
-        let ty = self.resolve_vars_if_possible(ty);
+        let ty = fcx.typeck_results.borrow().expr_ty_adjusted(expr);
+        let ty = fcx.resolve_vars_if_possible(ty);
         let asm_ty_isize = match self.tcx.sess.target.pointer_width {
             16 => InlineAsmType::I16,
             32 => InlineAsmType::I32,
@@ -134,7 +150,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         // Expect types to be fully resolved, no const or type variables.
         if ty.has_infer_types_or_consts() {
-            assert!(self.is_tainted_by_errors());
+            assert!(fcx.is_tainted_by_errors());
             return None;
         }
 
@@ -151,7 +167,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             ty::Float(FloatTy::F32) => Some(InlineAsmType::F32),
             ty::Float(FloatTy::F64) => Some(InlineAsmType::F64),
             ty::FnPtr(_) => Some(asm_ty_isize),
-            ty::RawPtr(ty::TypeAndMut { ty, mutbl: _ }) if self.is_thin_ptr_ty(ty) => {
+            ty::RawPtr(ty::TypeAndMut { ty, mutbl: _ }) if fcx.is_thin_ptr_ty(ty) => {
                 Some(asm_ty_isize)
             }
             ty::Adt(adt, substs) if adt.repr().simd() => {
@@ -203,7 +219,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         // Check that the type implements Copy. The only case where this can
         // possibly fail is for SIMD types which don't #[derive(Copy)].
-        if !self.infcx.type_is_copy_modulo_regions(self.param_env, ty, DUMMY_SP) {
+        if !fcx.infcx.type_is_copy_modulo_regions(fcx.param_env, ty, DUMMY_SP) {
             let msg = "arguments for inline assembly must be copyable";
             let mut err = self.tcx.sess.struct_span_err(expr.span, msg);
             err.note(&format!("`{ty}` does not implement the Copy trait"));
@@ -224,8 +240,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 let msg = "incompatible types for asm inout argument";
                 let mut err = self.tcx.sess.struct_span_err(vec![in_expr.span, expr.span], msg);
 
-                let in_expr_ty = self.typeck_results.borrow().expr_ty_adjusted(in_expr);
-                let in_expr_ty = self.resolve_vars_if_possible(in_expr_ty);
+                let in_expr_ty = fcx.typeck_results.borrow().expr_ty_adjusted(in_expr);
+                let in_expr_ty = fcx.resolve_vars_if_possible(in_expr_ty);
                 err.span_label(in_expr.span, &format!("type `{in_expr_ty}`"));
                 err.span_label(expr.span, &format!("type `{ty}`"));
                 err.note(
diff --git a/compiler/rustc_typeck/src/check/method/mod.rs b/compiler/rustc_typeck/src/check/method/mod.rs
index c0b3a23fde4..c09f63f1e8f 100644
--- a/compiler/rustc_typeck/src/check/method/mod.rs
+++ b/compiler/rustc_typeck/src/check/method/mod.rs
@@ -10,7 +10,7 @@ mod suggest;
 pub use self::suggest::SelfSource;
 pub use self::MethodError::*;
 
-use crate::check::FnCtxt;
+use crate::check::{Expectation, FnCtxt};
 use crate::ObligationCause;
 use rustc_data_structures::sync::Lrc;
 use rustc_errors::{Applicability, Diagnostic};
@@ -20,8 +20,10 @@ use rustc_hir::def_id::DefId;
 use rustc_infer::infer::{self, InferOk};
 use rustc_middle::ty::subst::Subst;
 use rustc_middle::ty::subst::{InternalSubsts, SubstsRef};
-use rustc_middle::ty::{self, ToPredicate, Ty, TypeVisitable};
-use rustc_middle::ty::{DefIdTree, GenericParamDefKind};
+use rustc_middle::ty::{
+    self, AssocKind, DefIdTree, GenericParamDefKind, ProjectionPredicate, ProjectionTy, Term,
+    ToPredicate, Ty, TypeVisitable,
+};
 use rustc_span::symbol::Ident;
 use rustc_span::Span;
 use rustc_trait_selection::traits;
@@ -318,6 +320,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         self_ty: Ty<'tcx>,
         opt_input_type: Option<Ty<'tcx>>,
         opt_input_expr: Option<&'tcx hir::Expr<'tcx>>,
+        expected: Expectation<'tcx>,
     ) -> (traits::Obligation<'tcx, ty::Predicate<'tcx>>, &'tcx ty::List<ty::subst::GenericArg<'tcx>>)
     {
         // Construct a trait-reference `self_ty : Trait<input_tys>`
@@ -339,6 +342,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         // Construct an obligation
         let poly_trait_ref = ty::Binder::dummy(trait_ref);
+        let opt_output_ty =
+            expected.only_has_type(self).and_then(|ty| (!ty.needs_infer()).then(|| ty));
+        let opt_output_assoc_item = self.tcx.associated_items(trait_def_id).find_by_name_and_kind(
+            self.tcx,
+            Ident::from_str("Output"),
+            AssocKind::Type,
+            trait_def_id,
+        );
+        let output_pred =
+            opt_output_ty.zip(opt_output_assoc_item).map(|(output_ty, output_assoc_item)| {
+                ty::Binder::dummy(ty::PredicateKind::Projection(ProjectionPredicate {
+                    projection_ty: ProjectionTy { substs, item_def_id: output_assoc_item.def_id },
+                    term: Term::Ty(output_ty),
+                }))
+                .to_predicate(self.tcx)
+            });
+
         (
             traits::Obligation::new(
                 traits::ObligationCause::new(
@@ -348,6 +368,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         rhs_span: opt_input_expr.map(|expr| expr.span),
                         is_lit: opt_input_expr
                             .map_or(false, |expr| matches!(expr.kind, hir::ExprKind::Lit(_))),
+                        output_pred,
                     },
                 ),
                 self.param_env,
@@ -397,6 +418,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         self_ty: Ty<'tcx>,
         opt_input_type: Option<Ty<'tcx>>,
         opt_input_expr: Option<&'tcx hir::Expr<'tcx>>,
+        expected: Expectation<'tcx>,
     ) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
         let (obligation, substs) = self.obligation_for_op_method(
             span,
@@ -404,6 +426,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             self_ty,
             opt_input_type,
             opt_input_expr,
+            expected,
         );
         self.construct_obligation_for_trait(
             span,
@@ -505,6 +528,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     rhs_span: opt_input_expr.map(|expr| expr.span),
                     is_lit: opt_input_expr
                         .map_or(false, |expr| matches!(expr.kind, hir::ExprKind::Lit(_))),
+                    output_pred: None,
                 },
             )
         } else {
diff --git a/compiler/rustc_typeck/src/check/method/probe.rs b/compiler/rustc_typeck/src/check/method/probe.rs
index e9b91414a07..8f5f3657fc9 100644
--- a/compiler/rustc_typeck/src/check/method/probe.rs
+++ b/compiler/rustc_typeck/src/check/method/probe.rs
@@ -21,9 +21,7 @@ use rustc_middle::middle::stability;
 use rustc_middle::ty::fast_reject::{simplify_type, TreatParams};
 use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef};
 use rustc_middle::ty::GenericParamDefKind;
-use rustc_middle::ty::{
-    self, EarlyBinder, ParamEnvAnd, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeVisitable,
-};
+use rustc_middle::ty::{self, ParamEnvAnd, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeVisitable};
 use rustc_session::lint;
 use rustc_span::def_id::LocalDefId;
 use rustc_span::lev_distance::{
@@ -345,7 +343,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         OP: FnOnce(ProbeContext<'a, 'tcx>) -> Result<R, MethodError<'tcx>>,
     {
         let mut orig_values = OriginalQueryValues::default();
-        let param_env_and_self_ty = self.infcx.canonicalize_query(
+        let param_env_and_self_ty = self.canonicalize_query(
             ParamEnvAnd { param_env: self.param_env, value: self_ty },
             &mut orig_values,
         );
@@ -353,7 +351,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let steps = if mode == Mode::MethodCall {
             self.tcx.method_autoderef_steps(param_env_and_self_ty)
         } else {
-            self.infcx.probe(|_| {
+            self.probe(|_| {
                 // Mode::Path - the deref steps is "trivial". This turns
                 // our CanonicalQuery into a "trivial" QueryResponse. This
                 // is a bit inefficient, but I don't think that writing
@@ -713,7 +711,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
             }
 
             let (impl_ty, impl_substs) = self.impl_ty_and_substs(impl_def_id);
-            let impl_ty = EarlyBinder(impl_ty).subst(self.tcx, impl_substs);
+            let impl_ty = impl_ty.subst(self.tcx, impl_substs);
 
             debug!("impl_ty: {:?}", impl_ty);
 
@@ -1067,7 +1065,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
         let pick = self.pick_all_method(Some(&mut unstable_candidates));
 
         // In this case unstable picking is done by `pick_method`.
-        if !self.tcx.sess.opts.debugging_opts.pick_stable_methods_before_any_unstable {
+        if !self.tcx.sess.opts.unstable_opts.pick_stable_methods_before_any_unstable {
             return pick;
         }
 
@@ -1271,7 +1269,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
         self_ty: Ty<'tcx>,
         mut unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>,
     ) -> Option<PickResult<'tcx>> {
-        if !self.tcx.sess.opts.debugging_opts.pick_stable_methods_before_any_unstable {
+        if !self.tcx.sess.opts.unstable_opts.pick_stable_methods_before_any_unstable {
             return self.pick_method_with_unstable(self_ty);
         }
 
@@ -1811,9 +1809,12 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
         self.erase_late_bound_regions(xform_fn_sig)
     }
 
-    /// Gets the type of an impl and generate substitutions with placeholders.
-    fn impl_ty_and_substs(&self, impl_def_id: DefId) -> (Ty<'tcx>, SubstsRef<'tcx>) {
-        (self.tcx.type_of(impl_def_id), self.fresh_item_substs(impl_def_id))
+    /// Gets the type of an impl and generate substitutions with inference vars.
+    fn impl_ty_and_substs(
+        &self,
+        impl_def_id: DefId,
+    ) -> (ty::EarlyBinder<Ty<'tcx>>, SubstsRef<'tcx>) {
+        (self.tcx.bound_type_of(impl_def_id), self.fresh_item_substs(impl_def_id))
     }
 
     fn fresh_item_substs(&self, def_id: DefId) -> SubstsRef<'tcx> {
diff --git a/compiler/rustc_typeck/src/check/method/suggest.rs b/compiler/rustc_typeck/src/check/method/suggest.rs
index 7bf167426f7..56fcd9e0a89 100644
--- a/compiler/rustc_typeck/src/check/method/suggest.rs
+++ b/compiler/rustc_typeck/src/check/method/suggest.rs
@@ -19,6 +19,7 @@ use rustc_middle::ty::print::with_crate_prefix;
 use rustc_middle::ty::ToPolyTraitRef;
 use rustc_middle::ty::{self, DefIdTree, ToPredicate, Ty, TyCtxt, TypeVisitable};
 use rustc_span::symbol::{kw, sym, Ident};
+use rustc_span::Symbol;
 use rustc_span::{lev_distance, source_map, ExpnKind, FileName, MacroKind, Span};
 use rustc_trait_selection::traits::error_reporting::on_unimplemented::InferCtxtExt as _;
 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
@@ -864,27 +865,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             .join("\n");
                         let actual_prefix = actual.prefix_string(self.tcx);
                         info!("unimplemented_traits.len() == {}", unimplemented_traits.len());
-                        let (primary_message, label) = if unimplemented_traits.len() == 1
-                            && unimplemented_traits_only
-                        {
-                            unimplemented_traits
-                                .into_iter()
-                                .next()
-                                .map(|(_, (trait_ref, obligation))| {
-                                    if trait_ref.self_ty().references_error()
-                                        || actual.references_error()
-                                    {
-                                        // Avoid crashing.
-                                        return (None, None);
-                                    }
-                                    let OnUnimplementedNote { message, label, .. } =
-                                        self.infcx.on_unimplemented_note(trait_ref, &obligation);
-                                    (message, label)
-                                })
-                                .unwrap_or((None, None))
-                        } else {
-                            (None, None)
-                        };
+                        let (primary_message, label) =
+                            if unimplemented_traits.len() == 1 && unimplemented_traits_only {
+                                unimplemented_traits
+                                    .into_iter()
+                                    .next()
+                                    .map(|(_, (trait_ref, obligation))| {
+                                        if trait_ref.self_ty().references_error()
+                                            || actual.references_error()
+                                        {
+                                            // Avoid crashing.
+                                            return (None, None);
+                                        }
+                                        let OnUnimplementedNote { message, label, .. } =
+                                            self.on_unimplemented_note(trait_ref, &obligation);
+                                        (message, label)
+                                    })
+                                    .unwrap_or((None, None))
+                            } else {
+                                (None, None)
+                            };
                         let primary_message = primary_message.unwrap_or_else(|| format!(
                             "the {item_kind} `{item_name}` exists for {actual_prefix} `{ty_str}`, but its trait bounds were not satisfied"
                         ));
@@ -1122,7 +1122,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                          add a `use` for {one_of_them}:",
                         an = if candidates.len() == 1 { "an" } else { "" },
                         s = pluralize!(candidates.len()),
-                        were = if candidates.len() == 1 { "was" } else { "were" },
+                        were = pluralize!("was", candidates.len()),
                         one_of_them = if candidates.len() == 1 { "it" } else { "one_of_them" },
                     );
                     self.suggest_use_candidates(&mut err, help, candidates);
@@ -1548,7 +1548,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             Option<ObligationCause<'tcx>>,
         )],
     ) {
-        let mut derives = Vec::<(String, Span, String)>::new();
+        let mut derives = Vec::<(String, Span, Symbol)>::new();
         let mut traits = Vec::<Span>::new();
         for (pred, _, _) in unsatisfied_predicates {
             let ty::PredicateKind::Trait(trait_pred) = pred.kind().skip_binder() else { continue };
@@ -1581,12 +1581,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                 derives.push((
                                     self_name.clone(),
                                     self_span,
-                                    parent_diagnostic_name.to_string(),
+                                    parent_diagnostic_name,
                                 ));
                             }
                         }
                     }
-                    derives.push((self_name, self_span, diagnostic_name.to_string()));
+                    derives.push((self_name, self_span, diagnostic_name));
                 } else {
                     traits.push(self.tcx.def_span(trait_pred.def_id()));
                 }
@@ -1609,7 +1609,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     continue;
                 }
             }
-            derives_grouped.push((self_name, self_span, trait_name));
+            derives_grouped.push((self_name, self_span, trait_name.to_string()));
         }
 
         let len = traits.len();
@@ -1647,7 +1647,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         call: &hir::Expr<'_>,
         span: Span,
     ) {
-        let output_ty = match self.infcx.get_impl_future_output_ty(ty) {
+        let output_ty = match self.get_impl_future_output_ty(ty) {
             Some(output_ty) => self.resolve_vars_if_possible(output_ty).skip_binder(),
             _ => return,
         };
diff --git a/compiler/rustc_typeck/src/check/mod.rs b/compiler/rustc_typeck/src/check/mod.rs
index dee58791cec..17c2e4868aa 100644
--- a/compiler/rustc_typeck/src/check/mod.rs
+++ b/compiler/rustc_typeck/src/check/mod.rs
@@ -25,7 +25,7 @@ can be broken down into several distinct phases:
 
 - regionck: after main is complete, the regionck pass goes over all
   types looking for regions and making sure that they did not escape
-  into places they are not in scope.  This may also influence the
+  into places where they are not in scope.  This may also influence the
   final assignments of the various region variables if there is some
   flexibility.
 
@@ -87,10 +87,10 @@ mod op;
 mod pat;
 mod place_op;
 mod region;
-mod regionck;
+pub mod regionck;
 pub mod rvalue_scopes;
 mod upvar;
-mod wfcheck;
+pub mod wfcheck;
 pub mod writeback;
 
 use check::{check_abi, check_fn, check_mod_item_types};
@@ -128,8 +128,7 @@ use rustc_target::spec::abi::Abi;
 use rustc_trait_selection::traits;
 use rustc_trait_selection::traits::error_reporting::recursive_type_with_infinite_size_error;
 use rustc_trait_selection::traits::error_reporting::suggestions::ReturnsVisitor;
-
-use std::cell::{Ref, RefCell, RefMut};
+use std::cell::RefCell;
 
 use crate::require_c_abi_if_c_variadic;
 use crate::util::common::indenter;
@@ -534,7 +533,7 @@ fn fn_maybe_err(tcx: TyCtxt<'_>, sp: Span, abi: Abi) {
     }
 }
 
-fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: LocalDefId, span: Span) {
+fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: LocalDefId) {
     // Only restricted on wasm target for now
     if !tcx.sess.target.is_like_wasm {
         return;
@@ -560,7 +559,7 @@ fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: LocalDefId, span: S
         let msg = "statics with a custom `#[link_section]` must be a \
                         simple list of bytes on the wasm target with no \
                         extra levels of indirection such as references";
-        tcx.sess.span_err(span, msg);
+        tcx.sess.span_err(tcx.def_span(id), msg);
     }
 }
 
@@ -621,9 +620,8 @@ fn missing_items_err(
     // adding the associated item at the end of its body.
     let sugg_sp = full_impl_span.with_lo(hi).with_hi(hi);
     // Obtain the level of indentation ending in `sugg_sp`.
-    let indentation = tcx.sess.source_map().span_to_margin(sugg_sp).unwrap_or(0);
-    // Make the whitespace that will make the suggestion have the right indentation.
-    let padding: String = " ".repeat(indentation);
+    let padding =
+        tcx.sess.source_map().indentation_before(sugg_sp).unwrap_or_else(|| String::new());
 
     for trait_item in missing_items {
         let snippet = suggestion_signature(trait_item, tcx);
@@ -864,17 +862,14 @@ fn bad_non_zero_sized_fields<'tcx>(
     err.emit();
 }
 
-fn report_unexpected_variant_res(tcx: TyCtxt<'_>, res: Res, span: Span) {
+fn report_unexpected_variant_res(tcx: TyCtxt<'_>, res: Res, qpath: &hir::QPath<'_>, span: Span) {
     struct_span_err!(
         tcx.sess,
         span,
         E0533,
-        "expected unit struct, unit variant or constant, found {}{}",
+        "expected unit struct, unit variant or constant, found {} `{}`",
         res.descr(),
-        tcx.sess
-            .source_map()
-            .span_to_snippet(span)
-            .map_or_else(|_| String::new(), |s| format!(" `{s}`",)),
+        rustc_hir_pretty::qpath_to_string(qpath),
     )
     .emit();
 }
@@ -904,32 +899,6 @@ enum TupleArgumentsFlag {
     TupleArguments,
 }
 
-/// A wrapper for `InferCtxt`'s `in_progress_typeck_results` field.
-#[derive(Copy, Clone)]
-struct MaybeInProgressTables<'a, 'tcx> {
-    maybe_typeck_results: Option<&'a RefCell<ty::TypeckResults<'tcx>>>,
-}
-
-impl<'a, 'tcx> MaybeInProgressTables<'a, 'tcx> {
-    fn borrow(self) -> Ref<'a, ty::TypeckResults<'tcx>> {
-        match self.maybe_typeck_results {
-            Some(typeck_results) => typeck_results.borrow(),
-            None => bug!(
-                "MaybeInProgressTables: inh/fcx.typeck_results.borrow() with no typeck results"
-            ),
-        }
-    }
-
-    fn borrow_mut(self) -> RefMut<'a, ty::TypeckResults<'tcx>> {
-        match self.maybe_typeck_results {
-            Some(typeck_results) => typeck_results.borrow_mut(),
-            None => bug!(
-                "MaybeInProgressTables: inh/fcx.typeck_results.borrow_mut() with no typeck results"
-            ),
-        }
-    }
-}
-
 fn typeck_item_bodies(tcx: TyCtxt<'_>, (): ()) {
     tcx.hir().par_body_owners(|body_owner_def_id| tcx.ensure().typeck(body_owner_def_id));
 }
diff --git a/compiler/rustc_typeck/src/check/op.rs b/compiler/rustc_typeck/src/check/op.rs
index 0887c27ea36..920b3e68808 100644
--- a/compiler/rustc_typeck/src/check/op.rs
+++ b/compiler/rustc_typeck/src/check/op.rs
@@ -2,10 +2,12 @@
 
 use super::method::MethodCallee;
 use super::{has_expected_num_generic_args, FnCtxt};
+use crate::check::Expectation;
 use rustc_ast as ast;
 use rustc_errors::{self, struct_span_err, Applicability, Diagnostic};
 use rustc_hir as hir;
 use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
+use rustc_infer::traits::ObligationCauseCode;
 use rustc_middle::ty::adjustment::{
     Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability,
 };
@@ -30,9 +32,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         op: hir::BinOp,
         lhs: &'tcx hir::Expr<'tcx>,
         rhs: &'tcx hir::Expr<'tcx>,
+        expected: Expectation<'tcx>,
     ) -> Ty<'tcx> {
         let (lhs_ty, rhs_ty, return_ty) =
-            self.check_overloaded_binop(expr, lhs, rhs, op, IsAssign::Yes);
+            self.check_overloaded_binop(expr, lhs, rhs, op, IsAssign::Yes, expected);
 
         let ty =
             if !lhs_ty.is_ty_var() && !rhs_ty.is_ty_var() && is_builtin_binop(lhs_ty, rhs_ty, op) {
@@ -50,6 +53,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         Some(rhs_ty),
                         Some(rhs),
                         Op::Binary(op, IsAssign::Yes),
+                        expected,
                     )
                     .is_ok()
                 {
@@ -70,6 +74,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         op: hir::BinOp,
         lhs_expr: &'tcx hir::Expr<'tcx>,
         rhs_expr: &'tcx hir::Expr<'tcx>,
+        expected: Expectation<'tcx>,
     ) -> Ty<'tcx> {
         let tcx = self.tcx;
 
@@ -94,8 +99,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 // Otherwise, we always treat operators as if they are
                 // overloaded. This is the way to be most flexible w/r/t
                 // types that get inferred.
-                let (lhs_ty, rhs_ty, return_ty) =
-                    self.check_overloaded_binop(expr, lhs_expr, rhs_expr, op, IsAssign::No);
+                let (lhs_ty, rhs_ty, return_ty) = self.check_overloaded_binop(
+                    expr,
+                    lhs_expr,
+                    rhs_expr,
+                    op,
+                    IsAssign::No,
+                    expected,
+                );
 
                 // Supply type inference hints if relevant. Probably these
                 // hints should be enforced during select as part of the
@@ -176,6 +187,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         rhs_expr: &'tcx hir::Expr<'tcx>,
         op: hir::BinOp,
         is_assign: IsAssign,
+        expected: Expectation<'tcx>,
     ) -> (Ty<'tcx>, Ty<'tcx>, Ty<'tcx>) {
         debug!(
             "check_overloaded_binop(expr.hir_id={}, op={:?}, is_assign={:?})",
@@ -222,6 +234,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             Some(rhs_ty_var),
             Some(rhs_expr),
             Op::Binary(op, is_assign),
+            expected,
         );
 
         // see `NB` above
@@ -282,7 +295,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             Err(_) if lhs_ty.references_error() || rhs_ty.references_error() => self.tcx.ty_error(),
             Err(errors) => {
                 let source_map = self.tcx.sess.source_map();
-                let (mut err, missing_trait, _use_output) = match is_assign {
+                let (mut err, missing_trait, use_output) = match is_assign {
                     IsAssign::Yes => {
                         let mut err = struct_span_err!(
                             self.tcx.sess,
@@ -395,8 +408,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                 false,
                             ),
                         };
-                        let mut err =
-                            struct_span_err!(self.tcx.sess, op.span, E0369, "{}", message.as_str());
+                        let mut err = struct_span_err!(self.tcx.sess, op.span, E0369, "{message}");
                         if !lhs_expr.span.eq(&rhs_expr.span) {
                             self.add_type_neq_err_label(
                                 &mut err,
@@ -406,6 +418,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                 rhs_expr,
                                 op,
                                 is_assign,
+                                expected,
                             );
                             self.add_type_neq_err_label(
                                 &mut err,
@@ -415,6 +428,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                 lhs_expr,
                                 op,
                                 is_assign,
+                                expected,
                             );
                         }
                         self.note_unmet_impls_on_type(&mut err, errors);
@@ -429,6 +443,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             Some(rhs_ty),
                             Some(rhs_expr),
                             Op::Binary(op, is_assign),
+                            expected,
                         )
                         .is_ok()
                     {
@@ -460,7 +475,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         suggest_deref_binop(lhs_deref_ty);
                 } else if is_assign == IsAssign::No
                     && let Ref(_, lhs_deref_ty, _) = lhs_ty.kind() {
-                    if self.infcx.type_is_copy_modulo_regions(self.param_env, *lhs_deref_ty, lhs_expr.span) {
+                    if self.type_is_copy_modulo_regions(self.param_env, *lhs_deref_ty, lhs_expr.span) {
                         suggest_deref_binop(*lhs_deref_ty);
                     }
                 }
@@ -490,19 +505,31 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                 Some(rhs_ty),
                                 Some(rhs_expr),
                                 Op::Binary(op, is_assign),
+                                expected,
                             )
                             .unwrap_err();
-                        let predicates = errors
-                            .into_iter()
-                            .filter_map(|error| error.obligation.predicate.to_opt_poly_trait_pred())
-                            .collect::<Vec<_>>();
-                        if !predicates.is_empty() {
-                            for pred in predicates {
-                                self.infcx.suggest_restricting_param_bound(
-                                    &mut err,
-                                    pred,
-                                    self.body_id,
-                                );
+                        if !errors.is_empty() {
+                            for error in errors {
+                                if let Some(trait_pred) =
+                                    error.obligation.predicate.to_opt_poly_trait_pred()
+                                {
+                                    let proj_pred = match error.obligation.cause.code() {
+                                        ObligationCauseCode::BinOp {
+                                            output_pred: Some(output_pred),
+                                            ..
+                                        } if use_output => {
+                                            output_pred.to_opt_poly_projection_pred()
+                                        }
+                                        _ => None,
+                                    };
+
+                                    self.suggest_restricting_param_bound(
+                                        &mut err,
+                                        trait_pred,
+                                        proj_pred,
+                                        self.body_id,
+                                    );
+                                }
                             }
                         } else if *ty != lhs_ty {
                             // When we know that a missing bound is responsible, we don't show
@@ -532,6 +559,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         other_expr: &'tcx hir::Expr<'tcx>,
         op: hir::BinOp,
         is_assign: IsAssign,
+        expected: Expectation<'tcx>,
     ) -> bool /* did we suggest to call a function because of missing parentheses? */ {
         err.span_label(span, ty.to_string());
         if let FnDef(def_id, _) = *ty.kind() {
@@ -561,6 +589,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     Some(other_ty),
                     Some(other_expr),
                     Op::Binary(op, is_assign),
+                    expected,
                 )
                 .is_ok()
             {
@@ -601,18 +630,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let rm_borrow_msg = "remove the borrow to obtain an owned `String`";
         let to_owned_msg = "create an owned `String` from a string reference";
 
-        let string_type = self.tcx.get_diagnostic_item(sym::String);
-        let is_std_string = |ty: Ty<'tcx>| match ty.ty_adt_def() {
-            Some(ty_def) => Some(ty_def.did()) == string_type,
-            None => false,
+        let is_std_string = |ty: Ty<'tcx>| {
+            ty.ty_adt_def()
+                .map_or(false, |ty_def| self.tcx.is_diagnostic_item(sym::String, ty_def.did()))
         };
 
         match (lhs_ty.kind(), rhs_ty.kind()) {
             (&Ref(_, l_ty, _), &Ref(_, r_ty, _)) // &str or &String + &str, &String or &&str
-                if (*l_ty.kind() == Str || is_std_string(l_ty)) && (
-                        *r_ty.kind() == Str || is_std_string(r_ty) ||
-                        &format!("{:?}", rhs_ty) == "&&str"
-                    ) =>
+                if (*l_ty.kind() == Str || is_std_string(l_ty))
+                    && (*r_ty.kind() == Str
+                        || is_std_string(r_ty)
+                        || matches!(
+                            r_ty.kind(), Ref(_, inner_ty, _) if *inner_ty.kind() == Str
+                        )) =>
             {
                 if let IsAssign::No = is_assign { // Do not supply this message if `&str += &str`
                     err.span_label(op.span, "`+` cannot be used to concatenate two `&str` strings");
@@ -677,9 +707,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         ex: &'tcx hir::Expr<'tcx>,
         operand_ty: Ty<'tcx>,
         op: hir::UnOp,
+        expected: Expectation<'tcx>,
     ) -> Ty<'tcx> {
         assert!(op.is_by_value());
-        match self.lookup_op_method(operand_ty, None, None, Op::Unary(op, ex.span)) {
+        match self.lookup_op_method(operand_ty, None, None, Op::Unary(op, ex.span), expected) {
             Ok(method) => {
                 self.write_method_call(ex.hir_id, method);
                 method.sig.output()
@@ -709,9 +740,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                 error.obligation.predicate.to_opt_poly_trait_pred()
                             });
                         for pred in predicates {
-                            self.infcx.suggest_restricting_param_bound(
+                            self.suggest_restricting_param_bound(
                                 &mut err,
                                 pred,
+                                None,
                                 self.body_id,
                             );
                         }
@@ -772,6 +804,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         other_ty: Option<Ty<'tcx>>,
         other_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
         op: Op,
+        expected: Expectation<'tcx>,
     ) -> Result<MethodCallee<'tcx>, Vec<FulfillmentError<'tcx>>> {
         let lang = self.tcx.lang_items();
 
@@ -856,7 +889,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         let opname = Ident::with_dummy_span(opname);
         let method = trait_did.and_then(|trait_did| {
-            self.lookup_op_method_in_trait(span, opname, trait_did, lhs_ty, other_ty, other_ty_expr)
+            self.lookup_op_method_in_trait(
+                span,
+                opname,
+                trait_did,
+                lhs_ty,
+                other_ty,
+                other_ty_expr,
+                expected,
+            )
         });
 
         match (method, trait_did) {
@@ -867,8 +908,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             }
             (None, None) => Err(vec![]),
             (None, Some(trait_did)) => {
-                let (obligation, _) =
-                    self.obligation_for_op_method(span, trait_did, lhs_ty, other_ty, other_ty_expr);
+                let (obligation, _) = self.obligation_for_op_method(
+                    span,
+                    trait_did,
+                    lhs_ty,
+                    other_ty,
+                    other_ty_expr,
+                    expected,
+                );
                 let mut fulfill = <dyn TraitEngine<'_>>::new(self.tcx);
                 fulfill.register_predicate_obligation(self, obligation);
                 Err(fulfill.select_where_possible(&self.infcx))
diff --git a/compiler/rustc_typeck/src/check/pat.rs b/compiler/rustc_typeck/src/check/pat.rs
index fbfbfba5c2a..c7318cd6e53 100644
--- a/compiler/rustc_typeck/src/check/pat.rs
+++ b/compiler/rustc_typeck/src/check/pat.rs
@@ -183,7 +183,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             PatKind::TupleStruct(ref qpath, subpats, ddpos) => {
                 self.check_pat_tuple_struct(pat, qpath, subpats, ddpos, expected, def_bm, ti)
             }
-            PatKind::Path(_) => self.check_pat_path(pat, path_res.unwrap(), expected, ti),
+            PatKind::Path(ref qpath) => {
+                self.check_pat_path(pat, qpath, path_res.unwrap(), expected, ti)
+            }
             PatKind::Struct(ref qpath, fields, has_rest_pat) => {
                 self.check_pat_struct(pat, qpath, fields, has_rest_pat, expected, def_bm, ti)
             }
@@ -800,6 +802,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     fn check_pat_path<'b>(
         &self,
         pat: &Pat<'_>,
+        qpath: &hir::QPath<'_>,
         path_resolution: (Res, Option<Ty<'tcx>>, &'b [hir::PathSegment<'b>]),
         expected: Ty<'tcx>,
         ti: TopInfo<'tcx>,
@@ -814,7 +817,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 return tcx.ty_error();
             }
             Res::Def(DefKind::AssocFn | DefKind::Ctor(_, CtorKind::Fictive | CtorKind::Fn), _) => {
-                report_unexpected_variant_res(tcx, res, pat.span);
+                report_unexpected_variant_res(tcx, res, qpath, pat.span);
                 return tcx.ty_error();
             }
             Res::SelfCtor(..)
diff --git a/compiler/rustc_typeck/src/check/region.rs b/compiler/rustc_typeck/src/check/region.rs
index 9f1368a3e07..0c33a243e10 100644
--- a/compiler/rustc_typeck/src/check/region.rs
+++ b/compiler/rustc_typeck/src/check/region.rs
@@ -335,7 +335,7 @@ fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx h
     match expr.kind {
         // Manually recurse over closures and inline consts, because they are the only
         // case of nested bodies that share the parent environment.
-        hir::ExprKind::Closure { body, .. }
+        hir::ExprKind::Closure(&hir::Closure { body, .. })
         | hir::ExprKind::ConstBlock(hir::AnonConst { body, .. }) => {
             let body = visitor.tcx.hir().body(body);
             visitor.visit_body(body);
@@ -460,6 +460,7 @@ fn resolve_local<'tcx>(
     visitor: &mut RegionResolutionVisitor<'tcx>,
     pat: Option<&'tcx hir::Pat<'tcx>>,
     init: Option<&'tcx hir::Expr<'tcx>>,
+    els: Option<&'tcx hir::Block<'tcx>>,
 ) {
     debug!("resolve_local(pat={:?}, init={:?})", pat, init);
 
@@ -537,13 +538,18 @@ fn resolve_local<'tcx>(
         }
     }
 
-    // Make sure we visit the initializer first, so expr_and_pat_count remains correct
+    // Make sure we visit the initializer first, so expr_and_pat_count remains correct.
+    // The correct order, as shared between generator_interior, drop_ranges and intravisitor,
+    // is to walk initializer, followed by pattern bindings, finally followed by the `else` block.
     if let Some(expr) = init {
         visitor.visit_expr(expr);
     }
     if let Some(pat) = pat {
         visitor.visit_pat(pat);
     }
+    if let Some(els) = els {
+        visitor.visit_block(els);
+    }
 
     /// Returns `true` if `pat` match the `P&` non-terminal.
     ///
@@ -764,7 +770,7 @@ impl<'tcx> Visitor<'tcx> for RegionResolutionVisitor<'tcx> {
             // (i.e., `'static`), which means that after `g` returns, it drops,
             // and all the associated destruction scope rules apply.
             self.cx.var_parent = None;
-            resolve_local(self, None, Some(&body.value));
+            resolve_local(self, None, Some(&body.value), None);
         }
 
         if body.generator_kind.is_some() {
@@ -791,7 +797,7 @@ impl<'tcx> Visitor<'tcx> for RegionResolutionVisitor<'tcx> {
         resolve_expr(self, ex);
     }
     fn visit_local(&mut self, l: &'tcx Local<'tcx>) {
-        resolve_local(self, Some(&l.pat), l.init);
+        resolve_local(self, Some(&l.pat), l.init, l.els)
     }
 }
 
diff --git a/compiler/rustc_typeck/src/check/regionck.rs b/compiler/rustc_typeck/src/check/regionck.rs
index 1c3c5f999bc..d49a6138f7a 100644
--- a/compiler/rustc_typeck/src/check/regionck.rs
+++ b/compiler/rustc_typeck/src/check/regionck.rs
@@ -1,5 +1,5 @@
 use crate::outlives::outlives_bounds::InferCtxtExt as _;
-use rustc_data_structures::stable_set::FxHashSet;
+use rustc_data_structures::fx::FxHashSet;
 use rustc_hir as hir;
 use rustc_infer::infer::outlives::env::OutlivesEnvironment;
 use rustc_infer::infer::InferCtxt;
diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs
index 74546ed9080..d72e215934a 100644
--- a/compiler/rustc_typeck/src/check/upvar.rs
+++ b/compiler/rustc_typeck/src/check/upvar.rs
@@ -49,8 +49,7 @@ use rustc_span::sym;
 use rustc_span::{BytePos, Pos, Span, Symbol};
 use rustc_trait_selection::infer::InferCtxtExt;
 
-use rustc_data_structures::stable_map::FxHashMap;
-use rustc_data_structures::stable_set::FxHashSet;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_index::vec::Idx;
 use rustc_target::abi::VariantIdx;
 
@@ -142,7 +141,7 @@ struct InferBorrowKindVisitor<'a, 'tcx> {
 impl<'a, 'tcx> Visitor<'tcx> for InferBorrowKindVisitor<'a, 'tcx> {
     fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
         match expr.kind {
-            hir::ExprKind::Closure { capture_clause, body: body_id, .. } => {
+            hir::ExprKind::Closure(&hir::Closure { capture_clause, body: body_id, .. }) => {
                 let body = self.fcx.tcx.hir().body(body_id);
                 self.visit_body(body);
                 self.fcx.analyze_closure(expr.hir_id, expr.span, body_id, body, capture_clause);
@@ -315,7 +314,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             .collect();
         self.typeck_results.borrow_mut().closure_fake_reads.insert(closure_def_id, fake_reads);
 
-        if self.tcx.sess.opts.debugging_opts.profile_closures {
+        if self.tcx.sess.opts.unstable_opts.profile_closures {
             self.typeck_results.borrow_mut().closure_size_eval.insert(
                 closure_def_id,
                 ClosureSizeProfileData {
@@ -949,7 +948,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         let root_var_min_capture_list = min_captures.and_then(|m| m.get(&var_hir_id))?;
 
-        let ty = self.infcx.resolve_vars_if_possible(self.node_ty(var_hir_id));
+        let ty = self.resolve_vars_if_possible(self.node_ty(var_hir_id));
 
         let ty = match closure_clause {
             hir::CaptureBy::Value => ty, // For move closure the capture kind should be by value
@@ -1065,7 +1064,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         closure_clause: hir::CaptureBy,
         var_hir_id: hir::HirId,
     ) -> Option<FxHashSet<UpvarMigrationInfo>> {
-        let ty = self.infcx.resolve_vars_if_possible(self.node_ty(var_hir_id));
+        let ty = self.resolve_vars_if_possible(self.node_ty(var_hir_id));
 
         if !ty.has_significant_drop(self.tcx, self.tcx.param_env(closure_def_id.expect_local())) {
             debug!("does not have significant drop");
diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs
index 5621cf2e1a4..faab862cc3c 100644
--- a/compiler/rustc_typeck/src/check/wfcheck.rs
+++ b/compiler/rustc_typeck/src/check/wfcheck.rs
@@ -1,18 +1,16 @@
 use crate::check::regionck::OutlivesEnvironmentExt;
-use crate::check::{FnCtxt, Inherited};
 use crate::constrained_generic_params::{identify_constrained_generic_params, Parameter};
-
 use rustc_ast as ast;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed};
+use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed};
 use rustc_hir as hir;
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_hir::lang_items::LangItem;
 use rustc_hir::ItemKind;
-use rustc_infer::infer::outlives::env::OutlivesEnvironment;
+use rustc_infer::infer::outlives::env::{OutlivesEnvironment, RegionBoundPairs};
 use rustc_infer::infer::outlives::obligations::TypeOutlives;
-use rustc_infer::infer::region_constraints::GenericKind;
 use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt};
+use rustc_infer::traits::Normalized;
 use rustc_middle::ty::query::Providers;
 use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts, Subst};
 use rustc_middle::ty::trait_def::TraitSpecializationKind;
@@ -23,53 +21,95 @@ use rustc_middle::ty::{
 use rustc_session::parse::feature_err;
 use rustc_span::symbol::{sym, Ident, Symbol};
 use rustc_span::{Span, DUMMY_SP};
+use rustc_trait_selection::autoderef::Autoderef;
+use rustc_trait_selection::traits::error_reporting::InferCtxtExt;
 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
-use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode, WellFormedLoc};
+use rustc_trait_selection::traits::query::normalize::AtExt;
+use rustc_trait_selection::traits::query::NoSolution;
+use rustc_trait_selection::traits::{
+    self, ObligationCause, ObligationCauseCode, ObligationCtxt, WellFormedLoc,
+};
 
 use std::cell::LazyCell;
 use std::convert::TryInto;
 use std::iter;
-use std::ops::ControlFlow;
+use std::ops::{ControlFlow, Deref};
 
-/// Helper type of a temporary returned by `.for_item(...)`.
-/// This is necessary because we can't write the following bound:
-///
-/// ```ignore (illustrative)
-/// F: for<'b, 'tcx> where 'tcx FnOnce(FnCtxt<'b, 'tcx>)
-/// ```
-pub(super) struct CheckWfFcxBuilder<'tcx> {
-    inherited: super::InheritedBuilder<'tcx>,
-    id: hir::HirId,
+pub(super) struct WfCheckingCtxt<'a, 'tcx> {
+    pub(super) ocx: ObligationCtxt<'a, 'tcx>,
     span: Span,
+    body_id: hir::HirId,
     param_env: ty::ParamEnv<'tcx>,
 }
+impl<'a, 'tcx> Deref for WfCheckingCtxt<'a, 'tcx> {
+    type Target = ObligationCtxt<'a, 'tcx>;
+    fn deref(&self) -> &Self::Target {
+        &self.ocx
+    }
+}
 
-impl<'tcx> CheckWfFcxBuilder<'tcx> {
-    pub(super) fn with_fcx<F>(&mut self, f: F)
+impl<'tcx> WfCheckingCtxt<'_, 'tcx> {
+    fn tcx(&self) -> TyCtxt<'tcx> {
+        self.ocx.infcx.tcx
+    }
+
+    fn normalize<T>(&self, span: Span, loc: Option<WellFormedLoc>, value: T) -> T
     where
-        F: for<'b> FnOnce(&FnCtxt<'b, 'tcx>) -> FxHashSet<Ty<'tcx>>,
+        T: TypeFoldable<'tcx>,
     {
-        let id = self.id;
-        let span = self.span;
-        let param_env = self.param_env;
-        self.inherited.enter(|inh| {
-            let fcx = FnCtxt::new(&inh, param_env, id);
-            if !inh.tcx.features().trivial_bounds {
-                // As predicates are cached rather than obligations, this
-                // needs to be called first so that they are checked with an
-                // empty `param_env`.
-                check_false_global_bounds(&fcx, span, id);
-            }
-            let wf_tys = f(&fcx);
-            fcx.select_all_obligations_or_error();
+        self.ocx.normalize(
+            ObligationCause::new(span, self.body_id, ObligationCauseCode::WellFormed(loc)),
+            self.param_env,
+            value,
+        )
+    }
 
-            let mut outlives_environment = OutlivesEnvironment::new(param_env);
-            outlives_environment.add_implied_bounds(&fcx.infcx, wf_tys, id);
-            fcx.infcx.check_region_obligations_and_report_errors(&outlives_environment);
-        });
+    fn register_wf_obligation(
+        &self,
+        span: Span,
+        loc: Option<WellFormedLoc>,
+        arg: ty::GenericArg<'tcx>,
+    ) {
+        let cause =
+            traits::ObligationCause::new(span, self.body_id, ObligationCauseCode::WellFormed(loc));
+        self.ocx.register_obligation(traits::Obligation::new(
+            cause,
+            self.param_env,
+            ty::Binder::dummy(ty::PredicateKind::WellFormed(arg)).to_predicate(self.tcx()),
+        ));
     }
 }
 
+pub(super) fn enter_wf_checking_ctxt<'tcx, F>(
+    tcx: TyCtxt<'tcx>,
+    span: Span,
+    body_def_id: LocalDefId,
+    f: F,
+) where
+    F: for<'a> FnOnce(&WfCheckingCtxt<'a, 'tcx>) -> FxHashSet<Ty<'tcx>>,
+{
+    let param_env = tcx.param_env(body_def_id);
+    let body_id = tcx.hir().local_def_id_to_hir_id(body_def_id);
+    tcx.infer_ctxt().enter(|ref infcx| {
+        let ocx = ObligationCtxt::new(infcx);
+        let mut wfcx = WfCheckingCtxt { ocx, span, body_id, param_env };
+
+        if !tcx.features().trivial_bounds {
+            wfcx.check_false_global_bounds()
+        }
+        let wf_tys = f(&mut wfcx);
+        let errors = wfcx.select_all_or_error();
+        if !errors.is_empty() {
+            infcx.report_fulfillment_errors(&errors, None, false);
+            return;
+        }
+
+        let mut outlives_environment = OutlivesEnvironment::new(param_env);
+        outlives_environment.add_implied_bounds(infcx, wf_tys, body_id);
+        infcx.check_region_obligations_and_report_errors(body_def_id, &outlives_environment);
+    })
+}
+
 fn check_well_formed(tcx: TyCtxt<'_>, def_id: LocalDefId) {
     let node = tcx.hir().expect_owner(def_id);
     match node {
@@ -142,7 +182,7 @@ fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) {
             // We match on both `ty::ImplPolarity` and `ast::ImplPolarity` just to get the `!` span.
             match (tcx.impl_polarity(def_id), impl_.polarity) {
                 (ty::ImplPolarity::Positive, _) => {
-                    check_impl(tcx, item, impl_.self_ty, &impl_.of_trait);
+                    check_impl(tcx, item, impl_.self_ty, &impl_.of_trait, impl_.constness);
                 }
                 (ty::ImplPolarity::Negative, ast::ImplPolarity::Negative(span)) => {
                     // FIXME(#27579): what amount of WF checking do we need for neg impls?
@@ -174,17 +214,17 @@ fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) {
             check_item_type(tcx, item.def_id, ty.span, false);
         }
         hir::ItemKind::Struct(ref struct_def, ref ast_generics) => {
-            check_type_defn(tcx, item, false, |fcx| vec![fcx.non_enum_variant(struct_def)]);
+            check_type_defn(tcx, item, false, |wfcx| vec![wfcx.non_enum_variant(struct_def)]);
 
             check_variances_for_type_defn(tcx, item, ast_generics);
         }
         hir::ItemKind::Union(ref struct_def, ref ast_generics) => {
-            check_type_defn(tcx, item, true, |fcx| vec![fcx.non_enum_variant(struct_def)]);
+            check_type_defn(tcx, item, true, |wfcx| vec![wfcx.non_enum_variant(struct_def)]);
 
             check_variances_for_type_defn(tcx, item, ast_generics);
         }
         hir::ItemKind::Enum(ref enum_def, ref ast_generics) => {
-            check_type_defn(tcx, item, true, |fcx| fcx.enum_variants(enum_def));
+            check_type_defn(tcx, item, true, |wfcx| wfcx.enum_variants(enum_def));
 
             check_variances_for_type_defn(tcx, item, ast_generics);
         }
@@ -433,7 +473,7 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe
         unsatisfied_bounds.sort();
 
         if !unsatisfied_bounds.is_empty() {
-            let plural = if unsatisfied_bounds.len() > 1 { "s" } else { "" };
+            let plural = pluralize!(unsatisfied_bounds.len());
             let mut err = tcx.sess.struct_span_err(
                 gat_item_hir.span,
                 &format!("missing required bound{} on `{}`", plural, gat_item_hir.ident),
@@ -648,10 +688,7 @@ fn resolve_regions_with_wf_tys<'tcx>(
     id: hir::HirId,
     param_env: ty::ParamEnv<'tcx>,
     wf_tys: &FxHashSet<Ty<'tcx>>,
-    add_constraints: impl for<'a> FnOnce(
-        &'a InferCtxt<'a, 'tcx>,
-        &'a Vec<(ty::Region<'tcx>, GenericKind<'tcx>)>,
-    ),
+    add_constraints: impl for<'a> FnOnce(&'a InferCtxt<'a, 'tcx>, &'a RegionBoundPairs<'tcx>),
 ) -> bool {
     // Unfortunately, we have to use a new `InferCtxt` each call, because
     // region constraints get added and solved there and we need to test each
@@ -807,29 +844,13 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) {
             let ty = tcx.type_of(tcx.hir().local_def_id(param.hir_id));
 
             if tcx.features().adt_const_params {
-                let err = match ty.peel_refs().kind() {
-                    ty::FnPtr(_) => Some("function pointers"),
-                    ty::RawPtr(_) => Some("raw pointers"),
-                    _ => None,
-                };
-
-                if let Some(unsupported_type) = err {
-                    tcx.sess.span_err(
-                        hir_ty.span,
-                        &format!(
-                            "using {} as const generic parameters is forbidden",
-                            unsupported_type
-                        ),
-                    );
-                }
-
                 if let Some(non_structural_match_ty) =
-                    traits::search_for_structural_match_violation(param.span, tcx, ty, false)
+                    traits::search_for_adt_const_param_violation(param.span, tcx, ty)
                 {
                     // We use the same error code in both branches, because this is really the same
                     // issue: we just special-case the message for type parameters to make it
                     // clearer.
-                    match ty.peel_refs().kind() {
+                    match non_structural_match_ty.kind() {
                         ty::Param(_) => {
                             // Const parameters may not have type parameters as their types,
                             // because we cannot be sure that the type parameter derives `PartialEq`
@@ -861,6 +882,24 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) {
                             .note("floats do not derive `Eq` or `Ord`, which are required for const parameters")
                             .emit();
                         }
+                        ty::FnPtr(_) => {
+                            struct_span_err!(
+                                tcx.sess,
+                                hir_ty.span,
+                                E0741,
+                                "using function pointers as const generic parameters is forbidden",
+                            )
+                            .emit();
+                        }
+                        ty::RawPtr(_) => {
+                            struct_span_err!(
+                                tcx.sess,
+                                hir_ty.span,
+                                E0741,
+                                "using raw pointers as const generic parameters is forbidden",
+                            )
+                            .emit();
+                        }
                         _ => {
                             let mut diag = struct_span_err!(
                                 tcx.sess,
@@ -868,10 +907,10 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) {
                                 E0741,
                                 "`{}` must be annotated with `#[derive(PartialEq, Eq)]` to be used as \
                                 the type of a const parameter",
-                                non_structural_match_ty.ty,
+                                non_structural_match_ty,
                             );
 
-                            if ty == non_structural_match_ty.ty {
+                            if ty == non_structural_match_ty {
                                 diag.span_label(
                                     hir_ty.span,
                                     format!("`{ty}` doesn't derive both `PartialEq` and `Eq`"),
@@ -933,45 +972,45 @@ fn check_associated_item(
     span: Span,
     sig_if_method: Option<&hir::FnSig<'_>>,
 ) {
-    let code = ObligationCauseCode::WellFormed(Some(WellFormedLoc::Ty(item_id)));
-    for_id(tcx, item_id, span).with_fcx(|fcx| {
-        let item = fcx.tcx.associated_item(item_id);
+    let loc = Some(WellFormedLoc::Ty(item_id));
+    enter_wf_checking_ctxt(tcx, span, item_id, |wfcx| {
+        let item = tcx.associated_item(item_id);
 
         let (mut implied_bounds, self_ty) = match item.container {
-            ty::TraitContainer(_) => (FxHashSet::default(), fcx.tcx.types.self_param),
-            ty::ImplContainer(def_id) => {
-                (fcx.impl_implied_bounds(def_id, span), fcx.tcx.type_of(def_id))
-            }
+            ty::TraitContainer(_) => (FxHashSet::default(), tcx.types.self_param),
+            ty::ImplContainer(def_id) => (
+                impl_implied_bounds(tcx, wfcx.param_env, def_id.expect_local(), span),
+                tcx.type_of(def_id),
+            ),
         };
 
         match item.kind {
             ty::AssocKind::Const => {
-                let ty = fcx.tcx.type_of(item.def_id);
-                let ty = fcx.normalize_associated_types_in_wf(span, ty, WellFormedLoc::Ty(item_id));
-                fcx.register_wf_obligation(ty.into(), span, code.clone());
+                let ty = tcx.type_of(item.def_id);
+                let ty = wfcx.normalize(span, Some(WellFormedLoc::Ty(item_id)), ty);
+                wfcx.register_wf_obligation(span, loc, ty.into());
             }
             ty::AssocKind::Fn => {
-                let sig = fcx.tcx.fn_sig(item.def_id);
+                let sig = tcx.fn_sig(item.def_id);
                 let hir_sig = sig_if_method.expect("bad signature for method");
                 check_fn_or_method(
-                    fcx,
-                    item.ident(fcx.tcx).span,
+                    wfcx,
+                    item.ident(tcx).span,
                     sig,
                     hir_sig.decl,
                     item.def_id.expect_local(),
                     &mut implied_bounds,
                 );
-                check_method_receiver(fcx, hir_sig, item, self_ty);
+                check_method_receiver(wfcx, hir_sig, item, self_ty);
             }
             ty::AssocKind::Type => {
                 if let ty::AssocItemContainer::TraitContainer(_) = item.container {
-                    check_associated_type_bounds(fcx, item, span)
+                    check_associated_type_bounds(wfcx, item, span)
                 }
                 if item.defaultness.has_value() {
-                    let ty = fcx.tcx.type_of(item.def_id);
-                    let ty =
-                        fcx.normalize_associated_types_in_wf(span, ty, WellFormedLoc::Ty(item_id));
-                    fcx.register_wf_obligation(ty.into(), span, code.clone());
+                    let ty = tcx.type_of(item.def_id);
+                    let ty = wfcx.normalize(span, Some(WellFormedLoc::Ty(item_id)), ty);
+                    wfcx.register_wf_obligation(span, loc, ty.into());
                 }
             }
         }
@@ -980,19 +1019,6 @@ fn check_associated_item(
     })
 }
 
-pub(super) fn for_item<'tcx>(tcx: TyCtxt<'tcx>, item: &hir::Item<'_>) -> CheckWfFcxBuilder<'tcx> {
-    for_id(tcx, item.def_id, item.span)
-}
-
-fn for_id(tcx: TyCtxt<'_>, def_id: LocalDefId, span: Span) -> CheckWfFcxBuilder<'_> {
-    CheckWfFcxBuilder {
-        inherited: Inherited::build(tcx, def_id),
-        id: hir::HirId::make_owner(def_id),
-        span,
-        param_env: tcx.param_env(def_id),
-    }
-}
-
 fn item_adt_kind(kind: &ItemKind<'_>) -> Option<AdtKind> {
     match kind {
         ItemKind::Struct(..) => Some(AdtKind::Struct),
@@ -1009,19 +1035,19 @@ fn check_type_defn<'tcx, F>(
     all_sized: bool,
     mut lookup_fields: F,
 ) where
-    F: for<'fcx> FnMut(&FnCtxt<'fcx, 'tcx>) -> Vec<AdtVariant<'tcx>>,
+    F: FnMut(&WfCheckingCtxt<'_, 'tcx>) -> Vec<AdtVariant<'tcx>>,
 {
-    for_item(tcx, item).with_fcx(|fcx| {
-        let variants = lookup_fields(fcx);
+    enter_wf_checking_ctxt(tcx, item.span, item.def_id, |wfcx| {
+        let variants = lookup_fields(wfcx);
         let packed = tcx.adt_def(item.def_id).repr().packed();
 
         for variant in &variants {
             // All field types must be well-formed.
             for field in &variant.fields {
-                fcx.register_wf_obligation(
-                    field.ty.into(),
+                wfcx.register_wf_obligation(
                     field.span,
-                    ObligationCauseCode::WellFormed(Some(WellFormedLoc::Ty(field.def_id))),
+                    Some(WellFormedLoc::Ty(field.def_id)),
+                    field.ty.into(),
                 )
             }
 
@@ -1048,12 +1074,10 @@ fn check_type_defn<'tcx, F>(
                 variant.fields[..variant.fields.len() - unsized_len].iter().enumerate()
             {
                 let last = idx == variant.fields.len() - 1;
-                fcx.register_bound(
-                    field.ty,
-                    tcx.require_lang_item(LangItem::Sized, None),
+                wfcx.register_bound(
                     traits::ObligationCause::new(
                         field.span,
-                        fcx.body_id,
+                        wfcx.body_id,
                         traits::FieldSized {
                             adt_kind: match item_adt_kind(&item.kind) {
                                 Some(i) => i,
@@ -1063,6 +1087,9 @@ fn check_type_defn<'tcx, F>(
                             last,
                         },
                     ),
+                    wfcx.param_env,
+                    field.ty,
+                    tcx.require_lang_item(LangItem::Sized, None),
                 );
             }
 
@@ -1072,12 +1099,12 @@ fn check_type_defn<'tcx, F>(
 
                 let cause = traits::ObligationCause::new(
                     tcx.def_span(discr_def_id),
-                    fcx.body_id,
+                    wfcx.body_id,
                     traits::MiscObligation,
                 );
-                fcx.register_predicate(traits::Obligation::new(
+                wfcx.register_obligation(traits::Obligation::new(
                     cause,
-                    fcx.param_env,
+                    wfcx.param_env,
                     ty::Binder::dummy(ty::PredicateKind::ConstEvaluatable(ty::Unevaluated::new(
                         ty::WithOptConstParam::unknown(discr_def_id.to_def_id()),
                         discr_substs,
@@ -1087,7 +1114,7 @@ fn check_type_defn<'tcx, F>(
             }
         }
 
-        check_where_clauses(fcx, item.span, item.def_id, None);
+        check_where_clauses(wfcx, item.span, item.def_id);
 
         // No implied bounds in a struct definition.
         FxHashSet::default()
@@ -1113,9 +1140,8 @@ fn check_trait(tcx: TyCtxt<'_>, item: &hir::Item<'_>) {
         }
     }
 
-    // FIXME: this shouldn't use an `FnCtxt` at all.
-    for_item(tcx, item).with_fcx(|fcx| {
-        check_where_clauses(fcx, item.span, item.def_id, None);
+    enter_wf_checking_ctxt(tcx, item.span, item.def_id, |wfcx| {
+        check_where_clauses(wfcx, item.span, item.def_id);
 
         FxHashSet::default()
     });
@@ -1130,27 +1156,22 @@ fn check_trait(tcx: TyCtxt<'_>, item: &hir::Item<'_>) {
 ///
 /// Assuming the defaults are used, check that all predicates (bounds on the
 /// assoc type and where clauses on the trait) hold.
-fn check_associated_type_bounds(fcx: &FnCtxt<'_, '_>, item: &ty::AssocItem, span: Span) {
-    let tcx = fcx.tcx;
-
-    let bounds = tcx.explicit_item_bounds(item.def_id);
+fn check_associated_type_bounds(wfcx: &WfCheckingCtxt<'_, '_>, item: &ty::AssocItem, span: Span) {
+    let bounds = wfcx.tcx().explicit_item_bounds(item.def_id);
 
     debug!("check_associated_type_bounds: bounds={:?}", bounds);
     let wf_obligations = bounds.iter().flat_map(|&(bound, bound_span)| {
-        let normalized_bound = fcx.normalize_associated_types_in(span, bound);
+        let normalized_bound = wfcx.normalize(span, None, bound);
         traits::wf::predicate_obligations(
-            fcx,
-            fcx.param_env,
-            fcx.body_id,
+            wfcx.infcx,
+            wfcx.param_env,
+            wfcx.body_id,
             normalized_bound,
             bound_span,
         )
     });
 
-    for obligation in wf_obligations {
-        debug!("next obligation cause: {:?}", obligation.cause);
-        fcx.register_predicate(obligation);
-    }
+    wfcx.register_obligations(wf_obligations);
 }
 
 fn check_item_fn(
@@ -1160,10 +1181,10 @@ fn check_item_fn(
     span: Span,
     decl: &hir::FnDecl<'_>,
 ) {
-    for_id(tcx, def_id, span).with_fcx(|fcx| {
+    enter_wf_checking_ctxt(tcx, span, def_id, |wfcx| {
         let sig = tcx.fn_sig(def_id);
         let mut implied_bounds = FxHashSet::default();
-        check_fn_or_method(fcx, ident.span, sig, decl, def_id, &mut implied_bounds);
+        check_fn_or_method(wfcx, ident.span, sig, decl, def_id, &mut implied_bounds);
         implied_bounds
     })
 }
@@ -1171,28 +1192,25 @@ fn check_item_fn(
 fn check_item_type(tcx: TyCtxt<'_>, item_id: LocalDefId, ty_span: Span, allow_foreign_ty: bool) {
     debug!("check_item_type: {:?}", item_id);
 
-    for_id(tcx, item_id, ty_span).with_fcx(|fcx| {
+    enter_wf_checking_ctxt(tcx, ty_span, item_id, |wfcx| {
         let ty = tcx.type_of(item_id);
-        let item_ty = fcx.normalize_associated_types_in_wf(ty_span, ty, WellFormedLoc::Ty(item_id));
+        let item_ty = wfcx.normalize(ty_span, Some(WellFormedLoc::Ty(item_id)), ty);
 
         let mut forbid_unsized = true;
         if allow_foreign_ty {
-            let tail = fcx.tcx.struct_tail_erasing_lifetimes(item_ty, fcx.param_env);
+            let tail = tcx.struct_tail_erasing_lifetimes(item_ty, wfcx.param_env);
             if let ty::Foreign(_) = tail.kind() {
                 forbid_unsized = false;
             }
         }
 
-        fcx.register_wf_obligation(
-            item_ty.into(),
-            ty_span,
-            ObligationCauseCode::WellFormed(Some(WellFormedLoc::Ty(item_id))),
-        );
+        wfcx.register_wf_obligation(ty_span, Some(WellFormedLoc::Ty(item_id)), item_ty.into());
         if forbid_unsized {
-            fcx.register_bound(
+            wfcx.register_bound(
+                traits::ObligationCause::new(ty_span, wfcx.body_id, traits::WellFormed(None)),
+                wfcx.param_env,
                 item_ty,
                 tcx.require_lang_item(LangItem::Sized, None),
-                traits::ObligationCause::new(ty_span, fcx.body_id, traits::WellFormed(None)),
             );
         }
 
@@ -1203,10 +1221,11 @@ fn check_item_type(tcx: TyCtxt<'_>, item_id: LocalDefId, ty_span: Span, allow_fo
             && !tcx.is_thread_local_static(item_id.to_def_id());
 
         if should_check_for_sync {
-            fcx.register_bound(
+            wfcx.register_bound(
+                traits::ObligationCause::new(ty_span, wfcx.body_id, traits::SharedStatic),
+                wfcx.param_env,
                 item_ty,
                 tcx.require_lang_item(LangItem::Sync, Some(ty_span)),
-                traits::ObligationCause::new(ty_span, fcx.body_id, traits::SharedStatic),
             );
         }
 
@@ -1221,57 +1240,57 @@ fn check_impl<'tcx>(
     item: &'tcx hir::Item<'tcx>,
     ast_self_ty: &hir::Ty<'_>,
     ast_trait_ref: &Option<hir::TraitRef<'_>>,
+    constness: hir::Constness,
 ) {
-    for_item(tcx, item).with_fcx(|fcx| {
+    enter_wf_checking_ctxt(tcx, item.span, item.def_id, |wfcx| {
         match *ast_trait_ref {
             Some(ref ast_trait_ref) => {
                 // `#[rustc_reservation_impl]` impls are not real impls and
                 // therefore don't need to be WF (the trait's `Self: Trait` predicate
                 // won't hold).
                 let trait_ref = tcx.impl_trait_ref(item.def_id).unwrap();
-                let trait_ref =
-                    fcx.normalize_associated_types_in(ast_trait_ref.path.span, trait_ref);
+                let trait_ref = wfcx.normalize(ast_trait_ref.path.span, None, trait_ref);
+                let trait_pred = ty::TraitPredicate {
+                    trait_ref,
+                    constness: match constness {
+                        hir::Constness::Const => ty::BoundConstness::ConstIfConst,
+                        hir::Constness::NotConst => ty::BoundConstness::NotConst,
+                    },
+                    polarity: ty::ImplPolarity::Positive,
+                };
                 let obligations = traits::wf::trait_obligations(
-                    fcx,
-                    fcx.param_env,
-                    fcx.body_id,
-                    &trait_ref,
+                    wfcx.infcx,
+                    wfcx.param_env,
+                    wfcx.body_id,
+                    &trait_pred,
                     ast_trait_ref.path.span,
                     item,
                 );
                 debug!(?obligations);
-                for obligation in obligations {
-                    fcx.register_predicate(obligation);
-                }
+                wfcx.register_obligations(obligations);
             }
             None => {
                 let self_ty = tcx.type_of(item.def_id);
-                let self_ty = fcx.normalize_associated_types_in(item.span, self_ty);
-                fcx.register_wf_obligation(
-                    self_ty.into(),
+                let self_ty = wfcx.normalize(item.span, None, self_ty);
+                wfcx.register_wf_obligation(
                     ast_self_ty.span,
-                    ObligationCauseCode::WellFormed(Some(WellFormedLoc::Ty(
-                        item.hir_id().expect_owner(),
-                    ))),
+                    Some(WellFormedLoc::Ty(item.hir_id().expect_owner())),
+                    self_ty.into(),
                 );
             }
         }
 
-        check_where_clauses(fcx, item.span, item.def_id, None);
+        check_where_clauses(wfcx, item.span, item.def_id);
 
-        fcx.impl_implied_bounds(item.def_id.to_def_id(), item.span)
+        impl_implied_bounds(tcx, wfcx.param_env, item.def_id, item.span)
     });
 }
 
 /// Checks where-clauses and inline bounds that are declared on `def_id`.
-#[instrument(skip(fcx), level = "debug")]
-fn check_where_clauses<'tcx, 'fcx>(
-    fcx: &FnCtxt<'fcx, 'tcx>,
-    span: Span,
-    def_id: LocalDefId,
-    return_ty: Option<(Ty<'tcx>, Span)>,
-) {
-    let tcx = fcx.tcx;
+#[instrument(level = "debug", skip(wfcx))]
+fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, span: Span, def_id: LocalDefId) {
+    let infcx = wfcx.infcx;
+    let tcx = wfcx.tcx();
 
     let predicates = tcx.predicates_of(def_id);
     let generics = tcx.generics_of(def_id);
@@ -1299,11 +1318,7 @@ fn check_where_clauses<'tcx, 'fcx>(
                     // parameter includes another (e.g., `<T, U = T>`). In those cases, we can't
                     // be sure if it will error or not as user might always specify the other.
                     if !ty.needs_subst() {
-                        fcx.register_wf_obligation(
-                            ty.into(),
-                            tcx.def_span(param.def_id),
-                            ObligationCauseCode::MiscObligation,
-                        );
+                        wfcx.register_wf_obligation(tcx.def_span(param.def_id), None, ty.into());
                     }
                 }
             }
@@ -1315,10 +1330,10 @@ fn check_where_clauses<'tcx, 'fcx>(
                     // we should eagerly error.
                     let default_ct = tcx.const_param_default(param.def_id);
                     if !default_ct.needs_subst() {
-                        fcx.register_wf_obligation(
-                            default_ct.into(),
+                        wfcx.register_wf_obligation(
                             tcx.def_span(param.def_id),
-                            ObligationCauseCode::WellFormed(None),
+                            None,
+                            default_ct.into(),
                         );
                     }
                 }
@@ -1429,48 +1444,41 @@ fn check_where_clauses<'tcx, 'fcx>(
             // Note the subtle difference from how we handle `predicates`
             // below: there, we are not trying to prove those predicates
             // to be *true* but merely *well-formed*.
-            let pred = fcx.normalize_associated_types_in(sp, pred);
+            let pred = wfcx.normalize(sp, None, pred);
             let cause = traits::ObligationCause::new(
                 sp,
-                fcx.body_id,
+                wfcx.body_id,
                 traits::ItemObligation(def_id.to_def_id()),
             );
-            traits::Obligation::new(cause, fcx.param_env, pred)
+            traits::Obligation::new(cause, wfcx.param_env, pred)
         });
 
     let predicates = predicates.instantiate_identity(tcx);
 
-    if let Some((return_ty, _)) = return_ty {
-        if return_ty.has_infer_types_or_consts() {
-            fcx.select_obligations_where_possible(false, |_| {});
-        }
-    }
-
-    let predicates = fcx.normalize_associated_types_in(span, predicates);
+    let predicates = wfcx.normalize(span, None, predicates);
 
     debug!(?predicates.predicates);
     assert_eq!(predicates.predicates.len(), predicates.spans.len());
     let wf_obligations =
         iter::zip(&predicates.predicates, &predicates.spans).flat_map(|(&p, &sp)| {
-            traits::wf::predicate_obligations(fcx, fcx.param_env, fcx.body_id, p, sp)
+            traits::wf::predicate_obligations(infcx, wfcx.param_env, wfcx.body_id, p, sp)
         });
 
-    for obligation in wf_obligations.chain(default_obligations) {
-        debug!("next obligation cause: {:?}", obligation.cause);
-        fcx.register_predicate(obligation);
-    }
+    let obligations: Vec<_> = wf_obligations.chain(default_obligations).collect();
+    wfcx.register_obligations(obligations);
 }
 
-#[tracing::instrument(level = "debug", skip(fcx, span, hir_decl))]
-fn check_fn_or_method<'fcx, 'tcx>(
-    fcx: &FnCtxt<'fcx, 'tcx>,
+#[tracing::instrument(level = "debug", skip(wfcx, span, hir_decl))]
+fn check_fn_or_method<'tcx>(
+    wfcx: &WfCheckingCtxt<'_, 'tcx>,
     span: Span,
     sig: ty::PolyFnSig<'tcx>,
     hir_decl: &hir::FnDecl<'_>,
     def_id: LocalDefId,
     implied_bounds: &mut FxHashSet<Ty<'tcx>>,
 ) {
-    let sig = fcx.tcx.liberate_late_bound_regions(def_id.to_def_id(), sig);
+    let tcx = wfcx.tcx();
+    let sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), sig);
 
     // Normalize the input and output types one at a time, using a different
     // `WellFormedLoc` for each. We cannot call `normalize_associated_types`
@@ -1478,70 +1486,61 @@ fn check_fn_or_method<'fcx, 'tcx>(
     // for each type, preventing the HIR wf check from generating
     // a nice error message.
     let ty::FnSig { mut inputs_and_output, c_variadic, unsafety, abi } = sig;
-    inputs_and_output =
-        fcx.tcx.mk_type_list(inputs_and_output.iter().enumerate().map(|(i, ty)| {
-            fcx.normalize_associated_types_in_wf(
-                span,
-                ty,
-                WellFormedLoc::Param {
-                    function: def_id,
-                    // Note that the `param_idx` of the output type is
-                    // one greater than the index of the last input type.
-                    param_idx: i.try_into().unwrap(),
-                },
-            )
-        }));
+    inputs_and_output = tcx.mk_type_list(inputs_and_output.iter().enumerate().map(|(i, ty)| {
+        wfcx.normalize(
+            span,
+            Some(WellFormedLoc::Param {
+                function: def_id,
+                // Note that the `param_idx` of the output type is
+                // one greater than the index of the last input type.
+                param_idx: i.try_into().unwrap(),
+            }),
+            ty,
+        )
+    }));
     // Manually call `normalize_associated_types_in` on the other types
     // in `FnSig`. This ensures that if the types of these fields
     // ever change to include projections, we will start normalizing
     // them automatically.
     let sig = ty::FnSig {
         inputs_and_output,
-        c_variadic: fcx.normalize_associated_types_in(span, c_variadic),
-        unsafety: fcx.normalize_associated_types_in(span, unsafety),
-        abi: fcx.normalize_associated_types_in(span, abi),
+        c_variadic: wfcx.normalize(span, None, c_variadic),
+        unsafety: wfcx.normalize(span, None, unsafety),
+        abi: wfcx.normalize(span, None, abi),
     };
 
     for (i, (&input_ty, ty)) in iter::zip(sig.inputs(), hir_decl.inputs).enumerate() {
-        fcx.register_wf_obligation(
-            input_ty.into(),
+        wfcx.register_wf_obligation(
             ty.span,
-            ObligationCauseCode::WellFormed(Some(WellFormedLoc::Param {
-                function: def_id,
-                param_idx: i.try_into().unwrap(),
-            })),
+            Some(WellFormedLoc::Param { function: def_id, param_idx: i.try_into().unwrap() }),
+            input_ty.into(),
         );
     }
 
     implied_bounds.extend(sig.inputs());
 
-    fcx.register_wf_obligation(
-        sig.output().into(),
-        hir_decl.output.span(),
-        ObligationCauseCode::ReturnType,
-    );
+    wfcx.register_wf_obligation(hir_decl.output.span(), None, sig.output().into());
 
     // FIXME(#27579) return types should not be implied bounds
     implied_bounds.insert(sig.output());
 
     debug!(?implied_bounds);
 
-    check_where_clauses(fcx, span, def_id, Some((sig.output(), hir_decl.output.span())));
+    check_where_clauses(wfcx, span, def_id);
 }
 
 const HELP_FOR_SELF_TYPE: &str = "consider changing to `self`, `&self`, `&mut self`, `self: Box<Self>`, \
      `self: Rc<Self>`, `self: Arc<Self>`, or `self: Pin<P>` (where P is one \
      of the previous types except `Self`)";
 
-#[tracing::instrument(level = "debug", skip(fcx))]
-fn check_method_receiver<'fcx, 'tcx>(
-    fcx: &FnCtxt<'fcx, 'tcx>,
+#[tracing::instrument(level = "debug", skip(wfcx))]
+fn check_method_receiver<'tcx>(
+    wfcx: &WfCheckingCtxt<'_, 'tcx>,
     fn_sig: &hir::FnSig<'_>,
     method: &ty::AssocItem,
     self_ty: Ty<'tcx>,
 ) {
-    // Check that the method has a valid receiver type, given the type `Self`.
-    debug!("check_method_receiver({:?}, self_ty={:?})", method, self_ty);
+    let tcx = wfcx.tcx();
 
     if !method.fn_has_self_parameter {
         return;
@@ -1549,28 +1548,28 @@ fn check_method_receiver<'fcx, 'tcx>(
 
     let span = fn_sig.decl.inputs[0].span;
 
-    let sig = fcx.tcx.fn_sig(method.def_id);
-    let sig = fcx.tcx.liberate_late_bound_regions(method.def_id, sig);
-    let sig = fcx.normalize_associated_types_in(span, sig);
+    let sig = tcx.fn_sig(method.def_id);
+    let sig = tcx.liberate_late_bound_regions(method.def_id, sig);
+    let sig = wfcx.normalize(span, None, sig);
 
     debug!("check_method_receiver: sig={:?}", sig);
 
-    let self_ty = fcx.normalize_associated_types_in(span, self_ty);
+    let self_ty = wfcx.normalize(span, None, self_ty);
 
     let receiver_ty = sig.inputs()[0];
-    let receiver_ty = fcx.normalize_associated_types_in(span, receiver_ty);
+    let receiver_ty = wfcx.normalize(span, None, receiver_ty);
 
-    if fcx.tcx.features().arbitrary_self_types {
-        if !receiver_is_valid(fcx, span, receiver_ty, self_ty, true) {
+    if tcx.features().arbitrary_self_types {
+        if !receiver_is_valid(wfcx, span, receiver_ty, self_ty, true) {
             // Report error; `arbitrary_self_types` was enabled.
-            e0307(fcx, span, receiver_ty);
+            e0307(tcx, span, receiver_ty);
         }
     } else {
-        if !receiver_is_valid(fcx, span, receiver_ty, self_ty, false) {
-            if receiver_is_valid(fcx, span, receiver_ty, self_ty, true) {
+        if !receiver_is_valid(wfcx, span, receiver_ty, self_ty, false) {
+            if receiver_is_valid(wfcx, span, receiver_ty, self_ty, true) {
                 // Report error; would have worked with `arbitrary_self_types`.
                 feature_err(
-                    &fcx.tcx.sess.parse_sess,
+                    &tcx.sess.parse_sess,
                     sym::arbitrary_self_types,
                     span,
                     &format!(
@@ -1582,15 +1581,15 @@ fn check_method_receiver<'fcx, 'tcx>(
                 .emit();
             } else {
                 // Report error; would not have worked with `arbitrary_self_types`.
-                e0307(fcx, span, receiver_ty);
+                e0307(tcx, span, receiver_ty);
             }
         }
     }
 }
 
-fn e0307<'tcx>(fcx: &FnCtxt<'_, 'tcx>, span: Span, receiver_ty: Ty<'_>) {
+fn e0307<'tcx>(tcx: TyCtxt<'tcx>, span: Span, receiver_ty: Ty<'_>) {
     struct_span_err!(
-        fcx.tcx.sess.diagnostic(),
+        tcx.sess.diagnostic(),
         span,
         E0307,
         "invalid `self` parameter type: {receiver_ty}"
@@ -1609,26 +1608,30 @@ fn e0307<'tcx>(fcx: &FnCtxt<'_, 'tcx>, span: Span, receiver_ty: Ty<'_>) {
 /// N.B., there are cases this function returns `true` but causes an error to be emitted,
 /// particularly when `receiver_ty` derefs to a type that is the same as `self_ty` but has the
 /// wrong lifetime. Be careful of this if you are calling this function speculatively.
-fn receiver_is_valid<'fcx, 'tcx>(
-    fcx: &FnCtxt<'fcx, 'tcx>,
+fn receiver_is_valid<'tcx>(
+    wfcx: &WfCheckingCtxt<'_, 'tcx>,
     span: Span,
     receiver_ty: Ty<'tcx>,
     self_ty: Ty<'tcx>,
     arbitrary_self_types_enabled: bool,
 ) -> bool {
-    let cause = fcx.cause(span, traits::ObligationCauseCode::MethodReceiver);
+    let infcx = wfcx.infcx;
+    let tcx = wfcx.tcx();
+    let cause =
+        ObligationCause::new(span, wfcx.body_id, traits::ObligationCauseCode::MethodReceiver);
 
-    let can_eq_self = |ty| fcx.infcx.can_eq(fcx.param_env, self_ty, ty).is_ok();
+    let can_eq_self = |ty| infcx.can_eq(wfcx.param_env, self_ty, ty).is_ok();
 
     // `self: Self` is always valid.
     if can_eq_self(receiver_ty) {
-        if let Some(mut err) = fcx.demand_eqtype_with_origin(&cause, self_ty, receiver_ty) {
-            err.emit();
+        if let Err(err) = wfcx.equate_types(&cause, wfcx.param_env, self_ty, receiver_ty) {
+            infcx.report_mismatched_types(&cause, self_ty, receiver_ty, err).emit();
         }
         return true;
     }
 
-    let mut autoderef = fcx.autoderef(span, receiver_ty);
+    let mut autoderef =
+        Autoderef::new(infcx, wfcx.param_env, wfcx.body_id, span, receiver_ty, span);
 
     // The `arbitrary_self_types` feature allows raw pointer receivers like `self: *const Self`.
     if arbitrary_self_types_enabled {
@@ -1638,7 +1641,7 @@ fn receiver_is_valid<'fcx, 'tcx>(
     // The first type is `receiver_ty`, which we know its not equal to `self_ty`; skip it.
     autoderef.next();
 
-    let receiver_trait_def_id = fcx.tcx.require_lang_item(LangItem::Receiver, None);
+    let receiver_trait_def_id = tcx.require_lang_item(LangItem::Receiver, None);
 
     // Keep dereferencing `receiver_ty` until we get to `self_ty`.
     loop {
@@ -1649,12 +1652,12 @@ fn receiver_is_valid<'fcx, 'tcx>(
             );
 
             if can_eq_self(potential_self_ty) {
-                fcx.register_predicates(autoderef.into_obligations());
+                wfcx.register_obligations(autoderef.into_obligations());
 
-                if let Some(mut err) =
-                    fcx.demand_eqtype_with_origin(&cause, self_ty, potential_self_ty)
+                if let Err(err) =
+                    wfcx.equate_types(&cause, wfcx.param_env, self_ty, potential_self_ty)
                 {
-                    err.emit();
+                    infcx.report_mismatched_types(&cause, self_ty, potential_self_ty, err).emit();
                 }
 
                 break;
@@ -1663,7 +1666,7 @@ fn receiver_is_valid<'fcx, 'tcx>(
                 // deref chain implement `receiver`
                 if !arbitrary_self_types_enabled
                     && !receiver_is_implemented(
-                        fcx,
+                        wfcx,
                         receiver_trait_def_id,
                         cause.clone(),
                         potential_self_ty,
@@ -1682,7 +1685,7 @@ fn receiver_is_valid<'fcx, 'tcx>(
 
     // Without `feature(arbitrary_self_types)`, we require that `receiver_ty` implements `Receiver`.
     if !arbitrary_self_types_enabled
-        && !receiver_is_implemented(fcx, receiver_trait_def_id, cause.clone(), receiver_ty)
+        && !receiver_is_implemented(wfcx, receiver_trait_def_id, cause.clone(), receiver_ty)
     {
         return false;
     }
@@ -1691,23 +1694,21 @@ fn receiver_is_valid<'fcx, 'tcx>(
 }
 
 fn receiver_is_implemented<'tcx>(
-    fcx: &FnCtxt<'_, 'tcx>,
+    wfcx: &WfCheckingCtxt<'_, 'tcx>,
     receiver_trait_def_id: DefId,
     cause: ObligationCause<'tcx>,
     receiver_ty: Ty<'tcx>,
 ) -> bool {
+    let tcx = wfcx.tcx();
     let trait_ref = ty::Binder::dummy(ty::TraitRef {
         def_id: receiver_trait_def_id,
-        substs: fcx.tcx.mk_substs_trait(receiver_ty, &[]),
+        substs: tcx.mk_substs_trait(receiver_ty, &[]),
     });
 
-    let obligation = traits::Obligation::new(
-        cause,
-        fcx.param_env,
-        trait_ref.without_const().to_predicate(fcx.tcx),
-    );
+    let obligation =
+        traits::Obligation::new(cause, wfcx.param_env, trait_ref.without_const().to_predicate(tcx));
 
-    if fcx.predicate_must_hold_modulo_regions(&obligation) {
+    if wfcx.infcx.predicate_must_hold_modulo_regions(&obligation) {
         true
     } else {
         debug!(
@@ -1809,55 +1810,56 @@ fn report_bivariance(
     err.emit()
 }
 
-/// Feature gates RFC 2056 -- trivial bounds, checking for global bounds that
-/// aren't true.
-fn check_false_global_bounds(fcx: &FnCtxt<'_, '_>, mut span: Span, id: hir::HirId) {
-    let empty_env = ty::ParamEnv::empty();
-
-    let def_id = fcx.tcx.hir().local_def_id(id);
-    let predicates_with_span =
-        fcx.tcx.predicates_of(def_id).predicates.iter().map(|(p, span)| (*p, *span));
-    // Check elaborated bounds.
-    let implied_obligations = traits::elaborate_predicates_with_span(fcx.tcx, predicates_with_span);
-
-    for obligation in implied_obligations {
-        // We lower empty bounds like `Vec<dyn Copy>:` as
-        // `WellFormed(Vec<dyn Copy>)`, which will later get checked by
-        // regular WF checking
-        if let ty::PredicateKind::WellFormed(..) = obligation.predicate.kind().skip_binder() {
-            continue;
-        }
-        let pred = obligation.predicate;
-        // Match the existing behavior.
-        if pred.is_global() && !pred.has_late_bound_regions() {
-            let pred = fcx.normalize_associated_types_in(span, pred);
-            let hir_node = fcx.tcx.hir().find(id);
+impl<'tcx> WfCheckingCtxt<'_, 'tcx> {
+    /// Feature gates RFC 2056 -- trivial bounds, checking for global bounds that
+    /// aren't true.
+    fn check_false_global_bounds(&mut self) {
+        let tcx = self.ocx.infcx.tcx;
+        let mut span = self.span;
+        let empty_env = ty::ParamEnv::empty();
+
+        let def_id = tcx.hir().local_def_id(self.body_id);
+        let predicates_with_span = tcx.predicates_of(def_id).predicates.iter().copied();
+        // Check elaborated bounds.
+        let implied_obligations = traits::elaborate_predicates_with_span(tcx, predicates_with_span);
+
+        for obligation in implied_obligations {
+            // We lower empty bounds like `Vec<dyn Copy>:` as
+            // `WellFormed(Vec<dyn Copy>)`, which will later get checked by
+            // regular WF checking
+            if let ty::PredicateKind::WellFormed(..) = obligation.predicate.kind().skip_binder() {
+                continue;
+            }
+            let pred = obligation.predicate;
+            // Match the existing behavior.
+            if pred.is_global() && !pred.has_late_bound_regions() {
+                let pred = self.normalize(span, None, pred);
+                let hir_node = tcx.hir().find(self.body_id);
 
-            // only use the span of the predicate clause (#90869)
+                // only use the span of the predicate clause (#90869)
 
-            if let Some(hir::Generics { predicates, .. }) =
-                hir_node.and_then(|node| node.generics())
-            {
-                let obligation_span = obligation.cause.span(fcx.tcx);
-
-                span = predicates
-                    .iter()
-                    // There seems to be no better way to find out which predicate we are in
-                    .find(|pred| pred.span().contains(obligation_span))
-                    .map(|pred| pred.span())
-                    .unwrap_or(obligation_span);
-            }
+                if let Some(hir::Generics { predicates, .. }) =
+                    hir_node.and_then(|node| node.generics())
+                {
+                    let obligation_span = obligation.cause.span();
+
+                    span = predicates
+                        .iter()
+                        // There seems to be no better way to find out which predicate we are in
+                        .find(|pred| pred.span().contains(obligation_span))
+                        .map(|pred| pred.span())
+                        .unwrap_or(obligation_span);
+                }
 
-            let obligation = traits::Obligation::new(
-                traits::ObligationCause::new(span, id, traits::TrivialBound),
-                empty_env,
-                pred,
-            );
-            fcx.register_predicate(obligation);
+                let obligation = traits::Obligation::new(
+                    traits::ObligationCause::new(span, self.body_id, traits::TrivialBound),
+                    empty_env,
+                    pred,
+                );
+                self.ocx.register_obligation(obligation);
+            }
         }
     }
-
-    fcx.select_all_obligations_or_error();
 }
 
 fn check_mod_type_wf(tcx: TyCtxt<'_>, module: LocalDefId) {
@@ -1887,17 +1889,16 @@ struct AdtField<'tcx> {
     span: Span,
 }
 
-impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
+impl<'a, 'tcx> WfCheckingCtxt<'a, 'tcx> {
     // FIXME(eddyb) replace this with getting fields through `ty::AdtDef`.
     fn non_enum_variant(&self, struct_def: &hir::VariantData<'_>) -> AdtVariant<'tcx> {
         let fields = struct_def
             .fields()
             .iter()
             .map(|field| {
-                let def_id = self.tcx.hir().local_def_id(field.hir_id);
-                let field_ty = self.tcx.type_of(def_id);
-                let field_ty = self.normalize_associated_types_in(field.ty.span, field_ty);
-                let field_ty = self.resolve_vars_if_possible(field_ty);
+                let def_id = self.tcx().hir().local_def_id(field.hir_id);
+                let field_ty = self.tcx().type_of(def_id);
+                let field_ty = self.normalize(field.ty.span, None, field_ty);
                 debug!("non_enum_variant: type of field {:?} is {:?}", field, field_ty);
                 AdtField { ty: field_ty, span: field.ty.span, def_id }
             })
@@ -1913,32 +1914,44 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 fields: self.non_enum_variant(&variant.data).fields,
                 explicit_discr: variant
                     .disr_expr
-                    .map(|explicit_discr| self.tcx.hir().local_def_id(explicit_discr.hir_id)),
+                    .map(|explicit_discr| self.tcx().hir().local_def_id(explicit_discr.hir_id)),
             })
             .collect()
     }
+}
 
-    pub(super) fn impl_implied_bounds(
-        &self,
-        impl_def_id: DefId,
-        span: Span,
-    ) -> FxHashSet<Ty<'tcx>> {
-        match self.tcx.impl_trait_ref(impl_def_id) {
+pub fn impl_implied_bounds<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    impl_def_id: LocalDefId,
+    span: Span,
+) -> FxHashSet<Ty<'tcx>> {
+    // We completely ignore any obligations caused by normalizing the types
+    // we assume to be well formed. Considering that the user of the implied
+    // bounds will also normalize them, we leave it to them to emit errors
+    // which should result in better causes and spans.
+    tcx.infer_ctxt().enter(|infcx| {
+        let cause = ObligationCause::misc(span, tcx.hir().local_def_id_to_hir_id(impl_def_id));
+        match tcx.impl_trait_ref(impl_def_id) {
             Some(trait_ref) => {
                 // Trait impl: take implied bounds from all types that
                 // appear in the trait reference.
-                let trait_ref = self.normalize_associated_types_in(span, trait_ref);
-                trait_ref.substs.types().collect()
+                match infcx.at(&cause, param_env).normalize(trait_ref) {
+                    Ok(Normalized { value, obligations: _ }) => value.substs.types().collect(),
+                    Err(NoSolution) => FxHashSet::default(),
+                }
             }
 
             None => {
                 // Inherent impl: take implied bounds from the `self` type.
-                let self_ty = self.tcx.type_of(impl_def_id);
-                let self_ty = self.normalize_associated_types_in(span, self_ty);
-                FxHashSet::from_iter([self_ty])
+                let self_ty = tcx.type_of(impl_def_id);
+                match infcx.at(&cause, param_env).normalize(self_ty) {
+                    Ok(Normalized { value, obligations: _ }) => FxHashSet::from_iter([value]),
+                    Err(NoSolution) => FxHashSet::default(),
+                }
             }
         }
-    }
+    })
 }
 
 fn error_392(
diff --git a/compiler/rustc_typeck/src/check/writeback.rs b/compiler/rustc_typeck/src/check/writeback.rs
index 41d241f84ac..fa6053ac395 100644
--- a/compiler/rustc_typeck/src/check/writeback.rs
+++ b/compiler/rustc_typeck/src/check/writeback.rs
@@ -4,7 +4,8 @@
 
 use crate::check::FnCtxt;
 
-use rustc_data_structures::stable_map::FxHashMap;
+use hir::def_id::LocalDefId;
+use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::ErrorGuaranteed;
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
@@ -263,7 +264,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> {
         self.fix_index_builtin_expr(e);
 
         match e.kind {
-            hir::ExprKind::Closure { body, .. } => {
+            hir::ExprKind::Closure(&hir::Closure { body, .. }) => {
                 let body = self.fcx.tcx.hir().body(body);
                 for param in body.params {
                     self.visit_node_id(e.span, param.hir_id);
@@ -509,13 +510,13 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
                 hir::OpaqueTyOrigin::FnReturn(_) | hir::OpaqueTyOrigin::AsyncFn(_) => {
                     let ty = self.resolve(decl.hidden_type.ty, &decl.hidden_type.span);
                     struct RecursionChecker {
-                        def_id: DefId,
+                        def_id: LocalDefId,
                     }
                     impl<'tcx> ty::TypeVisitor<'tcx> for RecursionChecker {
                         type BreakTy = ();
                         fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
                             if let ty::Opaque(def_id, _) = *t.kind() {
-                                if def_id == self.def_id {
+                                if def_id == self.def_id.to_def_id() {
                                     return ControlFlow::Break(());
                                 }
                             }
@@ -747,7 +748,7 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Resolver<'cx, 'tcx> {
                 // (e.g. keep `for<'a>` named `for<'a>`).
                 // This allows NLL to generate error messages that
                 // refer to the higher-ranked lifetime names written by the user.
-                EraseEarlyRegions { tcx: self.infcx.tcx }.fold_ty(t)
+                EraseEarlyRegions { tcx: self.tcx }.fold_ty(t)
             }
             Err(_) => {
                 debug!("Resolver::fold_ty: input type `{:?}` not fully resolvable", t);
@@ -765,7 +766,7 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Resolver<'cx, 'tcx> {
 
     fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
         match self.infcx.fully_resolve(ct) {
-            Ok(ct) => self.infcx.tcx.erase_regions(ct),
+            Ok(ct) => self.tcx.erase_regions(ct),
             Err(_) => {
                 debug!("Resolver::fold_const: input const `{:?}` not fully resolvable", ct);
                 self.report_const_error(ct);
diff --git a/compiler/rustc_typeck/src/coherence/builtin.rs b/compiler/rustc_typeck/src/coherence/builtin.rs
index d532050d050..d8e42729ff3 100644
--- a/compiler/rustc_typeck/src/coherence/builtin.rs
+++ b/compiler/rustc_typeck/src/coherence/builtin.rs
@@ -116,8 +116,8 @@ fn visit_implementation_of_copy(tcx: TyCtxt<'_>, impl_did: LocalDefId) {
                 // why this field does not implement Copy. This is useful because sometimes
                 // it is not immediately clear why Copy is not implemented for a field, since
                 // all we point at is the field itself.
-                tcx.infer_ctxt().enter(|infcx| {
-                    let mut fulfill_cx = traits::FulfillmentContext::new_ignoring_regions();
+                tcx.infer_ctxt().ignoring_regions().enter(|infcx| {
+                    let mut fulfill_cx = traits::FulfillmentContext::new();
                     fulfill_cx.register_bound(
                         &infcx,
                         param_env,
@@ -349,7 +349,7 @@ fn visit_implementation_of_dispatch_from_dyn<'tcx>(tcx: TyCtxt<'tcx>, impl_did:
 
                     // Finally, resolve all regions.
                     let outlives_env = OutlivesEnvironment::new(param_env);
-                    infcx.resolve_regions_and_report_errors(&outlives_env);
+                    infcx.resolve_regions_and_report_errors(impl_did, &outlives_env);
                 }
             }
             _ => {
@@ -606,7 +606,7 @@ pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUn
 
         // Finally, resolve all regions.
         let outlives_env = OutlivesEnvironment::new(param_env);
-        infcx.resolve_regions_and_report_errors(&outlives_env);
+        infcx.resolve_regions_and_report_errors(impl_did, &outlives_env);
 
         CoerceUnsizedInfo { custom_kind: kind }
     })
diff --git a/compiler/rustc_typeck/src/coherence/inherent_impls.rs b/compiler/rustc_typeck/src/coherence/inherent_impls.rs
index 7a9b874b5e4..52aad636fd8 100644
--- a/compiler/rustc_typeck/src/coherence/inherent_impls.rs
+++ b/compiler/rustc_typeck/src/coherence/inherent_impls.rs
@@ -219,8 +219,9 @@ impl<'tcx> InherentCollect<'tcx> {
             | ty::RawPtr(_)
             | ty::Ref(..)
             | ty::Never
+            | ty::FnPtr(_)
             | ty::Tuple(..) => self.check_primitive_impl(item.def_id, self_ty, items, ty.span),
-            ty::FnPtr(_) | ty::Projection(..) | ty::Opaque(..) | ty::Param(_) => {
+            ty::Projection(..) | ty::Opaque(..) | ty::Param(_) => {
                 let mut err = struct_span_err!(
                     self.tcx.sess,
                     ty.span,
diff --git a/compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs b/compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs
index db67c1f7c9e..03e076bf5ec 100644
--- a/compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs
+++ b/compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs
@@ -76,17 +76,17 @@ impl<'tcx> InherentOverlapChecker<'tcx> {
                 let name = item1.ident(self.tcx).normalize_to_macros_2_0();
                 let mut err = struct_span_err!(
                     self.tcx.sess,
-                    self.tcx.span_of_impl(item1.def_id).unwrap(),
+                    self.tcx.def_span(item1.def_id),
                     E0592,
                     "duplicate definitions with name `{}`",
                     name
                 );
                 err.span_label(
-                    self.tcx.span_of_impl(item1.def_id).unwrap(),
+                    self.tcx.def_span(item1.def_id),
                     format!("duplicate definitions for `{}`", name),
                 );
                 err.span_label(
-                    self.tcx.span_of_impl(item2.def_id).unwrap(),
+                    self.tcx.def_span(item2.def_id),
                     format!("other definition for `{}`", name),
                 );
 
diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs
index 44b9c8392f8..60c0694ca0e 100644
--- a/compiler/rustc_typeck/src/collect.rs
+++ b/compiler/rustc_typeck/src/collect.rs
@@ -1346,16 +1346,8 @@ fn has_late_bound_regions<'tcx>(tcx: TyCtxt<'tcx>, node: Node<'tcx>) -> Option<S
 
             match self.tcx.named_region(lt.hir_id) {
                 Some(rl::Region::Static | rl::Region::EarlyBound(..)) => {}
-                Some(
-                    rl::Region::LateBound(debruijn, _, _)
-                    | rl::Region::LateBoundAnon(debruijn, _, _),
-                ) if debruijn < self.outer_index => {}
-                Some(
-                    rl::Region::LateBound(..)
-                    | rl::Region::LateBoundAnon(..)
-                    | rl::Region::Free(..),
-                )
-                | None => {
+                Some(rl::Region::LateBound(debruijn, _, _)) if debruijn < self.outer_index => {}
+                Some(rl::Region::LateBound(..) | rl::Region::Free(..)) | None => {
                     self.has_late_bound_regions = Some(lt.span);
                 }
             }
@@ -1717,8 +1709,10 @@ fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics {
     // provide junk type parameter defs - the only place that
     // cares about anything but the length is instantiation,
     // and we don't do that for closures.
-    if let Node::Expr(&hir::Expr { kind: hir::ExprKind::Closure { movability: gen, .. }, .. }) =
-        node
+    if let Node::Expr(&hir::Expr {
+        kind: hir::ExprKind::Closure(hir::Closure { movability: gen, .. }),
+        ..
+    }) = node
     {
         let dummy_args = if gen.is_some() {
             &["<resume_ty>", "<yield_ty>", "<return_ty>", "<witness>", "<upvars>"][..]
@@ -1927,7 +1921,7 @@ fn infer_return_ty_for_fn_sig<'tcx>(
             visitor.visit_ty(ty);
             let mut diag = bad_placeholder(tcx, visitor.0, "return type");
             let ret_ty = fn_sig.skip_binder().output();
-            if ret_ty.is_suggestable(tcx) {
+            if ret_ty.is_suggestable(tcx, false) {
                 diag.span_suggestion(
                     ty.span,
                     "replace with the correct return type",
@@ -1936,7 +1930,12 @@ fn infer_return_ty_for_fn_sig<'tcx>(
                 );
             } else if matches!(ret_ty.kind(), ty::FnDef(..)) {
                 let fn_sig = ret_ty.fn_sig(tcx);
-                if fn_sig.skip_binder().inputs_and_output.iter().all(|t| t.is_suggestable(tcx)) {
+                if fn_sig
+                    .skip_binder()
+                    .inputs_and_output
+                    .iter()
+                    .all(|t| t.is_suggestable(tcx, false))
+                {
                     diag.span_suggestion(
                         ty.span,
                         "replace with the correct return type",
@@ -2083,10 +2082,17 @@ fn predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicates<'_> {
         // from the trait itself that *shouldn't* be shown as the source of
         // an obligation and instead be skipped. Otherwise we'd use
         // `tcx.def_span(def_id);`
+
+        let constness = if tcx.has_attr(def_id, sym::const_trait) {
+            ty::BoundConstness::ConstIfConst
+        } else {
+            ty::BoundConstness::NotConst
+        };
+
         let span = rustc_span::DUMMY_SP;
         result.predicates =
             tcx.arena.alloc_from_iter(result.predicates.iter().copied().chain(std::iter::once((
-                ty::TraitRef::identity(tcx, def_id).without_const().to_predicate(tcx),
+                ty::TraitRef::identity(tcx, def_id).with_constness(constness).to_predicate(tcx),
                 span,
             ))));
     }
@@ -2564,7 +2570,7 @@ fn is_foreign_item(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
 fn generator_kind(tcx: TyCtxt<'_>, def_id: DefId) -> Option<hir::GeneratorKind> {
     match tcx.hir().get_if_local(def_id) {
         Some(Node::Expr(&rustc_hir::Expr {
-            kind: rustc_hir::ExprKind::Closure { body, .. },
+            kind: rustc_hir::ExprKind::Closure(&rustc_hir::Closure { body, .. }),
             ..
         })) => tcx.hir().body(body).generator_kind(),
         Some(_) => None,
@@ -2768,6 +2774,12 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: DefId) -> CodegenFnAttrs {
             }
         } else if attr.has_name(sym::rustc_allocator_nounwind) {
             codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND;
+        } else if attr.has_name(sym::rustc_reallocator) {
+            codegen_fn_attrs.flags |= CodegenFnAttrFlags::REALLOCATOR;
+        } else if attr.has_name(sym::rustc_deallocator) {
+            codegen_fn_attrs.flags |= CodegenFnAttrFlags::DEALLOCATOR;
+        } else if attr.has_name(sym::rustc_allocator_zeroed) {
+            codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR_ZEROED;
         } else if attr.has_name(sym::naked) {
             codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED;
         } else if attr.has_name(sym::no_mangle) {
@@ -2811,7 +2823,37 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: DefId) -> CodegenFnAttrs {
                         )
                         .emit();
                 }
-                None => codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED,
+                None => {
+                    // Unfortunately, unconditionally using `llvm.used` causes
+                    // issues in handling `.init_array` with the gold linker,
+                    // but using `llvm.compiler.used` caused a nontrival amount
+                    // of unintentional ecosystem breakage -- particularly on
+                    // Mach-O targets.
+                    //
+                    // As a result, we emit `llvm.compiler.used` only on ELF
+                    // targets. This is somewhat ad-hoc, but actually follows
+                    // our pre-LLVM 13 behavior (prior to the ecosystem
+                    // breakage), and seems to match `clang`'s behavior as well
+                    // (both before and after LLVM 13), possibly because they
+                    // have similar compatibility concerns to us. See
+                    // https://github.com/rust-lang/rust/issues/47384#issuecomment-1019080146
+                    // and following comments for some discussion of this, as
+                    // well as the comments in `rustc_codegen_llvm` where these
+                    // flags are handled.
+                    //
+                    // Anyway, to be clear: this is still up in the air
+                    // somewhat, and is subject to change in the future (which
+                    // is a good thing, because this would ideally be a bit
+                    // more firmed up).
+                    let is_like_elf = !(tcx.sess.target.is_like_osx
+                        || tcx.sess.target.is_like_windows
+                        || tcx.sess.target.is_like_wasm);
+                    codegen_fn_attrs.flags |= if is_like_elf {
+                        CodegenFnAttrFlags::USED
+                    } else {
+                        CodegenFnAttrFlags::USED_LINKER
+                    };
+                }
             }
         } else if attr.has_name(sym::cmse_nonsecure_entry) {
             if !matches!(tcx.fn_sig(did).abi(), abi::Abi::C { .. }) {
@@ -2937,6 +2979,8 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: DefId) -> CodegenFnAttrs {
                         codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMORY;
                     } else if item.has_name(sym::memtag) {
                         codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMTAG;
+                    } else if item.has_name(sym::shadow_call_stack) {
+                        codegen_fn_attrs.no_sanitize |= SanitizerSet::SHADOWCALLSTACK;
                     } else if item.has_name(sym::thread) {
                         codegen_fn_attrs.no_sanitize |= SanitizerSet::THREAD;
                     } else if item.has_name(sym::hwaddress) {
@@ -2944,7 +2988,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: DefId) -> CodegenFnAttrs {
                     } else {
                         tcx.sess
                             .struct_span_err(item.span(), "invalid argument for `no_sanitize`")
-                            .note("expected one of: `address`, `cfi`, `hwaddress`, `memory`, `memtag`, or `thread`")
+                            .note("expected one of: `address`, `cfi`, `hwaddress`, `memory`, `memtag`, `shadow-call-stack`, or `thread`")
                             .emit();
                     }
                 }
@@ -3196,7 +3240,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: DefId) -> CodegenFnAttrs {
 /// Computes the set of target features used in a function for the purposes of
 /// inline assembly.
 fn asm_target_features<'tcx>(tcx: TyCtxt<'tcx>, did: DefId) -> &'tcx FxHashSet<Symbol> {
-    let mut target_features = tcx.sess.target_features.clone();
+    let mut target_features = tcx.sess.unstable_target_features.clone();
     if tcx.def_kind(did).has_codegen_attrs() {
         let attrs = tcx.codegen_fn_attrs(did);
         target_features.extend(&attrs.target_features);
diff --git a/compiler/rustc_typeck/src/collect/type_of.rs b/compiler/rustc_typeck/src/collect/type_of.rs
index f942a4fb53a..64ac655e0c3 100644
--- a/compiler/rustc_typeck/src/collect/type_of.rs
+++ b/compiler/rustc_typeck/src/collect/type_of.rs
@@ -335,37 +335,11 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> {
                     tcx.mk_adt(def, substs)
                 }
                 ItemKind::OpaqueTy(OpaqueTy { origin: hir::OpaqueTyOrigin::TyAlias, .. }) => {
-                    find_opaque_ty_constraints(tcx, def_id)
+                    find_opaque_ty_constraints_for_tait(tcx, def_id)
                 }
                 // Opaque types desugared from `impl Trait`.
                 ItemKind::OpaqueTy(OpaqueTy { origin: hir::OpaqueTyOrigin::FnReturn(owner) | hir::OpaqueTyOrigin::AsyncFn(owner), .. }) => {
-                    let concrete_ty = tcx
-                        .mir_borrowck(owner)
-                        .concrete_opaque_types
-                        .get(&def_id.to_def_id())
-                        .copied()
-                        .map(|concrete| concrete.ty)
-                        .unwrap_or_else(|| {
-                            let table = tcx.typeck(owner);
-                            if let Some(_) = table.tainted_by_errors {
-                                // Some error in the
-                                // owner fn prevented us from populating
-                                // the `concrete_opaque_types` table.
-                                tcx.ty_error()
-                            } else {
-                                table.concrete_opaque_types.get(&def_id.to_def_id()).copied().unwrap_or_else(|| {
-                                    // We failed to resolve the opaque type or it
-                                    // resolves to itself. We interpret this as the
-                                    // no values of the hidden type ever being constructed,
-                                    // so we can just make the hidden type be `!`.
-                                    // For backwards compatibility reasons, we fall back to
-                                    // `()` until we the diverging default is changed.
-                                    Some(tcx.mk_diverging_default())
-                                }).expect("RPIT always have a hidden type from typeck")
-                            }
-                        });
-                    debug!("concrete_ty = {:?}", concrete_ty);
-                    concrete_ty
+                    find_opaque_ty_constraints_for_rpit(tcx, def_id, owner)
                 }
                 ItemKind::Trait(..)
                 | ItemKind::TraitAlias(..)
@@ -519,14 +493,14 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> {
 /// fn b<T>() -> Foo<T, u32> { .. }
 /// ```
 ///
-fn find_opaque_ty_constraints(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Ty<'_> {
+fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Ty<'_> {
     use rustc_hir::{Expr, ImplItem, Item, TraitItem};
 
     struct ConstraintLocator<'tcx> {
         tcx: TyCtxt<'tcx>,
 
         /// def_id of the opaque type whose defining uses are being checked
-        def_id: DefId,
+        def_id: LocalDefId,
 
         /// as we walk the defining uses, we are checking that all of them
         /// define the same hidden type. This variable is set to `Some`
@@ -538,9 +512,9 @@ fn find_opaque_ty_constraints(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Ty<'_> {
 
     impl ConstraintLocator<'_> {
         #[instrument(skip(self), level = "debug")]
-        fn check(&mut self, def_id: LocalDefId) {
+        fn check(&mut self, item_def_id: LocalDefId) {
             // Don't try to check items that cannot possibly constrain the type.
-            if !self.tcx.has_typeck_results(def_id) {
+            if !self.tcx.has_typeck_results(item_def_id) {
                 debug!("no constraint: no typeck results");
                 return;
             }
@@ -555,26 +529,20 @@ fn find_opaque_ty_constraints(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Ty<'_> {
             // // because we again need to reveal `Foo` so we can check whether the
             // // constant does not contain interior mutability.
             // ```
-            let tables = self.tcx.typeck(def_id);
+            let tables = self.tcx.typeck(item_def_id);
             if let Some(_) = tables.tainted_by_errors {
                 self.found = Some(ty::OpaqueHiddenType { span: DUMMY_SP, ty: self.tcx.ty_error() });
                 return;
             }
-            if tables.concrete_opaque_types.get(&self.def_id).is_none() {
+            if !tables.concrete_opaque_types.contains_key(&self.def_id) {
                 debug!("no constraints in typeck results");
                 return;
             }
             // Use borrowck to get the type with unerased regions.
-            let concrete_opaque_types = &self.tcx.mir_borrowck(def_id).concrete_opaque_types;
+            let concrete_opaque_types = &self.tcx.mir_borrowck(item_def_id).concrete_opaque_types;
             debug!(?concrete_opaque_types);
-            for &(def_id, concrete_type) in concrete_opaque_types {
-                if def_id != self.def_id {
-                    // Ignore constraints for other opaque types.
-                    continue;
-                }
-
+            if let Some(&concrete_type) = concrete_opaque_types.get(&self.def_id) {
                 debug!(?concrete_type, "found constraint");
-
                 if let Some(prev) = self.found {
                     if concrete_type.ty != prev.ty && !(concrete_type, prev).references_error() {
                         prev.report_mismatch(&concrete_type, self.tcx);
@@ -602,7 +570,7 @@ fn find_opaque_ty_constraints(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Ty<'_> {
         fn visit_item(&mut self, it: &'tcx Item<'tcx>) {
             trace!(?it.def_id);
             // The opaque type itself or its children are not within its reveal scope.
-            if it.def_id.to_def_id() != self.def_id {
+            if it.def_id != self.def_id {
                 self.check(it.def_id);
                 intravisit::walk_item(self, it);
             }
@@ -610,7 +578,7 @@ fn find_opaque_ty_constraints(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Ty<'_> {
         fn visit_impl_item(&mut self, it: &'tcx ImplItem<'tcx>) {
             trace!(?it.def_id);
             // The opaque type itself or its children are not within its reveal scope.
-            if it.def_id.to_def_id() != self.def_id {
+            if it.def_id != self.def_id {
                 self.check(it.def_id);
                 intravisit::walk_impl_item(self, it);
             }
@@ -624,7 +592,7 @@ fn find_opaque_ty_constraints(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Ty<'_> {
 
     let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
     let scope = tcx.hir().get_defining_scope(hir_id);
-    let mut locator = ConstraintLocator { def_id: def_id.to_def_id(), tcx, found: None };
+    let mut locator = ConstraintLocator { def_id: def_id, tcx, found: None };
 
     debug!(?scope);
 
@@ -666,6 +634,122 @@ fn find_opaque_ty_constraints(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Ty<'_> {
     }
 }
 
+fn find_opaque_ty_constraints_for_rpit(
+    tcx: TyCtxt<'_>,
+    def_id: LocalDefId,
+    owner_def_id: LocalDefId,
+) -> Ty<'_> {
+    use rustc_hir::{Expr, ImplItem, Item, TraitItem};
+
+    struct ConstraintChecker<'tcx> {
+        tcx: TyCtxt<'tcx>,
+
+        /// def_id of the opaque type whose defining uses are being checked
+        def_id: LocalDefId,
+
+        found: ty::OpaqueHiddenType<'tcx>,
+    }
+
+    impl ConstraintChecker<'_> {
+        #[instrument(skip(self), level = "debug")]
+        fn check(&self, def_id: LocalDefId) {
+            // Use borrowck to get the type with unerased regions.
+            let concrete_opaque_types = &self.tcx.mir_borrowck(def_id).concrete_opaque_types;
+            debug!(?concrete_opaque_types);
+            for &(def_id, concrete_type) in concrete_opaque_types {
+                if def_id != self.def_id {
+                    // Ignore constraints for other opaque types.
+                    continue;
+                }
+
+                debug!(?concrete_type, "found constraint");
+
+                if concrete_type.ty != self.found.ty
+                    && !(concrete_type, self.found).references_error()
+                {
+                    self.found.report_mismatch(&concrete_type, self.tcx);
+                }
+            }
+        }
+    }
+
+    impl<'tcx> intravisit::Visitor<'tcx> for ConstraintChecker<'tcx> {
+        type NestedFilter = nested_filter::OnlyBodies;
+
+        fn nested_visit_map(&mut self) -> Self::Map {
+            self.tcx.hir()
+        }
+        fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
+            if let hir::ExprKind::Closure { .. } = ex.kind {
+                let def_id = self.tcx.hir().local_def_id(ex.hir_id);
+                self.check(def_id);
+            }
+            intravisit::walk_expr(self, ex);
+        }
+        fn visit_item(&mut self, it: &'tcx Item<'tcx>) {
+            trace!(?it.def_id);
+            // The opaque type itself or its children are not within its reveal scope.
+            if it.def_id != self.def_id {
+                self.check(it.def_id);
+                intravisit::walk_item(self, it);
+            }
+        }
+        fn visit_impl_item(&mut self, it: &'tcx ImplItem<'tcx>) {
+            trace!(?it.def_id);
+            // The opaque type itself or its children are not within its reveal scope.
+            if it.def_id != self.def_id {
+                self.check(it.def_id);
+                intravisit::walk_impl_item(self, it);
+            }
+        }
+        fn visit_trait_item(&mut self, it: &'tcx TraitItem<'tcx>) {
+            trace!(?it.def_id);
+            self.check(it.def_id);
+            intravisit::walk_trait_item(self, it);
+        }
+    }
+
+    let concrete = tcx.mir_borrowck(owner_def_id).concrete_opaque_types.get(&def_id).copied();
+
+    if let Some(concrete) = concrete {
+        let scope = tcx.hir().local_def_id_to_hir_id(owner_def_id);
+        debug!(?scope);
+        let mut locator = ConstraintChecker { def_id: def_id, tcx, found: concrete };
+
+        match tcx.hir().get(scope) {
+            Node::Item(it) => intravisit::walk_item(&mut locator, it),
+            Node::ImplItem(it) => intravisit::walk_impl_item(&mut locator, it),
+            Node::TraitItem(it) => intravisit::walk_trait_item(&mut locator, it),
+            other => bug!("{:?} is not a valid scope for an opaque type item", other),
+        }
+    }
+
+    concrete.map(|concrete| concrete.ty).unwrap_or_else(|| {
+        let table = tcx.typeck(owner_def_id);
+        if let Some(_) = table.tainted_by_errors {
+            // Some error in the
+            // owner fn prevented us from populating
+            // the `concrete_opaque_types` table.
+            tcx.ty_error()
+        } else {
+            table
+                .concrete_opaque_types
+                .get(&def_id)
+                .copied()
+                .unwrap_or_else(|| {
+                    // We failed to resolve the opaque type or it
+                    // resolves to itself. We interpret this as the
+                    // no values of the hidden type ever being constructed,
+                    // so we can just make the hidden type be `!`.
+                    // For backwards compatibility reasons, we fall back to
+                    // `()` until we the diverging default is changed.
+                    Some(tcx.mk_diverging_default())
+                })
+                .expect("RPIT always have a hidden type from typeck")
+        }
+    })
+}
+
 fn infer_placeholder_type<'a>(
     tcx: TyCtxt<'a>,
     def_id: LocalDefId,
diff --git a/compiler/rustc_typeck/src/errors.rs b/compiler/rustc_typeck/src/errors.rs
index 4cdec615d82..0438ac02ea9 100644
--- a/compiler/rustc_typeck/src/errors.rs
+++ b/compiler/rustc_typeck/src/errors.rs
@@ -244,7 +244,7 @@ pub struct UnconstrainedOpaqueType {
 pub struct MissingTypeParams {
     pub span: Span,
     pub def_span: Span,
-    pub missing_type_params: Vec<String>,
+    pub missing_type_params: Vec<Symbol>,
     pub empty_generic_args: bool,
 }
 
@@ -285,7 +285,15 @@ impl<'a> SessionDiagnostic<'a> for MissingTypeParams {
                 err.span_suggestion(
                     self.span,
                     rustc_errors::fluent::typeck::suggestion,
-                    format!("{}<{}>", snippet, self.missing_type_params.join(", ")),
+                    format!(
+                        "{}<{}>",
+                        snippet,
+                        self.missing_type_params
+                            .iter()
+                            .map(|n| n.to_string())
+                            .collect::<Vec<_>>()
+                            .join(", ")
+                    ),
                     Applicability::HasPlaceholders,
                 );
                 suggested = true;
diff --git a/compiler/rustc_typeck/src/expr_use_visitor.rs b/compiler/rustc_typeck/src/expr_use_visitor.rs
index 920e3d578c8..9d7420acd26 100644
--- a/compiler/rustc_typeck/src/expr_use_visitor.rs
+++ b/compiler/rustc_typeck/src/expr_use_visitor.rs
@@ -2,7 +2,10 @@
 //! normal visitor, which just walks the entire body in one shot, the
 //! `ExprUseVisitor` determines how expressions are being used.
 
+use std::slice::from_ref;
+
 use hir::def::DefKind;
+use hir::Expr;
 // Export these here so that Clippy can use them.
 pub use rustc_middle::hir::place::{Place, PlaceBase, PlaceWithHirId, Projection};
 
@@ -252,96 +255,16 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
             }
 
             hir::ExprKind::Let(hir::Let { pat, init, .. }) => {
-                self.walk_local(init, pat, |t| t.borrow_expr(init, ty::ImmBorrow));
+                self.walk_local(init, pat, None, |t| t.borrow_expr(init, ty::ImmBorrow))
             }
 
             hir::ExprKind::Match(ref discr, arms, _) => {
                 let discr_place = return_if_err!(self.mc.cat_expr(discr));
-
-                // Matching should not always be considered a use of the place, hence
-                // discr does not necessarily need to be borrowed.
-                // We only want to borrow discr if the pattern contain something other
-                // than wildcards.
-                let ExprUseVisitor { ref mc, body_owner: _, delegate: _ } = *self;
-                let mut needs_to_be_read = false;
-                for arm in arms.iter() {
-                    return_if_err!(mc.cat_pattern(discr_place.clone(), arm.pat, |place, pat| {
-                        match &pat.kind {
-                            PatKind::Binding(.., opt_sub_pat) => {
-                                // If the opt_sub_pat is None, than the binding does not count as
-                                // a wildcard for the purpose of borrowing discr.
-                                if opt_sub_pat.is_none() {
-                                    needs_to_be_read = true;
-                                }
-                            }
-                            PatKind::Path(qpath) => {
-                                // A `Path` pattern is just a name like `Foo`. This is either a
-                                // named constant or else it refers to an ADT variant
-
-                                let res = self.mc.typeck_results.qpath_res(qpath, pat.hir_id);
-                                match res {
-                                    Res::Def(DefKind::Const, _)
-                                    | Res::Def(DefKind::AssocConst, _) => {
-                                        // Named constants have to be equated with the value
-                                        // being matched, so that's a read of the value being matched.
-                                        //
-                                        // FIXME: We don't actually  reads for ZSTs.
-                                        needs_to_be_read = true;
-                                    }
-                                    _ => {
-                                        // Otherwise, this is a struct/enum variant, and so it's
-                                        // only a read if we need to read the discriminant.
-                                        needs_to_be_read |= is_multivariant_adt(place.place.ty());
-                                    }
-                                }
-                            }
-                            PatKind::TupleStruct(..) | PatKind::Struct(..) | PatKind::Tuple(..) => {
-                                // For `Foo(..)`, `Foo { ... }` and `(...)` patterns, check if we are matching
-                                // against a multivariant enum or struct. In that case, we have to read
-                                // the discriminant. Otherwise this kind of pattern doesn't actually
-                                // read anything (we'll get invoked for the `...`, which may indeed
-                                // perform some reads).
-
-                                let place_ty = place.place.ty();
-                                needs_to_be_read |= is_multivariant_adt(place_ty);
-                            }
-                            PatKind::Lit(_) | PatKind::Range(..) => {
-                                // If the PatKind is a Lit or a Range then we want
-                                // to borrow discr.
-                                needs_to_be_read = true;
-                            }
-                            PatKind::Or(_)
-                            | PatKind::Box(_)
-                            | PatKind::Slice(..)
-                            | PatKind::Ref(..)
-                            | PatKind::Wild => {
-                                // If the PatKind is Or, Box, Slice or Ref, the decision is made later
-                                // as these patterns contains subpatterns
-                                // If the PatKind is Wild, the decision is made based on the other patterns being
-                                // examined
-                            }
-                        }
-                    }));
-                }
-
-                if needs_to_be_read {
-                    self.borrow_expr(discr, ty::ImmBorrow);
-                } else {
-                    let closure_def_id = match discr_place.place.base {
-                        PlaceBase::Upvar(upvar_id) => Some(upvar_id.closure_expr_id.to_def_id()),
-                        _ => None,
-                    };
-
-                    self.delegate.fake_read(
-                        &discr_place,
-                        FakeReadCause::ForMatchedPlace(closure_def_id),
-                        discr_place.hir_id,
-                    );
-
-                    // We always want to walk the discriminant. We want to make sure, for instance,
-                    // that the discriminant has been initialized.
-                    self.walk_expr(discr);
-                }
+                self.maybe_read_scrutinee(
+                    discr,
+                    discr_place.clone(),
+                    arms.iter().map(|arm| arm.pat),
+                );
 
                 // treatment of the discriminant is handled while walking the arms.
                 for arm in arms {
@@ -453,8 +376,8 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
 
     fn walk_stmt(&mut self, stmt: &hir::Stmt<'_>) {
         match stmt.kind {
-            hir::StmtKind::Local(hir::Local { pat, init: Some(expr), .. }) => {
-                self.walk_local(expr, pat, |_| {});
+            hir::StmtKind::Local(hir::Local { pat, init: Some(expr), els, .. }) => {
+                self.walk_local(expr, pat, *els, |_| {})
             }
 
             hir::StmtKind::Local(_) => {}
@@ -470,13 +393,114 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
         }
     }
 
-    fn walk_local<F>(&mut self, expr: &hir::Expr<'_>, pat: &hir::Pat<'_>, mut f: F)
-    where
+    fn maybe_read_scrutinee<'t>(
+        &mut self,
+        discr: &Expr<'_>,
+        discr_place: PlaceWithHirId<'tcx>,
+        pats: impl Iterator<Item = &'t hir::Pat<'t>>,
+    ) {
+        // Matching should not always be considered a use of the place, hence
+        // discr does not necessarily need to be borrowed.
+        // We only want to borrow discr if the pattern contain something other
+        // than wildcards.
+        let ExprUseVisitor { ref mc, body_owner: _, delegate: _ } = *self;
+        let mut needs_to_be_read = false;
+        for pat in pats {
+            return_if_err!(mc.cat_pattern(discr_place.clone(), pat, |place, pat| {
+                match &pat.kind {
+                    PatKind::Binding(.., opt_sub_pat) => {
+                        // If the opt_sub_pat is None, than the binding does not count as
+                        // a wildcard for the purpose of borrowing discr.
+                        if opt_sub_pat.is_none() {
+                            needs_to_be_read = true;
+                        }
+                    }
+                    PatKind::Path(qpath) => {
+                        // A `Path` pattern is just a name like `Foo`. This is either a
+                        // named constant or else it refers to an ADT variant
+
+                        let res = self.mc.typeck_results.qpath_res(qpath, pat.hir_id);
+                        match res {
+                            Res::Def(DefKind::Const, _) | Res::Def(DefKind::AssocConst, _) => {
+                                // Named constants have to be equated with the value
+                                // being matched, so that's a read of the value being matched.
+                                //
+                                // FIXME: We don't actually  reads for ZSTs.
+                                needs_to_be_read = true;
+                            }
+                            _ => {
+                                // Otherwise, this is a struct/enum variant, and so it's
+                                // only a read if we need to read the discriminant.
+                                needs_to_be_read |= is_multivariant_adt(place.place.ty());
+                            }
+                        }
+                    }
+                    PatKind::TupleStruct(..) | PatKind::Struct(..) | PatKind::Tuple(..) => {
+                        // For `Foo(..)`, `Foo { ... }` and `(...)` patterns, check if we are matching
+                        // against a multivariant enum or struct. In that case, we have to read
+                        // the discriminant. Otherwise this kind of pattern doesn't actually
+                        // read anything (we'll get invoked for the `...`, which may indeed
+                        // perform some reads).
+
+                        let place_ty = place.place.ty();
+                        needs_to_be_read |= is_multivariant_adt(place_ty);
+                    }
+                    PatKind::Lit(_) | PatKind::Range(..) => {
+                        // If the PatKind is a Lit or a Range then we want
+                        // to borrow discr.
+                        needs_to_be_read = true;
+                    }
+                    PatKind::Or(_)
+                    | PatKind::Box(_)
+                    | PatKind::Slice(..)
+                    | PatKind::Ref(..)
+                    | PatKind::Wild => {
+                        // If the PatKind is Or, Box, Slice or Ref, the decision is made later
+                        // as these patterns contains subpatterns
+                        // If the PatKind is Wild, the decision is made based on the other patterns being
+                        // examined
+                    }
+                }
+            }));
+        }
+
+        if needs_to_be_read {
+            self.borrow_expr(discr, ty::ImmBorrow);
+        } else {
+            let closure_def_id = match discr_place.place.base {
+                PlaceBase::Upvar(upvar_id) => Some(upvar_id.closure_expr_id.to_def_id()),
+                _ => None,
+            };
+
+            self.delegate.fake_read(
+                &discr_place,
+                FakeReadCause::ForMatchedPlace(closure_def_id),
+                discr_place.hir_id,
+            );
+
+            // We always want to walk the discriminant. We want to make sure, for instance,
+            // that the discriminant has been initialized.
+            self.walk_expr(discr);
+        }
+    }
+
+    fn walk_local<F>(
+        &mut self,
+        expr: &hir::Expr<'_>,
+        pat: &hir::Pat<'_>,
+        els: Option<&hir::Block<'_>>,
+        mut f: F,
+    ) where
         F: FnMut(&mut Self),
     {
         self.walk_expr(expr);
         let expr_place = return_if_err!(self.mc.cat_expr(expr));
         f(self);
+        if let Some(els) = els {
+            // borrowing because we need to test the descriminant
+            self.maybe_read_scrutinee(expr, expr_place.clone(), from_ref(pat).iter());
+            self.walk_block(els)
+        }
         self.walk_irrefutable_pat(&expr_place, &pat);
     }
 
@@ -667,7 +691,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
         let ExprUseVisitor { ref mc, body_owner: _, ref mut delegate } = *self;
         return_if_err!(mc.cat_pattern(discr_place.clone(), pat, |place, pat| {
             if let PatKind::Binding(_, canonical_id, ..) = pat.kind {
-                debug!("walk_pat: binding place={:?} pat={:?}", place, pat,);
+                debug!("walk_pat: binding place={:?} pat={:?}", place, pat);
                 if let Some(bm) =
                     mc.typeck_results.extract_binding_mode(tcx.sess, pat.hir_id, pat.span)
                 {
diff --git a/compiler/rustc_typeck/src/impl_wf_check.rs b/compiler/rustc_typeck/src/impl_wf_check.rs
index e7ca70de4ba..8c26c96816d 100644
--- a/compiler/rustc_typeck/src/impl_wf_check.rs
+++ b/compiler/rustc_typeck/src/impl_wf_check.rs
@@ -17,7 +17,7 @@ use rustc_hir::def::DefKind;
 use rustc_hir::def_id::LocalDefId;
 use rustc_middle::ty::query::Providers;
 use rustc_middle::ty::{self, TyCtxt, TypeVisitable};
-use rustc_span::Span;
+use rustc_span::{Span, Symbol};
 
 use std::collections::hash_map::Entry::{Occupied, Vacant};
 
@@ -123,12 +123,7 @@ fn enforce_impl_params_are_constrained(tcx: TyCtxt<'_>, impl_def_id: LocalDefId)
             ty::GenericParamDefKind::Type { .. } => {
                 let param_ty = ty::ParamTy::for_def(param);
                 if !input_parameters.contains(&cgp::Parameter::from(param_ty)) {
-                    report_unused_parameter(
-                        tcx,
-                        tcx.def_span(param.def_id),
-                        "type",
-                        &param_ty.to_string(),
-                    );
+                    report_unused_parameter(tcx, tcx.def_span(param.def_id), "type", param_ty.name);
                 }
             }
             ty::GenericParamDefKind::Lifetime => {
@@ -140,7 +135,7 @@ fn enforce_impl_params_are_constrained(tcx: TyCtxt<'_>, impl_def_id: LocalDefId)
                         tcx,
                         tcx.def_span(param.def_id),
                         "lifetime",
-                        &param.name.to_string(),
+                        param.name,
                     );
                 }
             }
@@ -151,7 +146,7 @@ fn enforce_impl_params_are_constrained(tcx: TyCtxt<'_>, impl_def_id: LocalDefId)
                         tcx,
                         tcx.def_span(param.def_id),
                         "const",
-                        &param_ct.to_string(),
+                        param_ct.name,
                     );
                 }
             }
@@ -178,7 +173,7 @@ fn enforce_impl_params_are_constrained(tcx: TyCtxt<'_>, impl_def_id: LocalDefId)
     // used elsewhere are not projected back out.
 }
 
-fn report_unused_parameter(tcx: TyCtxt<'_>, span: Span, kind: &str, name: &str) {
+fn report_unused_parameter(tcx: TyCtxt<'_>, span: Span, kind: &str, name: Symbol) {
     let mut err = struct_span_err!(
         tcx.sess,
         span,
diff --git a/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs b/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs
index c46b825f457..f16888345e9 100644
--- a/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs
+++ b/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs
@@ -65,6 +65,8 @@
 //! cause use after frees with purely safe code in the same way as specializing
 //! on traits with methods can.
 
+use crate::check::regionck::OutlivesEnvironmentExt;
+use crate::check::wfcheck::impl_implied_bounds;
 use crate::constrained_generic_params as cgp;
 use crate::errors::SubstsOnOverriddenImpl;
 
@@ -148,9 +150,16 @@ fn get_impl_substs<'tcx>(
     let impl2_substs =
         translate_substs(infcx, param_env, impl1_def_id.to_def_id(), impl1_substs, impl2_node);
 
-    // Conservatively use an empty `ParamEnv`.
-    let outlives_env = OutlivesEnvironment::new(ty::ParamEnv::empty());
-    infcx.resolve_regions_and_report_errors(&outlives_env);
+    let mut outlives_env = OutlivesEnvironment::new(param_env);
+    let implied_bounds =
+        impl_implied_bounds(infcx.tcx, param_env, impl1_def_id, tcx.def_span(impl1_def_id));
+    outlives_env.add_implied_bounds(
+        infcx,
+        implied_bounds,
+        tcx.hir().local_def_id_to_hir_id(impl1_def_id),
+    );
+    infcx.process_registered_region_obligations(outlives_env.region_bound_pairs(), param_env);
+    infcx.resolve_regions_and_report_errors(impl1_def_id, &outlives_env);
     let Ok(impl2_substs) = infcx.fully_resolve(impl2_substs) else {
         let span = tcx.def_span(impl1_def_id);
         tcx.sess.emit_err(SubstsOnOverriddenImpl { span });
diff --git a/compiler/rustc_typeck/src/lib.rs b/compiler/rustc_typeck/src/lib.rs
index dd712fd7ed7..706b9ee40aa 100644
--- a/compiler/rustc_typeck/src/lib.rs
+++ b/compiler/rustc_typeck/src/lib.rs
@@ -65,13 +65,14 @@ This API is completely unstable and subject to change.
 #![feature(is_sorted)]
 #![feature(iter_intersperse)]
 #![feature(label_break_value)]
-#![feature(let_chains)]
+#![cfg_attr(bootstrap, feature(let_chains))]
 #![feature(let_else)]
 #![feature(min_specialization)]
 #![feature(never_type)]
 #![feature(once_cell)]
 #![feature(slice_partition_dedup)]
 #![feature(try_blocks)]
+#![feature(is_some_with)]
 #![recursion_limit = "256"]
 
 #[macro_use]
diff --git a/compiler/rustc_typeck/src/outlives/explicit.rs b/compiler/rustc_typeck/src/outlives/explicit.rs
index bbf31de527e..7534482cce9 100644
--- a/compiler/rustc_typeck/src/outlives/explicit.rs
+++ b/compiler/rustc_typeck/src/outlives/explicit.rs
@@ -6,7 +6,7 @@ use super::utils::*;
 
 #[derive(Debug)]
 pub struct ExplicitPredicatesMap<'tcx> {
-    map: FxHashMap<DefId, RequiredPredicates<'tcx>>,
+    map: FxHashMap<DefId, ty::EarlyBinder<RequiredPredicates<'tcx>>>,
 }
 
 impl<'tcx> ExplicitPredicatesMap<'tcx> {
@@ -14,11 +14,11 @@ impl<'tcx> ExplicitPredicatesMap<'tcx> {
         ExplicitPredicatesMap { map: FxHashMap::default() }
     }
 
-    pub fn explicit_predicates_of(
+    pub(crate) fn explicit_predicates_of(
         &mut self,
         tcx: TyCtxt<'tcx>,
         def_id: DefId,
-    ) -> &RequiredPredicates<'tcx> {
+    ) -> &ty::EarlyBinder<RequiredPredicates<'tcx>> {
         self.map.entry(def_id).or_insert_with(|| {
             let predicates = if def_id.is_local() {
                 tcx.explicit_predicates_of(def_id)
@@ -63,7 +63,7 @@ impl<'tcx> ExplicitPredicatesMap<'tcx> {
                 }
             }
 
-            required_predicates
+            ty::EarlyBinder(required_predicates)
         })
     }
 }
diff --git a/compiler/rustc_typeck/src/outlives/implicit_infer.rs b/compiler/rustc_typeck/src/outlives/implicit_infer.rs
index 52f9e386441..257a9520eeb 100644
--- a/compiler/rustc_typeck/src/outlives/implicit_infer.rs
+++ b/compiler/rustc_typeck/src/outlives/implicit_infer.rs
@@ -2,7 +2,7 @@ use rustc_data_structures::fx::FxHashMap;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::DefId;
 use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst};
-use rustc_middle::ty::{self, EarlyBinder, Ty, TyCtxt};
+use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_span::Span;
 
 use super::explicit::ExplicitPredicatesMap;
@@ -13,20 +13,19 @@ use super::utils::*;
 /// `global_inferred_outlives`: this is initially the empty map that
 ///     was generated by walking the items in the crate. This will
 ///     now be filled with inferred predicates.
-pub fn infer_predicates<'tcx>(
+pub(super) fn infer_predicates<'tcx>(
     tcx: TyCtxt<'tcx>,
-    explicit_map: &mut ExplicitPredicatesMap<'tcx>,
-) -> FxHashMap<DefId, RequiredPredicates<'tcx>> {
+) -> FxHashMap<DefId, ty::EarlyBinder<RequiredPredicates<'tcx>>> {
     debug!("infer_predicates");
 
-    let mut predicates_added = true;
+    let mut explicit_map = ExplicitPredicatesMap::new();
 
     let mut global_inferred_outlives = FxHashMap::default();
 
     // If new predicates were added then we need to re-calculate
     // all crates since there could be new implied predicates.
-    while predicates_added {
-        predicates_added = false;
+    'outer: loop {
+        let mut predicates_added = false;
 
         // Visit all the crates and infer predicates
         for id in tcx.hir().items() {
@@ -53,9 +52,9 @@ pub fn infer_predicates<'tcx>(
                             tcx,
                             field_ty,
                             field_span,
-                            &mut global_inferred_outlives,
+                            &global_inferred_outlives,
                             &mut item_required_predicates,
-                            explicit_map,
+                            &mut explicit_map,
                         );
                     }
                 }
@@ -70,12 +69,17 @@ pub fn infer_predicates<'tcx>(
             // we walk the crates again and re-calculate predicates for all
             // items.
             let item_predicates_len: usize =
-                global_inferred_outlives.get(&item_did.to_def_id()).map_or(0, |p| p.len());
+                global_inferred_outlives.get(&item_did.to_def_id()).map_or(0, |p| p.0.len());
             if item_required_predicates.len() > item_predicates_len {
                 predicates_added = true;
-                global_inferred_outlives.insert(item_did.to_def_id(), item_required_predicates);
+                global_inferred_outlives
+                    .insert(item_did.to_def_id(), ty::EarlyBinder(item_required_predicates));
             }
         }
+
+        if !predicates_added {
+            break 'outer;
+        }
     }
 
     global_inferred_outlives
@@ -85,7 +89,7 @@ fn insert_required_predicates_to_be_wf<'tcx>(
     tcx: TyCtxt<'tcx>,
     field_ty: Ty<'tcx>,
     field_span: Span,
-    global_inferred_outlives: &FxHashMap<DefId, RequiredPredicates<'tcx>>,
+    global_inferred_outlives: &FxHashMap<DefId, ty::EarlyBinder<RequiredPredicates<'tcx>>>,
     required_predicates: &mut RequiredPredicates<'tcx>,
     explicit_map: &mut ExplicitPredicatesMap<'tcx>,
 ) {
@@ -133,11 +137,13 @@ fn insert_required_predicates_to_be_wf<'tcx>(
                 // 'a` holds for `Foo`.
                 debug!("Adt");
                 if let Some(unsubstituted_predicates) = global_inferred_outlives.get(&def.did()) {
-                    for (unsubstituted_predicate, &span) in unsubstituted_predicates {
+                    for (unsubstituted_predicate, &span) in &unsubstituted_predicates.0 {
                         // `unsubstituted_predicate` is `U: 'b` in the
                         // example above.  So apply the substitution to
                         // get `T: 'a` (or `predicate`):
-                        let predicate = EarlyBinder(*unsubstituted_predicate).subst(tcx, substs);
+                        let predicate = unsubstituted_predicates
+                            .rebind(*unsubstituted_predicate)
+                            .subst(tcx, substs);
                         insert_outlives_predicate(
                             tcx,
                             predicate.0,
@@ -224,7 +230,7 @@ fn insert_required_predicates_to_be_wf<'tcx>(
 /// will give us `U: 'static` and `U: Foo`. The latter we
 /// can ignore, but we will want to process `U: 'static`,
 /// applying the substitution as above.
-pub fn check_explicit_predicates<'tcx>(
+fn check_explicit_predicates<'tcx>(
     tcx: TyCtxt<'tcx>,
     def_id: DefId,
     substs: &[GenericArg<'tcx>],
@@ -242,7 +248,7 @@ pub fn check_explicit_predicates<'tcx>(
     );
     let explicit_predicates = explicit_map.explicit_predicates_of(tcx, def_id);
 
-    for (outlives_predicate, &span) in explicit_predicates {
+    for (outlives_predicate, &span) in &explicit_predicates.0 {
         debug!("outlives_predicate = {:?}", &outlives_predicate);
 
         // Careful: If we are inferring the effects of a `dyn Trait<..>`
@@ -287,7 +293,7 @@ pub fn check_explicit_predicates<'tcx>(
             continue;
         }
 
-        let predicate = EarlyBinder(*outlives_predicate).subst(tcx, substs);
+        let predicate = explicit_predicates.rebind(*outlives_predicate).subst(tcx, substs);
         debug!("predicate = {:?}", &predicate);
         insert_outlives_predicate(tcx, predicate.0, predicate.1, span, required_predicates);
     }
diff --git a/compiler/rustc_typeck/src/outlives/mod.rs b/compiler/rustc_typeck/src/outlives/mod.rs
index dccfee19960..8fa65d51e3b 100644
--- a/compiler/rustc_typeck/src/outlives/mod.rs
+++ b/compiler/rustc_typeck/src/outlives/mod.rs
@@ -88,9 +88,7 @@ fn inferred_outlives_crate(tcx: TyCtxt<'_>, (): ()) -> CratePredicatesMap<'_> {
     // for the type.
 
     // Compute the inferred predicates
-    let mut exp_map = explicit::ExplicitPredicatesMap::new();
-
-    let global_inferred_outlives = implicit_infer::infer_predicates(tcx, &mut exp_map);
+    let global_inferred_outlives = implicit_infer::infer_predicates(tcx);
 
     // Convert the inferred predicates into the "collected" form the
     // global data structure expects.
@@ -100,7 +98,7 @@ fn inferred_outlives_crate(tcx: TyCtxt<'_>, (): ()) -> CratePredicatesMap<'_> {
     let predicates = global_inferred_outlives
         .iter()
         .map(|(&def_id, set)| {
-            let predicates = &*tcx.arena.alloc_from_iter(set.iter().filter_map(
+            let predicates = &*tcx.arena.alloc_from_iter(set.0.iter().filter_map(
                 |(ty::OutlivesPredicate(kind1, region2), &span)| {
                     match kind1.unpack() {
                         GenericArgKind::Type(ty1) => Some((
diff --git a/compiler/rustc_typeck/src/outlives/utils.rs b/compiler/rustc_typeck/src/outlives/utils.rs
index 14e3048cadc..b718ca94213 100644
--- a/compiler/rustc_typeck/src/outlives/utils.rs
+++ b/compiler/rustc_typeck/src/outlives/utils.rs
@@ -7,12 +7,12 @@ use std::collections::BTreeMap;
 
 /// Tracks the `T: 'a` or `'a: 'a` predicates that we have inferred
 /// must be added to the struct header.
-pub type RequiredPredicates<'tcx> =
+pub(crate) type RequiredPredicates<'tcx> =
     BTreeMap<ty::OutlivesPredicate<GenericArg<'tcx>, ty::Region<'tcx>>, Span>;
 
 /// Given a requirement `T: 'a` or `'b: 'a`, deduce the
 /// outlives_component and add it to `required_predicates`
-pub fn insert_outlives_predicate<'tcx>(
+pub(crate) fn insert_outlives_predicate<'tcx>(
     tcx: TyCtxt<'tcx>,
     kind: GenericArg<'tcx>,
     outlived_region: Region<'tcx>,
diff --git a/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs b/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs
index 469f7d1172a..99729391e02 100644
--- a/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs
+++ b/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs
@@ -420,12 +420,10 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
         let provided_lt_args = self.num_provided_lifetime_args();
         let provided_type_or_const_args = self.num_provided_type_or_const_args();
 
-        let get_verb = |num_args| if num_args == 1 { "was" } else { "were" };
-
         let (provided_args_str, verb) = match self.gen_args_info {
             MissingLifetimes { .. } | ExcessLifetimes { .. } => (
                 format!("{} lifetime argument{}", provided_lt_args, pluralize!(provided_lt_args)),
-                get_verb(provided_lt_args),
+                pluralize!("was", provided_lt_args),
             ),
             MissingTypesOrConsts { .. } | ExcessTypesOrConsts { .. } => (
                 format!(
@@ -433,7 +431,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
                     provided_type_or_const_args,
                     pluralize!(provided_type_or_const_args)
                 ),
-                get_verb(provided_type_or_const_args),
+                pluralize!("was", provided_type_or_const_args),
             ),
         };